From a443531280c49b5e98291245f29010a2c3336231 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 12 Jan 2026 07:48:36 +0000 Subject: [PATCH 1/2] Remember last pane or type of location for double and triple clicks and correctly handle it changes between first and second or second and third. GitHub issue 4795 from Shaobo Song. --- server-client.c | 61 ++++++++++++++++++++++++++++++------------------- tmux.h | 2 ++ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/server-client.c b/server-client.c index af67a327..c443d4c4 100644 --- a/server-client.c +++ b/server-client.c @@ -315,6 +315,8 @@ server_client_create(int fd) evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); evtimer_set(&c->click_timer, server_client_click_timer, c); + c->click_wp = -1; + TAILQ_INIT(&c->input_requests); TAILQ_INSERT_TAIL(&clients, c, entry); @@ -737,21 +739,17 @@ server_client_check_mouse(struct client *c, struct key_event *event) if (c->flags & CLIENT_DOUBLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; - if (m->b == c->click_button) { - type = SECOND; - x = m->x, y = m->y, b = m->b; - log_debug("second-click at %u,%u", x, y); - c->flags |= CLIENT_TRIPLECLICK; - } + type = SECOND; + x = m->x, y = m->y, b = m->b; + log_debug("second-click at %u,%u", x, y); + c->flags |= CLIENT_TRIPLECLICK; } else if (c->flags & CLIENT_TRIPLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_TRIPLECLICK; - if (m->b == c->click_button) { - type = TRIPLE; - x = m->x, y = m->y, b = m->b; - log_debug("triple-click at %u,%u", x, y); - goto have_event; - } + type = TRIPLE; + x = m->x, y = m->y, b = m->b; + log_debug("triple-click at %u,%u", x, y); + goto have_event; } /* DOWN is the only remaining event type. */ @@ -761,17 +759,6 @@ server_client_check_mouse(struct client *c, struct key_event *event) log_debug("down at %u,%u", x, y); c->flags |= CLIENT_DOUBLECLICK; } - - if (KEYC_CLICK_TIMEOUT != 0) { - memcpy(&c->click_event, m, sizeof c->click_event); - c->click_button = m->b; - - log_debug("click timer started"); - tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; - tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; - evtimer_del(&c->click_timer); - evtimer_add(&c->click_timer, &tv); - } } have_event: @@ -887,6 +874,34 @@ have_event: } } + /* Reset click type or add a click timer if needed. */ + if (type == DOWN || + type == SECOND || + type == TRIPLE) { + if (type != DOWN && + (m->b != c->click_button || + where != (enum mouse_where)c->click_where || + m->wp != c->click_wp)) { + type = DOWN; + log_debug("click sequence reset at %u,%u", x, y); + c->flags &= ~CLIENT_TRIPLECLICK; + c->flags |= CLIENT_DOUBLECLICK; + } + + if (type != TRIPLE && KEYC_CLICK_TIMEOUT != 0) { + memcpy(&c->click_event, m, sizeof c->click_event); + c->click_button = m->b; + c->click_where = where; + c->click_wp = m->wp; + + log_debug("click timer started"); + tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; + tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; + evtimer_del(&c->click_timer); + evtimer_add(&c->click_timer, &tv); + } + } + /* Stop dragging if needed. */ if (type != DRAG && type != WHEEL && diff --git a/tmux.h b/tmux.h index 36549125..a3604935 100644 --- a/tmux.h +++ b/tmux.h @@ -1942,6 +1942,8 @@ struct client { struct event repeat_timer; struct event click_timer; + int click_where; + int click_wp; u_int click_button; struct mouse_event click_event; From 5721767921d78461f2d1f32e58bc51c14442a754 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 12 Jan 2026 07:50:16 +0000 Subject: [PATCH 2/2] Correctly draw indicators when pane-border-indicators is set to both. Reported by Ilya Grigoriev in GitHub issue 4780. --- screen-redraw.c | 103 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 84ab17a6..57ad0107 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -165,7 +165,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, if (px == wp->xoff + wp->sx + sb_w - 1) return (SCREEN_REDRAW_BORDER_RIGHT); } - } else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled*/ + } else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled */ if (wp->xoff == 0 && px == wp->sx + sb_w) if (!hsplit || (hsplit && py <= wp->sy / 2)) return (SCREEN_REDRAW_BORDER_RIGHT); @@ -732,6 +732,78 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, return (&wp->border_gc); } +/* Draw arrow indicator if enabled. */ +static void +screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i, + u_int j, u_int cell_type, struct window_pane *wp, + struct window_pane *active, struct grid_cell *gc) +{ + struct client *c = ctx->c; + struct session *s = c->session; + struct window *w = s->curw->window; + struct options *oo = w->options; + u_int x = ctx->ox + i, y = ctx->oy + j; + int value, arrows = 0, border; + + if (wp == NULL) + return; + if (i != wp->xoff + 1 && j != wp->yoff + 1) + return; + + value = options_get_number(oo, "pane-border-indicators"); + if (value != PANE_BORDER_ARROWS && value != PANE_BORDER_BOTH) + return; + + border = screen_redraw_pane_border(ctx, active, x, y); + if (border == SCREEN_REDRAW_INSIDE) + return; + + if (i == wp->xoff + 1) { + if (border == SCREEN_REDRAW_OUTSIDE) { + if (screen_redraw_two_panes(wp->window, 1)) { + if (active == TAILQ_FIRST(&w->panes)) + border = SCREEN_REDRAW_BORDER_BOTTOM; + else + border = SCREEN_REDRAW_BORDER_TOP; + arrows = 1; + } + } else { + if (cell_type == CELL_LEFTRIGHT) + arrows = 1; + else if (cell_type == CELL_TOPJOIN && + border == SCREEN_REDRAW_BORDER_BOTTOM) + arrows = 1; + else if (cell_type == CELL_BOTTOMJOIN && + border == SCREEN_REDRAW_BORDER_TOP) + arrows = 1; + } + } + if (j == wp->yoff + 1) { + if (border == SCREEN_REDRAW_OUTSIDE) { + if (screen_redraw_two_panes(wp->window, 0)) { + if (active == TAILQ_FIRST(&w->panes)) + border = SCREEN_REDRAW_BORDER_RIGHT; + else + border = SCREEN_REDRAW_BORDER_LEFT; + arrows = 1; + } + } else { + if (cell_type == CELL_TOPBOTTOM) + arrows = 1; + else if (cell_type == CELL_LEFTJOIN && + border == SCREEN_REDRAW_BORDER_RIGHT) + arrows = 1; + else if (cell_type == CELL_RIGHTJOIN && + border == SCREEN_REDRAW_BORDER_LEFT) + arrows = 1; + } + } + if (arrows) { + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, BORDER_MARKERS[border]); + } +} + /* Draw a border cell. */ static void screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) @@ -747,7 +819,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) const struct grid_cell *tmp; struct overlay_ranges r; u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; - int arrows = 0, border, isolates; + int isolates; if (c->overlay_check != NULL) { c->overlay_check(c, c->overlay_data, x, y, 1, &r); @@ -795,32 +867,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) if (isolates) tty_puts(tty, END_ISOLATE); - switch (options_get_number(oo, "pane-border-indicators")) { - case PANE_BORDER_ARROWS: - case PANE_BORDER_BOTH: - arrows = 1; - break; - } - - if (wp != NULL && arrows) { - border = screen_redraw_pane_border(ctx, active, x, y); - if (((i == wp->xoff + 1 && - (cell_type == CELL_LEFTRIGHT || - (cell_type == CELL_TOPJOIN && - border == SCREEN_REDRAW_BORDER_BOTTOM) || - (cell_type == CELL_BOTTOMJOIN && - border == SCREEN_REDRAW_BORDER_TOP))) || - (j == wp->yoff + 1 && - (cell_type == CELL_TOPBOTTOM || - (cell_type == CELL_LEFTJOIN && - border == SCREEN_REDRAW_BORDER_RIGHT) || - (cell_type == CELL_RIGHTJOIN && - border == SCREEN_REDRAW_BORDER_LEFT)))) && - screen_redraw_check_is(ctx, x, y, active)) { - gc.attr |= GRID_ATTR_CHARSET; - utf8_set(&gc.data, BORDER_MARKERS[border]); - } - } + screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc); tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); if (isolates)