From 17bab327943824ae6c4b6c0f76cf8d2fd0055d12 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Oct 2024 06:15:47 +0000 Subject: [PATCH 1/2] Change pasting to bypass the output key processing entirely and write what was originally received. Fixes problems with pasted text being interpreted as extended keys reported by Mark Kelly. --- cmd-send-keys.c | 6 +++-- input-keys.c | 7 ++++-- server-client.c | 62 +++++++++++++++++++++++++++++++------------------ session.c | 7 ++---- tmux.h | 13 +++++++---- tty-keys.c | 17 ++++++++++---- window.c | 34 +++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 40 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index ac99a6fd..c270fbd1 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -73,11 +73,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, if (args_has(args, 'K')) { if (tc == NULL) return (item); - event = xmalloc(sizeof *event); + event = xcalloc(1, sizeof *event); event->key = key|KEYC_SENT; memset(&event->m, 0, sizeof event->m); - if (server_client_handle_key(tc, event) == 0) + if (server_client_handle_key(tc, event) == 0) { + free(event->buf); free(event); + } return (item); } diff --git a/input-keys.c b/input-keys.c index 479db928..e690a3a8 100644 --- a/input-keys.c +++ b/input-keys.c @@ -499,9 +499,12 @@ input_key_vt10x(struct bufferevent *bev, key_code key) return (0); } - /* Prevent TAB and RET from being swallowed by C0 remapping logic. */ + /* + * Prevent TAB, CR and LF from being swallowed by the C0 remapping + * logic. + */ onlykey = key & KEYC_MASK_KEY; - if (onlykey == '\r' || onlykey == '\t') + if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t') key &= ~KEYC_CTRL; /* diff --git a/server-client.c b/server-client.c index b1236688..851d9fd2 100644 --- a/server-client.c +++ b/server-client.c @@ -46,8 +46,6 @@ static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_set_path(struct client *); static void server_client_reset_state(struct client *); -static int server_client_is_bracket_pasting(struct client *, key_code); -static int server_client_assume_paste(struct session *); static void server_client_update_latest(struct client *); static void server_client_dispatch(struct imsg *, void *); @@ -1801,18 +1799,18 @@ out: /* Is this a bracket paste key? */ static int -server_client_is_bracket_pasting(struct client *c, key_code key) +server_client_is_bracket_paste(struct client *c, key_code key) { if (key == KEYC_PASTE_START) { c->flags |= CLIENT_BRACKETPASTING; log_debug("%s: bracket paste on", c->name); - return (1); + return (0); } if (key == KEYC_PASTE_END) { - c->flags &= ~CLIENT_BRACKETPASTING; + c->flags &= ~CLIENT_BRACKETPASTING; log_debug("%s: bracket paste off", c->name); - return (1); + return (0); } return !!(c->flags & CLIENT_BRACKETPASTING); @@ -1820,25 +1818,29 @@ server_client_is_bracket_pasting(struct client *c, key_code key) /* Is this fast enough to probably be a paste? */ static int -server_client_assume_paste(struct session *s) +server_client_is_assume_paste(struct client *c) { - struct timeval tv; - int t; + struct session *s = c->session; + struct timeval tv; + int t; + if (c->flags & CLIENT_BRACKETPASTING) + return (0); if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); - timersub(&s->activity_time, &s->last_activity_time, &tv); + timersub(&c->activity_time, &c->last_activity_time, &tv); if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { - log_debug("session %s pasting (flag %d)", s->name, - !!(s->flags & SESSION_PASTING)); - if (s->flags & SESSION_PASTING) + if (c->flags & CLIENT_ASSUMEPASTING) return (1); - s->flags |= SESSION_PASTING; + c->flags |= CLIENT_ASSUMEPASTING; + log_debug("%s: assume paste on", c->name); return (0); } - log_debug("session %s not pasting", s->name); - s->flags &= ~SESSION_PASTING; + if (c->flags & CLIENT_ASSUMEPASTING) { + c->flags &= ~CLIENT_ASSUMEPASTING; + log_debug("%s: assume paste off", c->name); + } return (0); } @@ -1891,6 +1893,8 @@ server_client_key_callback(struct cmdq_item *item, void *data) wl = s->curw; /* Update the activity timer. */ + memcpy(&c->last_activity_time, &c->activity_time, + sizeof c->last_activity_time); if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &c->activity_time); @@ -1928,14 +1932,16 @@ server_client_key_callback(struct cmdq_item *item, void *data) goto forward_key; /* Forward if bracket pasting. */ - if (server_client_is_bracket_pasting(c, key)) - goto forward_key; + if (server_client_is_bracket_paste (c, key)) + goto paste_key; /* Treat everything as a regular key when pasting is detected. */ if (!KEYC_IS_MOUSE(key) && + key != KEYC_FOCUS_IN && + key != KEYC_FOCUS_OUT && (~key & KEYC_SENT) && - server_client_assume_paste(s)) - goto forward_key; + server_client_is_assume_paste(c)) + goto paste_key; /* * Work out the current key table. If the pane is in a mode, use @@ -2104,10 +2110,20 @@ forward_key: goto out; if (wp != NULL) window_pane_key(wp, c, s, wl, key, m); + goto out; + +paste_key: + if (c->flags & CLIENT_READONLY) + goto out; + if (event->buf != NULL) + window_pane_paste(wp, event->buf, event->len); + key = KEYC_NONE; + goto out; out: if (s != NULL && key != KEYC_FOCUS_OUT) server_client_update_latest(c); + free(event->buf); free(event); return (CMD_RETURN_NORMAL); } @@ -2521,11 +2537,13 @@ server_client_click_timer(__unused int fd, __unused short events, void *data) * Waiting for a third click that hasn't happened, so this must * have been a double click. */ - event = xmalloc(sizeof *event); + event = xcalloc(1, sizeof *event); event->key = KEYC_DOUBLECLICK; memcpy(&event->m, &c->click_event, sizeof event->m); - if (!server_client_handle_key(c, event)) + if (!server_client_handle_key(c, event)) { + free(event->buf); free(event); + } } c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); } diff --git a/session.c b/session.c index 1d3b8d0e..d01e4ba2 100644 --- a/session.c +++ b/session.c @@ -272,19 +272,16 @@ session_lock_timer(__unused int fd, __unused short events, void *arg) void session_update_activity(struct session *s, struct timeval *from) { - struct timeval *last = &s->last_activity_time; struct timeval tv; - memcpy(last, &s->activity_time, sizeof *last); if (from == NULL) gettimeofday(&s->activity_time, NULL); else memcpy(&s->activity_time, from, sizeof s->activity_time); - log_debug("session $%u %s activity %lld.%06d (last %lld.%06d)", s->id, + log_debug("session $%u %s activity %lld.%06d", s->id, s->name, (long long)s->activity_time.tv_sec, - (int)s->activity_time.tv_usec, (long long)last->tv_sec, - (int)last->tv_usec); + (int)s->activity_time.tv_usec); if (evtimer_initialized(&s->lock_timer)) evtimer_del(&s->lock_timer); diff --git a/tmux.h b/tmux.h index 2414db17..d8c720f9 100644 --- a/tmux.h +++ b/tmux.h @@ -1311,8 +1311,7 @@ struct session { struct options *options; -#define SESSION_PASTING 0x1 -#define SESSION_ALERTED 0x2 +#define SESSION_ALERTED 0x1 int flags; u_int attached; @@ -1390,8 +1389,11 @@ struct mouse_event { /* Key event. */ struct key_event { - key_code key; - struct mouse_event m; + key_code key; + struct mouse_event m; + + char *buf; + size_t len; }; /* Terminal definition. */ @@ -1806,6 +1808,7 @@ struct client { struct timeval creation_time; struct timeval activity_time; + struct timeval last_activity_time; struct environ *environ; struct format_job_tree *jobs; @@ -1872,6 +1875,7 @@ struct client { #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_BRACKETPASTING 0x1000000000ULL +#define CLIENT_ASSUMEPASTING 0x2000000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -3097,6 +3101,7 @@ void window_pane_reset_mode_all(struct window_pane *); int window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); +void window_pane_paste(struct window_pane *, char *, size_t); int window_pane_visible(struct window_pane *); int window_pane_exited(struct window_pane *); u_int window_pane_search(struct window_pane *, const char *, int, diff --git a/tty-keys.c b/tty-keys.c index 3c08db11..9a0f3ea6 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -944,9 +944,6 @@ complete_key: if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace) key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE; - /* Remove data from buffer. */ - evbuffer_drain(tty->in, size); - /* Remove key timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); @@ -965,13 +962,23 @@ complete_key: /* Fire the key. */ if (key != KEYC_UNKNOWN) { - event = xmalloc(sizeof *event); + event = xcalloc(1, sizeof *event); event->key = key; memcpy(&event->m, &m, sizeof event->m); - if (!server_client_handle_key(c, event)) + + event->buf = xmalloc(size); + event->len = size; + memcpy (event->buf, buf, event->len); + + if (!server_client_handle_key(c, event)) { + free(event->buf); free(event); + } } + /* Remove data from buffer. */ + evbuffer_drain(tty->in, size); + return (1); discard_key: diff --git a/window.c b/window.c index be54ad02..6621f89b 100644 --- a/window.c +++ b/window.c @@ -1154,6 +1154,24 @@ window_pane_reset_mode_all(struct window_pane *wp) window_pane_reset_mode(wp); } +static void +window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len) +{ + struct window_pane *loop; + + TAILQ_FOREACH(loop, &wp->window->panes, entry) { + if (loop != wp && + TAILQ_EMPTY(&loop->modes) && + loop->fd != -1 && + (~loop->flags & PANE_INPUTOFF) && + window_pane_visible(loop) && + options_get_number(loop->options, "synchronize-panes")) { + log_debug("%s: %.*s", __func__, (int)len, buf); + bufferevent_write(loop->event, buf, len); + } + } +} + static void window_pane_copy_key(struct window_pane *wp, key_code key) { @@ -1170,6 +1188,22 @@ window_pane_copy_key(struct window_pane *wp, key_code key) } } +void +window_pane_paste(struct window_pane *wp, char *buf, size_t len) +{ + if (!TAILQ_EMPTY(&wp->modes)) + return; + + if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) + return; + + log_debug("%s: %.*s", __func__, (int)len, buf); + bufferevent_write(wp->event, buf, len); + + if (options_get_number(wp->options, "synchronize-panes")) + window_pane_copy_paste(wp, buf, len); +} + int window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, key_code key, struct mouse_event *m) From 1c1f4c121916821e717a2c1fd95c1ad015f1b563 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Oct 2024 08:01:19 +0000 Subject: [PATCH 2/2] Use global cursor style and colour options for modes instead of default, GitHub issue 4117. --- screen.c | 14 ++++++++++++++ tmux.h | 1 + window-copy.c | 1 + window.c | 10 +--------- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/screen.c b/screen.c index d51af6ac..a6a3fadc 100644 --- a/screen.c +++ b/screen.c @@ -170,6 +170,20 @@ screen_reset_tabs(struct screen *s) bit_set(s->tabs, i); } +/* Set default cursor style and colour from options. */ +void +screen_set_default_cursor(struct screen *s, struct options *oo) +{ + int c; + + c = options_get_number(oo, "cursor-colour"); + s->default_ccolour = c; + + c = options_get_number(oo, "cursor-style"); + s->default_mode = 0; + screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode); +} + /* Set screen cursor style and mode. */ void screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle, diff --git a/tmux.h b/tmux.h index d8c720f9..5e86dac9 100644 --- a/tmux.h +++ b/tmux.h @@ -3014,6 +3014,7 @@ void screen_reinit(struct screen *); void screen_free(struct screen *); void screen_reset_tabs(struct screen *); void screen_reset_hyperlinks(struct screen *); +void screen_set_default_cursor(struct screen *, struct options *); void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *); void screen_set_cursor_colour(struct screen *, int); int screen_set_title(struct screen *, const char *); diff --git a/window-copy.c b/window-copy.c index 35818644..d0541657 100644 --- a/window-copy.c +++ b/window-copy.c @@ -417,6 +417,7 @@ window_copy_common_init(struct window_mode_entry *wme) data->jumpchar = NULL; screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); + screen_set_default_cursor(&data->screen, global_w_options); data->modekeys = options_get_number(wp->window->options, "mode-keys"); evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); diff --git a/window.c b/window.c index 6621f89b..06d37453 100644 --- a/window.c +++ b/window.c @@ -1686,15 +1686,7 @@ window_set_fill_character(struct window *w) void window_pane_default_cursor(struct window_pane *wp) { - struct screen *s = wp->screen; - int c; - - c = options_get_number(wp->options, "cursor-colour"); - s->default_ccolour = c; - - c = options_get_number(wp->options, "cursor-style"); - s->default_mode = 0; - screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode); + screen_set_default_cursor(wp->screen, wp->options); } int