From b2e791b574a60e481d2a5813157ab00cb99cc32f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 May 2014 22:54:18 +0000 Subject: [PATCH 01/13] Don't allow multiple buffers with the same name, from Thomas Adam. --- paste.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/paste.c b/paste.c index 11a4f006..245cca37 100644 --- a/paste.c +++ b/paste.c @@ -176,7 +176,7 @@ paste_add(char *data, size_t size) int paste_rename(const char *oldname, const char *newname, char **cause) { - struct paste_buffer *pb; + struct paste_buffer *pb, *pb_new; if (cause != NULL) *cause = NULL; @@ -195,7 +195,14 @@ paste_rename(const char *oldname, const char *newname, char **cause) pb = paste_get_name(oldname); if (pb == NULL) { if (cause != NULL) - xasprintf(cause, "no buffer %s", oldname); + xasprintf(cause, "no buffer %s", oldname); + return (-1); + } + + pb_new = paste_get_name(newname); + if (pb_new != NULL) { + if (cause != NULL) + xasprintf(cause, "buffer %s already exists", newname); return (-1); } From 53cbae544f79daede0f9457f31947f5d001ac788 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:21:19 +0000 Subject: [PATCH 02/13] Now that cmdlists are reference counted, there is no need for two-step deletion via the dead_key_bindings tree. From Keith Amling. --- key-bindings.c | 17 ++--------------- server.c | 1 - tmux.h | 1 - 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index db48f9e7..55c8aebe 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -27,7 +27,6 @@ RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); struct key_bindings key_bindings; -struct key_bindings dead_key_bindings; int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) @@ -78,20 +77,8 @@ key_bindings_remove(int key) if ((bd = key_bindings_lookup(key)) == NULL) return; RB_REMOVE(key_bindings, &key_bindings, bd); - RB_INSERT(key_bindings, &dead_key_bindings, bd); -} - -void -key_bindings_clean(void) -{ - struct key_binding *bd; - - while (!RB_EMPTY(&dead_key_bindings)) { - bd = RB_ROOT(&dead_key_bindings); - RB_REMOVE(key_bindings, &dead_key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + cmd_list_free(bd->cmdlist); + free(bd); } void diff --git a/server.c b/server.c index 0d6c4f15..7ddc6ec1 100644 --- a/server.c +++ b/server.c @@ -208,7 +208,6 @@ server_loop(void) server_window_loop(); server_client_loop(); - key_bindings_clean(); server_clean_dead(); } } diff --git a/tmux.h b/tmux.h index c42df6ab..8a49b9d2 100644 --- a/tmux.h +++ b/tmux.h @@ -1889,7 +1889,6 @@ 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); -void key_bindings_clean(void); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *); From 58c97695c9516ae2d95e1f26c8fa2539fece862a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:39:58 +0000 Subject: [PATCH 03/13] Simplify copy lines, from Keith Amling. --- window-copy.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/window-copy.c b/window-copy.c index aa6b2d73..1d603353 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1442,17 +1442,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Copy the lines. */ - if (sy == ey) - window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); - else { - window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); - if (ey - sy > 1) { - for (i = sy + 1; i < ey; i++) { - window_copy_copy_line( - wp, &buf, &off, i, restsx, restex); - } - } - window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); + for (i = sy; i <= ey; i++) { + window_copy_copy_line(wp, &buf, &off, i, + (i == sy ? firstsx : restsx), + (i == ey ? lastex : restex)); } /* Don't bother if no data. */ From 191f695bad17f89c1af67d6b03dcfe55997fc5ff Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 May 2014 06:45:35 +0000 Subject: [PATCH 04/13] Change key-table to mode-table to allow for some future work. From Keith Amling. --- cmd-bind-key.c | 8 ++++---- cmd-unbind-key.c | 8 ++++---- tmux.1 | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 1ca31484..dce0bbf0 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -29,12 +29,12 @@ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); +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 key-table] key command [arguments]", + "[-cnr] [-t mode-table] key command [arguments]", 0, NULL, cmd_bind_key_exec @@ -67,7 +67,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 't')) - return (cmd_bind_key_table(self, cmdq, key)); + return (cmd_bind_key_mode_table(self, cmdq, key)); cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); @@ -84,7 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 5d86665b..92657c29 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -27,12 +27,12 @@ */ enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); +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 key-table] key", + "[-acn] [-t mode-table] key", 0, NULL, cmd_unbind_key_exec @@ -64,7 +64,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 't')) - return (cmd_unbind_key_table(self, cmdq, key)); + return (cmd_unbind_key_mode_table(self, cmdq, key)); if (key == KEYC_NONE) { while (!RB_EMPTY(&key_bindings)) { @@ -81,7 +81,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; diff --git a/tmux.1 b/tmux.1 index 24dde9fa..4aa66f9c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1861,7 +1861,7 @@ Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl cnr -.Op Fl t Ar key-table +.Op Fl t Ar mode-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -1890,7 +1890,7 @@ If is present, .Ar key is bound in -.Ar key-table : +.Ar mode-table : the binding for command mode with .Fl c or for normal mode without. @@ -1948,7 +1948,7 @@ Send the prefix key, or with the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl acn -.Op Fl t Ar key-table +.Op Fl t Ar mode-table .Ar key .Xc .D1 (alias: Ic unbind ) @@ -1970,7 +1970,7 @@ If is present, .Ar key in -.Ar key-table +.Ar mode-table is unbound: the binding for command mode with .Fl c or for normal mode without. From 7160b8c2d5d17565ef8da0fb532cb49b2d5718bb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 May 2014 12:49:36 +0000 Subject: [PATCH 05/13] Add some formats for pane bounds. --- format.c | 7 +++++++ tmux.1 | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 10e5e0a8..e10f08c9 100644 --- a/format.c +++ b/format.c @@ -552,6 +552,13 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_dead", "%d", wp->fd == -1); + if (window_pane_visible(wp)) { + format_add(ft, "pane_left", "%u", wp->xoff); + format_add(ft, "pane_top", "%u", wp->yoff); + format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); + format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 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")); diff --git a/tmux.1 b/tmux.1 index 4aa66f9c..9a5a1c4d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3075,17 +3075,21 @@ The following variables are available, where appropriate: .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .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_left" Ta "" Ta "Left of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" +.It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" From 193b6bcf36dda3754c99beb0dcc3dfdfceebdb09 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 May 2014 13:04:42 +0000 Subject: [PATCH 06/13] Handle the top bit of xterm(1)-style modifier keys, based on a diff from Balazs Kezes. --- xterm-keys.c | 79 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/xterm-keys.c b/xterm-keys.c index 0e20165b..a013cbde 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -40,8 +40,8 @@ * We accept any but always output the latter (it comes first in the table). */ -int xterm_keys_match(const char *, const char *, size_t); -int xterm_keys_modifiers(const char *, const char *, size_t); +int xterm_keys_match(const char *, const char *, size_t, size_t *, u_int *); +int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *); struct xterm_keys_entry { int key; @@ -122,49 +122,62 @@ const struct xterm_keys_entry xterm_keys_table[] = { * 0 for match, 1 if the end of the buffer is reached (need more data). */ int -xterm_keys_match(const char *template, const char *buf, size_t len) +xterm_keys_match(const char *template, const char *buf, size_t len, + size_t *size, u_int *modifiers) { size_t pos; + int retval; if (len == 0) return (0); pos = 0; do { - if (*template == '_' && buf[pos] >= '1' && buf[pos] <= '8') + if (*template == '_') { + retval = xterm_keys_modifiers(buf, len, &pos, + modifiers); + if (retval != 0) + return (retval); continue; + } if (buf[pos] != *template) return (-1); - } while (*++template != '\0' && ++pos != len); + pos++; + } while (*++template != '\0' && pos != len); if (*template != '\0') /* partial */ return (1); + *size = pos; return (0); } -/* Find modifiers based on template. */ +/* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *template, const char *buf, size_t len) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) { - size_t idx; - int param, modifiers; + u_int flags; - idx = strcspn(template, "_"); - if (idx >= len) - return (0); - param = buf[idx] - '1'; + if (len - *pos < 2) + return (1); - modifiers = 0; - if (param & 1) - modifiers |= KEYC_SHIFT; - if (param & 2) - modifiers |= KEYC_ESCAPE; - if (param & 4) - modifiers |= KEYC_CTRL; - if (param & 8) - modifiers |= KEYC_ESCAPE; - return (modifiers); + if (buf[*pos] < '0' || buf[*pos] > '9') + return (-1); + flags = buf[(*pos)++] - '0'; + if (buf[*pos] >= '0' && buf[*pos] <= '9') + flags = (flags * 10) + (buf[(*pos)++] - '0'); + flags -= 1; + + *modifiers = 0; + if (flags & 1) + *modifiers |= KEYC_SHIFT; + if (flags & 2) + *modifiers |= KEYC_ESCAPE; + if (flags & 4) + *modifiers |= KEYC_CTRL; + if (flags & 8) + *modifiers |= KEYC_ESCAPE; + return (0); } /* @@ -175,19 +188,19 @@ int xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) { const struct xterm_keys_entry *entry; - u_int i; + u_int i, modifiers; + int matched; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; - switch (xterm_keys_match(entry->template, buf, len)) { - case 0: - *size = strlen(entry->template); - *key = entry->key; - *key |= xterm_keys_modifiers(entry->template, buf, len); - return (0); - case 1: - return (1); - } + + matched = xterm_keys_match(entry->template, buf, len, size, + &modifiers); + if (matched == -1) + continue; + if (matched == 0) + *key = entry->key | modifiers; + return (matched); } return (-1); } From 74becbfd6f0f497424d32c03eb00a9bc226b8454 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Jun 2014 22:14:29 +0000 Subject: [PATCH 07/13] Reset properly when c0-change-trigger is increased from zero so panes don't get stuck. --- window.c | 1 - 1 file changed, 1 deletion(-) diff --git a/window.c b/window.c index 9a7226e9..d29ee2f2 100644 --- a/window.c +++ b/window.c @@ -877,7 +877,6 @@ window_pane_timer_callback(unused int fd, unused short events, void *data) if (wp->changes_redraw++ == interval) { wp->flags |= PANE_REDRAW; wp->changes_redraw = 0; - } if (trigger == 0 || wp->changes < trigger) { From 21ade85f24c122ecc3148a3bfeed3044a59c3214 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Jun 2014 13:21:41 +0000 Subject: [PATCH 08/13] Properly track switching G0 and G1 modes between US-ASCII and VT100 line drawing rather than just treating them as SO and SI. --- input.c | 104 +++++++++++++++++++++++++++++++++----------------------- tmux.h | 14 ++++++-- 2 files changed, 72 insertions(+), 46 deletions(-) diff --git a/input.c b/input.c index 1123f952..4c1e0730 100644 --- a/input.c +++ b/input.c @@ -52,6 +52,7 @@ int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); void input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); +void input_reset_cell(struct input_ctx *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); @@ -104,19 +105,23 @@ enum input_esc_type { INPUT_ESC_NEL, INPUT_ESC_RI, INPUT_ESC_RIS, - INPUT_ESC_SCSOFF_G0, - INPUT_ESC_SCSON_G0, + INPUT_ESC_SCSG0_OFF, + INPUT_ESC_SCSG0_ON, + INPUT_ESC_SCSG1_OFF, + INPUT_ESC_SCSG1_ON, }; /* Escape command table. */ const struct input_table_entry input_esc_table[] = { - { '0', "(", INPUT_ESC_SCSOFF_G0 }, + { '0', "(", INPUT_ESC_SCSG0_ON }, + { '0', ")", INPUT_ESC_SCSG1_ON }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, - { 'B', "(", INPUT_ESC_SCSON_G0 }, + { 'B', "(", INPUT_ESC_SCSG0_OFF }, + { 'B', ")", INPUT_ESC_SCSG1_OFF }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, @@ -684,17 +689,26 @@ input_table_compare(const void *key, const void *value) return (strcmp(ictx->interm_buf, entry->interm)); } +/* Reset cell state to default. */ +void +input_reset_cell(struct input_ctx *ictx) +{ + memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); + ictx->cell.set = 0; + ictx->cell.g0set = ictx->cell.g1set = 0; + + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; +} + /* Initialise input parser. */ void input_init(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - - memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; + input_reset_cell(ictx); *ictx->interm_buf = '\0'; ictx->interm_len = 0; @@ -903,8 +917,18 @@ input_ground(struct input_ctx *ictx) int input_print(struct input_ctx *ictx) { - grid_cell_one(&ictx->cell, ictx->ch); - screen_write_cell(&ictx->ctx, &ictx->cell); + int set; + + set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; + if (set == 1) + ictx->cell.cell.attr |= GRID_ATTR_CHARSET; + else + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; + + grid_cell_one(&ictx->cell.cell, ictx->ch); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); + + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; return (0); } @@ -1000,10 +1024,10 @@ input_c0_dispatch(struct input_ctx *ictx) screen_write_carriagereturn(sctx); goto count_c0; case '\016': /* SO */ - ictx->cell.attr |= GRID_ATTR_CHARSET; + ictx->cell.set = 1; break; case '\017': /* SI */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + ictx->cell.set = 0; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1014,7 +1038,7 @@ input_c0_dispatch(struct input_ctx *ictx) count_c0: trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (++wp->changes == trigger) { + if (trigger != 0 && ++wp->changes >= trigger) { wp->flags |= PANE_DROP; window_pane_timer_start(wp); } @@ -1043,11 +1067,7 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - + input_reset_cell(ictx); screen_write_reset(sctx); break; case INPUT_ESC_IND: @@ -1082,16 +1102,17 @@ input_esc_dispatch(struct input_ctx *ictx) case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; - case INPUT_ESC_SCSON_G0: - /* - * Not really supported, but fake it up enough for those that - * use it to switch character sets (by redefining G0 to - * graphics set, rather than switching to G1). - */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_ON: + ictx->cell.g0set = 1; break; - case INPUT_ESC_SCSOFF_G0: - ictx->cell.attr |= GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_OFF: + ictx->cell.g0set = 0; + break; + case INPUT_ESC_SCSG1_ON: + ictx->cell.g1set = 1; + break; + case INPUT_ESC_SCSG1_OFF: + ictx->cell.g1set = 0; break; } @@ -1332,7 +1353,8 @@ input_csi_dispatch_rm(struct input_ctx *ictx) void input_csi_dispatch_rm_private(struct input_ctx *ictx) { - u_int i; + struct window_pane *wp = ictx->wp; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { @@ -1366,10 +1388,10 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_off(ictx->wp, &ictx->cell, 0); + window_pane_alternate_off(wp, &ictx->cell.cell, 0); break; case 1049: - window_pane_alternate_off(ictx->wp, &ictx->cell, 1); + window_pane_alternate_off(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); @@ -1403,7 +1425,8 @@ input_csi_dispatch_sm(struct input_ctx *ictx) void input_csi_dispatch_sm_private(struct input_ctx *ictx) { - u_int i; + struct window_pane *wp = ictx->wp; + u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { @@ -1436,7 +1459,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (ictx->ctx.s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */ + wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); @@ -1446,10 +1469,10 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) break; case 47: case 1047: - window_pane_alternate_on(ictx->wp, &ictx->cell, 0); + window_pane_alternate_on(wp, &ictx->cell.cell, 0); break; case 1049: - window_pane_alternate_on(ictx->wp, &ictx->cell, 1); + window_pane_alternate_on(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); @@ -1514,15 +1537,12 @@ input_csi_dispatch_winops(struct input_ctx *ictx) void input_csi_dispatch_sgr(struct input_ctx *ictx) { - struct grid_cell *gc = &ictx->cell; + struct grid_cell *gc = &ictx->cell.cell; u_int i; int n, m; - u_char attr; if (ictx->param_list_len == 0) { - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); return; } @@ -1560,9 +1580,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) switch (n) { case 0: case 10: - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); break; case 1: gc->attr |= GRID_ATTR_BRIGHT; @@ -1806,8 +1824,8 @@ input_utf8_close(struct input_ctx *ictx) utf8_append(&ictx->utf8data, ictx->ch); - grid_cell_set(&ictx->cell, &ictx->utf8data); - screen_write_cell(&ictx->ctx, &ictx->cell); + grid_cell_set(&ictx->cell.cell, &ictx->utf8data); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); } diff --git a/tmux.h b/tmux.h index 8a49b9d2..4f937f3a 100644 --- a/tmux.h +++ b/tmux.h @@ -803,14 +803,22 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) +/* Input parser cell. */ +struct input_cell { + struct grid_cell cell; + int set; + int g0set; /* 1 if ACS */ + int g1set; /* 1 if ACS */ +}; + /* Input parser context. */ struct input_ctx { struct window_pane *wp; struct screen_write_ctx ctx; - struct grid_cell cell; + struct input_cell cell; - struct grid_cell old_cell; + struct input_cell old_cell; u_int old_cx; u_int old_cy; @@ -822,7 +830,7 @@ struct input_ctx { #define INPUT_BUF_START 32 #define INPUT_BUF_LIMIT 1048576 - u_char* input_buf; + u_char *input_buf; size_t input_len; size_t input_space; From a94696defa108dcddc39d50596e69266e595eb74 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:26:43 +0000 Subject: [PATCH 09/13] Some terminals send spurious releases for mouse wheel in SGR mouse mode, this causes confusion when tmux uses SGR outside but the application inside tmux is using conventional xterm mouse reporting. So suppress obviously bad input. From Timothy Allen, SF bug 128. --- tty-keys.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index 297e22c8..aaea06c6 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -475,6 +475,8 @@ tty_keys_next(struct tty *tty) goto complete_key; case -1: /* no, or not valid */ break; + case -2: /* yes, but we don't care. */ + goto discard_key; case 1: /* partial */ goto partial_key; } @@ -586,6 +588,14 @@ complete_key: server_client_handle_key(tty->client, key); return (1); + +discard_key: + log_debug("discard key %.*s %#x", (int) size, buf, key); + + /* Remove data from buffer. */ + evbuffer_drain(tty->event->input, size); + + return (1); } /* Key timer callback. */ @@ -730,6 +740,15 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) sgr = 1; sgr_rel = (c == 'm'); + /* + * Some terminals (like PuTTY 0.63) mistakenly send + * button-release events for scroll-wheel button-press event. + * Discard it before it reaches any program running inside + * tmux. + */ + if (sgr_rel && (sgr_b & 64)) + return (-2); + /* Figure out what b would be in old format. */ b = sgr_b; if (sgr_rel) From fd9a53b4a46f53c864fd1bbf5175a8752c68d348 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:32:12 +0000 Subject: [PATCH 10/13] Reset the buttons when the wheel is used, from Balazs Kezes. --- tty-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tty-keys.c b/tty-keys.c index aaea06c6..02be49fa 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -783,6 +783,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; + + m->button = 3; } else if ((b & MOUSE_MASK_BUTTONS) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) m->event = MOUSE_EVENT_CLICK; From 00ac1af43fe6fe51cc199a9d983499ef7fd78cf5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Jun 2014 07:37:59 +0000 Subject: [PATCH 11/13] Copy newline when at EOL in vi(1) mode, from Balazs Kezes. --- window-copy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index 1d603353..17f9751b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1360,7 +1360,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) struct screen *s = &data->screen; char *buf; size_t off; - u_int i, xx, yy, sx, sy, ex, ey; + u_int i, xx, yy, sx, sy, ex, ey, ey_last; u_int firstsx, lastex, restex, restsx; int keys; @@ -1389,9 +1389,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Trim ex to end of line. */ - xx = window_copy_find_length(wp, ey); - if (ex > xx) - ex = xx; + ey_last = window_copy_find_length(wp, ey); + if (ex > ey_last) + ex = ey_last; /* * Deal with rectangle-copy if necessary; four situations: start of @@ -1453,7 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) free(buf); return (NULL); } - *len = off - 1; /* remove final \n */ + if (keys == MODEKEY_EMACS || lastex <= ey_last) + off -= 1; /* remove final \n (unless at end in vi mode) */ + *len = off; return (buf); } From c8efffb4db5c521640dd5687d2a640ca04664aad Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Jun 2014 10:46:59 +0000 Subject: [PATCH 12/13] Make -S- and -E- mean the start and end to capture-pane to avoid having to faff around with huge numbers to get everything. --- cmd-capture-pane.c | 47 ++++++++++++++++++++++++++++------------------ tmux.1 | 6 ++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index c6a80ebd..e3c551ab 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -94,6 +94,7 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, int n, with_codes, escape_c0, join_lines; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; + const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); @@ -109,27 +110,37 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, } else gd = wp->base.grid; - n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - top = gd->hsize; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) + Sflag = args_get(args, 'S'); + if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; - else - top = gd->hsize + n; - if (top > gd->hsize + gd->sy - 1) - top = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + top = gd->hsize; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + top = 0; + else + top = gd->hsize + n; + if (top > gd->hsize + gd->sy - 1) + top = gd->hsize + gd->sy - 1; + } - n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - bottom = gd->hsize + gd->sy - 1; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) - bottom = 0; - else - bottom = gd->hsize + n; - if (bottom > gd->hsize + gd->sy - 1) + Eflag = args_get(args, 'E'); + if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + bottom = gd->hsize + gd->sy - 1; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + bottom = 0; + else + bottom = gd->hsize + n; + if (bottom > gd->hsize + gd->sy - 1) + bottom = gd->hsize + gd->sy - 1; + } if (bottom < top) { tmp = bottom; diff --git a/tmux.1 b/tmux.1 index 9a5a1c4d..f70ac391 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1148,6 +1148,12 @@ and .Fl E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. +.Ql - +to +.Fl S +is the start of the history and to +.Fl E +the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client From 0c5ed177c4e5178127dc8fe3528985df8ff988a6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 20 Jun 2014 13:17:28 +0100 Subject: [PATCH 13/13] We have utmp with utempter now and that's the best we're going to get. --- FAQ | 3 --- 1 file changed, 3 deletions(-) diff --git a/FAQ b/FAQ index 6181ad89..97d8f6b6 100644 --- a/FAQ +++ b/FAQ @@ -95,9 +95,6 @@ aware of are (bearing in mind I haven't used screen for a few years now): - screen has builtin serial and telnet support; this is bloat and is unlikely to be added to tmux. -- screen has support for updating utmp. Nobody has really come up with a clean, - portable way to do this without making tmux setuid or setgid yet. - - Environment handling is different. - tmux tends to be more demanding on the terminal so tends to show up terminal