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.
This commit is contained in:
nicm 2024-10-01 06:15:47 +00:00
parent 89adec0ca5
commit 17bab32794
7 changed files with 106 additions and 40 deletions

View File

@ -73,11 +73,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
if (args_has(args, 'K')) { if (args_has(args, 'K')) {
if (tc == NULL) if (tc == NULL)
return (item); return (item);
event = xmalloc(sizeof *event); event = xcalloc(1, sizeof *event);
event->key = key|KEYC_SENT; event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m); 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); free(event);
}
return (item); return (item);
} }

View File

@ -499,9 +499,12 @@ input_key_vt10x(struct bufferevent *bev, key_code key)
return (0); 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; onlykey = key & KEYC_MASK_KEY;
if (onlykey == '\r' || onlykey == '\t') if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
key &= ~KEYC_CTRL; key &= ~KEYC_CTRL;
/* /*

View File

@ -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_title(struct client *);
static void server_client_set_path(struct client *); static void server_client_set_path(struct client *);
static void server_client_reset_state(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_update_latest(struct client *);
static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch(struct imsg *, void *);
@ -1801,18 +1799,18 @@ out:
/* Is this a bracket paste key? */ /* Is this a bracket paste key? */
static int 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) { if (key == KEYC_PASTE_START) {
c->flags |= CLIENT_BRACKETPASTING; c->flags |= CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste on", c->name); log_debug("%s: bracket paste on", c->name);
return (1); return (0);
} }
if (key == KEYC_PASTE_END) { if (key == KEYC_PASTE_END) {
c->flags &= ~CLIENT_BRACKETPASTING; c->flags &= ~CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste off", c->name); log_debug("%s: bracket paste off", c->name);
return (1); return (0);
} }
return !!(c->flags & CLIENT_BRACKETPASTING); 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? */ /* Is this fast enough to probably be a paste? */
static int static int
server_client_assume_paste(struct session *s) server_client_is_assume_paste(struct client *c)
{ {
struct timeval tv; struct session *s = c->session;
int t; struct timeval tv;
int t;
if (c->flags & CLIENT_BRACKETPASTING)
return (0);
if ((t = options_get_number(s->options, "assume-paste-time")) == 0) if ((t = options_get_number(s->options, "assume-paste-time")) == 0)
return (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) { if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) {
log_debug("session %s pasting (flag %d)", s->name, if (c->flags & CLIENT_ASSUMEPASTING)
!!(s->flags & SESSION_PASTING));
if (s->flags & SESSION_PASTING)
return (1); return (1);
s->flags |= SESSION_PASTING; c->flags |= CLIENT_ASSUMEPASTING;
log_debug("%s: assume paste on", c->name);
return (0); return (0);
} }
log_debug("session %s not pasting", s->name); if (c->flags & CLIENT_ASSUMEPASTING) {
s->flags &= ~SESSION_PASTING; c->flags &= ~CLIENT_ASSUMEPASTING;
log_debug("%s: assume paste off", c->name);
}
return (0); return (0);
} }
@ -1891,6 +1893,8 @@ server_client_key_callback(struct cmdq_item *item, void *data)
wl = s->curw; wl = s->curw;
/* Update the activity timer. */ /* Update the activity timer. */
memcpy(&c->last_activity_time, &c->activity_time,
sizeof c->last_activity_time);
if (gettimeofday(&c->activity_time, NULL) != 0) if (gettimeofday(&c->activity_time, NULL) != 0)
fatal("gettimeofday failed"); fatal("gettimeofday failed");
session_update_activity(s, &c->activity_time); session_update_activity(s, &c->activity_time);
@ -1928,14 +1932,16 @@ server_client_key_callback(struct cmdq_item *item, void *data)
goto forward_key; goto forward_key;
/* Forward if bracket pasting. */ /* Forward if bracket pasting. */
if (server_client_is_bracket_pasting(c, key)) if (server_client_is_bracket_paste (c, key))
goto forward_key; goto paste_key;
/* Treat everything as a regular key when pasting is detected. */ /* Treat everything as a regular key when pasting is detected. */
if (!KEYC_IS_MOUSE(key) && if (!KEYC_IS_MOUSE(key) &&
key != KEYC_FOCUS_IN &&
key != KEYC_FOCUS_OUT &&
(~key & KEYC_SENT) && (~key & KEYC_SENT) &&
server_client_assume_paste(s)) server_client_is_assume_paste(c))
goto forward_key; goto paste_key;
/* /*
* Work out the current key table. If the pane is in a mode, use * Work out the current key table. If the pane is in a mode, use
@ -2104,10 +2110,20 @@ forward_key:
goto out; goto out;
if (wp != NULL) if (wp != NULL)
window_pane_key(wp, c, s, wl, key, m); 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: out:
if (s != NULL && key != KEYC_FOCUS_OUT) if (s != NULL && key != KEYC_FOCUS_OUT)
server_client_update_latest(c); server_client_update_latest(c);
free(event->buf);
free(event); free(event);
return (CMD_RETURN_NORMAL); 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 * Waiting for a third click that hasn't happened, so this must
* have been a double click. * have been a double click.
*/ */
event = xmalloc(sizeof *event); event = xcalloc(1, sizeof *event);
event->key = KEYC_DOUBLECLICK; event->key = KEYC_DOUBLECLICK;
memcpy(&event->m, &c->click_event, sizeof event->m); 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); free(event);
}
} }
c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK);
} }

View File

@ -272,19 +272,16 @@ session_lock_timer(__unused int fd, __unused short events, void *arg)
void void
session_update_activity(struct session *s, struct timeval *from) session_update_activity(struct session *s, struct timeval *from)
{ {
struct timeval *last = &s->last_activity_time;
struct timeval tv; struct timeval tv;
memcpy(last, &s->activity_time, sizeof *last);
if (from == NULL) if (from == NULL)
gettimeofday(&s->activity_time, NULL); gettimeofday(&s->activity_time, NULL);
else else
memcpy(&s->activity_time, from, sizeof s->activity_time); 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, s->name, (long long)s->activity_time.tv_sec,
(int)s->activity_time.tv_usec, (long long)last->tv_sec, (int)s->activity_time.tv_usec);
(int)last->tv_usec);
if (evtimer_initialized(&s->lock_timer)) if (evtimer_initialized(&s->lock_timer))
evtimer_del(&s->lock_timer); evtimer_del(&s->lock_timer);

13
tmux.h
View File

@ -1311,8 +1311,7 @@ struct session {
struct options *options; struct options *options;
#define SESSION_PASTING 0x1 #define SESSION_ALERTED 0x1
#define SESSION_ALERTED 0x2
int flags; int flags;
u_int attached; u_int attached;
@ -1390,8 +1389,11 @@ struct mouse_event {
/* Key event. */ /* Key event. */
struct key_event { struct key_event {
key_code key; key_code key;
struct mouse_event m; struct mouse_event m;
char *buf;
size_t len;
}; };
/* Terminal definition. */ /* Terminal definition. */
@ -1806,6 +1808,7 @@ struct client {
struct timeval creation_time; struct timeval creation_time;
struct timeval activity_time; struct timeval activity_time;
struct timeval last_activity_time;
struct environ *environ; struct environ *environ;
struct format_job_tree *jobs; struct format_job_tree *jobs;
@ -1872,6 +1875,7 @@ struct client {
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL #define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
@ -3097,6 +3101,7 @@ void window_pane_reset_mode_all(struct window_pane *);
int window_pane_key(struct window_pane *, struct client *, int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code, struct session *, struct winlink *, key_code,
struct mouse_event *); struct mouse_event *);
void window_pane_paste(struct window_pane *, char *, size_t);
int window_pane_visible(struct window_pane *); int window_pane_visible(struct window_pane *);
int window_pane_exited(struct window_pane *); int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int, u_int window_pane_search(struct window_pane *, const char *, int,

View File

@ -944,9 +944,6 @@ complete_key:
if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace) if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace)
key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE; key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE;
/* Remove data from buffer. */
evbuffer_drain(tty->in, size);
/* Remove key timer. */ /* Remove key timer. */
if (event_initialized(&tty->key_timer)) if (event_initialized(&tty->key_timer))
evtimer_del(&tty->key_timer); evtimer_del(&tty->key_timer);
@ -965,13 +962,23 @@ complete_key:
/* Fire the key. */ /* Fire the key. */
if (key != KEYC_UNKNOWN) { if (key != KEYC_UNKNOWN) {
event = xmalloc(sizeof *event); event = xcalloc(1, sizeof *event);
event->key = key; event->key = key;
memcpy(&event->m, &m, sizeof event->m); 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); free(event);
}
} }
/* Remove data from buffer. */
evbuffer_drain(tty->in, size);
return (1); return (1);
discard_key: discard_key:

View File

@ -1154,6 +1154,24 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(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 static void
window_pane_copy_key(struct window_pane *wp, key_code key) 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 int
window_pane_key(struct window_pane *wp, struct client *c, struct session *s, window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct winlink *wl, key_code key, struct mouse_event *m) struct winlink *wl, key_code key, struct mouse_event *m)