aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.c9
-rw-r--r--cmd-bind-key.c16
-rw-r--r--cmd-copy-mode.c5
-rw-r--r--cmd-if-shell.c33
-rw-r--r--cmd-list-keys.c86
-rw-r--r--cmd-move-window.c12
-rw-r--r--cmd-switch-client.c19
-rw-r--r--cmd-unbind-key.c37
-rw-r--r--cmd.c8
-rw-r--r--format.c13
-rw-r--r--input-keys.c1
-rw-r--r--key-bindings.c114
-rw-r--r--layout-custom.c4
-rw-r--r--server-client.c156
-rw-r--r--server-fn.c1
-rw-r--r--server.c22
-rw-r--r--tmux.1108
-rw-r--r--tmux.h41
-rw-r--r--utf8.c4
-rw-r--r--window-copy.c16
-rw-r--r--window.c1
21 files changed, 469 insertions, 237 deletions
diff --git a/client.c b/client.c
index c16aface..1929b2a8 100644
--- a/client.c
+++ b/client.c
@@ -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);
}
diff --git a/cmd.c b/cmd.c
index 9e41c1f9..880a1034 100644
--- a/cmd.c
+++ b/cmd.c
@@ -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);
}
diff --git a/format.c b/format.c
index 33c00026..9e9b6c89 100644
--- a/format.c
+++ b/format.c
@@ -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
diff --git a/server.c b/server.c
index 9b11e019..c02703b3 100644
--- a/server.c
+++ b/server.c
@@ -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;
}
diff --git a/tmux.1 b/tmux.1
index ec5dfc3f..d9011c46 100644
--- a/tmux.1
+++ b/tmux.1
@@ -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
diff --git a/tmux.h b/tmux.h
index b0b13c2d..f451a479 100644
--- a/tmux.h
+++ b/tmux.h
@@ -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 */
diff --git a/utf8.c b/utf8.c
index 39e708ba..76b4846a 100644
--- a/utf8.c
+++ b/utf8.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);
diff --git a/window.c b/window.c
index 38c49a76..55934771 100644
--- a/window.c
+++ b/window.c
@@ -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;