From 662d471215d66d3c44a9251cef7d00b25587851d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:41:54 +0000 Subject: [PATCH 1/8] Mark control commands specially so the client can identify them, based on a diff from George Nachman a while back. --- cmd-queue.c | 7 +++++-- control.c | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index a64d332c..904b092c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -157,14 +157,17 @@ int cmdq_guard(struct cmd_q *cmdq, const char *guard) { struct client *c = cmdq->client; + int flags; if (c == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, - (long) cmdq->time, cmdq->number); + flags = !!(cmdq->cmd->flags & CMD_CONTROL); + + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, + (long) cmdq->time, cmdq->number, flags); server_push_stdout(c); return 1; } diff --git a/control.c b/control.c index 8986f5c6..1f3739fc 100644 --- a/control.c +++ b/control.c @@ -55,6 +55,7 @@ control_callback(struct client *c, int closed, unused void *data) { char *line, *cause; struct cmd_list *cmdlist; + struct cmd *cmd; if (closed) c->flags |= CLIENT_EXIT; @@ -78,6 +79,8 @@ control_callback(struct client *c, int closed, unused void *data) free(cause); } else { + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) + cmd->flags |= CMD_CONTROL; cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); } From a41cd8d75b55da5a1e75e187b30b33917c18c1b2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 12:51:28 +0000 Subject: [PATCH 2/8] Always push a focus event when the application turns it on, prompted by discussion with Hayaki Saito a while ago. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 0dcdee96..5f70f872 100644 --- a/input.c +++ b/input.c @@ -1333,7 +1333,7 @@ input_csi_dispatch(struct input_ctx *ictx) if (s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags &= ~PANE_FOCUSED; /* force update if needed */ + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); From 3977dba76139b5d4b0a6e60458d39a374beeedf3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 23 Jun 2013 13:10:46 +0000 Subject: [PATCH 3/8] Focus events can cause trouble if left on and they can't be turned off during idle periods (like the other states are) because we'd miss events. So add a server option to control them. Defaults to off. --- options-table.c | 5 +++++ server-client.c | 13 +++++++++++-- tmux.1 | 8 ++++++++ tmux.h | 5 +++++ tty.c | 14 ++++++++++++-- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/options-table.c b/options-table.c index c73842f7..cf0202b7 100644 --- a/options-table.c +++ b/options-table.c @@ -76,6 +76,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "focus-events", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ diff --git a/server-client.c b/server-client.c index 3b7b988a..5f61f5c0 100644 --- a/server-client.c +++ b/server-client.c @@ -548,6 +548,15 @@ server_client_check_focus(struct window_pane *wp) { u_int i; struct client *c; + int push; + + /* Are focus events off? */ + if (!options_get_number(&global_options, "focus-events")) + return; + + /* Do we need to push the focus state? */ + push = wp->flags & PANE_FOCUSPUSH; + wp->flags &= ~PANE_FOCUSPUSH; /* If we don't care about focus, forget it. */ if (!(wp->base.mode & MODE_FOCUSON)) @@ -580,13 +589,13 @@ server_client_check_focus(struct window_pane *wp) } not_focused: - if (wp->flags & PANE_FOCUSED) + if (push || (wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: - if (!(wp->flags & PANE_FOCUSED)) + if (push || !(wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[I", 3); wp->flags |= PANE_FOCUSED; } diff --git a/tmux.1 b/tmux.1 index e59b9a6e..012f2069 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2092,6 +2092,14 @@ The default is 500 milliseconds. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic focus-events +.Op Ic on | off +.Xc +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. .It Xo Ic quiet .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index b1624b6a..b3f314c5 100644 --- a/tmux.h +++ b/tmux.h @@ -941,6 +941,7 @@ struct window_pane { #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 +#define PANE_FOCUSPUSH 0x10 char *cmd; char *shell; @@ -1232,6 +1233,7 @@ struct tty { #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 +#define TTY_FOCUS 0x40 int flags; int term_flags; @@ -1383,6 +1385,9 @@ struct cmd { char *file; u_int line; +#define CMD_CONTROL 0x1 + int flags; + TAILQ_ENTRY(cmd) qentry; }; struct cmd_list { diff --git a/tty.c b/tty.c index cf6e9652..c989aaaa 100644 --- a/tty.c +++ b/tty.c @@ -219,8 +219,13 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(tty->term, TTYC_XT)) { + if (options_get_number(&global_options, "focus-events")) { + tty->flags |= TTY_FOCUS; + tty_puts(tty, "\033[?1004h"); + } tty_puts(tty, "\033[c\033[>4;1m\033[m"); + } tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -282,8 +287,13 @@ tty_stop_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) + if (tty_term_has(tty->term, TTYC_XT)) { + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_puts(tty, "\033[?1004l"); + } tty_raw(tty, "\033[>4m\033[m"); + } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); From f884fff8693de02cea0347f1c267dbd925e3b5f5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:38:23 +0000 Subject: [PATCH 4/8] Implement s, S, C mode switch commands in vi(1) mode, from Ben Boeckel. --- tmux.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tmux.h b/tmux.h index b3f314c5..b5608da3 100644 --- a/tmux.h +++ b/tmux.h @@ -548,6 +548,9 @@ enum mode_key_cmd { MODEKEYEDIT_SWITCHMODEAPPEND, MODEKEYEDIT_SWITCHMODEAPPENDLINE, MODEKEYEDIT_SWITCHMODEBEGINLINE, + MODEKEYEDIT_SWITCHMODECHANGELINE, + MODEKEYEDIT_SWITCHMODESUBSTITUTE, + MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, MODEKEYEDIT_TRANSPOSECHARS, /* Menu (choice) keys. */ From 7af5fec0387008c71bf29ad1e394871873acaf40 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:44:06 +0000 Subject: [PATCH 5/8] Whitespace nits, from Ben Boeckel. --- mode-key.c | 21 ++++++++++++++------- status.c | 13 ++++++++++--- window-copy.c | 7 ++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/mode-key.c b/mode-key.c index 54abcf32..78a8bdf3 100644 --- a/mode-key.c +++ b/mode-key.c @@ -35,9 +35,7 @@ * * vi command mode is handled by having a mode flag in the struct which allows * two sets of bindings to be swapped between. A couple of editing commands - * (MODEKEYEDIT_SWITCHMODE, MODEKEYEDIT_SWITCHMODEAPPEND, - * MODEKEYEDIT_SWITCHMODEAPPENDLINE, and MODEKEYEDIT_SWITCHMODEBEGINLINE) - * are special-cased to do this. + * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ /* Edit keys command strings. */ @@ -67,6 +65,9 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, { MODEKEYEDIT_SWITCHMODEAPPENDLINE, "switch-mode-append-line" }, { MODEKEYEDIT_SWITCHMODEBEGINLINE, "switch-mode-begin-line" }, + { MODEKEYEDIT_SWITCHMODECHANGELINE, "switch-mode-change-line" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTE, "switch-mode-substitute" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, "switch-mode-substitute-line" }, { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, { 0, NULL } @@ -166,9 +167,11 @@ const struct mode_key_entry mode_key_vi_edit[] = { { '0', 1, MODEKEYEDIT_STARTOFLINE }, { 'A', 1, MODEKEYEDIT_SWITCHMODEAPPENDLINE }, { 'B', 1, MODEKEYEDIT_PREVIOUSSPACE }, + { 'C', 1, MODEKEYEDIT_SWITCHMODECHANGELINE }, { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, { 'E', 1, MODEKEYEDIT_NEXTSPACEEND }, { 'I', 1, MODEKEYEDIT_SWITCHMODEBEGINLINE }, + { 'S', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTELINE }, { 'W', 1, MODEKEYEDIT_NEXTSPACE }, { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, @@ -185,6 +188,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { 'k', 1, MODEKEYEDIT_HISTORYUP }, { 'l', 1, MODEKEYEDIT_CURSORRIGHT }, { 'p', 1, MODEKEYEDIT_PASTE }, + { 's', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTE }, { 'w', 1, MODEKEYEDIT_NEXTWORD }, { 'x', 1, MODEKEYEDIT_DELETE }, { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE }, @@ -227,8 +231,8 @@ const struct mode_key_entry mode_key_vi_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, @@ -369,8 +373,8 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, @@ -545,6 +549,9 @@ mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) case MODEKEYEDIT_SWITCHMODEAPPEND: case MODEKEYEDIT_SWITCHMODEAPPENDLINE: case MODEKEYEDIT_SWITCHMODEBEGINLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: diff --git a/status.c b/status.c index ecfd7e58..c5572b74 100644 --- a/status.c +++ b/status.c @@ -142,10 +142,8 @@ status_set_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - if (x < wl->status_width && - session_select(s, wl->idx) == 0) { + if (x < wl->status_width && session_select(s, wl->idx) == 0) server_redraw_session(s); - } x -= wl->status_width + 1; } } @@ -938,6 +936,7 @@ status_prompt_redraw(struct client *c) off = 0; memcpy(&gc, &grid_default_cell, sizeof gc); + /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) { colour_set_fg(&gc, options_get_number(&s->options, "message-command-fg")); @@ -1099,6 +1098,7 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: if (c->prompt_index != size) { memmove(c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index + 1, @@ -1107,11 +1107,13 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: *c->prompt_buffer = '\0'; c->prompt_index = 0; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_DELETETOENDOFLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: if (c->prompt_index < size) { c->prompt_buffer[c->prompt_index] = '\0'; c->flags |= CLIENT_STATUS; @@ -1190,6 +1192,11 @@ status_prompt_key(struct client *c, int key) break; } + /* Back up to the end-of-word like vi. */ + if (options_get_number(oo, "status-keys") == MODEKEY_VI && + c->prompt_index != 0) + c->prompt_index--; + c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PREVIOUSSPACE: diff --git a/window-copy.c b/window-copy.c index f5f78cf2..9d4ff20e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1116,7 +1116,7 @@ window_copy_write_line( struct options *oo = &wp->window->options; struct grid_cell gc; char hdr[32]; - size_t last, xoff = 0, size = 0; + size_t last, xoff = 0, size = 0; window_mode_attrs(&gc, oo); @@ -1894,6 +1894,7 @@ void window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; + struct options *oo = &wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int expected = 1; @@ -1927,6 +1928,10 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) expected = !expected; } while (expected == 0); + /* Back up to the end-of-word like vi. */ + if (options_get_number(oo, "status-keys") == MODEKEY_VI && px != 0) + px--; + window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); From 064124cc5f3df250f0f866ae1df7bc4cd19bf833 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 14:52:33 +0000 Subject: [PATCH 6/8] When the session option renumber-window is used, ensure we iterate over all sessions in that group when the winlinks are reordered, otherwise the winlink lists are out of sync with one another. From Thomas Adam. --- server-fn.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server-fn.c b/server-fn.c index d92754e1..efb95acd 100644 --- a/server-fn.c +++ b/server-fn.c @@ -262,8 +262,9 @@ server_lock_client(struct client *c) void server_kill_window(struct window *w) { - struct session *s, *next_s; - struct winlink *wl; + struct session *s, *next_s, *target_s; + struct session_group *sg; + struct winlink *wl; next_s = RB_MIN(sessions, &sessions); while (next_s != NULL) { @@ -280,8 +281,13 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) - session_renumber_windows(s); + if (options_get_number(&s->options, "renumber-windows")) { + if ((sg = session_group_find(s)) != NULL) { + TAILQ_FOREACH(target_s, &sg->sessions, gentry) + session_renumber_windows(target_s); + } else + session_renumber_windows(s); + } } recalculate_sizes(); } From a96a8a1aabd64f0d79602ed7248cf73655066d92 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 15:10:38 +0000 Subject: [PATCH 7/8] Clarify error messages when setting options, from Thomas Adam. --- cmd-set-option.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 6c0fefbf..3b822d8b 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -128,8 +128,13 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) oo = &global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) + if (wl == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target window or -g" : ""); return (CMD_RETURN_ERROR); + } oo = &wl->window->options; } } else if (table == session_options_table) { @@ -137,8 +142,13 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) oo = &global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) + if (s == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target session or -g" : ""); return (CMD_RETURN_ERROR); + } oo = &s->options; } } else { From f5b041e3949e9a129d68d9919725c3afcd81ed5a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jul 2013 15:27:14 +0000 Subject: [PATCH 8/8] Add pane_synchronized format, from Romain Francoise. --- format.c | 2 ++ tmux.1 | 1 + 2 files changed, 3 insertions(+) diff --git a/format.c b/format.c index 8ed1c1be..168ff5b9 100644 --- a/format.c +++ b/format.c @@ -460,6 +460,8 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_dead", "%d", wp->fd == -1); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); + format_add(ft, "pane_synchronized", "%d", + !!options_get_number(&wp->window->options, "synchronize-panes")); if (wp->tty != NULL) format_add(ft, "pane_tty", "%s", wp->tty); diff --git a/tmux.1 b/tmux.1 index 012f2069..da010d17 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3068,6 +3068,7 @@ The following variables are available, where appropriate: .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" +.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with"