summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTiago Cunha <tcunha@gmx.com>2009-10-11 23:38:16 +0000
committerTiago Cunha <tcunha@gmx.com>2009-10-11 23:38:16 +0000
commit6a1ebb11df452e570b430ba16192c38b0e3a0f90 (patch)
treef2165e41abebbb503f4f8feba689468128a96cf7
parent1fd3a405e69c7478ab29d29cd56ac5b23f2f4f71 (diff)
Sync OpenBSD patchset 371:
Add "grouped sessions" which have independent name, options, current window and so on but where the linked windows are synchronized (ie creating, killing windows and so on are mirrored between the sessions). A grouped session may be created by passing -t to new-session. Had this around for a while, tested by a couple of people.
-rw-r--r--cmd-break-pane.c3
-rw-r--r--cmd-choose-session.c16
-rw-r--r--cmd-choose-window.c4
-rw-r--r--cmd-kill-session.c4
-rw-r--r--cmd-link-window.c8
-rw-r--r--cmd-list-sessions.c23
-rw-r--r--cmd-move-window.c4
-rw-r--r--cmd-new-session.c38
-rw-r--r--cmd-new-window.c8
-rw-r--r--cmd-rename-window.c4
-rw-r--r--cmd-swap-window.c20
-rw-r--r--cmd-unlink-window.c20
-rw-r--r--server-fn.c75
-rw-r--r--server.c6
-rw-r--r--session.c191
-rw-r--r--status.c4
-rw-r--r--tmux.123
-rw-r--r--tmux.h28
-rw-r--r--window.c10
19 files changed, 411 insertions, 78 deletions
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index 63e4c6a9..e2e8768f 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-break-pane.c,v 1.8 2009-08-16 19:16:27 tcunha Exp $ */
+/* $Id: cmd-break-pane.c,v 1.9 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -78,6 +78,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
session_select(s, wl->idx);
server_redraw_session(s);
+ server_status_session_group(s);
return (0);
}
diff --git a/cmd-choose-session.c b/cmd-choose-session.c
index da15b9bb..27e03d19 100644
--- a/cmd-choose-session.c
+++ b/cmd-choose-session.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-choose-session.c,v 1.13 2009-09-07 23:59:19 tcunha Exp $ */
+/* $Id: cmd-choose-session.c,v 1.14 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -54,7 +54,9 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_choose_session_data *cdata;
struct winlink *wl;
struct session *s;
+ struct session_group *sg;
u_int i, idx, cur;
+ char tmp[64];
if (ctx->curclient == NULL) {
ctx->error(ctx, "must be run interactively");
@@ -76,10 +78,18 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
cur = idx;
idx++;
+ sg = session_group_find(s);
+ if (sg == NULL)
+ *tmp = '\0';
+ else {
+ idx = session_group_index(sg);
+ xsnprintf(tmp, sizeof tmp, " (group %u)", idx);
+ }
+
window_choose_add(wl->window->active, i,
- "%s: %u windows [%ux%u]%s", s->name,
+ "%s: %u windows [%ux%u]%s%s", s->name,
winlink_count(&s->windows), s->sx, s->sy,
- s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+ tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
}
cdata = xmalloc(sizeof *cdata);
diff --git a/cmd-choose-window.c b/cmd-choose-window.c
index 04d77e73..8137c6eb 100644
--- a/cmd-choose-window.c
+++ b/cmd-choose-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-choose-window.c,v 1.17 2009-09-07 23:59:19 tcunha Exp $ */
+/* $Id: cmd-choose-window.c,v 1.18 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -89,7 +89,7 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
flag = '+';
else if (wm == s->curw)
flag = '*';
- else if (wm == SLIST_FIRST(&s->lastw))
+ else if (wm == TAILQ_FIRST(&s->lastw))
flag = '-';
title = w->active->screen->title;
diff --git a/cmd-kill-session.c b/cmd-kill-session.c
index cc1ff254..55e3ff6e 100644
--- a/cmd-kill-session.c
+++ b/cmd-kill-session.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-kill-session.c,v 1.14 2009-07-28 22:12:16 tcunha Exp $ */
+/* $Id: cmd-kill-session.c,v 1.15 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -53,7 +53,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
- if (c->session == s) {
+ if (c != NULL && c->session == s) {
c->session = NULL;
server_write_client(c, MSG_EXIT, NULL, 0);
}
diff --git a/cmd-link-window.c b/cmd-link-window.c
index 59a59109..0f55c82d 100644
--- a/cmd-link-window.c
+++ b/cmd-link-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-link-window.c,v 1.34 2009-09-20 22:17:03 tcunha Exp $ */
+/* $Id: cmd-link-window.c,v 1.35 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -43,19 +43,19 @@ int
cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_srcdst_data *data = self->data;
- struct session *dst;
+ struct session *src, *dst;
struct winlink *wl;
char *cause;
int idx, kflag, dflag;
- if ((wl = cmd_find_window(ctx, data->src, NULL)) == NULL)
+ if ((wl = cmd_find_window(ctx, data->src, &src)) == NULL)
return (-1);
if ((idx = cmd_find_index(ctx, data->dst, &dst)) == -2)
return (-1);
kflag = data->chflags & CMD_CHFLAG('k');
dflag = data->chflags & CMD_CHFLAG('d');
- if (server_link_window(wl, dst, idx, kflag, !dflag, &cause) != 0) {
+ if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
ctx->error(ctx, "can't link window: %s", cause);
xfree(cause);
return (-1);
diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c
index 74274e05..e6be3bbc 100644
--- a/cmd-list-sessions.c
+++ b/cmd-list-sessions.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-list-sessions.c,v 1.21 2009-07-28 22:12:16 tcunha Exp $ */
+/* $Id: cmd-list-sessions.c,v 1.22 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -42,23 +42,32 @@ const struct cmd_entry cmd_list_sessions_entry = {
int
cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx)
{
- struct session *s;
- char *tim;
- u_int i;
- time_t t;
+ struct session *s;
+ struct session_group *sg;
+ char *tim, tmp[64];
+ u_int i, idx;
+ time_t t;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
+ sg = session_group_find(s);
+ if (sg == NULL)
+ *tmp = '\0';
+ else {
+ idx = session_group_index(sg);
+ xsnprintf(tmp, sizeof tmp, " (group %u)", idx);
+ }
+
t = s->tv.tv_sec;
tim = ctime(&t);
*strchr(tim, '\n') = '\0';
- ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s",
+ ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s%s",
s->name, winlink_count(&s->windows), tim, s->sx, s->sy,
- s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+ tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
}
return (0);
diff --git a/cmd-move-window.c b/cmd-move-window.c
index ae0595d2..6ac07c1c 100644
--- a/cmd-move-window.c
+++ b/cmd-move-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-move-window.c,v 1.11 2009-09-20 22:17:03 tcunha Exp $ */
+/* $Id: cmd-move-window.c,v 1.12 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -55,7 +55,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx)
kflag = data->chflags & CMD_CHFLAG('k');
dflag = data->chflags & CMD_CHFLAG('d');
- if (server_link_window(wl, dst, idx, kflag, !dflag, &cause) != 0) {
+ if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
ctx->error(ctx, "can't move window: %s", cause);
xfree(cause);
return (-1);
diff --git a/cmd-new-session.c b/cmd-new-session.c
index 8ee40eca..426fd4de 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-new-session.c,v 1.67 2009-09-22 14:06:40 tcunha Exp $ */
+/* $Id: cmd-new-session.c,v 1.68 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -35,6 +35,7 @@ void cmd_new_session_init(struct cmd *, int);
size_t cmd_new_session_print(struct cmd *, char *, size_t);
struct cmd_new_session_data {
+ char *target;
char *newname;
char *winname;
char *cmd;
@@ -43,7 +44,7 @@ struct cmd_new_session_data {
const struct cmd_entry cmd_new_session_entry = {
"new-session", "new",
- "[-d] [-n window-name] [-s session-name] [command]",
+ "[-d] [-n window-name] [-s session-name] [-t target-session] [command]",
CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, 0,
cmd_new_session_init,
cmd_new_session_parse,
@@ -59,6 +60,7 @@ cmd_new_session_init(struct cmd *self, unused int arg)
self->data = data = xmalloc(sizeof *data);
data->flag_detached = 0;
+ data->target = NULL;
data->newname = NULL;
data->winname = NULL;
data->cmd = NULL;
@@ -73,7 +75,7 @@ cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause)
self->entry->init(self, KEYC_NONE);
data = self->data;
- while ((opt = getopt(argc, argv, "ds:n:")) != -1) {
+ while ((opt = getopt(argc, argv, "ds:t:n:")) != -1) {
switch (opt) {
case 'd':
data->flag_detached = 1;
@@ -82,6 +84,10 @@ cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause)
if (data->newname == NULL)
data->newname = xstrdup(optarg);
break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
case 'n':
if (data->winname == NULL)
data->winname = xstrdup(optarg);
@@ -95,6 +101,9 @@ cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause)
if (argc != 0 && argc != 1)
goto usage;
+ if (data->target != NULL && (argc == 1 || data->winname != NULL))
+ goto usage;
+
if (argc == 1)
data->cmd = xstrdup(argv[0]);
@@ -111,7 +120,7 @@ int
cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_new_session_data *data = self->data;
- struct session *s;
+ struct session *s, *groupwith;
struct window *w;
struct environ env;
struct termios tio, *tiop;
@@ -125,6 +134,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1);
}
+ groupwith = NULL;
+ if (data->target != NULL &&
+ (groupwith = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
/*
* There are three cases:
*
@@ -205,7 +219,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
sy = 1;
/* Figure out the command for the new window. */
- if (data->cmd != NULL)
+ if (data->target != NULL)
+ cmd = NULL;
+ else if (data->cmd != NULL)
cmd = data->cmd;
else
cmd = options_get_string(&global_s_options, "default-command");
@@ -228,7 +244,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
environ_free(&env);
/* Set the initial window name if one given. */
- if (data->winname != NULL) {
+ if (cmd != NULL && data->winname != NULL) {
w = s->curw->window;
xfree(w->name);
@@ -238,6 +254,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
}
/*
+ * If a target session is given, this is to be part of a session group,
+ * so add it to the group and synchronize.
+ */
+ if (groupwith != NULL) {
+ session_group_add(groupwith, s);
+ session_group_synchronize_to(s);
+ session_select(s, RB_ROOT(&s->windows)->idx);
+ }
+
+ /*
* Set the client to the new session. If a command client exists, it is
* taking this session and needs to get MSG_READY and stay around.
*/
diff --git a/cmd-new-window.c b/cmd-new-window.c
index 4a258c41..779ca418 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-new-window.c,v 1.38 2009-09-22 14:06:40 tcunha Exp $ */
+/* $Id: cmd-new-window.c,v 1.39 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -164,9 +164,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx)
}
if (!data->flag_detached) {
session_select(s, wl->idx);
- server_redraw_session(s);
- } else
- server_status_session(s);
+ server_redraw_session_group(s);
+ } else
+ server_status_session_group(s);
return (0);
}
diff --git a/cmd-rename-window.c b/cmd-rename-window.c
index 518e013d..44242d5b 100644
--- a/cmd-rename-window.c
+++ b/cmd-rename-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-rename-window.c,v 1.29 2009-07-28 22:12:16 tcunha Exp $ */
+/* $Id: cmd-rename-window.c,v 1.30 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -53,7 +53,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx)
wl->window->name = xstrdup(data->arg);
options_set_number(&wl->window->options, "automatic-rename", 0);
- server_status_session(s);
+ server_status_window(wl->window);
return (0);
}
diff --git a/cmd-swap-window.c b/cmd-swap-window.c
index fdd25ce6..8d6e0b32 100644
--- a/cmd-swap-window.c
+++ b/cmd-swap-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-swap-window.c,v 1.17 2009-07-28 22:12:16 tcunha Exp $ */
+/* $Id: cmd-swap-window.c,v 1.18 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -44,6 +44,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_srcdst_data *data = self->data;
struct session *src, *dst;
+ struct session_group *sg_src, *sg_dst;
struct winlink *wl_src, *wl_dst;
struct window *w;
@@ -52,6 +53,14 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
if ((wl_dst = cmd_find_window(ctx, data->dst, &dst)) == NULL)
return (-1);
+ sg_src = session_group_find(src);
+ sg_dst = session_group_find(dst);
+ if (src != dst &&
+ sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) {
+ ctx->error(ctx, "can't move window, sessions are grouped");
+ return (-1);
+ }
+
if (wl_dst->window == wl_src->window)
return (0);
@@ -64,9 +73,12 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
if (src != dst)
session_select(src, wl_src->idx);
}
- server_redraw_session(src);
- if (src != dst)
- server_redraw_session(dst);
+ session_group_synchronize_from(src);
+ server_redraw_session_group(src);
+ if (src != dst) {
+ session_group_synchronize_from(dst);
+ server_redraw_session_group(dst);
+ }
recalculate_sizes();
return (0);
diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c
index dea0a058..936a7e4f 100644
--- a/cmd-unlink-window.c
+++ b/cmd-unlink-window.c
@@ -1,4 +1,4 @@
-/* $Id: cmd-unlink-window.c,v 1.18 2009-09-20 22:17:03 tcunha Exp $ */
+/* $Id: cmd-unlink-window.c,v 1.19 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -42,16 +42,28 @@ cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_target_data *data = self->data;
struct winlink *wl;
- struct session *s;
+ struct window *w;
+ struct session *s, *s2;
+ struct session_group *sg;
+ u_int references;
if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
return (-1);
+ w = wl->window;
- if (!(data->chflags & CMD_CHFLAG('k')) && wl->window->references == 1) {
+ sg = session_group_find(s);
+ if (sg != NULL) {
+ references = 0;
+ TAILQ_FOREACH(s2, &sg->sessions, gentry)
+ references++;
+ } else
+ references = 1;
+
+ if (!(data->chflags & CMD_CHFLAG('k')) && w->references == references) {
ctx->error(ctx, "window is only linked to one session");
return (-1);
}
-
+
server_unlink_window(s, wl);
recalculate_sizes();
diff --git a/server-fn.c b/server-fn.c
index 03c14c35..f34c8aec 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -1,4 +1,4 @@
-/* $Id: server-fn.c,v 1.92 2009-10-05 18:23:31 tcunha Exp $ */
+/* $Id: server-fn.c,v 1.93 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -105,6 +105,19 @@ server_redraw_session(struct session *s)
}
void
+server_redraw_session_group(struct session *s)
+{
+ struct session_group *sg;
+
+ if ((sg = session_group_find(s)) == NULL)
+ server_redraw_session(s);
+ else {
+ TAILQ_FOREACH(s, &sg->sessions, gentry)
+ server_redraw_session(s);
+ }
+}
+
+void
server_status_session(struct session *s)
{
struct client *c;
@@ -120,6 +133,19 @@ server_status_session(struct session *s)
}
void
+server_status_session_group(struct session *s)
+{
+ struct session_group *sg;
+
+ if ((sg = session_group_find(s)) == NULL)
+ server_status_session(s);
+ else {
+ TAILQ_FOREACH(s, &sg->sessions, gentry)
+ server_status_session(s);
+ }
+}
+
+void
server_redraw_window(struct window *w)
{
struct client *c;
@@ -220,18 +246,27 @@ server_kill_window(struct window *w)
continue;
if (session_detach(s, wl))
- server_destroy_session(s);
- else
+ server_destroy_session_group(s);
+ else {
server_redraw_session(s);
+ server_status_session_group(s);
+ }
}
}
int
-server_link_window(
- struct winlink *srcwl, struct session *dst, int dstidx,
- int killflag, int selectflag, char **cause)
+server_link_window(struct session *src, struct winlink *srcwl,
+ struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
{
- struct winlink *dstwl;
+ struct winlink *dstwl;
+ struct session_group *srcsg, *dstsg;
+
+ srcsg = session_group_find(src);
+ dstsg = session_group_find(dst);
+ if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
+ xasprintf(cause, "sessions are grouped");
+ return (-1);
+ }
dstwl = NULL;
if (dstidx != -1)
@@ -260,12 +295,9 @@ server_link_window(
if (dstwl == NULL)
return (-1);
- if (!selectflag)
- server_status_session(dst);
- else {
+ if (selectflag)
session_select(dst, dstwl->idx);
- server_redraw_session(dst);
- }
+ server_redraw_session_group(dst);
return (0);
}
@@ -274,9 +306,24 @@ void
server_unlink_window(struct session *s, struct winlink *wl)
{
if (session_detach(s, wl))
- server_destroy_session(s);
+ server_destroy_session_group(s);
else
- server_redraw_session(s);
+ server_redraw_session_group(s);
+}
+
+void
+server_destroy_session_group(struct session *s)
+{
+ struct session_group *sg;
+
+ if ((sg = session_group_find(s)) == NULL)
+ server_destroy_session(s);
+ else {
+ TAILQ_FOREACH(s, &sg->sessions, gentry)
+ server_destroy_session(s);
+ TAILQ_REMOVE(&session_groups, sg, entry);
+ xfree(sg);
+ }
}
void
diff --git a/server.c b/server.c
index b6de3986..6a7e7a54 100644
--- a/server.c
+++ b/server.c
@@ -1,4 +1,4 @@
-/* $Id: server.c,v 1.197 2009-10-11 23:30:28 tcunha Exp $ */
+/* $Id: server.c,v 1.198 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -248,6 +248,7 @@ server_start(char *path)
ARRAY_INIT(&dead_clients);
ARRAY_INIT(&sessions);
ARRAY_INIT(&dead_sessions);
+ TAILQ_INIT(&session_groups);
mode_key_init_trees();
key_bindings_init();
utf8_build();
@@ -1246,10 +1247,11 @@ server_check_window(struct window *w)
if (wl->window != w)
continue;
if (session_detach(s, wl)) {
- server_destroy_session(s);
+ server_destroy_session_group(s);
break;
}
server_redraw_session(s);
+ server_status_session_group(s);
goto restart;
}
}
diff --git a/session.c b/session.c
index b6a23ad7..e4f5055c 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $Id: session.c,v 1.68 2009-10-11 23:30:28 tcunha Exp $ */
+/* $Id: session.c,v 1.69 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -29,6 +29,7 @@
/* Global session list. */
struct sessions sessions;
struct sessions dead_sessions;
+struct session_groups session_groups;
struct winlink *session_next_activity(struct session *, struct winlink *);
struct winlink *session_previous_activity(struct session *, struct winlink *);
@@ -130,7 +131,7 @@ session_create(const char *name, const char *cmd, const char *cwd,
fatal("gettimeofday failed");
s->curw = NULL;
- SLIST_INIT(&s->lastw);
+ TAILQ_INIT(&s->lastw);
RB_INIT(&s->windows);
SLIST_INIT(&s->alerts);
@@ -163,11 +164,14 @@ session_create(const char *name, const char *cmd, const char *cwd,
s->name = xstrdup(name);
else
xasprintf(&s->name, "%u", i);
- if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
- session_destroy(s);
- return (NULL);
+
+ if (cmd != NULL) {
+ if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
+ session_destroy(s);
+ return (NULL);
+ }
+ session_select(s, RB_ROOT(&s->windows)->idx);
}
- session_select(s, RB_ROOT(&s->windows)->idx);
log_debug("session %s created", s->name);
@@ -191,13 +195,14 @@ session_destroy(struct session *s)
if (s->tio != NULL)
xfree(s->tio);
+ session_group_remove(s);
session_alert_cancel(s, NULL);
environ_free(&s->environ);
options_free(&s->options);
paste_free_stack(&s->buffers);
- while (!SLIST_EMPTY(&s->lastw))
- winlink_stack_remove(&s->lastw, SLIST_FIRST(&s->lastw));
+ while (!TAILQ_EMPTY(&s->lastw))
+ winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
while (!RB_EMPTY(&s->windows))
winlink_remove(&s->windows, RB_ROOT(&s->windows));
@@ -267,6 +272,7 @@ session_attach(struct session *s, struct window *w, int idx, char **cause)
if ((wl = winlink_add(&s->windows, w, idx)) == NULL)
xasprintf(cause, "index in use: %d", idx);
+ session_group_synchronize_from(s);
return (wl);
}
@@ -281,6 +287,7 @@ session_detach(struct session *s, struct winlink *wl)
session_alert_cancel(s, wl);
winlink_stack_remove(&s->lastw, wl);
winlink_remove(&s->windows, wl);
+ session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) {
session_destroy(s);
return (1);
@@ -407,7 +414,7 @@ session_last(struct session *s)
{
struct winlink *wl;
- wl = SLIST_FIRST(&s->lastw);
+ wl = TAILQ_FIRST(&s->lastw);
if (wl == NULL)
return (-1);
if (wl == s->curw)
@@ -419,3 +426,169 @@ session_last(struct session *s)
session_alert_cancel(s, wl);
return (0);
}
+
+/* Find the session group containing a session. */
+struct session_group *
+session_group_find(struct session *target)
+{
+ struct session_group *sg;
+ struct session *s;
+
+ TAILQ_FOREACH(sg, &session_groups, entry) {
+ TAILQ_FOREACH(s, &sg->sessions, gentry) {
+ if (s == target)
+ return (sg);
+ }
+ }
+ return (NULL);
+}
+
+/* Find session group index. */
+u_int
+session_group_index(struct session_group *sg)
+{
+ struct session_group *sg2;
+ u_int i;
+
+ i = 0;
+ TAILQ_FOREACH(sg2, &session_groups, entry) {
+ if (sg == sg2)
+ return (i);
+ i++;
+ }
+
+ fatalx("session group not found");
+}
+
+/*
+ * Add a session to the session group containing target, creating it if
+ * necessary.
+ */
+void
+session_group_add(struct session *target, struct session *s)
+{
+ struct session_group *sg;
+
+ if ((sg = session_group_find(target)) == NULL) {
+ sg = xmalloc(sizeof *sg);
+ TAILQ_INSERT_TAIL(&session_groups, sg, entry);
+ TAILQ_INIT(&sg->sessions);
+ TAILQ_INSERT_TAIL(&sg->sessions, target, gentry);
+ }
+ TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
+}
+
+/* Remove a session from its group and destroy the group if empty. */
+void
+session_group_remove(struct session *s)
+{
+ struct session_group *sg;
+
+ if ((sg = session_group_find(s)) == NULL)
+ return;
+ TAILQ_REMOVE(&sg->sessions, s, gentry);
+ if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL)
+ TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry);
+ if (TAILQ_EMPTY(&sg->sessions)) {
+ TAILQ_REMOVE(&session_groups, sg, entry);
+ xfree(sg);
+ }
+}
+
+/* Synchronize a session to its session group. */
+void
+session_group_synchronize_to(struct session *s)
+{
+ struct session_group *sg;
+ struct session *target;
+
+ if ((sg = session_group_find(s)) == NULL)
+ return;
+
+ target = NULL;
+ TAILQ_FOREACH(target, &sg->sessions, gentry) {
+ if (target != s)
+ break;
+ }
+ session_group_synchronize1(target, s);
+}
+
+/* Synchronize a session group to a session. */
+void
+session_group_synchronize_from(struct session *target)
+{
+ struct session_group *sg;
+ struct session *s;
+
+ if ((sg = session_group_find(target)) == NULL)
+ return;
+
+ TAILQ_FOREACH(s, &sg->sessions, gentry) {
+ if (s != target)
+ session_group_synchronize1(target, s);
+ }
+}
+
+/*
+ * Synchronize a session with a target session. This means destroying all
+ * winlinks then recreating them, then updating the current window, last window
+ * stack and alerts.
+ */
+void
+session_group_synchronize1(struct session *target, struct session *s)
+{
+ struct winlinks old_windows, *ww;
+ struct winlink_stack old_lastw;
+ struct winlink *wl, *wl2;
+ struct session_alert *sa;
+
+ /* Don't do anything if the session is empty (it'll be destroyed). */
+ ww = &target->windows;
+ if (RB_EMPTY(ww))
+ return;
+
+ /* If the current window has vanished, move to the next now. */
+ if (s->curw != NULL) {
+ while (winlink_find_by_index(ww, s->curw->idx) == NULL)
+ session_next(s, 0);
+ }
+
+ /* Save the old pointer and reset it. */
+ memcpy(&old_windows, &s->windows, sizeof old_windows);
+ RB_INIT(&s->windows);
+
+ /* Link all the windows from the target. */
+ RB_FOREACH(wl, winlinks, ww)
+ winlink_add(&s->windows, wl->window, wl->idx);
+
+ /* Fix up the current window. */
+ if (s->curw != NULL)
+ s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
+ else
+ s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
+
+ /* Fix up the last window stack. */
+ memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
+ TAILQ_INIT(&s->lastw);
+ TAILQ_FOREACH(wl, &old_lastw, sentry) {
+ wl2 = winlink_find_by_index(&s->windows, wl->idx);
+ if (wl2 != NULL)
+ TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
+ }
+
+ /* And update the alerts list. */
+ SLIST_FOREACH(sa, &s->alerts, entry) {
+ wl = winlink_find_by_index(&s->windows, sa->wl->idx);
+ if (wl == NULL)
+ session_alert_cancel(s, sa->wl);
+ else
+ sa->wl = wl;
+ }
+
+ /* Then free the old winlinks list. */
+ while (!RB_EMPTY(&old_windows)) {
+ wl = RB_ROOT(&old_windows);
+ RB_REMOVE(winlinks, &old_windows, wl);
+ xfree(wl);
+ }
+}
diff --git a/status.c b/status.c
index 7f4f31e1..795f8efc 100644
--- a/status.c
+++ b/status.c
@@ -1,4 +1,4 @@
-/* $Id: status.c,v 1.121 2009-09-25 17:45:46 tcunha Exp $ */
+/* $Id: status.c,v 1.122 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -516,7 +516,7 @@ status_print(struct session *s, struct winlink *wl, struct grid_cell *gc)
gc->attr = attr;
flag = ' ';
- if (wl == SLIST_FIRST(&s->lastw))
+ if (wl == TAILQ_FIRST(&s->lastw))
flag = '-';
if (wl == s->curw) {
fg = options_get_number(oo, "window-status-current-fg");
diff --git a/tmux.1 b/tmux.1
index 328ec5d9..4600910d 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1,4 +1,4 @@
-.\" $Id: tmux.1,v 1.182 2009-10-11 23:30:28 tcunha Exp $
+.\" $Id: tmux.1,v 1.183 2009-10-11 23:38:16 tcunha Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
.\"
@@ -409,6 +409,7 @@ Lock all clients attached to
.Op Fl d
.Op Fl n Ar window-name
.Op Fl s Ar session-name
+.Op Fl t Ar target-session
.Op Ar command
.Xc
.D1 (alias: Ic new )
@@ -425,6 +426,26 @@ are the name of and command to execute in the initial window.
If run from a terminal, any
.Xr termios 4
special characters are saved and used for new windows in the new session.
+.Pp
+If
+.Fl t
+is given, the new session is
+.Em grouped
+with
+.Ar target-session .
+This means they share the same set of windows - all windows from
+.Ar target-session
+are linked to the new session and any subsequent new windows or windows being
+closed are applied to both sessions.
+The current and previous window and any session options remain independent and
+either session may be killed without affecting the other.
+Giving
+.Fl n
+or
+.Ar command
+are invalid if
+.Fl t
+is used.
.It Ic refresh-client Op Fl t Ar target-client
.D1 (alias: Ic refresh )
Refresh the current client if bound to a key, or a single client if one is given
diff --git a/tmux.h b/tmux.h
index 63d05fc5..f53b3b42 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1,4 +1,4 @@
-/* $Id: tmux.h,v 1.461 2009-10-11 23:30:28 tcunha Exp $ */
+/* $Id: tmux.h,v 1.462 2009-10-11 23:38:16 tcunha Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -737,10 +737,10 @@ struct winlink {
struct window *window;
RB_ENTRY(winlink) entry;
- SLIST_ENTRY(winlink) sentry;
+ TAILQ_ENTRY(winlink) sentry;
};
RB_HEAD(winlinks, winlink);
-SLIST_HEAD(winlink_stack, winlink);
+TAILQ_HEAD(winlink_stack, winlink);
/* Layout direction. */
enum layout_type {
@@ -795,6 +795,13 @@ struct session_al