Skip to content

Commit

Permalink
Change pasting to bypass the output key processing entirely and write
Browse files Browse the repository at this point in the history
what was originally received. Fixes problems with pasted text being
interpreted as extended keys reported by Mark Kelly.
  • Loading branch information
nicm committed Oct 1, 2024
1 parent 89adec0 commit 17bab32
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 40 deletions.
6 changes: 4 additions & 2 deletions cmd-send-keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
7 changes: 5 additions & 2 deletions input-keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down
62 changes: 40 additions & 22 deletions server-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down Expand Up @@ -1801,44 +1799,48 @@ server_client_check_mouse(struct client *c, struct key_event *event)

/* 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);
}

/* 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);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2104,10 +2110,20 @@ server_client_key_callback(struct cmdq_item *item, void *data)
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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
7 changes: 2 additions & 5 deletions session.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 9 additions & 4 deletions tmux.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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| \
Expand Down Expand Up @@ -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,
Expand Down
17 changes: 12 additions & 5 deletions tty-keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -944,9 +944,6 @@ tty_keys_next(struct tty *tty)
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);
Expand All @@ -965,13 +962,23 @@ tty_keys_next(struct tty *tty)

/* 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:
Expand Down
34 changes: 34 additions & 0 deletions window.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
Expand Down

0 comments on commit 17bab32

Please sign in to comment.