From d05debbe198699a84ddeaacfa4d5bf57e6afaac8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 Mar 2013 18:00:14 +0000 Subject: [PATCH 01/17] Unzoom on last-pane and fix a typo, from Romain Francoise. --- cmd-select-pane.c | 1 + tmux.1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index d24d7b3d..b8a12671 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -80,6 +80,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } + server_unzoom_window(wl->window); window_set_active_pane(wl->window, wl->window->last); server_status_window(wl->window); server_redraw_window_borders(wl->window); diff --git a/tmux.1 b/tmux.1 index a65ce221..1a9c0583 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1619,7 +1619,7 @@ is given in lines or cells (the default is 1). .Pp With .Fl Z , -the active pane is toggled between occupying the whole of the window and it's +the active pane is toggled between occupying the whole of the window and its normal position in the layout. .It Xo Ic respawn-pane .Op Fl k From f9e46a373f1ac7155b77754b43b05ed885a9bec9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 5 Mar 2013 20:01:16 +0000 Subject: [PATCH 02/17] Do not leak command in formats, from Romain Francoise. --- format.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index ad52caea..b711111b 100644 --- a/format.c +++ b/format.c @@ -398,7 +398,8 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) struct grid_line *gl; unsigned long long size; u_int i, idx; - const char *cwd, *cmd; + const char *cwd; + char *cmd; size = 0; for (i = 0; i < gd->hsize; i++) { @@ -432,8 +433,10 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); - if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) + if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); + free(cmd); + } format_add(ft, "cursor_x", "%d", wp->base.cx); format_add(ft, "cursor_y", "%d", wp->base.cy); From ec75f9d1a36b6f46e7ec6597d295f113c4aa12b3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 09:56:31 +0000 Subject: [PATCH 03/17] Allow lastgc to be NULL in grid_string_cells so find-window doesn't crash, problem reported by eugene everson. --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index b30127f3..551a7dc9 100644 --- a/grid.c +++ b/grid.c @@ -595,7 +595,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - if (*lastgc == NULL) { + if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; } From 7fd4d49d562d2f57e21d9198bc28914891478f7c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 09:57:26 +0000 Subject: [PATCH 04/17] Add a wait-for command which blocks a client on a named channel until it is wokrn up again (with wait-for -S). From Thiago Padilha. --- Makefile.am | 1 + cmd-wait-for.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ cmd.c | 1 + tmux.1 | 13 ++++++ tmux.h | 3 ++ 5 files changed, 142 insertions(+) create mode 100644 cmd-wait-for.c diff --git a/Makefile.am b/Makefile.am index 5caa4983..19220d8e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -135,6 +135,7 @@ dist_tmux_SOURCES = \ cmd-switch-client.c \ cmd-unbind-key.c \ cmd-unlink-window.c \ + cmd-wait-for.c \ cmd.c \ colour.c \ control.c \ diff --git a/cmd-wait-for.c b/cmd-wait-for.c new file mode 100644 index 00000000..6313358a --- /dev/null +++ b/cmd-wait-for.c @@ -0,0 +1,124 @@ +/* $Id$ */ + +/* + * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Thiago de Arruda + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Block or wake a client on a named wait channel. + */ + +enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_wait_for_entry = { + "wait-for", "wait", + "S", 1, 1, + "[-S] channel", + 0, + NULL, + NULL, + cmd_wait_for_exec +}; + +struct wait_channel { + const char *name; + TAILQ_HEAD(, cmd_q) waiters; + + RB_ENTRY(wait_channel) entry; +}; +RB_HEAD(wait_channels, wait_channel); +struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); + +int wait_channel_cmp(struct wait_channel *, struct wait_channel *); +RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); +RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); + +int +wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) +{ + return (strcmp(wc1->name, wc2->name)); +} + +enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, + struct wait_channel *); + +enum cmd_retval +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + const char *name = args->argv[0]; + struct wait_channel *wc, wc0; + + wc0.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &wc0); + + if (args_has(args, 'S')) + return (cmd_wait_for_signal(cmdq, name, wc)); + return (cmd_wait_for_wait(cmdq, name, wc)); +} + +enum cmd_retval +cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq, *wq1; + + if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { + cmdq_error(cmdq, "no waiting clients on %s", name); + return (CMD_RETURN_ERROR); + } + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to wait"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + TAILQ_INIT(&wc->waiters); + RB_INSERT(wait_channels, &wait_channels, wc); + } + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); + cmdq->references++; + + return (CMD_RETURN_WAIT); +} diff --git a/cmd.c b/cmd.c index 0d6a85ff..20484ed8 100644 --- a/cmd.c +++ b/cmd.c @@ -112,6 +112,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_switch_client_entry, &cmd_unbind_key_entry, &cmd_unlink_window_entry, + &cmd_wait_for_entry, NULL }; diff --git a/tmux.1 b/tmux.1 index 1a9c0583..8df7975e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3553,6 +3553,19 @@ If the command doesn't return success, the exit status is also displayed. .It Ic server-info .D1 (alias: Ic info ) Show server information and terminal details. +.It Xo Ic wait-for +.Fl S +.Ar channel +.Xc +.D1 (alias: Ic wait ) +When used without +.Fl S , +prevents the client from exiting until woken using +.Ic wait-for +.Fl S +with the same channel. +This command only works from outside +.Nm . .El .Sh TERMINFO EXTENSIONS .Nm diff --git a/tmux.h b/tmux.h index c1ad6628..e58c1dec 100644 --- a/tmux.h +++ b/tmux.h @@ -1416,6 +1416,8 @@ struct cmd_q { void *data; struct msg_command_data *msgdata; + + TAILQ_ENTRY(cmd_q) waitentry; }; /* Command definition. */ @@ -1835,6 +1837,7 @@ extern const struct cmd_entry cmd_switch_client_entry; extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_unlink_window_entry; extern const struct cmd_entry cmd_up_pane_entry; +extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); From bc3580fa066ee38fd752b8414cb72af7bf3861b8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 11:00:55 +0000 Subject: [PATCH 05/17] Add wait-for -L and -U for lock and unlock, from Thiago Padilha. --- cmd-wait-for.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++--- tmux.1 | 12 +++++--- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 6313358a..d40ba49e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -32,8 +32,8 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { "wait-for", "wait", - "S", 1, 1, - "[-S] channel", + "LSU", 1, 1, + "[-LSU] channel", 0, NULL, NULL, @@ -42,7 +42,10 @@ const struct cmd_entry cmd_wait_for_entry = { struct wait_channel { const char *name; + int locked; + TAILQ_HEAD(, cmd_q) waiters; + TAILQ_HEAD(, cmd_q) lockers; RB_ENTRY(wait_channel) entry; }; @@ -63,6 +66,10 @@ enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, struct wait_channel *); enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, struct wait_channel *); +enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, + struct wait_channel *); +enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, + struct wait_channel *); enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) @@ -76,6 +83,10 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'S')) return (cmd_wait_for_signal(cmdq, name, wc)); + if (args_has(args, 'L')) + return (cmd_wait_for_lock(cmdq, name, wc)); + if (args_has(args, 'U')) + return (cmd_wait_for_unlock(cmdq, name, wc)); return (cmd_wait_for_wait(cmdq, name, wc)); } @@ -95,9 +106,12 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, if (!cmdq_free(wq)) cmdq_continue(wq); } - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } return (CMD_RETURN_NORMAL); } @@ -114,11 +128,70 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, if (wc == NULL) { wc = xmalloc(sizeof *wc); wc->name = xstrdup(name); + wc->locked = 0; TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); RB_INSERT(wait_channels, &wait_channels, wc); } + TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; return (CMD_RETURN_WAIT); } + +enum cmd_retval +cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + if (cmdq->client == NULL || cmdq->client->session != NULL) { + cmdq_error(cmdq, "not able to lock"); + return (CMD_RETURN_ERROR); + } + + if (wc == NULL) { + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + wc->locked = 0; + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + RB_INSERT(wait_channels, &wait_channels, wc); + } + + if (wc->locked) { + TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); + cmdq->references++; + return (CMD_RETURN_WAIT); + } + wc->locked = 1; + + return (CMD_RETURN_NORMAL); +} + +enum cmd_retval +cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, + struct wait_channel *wc) +{ + struct cmd_q *wq; + + if (wc == NULL || !wc->locked) { + cmdq_error(cmdq, "channel %s not locked", name); + return (CMD_RETURN_ERROR); + } + + if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + TAILQ_REMOVE(&wc->lockers, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } else { + wc->locked = 0; + if (TAILQ_EMPTY(&wc->waiters)) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } + } + + return (CMD_RETURN_NORMAL); +} + diff --git a/tmux.1 b/tmux.1 index 8df7975e..0b5d6a6a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3554,16 +3554,20 @@ If the command doesn't return success, the exit status is also displayed. .D1 (alias: Ic info ) Show server information and terminal details. .It Xo Ic wait-for -.Fl S +.Fl LSU .Ar channel .Xc .D1 (alias: Ic wait ) -When used without -.Fl S , -prevents the client from exiting until woken using +When used without options, prevents the client from exiting until woken using .Ic wait-for .Fl S with the same channel. +When +.Fl L +is used, the channel is locked and any clients that try to lock the same +channel are made to wait until the channel is unlocked with +.Ic wait-for +.Fl U . This command only works from outside .Nm . .El From e964ff70e696f30f0301d11deb45c8ada54e0c55 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 6 Mar 2013 14:58:48 +0000 Subject: [PATCH 06/17] Fix --disable-static, reported by Shea Levy. --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 84aec0fb..9e060df7 100644 --- a/configure.ac +++ b/configure.ac @@ -53,8 +53,11 @@ AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) AC_ARG_ENABLE( static, AC_HELP_STRING(--enable-static, create a static build), - [LDFLAGS="$LDFLAGS -static"] + found_static=$enable_static ) +if test "x$found_static" = xyes; then + LDFLAGS="$LDFLAGS -static" +fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) From f47a063841f2baa1590fb9e53713b3713a03020d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 Mar 2013 10:07:22 +0000 Subject: [PATCH 07/17] Rename session idx to session id throughout and add $ prefix to targets to use it, extended from a diff from George Nachman. --- client.c | 2 +- cmd-server-info.c | 2 +- cmd.c | 24 ++++++++++++++++++++++-- control-notify.c | 2 +- format.c | 1 + server-fn.c | 2 +- session.c | 14 +++++++------- tmux.1 | 5 +++-- tmux.c | 8 ++++---- tmux.h | 10 +++++----- window-choose.c | 2 +- 11 files changed, 47 insertions(+), 25 deletions(-) diff --git a/client.c b/client.c index 5b227b44..56caa338 100644 --- a/client.c +++ b/client.c @@ -270,7 +270,7 @@ client_main(int argc, char **argv, int flags) if (msg == MSG_COMMAND) { /* Fill in command line arguments. */ cmddata.pid = environ_pid; - cmddata.idx = environ_idx; + cmddata.session_id = environ_session_id; /* Prepare command for server. */ cmddata.argc = argc; diff --git a/cmd-server-info.c b/cmd-server-info.c index b044649c..8eba172a 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -102,7 +102,7 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) *strchr(tim, '\n') = '\0'; cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", s->idx, s->name, + "[flags=0x%x]", s->id, s->name, winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; diff --git a/cmd.c b/cmd.c index 20484ed8..c8e9702d 100644 --- a/cmd.c +++ b/cmd.c @@ -122,6 +122,7 @@ struct session *cmd_choose_session(int); struct client *cmd_choose_client(struct clients *); struct client *cmd_lookup_client(const char *); struct session *cmd_lookup_session(const char *, int *); +struct session *cmd_lookup_session_id(const char *); struct winlink *cmd_lookup_window(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *); struct window_pane *cmd_lookup_paneid(const char *); @@ -357,8 +358,8 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) } /* Use the session from the TMUX environment variable. */ - if (data != NULL && data->pid == getpid() && data->idx != -1) { - s = session_find_by_index(data->idx); + if (data != NULL && data->pid == getpid() && data->session_id != -1) { + s = session_find_by_id(data->session_id); if (s != NULL) return (s); } @@ -550,6 +551,21 @@ cmd_lookup_client(const char *name) return (NULL); } +/* Find the target session or report an error and return NULL. */ +struct session * +cmd_lookup_session_id(const char *arg) +{ + char *endptr; + long id; + + if (arg[0] != '$') + return (NULL); + id = strtol(arg + 1, &endptr, 10); + if (arg[1] != '\0' && *endptr == '\0') + return (session_find_by_id(id)); + return (NULL); +} + /* Lookup a session by name. If no session is found, NULL is returned. */ struct session * cmd_lookup_session(const char *name, int *ambiguous) @@ -558,6 +574,10 @@ cmd_lookup_session(const char *name, int *ambiguous) *ambiguous = 0; + /* Look for $id first. */ + if ((s = cmd_lookup_session_id(name)) != NULL) + return (s); + /* * Look for matches. First look for exact matches - session names must * be unique so an exact match can't be ambigious and can just be diff --git a/control-notify.c b/control-notify.c index bb9708c8..6bc98b6f 100644 --- a/control-notify.c +++ b/control-notify.c @@ -154,7 +154,7 @@ control_notify_attached_session_changed(struct client *c) return; s = c->session; - control_write(c, "%%session-changed %d %s", s->idx, s->name); + control_write(c, "%%session-changed %d %s", s->id, s->name); } void diff --git a/format.c b/format.c index b711111b..4d70d59b 100644 --- a/format.c +++ b/format.c @@ -280,6 +280,7 @@ format_session(struct format_tree *ft, struct session *s) format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_width", "%u", s->sx); format_add(ft, "session_height", "%u", s->sy); + format_add(ft, "session_id", "%u", s->id); sg = session_group_find(s); format_add(ft, "session_grouped", "%d", sg != NULL); diff --git a/server-fn.c b/server-fn.c index b09415e0..4d8a658c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -39,7 +39,7 @@ server_fill_environ(struct session *s, struct environ *env) term = options_get_string(&s->options, "default-terminal"); environ_set(env, "TERM", term); - idx = s->idx; + idx = s->id; } else idx = -1; pid = getpid(); diff --git a/session.c b/session.c index 72e8fb05..74eb06a5 100644 --- a/session.c +++ b/session.c @@ -29,7 +29,7 @@ /* Global session list. */ struct sessions sessions; struct sessions dead_sessions; -u_int next_session; +u_int next_session_id; struct session_groups session_groups; struct winlink *session_next_alert(struct winlink *); @@ -69,14 +69,14 @@ session_find(const char *name) return (RB_FIND(sessions, &sessions, &s)); } -/* Find session by index. */ +/* Find session by id. */ struct session * -session_find_by_index(u_int idx) +session_find_by_id(u_int id) { struct session *s; RB_FOREACH(s, sessions, &sessions) { - if (s->idx == idx) + if (s->id == id) return (s); } return (NULL); @@ -120,13 +120,13 @@ session_create(const char *name, const char *cmd, const char *cwd, if (name != NULL) { s->name = xstrdup(name); - s->idx = next_session++; + s->id = next_session_id++; } else { s->name = NULL; do { - s->idx = next_session++; + s->id = next_session_id++; free (s->name); - xasprintf(&s->name, "%u", s->idx); + xasprintf(&s->name, "%u", s->id); } while (RB_FIND(sessions, &sessions, s) != NULL); } RB_INSERT(sessions, &sessions, s); diff --git a/tmux.1 b/tmux.1 index 0b5d6a6a..f219615e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -369,9 +369,9 @@ Clients may be listed with the command. .Pp .Ar target-session -is either the name of a session (as listed by the +is the session id prefixed with a $, the name of a session (as listed by the .Ic list-sessions -command) or the name of a client with the same syntax as +command), or the name of a client with the same syntax as .Ar target-client , in which case the session attached to the client is used. When looking for the session name, @@ -3085,6 +3085,7 @@ The following variables are available, where appropriate: .It Li "session_group" Ta "Number of session group" .It Li "session_grouped" Ta "1 if session in a group" .It Li "session_height" Ta "Height of session" +.It Li "session_id" Ta "Unique session ID" .It Li "session_name" Ta "Name of session" .It Li "session_width" Ta "Width of session" .It Li "session_windows" Ta "Number of windows in session" diff --git a/tmux.c b/tmux.c index f6856605..8ea91ebe 100644 --- a/tmux.c +++ b/tmux.c @@ -48,7 +48,7 @@ char socket_path[MAXPATHLEN]; int login_shell; char *environ_path; pid_t environ_pid = -1; -int environ_idx = -1; +int environ_session_id = -1; __dead void usage(void); void parseenvironment(void); @@ -147,16 +147,16 @@ parseenvironment(void) { char *env, path[256]; long pid; - int idx; + int id; if ((env = getenv("TMUX")) == NULL) return; - if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) + if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) return; environ_path = xstrdup(path); environ_pid = pid; - environ_idx = idx; + environ_session_id = id; } char * diff --git a/tmux.h b/tmux.h index e58c1dec..e45cd487 100644 --- a/tmux.h +++ b/tmux.h @@ -462,8 +462,8 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* PID from $TMUX or -1 */ - int idx; /* index from $TMUX or -1 */ + pid_t pid; /* from $TMUX or -1 */ + int session_id; /* from $TMUX or -1 */ int argc; char argv[COMMAND_LENGTH]; @@ -1086,7 +1086,7 @@ struct session_group { TAILQ_HEAD(session_groups, session_group); struct session { - u_int idx; + u_int id; char *name; char *cwd; @@ -1513,7 +1513,7 @@ extern char socket_path[MAXPATHLEN]; extern int login_shell; extern char *environ_path; extern pid_t environ_pid; -extern int environ_idx; +extern int environ_session_id; void logfile(const char *); const char *getshell(void); int checkshell(const char *); @@ -2287,7 +2287,7 @@ int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); -struct session *session_find_by_index(u_int); +struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct termios *, int, u_int, u_int, char **); diff --git a/window-choose.c b/window-choose.c index b56b2022..3c68d101 100644 --- a/window-choose.c +++ b/window-choose.c @@ -859,7 +859,7 @@ window_choose_add_session(struct window_pane *wp, struct client *c, struct window_choose_data *wcd; wcd = window_choose_data_create(TREE_SESSION, c, c->session); - wcd->idx = s->idx; + wcd->idx = s->id; wcd->tree_session = s; wcd->tree_session->references++; From dde5d49a5ed305cfa32f18c08d6f1b769d8ccef7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 Mar 2013 17:29:22 +0000 Subject: [PATCH 08/17] Do not redraw panes if invisible. --- screen-redraw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/screen-redraw.c b/screen-redraw.c index 899f741b..14b73164 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -273,6 +273,9 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) { u_int i, yoff; + if (!window_pane_visible(wp)) + return; + yoff = wp->yoff; if (status_at_line(c) == 0) yoff++; From 06ac399ce6dc25201cfd1670b08ed8758034e2dc Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 9 Mar 2013 21:32:47 +0000 Subject: [PATCH 09/17] Zoom script is no longer needed. --- examples/tmux-zoom.sh | 57 ------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 examples/tmux-zoom.sh diff --git a/examples/tmux-zoom.sh b/examples/tmux-zoom.sh deleted file mode 100644 index 3a604564..00000000 --- a/examples/tmux-zoom.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2012 Juan Ignacio Pumarino, jipumarino@gmail.com -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# Instructions -# ------------ -# -# 1. Install this script and give it execute permission somewhere in your PATH. -# For example: -# -# $ mkdir -p ~/bin -# $ wget https://raw.github.com/jipumarino/tmux-zoom/master/tmux-zoom.sh -O ~/bin/tmux-zoom.sh -# $ chmod +x ~/bin/tmux-zoom.sh -# -# 2. Add a shortcut in your ~/.tmux.conf file: -# -# bind C-k run "tmux-zoom.sh" -# -# 3. When using this shortcut, the current tmux pane will open in a new window by itself. -# Running it again in the zoomed window will return it to its original pane. You can have -# as many zoomed windows as you want. - -current=$(tmux display-message -p '#W-#I-#P') -list=$(tmux list-window) - -[[ "$current" =~ ^(.*)-([0-9]+)-([0-9]+) ]] -current_window=${BASH_REMATCH[1]} -current_pane=${BASH_REMATCH[2]}-${BASH_REMATCH[3]} -new_zoom_window=ZOOM-$current_pane - -if [[ $current_window =~ ZOOM-([0-9]+)-([0-9+]) ]]; then - old_zoom_window=ZOOM-${BASH_REMATCH[1]}-${BASH_REMATCH[2]} - tmux select-window -t ${BASH_REMATCH[1]} \; select-pane -t ${BASH_REMATCH[2]} \; swap-pane -s $old_zoom_window.1 \; kill-window -t $old_zoom_window -elif [[ $list =~ $new_zoom_window ]]; then - tmux select-window -t $new_zoom_window -else - tmux new-window -d -n $new_zoom_window \; swap-pane -s $new_zoom_window.1 \; select-window -t $new_zoom_window -fi From a060aa2bf091c7befbb37f86ef450cd575a3e53e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 Mar 2013 23:41:59 +0000 Subject: [PATCH 10/17] Fix handling of short (< 4 character) checksums and a bug with parsing old-style custom layouts. Based on fix from Chris Johnsen. --- layout-custom.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/layout-custom.c b/layout-custom.c index c076232f..e32d9d9d 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -63,7 +63,7 @@ layout_dump(struct window *w) if (layout_append(w->layout_root, layout, sizeof layout) != 0) return (NULL); - xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); + xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); return (out); } @@ -206,11 +206,11 @@ layout_construct(struct layout_cell *lcparent, const char **layout) { struct layout_cell *lc, *lcchild; u_int sx, sy, xoff, yoff; + const char *saved; if (!isdigit((u_char) **layout)) return (NULL); - if (sscanf(*layout, "%ux%u,%u,%u,%*u", &sx, &sy, &xoff, &yoff) != 4 && - sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) + if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) return (NULL); while (isdigit((u_char) **layout)) @@ -231,9 +231,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout) while (isdigit((u_char) **layout)) (*layout)++; if (**layout == ',') { + saved = *layout; (*layout)++; while (isdigit((u_char) **layout)) (*layout)++; + if (**layout == 'x') + *layout = saved; } lc = layout_create_cell(lcparent); From c41d92d27a8083286793a58bc02ffc015d8c70ac Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 09:35:44 +0000 Subject: [PATCH 11/17] Add time and a command count to control mode guards, based on code from George Nachman. --- cmd-queue.c | 39 +++++++++++++++++++++++++-------------- control.c | 9 +++++++-- tmux.h | 4 ++++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 4b00fceb..52674442 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -151,6 +151,22 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) free(msg); } +/* Print a guard line. */ +void +cmdq_guard(struct cmd_q *cmdq, const char *guard) +{ + struct client *c = cmdq->client; + + if (c == NULL || c->session == NULL) + return; + if (!(c->flags & CLIENT_CONTROL)) + return; + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, + cmdq->time, cmdq->number); + server_push_stdout(c); +} + /* Add command list to queue and begin processing if needed. */ void cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) @@ -179,16 +195,11 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) int cmdq_continue(struct cmd_q *cmdq) { - struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; - int guards, empty; + int empty; char s[1024]; - guards = 0; - if (c != NULL && c->session != NULL) - guards = c->flags & CLIENT_CONTROL; - notify_disable(); empty = TAILQ_EMPTY(&cmdq->queue); @@ -209,15 +220,15 @@ cmdq_continue(struct cmd_q *cmdq) log_debug("cmdq %p: %s (client %d)", cmdq, s, cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); - if (guards) - cmdq_print(cmdq, "%%begin"); + cmdq->time = time(NULL); + cmdq->number++; + + cmdq_guard(cmdq, "begin"); retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); - if (guards) { - if (retval == CMD_RETURN_ERROR) - cmdq_print(cmdq, "%%error"); - else - cmdq_print(cmdq, "%%end"); - } + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error"); + else + cmdq_guard(cmdq, "end"); if (retval == CMD_RETURN_ERROR) break; diff --git a/control.c b/control.c index 06f20bbb..c888877e 100644 --- a/control.c +++ b/control.c @@ -68,8 +68,13 @@ control_callback(struct client *c, int closed, unused void *data) } if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) { - control_write(c, "%%error in line \"%s\": %s", line, - cause); + c->cmdq->time = time(NULL); + c->cmdq->number++; + + cmdq_guard(c->cmdq, "begin"); + control_write(c, "parse error: %s", cause); + cmdq_guard(c->cmdq, "error"); + free(cause); } else { cmdq_run(c->cmdq, cmdlist); diff --git a/tmux.h b/tmux.h index e45cd487..89860cb8 100644 --- a/tmux.h +++ b/tmux.h @@ -1412,6 +1412,9 @@ struct cmd_q { struct cmd_q_item *item; struct cmd *cmd; + time_t time; + u_int number; + void (*emptyfn)(struct cmd_q *); void *data; @@ -1853,6 +1856,7 @@ int cmdq_free(struct cmd_q *); void printflike2 cmdq_print(struct cmd_q *, const char *, ...); void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...); +void cmdq_guard(struct cmd_q *, const char *); void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); From 49ed75d88369ae8a3203cdce7fa537c792f4ba69 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 09:37:16 +0000 Subject: [PATCH 12/17] Fix a warning. --- cmd-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 52674442..17992a37 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -163,7 +163,7 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard) return; evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, - cmdq->time, cmdq->number); + (long) cmdq->time, cmdq->number); server_push_stdout(c); } From 97620bb5becf29571a8f5bd1ee89e0b12908f799 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 09:37:52 +0000 Subject: [PATCH 13/17] Add a home and end as modified by xterm in keypad mode, from Chris Johnsen. --- tty-keys.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 575920e6..fab8c3fb 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -82,6 +82,13 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[C", KEYC_RIGHT }, { "\033[D", KEYC_LEFT }, + /* Other (xterm) "cursor" keys. */ + { "\033OH", KEYC_HOME }, + { "\033OF", KEYC_END }, + + { "\033[H", KEYC_HOME }, + { "\033[F", KEYC_END }, + /* rxvt-style arrow + modifier keys. */ { "\033Oa", KEYC_UP|KEYC_CTRL }, { "\033Ob", KEYC_DOWN|KEYC_CTRL }, From a6ad44f111a42553ad38a850a0e215914ea1f037 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 09:43:56 +0000 Subject: [PATCH 14/17] Fix if-shell and run-shell if there are no sessions. Batted around through several people, finished off by Chris Johnsen. --- cmd-if-shell.c | 20 +++++++++++--------- cmd-run-shell.c | 29 ++++++++++++++++------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index b921f418..b22e3269 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -59,19 +59,21 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct cmd_if_shell_data *cdata; char *shellcmd; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; struct format_tree *ft; - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); + if (args_has(args, 't')) + wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); ft = format_create(); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + if (s != NULL) + format_session(ft, s); + if (s != NULL && wl != NULL) + format_winlink(ft, s, wl); + if (wp != NULL) + format_window_pane(ft, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index aaa310b9..b163542f 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -49,16 +49,17 @@ struct cmd_run_shell_data { char *cmd; struct cmd_q *cmdq; int bflag; - u_int wp_id; + int wp_id; }; void cmd_run_shell_print(struct job *job, const char *msg) { struct cmd_run_shell_data *cdata = job->data; - struct window_pane *wp; + struct window_pane *wp = NULL; - wp = window_pane_find_by_id(cdata->wp_id); + if (cdata->wp_id != -1) + wp = window_pane_find_by_id(cdata->wp_id); if (wp == NULL) { cmdq_print(cdata->cmdq, "%s", msg); return; @@ -76,26 +77,28 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct cmd_run_shell_data *cdata; char *shellcmd; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; struct format_tree *ft; - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); + if (args_has(args, 't')) + wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); ft = format_create(); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + if (s != NULL) + format_session(ft, s); + if (s != NULL && wl != NULL) + format_winlink(ft, s, wl); + if (wp != NULL) + format_window_pane(ft, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); cdata = xmalloc(sizeof *cdata); cdata->cmd = shellcmd; cdata->bflag = args_has(args, 'b'); - cdata->wp_id = wp->id; + cdata->wp_id = wp != NULL ? (int) wp->id : -1; cdata->cmdq = cmdq; cmdq->references++; From 412ac6bc3a690628f72101494ac88e91c7d3377c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 09:46:18 +0000 Subject: [PATCH 15/17] Use single stdout and stderr for control clients. --- server-client.c | 5 ++++- server-fn.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index b905a7c7..77e6de78 100644 --- a/server-client.c +++ b/server-client.c @@ -153,7 +153,8 @@ server_client_lost(struct client *c) evbuffer_free (c->stdin_data); evbuffer_free (c->stdout_data); - evbuffer_free (c->stderr_data); + if (c->stderr_data != c->stdout_data) + evbuffer_free (c->stderr_data); status_free_jobs(&c->status_new); status_free_jobs(&c->status_old); @@ -955,6 +956,8 @@ server_client_msg_identify( if (data->flags & IDENTIFY_CONTROL) { c->stdin_callback = control_callback; + evbuffer_free(c->stderr_data); + c->stderr_data = c->stdout_data; c->flags |= CLIENT_CONTROL; if (data->flags & IDENTIFY_TERMIOS) evbuffer_add_printf(c->stdout_data, "\033P1000p"); diff --git a/server-fn.c b/server-fn.c index 4d8a658c..566925f0 100644 --- a/server-fn.c +++ b/server-fn.c @@ -546,6 +546,10 @@ server_push_stderr(struct client *c) struct msg_stderr_data data; size_t size; + if (c->stderr_data == c->stdout_data) { + server_push_stdout(c); + return; + } size = EVBUFFER_LENGTH(c->stderr_data); if (size == 0) return; From 7b4084a15a5c82824737051143dfe0115cff52e5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 13:06:30 +0000 Subject: [PATCH 16/17] Document control mode in the manpage, from George Nachman. --- tmux.1 | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index f219615e..f73af3fa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 28lquvV +.Op Fl 28lCquvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -102,6 +102,11 @@ to assume the terminal supports 256 colours. Like .Fl 2 , but indicates that the terminal supports 88 colours. +.It Fl C +Start in control mode. +Given twice +.Xo ( Fl CC ) Xc +disables echo. .It Fl c Ar shell-command Execute .Ar shell-command @@ -3610,6 +3615,94 @@ option above and the .Xr xterm 1 man page. .El +.Sh CONTROL MODE +.Nm +offers a textual interface called +.Em control mode . +This allows applications to communicate with +.Nm +using a simple text-only protocol. +.Pp +In control mode, a client sends +.Nm +commands or command sequences terminated by newlines on standard input. +Each command will produce one block of output on standard output. +An output block consists of a +.Em %begin +line followed by the output (which may be empty). +The output block ends with a +.Em %end +or +.Em %error . +.Em %begin +and matching +.Em %end +or +.Em %error +have two arguments: an integer time (as seconds from epoch) and command number. +For example: +.Bd -literal -offset indent +%begin 1363006971 2 +0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) +%end 1363006971 2 +.Ed +.Pp +In control mode, +.Nm +outputs notifications. +A notification will never occur inside an output block. +.Pp +The following notifications are defined: +.Pp +.Bl -tag -width Ds +.It Ic %exit Op Ar reason +The +.Nm +client is exiting immediately, either because it is not attached to any session +or an error occurred. +If present, +.Ar reason +describes why the client exited. +.It Ic %layout-change Ar window-id Ar window-layout +The layout of a window with ID +.Ar window-id +changed. +The new layout is +.Ar window-layout . +.It Ic %output Ar source-pane Ar value +A window pane, +.Ar source-pane , +produced output. +.Ar value +contains that output with each byte encoded as two hex digits. +.It Ic %session-changed Ar session-id Ar name +The client is now attached to the session with ID +.Ar session-id , +which is named +.Ar name . +.It Ic %session-renamed Ar name +The current session was renamed to +.Ar name . +.It Ic %sessions-changed +A session was created or destroyed. +.It Ic %unlinked-window-add Ar window-id +The window with ID +.Ar window-id +was created but is not linked to the current session. +.It Ic %window-add Ar window-id +The window with ID +.Ar window-id +was linked to the current session. +.It Ic %window-close Ar window-id +The window with ID +.Ar window-id +closed. +.It Ic %window-renamed Ar window-id Ar name +The window with ID +.Ar window-id +was renamed to +.Ar name . +.El .Sh FILES .Bl -tag -width "/etc/tmux.confXXX" -compact .It Pa ~/.tmux.conf From 7c009509676b4580065fdc6f0084a93b9758fac0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 11 Mar 2013 15:28:34 +0000 Subject: [PATCH 17/17] Don't add prefix to %output pane id. --- control-notify.c | 6 +++--- tmux.1 | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/control-notify.c b/control-notify.c index 6bc98b6f..90ee4ffa 100644 --- a/control-notify.c +++ b/control-notify.c @@ -45,7 +45,7 @@ control_notify_input(struct client *c, struct window_pane *wp, */ if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) { message = evbuffer_new(); - evbuffer_add_printf(message, "%%output %%%u ", wp->id); + evbuffer_add_printf(message, "%%output %u ", wp->id); for (i = 0; i < len; i++) evbuffer_add_printf(message, "%02hhx", buf[i]); control_write_buffer(c, message); @@ -141,7 +141,7 @@ control_notify_window_renamed(struct window *w) continue; s = c->session; - control_write(c, "%%window-renamed %u %s", w->id, w->name); + control_write(c, "%%window-renamed %u %s", w->id, w->name); } } @@ -154,7 +154,7 @@ control_notify_attached_session_changed(struct client *c) return; s = c->session; - control_write(c, "%%session-changed %d %s", s->id, s->name); + control_write(c, "%%session-changed %u %s", s->id, s->name); } void diff --git a/tmux.1 b/tmux.1 index f73af3fa..519bf6fa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3669,10 +3669,8 @@ The layout of a window with ID changed. The new layout is .Ar window-layout . -.It Ic %output Ar source-pane Ar value -A window pane, -.Ar source-pane , -produced output. +.It Ic %output Ar pane-id Ar value +A window pane produced output. .Ar value contains that output with each byte encoded as two hex digits. .It Ic %session-changed Ar session-id Ar name