The branch, master has been updated
       via  0a88377086329786c438d4973365fdb21186f4e4 (commit)
       via  3909aff06aa6de748ae057cab1e723eec2387edd (commit)
       via  69f292a90e2fb9d48a77a9753127da13554715de (commit)
       via  93b2871cab399388a16ace64d8dfb9a8e2143543 (commit)
       via  7a72eff4a4717e975ab7ef39baff5667b12e8a13 (commit)
       via  d16b640fe8583878d23436bd7515a8adade9faeb (commit)
       via  0e7219d437ce565a518b336a5db112eaeaeddc5c (commit)
       via  d1337053b6119c4aab0c38b5fc082acf2fdfcbb1 (commit)
       via  4cf4302962558be4d5e7a87c57f084d1187fa330 (commit)
       via  1f404f6a23969516007c77eeec89876154c9a4a4 (commit)
       via  bc3786ece940dc9c68f731734b45c129e0542ac7 (commit)
       via  0610f443808cc284171a4abc14bd3f95a9f3a57b (commit)
       via  07d93db427751512e6ae96a54e69846189da2dc2 (commit)
       via  bded7437064c76dd6cf4e76e558d826859adcc79 (commit)
       via  3497843f0272e573d0a63cb6e94948591ae07667 (commit)
      from  b25dc423b0b1369aaec5dee8e051d541bd55043f (commit)

- Log -----------------------------------------------------------------
commit 0a88377086329786c438d4973365fdb21186f4e4
Merge: b25dc42 3909aff
Author: Thomas Adam <tho...@xteddy.org>
Commit: Thomas Adam <tho...@xteddy.org>

    Merge branch 'obsd-master'

 client.c            |    9 ++-
 cmd-bind-key.c      |   16 ++++--
 cmd-copy-mode.c     |    5 +-
 cmd-if-shell.c      |   33 +++++++----
 cmd-list-keys.c     |   86 ++++++++++++++++------------
 cmd-move-window.c   |   12 ++++-
 cmd-switch-client.c |   19 +++++-
 cmd-unbind-key.c    |   37 +++++++++----
 cmd.c               |    8 +-
 format.c            |   13 ++++-
 input-keys.c        |    1 +
 key-bindings.c      |  114 ++++++++++++++++++++++++++++---------
 layout-custom.c     |    4 +-
 server-client.c     |  156 +++++++++++++++++++++++++++++++--------------------
 server-fn.c         |    1 -
 server.c            |   22 +++++---
 tmux.1              |  108 +++++++++++++++++++++++++----------
 tmux.h              |   41 +++++++++-----
 utf8.c              |    4 +-
 window-copy.c       |   16 ++---
 window.c            |    1 +
 21 files changed, 469 insertions(+), 237 deletions(-)

diff --cc server.c
index 9b11e01,c810177..c02703b
--- a/server.c
+++ b/server.c
@@@ -151,11 -150,12 +149,14 @@@ server_start(int lockfd, char *lockfile
  
        start_time = time(NULL);
        log_debug("socket path %s", socket_path);
 +#ifdef HAVE_SETPROCTITLE
        setproctitle("server (%s)", socket_path);
 +#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);


commit 3909aff06aa6de748ae057cab1e723eec2387edd
Author: nicm <nicm>
Commit: nicm <nicm>

    Look up indexes as number before name, makes more sense if windows are
    named starting with numbers. From Thomas Adam.
---
 cmd.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/cmd.c b/cmd.c
index dd1a0a7..991e079 100644
--- a/cmd.c
+++ b/cmd.c
@@ -781,15 +781,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);
 }
 


commit 69f292a90e2fb9d48a77a9753127da13554715de
Author: nicm <nicm>
Commit: nicm <nicm>

    Always format real layout even when zoomed.
---
 format.c        |    5 ++++-
 layout-custom.c |    4 ++--
 tmux.h          |    2 +-
 window.c        |    1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/format.c b/format.c
index 2be9834..7ded05d 100644
--- a/format.c
+++ b/format.c
@@ -577,7 +577,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);
diff --git a/layout-custom.c b/layout-custom.c
index c9cf49c..5750351 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/tmux.h b/tmux.h
index 1af4d07..53fd9b2 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2225,7 +2225,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/window.c b/window.c
index 86cc8bb..3c8d495 100644
--- a/window.c
+++ b/window.c
@@ -519,6 +519,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;


commit 93b2871cab399388a16ace64d8dfb9a8e2143543
Author: nicm <nicm>
Commit: nicm <nicm>

    Do not die on USR1 if any of the socket parent directories are
    missing. Reported by Robin Powell.
---
 server.c |   22 ++++++++++++++--------
 tmux.1   |    6 ++++--
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/server.c b/server.c
index 26bfddf..c810177 100644
--- a/server.c
+++ b/server.c
@@ -79,24 +79,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);
 }
 
@@ -155,6 +153,9 @@ server_start(int lockfd, char *lockfile)
        setproctitle("server (%s)", socket_path);
 
        server_fd = server_create_socket();
+       if (server_fd == -1)
+               fatal("couldn't create socket");
+       server_update_socket();
        server_client_create(pair[1]);
 
        unlink(lockfile);
@@ -387,6 +388,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;
@@ -397,8 +399,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 ab76238..68e9b9d 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
@@ -2004,7 +2005,8 @@ is bound in
 .Ar mode-table :
 the binding for command mode with
 .Fl c
-or for normal mode without. See the
+or for normal mode without.
+See the
 .Sx WINDOWS AND PANES
 section and the
 .Ic list-keys


commit 7a72eff4a4717e975ab7ef39baff5667b12e8a13
Author: nicm <nicm>
Commit: nicm <nicm>

    Simplify error messages when socket connect fails, suggested by "Karthik K".
---
 client.c |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/client.c b/client.c
index 6d4b8a5..d3ff05a 100644
--- a/client.c
+++ b/client.c
@@ -265,8 +265,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);
        }
 


commit d16b640fe8583878d23436bd7515a8adade9faeb
Author: nicm <nicm>
Commit: nicm <nicm>

    The free callback could end up being fired before the done callback
    (happens on Cygwin), so use a reference count instead of a single
    flag. SF bug 188 reported by "iceboy".
---
 cmd-if-shell.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index 9c57761..cdd2135 100644
--- a/cmd-if-shell.c
+++ b/cmd-if-shell.c
@@ -51,7 +51,7 @@ struct cmd_if_shell_data {
        struct mouse_event       mouse;
 
        int                      bflag;
-       int                      started;
+       int                      references;
 };
 
 enum cmd_retval
@@ -113,11 +113,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
 
        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);
 
@@ -152,12 +152,11 @@ 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;
 
+       cdata->references++;
        cmdq_run(cmdq1, cmdlist, &cdata->mouse);
        cmd_list_free(cmdlist);
 }
@@ -170,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);
@@ -187,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)


commit 0e7219d437ce565a518b336a5db112eaeaeddc5c
Author: nicm <nicm>
Commit: nicm <nicm>

    Fix moving windows to nonexistent indexes when renumber-windows is
    off. From Thomas Adam, reported by Daniel Levai and Theo Buehler.
---
 cmd-move-window.c |   12 +++++++++++-
 server-fn.c       |    1 -
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/cmd-move-window.c b/cmd-move-window.c
index 3064cd6..e765b62 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/server-fn.c b/server-fn.c
index a065dd7..83ea947 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


commit d1337053b6119c4aab0c38b5fc082acf2fdfcbb1
Author: nicm <nicm>
Commit: nicm <nicm>

    Bind mouse dragging so that it is passed through to applications if they
    want it rather than entering copy mode.
---
 cmd-copy-mode.c |    5 ++++-
 key-bindings.c  |    2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c
index 19dca5f..d729ada 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/key-bindings.c b/key-bindings.c
index c34db71..670300a 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -221,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;


commit 4cf4302962558be4d5e7a87c57f084d1187fa330
Author: nicm <nicm>
Commit: nicm <nicm>

    Don't eat the mouse event that triggers a drag end because we may want
    to pass it on to application inside the pane.
---
 input-keys.c    |    1 +
 server-client.c |    2 +-
 2 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/input-keys.c b/input-keys.c
index ae00e4a..d812a90 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -251,5 +251,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/server-client.c b/server-client.c
index b4d1f74..c022a36 100644
--- a/server-client.c
+++ b/server-client.c
@@ -391,7 +391,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. */


commit 1f404f6a23969516007c77eeec89876154c9a4a4
Author: nicm <nicm>
Commit: nicm <nicm>

    Put mouse_any_flag back, don't know where it went to (still in man page).
---
 format.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/format.c b/format.c
index eb4664d..2be9834 100644
--- a/format.c
+++ b/format.c
@@ -729,6 +729,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",


commit bc3786ece940dc9c68f731734b45c129e0542ac7
Author: nicm <nicm>
Commit: nicm <nicm>

    Pass mouse events through to commands for if-shell.
---
 cmd-if-shell.c |   20 +++++++++++++-------
 1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index 9659511..9c57761 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                      started;
 };
 
 enum cmd_retval
@@ -95,21 +98,24 @@ 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++;
 
        job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata);
@@ -152,7 +158,7 @@ cmd_if_shell_callback(struct job *job)
        cmdq1->emptyfn = cmd_if_shell_done;
        cmdq1->data = cdata;
 
-       cmdq_run(cmdq1, cmdlist, NULL);
+       cmdq_run(cmdq1, cmdlist, &cdata->mouse);
        cmd_list_free(cmdlist);
 }
 


commit 0610f443808cc284171a4abc14bd3f95a9f3a57b
Author: nicm <nicm>
Commit: nicm <nicm>

    cmd_mouse_pane can return NULL, check for that.
---
 window-copy.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/window-copy.c b/window-copy.c
index 4dc0735..eb00131 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -2220,7 +2220,7 @@ window_copy_start_drag(struct client *c, unused struct 
mouse_event *m)
        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;
 
        if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
@@ -2242,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;
 
@@ -2261,7 +2261,7 @@ window_copy_drag_release(unused struct client *c, struct 
mouse_event *m)
        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;
 
        window_copy_copy_selection(wp, NULL);


commit 07d93db427751512e6ae96a54e69846189da2dc2
Author: nicm <nicm>
Commit: nicm <nicm>

    Remove unused-but-set variables, from Thomas Adam.
---
 window-copy.c |   10 +++-------
 1 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/window-copy.c b/window-copy.c
index afa609e..4dc0735 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)
                return;
-       data = wp->modedata;
 
        if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
                return;
@@ -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)
                return;
-       data = wp->modedata;
 
        window_copy_copy_selection(wp, NULL);
        window_pane_reset_mode(wp);


commit bded7437064c76dd6cf4e76e558d826859adcc79
Author: nicm <nicm>
Commit: nicm <nicm>

    Support for multiple key tables to commands to be bound to sequences of
    keys. The default key bindings become the "prefix" table and -n the
    "root" table. Keys may be bound in new tables with bind -T and
    switch-client -T used to specify the table in which the next key should
    be looked up. Based on a diff from Keith Amling.
---
 cmd-bind-key.c      |   16 ++++--
 cmd-list-keys.c     |   86 ++++++++++++++++------------
 cmd-switch-client.c |   19 +++++-
 cmd-unbind-key.c    |   37 +++++++++----
 format.c            |    6 ++-
 key-bindings.c      |  112 ++++++++++++++++++++++++++++---------
 server-client.c     |  156 +++++++++++++++++++++++++++++++--------------------
 tmux.1              |  106 ++++++++++++++++++++++++----------
 tmux.h              |   39 +++++++++----
 9 files changed, 387 insertions(+), 190 deletions(-)

diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 47c58e5..fda39ef 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-list-keys.c b/cmd-list-keys.c
index 0733ee2..e2d5dc5 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-switch-client.c b/cmd-switch-client.c
index 439f593..8c4daf9 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 710210c..493f6dd 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/format.c b/format.c
index cf1713b..eb4664d 100644
--- a/format.c
+++ b/format.c
@@ -545,7 +545,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);
diff --git a/key-bindings.c b/key-bindings.c
index c6cdeeb..c34db71 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
@@ -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/server-client.c b/server-client.c
index 3968c50..b4d1f74 100644
--- a/server-client.c
+++ b/server-client.c
@@ -30,6 +30,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 *);
@@ -45,6 +46,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)
@@ -93,6 +103,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++) {
@@ -164,6 +177,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);
 
@@ -527,16 +542,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)
@@ -552,7 +570,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;
@@ -593,74 +611,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;
-       }
-
-       /* 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) {
+               /*
+                * Take a reference to this table to make sure the key binding
+                * doesn't disappear.
+                */
+               table->references++;
+
+               /*
+                * 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. */
@@ -848,9 +880,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/tmux.1 b/tmux.1
index 9b6f834..ab76238 100644
--- a/tmux.1
+++ b/tmux.1
@@ -838,6 +838,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
@@ -855,6 +856,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
@@ -1931,6 +1948,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 )
@@ -1938,16 +1956,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
@@ -1962,22 +2004,33 @@ is bound in
 .Ar mode-table :
 the binding for command mode with
 .Fl c
-or for normal mode without.
+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 ,
@@ -2022,31 +2075,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 477fd43..1af4d07 100644
--- a/tmux.h
+++ b/tmux.h
@@ -89,10 +89,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? */
@@ -1301,7 +1300,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
@@ -1320,6 +1319,7 @@ struct client {
 #define CLIENT_256COLOURS 0x20000
 #define CLIENT_IDENTIFIED 0x40000
        int              flags;
+       struct key_table *keytable;
 
        struct event     identify_timer;
 
@@ -1440,15 +1440,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
@@ -1876,12 +1885,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 *);


commit 3497843f0272e573d0a63cb6e94948591ae07667
Author: nicm <nicm>
Commit: nicm <nicm>

    Style nit - unnecessary brackets.
---
 utf8.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/utf8.c b/utf8.c
index 2940ebb..12a0b29 100644
--- a/utf8.c
+++ b/utf8.c
@@ -290,9 +290,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;
        }


-----------------------------------------------------------------------

Summary of changes:
 client.c            |    9 ++-
 cmd-bind-key.c      |   16 ++++--
 cmd-copy-mode.c     |    5 +-
 cmd-if-shell.c      |   33 +++++++----
 cmd-list-keys.c     |   86 ++++++++++++++++------------
 cmd-move-window.c   |   12 ++++-
 cmd-switch-client.c |   19 +++++-
 cmd-unbind-key.c    |   37 +++++++++----
 cmd.c               |    8 +-
 format.c            |   13 ++++-
 input-keys.c        |    1 +
 key-bindings.c      |  114 ++++++++++++++++++++++++++++---------
 layout-custom.c     |    4 +-
 server-client.c     |  156 +++++++++++++++++++++++++++++++--------------------
 server-fn.c         |    1 -
 server.c            |   22 +++++---
 tmux.1              |  108 +++++++++++++++++++++++++----------
 tmux.h              |   41 +++++++++-----
 utf8.c              |    4 +-
 window-copy.c       |   16 ++---
 window.c            |    1 +
 21 files changed, 469 insertions(+), 237 deletions(-)


hooks/post-receive
-- 
tmux

------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-cvs mailing list
tmux-cvs@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-cvs

Reply via email to