Instead of numbering session groups, give them a name which may be given

to -t instead of a target session. Also allow them to contain only one
session.
pull/777/merge
nicm 2017-02-09 15:04:53 +00:00
parent 8de4c15dfa
commit c6a3446398
8 changed files with 135 additions and 85 deletions

View File

@ -68,12 +68,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = self->args;
struct client *c = item->client; struct client *c = item->client;
struct session *s, *as; struct session *s, *as, *groupwith;
struct session *groupwith = item->state.tflag.s;
struct window *w; struct window *w;
struct environ *env; struct environ *env;
struct termios tio, *tiop; struct termios tio, *tiop;
const char *newname, *target, *errstr, *template; struct session_group *sg;
const char *newname, *errstr, *template, *group, *prefix;
const char *path, *cmd, *cwd, *to_free = NULL; const char *path, *cmd, *cwd, *to_free = NULL;
char **argv, *cause, *cp; char **argv, *cause, *cp;
int detached, already_attached, idx, argc; int detached, already_attached, idx, argc;
@ -119,13 +119,29 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} }
} }
if ((target = args_get(args, 't')) != NULL) { /* Is this going to be part of a session group? */
group = args_get(args, 't');
if (group != NULL) {
groupwith = item->state.tflag.s;
if (groupwith == NULL) { if (groupwith == NULL) {
cmdq_error(item, "no such session: %s", target); if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
goto error; goto error;
} }
sg = session_group_find(group);
} else } else
sg = session_group_contains(groupwith);
if (sg != NULL)
prefix = sg->name;
else if (groupwith != NULL)
prefix = groupwith->name;
else
prefix = group;
} else {
groupwith = NULL; groupwith = NULL;
sg = NULL;
prefix = NULL;
}
/* Set -d if no client. */ /* Set -d if no client. */
detached = args_has(args, 'd'); detached = args_has(args, 'd');
@ -213,7 +229,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(args, 't') && args->argc != 0) { if (!args_has(args, 't') && args->argc != 0) {
argc = args->argc; argc = args->argc;
argv = args->argv; argv = args->argv;
} else if (groupwith == NULL) { } else if (sg == NULL && groupwith == NULL) {
cmd = options_get_string(global_s_options, "default-command"); cmd = options_get_string(global_s_options, "default-command");
if (cmd != NULL && *cmd != '\0') { if (cmd != NULL && *cmd != '\0') {
argc = 1; argc = 1;
@ -239,8 +255,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Create the new session. */ /* Create the new session. */
idx = -1 - options_get_number(global_s_options, "base-index"); idx = -1 - options_get_number(global_s_options, "base-index");
s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx, s = session_create(prefix, newname, argc, argv, path, cwd, env, tiop,
sy, &cause); idx, sx, sy, &cause);
environ_free(env); environ_free(env);
if (s == NULL) { if (s == NULL) {
cmdq_error(item, "create session failed: %s", cause); cmdq_error(item, "create session failed: %s", cause);
@ -259,8 +275,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
* If a target session is given, this is to be part of a session group, * If a target session is given, this is to be part of a session group,
* so add it to the group and synchronize. * so add it to the group and synchronize.
*/ */
if (group != NULL) {
if (sg == NULL) {
if (groupwith != NULL) { if (groupwith != NULL) {
session_group_add(groupwith, s); sg = session_group_new(groupwith->name);
session_group_add(sg, groupwith);
} else
sg = session_group_new(group);
}
session_group_add(sg, s);
session_group_synchronize_to(s); session_group_synchronize_to(s);
session_select(s, RB_MIN(winlinks, &s->windows)->idx); session_select(s, RB_MIN(winlinks, &s->windows)->idx);
} }

View File

@ -52,11 +52,11 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
wl_src = item->state.sflag.wl; wl_src = item->state.sflag.wl;
src = item->state.sflag.s; src = item->state.sflag.s;
sg_src = session_group_find(src); sg_src = session_group_contains(src);
wl_dst = item->state.tflag.wl; wl_dst = item->state.tflag.wl;
dst = item->state.tflag.s; dst = item->state.tflag.s;
sg_dst = session_group_find(dst); sg_dst = session_group_contains(dst);
if (src != dst && sg_src != NULL && sg_dst != NULL && if (src != dst && sg_src != NULL && sg_dst != NULL &&
sg_src == sg_dst) { sg_src == sg_dst) {

View File

@ -1097,10 +1097,10 @@ format_defaults_session(struct format_tree *ft, struct session *s)
format_add(ft, "session_height", "%u", s->sy); format_add(ft, "session_height", "%u", s->sy);
format_add(ft, "session_id", "$%u", s->id); format_add(ft, "session_id", "$%u", s->id);
sg = session_group_find(s); sg = session_group_contains(s);
format_add(ft, "session_grouped", "%d", sg != NULL); format_add(ft, "session_grouped", "%d", sg != NULL);
if (sg != NULL) if (sg != NULL)
format_add(ft, "session_group", "%u", session_group_index(sg)); format_add(ft, "session_group", "%s", sg->name);
format_add_tv(ft, "session_created", &s->creation_time); format_add_tv(ft, "session_created", &s->creation_time);
format_add_tv(ft, "session_last_attached", &s->last_attached_time); format_add_tv(ft, "session_last_attached", &s->last_attached_time);

View File

@ -78,7 +78,7 @@ server_redraw_session_group(struct session *s)
{ {
struct session_group *sg; struct session_group *sg;
if ((sg = session_group_find(s)) == NULL) if ((sg = session_group_contains(s)) == NULL)
server_redraw_session(s); server_redraw_session(s);
else { else {
TAILQ_FOREACH(s, &sg->sessions, gentry) TAILQ_FOREACH(s, &sg->sessions, gentry)
@ -102,7 +102,7 @@ server_status_session_group(struct session *s)
{ {
struct session_group *sg; struct session_group *sg;
if ((sg = session_group_find(s)) == NULL) if ((sg = session_group_contains(s)) == NULL)
server_status_session(s); server_status_session(s);
else { else {
TAILQ_FOREACH(s, &sg->sessions, gentry) TAILQ_FOREACH(s, &sg->sessions, gentry)
@ -220,7 +220,7 @@ server_kill_window(struct window *w)
} }
if (options_get_number(s->options, "renumber-windows")) { if (options_get_number(s->options, "renumber-windows")) {
if ((sg = session_group_find(s)) != NULL) { if ((sg = session_group_contains(s)) != NULL) {
TAILQ_FOREACH(target_s, &sg->sessions, gentry) TAILQ_FOREACH(target_s, &sg->sessions, gentry)
session_renumber_windows(target_s); session_renumber_windows(target_s);
} else } else
@ -238,8 +238,8 @@ server_link_window(struct session *src, struct winlink *srcwl,
struct winlink *dstwl; struct winlink *dstwl;
struct session_group *srcsg, *dstsg; struct session_group *srcsg, *dstsg;
srcsg = session_group_find(src); srcsg = session_group_contains(src);
dstsg = session_group_find(dst); dstsg = session_group_contains(dst);
if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
xasprintf(cause, "sessions are grouped"); xasprintf(cause, "sessions are grouped");
return (-1); return (-1);
@ -348,7 +348,7 @@ server_destroy_session_group(struct session *s)
struct session_group *sg; struct session_group *sg;
struct session *s1; struct session *s1;
if ((sg = session_group_find(s)) == NULL) if ((sg = session_group_contains(s)) == NULL)
server_destroy_session(s); server_destroy_session(s);
else { else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {

View File

@ -156,8 +156,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
RB_INIT(&all_window_panes); RB_INIT(&all_window_panes);
TAILQ_INIT(&clients); TAILQ_INIT(&clients);
RB_INIT(&sessions); RB_INIT(&sessions);
TAILQ_INIT(&session_groups); RB_INIT(&session_groups);
mode_key_init_trees();
key_bindings_init(); key_bindings_init();
gettimeofday(&start_time, NULL); gettimeofday(&start_time, NULL);

View File

@ -42,6 +42,9 @@ static void session_group_remove(struct session *);
static u_int session_group_count(struct session_group *); static u_int session_group_count(struct session_group *);
static void session_group_synchronize1(struct session *, struct session *); static void session_group_synchronize1(struct session *, struct session *);
static u_int session_group_count(struct session_group *);
static void session_group_synchronize1(struct session *, struct session *);
RB_GENERATE(sessions, session, entry, session_cmp); RB_GENERATE(sessions, session, entry, session_cmp);
int int
@ -50,6 +53,14 @@ session_cmp(struct session *s1, struct session *s2)
return (strcmp(s1->name, s2->name)); return (strcmp(s1->name, s2->name));
} }
RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
int
session_group_cmp(struct session_group *s1, struct session_group *s2)
{
return (strcmp(s1->name, s2->name));
}
/* /*
* Find if session is still alive. This is true if it is still on the global * Find if session is still alive. This is true if it is still on the global
* sessions list. * sessions list.
@ -107,9 +118,9 @@ session_find_by_id(u_int id)
/* Create a new session. */ /* Create a new session. */
struct session * struct session *
session_create(const char *name, int argc, char **argv, const char *path, session_create(const char *prefix, const char *name, int argc, char **argv,
const char *cwd, struct environ *env, struct termios *tio, int idx, const char *path, const char *cwd, struct environ *env, struct termios *tio,
u_int sx, u_int sy, char **cause) int idx, u_int sx, u_int sy, char **cause)
{ {
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
@ -150,6 +161,9 @@ session_create(const char *name, int argc, char **argv, const char *path,
do { do {
s->id = next_session_id++; s->id = next_session_id++;
free(s->name); free(s->name);
if (prefix != NULL)
xasprintf(&s->name, "%s-%u", prefix, s->id);
else
xasprintf(&s->name, "%u", s->id); xasprintf(&s->name, "%u", s->id);
} while (RB_FIND(sessions, &sessions, s) != NULL); } while (RB_FIND(sessions, &sessions, s) != NULL);
} }
@ -429,7 +443,7 @@ session_is_linked(struct session *s, struct window *w)
{ {
struct session_group *sg; struct session_group *sg;
if ((sg = session_group_find(s)) != NULL) if ((sg = session_group_contains(s)) != NULL)
return (w->references != session_group_count(sg)); return (w->references != session_group_count(sg));
return (w->references != 1); return (w->references != 1);
} }
@ -540,12 +554,12 @@ session_set_current(struct session *s, struct winlink *wl)
/* Find the session group containing a session. */ /* Find the session group containing a session. */
struct session_group * struct session_group *
session_group_find(struct session *target) session_group_contains(struct session *target)
{ {
struct session_group *sg; struct session_group *sg;
struct session *s; struct session *s;
TAILQ_FOREACH(sg, &session_groups, entry) { RB_FOREACH(sg, session_groups, &session_groups) {
TAILQ_FOREACH(s, &sg->sessions, gentry) { TAILQ_FOREACH(s, &sg->sessions, gentry) {
if (s == target) if (s == target)
return (sg); return (sg);
@ -554,38 +568,38 @@ session_group_find(struct session *target)
return (NULL); return (NULL);
} }
/* Find session group index. */ /* Find session group by name. */
u_int struct session_group *
session_group_index(struct session_group *sg) session_group_find(const char *name)
{ {
struct session_group *sg2; struct session_group sg;
u_int i;
i = 0; sg.name = name;
TAILQ_FOREACH(sg2, &session_groups, entry) { return (RB_FIND(session_groups, &session_groups, &sg));
if (sg == sg2)
return (i);
i++;
} }
fatalx("session group not found"); /* Create a new session group. */
} struct session_group *
session_group_new(const char *name)
/*
* 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; struct session_group *sg;
if ((sg = session_group_find(target)) == NULL) { if ((sg = session_group_find(name)) != NULL)
sg = xmalloc(sizeof *sg); return (sg);
TAILQ_INSERT_TAIL(&session_groups, sg, entry);
sg = xcalloc(1, sizeof *sg);
sg->name = xstrdup(name);
TAILQ_INIT(&sg->sessions); TAILQ_INIT(&sg->sessions);
TAILQ_INSERT_TAIL(&sg->sessions, target, gentry);
RB_INSERT(session_groups, &session_groups, sg);
return (sg);
} }
/* Add a session to a session group. */
void
session_group_add(struct session_group *sg, struct session *s)
{
if (session_group_contains(s) == NULL)
TAILQ_INSERT_TAIL(&sg->sessions, s, gentry); TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
} }
@ -595,13 +609,11 @@ session_group_remove(struct session *s)
{ {
struct session_group *sg; struct session_group *sg;
if ((sg = session_group_find(s)) == NULL) if ((sg = session_group_contains(s)) == NULL)
return; return;
TAILQ_REMOVE(&sg->sessions, s, gentry); 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)) { if (TAILQ_EMPTY(&sg->sessions)) {
TAILQ_REMOVE(&session_groups, sg, entry); RB_REMOVE(session_groups, &session_groups, sg);
free(sg); free(sg);
} }
} }
@ -626,7 +638,7 @@ session_group_synchronize_to(struct session *s)
struct session_group *sg; struct session_group *sg;
struct session *target; struct session *target;
if ((sg = session_group_find(s)) == NULL) if ((sg = session_group_contains(s)) == NULL)
return; return;
target = NULL; target = NULL;
@ -634,6 +646,7 @@ session_group_synchronize_to(struct session *s)
if (target != s) if (target != s)
break; break;
} }
if (target != NULL)
session_group_synchronize1(target, s); session_group_synchronize1(target, s);
} }
@ -644,7 +657,7 @@ session_group_synchronize_from(struct session *target)
struct session_group *sg; struct session_group *sg;
struct session *s; struct session *s;
if ((sg = session_group_find(target)) == NULL) if ((sg = session_group_contains(target)) == NULL)
return; return;
TAILQ_FOREACH(s, &sg->sessions, gentry) { TAILQ_FOREACH(s, &sg->sessions, gentry) {

33
tmux.1
View File

@ -812,7 +812,7 @@ Lock all clients attached to
.Op Fl F Ar format .Op Fl F Ar format
.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 Fl t Ar group-name
.Op Fl x Ar width .Op Fl x Ar width
.Op Fl y Ar height .Op Fl y Ar height
.Op Ar shell-command .Op Ar shell-command
@ -857,16 +857,27 @@ to
.Pp .Pp
If If
.Fl t .Fl t
is given, the new session is is given, it specifies a
.Em grouped .Ic session group .
with Sessions in the same group share the same set of windows - new windows are
.Ar target-session . linked to all sessions in the grouo and any windows closed removed from all
This means they share the same set of windows - all windows from sessions.
.Ar target-session
are linked to the new session, any new windows are linked to both sessions and
any windows closed removed from both sessions.
The current and previous window and any session options remain independent and The current and previous window and any session options remain independent and
either session may be killed without affecting the other. any session in a group may be killed without affecting the others.
The
.Ar group-name
argument may be:
.Bl -enum -width Ds
.It
the name of an existing group, in which case the new session is added to that
group;
.It
the name of an existing session - the new session is added to the same group
as that session, creating a new group if necessary;
.It
the name for a new group containing only the new session.
.El
.Pp
.Fl n .Fl n
and and
.Ar shell-command .Ar shell-command
@ -3562,7 +3573,7 @@ The following variables are available, where appropriate:
.It Li "session_activity" Ta "" Ta "Integer time of session last activity" .It Li "session_activity" Ta "" Ta "Integer time of session last activity"
.It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created" Ta "" Ta "Integer time session created"
.It Li "session_last_attached" Ta "" Ta "Integer time session last attached" .It Li "session_last_attached" Ta "" Ta "Integer time session last attached"
.It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_group" Ta "" Ta "Name of session group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_height" Ta "" Ta "Height of session" .It Li "session_height" Ta "" Ta "Height of session"
.It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_id" Ta "" Ta "Unique session ID"

20
tmux.h
View File

@ -901,11 +901,12 @@ struct environ_entry {
/* Client session. */ /* Client session. */
struct session_group { struct session_group {
const char *name;
TAILQ_HEAD(, session) sessions; TAILQ_HEAD(, session) sessions;
TAILQ_ENTRY(session_group) entry; RB_ENTRY(session_group) entry;
}; };
TAILQ_HEAD(session_groups, session_group); RB_HEAD(session_groups, session_group);
struct session { struct session {
u_int id; u_int id;
@ -2210,13 +2211,15 @@ extern struct sessions sessions;
extern struct session_groups session_groups; extern struct session_groups session_groups;
int session_cmp(struct session *, struct session *); int session_cmp(struct session *, struct session *);
RB_PROTOTYPE(sessions, session, entry, session_cmp); RB_PROTOTYPE(sessions, session, entry, session_cmp);
int session_group_cmp(struct session_group *, struct session_group *);
RB_PROTOTYPE(session_groups, session_group, entry, session_group_cmp);
int session_alive(struct session *); int session_alive(struct session *);
struct session *session_find(const char *); struct session *session_find(const char *);
struct session *session_find_by_id_str(const char *); struct session *session_find_by_id_str(const char *);
struct session *session_find_by_id(u_int); struct session *session_find_by_id(u_int);
struct session *session_create(const char *, int, char **, const char *, struct session *session_create(const char *, const char *, int, char **,
const char *, struct environ *, struct termios *, int, const char *, const char *, struct environ *,
u_int, u_int, char **); struct termios *, int, u_int, u_int, char **);
void session_destroy(struct session *); void session_destroy(struct session *);
void session_unref(struct session *); void session_unref(struct session *);
int session_check_name(const char *); int session_check_name(const char *);
@ -2235,9 +2238,10 @@ 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 *);
int session_set_current(struct session *, struct winlink *); int session_set_current(struct session *, struct winlink *);
struct session_group *session_group_find(struct session *); struct session_group *session_group_contains(struct session *);
u_int session_group_index(struct session_group *); struct session_group *session_group_find(const char *);
void session_group_add(struct session *, struct session *); struct session_group *session_group_new(const char *);
void session_group_add(struct session_group *, struct session *);
void session_group_synchronize_to(struct session *); void session_group_synchronize_to(struct session *);
void session_group_synchronize_from(struct session *); void session_group_synchronize_from(struct session *);
void session_renumber_windows(struct session *); void session_renumber_windows(struct session *);