diff options
-rw-r--r-- | client.c | 9 | ||||
-rw-r--r-- | cmd-bind-key.c | 16 | ||||
-rw-r--r-- | cmd-copy-mode.c | 5 | ||||
-rw-r--r-- | cmd-if-shell.c | 33 | ||||
-rw-r--r-- | cmd-list-keys.c | 86 | ||||
-rw-r--r-- | cmd-move-window.c | 12 | ||||
-rw-r--r-- | cmd-switch-client.c | 19 | ||||
-rw-r--r-- | cmd-unbind-key.c | 37 | ||||
-rw-r--r-- | cmd.c | 8 | ||||
-rw-r--r-- | format.c | 13 | ||||
-rw-r--r-- | input-keys.c | 1 | ||||
-rw-r--r-- | key-bindings.c | 114 | ||||
-rw-r--r-- | layout-custom.c | 4 | ||||
-rw-r--r-- | server-client.c | 156 | ||||
-rw-r--r-- | server-fn.c | 1 | ||||
-rw-r--r-- | server.c | 22 | ||||
-rw-r--r-- | tmux.1 | 108 | ||||
-rw-r--r-- | tmux.h | 41 | ||||
-rw-r--r-- | utf8.c | 4 | ||||
-rw-r--r-- | window-copy.c | 16 | ||||
-rw-r--r-- | window.c | 1 |
21 files changed, 469 insertions, 237 deletions
@@ -261,8 +261,13 @@ client_main(int argc, char **argv, int flags) /* Initialize the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { - fprintf(stderr, "failed to connect to server: %s\n", - strerror(errno)); + if (errno == ECONNREFUSED) { + fprintf(stderr, "no server running on %s\n", + socket_path); + } else { + fprintf(stderr, "error connecting to %s (%s)\n", + socket_path, strerror(errno)); + } return (1); } diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 47c58e51..fda39efc 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", - "cnrt:", 1, -1, - "[-cnr] [-t mode-table] key command [arguments]", + "cnrt:T:", 1, -1, + "[-cnr] [-t mode-table] [-T key-table] key command [arguments]", 0, cmd_bind_key_exec }; @@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) char *cause; struct cmd_list *cmdlist; int key; + const char *tablename; if (args_has(args, 't')) { if (args->argc != 2 && args->argc != 3) { @@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 't')) return (cmd_bind_key_mode_table(self, cmdq, key)); + if (args_has(args, 'T')) + tablename = args_get(args, 'T'); + else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); if (cmdlist == NULL) { @@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_add(key, args_has(args, 'r'), cmdlist); + key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist); return (CMD_RETURN_NORMAL); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 19dca5ff..d729ada6 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -68,8 +68,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); window_copy_init_from_pane(wp); } - if (args_has(args, 'M')) + if (args_has(args, 'M')) { + if (wp->mode != NULL && wp->mode != &window_copy_mode) + return (CMD_RETURN_NORMAL); window_copy_start_drag(c, &cmdq->item->mouse); + } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9659511e..cdd2135c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -44,11 +44,14 @@ const struct cmd_entry cmd_if_shell_entry = { }; struct cmd_if_shell_data { - char *cmd_if; - char *cmd_else; - struct cmd_q *cmdq; - int bflag; - int started; + char *cmd_if; + char *cmd_else; + + struct cmd_q *cmdq; + struct mouse_event mouse; + + int bflag; + int references; }; enum cmd_retval @@ -95,23 +98,26 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_ERROR); } - cmdq_run(cmdq, cmdlist, NULL); + cmdq_run(cmdq, cmdlist, &cmdq->item->mouse); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } cdata = xmalloc(sizeof *cdata); + cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; + cdata->bflag = args_has(args, 'b'); - cdata->started = 0; cdata->cmdq = cmdq; + memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); cmdq->references++; + cdata->references = 1; job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); free(shellcmd); @@ -146,13 +152,12 @@ cmd_if_shell_callback(struct job *job) return; } - cdata->started = 1; - cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; - cmdq_run(cmdq1, cmdlist, NULL); + cdata->references++; + cmdq_run(cmdq1, cmdlist, &cdata->mouse); cmd_list_free(cmdlist); } @@ -164,12 +169,14 @@ cmd_if_shell_done(struct cmd_q *cmdq1) if (cmdq1->client_exit >= 0) cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); + + if (--cdata->references != 0) + return; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); - cmdq_free(cmdq1); - free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); @@ -181,7 +188,7 @@ cmd_if_shell_free(void *data) struct cmd_if_shell_data *cdata = data; struct cmd_q *cmdq = cdata->cmdq; - if (cdata->started) + if (--cdata->references != 0) return; if (!cmdq_free(cmdq) && !cdata->bflag) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 0733ee28..e2d5dc52 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", - "t:", 0, 0, - "[-t key-table]", + "t:T:", 0, 0, + "[-t mode-table] [-T key-table]", 0, cmd_list_keys_exec }; @@ -51,11 +51,12 @@ enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct key_table *table; struct key_binding *bd; - const char *key; - char tmp[BUFSIZ], flags[8]; + const char *key, *tablename, *r; + char tmp[BUFSIZ]; size_t used; - int width, keywidth; + int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -63,46 +64,57 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); - width = 0; + tablename = args_get(args, 'T'); + if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + repeat = 0; + tablewidth = keywidth = 0; + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); + if (key == NULL) + continue; - keywidth = strlen(key); - if (!(bd->key & KEYC_PREFIX)) { if (bd->can_repeat) - keywidth += 4; - else - keywidth += 3; - } else if (bd->can_repeat) - keywidth += 3; - if (keywidth > width) - width = keywidth; + repeat = 1; + + width = strlen(table->name); + if (width > tablewidth) + tablewidth =width; + width = strlen(key); + if (width > keywidth) + keywidth = width; + } } - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; - - *flags = '\0'; - if (!(bd->key & KEYC_PREFIX)) { - if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-rn "); + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); + if (key == NULL) + continue; + + if (!repeat) + r = ""; + else if (bd->can_repeat) + r = "-r "; else - xsnprintf(flags, sizeof flags, "-n "); - } else if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-r "); - - used = xsnprintf(tmp, sizeof tmp, "%s%*s ", - flags, (int) (width - strlen(flags)), key); - if (used >= sizeof tmp) - continue; - - cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); - cmdq_print(cmdq, "bind-key %s", tmp); + r = " "; + used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, + (int)tablewidth, table->name, (int)keywidth, key); + if (used < sizeof tmp) { + cmd_list_print(bd->cmdlist, tmp + used, + (sizeof tmp) - used); + } + + cmdq_print(cmdq, "bind-key %s", tmp); + } } return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index 3064cd6e..e765b625 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -51,7 +51,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) struct session *src, *dst, *s; struct winlink *wl; char *cause; - int idx, kflag, dflag; + int idx, kflag, dflag, sflag; if (args_has(args, 'r')) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); @@ -71,6 +71,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); + sflag = args_has(self->args, 's'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); @@ -79,6 +80,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) } if (self->entry == &cmd_move_window_entry) server_unlink_window(src, wl); + + /* + * Renumber the winlinks in the src session only, the destination + * session already has the correct winlink id to us, either + * automatically or specified by -s. + */ + if (!sflag && options_get_number(&src->options, "renumber-windows")) + session_renumber_windows(src); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 439f593b..8c4daf97 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", - "lc:npt:r", 0, 0, - "[-lnpr] [-c target-client] [-t target-session]", + "lc:npt:rT:", 0, 0, + "[-lnpr] [-c target-client] [-t target-session] [-T key-table]", CMD_READONLY, cmd_switch_client_exec }; @@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; - const char *tflag; + const char *tflag, *tablename; + struct key_table *table; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); @@ -58,6 +59,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->flags |= CLIENT_READONLY; } + tablename = args_get(args, 'T'); + if (tablename != NULL) { + table = key_bindings_get_table(tablename, 0); + if (table == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + table->references++; + key_bindings_unref_table(c->keytable); + c->keytable = table; + } + tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 710210ce..493f6dde 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", - "acnt:", 0, 1, - "[-acn] [-t mode-table] key", + "acnt:T:", 0, 1, + "[-acn] [-t mode-table] [-T key-table] key", 0, cmd_unbind_key_exec }; @@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_entry = { enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct key_binding *bd; - int key; + struct args *args = self->args; + int key; + const char *tablename; if (!args_has(args, 'a')) { if (args->argc != 1) { @@ -66,16 +66,31 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_unbind_key_mode_table(self, cmdq, key)); if (key == KEYC_NONE) { - while (!RB_EMPTY(&key_bindings)) { - bd = RB_ROOT(&key_bindings); - key_bindings_remove(bd->key); + tablename = args_get(args, 'T'); + if (tablename == NULL) { + key_bindings_remove_table("root"); + key_bindings_remove_table("prefix"); + return (CMD_RETURN_NORMAL); } + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_remove(key); + if (args_has(args, 'T')) { + tablename = args_get(args, 'T'); + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + } else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } @@ -780,15 +780,15 @@ cmd_lookup_index(struct session *s, const char *name, int *ambiguous) const char *errstr; u_int idx; + idx = strtonum(name, 0, INT_MAX, &errstr); + if (errstr == NULL) + return (idx); + if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) return (wl->idx); if (*ambiguous) return (-1); - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) - return (idx); - return (-1); } @@ -546,7 +546,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_activity", "%lld", (long long) t); format_add(ft, "client_activity_string", "%s", format_time_string(t)); - format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); + if (strcmp(c->keytable->name, "root") == 0) + format_add(ft, "client_prefix", "%d", 0); + else + format_add(ft, "client_prefix", "%d", 1); + format_add(ft, "client_key_table", "%s", c->keytable->name); if (c->tty.flags & TTY_UTF8) format_add(ft, "client_utf8", "%d", 1); @@ -574,7 +578,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) ft->w = w; - layout = layout_dump(w); + if (w->saved_layout_root != NULL) + layout = layout_dump(w->saved_layout_root); + else + layout = layout_dump(w->layout_root); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); @@ -728,6 +735,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); + format_add(ft, "mouse_any_flag", "%d", + !!(wp->base.mode & (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON))); format_add(ft, "mouse_standard_flag", "%d", !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", diff --git a/input-keys.c b/input-keys.c index ed5556f1..123044fe 100644 --- a/input-keys.c +++ b/input-keys.c @@ -250,5 +250,6 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) buf[len++] = x + 33; buf[len++] = y + 33; } + log_debug("writing mouse %.*s", (int)len, buf); bufferevent_write(wp->event, buf, len); } diff --git a/key-bindings.c b/key-bindings.c index c6cdeeb7..670300a7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -25,60 +25,120 @@ #include "tmux.h" RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); +RB_GENERATE(key_tables, key_table, entry, key_table_cmp); +struct key_tables key_tables = RB_INITIALIZER(&key_tables); -struct key_bindings key_bindings; +int +key_table_cmp(struct key_table *e1, struct key_table *e2) +{ + return (strcmp(e1->name, e2->name)); +} int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - int key1, key2; - - key1 = bd1->key & ~KEYC_PREFIX; - key2 = bd2->key & ~KEYC_PREFIX; - if (key1 != key2) - return (key1 - key2); - - if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) - return (-1); - if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) - return (1); - return (0); + return (bd1->key - bd2->key); } -struct key_binding * -key_bindings_lookup(int key) +struct key_table * +key_bindings_get_table(const char *name, int create) { - struct key_binding bd; + struct key_table table_find, *table; + + table_find.name = name; + table = RB_FIND(key_tables, &key_tables, &table_find); + if (table != NULL || !create) + return (table); - bd.key = key; - return (RB_FIND(key_bindings, &key_bindings, &bd)); + table = xmalloc(sizeof *table); + table->name = xstrdup(name); + RB_INIT(&table->key_bindings); + + table->references = 1; /* one reference in key_tables */ + RB_INSERT(key_tables, &key_tables, table); + + return (table); } void -key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) +key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; - key_bindings_remove(key); + if (--table->references != 0) + return; + + while (!RB_EMPTY(&table->key_bindings)) { + bd = RB_ROOT(&table->key_bindings); + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } + + free((void *)table->name); + free(table); +} + +void +key_bindings_add(const char *name, int key, int can_repeat, + struct cmd_list *cmdlist) +{ + struct key_table *table; + struct key_binding bd_find, *bd; + + table = key_bindings_get_table(name, 1); + + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd != NULL) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } bd = xmalloc(sizeof *bd); bd->key = key; - RB_INSERT(key_bindings, &key_bindings, bd); + RB_INSERT(key_bindings, &table->key_bindings, bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; } void -key_bindings_remove(int key) +key_bindings_remove(const char *name, int key) { - struct key_binding *bd; + struct key_table *table; + struct key_binding bd_find, *bd; + + table = key_bindings_get_table(name, 0); + if (table == NULL) + return; - if ((bd = key_bindings_lookup(key)) == NULL) + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd == NULL) return; - RB_REMOVE(key_bindings, &key_bindings, bd); + + RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); + + if (RB_EMPTY(&table->key_bindings)) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); + } +} + +void +key_bindings_remove_table(const char *name) +{ + struct key_table *table; + + table = key_bindings_get_table(name, 0); + if (table != NULL) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); + } } void @@ -161,7 +221,7 @@ key_bindings_init(void) "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", - "bind -n MouseDrag1Pane copy-mode -M", + "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", }; u_int i; struct cmd_list *cmdlist; @@ -169,8 +229,6 @@ key_bindings_init(void) int error; struct cmd_q *cmdq; - RB_INIT(&key_bindings); - cmdq = cmdq_new(NULL); for (i = 0; i < nitems(defaults); i++) { error = cmd_string_parse(defaults[i], &cmdlist, diff --git a/layout-custom.c b/layout-custom.c index c9cf49c2..57503518 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -55,12 +55,12 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(struct window *w) +layout_dump(struct layout_cell *root) { char layout[BUFSIZ], *out; *layout = '\0'; - if (layout_append(w->layout_root, layout, sizeof layout) != 0) + if (layout_append(root, layout, sizeof layout) != 0) return (NULL); xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); diff --git a/server-client.c b/server-client.c index e0abd90c..72edbdc8 100644 --- a/server-client.c +++ b/server-client.c @@ -29,6 +29,7 @@ #include "tmux.h" +void server_client_key_table(struct client *, const char *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); int server_client_check_mouse(struct client *); @@ -44,6 +45,15 @@ void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); +/* Set client key table. */ +void +server_client_key_table(struct client *c, const char *name) +{ + key_bindings_unref_table(c->keytable); + c->keytable = key_bindings_get_table(name, 1); + c->keytable->references++; +} + /* Create a new client. */ void server_client_create(int fd) @@ -92,6 +102,9 @@ server_client_create(int fd) c->flags |= CLIENT_FOCUSED; + c->keytable = key_bindings_get_table("root", 1); + c->keytable->references++; + evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { @@ -163,6 +176,8 @@ server_client_lost(struct client *c) evtimer_del(&c->repeat_timer); + key_bindings_unref_table(c->keytable); + if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); @@ -375,7 +390,7 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_release = NULL; c->tty.mouse_drag_flag = 0; - return (KEYC_NONE); + return (KEYC_MOUSE); /* not a key, but still may want to pass */ } /* Convert to a key binding. */ @@ -526,16 +541,19 @@ void server_client_handle_key(struct client *c, int key) { struct mouse_event *m = &c->tty.mouse; - struct session *s; + struct session *s = c->session; struct window *w; struct window_pane *wp; struct timeval tv; - struct key_binding *bd; - int xtimeout, isprefix, ispaste; + struct key_table *table = c->keytable; + struct key_binding bd_find, *bd; + int xtimeout; /* Check the client is good to accept input. */ - if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; + w = s->curw->window; + wp = w->active; /* No session, do nothing. */ if (c->session == NULL) @@ -551,7 +569,7 @@ server_client_handle_key(struct client *c, int key) sizeof s->last_activity_time); memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); - /* Special case: number keys jump to pane in identify mode. */ + /* Number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) return; @@ -592,74 +610,88 @@ server_client_handle_key(struct client *c, int key) } else m->valid = 0; - /* Is this a prefix key? */ - if (key == options_get_number(&s->options, "prefix")) - isprefix = 1; - else if (key == options_get_number(&s->options, "prefix2")) - isprefix = 1; - else - isprefix = 0; - - /* Treat prefix as a regular key when pasting is detected. */ - ispaste = server_client_assume_paste(s); - if (ispaste) - isprefix = 0; + /* Treat everything as a regular key when pasting is detected. */ + if (server_client_assume_paste(s)) { + if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c, s, key, m); + return; + } - /* No previous prefix key. */ - if (!(c->flags & CLIENT_PREFIX)) { - if (isprefix) { - c->flags |= CLIENT_PREFIX; +retry: + /* Try to see if there is a key binding in the current table. */ + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd != NULL) { + /* + * Key was matched in this table. If currently repeating but a + * non-repeating binding was found, stop repeating and try + * again in the root table. + */ + if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { + server_client_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; server_status_client(c); - return; + goto retry; } - /* Try as a non-prefix key binding. */ - if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - } else - key_bindings_dispatch(bd, c, m); - return; - } + /* + * Take a reference to this table to make sure the key binding + * doesn't disappear. + */ + table->references++; - /* Prefix key already pressed. Reset prefix and lookup key. */ - c->flags &= ~CLIENT_PREFIX; - server_status_client(c); - if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { - /* If repeating, treat this as a key, else ignore. */ - if (c->flags & CLIENT_REPEAT) { + /* + * If this is a repeating key, start the timer. Otherwise reset + * the client back to the root table. + */ + xtimeout = options_get_number(&s->options, "repeat-time"); + if (xtimeout != 0 && bd->can_repeat) { + c->flags |= CLIENT_REPEAT; + + tv.tv_sec = xtimeout / 1000; + tv.tv_usec = (xtimeout % 1000) * 1000L; + evtimer_del(&c->repeat_timer); + evtimer_add(&c->repeat_timer, &tv); + } else { c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); + server_client_key_table(c, "root"); } + server_status_client(c); + + /* Dispatch the key binding. */ + key_bindings_dispatch(bd, c, m); + key_bindings_unref_table(table); return; } - /* If already repeating, but this key can't repeat, skip it. */ - if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { + /* + * No match in this table. If repeating, switch the client back to the + * root table and try again. + */ + if (c->flags & CLIENT_REPEAT) { + server_client_key_table(c, "root"); c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - return; + server_status_client(c); + goto retry; } - /* If this key can repeat, reset the repeat flags and timer. */ - xtimeout = options_get_number(&s->options, "repeat-time"); - if (xtimeout != 0 && bd->can_repeat) { - c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; - - tv.tv_sec = xtimeout / 1000; - tv.tv_usec = (xtimeout % 1000) * 1000L; - evtimer_del(&c->repeat_timer); - evtimer_add(&c->repeat_timer, &tv); + /* If no match and we're not in the root table, that's it. */ + if (strcmp(c->keytable->name, "root") != 0) { + server_client_key_table(c, "root"); + server_status_client(c); + return; } - /* Dispatch the command. */ - key_bindings_dispatch(bd, c, m); + /* + * No match, but in the root table. Prefix switches to the prefix table + * and everything else is passed through. + */ + if (key == options_get_number(&s->options, "prefix") || + key == options_get_number(&s->options, "prefix2")) { + server_client_key_table(c, "prefix"); + server_status_client(c); + } else if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c, s, key, m); } /* Client functions that need to happen every loop. */ @@ -857,9 +889,9 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) struct client *c = data; if (c->flags & CLIENT_REPEAT) { - if (c->flags & CLIENT_PREFIX) - server_status_client(c); - c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + server_client_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); } } diff --git a/server-fn.c b/server-fn.c index b35eace0..f2b2e269 100644 --- a/server-fn.c +++ b/server-fn.c @@ -351,7 +351,6 @@ server_unlink_window(struct session *s, struct winlink *wl) server_destroy_session_group(s); else server_redraw_session_group(s); - session_renumber_windows(s); } void @@ -78,24 +78,22 @@ server_create_socket(void) size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); if (size >= sizeof sa.sun_path) { errno = ENAMETOOLONG; - fatal("socket failed"); + return (-1); } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) - fatal("bind failed"); + return (-1); umask(mask); if (listen(fd, 16) == -1) - fatal("listen failed"); + return (-1); setblocking(fd, 0); - server_update_socket(); - return (fd); } @@ -156,6 +154,9 @@ server_start(int lockfd, char *lockfile) #endif server_fd = server_create_socket(); + if (server_fd == -1) + fatal("couldn't create socket"); + server_update_socket(); server_client_create(pair[1]); unlink(lockfile); @@ -388,6 +389,7 @@ server_add_accept(int timeout) void server_signal_callback(int sig, unused short events, unused void *data) { + int fd; switch (sig) { case SIGTERM: server_shutdown = 1; @@ -398,8 +400,12 @@ server_signal_callback(int sig, unused short events, unused void *data) break; case SIGUSR1: event_del(&server_ev_accept); - close(server_fd); - server_fd = server_create_socket(); + fd = server_create_socket(); + if (fd != -1) { + close(server_fd); + server_fd = fd; + server_update_socket(); + } server_add_accept(0); break; } @@ -163,7 +163,8 @@ If the socket is accidentally removed, the .Dv SIGUSR1 signal may be sent to the .Nm -server process to recreate it. +server process to recreate it (note that this will fail if any parent +directories are missing). .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells @@ -842,6 +843,7 @@ Suspend a client by sending .Op Fl lnpr .Op Fl c Ar target-client .Op Fl t Ar target-session +.Op Fl T Ar key-table .Xc .D1 (alias: Ic switchc ) Switch the current session for client @@ -859,6 +861,22 @@ respectively. toggles whether a client is read-only (see the .Ic attach-session command). +.Pp +.Fl T +sets the client's key table; the next key from the client will be interpreted from +.Ar key-table . +This may be used to configure multiple prefix keys, or to bind commands to +sequences of keys. +For example, to make typing +.Ql abc +run the +.Ic list-keys +command: +.Bd -literal -offset indent +bind-key -Ttable2 c list-keys +bind-key -Ttable1 b switch-client -Ttable2 +bind-key -Troot a switch-client -Ttable1 +.Ed .El .Sh WINDOWS AND PANES A @@ -1935,6 +1953,7 @@ Commands related to key bindings are as follows: .It Xo Ic bind-key .Op Fl cnr .Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -1942,16 +1961,40 @@ Bind key .Ar key to .Ar command . -By default (without -.Fl t ) -the primary key bindings are modified (those normally activated with the prefix -key); in this case, if -.Fl n -is specified, it is not necessary to use the prefix key, -.Ar command +Keys are bound in a key table. +By default (without -T), the key is bound in +the +.Em prefix +key table. +This table is used for keys pressed after the prefix key (for example, +by default +.Ql c is bound to -.Ar key -alone. +.Ic new-window +in the +.Em prefix +table, so +.Ql C-b c +creates a new window). +The +.Em root +table is used for keys pressed without the prefix key: binding +.Ql c +to +.Ic new-window +in the +.Em root +table (not recommended) means a plain +.Ql c +will create a new window. +.Fl n +is an alias +for +.Fl T Ar root . +Keys may also be bound in custom key tables and the +.Ic switch-client +.Fl T +command used to switch to them from a key binding. The .Fl r flag indicates this key may repeat, see the @@ -1967,21 +2010,33 @@ is bound in the binding for command mode with .Fl c or for normal mode without. +See the +.Sx WINDOWS AND PANES +section and the +.Ic list-keys +command for information on mode key bindings. +.Pp To view the default bindings and possible commands, see the .Ic list-keys command. -.It Ic list-keys Op Fl t Ar key-table +.It Xo Ic list-keys +.Op Fl t Ar mode-table +.Op Fl T Ar key-table +.Xc .D1 (alias: Ic lsk ) List all key bindings. Without -.Fl t -the primary key bindings - those executed when preceded by the prefix key - -are printed. +.Fl T +all key tables are printed. +With +.Fl T +only +.Ar key-table . .Pp With .Fl t , the key bindings in -.Ar key-table +.Ar mode-table are listed; this may be one of: .Em vi-edit , .Em emacs-edit , @@ -2026,31 +2081,22 @@ the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl acn .Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . -Without +.Fl c , +.Fl n , +.Fl T +and .Fl t -the primary key bindings are modified; in this case, if -.Fl n -is specified, the command bound to -.Ar key -without a prefix (if any) is removed. +are the same as for +.Ic bind-key . If .Fl a is present, all key bindings are removed. -.Pp -If -.Fl t -is present, -.Ar key -in -.Ar mode-table -is unbound: the binding for command mode with -.Fl c -or for normal mode without. .El .Sh OPTIONS The appearance and behaviour of @@ -87,10 +87,9 @@ extern char **environ; #define KEYC_ESCAPE 0x2000 #define KEYC_CTRL 0x4000 #define KEYC_SHIFT 0x8000 -#define KEYC_PREFIX 0x10000 /* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ @@ -1299,7 +1298,7 @@ struct client { struct screen status; #define CLIENT_TERMINAL 0x1 -#define CLIENT_PREFIX 0x2 +/* 0x2 unused */ #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 @@ -1318,6 +1317,7 @@ struct client { #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 int flags; + struct key_table *keytable; struct event identify_timer; @@ -1438,15 +1438,24 @@ struct cmd_entry { enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; -/* Key binding. */ +/* Key binding and key table. */ struct key_binding { - int key; - struct cmd_list *cmdlist; - int can_repeat; + int key; + struct cmd_list *cmdlist; + int can_repeat; - RB_ENTRY(key_binding) entry; + RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); +struct key_table { + const char *name; + struct key_bindings key_bindings; + + u_int references; + + RB_ENTRY(key_table) entry; +}; +RB_HEAD(key_tables, key_table); /* * Option table entries. The option table is the user-visible part of the @@ -1874,12 +1883,16 @@ void cmd_wait_for_flush(void); int client_main(int, char **, int); /* key-bindings.c */ -extern struct key_bindings key_bindings; -int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); -struct key_binding *key_bindings_lookup(int); -void key_bindings_add(int, int, struct cmd_list *); -void key_bindings_remove(int); +RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); +extern struct key_tables key_tables; +int key_table_cmp(struct key_table *, struct key_table *); +int key_bindings_cmp(struct key_binding *, struct key_binding *); +struct key_table *key_bindings_get_table(const char *, int); +void key_bindings_unref_table(struct key_table *); +void key_bindings_add(const char *, int, int, struct cmd_list *); +void key_bindings_remove(const char *, int); +void key_bindings_remove_table(const char *); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *, struct mouse_event *); @@ -2210,7 +2223,7 @@ struct layout_cell *layout_split_pane( void layout_close_pane(struct window_pane *); /* layout-custom.c */ -char *layout_dump(struct window *); +char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ @@ -291,9 +291,9 @@ utf8_build(void) while (*ptr != NULL) { node = *ptr; if (item->last < node->first) - ptr = &(node->left); + ptr = &node->left; else if (item->first > node->last) - ptr = &(node->right); + ptr = &node->right; } *ptr = item; } diff --git a/window-copy.c b/window-copy.c index afa609ed..eb00131e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2216,14 +2216,12 @@ window_copy_rectangle_toggle(struct window_pane *wp) void window_copy_start_drag(struct client *c, unused struct mouse_event *m) { - struct window_pane *wp; - struct window_copy_mode_data *data; - u_int x, y; + struct window_pane *wp; + u_int x, y; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; - data = wp->modedata; if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) return; @@ -2244,7 +2242,7 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) u_int x, y, old_cy; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; data = wp->modedata; @@ -2260,13 +2258,11 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) void window_copy_drag_release(unused struct client *c, struct mouse_event *m) { - struct window_pane *wp; - struct window_copy_mode_data *data; + struct window_pane *wp; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; - data = wp->modedata; window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); @@ -518,6 +518,7 @@ window_unzoom(struct window *w) w->flags &= ~WINDOW_ZOOMED; layout_free(w); w->layout_root = w->saved_layout_root; + w->saved_layout_root = NULL; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; |