From 6c2a6b8206877f2f701d369d7d41c80e581f12b4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Oct 2024 06:15:48 +0000 Subject: 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. --- usr.bin/tmux/cmd-send-keys.c | 8 +++--- usr.bin/tmux/input-keys.c | 9 ++++--- usr.bin/tmux/server-client.c | 64 ++++++++++++++++++++++++++++---------------- usr.bin/tmux/session.c | 9 +++---- usr.bin/tmux/tmux.h | 15 +++++++---- usr.bin/tmux/tty-keys.c | 19 ++++++++----- usr.bin/tmux/window.c | 36 ++++++++++++++++++++++++- 7 files changed, 113 insertions(+), 47 deletions(-) (limited to 'usr.bin/tmux') diff --git a/usr.bin/tmux/cmd-send-keys.c b/usr.bin/tmux/cmd-send-keys.c index b49fd82dd03..8ea5d92badb 100644 --- a/usr.bin/tmux/cmd-send-keys.c +++ b/usr.bin/tmux/cmd-send-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-send-keys.c,v 1.75 2023/01/16 11:26:14 nicm Exp $ */ +/* $OpenBSD: cmd-send-keys.c,v 1.76 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -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/usr.bin/tmux/input-keys.c b/usr.bin/tmux/input-keys.c index 42b5b235ee0..db37bdf171a 100644 --- a/usr.bin/tmux/input-keys.c +++ b/usr.bin/tmux/input-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input-keys.c,v 1.98 2024/08/26 07:45:05 nicm Exp $ */ +/* $OpenBSD: input-keys.c,v 1.99 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c index b929c2002c8..6e63416e9cd 100644 --- a/usr.bin/tmux/server-client.c +++ b/usr.bin/tmux/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.409 2024/09/16 20:28:22 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.410 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -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/usr.bin/tmux/session.c b/usr.bin/tmux/session.c index 548d11c83ef..7777b4843af 100644 --- a/usr.bin/tmux/session.c +++ b/usr.bin/tmux/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.96 2023/09/02 08:38:37 nicm Exp $ */ +/* $OpenBSD: session.c,v 1.97 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 606c23cfe0b..19b429d0f44 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1229 2024/09/30 08:10:20 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1230 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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/usr.bin/tmux/tty-keys.c b/usr.bin/tmux/tty-keys.c index 5109b880d02..e01f52ac53a 100644 --- a/usr.bin/tmux/tty-keys.c +++ b/usr.bin/tmux/tty-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-keys.c,v 1.179 2024/09/30 08:10:20 nicm Exp $ */ +/* $OpenBSD: tty-keys.c,v 1.180 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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/usr.bin/tmux/window.c b/usr.bin/tmux/window.c index 1a085966a34..dd2f1f8b0e6 100644 --- a/usr.bin/tmux/window.c +++ b/usr.bin/tmux/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.292 2024/08/26 07:14:40 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.293 2024/10/01 06:15:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -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) -- cgit v1.2.3