From 39cf9c9d31954198ad73e2b6721a92fe782ee56c Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Sat, 12 Dec 2015 18:19:00 +0000
Subject: [PATCH 1/3] Allow prefix and prefix2 to be set to None to disable
 (useful if you would rather bind the prefix in the root table).

---
 cmd-bind-key.c   |  2 +-
 cmd-send-keys.c  | 23 +++++++++++++----------
 cmd-set-option.c |  3 ++-
 cmd-unbind-key.c |  8 ++++----
 input-keys.c     |  4 ++--
 key-string.c     | 40 ++++++++++++++++++++++++----------------
 server-client.c  | 14 +++++++-------
 tmux.1           | 11 +++++++++++
 tmux.h           |  1 +
 tty-keys.c       |  8 ++++----
 10 files changed, 69 insertions(+), 45 deletions(-)

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-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..e2a4768c 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -396,7 +396,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-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/input-keys.c b/input-keys.c
index 2915cb45..3bc1f812 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -161,14 +161,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/server-client.c b/server-client.c
index 8b6be5d9..ed0eefdd 100644
--- a/server-client.c
+++ b/server-client.c
@@ -309,7 +309,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;
@@ -319,7 +319,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
@@ -352,7 +352,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
@@ -371,7 +371,7 @@ server_client_check_mouse(struct client *c)
 	}
 
 	/* Convert to a key binding. */
-	key = KEYC_NONE;
+	key = KEYC_UNKNOWN;
 	switch (type) {
 	case NOTYPE:
 		break;
@@ -483,8 +483,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)
@@ -572,7 +572,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;
diff --git a/tmux.1 b/tmux.1
index 115050a6..b02237f1 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2664,8 +2664,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 60d16fe3..d5e5d822 100644
--- a/tmux.h
+++ b/tmux.h
@@ -85,6 +85,7 @@ struct tmuxproc;
 
 /* Special key codes. */
 #define KEYC_NONE 0xffff00000000ULL
+#define KEYC_UNKNOWN 0xfffe00000000ULL
 #define KEYC_BASE 0x100000000000ULL
 
 /* Key modifier bits. */
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);

From 6a50cf89b40d472cd1f8b439913ca4caddbfd001 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Sat, 12 Dec 2015 18:28:47 +0000
Subject: [PATCH 2/3] Return after changing key table.

---
 cmd-switch-client.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/cmd-switch-client.c b/cmd-switch-client.c
index edc0fd69..85cbce78 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');

From 5ed17e84faed0a7655ec1eb3de291b60839dcb12 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Sat, 12 Dec 2015 18:32:24 +0000
Subject: [PATCH 3/3] Add key-table option to set the default key table for a
 session, allows different key bindings for different sessions and a few other
 things.

---
 cmd-attach-session.c |  2 ++
 cmd-new-session.c    |  1 +
 cmd-set-option.c     |  4 ++++
 cmd-switch-client.c  |  1 +
 format.c             |  4 +++-
 options-table.c      |  6 ++++++
 server-client.c      | 36 +++++++++++++++++++++++++++---------
 server-fn.c          |  1 +
 tmux.1               |  5 +++++
 tmux.h               |  2 ++
 10 files changed, 52 insertions(+), 10 deletions(-)

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-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-set-option.c b/cmd-set-option.c
index e2a4768c..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();
diff --git a/cmd-switch-client.c b/cmd-switch-client.c
index 85cbce78..4746b15a 100644
--- a/cmd-switch-client.c
+++ b/cmd-switch-client.c
@@ -124,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/format.c b/format.c
index 3e666330..79445936 100644
--- a/format.c
+++ b/format.c
@@ -1035,6 +1035,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;
@@ -1052,7 +1053,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/options-table.c b/options-table.c
index 0dab0c0e..63e7ab61 100644
--- a/options-table.c
+++ b/options-table.c
@@ -211,6 +211,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 ed0eefdd..c2b43f38 100644
--- a/server-client.c
+++ b/server-client.c
@@ -32,7 +32,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 *);
@@ -72,13 +71,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)
@@ -598,7 +616,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;
@@ -625,7 +643,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);
 
@@ -640,15 +658,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;
 	}
@@ -659,7 +677,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;
 	}
@@ -834,7 +852,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 07ade08c..a4c2dfea 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -384,6 +384,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 b02237f1..39e9bd4b 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2572,6 +2572,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
diff --git a/tmux.h b/tmux.h
index d5e5d822..e10fbcdf 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1800,6 +1800,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);