diff --git a/cmd-send-prefix.c b/cmd-send-prefix.c index aeefc701..a149fdac 100644 --- a/cmd-send-prefix.c +++ b/cmd-send-prefix.c @@ -43,13 +43,13 @@ cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx) struct cmd_target_data *data = self->data; struct session *s; struct window_pane *wp; - int key; + struct keylist *keylist; if (cmd_find_pane(ctx, data->target, &s, &wp) == NULL) return (-1); - key = options_get_number(&s->options, "prefix"); - window_pane_key(wp, ctx->curclient, key); + keylist = options_get_data(&s->options, "prefix"); + window_pane_key(wp, ctx->curclient, ARRAY_FIRST(keylist)); return (0); } diff --git a/cmd-set-option.c b/cmd-set-option.c index 254a5767..3a6bf022 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -65,7 +65,7 @@ const struct set_option_entry set_option_table[] = { { "message-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL }, { "message-bg", SET_OPTION_COLOUR, 0, 0, NULL }, { "message-fg", SET_OPTION_COLOUR, 0, 0, NULL }, - { "prefix", SET_OPTION_KEY, 0, 0, NULL }, + { "prefix", SET_OPTION_KEYS, 0, 0, NULL }, { "repeat-time", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL }, { "set-remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, { "set-titles", SET_OPTION_FLAG, 0, 0, NULL }, @@ -162,8 +162,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) case SET_OPTION_NUMBER: set_option_number(ctx, oo, entry, data->arg2); break; - case SET_OPTION_KEY: - set_option_key(ctx, oo, entry, data->arg2); + case SET_OPTION_KEYS: + set_option_keys(ctx, oo, entry, data->arg2); break; case SET_OPTION_COLOUR: set_option_colour(ctx, oo, entry, data->arg2); diff --git a/cmd-set-window-option.c b/cmd-set-window-option.c index 01e10a35..76d00e19 100644 --- a/cmd-set-window-option.c +++ b/cmd-set-window-option.c @@ -140,8 +140,8 @@ cmd_set_window_option_exec(struct cmd *self, struct cmd_ctx *ctx) case SET_OPTION_NUMBER: set_option_number(ctx, oo, entry, data->arg2); break; - case SET_OPTION_KEY: - set_option_key(ctx, oo, entry, data->arg2); + case SET_OPTION_KEYS: + set_option_keys(ctx, oo, entry, data->arg2); break; case SET_OPTION_COLOUR: set_option_colour(ctx, oo, entry, data->arg2); diff --git a/options-cmd.c b/options-cmd.c index 649a89b2..8c0e0d1a 100644 --- a/options-cmd.c +++ b/options-cmd.c @@ -28,6 +28,8 @@ set_option_print(const struct set_option_entry *entry, struct options_entry *o) { static char out[BUFSIZ]; const char *s; + struct keylist *keylist; + u_int i; *out = '\0'; switch (entry->type) { @@ -37,9 +39,14 @@ set_option_print(const struct set_option_entry *entry, struct options_entry *o) case SET_OPTION_NUMBER: xsnprintf(out, sizeof out, "%lld", o->num); break; - case SET_OPTION_KEY: - s = key_string_lookup_key(o->num); - xsnprintf(out, sizeof out, "%s", s); + case SET_OPTION_KEYS: + keylist = o->data; + for (i = 0; i < ARRAY_LENGTH(keylist); i++) { + strlcat(out, key_string_lookup_key( + ARRAY_ITEM(keylist, i)), sizeof out); + if (i != ARRAY_LENGTH(keylist) - 1) + strlcat(out, ",", sizeof out); + } break; case SET_OPTION_COLOUR: s = colour_tostring(o->num); @@ -114,23 +121,35 @@ set_option_number(struct cmd_ctx *ctx, struct options *oo, } void -set_option_key(struct cmd_ctx *ctx, struct options *oo, +set_option_keys(struct cmd_ctx *ctx, struct options *oo, const struct set_option_entry *entry, char *value) { struct options_entry *o; - int key; + struct keylist *keylist; + char *copyvalue, *ptr, *str; + int key; if (value == NULL) { ctx->error(ctx, "empty value"); return; } - if ((key = key_string_lookup_string(value)) == KEYC_NONE) { - ctx->error(ctx, "unknown key: %s", value); - return; - } + keylist = xmalloc(sizeof *keylist); + ARRAY_INIT(keylist); - o = options_set_number(oo, entry->name, key); + ptr = copyvalue = xstrdup(value); + while ((str = strsep(&ptr, ",")) != NULL) { + if ((key = key_string_lookup_string(str)) == KEYC_NONE) { + xfree(keylist); + ctx->error(ctx, "unknown key: %s", str); + xfree(copyvalue); + return; + } + ARRAY_ADD(keylist, key); + } + xfree(copyvalue); + + o = options_set_data(oo, entry->name, keylist, xfree); ctx->info( ctx, "set option: %s -> %s", o->name, set_option_print(entry, o)); } diff --git a/options.c b/options.c index 6ee4bf1d..f7efdc29 100644 --- a/options.c +++ b/options.c @@ -54,6 +54,8 @@ options_free(struct options *oo) xfree(o->name); if (o->type == OPTIONS_STRING) xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); xfree(o); } } @@ -95,6 +97,8 @@ options_remove(struct options *oo, const char *name) xfree(o->name); if (o->type == OPTIONS_STRING) xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); xfree(o); } @@ -110,6 +114,8 @@ options_set_string(struct options *oo, const char *name, const char *fmt, ...) SPLAY_INSERT(options_tree, &oo->tree, o); } else if (o->type == OPTIONS_STRING) xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); va_start(ap, fmt); o->type = OPTIONS_STRING; @@ -141,6 +147,8 @@ options_set_number(struct options *oo, const char *name, long long value) SPLAY_INSERT(options_tree, &oo->tree, o); } else if (o->type == OPTIONS_STRING) xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); o->type = OPTIONS_NUMBER; o->num = value; @@ -158,3 +166,36 @@ options_get_number(struct options *oo, const char *name) fatalx("option not a number"); return (o->num); } + +struct options_entry * +options_set_data( + struct options *oo, const char *name, void *value, void (*freefn)(void *)) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + + o->type = OPTIONS_DATA; + o->data = value; + o->freefn = freefn; + return (o); +} + +void * +options_get_data(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_DATA) + fatalx("option not data"); + return (o->data); +} diff --git a/server.c b/server.c index 73998042..cb69df0f 100644 --- a/server.c +++ b/server.c @@ -798,8 +798,9 @@ server_handle_client(struct client *c) struct screen *s; struct timeval tv; struct key_binding *bd; - int key, prefix, status, xtimeout; - int mode; + struct keylist *keylist; + int key, status, xtimeout, mode, isprefix; + u_int i; u_char mouse[3]; xtimeout = options_get_number(&c->session->options, "repeat-time"); @@ -811,7 +812,7 @@ server_handle_client(struct client *c) } /* Process keys. */ - prefix = options_get_number(&c->session->options, "prefix"); + keylist = options_get_data(&c->session->options, "prefix"); while (tty_keys_next(&c->tty, &key, mouse) == 0) { server_activity = time(NULL); @@ -844,9 +845,18 @@ server_handle_client(struct client *c) continue; } + /* Is this a prefix key? */ + isprefix = 0; + for (i = 0; i < ARRAY_LENGTH(keylist); i++) { + if (key == ARRAY_ITEM(keylist, i)) { + isprefix = 1; + break; + } + } + /* No previous prefix key. */ if (!(c->flags & CLIENT_PREFIX)) { - if (key == prefix) + if (isprefix) c->flags |= CLIENT_PREFIX; else { /* Try as a non-prefix key binding. */ @@ -864,7 +874,7 @@ server_handle_client(struct client *c) /* If repeating, treat this as a key, else ignore. */ if (c->flags & CLIENT_REPEAT) { c->flags &= ~CLIENT_REPEAT; - if (key == prefix) + if (isprefix) c->flags |= CLIENT_PREFIX; else window_pane_key(wp, c, key); @@ -875,7 +885,7 @@ server_handle_client(struct client *c) /* If already repeating, but this key can't repeat, skip it. */ if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { c->flags &= ~CLIENT_REPEAT; - if (key == prefix) + if (isprefix) c->flags |= CLIENT_PREFIX; else window_pane_key(wp, c, key); diff --git a/tmux.1 b/tmux.1 index 6d833238..802b0fd9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1048,6 +1048,7 @@ characters. All arguments are sent sequentially from first to last. .It Ic send-prefix Op Fl t Ar target-pane Send the prefix key to a window as if it was pressed. +If multiple prefix keys are configured, only the first is sent. .It Xo Ic unbind-key .Op Fl cn .Op Fl t Ar key-table @@ -1258,8 +1259,11 @@ from the 256-colour palette, or .Ic default . .It Ic message-fg Ar colour Set status line message foreground colour. -.It Ic prefix Ar key -Set the current prefix key. +.It Ic prefix Ar keys +Set the keys accepted as a prefix key. +.Ar keys +is a comma-separated list of key names, each of which individually behave as +the prefix key. .It Ic repeat-time Ar time Allow multiple commands to be entered without pressing the prefix-key again in the specified diff --git a/tmux.c b/tmux.c index d75de607..02657e32 100644 --- a/tmux.c +++ b/tmux.c @@ -309,6 +309,7 @@ main(int argc, char **argv) enum msgtype msg; struct passwd *pw; struct options *so, *wo; + struct keylist *keylist; char *s, *path, *label, *home, *cause, **var; char cwd[MAXPATHLEN]; void *buf; @@ -409,7 +410,6 @@ main(int argc, char **argv) options_set_number(so, "message-attr", 0); options_set_number(so, "message-bg", 3); options_set_number(so, "message-fg", 0); - options_set_number(so, "prefix", '\002'); options_set_number(so, "repeat-time", 500); options_set_number(so, "set-remain-on-exit", 0); options_set_number(so, "set-titles", 0); @@ -439,6 +439,11 @@ main(int argc, char **argv) options_set_number(so, "visual-bell", 0); options_set_number(so, "visual-content", 0); + keylist = xmalloc(sizeof *keylist); + ARRAY_INIT(keylist); + ARRAY_ADD(keylist, '\002'); + options_set_data(so, "prefix", keylist, xfree); + options_init(&global_w_options, NULL); wo = &global_w_options; options_set_number(wo, "aggressive-resize", 0); diff --git a/tmux.h b/tmux.h index e8e35ba1..0ffb0b53 100644 --- a/tmux.h +++ b/tmux.h @@ -544,10 +544,14 @@ struct options_entry { enum { OPTIONS_STRING, OPTIONS_NUMBER, + OPTIONS_DATA, } type; char *str; long long num; + void *data; + + void (*freefn)(void *); SPLAY_ENTRY(options_entry) entry; }; @@ -557,6 +561,9 @@ struct options { struct options *parent; }; +/* Key list for prefix option. */ +ARRAY_DECL(keylist, int); + /* Screen selection. */ struct screen_sel { int flag; @@ -1085,7 +1092,7 @@ struct set_option_entry { enum { SET_OPTION_STRING, SET_OPTION_NUMBER, - SET_OPTION_KEY, + SET_OPTION_KEYS, SET_OPTION_COLOUR, SET_OPTION_ATTRIBUTES, SET_OPTION_FLAG, @@ -1165,6 +1172,9 @@ char *options_get_string(struct options *, const char *); struct options_entry *options_set_number( struct options *, const char *, long long); long long options_get_number(struct options *, const char *); +struct options_entry *options_set_data( + struct options *, const char *, void *, void (*)(void *)); +void *options_get_data(struct options *, const char *); /* environ.c */ int environ_cmp(struct environ_entry *, struct environ_entry *); @@ -1245,7 +1255,7 @@ void set_option_string(struct cmd_ctx *, struct options *, const struct set_option_entry *, char *, int); void set_option_number(struct cmd_ctx *, struct options *, const struct set_option_entry *, char *); -void set_option_key(struct cmd_ctx *, +void set_option_keys(struct cmd_ctx *, struct options *, const struct set_option_entry *, char *); void set_option_colour(struct cmd_ctx *, struct options *, const struct set_option_entry *, char *);