summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd-break-pane.c1
-rw-r--r--cmd-choose-session.c14
-rw-r--r--cmd-choose-window.c2
-rw-r--r--cmd-kill-session.c2
-rw-r--r--cmd-link-window.c6
-rw-r--r--cmd-list-sessions.c21
-rw-r--r--cmd-move-window.c2
-rw-r--r--cmd-new-session.c36
-rw-r--r--cmd-new-window.c6
-rw-r--r--cmd-rename-window.c2
-rw-r--r--cmd-swap-window.c18
-rw-r--r--cmd-unlink-window.c18
-rw-r--r--server-fn.c73
-rw-r--r--server.c4
-rw-r--r--session.c189
-rw-r--r--status.c2
-rw-r--r--tmux.121
-rw-r--r--tmux.h26
-rw-r--r--window.c8
19 files changed, 392 insertions, 59 deletions
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index b8d489de..58a93de2 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -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 7bc34853..b9c7c258 100644
--- a/cmd-choose-session.c
+++ b/cmd-choose-session.c
@@ -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 43a24585..9d6cd932 100644
--- a/cmd-choose-window.c
+++ b/cmd-choose-window.c
@@ -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 51a3edaf..402b869b 100644
--- a/cmd-kill-session.c
+++ b/cmd-kill-session.c
@@ -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 95514d40..a0b81dcc 100644
--- a/cmd-link-window.c
+++ b/cmd-link-window.c
@@ -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 95c26567..cfa1433b 100644
--- a/cmd-list-sessions.c
+++ b/cmd-list-sessions.c
@@ -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 4c92556a..175e6576 100644
--- a/cmd-move-window.c
+++ b/cmd-move-window.c
@@ -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 2aafdb1b..4889ab4b 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -34,6 +34,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;
@@ -42,7 +43,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,
@@ -58,6 +59,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;
@@ -72,7 +74,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;
@@ -81,6 +83,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);
@@ -94,6 +100,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]);
@@ -110,7 +119,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;
@@ -124,6 +133,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:
*
@@ -204,7 +218,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");
@@ -227,7 +243,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);
@@ -237,6 +253,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 bbffad14..44fe6b5a 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -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 1037b25a..6f4f9ab8 100644
--- a/cmd-rename-window.c
+++ b/cmd-rename-window.c
@@ -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 875291f2..f164f234 100644
--- a/cmd-swap-window.c
+++ b/cmd-swap-window.c
@@ -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 38904689..e38b6e1e 100644
--- a/cmd-unlink-window.c
+++ b/cmd-unlink-window.c
@@ -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 4e3e12ec..beaae07a 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -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 9c37b7c1..1de30ad6 100644
--- a/server.c
+++ b/server.c
@@ -247,6 +247,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();
@@ -1243,10 +1244,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 62c5b746..a477634e 100644
--- a/session.c
+++ b/session.c
@@ -30,6 +30,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 *);
@@ -131,7 +132,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);
@@ -164,11 +165,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);
@@ -192,13 +196,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));
@@ -268,6 +273,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);
}
@@ -282,6 +288,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);
@@ -408,7 +415,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)
@@ -420,3 +427,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 3ffee331..dbf82c14 100644
--- a/status.c
+++ b/status.c
@@ -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 750e0134..9c9b5caa 100644
--- a/tmux.1
+++ b/tmux.1
@@ -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 9fd864f5..f4a25b5d 100644
--- a/tmux.h
+++ b/tmux.h
@@ -739,10 +739,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 {
@@ -797,6 +797,13 @@ struct session_alert {
SLIST_ENTRY(session_alert) entry;
};
+struct session_group {
+ TAILQ_HEAD(, session) sessions;
+
+ TAILQ_ENTRY(session_group) entry;
+};
+TAILQ_HEAD(session_groups, session_group);
+
struct session {
char *name;
struct timeval tv;
@@ -824,6 +831,8 @@ struct session {
struct environ environ;
int references;
+
+ TAILQ_ENTRY(session) gentry;
};
ARRAY_DECL(sessions, struct session *);
@@ -1456,7 +1465,9 @@ void server_write_session(
void server_redraw_client(struct client *);
void server_status_client(struct client *);
void server_redraw_session(struct session *);
+void server_redraw_session_group(struct session *);
void server_status_session(struct session *);
+void server_status_session_group(struct session *);
void server_redraw_window(struct window *);
void server_status_window(struct window *);
void server_lock(void);
@@ -1464,9 +1475,10 @@ void server_lock_session(struct session *);
void server_lock_client(struct client *);
int server_unlock(const char *);
void server_kill_window(struct window *);
-int server_link_window(
+int server_link_window(struct session *,
struct winlink *, struct session *, int, int, int, char **);
void server_unlink_window(struct session *, struct winlink *);
+void server_destroy_session_group(struct session *);
void server_destroy_session(struct session *);
void server_set_identify(struct client *);
void server_clear_identify(struct client *);
@@ -1719,6 +1731,7 @@ char *default_window_name(struct window *);
/* session.c */
extern struct sessions sessions;
extern struct sessions dead_sessions;
+extern struct session_groups session_groups;
void session_alert_add(struct session *, struct window *, int);
void session_alert_cancel(struct session *, struct winlink *);
int session_alert_has(struct session *, struct winlink *, int);
@@ -1739,6 +1752,13 @@ int session_next(struct session *, int);
int session_previous(struct session *, int);
int session_select(struct session *, int);
int session_last(struct session *);
+struct session_group *session_group_find(struct session *);
+u_int session_group_index(struct session_group *);
+void session_group_add(struct session *, struct session *);
+void session_group_remove(struct session *);
+void session_group_synchronize_to(struct session *);
+void session_group_synchronize_from(struct session *);
+void session_group_synchronize1(struct session *, struct session *);
/* utf8.c */
void utf8_build(void);
diff --git a/window.c b/window.c
index 9d2f3975..24d74fd2 100644
--- a/window.c
+++ b/window.c
@@ -174,7 +174,7 @@ winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
return;
winlink_stack_remove(stack, wl);
- SLIST_INSERT_HEAD(stack, wl, sentry);
+ TAILQ_INSERT_HEAD(stack, wl, sentry);
}
void
@@ -184,10 +184,10 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
if (wl == NULL)
return;
-
- SLIST_FOREACH(wl2, stack, sentry) {
+
+ TAILQ_FOREACH(wl2, stack, sentry) {
if (wl2 == wl) {
- SLIST_REMOVE(stack, wl, winlink, sentry);
+ TAILQ_REMOVE(stack, wl, sentry);
return;
}
}