diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 73c82248..00ced9df 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -119,6 +119,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); @@ -150,6 +151,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 1867e814..b13409cb 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -62,7 +62,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index aee69e12..341399be 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -262,6 +262,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } else if (c->session != NULL) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 73a308ae..1461baa9 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,8 +52,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; - const u_char *str; - int i; + int i, literal; + const u_char *keystr; key_code key; if (args_has(args, 'M')) { @@ -82,14 +82,17 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) input_reset(wp); for (i = 0; i < args->argc; i++) { - str = args->argv[i]; - - if (!args_has(args, 'l') && - (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, NULL, s, key, NULL); - } else { - for (; *str != '\0'; str++) - window_pane_key(wp, NULL, s, *str, NULL); + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(args->argv[i]); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + window_pane_key(wp, NULL, s, key, NULL); + else + literal = 1; + } + if (literal) { + for (keystr = args->argv[i]; *keystr != '\0'; keystr++) + window_pane_key(wp, NULL, s, *keystr, NULL); } } diff --git a/cmd-set-option.c b/cmd-set-option.c index 7de91aa2..daee62ac 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -183,6 +183,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) w->active->flags |= PANE_CHANGED; } } + if (strcmp(oe->name, "key-table") == 0) { + TAILQ_FOREACH(c, &clients, entry) + server_client_set_key_table(c, NULL); + } if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); @@ -396,7 +400,8 @@ cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, { key_code key; - if ((key = key_string_lookup_string(value)) == KEYC_NONE) { + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { cmdq_error(cmdq, "bad key: %s", value); return (NULL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index edc0fd69..4746b15a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -65,6 +65,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; + return (CMD_RETURN_NORMAL); } tflag = args_get(args, 't'); @@ -123,6 +124,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index cec538c0..66a4525e 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -51,7 +51,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } @@ -60,13 +60,13 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_NONE; + key = KEYC_UNKNOWN; } if (args_has(args, 't')) return (cmd_unbind_key_mode_table(self, cmdq, key)); - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); @@ -109,7 +109,7 @@ cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) return (CMD_RETURN_ERROR); } - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { while (!RB_EMPTY(mtab->tree)) { mbind = RB_ROOT(mtab->tree); RB_REMOVE(mode_key_tree, mtab->tree, mbind); diff --git a/format.c b/format.c index 90f4c311..a2da1cc2 100644 --- a/format.c +++ b/format.c @@ -1052,6 +1052,7 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; + const char *name; if (ft->s == NULL) ft->s = c->session; @@ -1069,7 +1070,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); - if (strcmp(c->keytable->name, "root") == 0) + name = server_client_get_key_table(c); + if (strcmp(c->keytable->name, name) == 0) format_add(ft, "client_prefix", "%d", 0); else format_add(ft, "client_prefix", "%d", 1); diff --git a/input-keys.c b/input-keys.c index 0a18f37e..1572b7e7 100644 --- a/input-keys.c +++ b/input-keys.c @@ -160,14 +160,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); - if (key != KEYC_NONE && justkey <= 0x7f) { + if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); return; } - if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { + if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) diff --git a/key-string.c b/key-string.c index 9a44892d..1ff3ca30 100644 --- a/key-string.c +++ b/key-string.c @@ -22,8 +22,8 @@ #include "tmux.h" -key_code key_string_search_table(const char *); -key_code key_string_get_modifiers(const char **); +static key_code key_string_search_table(const char *); +static key_code key_string_get_modifiers(const char **); const struct { const char *string; @@ -98,7 +98,7 @@ const struct { }; /* Find key string in table. */ -key_code +static key_code key_string_search_table(const char *string) { u_int i; @@ -107,11 +107,11 @@ key_string_search_table(const char *string) if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } - return (KEYC_NONE); + return (KEYC_UNKNOWN); } /* Find modifiers. */ -key_code +static key_code key_string_get_modifiers(const char **string) { key_code modifiers; @@ -150,10 +150,14 @@ key_string_lookup_string(const char *string) u_int i; enum utf8_state more; + /* Is this no key? */ + if (strcasecmp(string, "None") == 0) + return (KEYC_NONE); + /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) - return (KEYC_NONE); + return (KEYC_UNKNOWN); return (u); } @@ -165,30 +169,30 @@ key_string_lookup_string(const char *string) } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) - return (KEYC_NONE); + return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) - return (KEYC_NONE); + return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); key = utf8_combine(&ud); return (key | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ @@ -202,7 +206,7 @@ key_string_lookup_string(const char *string) else if (key == 63) key = KEYC_BSPACE; else - return (KEYC_NONE); + return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } @@ -222,9 +226,13 @@ key_string_lookup_key(key_code key) /* Handle no key. */ if (key == KEYC_NONE) - return ("<NONE>"); + return ("None"); + + /* Handle special keys. */ + if (key == KEYC_UNKNOWN) + return ("Unknown"); if (key == KEYC_MOUSE) - return ("<MOUSE>"); + return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in @@ -265,7 +273,7 @@ key_string_lookup_key(key_code key) /* Invalid keys are errors. */ if (key == 127 || key > 255) { - snprintf(out, sizeof out, "<INVALID#%llx>", key); + snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } diff --git a/options-table.c b/options-table.c index ab61ef79..2d904124 100644 --- a/options-table.c +++ b/options-table.c @@ -210,6 +210,12 @@ const struct options_table_entry options_table[] = { .default_num = 2000 }, + { .name = "key-table", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "root" + }, + { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, diff --git a/server-client.c b/server-client.c index eb4361f5..aa4d407b 100644 --- a/server-client.c +++ b/server-client.c @@ -30,7 +30,6 @@ #include "tmux.h" -void server_client_key_table(struct client *, const char *); void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); @@ -70,13 +69,32 @@ server_client_check_nested(struct client *c) /* Set client key table. */ void -server_client_key_table(struct client *c, const char *name) +server_client_set_key_table(struct client *c, const char *name) { + if (name == NULL) + name = server_client_get_key_table(c); + key_bindings_unref_table(c->keytable); c->keytable = key_bindings_get_table(name, 1); c->keytable->references++; } +/* Get default key table. */ +const char * +server_client_get_key_table(struct client *c) +{ + struct session *s = c->session; + const char *name; + + if (s == NULL) + return ("root"); + + name = options_get_string(s->options, "key-table"); + if (*name == '\0') + return ("root"); + return (name); +} + /* Create a new client. */ void server_client_create(int fd) @@ -307,7 +325,7 @@ server_client_check_mouse(struct client *c) log_debug("down at %u,%u", x, y); } if (type == NOTYPE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Always save the session. */ m->s = s->id; @@ -317,7 +335,7 @@ server_client_check_mouse(struct client *c) if (m->statusat != -1 && y == (u_int)m->statusat) { w = status_get_window_at(c, x); if (w == NULL) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->w = w->id; where = STATUS; } else @@ -350,7 +368,7 @@ server_client_check_mouse(struct client *c) } } if (where == NOWHERE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->wp = wp->id; m->w = wp->window->id; } else @@ -369,7 +387,7 @@ server_client_check_mouse(struct client *c) } /* Convert to a key binding. */ - key = KEYC_NONE; + key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; @@ -481,8 +499,8 @@ server_client_check_mouse(struct client *c) } break; } - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) @@ -570,7 +588,7 @@ server_client_handle_key(struct client *c, key_code key) if (c->flags & CLIENT_READONLY) return; key = server_client_check_mouse(c); - if (key == KEYC_NONE) + if (key == KEYC_UNKNOWN) return; m->valid = 1; @@ -596,7 +614,7 @@ retry: * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; @@ -623,7 +641,7 @@ retry: evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); } server_status_client(c); @@ -638,15 +656,15 @@ retry: * root table and try again. */ if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; } /* 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"); + if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { + server_client_set_key_table(c, NULL); server_status_client(c); return; } @@ -657,7 +675,7 @@ retry: */ if (key == (key_code)options_get_number(s->options, "prefix") || key == (key_code)options_get_number(s->options, "prefix2")) { - server_client_key_table(c, "prefix"); + server_client_set_key_table(c, "prefix"); server_status_client(c); return; } @@ -842,7 +860,7 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) struct client *c = data; if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } diff --git a/server-fn.c b/server-fn.c index b6619f2e..3afeb090 100644 --- a/server-fn.c +++ b/server-fn.c @@ -385,6 +385,7 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); diff --git a/tmux.1 b/tmux.1 index 7a2d87d4..ac8a7394 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2576,6 +2576,11 @@ is in milliseconds. Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session @@ -2668,8 +2673,19 @@ See the section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 59bf9475..2670a05a 100644 --- a/tmux.h +++ b/tmux.h @@ -87,6 +87,7 @@ struct tmuxproc; /* Special key codes. */ #define KEYC_NONE 0xffff00000000ULL +#define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ @@ -1801,6 +1802,8 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +void server_client_set_key_table(struct client *, const char *); +const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, key_code); void server_client_create(int); diff --git a/tty-keys.c b/tty-keys.c index b5e36f8e..86839a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -344,7 +344,7 @@ tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; - tk->key = KEYC_NONE; + tk->key = KEYC_UNKNOWN; } /* Find the next entry. */ @@ -444,7 +444,7 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) (*size)++; /* At the end of the string, return the current node. */ - if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) + if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) return (tk); /* Move into the next tree for the following character. */ @@ -534,7 +534,7 @@ first_key: if (tk->next != NULL) goto partial_key; key = tk->key; - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) key |= KEYC_ESCAPE; goto complete_key; } @@ -620,7 +620,7 @@ complete_key: } /* Fire the key. */ - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) server_client_handle_key(tty->client, key); return (1);