Change pasting to bypass the output key processing entirely and write
authornicm <nicm@openbsd.org>
Tue, 1 Oct 2024 06:15:47 +0000 (06:15 +0000)
committernicm <nicm@openbsd.org>
Tue, 1 Oct 2024 06:15:47 +0000 (06:15 +0000)
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
usr.bin/tmux/input-keys.c
usr.bin/tmux/server-client.c
usr.bin/tmux/session.c
usr.bin/tmux/tmux.h
usr.bin/tmux/tty-keys.c
usr.bin/tmux/window.c

index b49fd82..8ea5d92 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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);
        }
 
index 42b5b23..db37bdf 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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;
 
        /*
index b929c20..6e63416 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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_past(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);
 }
index 548d11c..7777b48 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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);
index 606c23c..19b429d 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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,
index 5109b88..e01f52a 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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:
index 1a08596..dd2f1f8 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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)