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.
pull/1/head
Nicholas Marriott 2009-10-10 10:02:48 +00:00
parent b7d031cc92
commit 9dd72b9583
19 changed files with 392 additions and 59 deletions

View File

@ -78,6 +78,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
session_select(s, wl->idx); session_select(s, wl->idx);
server_redraw_session(s); server_redraw_session(s);
server_status_session_group(s);
return (0); return (0);
} }

View File

@ -54,7 +54,9 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_choose_session_data *cdata; struct cmd_choose_session_data *cdata;
struct winlink *wl; struct winlink *wl;
struct session *s; struct session *s;
struct session_group *sg;
u_int i, idx, cur; u_int i, idx, cur;
char tmp[64];
if (ctx->curclient == NULL) { if (ctx->curclient == NULL) {
ctx->error(ctx, "must be run interactively"); ctx->error(ctx, "must be run interactively");
@ -76,10 +78,18 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
cur = idx; cur = idx;
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, 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, winlink_count(&s->windows), s->sx, s->sy,
s->flags & SESSION_UNATTACHED ? "" : " (attached)"); tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
} }
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);

View File

@ -89,7 +89,7 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
flag = '+'; flag = '+';
else if (wm == s->curw) else if (wm == s->curw)
flag = '*'; flag = '*';
else if (wm == SLIST_FIRST(&s->lastw)) else if (wm == TAILQ_FIRST(&s->lastw))
flag = '-'; flag = '-';
title = w->active->screen->title; title = w->active->screen->title;

View File

@ -53,7 +53,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i); c = ARRAY_ITEM(&clients, i);
if (c->session == s) { if (c != NULL && c->session == s) {
c->session = NULL; c->session = NULL;
server_write_client(c, MSG_EXIT, NULL, 0); server_write_client(c, MSG_EXIT, NULL, 0);
} }

View File

@ -43,19 +43,19 @@ int
cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx) cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_srcdst_data *data = self->data; struct cmd_srcdst_data *data = self->data;
struct session *dst; struct session *src, *dst;
struct winlink *wl; struct winlink *wl;
char *cause; char *cause;
int idx, kflag, dflag; 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); return (-1);
if ((idx = cmd_find_index(ctx, data->dst, &dst)) == -2) if ((idx = cmd_find_index(ctx, data->dst, &dst)) == -2)
return (-1); return (-1);
kflag = data->chflags & CMD_CHFLAG('k'); kflag = data->chflags & CMD_CHFLAG('k');
dflag = data->chflags & CMD_CHFLAG('d'); 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); ctx->error(ctx, "can't link window: %s", cause);
xfree(cause); xfree(cause);
return (-1); return (-1);

View File

@ -42,23 +42,32 @@ const struct cmd_entry cmd_list_sessions_entry = {
int int
cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx) cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx)
{ {
struct session *s; struct session *s;
char *tim; struct session_group *sg;
u_int i; char *tim, tmp[64];
time_t t; u_int i, idx;
time_t t;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
s = ARRAY_ITEM(&sessions, i); s = ARRAY_ITEM(&sessions, i);
if (s == NULL) if (s == NULL)
continue; 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; t = s->tv.tv_sec;
tim = ctime(&t); tim = ctime(&t);
*strchr(tim, '\n') = '\0'; *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->name, winlink_count(&s->windows), tim, s->sx, s->sy,
s->flags & SESSION_UNATTACHED ? "" : " (attached)"); tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
} }
return (0); return (0);

View File

@ -55,7 +55,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx)
kflag = data->chflags & CMD_CHFLAG('k'); kflag = data->chflags & CMD_CHFLAG('k');
dflag = data->chflags & CMD_CHFLAG('d'); 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); ctx->error(ctx, "can't move window: %s", cause);
xfree(cause); xfree(cause);
return (-1); return (-1);

View File

@ -34,6 +34,7 @@ void cmd_new_session_init(struct cmd *, int);
size_t cmd_new_session_print(struct cmd *, char *, size_t); size_t cmd_new_session_print(struct cmd *, char *, size_t);
struct cmd_new_session_data { struct cmd_new_session_data {
char *target;
char *newname; char *newname;
char *winname; char *winname;
char *cmd; char *cmd;
@ -42,7 +43,7 @@ struct cmd_new_session_data {
const struct cmd_entry cmd_new_session_entry = { const struct cmd_entry cmd_new_session_entry = {
"new-session", "new", "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_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, 0,
cmd_new_session_init, cmd_new_session_init,
cmd_new_session_parse, cmd_new_session_parse,
@ -58,6 +59,7 @@ cmd_new_session_init(struct cmd *self, unused int arg)
self->data = data = xmalloc(sizeof *data); self->data = data = xmalloc(sizeof *data);
data->flag_detached = 0; data->flag_detached = 0;
data->target = NULL;
data->newname = NULL; data->newname = NULL;
data->winname = NULL; data->winname = NULL;
data->cmd = 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); self->entry->init(self, KEYC_NONE);
data = self->data; data = self->data;
while ((opt = getopt(argc, argv, "ds:n:")) != -1) { while ((opt = getopt(argc, argv, "ds:t:n:")) != -1) {
switch (opt) { switch (opt) {
case 'd': case 'd':
data->flag_detached = 1; 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) if (data->newname == NULL)
data->newname = xstrdup(optarg); data->newname = xstrdup(optarg);
break; break;
case 't':
if (data->target == NULL)
data->target = xstrdup(optarg);
break;
case 'n': case 'n':
if (data->winname == NULL) if (data->winname == NULL)
data->winname = xstrdup(optarg); 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) if (argc != 0 && argc != 1)
goto usage; goto usage;
if (data->target != NULL && (argc == 1 || data->winname != NULL))
goto usage;
if (argc == 1) if (argc == 1)
data->cmd = xstrdup(argv[0]); data->cmd = xstrdup(argv[0]);
@ -110,7 +119,7 @@ int
cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_new_session_data *data = self->data; struct cmd_new_session_data *data = self->data;
struct session *s; struct session *s, *groupwith;
struct window *w; struct window *w;
struct environ env; struct environ env;
struct termios tio, *tiop; struct termios tio, *tiop;
@ -124,6 +133,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1); return (-1);
} }
groupwith = NULL;
if (data->target != NULL &&
(groupwith = cmd_find_session(ctx, data->target)) == NULL)
return (-1);
/* /*
* There are three cases: * There are three cases:
* *
@ -204,7 +218,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
sy = 1; sy = 1;
/* Figure out the command for the new window. */ /* 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; cmd = data->cmd;
else else
cmd = options_get_string(&global_s_options, "default-command"); 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); environ_free(&env);
/* Set the initial window name if one given. */ /* Set the initial window name if one given. */
if (data->winname != NULL) { if (cmd != NULL && data->winname != NULL) {
w = s->curw->window; w = s->curw->window;
xfree(w->name); xfree(w->name);
@ -236,6 +252,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
options_set_number(&w->options, "automatic-rename", 0); options_set_number(&w->options, "automatic-rename", 0);
} }
/*
* 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 * 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. * taking this session and needs to get MSG_READY and stay around.

View File

@ -164,9 +164,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx)
} }
if (!data->flag_detached) { if (!data->flag_detached) {
session_select(s, wl->idx); session_select(s, wl->idx);
server_redraw_session(s); server_redraw_session_group(s);
} else } else
server_status_session(s); server_status_session_group(s);
return (0); return (0);
} }

View File

@ -53,7 +53,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx)
wl->window->name = xstrdup(data->arg); wl->window->name = xstrdup(data->arg);
options_set_number(&wl->window->options, "automatic-rename", 0); options_set_number(&wl->window->options, "automatic-rename", 0);
server_status_session(s); server_status_window(wl->window);
return (0); return (0);
} }

View File

@ -44,6 +44,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_srcdst_data *data = self->data; struct cmd_srcdst_data *data = self->data;
struct session *src, *dst; struct session *src, *dst;
struct session_group *sg_src, *sg_dst;
struct winlink *wl_src, *wl_dst; struct winlink *wl_src, *wl_dst;
struct window *w; 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) if ((wl_dst = cmd_find_window(ctx, data->dst, &dst)) == NULL)
return (-1); 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) if (wl_dst->window == wl_src->window)
return (0); return (0);
@ -64,9 +73,12 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
if (src != dst) if (src != dst)
session_select(src, wl_src->idx); session_select(src, wl_src->idx);
} }
server_redraw_session(src); session_group_synchronize_from(src);
if (src != dst) server_redraw_session_group(src);
server_redraw_session(dst); if (src != dst) {
session_group_synchronize_from(dst);
server_redraw_session_group(dst);
}
recalculate_sizes(); recalculate_sizes();
return (0); return (0);

View File

@ -42,16 +42,28 @@ cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_target_data *data = self->data; struct cmd_target_data *data = self->data;
struct winlink *wl; 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) if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
return (-1); 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"); ctx->error(ctx, "window is only linked to one session");
return (-1); return (-1);
} }
server_unlink_window(s, wl); server_unlink_window(s, wl);
recalculate_sizes(); recalculate_sizes();

View File

@ -104,6 +104,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 void
server_status_session(struct session *s) server_status_session(struct session *s)
{ {
@ -119,6 +132,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 void
server_redraw_window(struct window *w) server_redraw_window(struct window *w)
{ {
@ -220,18 +246,27 @@ server_kill_window(struct window *w)
continue; continue;
if (session_detach(s, wl)) if (session_detach(s, wl))
server_destroy_session(s); server_destroy_session_group(s);
else else {
server_redraw_session(s); server_redraw_session(s);
server_status_session_group(s);
}
} }
} }
int int
server_link_window( server_link_window(struct session *src, struct winlink *srcwl,
struct winlink *srcwl, struct session *dst, int dstidx, struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
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; dstwl = NULL;
if (dstidx != -1) if (dstidx != -1)
@ -260,12 +295,9 @@ server_link_window(
if (dstwl == NULL) if (dstwl == NULL)
return (-1); return (-1);
if (!selectflag) if (selectflag)
server_status_session(dst);
else {
session_select(dst, dstwl->idx); session_select(dst, dstwl->idx);
server_redraw_session(dst); server_redraw_session_group(dst);
}
return (0); return (0);
} }
@ -274,9 +306,24 @@ void
server_unlink_window(struct session *s, struct winlink *wl) server_unlink_window(struct session *s, struct winlink *wl)
{ {
if (session_detach(s, wl)) if (session_detach(s, wl))
server_destroy_session(s); server_destroy_session_group(s);
else 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 void

View File

@ -247,6 +247,7 @@ server_start(char *path)
ARRAY_INIT(&dead_clients); ARRAY_INIT(&dead_clients);
ARRAY_INIT(&sessions); ARRAY_INIT(&sessions);
ARRAY_INIT(&dead_sessions); ARRAY_INIT(&dead_sessions);
TAILQ_INIT(&session_groups);
mode_key_init_trees(); mode_key_init_trees();
key_bindings_init(); key_bindings_init();
utf8_build(); utf8_build();
@ -1243,10 +1244,11 @@ server_check_window(struct window *w)
if (wl->window != w) if (wl->window != w)
continue; continue;
if (session_detach(s, wl)) { if (session_detach(s, wl)) {
server_destroy_session(s); server_destroy_session_group(s);
break; break;
} }
server_redraw_session(s); server_redraw_session(s);
server_status_session_group(s);
goto restart; goto restart;
} }
} }

189
session.c
View File

@ -30,6 +30,7 @@
/* Global session list. */ /* Global session list. */
struct sessions sessions; struct sessions sessions;
struct sessions dead_sessions; struct sessions dead_sessions;
struct session_groups session_groups;
struct winlink *session_next_activity(struct session *, struct winlink *); struct winlink *session_next_activity(struct session *, struct winlink *);
struct winlink *session_previous_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"); fatal("gettimeofday failed");
s->curw = NULL; s->curw = NULL;
SLIST_INIT(&s->lastw); TAILQ_INIT(&s->lastw);
RB_INIT(&s->windows); RB_INIT(&s->windows);
SLIST_INIT(&s->alerts); SLIST_INIT(&s->alerts);
@ -164,11 +165,14 @@ session_create(const char *name, const char *cmd, const char *cwd,
s->name = xstrdup(name); s->name = xstrdup(name);
else else
xasprintf(&s->name, "%u", i); xasprintf(&s->name, "%u", i);
if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
session_destroy(s); if (cmd != NULL) {
return (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); log_debug("session %s created", s->name);
@ -192,13 +196,14 @@ session_destroy(struct session *s)
if (s->tio != NULL) if (s->tio != NULL)
xfree(s->tio); xfree(s->tio);
session_group_remove(s);
session_alert_cancel(s, NULL); session_alert_cancel(s, NULL);
environ_free(&s->environ); environ_free(&s->environ);
options_free(&s->options); options_free(&s->options);
paste_free_stack(&s->buffers); paste_free_stack(&s->buffers);
while (!SLIST_EMPTY(&s->lastw)) while (!TAILQ_EMPTY(&s->lastw))
winlink_stack_remove(&s->lastw, SLIST_FIRST(&s->lastw)); winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
while (!RB_EMPTY(&s->windows)) while (!RB_EMPTY(&s->windows))
winlink_remove(&s->windows, RB_ROOT(&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) if ((wl = winlink_add(&s->windows, w, idx)) == NULL)
xasprintf(cause, "index in use: %d", idx); xasprintf(cause, "index in use: %d", idx);
session_group_synchronize_from(s);
return (wl); return (wl);
} }
@ -282,6 +288,7 @@ session_detach(struct session *s, struct winlink *wl)
session_alert_cancel(s, wl); session_alert_cancel(s, wl);
winlink_stack_remove(&s->lastw, wl); winlink_stack_remove(&s->lastw, wl);
winlink_remove(&s->windows, wl); winlink_remove(&s->windows, wl);
session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) { if (RB_EMPTY(&s->windows)) {
session_destroy(s); session_destroy(s);
return (1); return (1);
@ -408,7 +415,7 @@ session_last(struct session *s)
{ {
struct winlink *wl; struct winlink *wl;
wl = SLIST_FIRST(&s->lastw); wl = TAILQ_FIRST(&s->lastw);
if (wl == NULL) if (wl == NULL)
return (-1); return (-1);
if (wl == s->curw) if (wl == s->curw)
@ -420,3 +427,169 @@ session_last(struct session *s)
session_alert_cancel(s, wl); session_alert_cancel(s, wl);
return (0); 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);
}
}

View File

@ -516,7 +516,7 @@ status_print(struct session *s, struct winlink *wl, struct grid_cell *gc)
gc->attr = attr; gc->attr = attr;
flag = ' '; flag = ' ';
if (wl == SLIST_FIRST(&s->lastw)) if (wl == TAILQ_FIRST(&s->lastw))
flag = '-'; flag = '-';
if (wl == s->curw) { if (wl == s->curw) {
fg = options_get_number(oo, "window-status-current-fg"); fg = options_get_number(oo, "window-status-current-fg");

21
tmux.1
View File

@ -409,6 +409,7 @@ Lock all clients attached to
.Op Fl d .Op Fl d
.Op Fl n Ar window-name .Op Fl n Ar window-name
.Op Fl s Ar session-name .Op Fl s Ar session-name
.Op Fl t Ar target-session
.Op Ar command .Op Ar command
.Xc .Xc
.D1 (alias: Ic new ) .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 If run from a terminal, any
.Xr termios 4 .Xr termios 4
special characters are saved and used for new windows in the new session. 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 .It Ic refresh-client Op Fl t Ar target-client
.D1 (alias: Ic refresh ) .D1 (alias: Ic refresh )
Refresh the current client if bound to a key, or a single client if one is given Refresh the current client if bound to a key, or a single client if one is given

26
tmux.h
View File

@ -739,10 +739,10 @@ struct winlink {
struct window *window; struct window *window;
RB_ENTRY(winlink) entry; RB_ENTRY(winlink) entry;
SLIST_ENTRY(winlink) sentry; TAILQ_ENTRY(winlink) sentry;
}; };
RB_HEAD(winlinks, winlink); RB_HEAD(winlinks, winlink);
SLIST_HEAD(winlink_stack, winlink); TAILQ_HEAD(winlink_stack, winlink);
/* Layout direction. */ /* Layout direction. */
enum layout_type { enum layout_type {
@ -797,6 +797,13 @@ struct session_alert {
SLIST_ENTRY(session_alert) entry; 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 { struct session {
char *name; char *name;
struct timeval tv; struct timeval tv;
@ -824,6 +831,8 @@ struct session {
struct environ environ; struct environ environ;
int references; int references;
TAILQ_ENTRY(session) gentry;
}; };
ARRAY_DECL(sessions, struct session *); ARRAY_DECL(sessions, struct session *);
@ -1456,7 +1465,9 @@ void server_write_session(
void server_redraw_client(struct client *); void server_redraw_client(struct client *);
void server_status_client(struct client *); void server_status_client(struct client *);
void server_redraw_session(struct session *); void server_redraw_session(struct session *);
void server_redraw_session_group(struct session *);
void server_status_session(struct session *); void server_status_session(struct session *);
void server_status_session_group(struct session *);
void server_redraw_window(struct window *); void server_redraw_window(struct window *);
void server_status_window(struct window *); void server_status_window(struct window *);
void server_lock(void); void server_lock(void);
@ -1464,9 +1475,10 @@ void server_lock_session(struct session *);
void server_lock_client(struct client *); void server_lock_client(struct client *);
int server_unlock(const char *); int server_unlock(const char *);
void server_kill_window(struct window *); void server_kill_window(struct window *);
int server_link_window( int server_link_window(struct session *,
struct winlink *, struct session *, int, int, int, char **); struct winlink *, struct session *, int, int, int, char **);
void server_unlink_window(struct session *, struct winlink *); void server_unlink_window(struct session *, struct winlink *);
void server_destroy_session_group(struct session *);
void server_destroy_session(struct session *); void server_destroy_session(struct session *);
void server_set_identify(struct client *); void server_set_identify(struct client *);
void server_clear_identify(struct client *); void server_clear_identify(struct client *);
@ -1719,6 +1731,7 @@ char *default_window_name(struct window *);
/* session.c */ /* session.c */
extern struct sessions sessions; extern struct sessions sessions;
extern struct sessions dead_sessions; extern struct sessions dead_sessions;
extern struct session_groups session_groups;
void session_alert_add(struct session *, struct window *, int); void session_alert_add(struct session *, struct window *, int);
void session_alert_cancel(struct session *, struct winlink *); void session_alert_cancel(struct session *, struct winlink *);
int session_alert_has(struct session *, struct winlink *, int); 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_previous(struct session *, int);
int session_select(struct session *, int); int session_select(struct session *, int);
int session_last(struct session *); 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 */ /* utf8.c */
void utf8_build(void); void utf8_build(void);

View File

@ -174,7 +174,7 @@ winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
return; return;
winlink_stack_remove(stack, wl); winlink_stack_remove(stack, wl);
SLIST_INSERT_HEAD(stack, wl, sentry); TAILQ_INSERT_HEAD(stack, wl, sentry);
} }
void void
@ -184,10 +184,10 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
if (wl == NULL) if (wl == NULL)
return; return;
SLIST_FOREACH(wl2, stack, sentry) { TAILQ_FOREACH(wl2, stack, sentry) {
if (wl2 == wl) { if (wl2 == wl) {
SLIST_REMOVE(stack, wl, winlink, sentry); TAILQ_REMOVE(stack, wl, sentry);
return; return;
} }
} }