From cf6034da92df1dec0a2ea6230bd7a424d0d181a8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 28 Apr 2021 09:16:30 +0100 Subject: [PATCH] Change resize timers and flags into one timer and a queue to fix problems with vim when resized multiple times. GitHub issue 2677. --- server-client.c | 117 +++++++++++++++++++++++------------------------- tmux.h | 25 ++++++++--- window.c | 51 +++++++++------------ 3 files changed, 94 insertions(+), 99 deletions(-) diff --git a/server-client.c b/server-client.c index 0f8ad687..e74d893d 100644 --- a/server-client.c +++ b/server-client.c @@ -1447,84 +1447,79 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) evtimer_del(&wp->resize_timer); } -/* Start the resize timer. */ -static void -server_client_start_resize_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 250000 }; - - log_debug("%s: %%%u resize timer started", __func__, wp->id); - evtimer_add(&wp->resize_timer, &tv); -} - -/* Force timer event. */ -static void -server_client_force_timer(__unused int fd, __unused short events, void *data) -{ - struct window_pane *wp = data; - - log_debug("%s: %%%u force timer expired", __func__, wp->id); - evtimer_del(&wp->force_timer); - wp->flags |= PANE_RESIZENOW; -} - -/* Start the force timer. */ -static void -server_client_start_force_timer(struct window_pane *wp) -{ - struct timeval tv = { .tv_usec = 10000 }; - - log_debug("%s: %%%u force timer started", __func__, wp->id); - evtimer_add(&wp->force_timer, &tv); -} - /* Check if pane should be resized. */ static void server_client_check_pane_resize(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + struct window_pane_resize *first; + struct window_pane_resize *last; + struct timeval tv = { .tv_usec = 250000 }; + + if (TAILQ_EMPTY(&wp->resize_queue)) + return; + if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_timer, wp); - if (!event_initialized(&wp->force_timer)) - evtimer_set(&wp->force_timer, server_client_force_timer, wp); - - if (~wp->flags & PANE_RESIZE) + if (evtimer_pending(&wp->resize_timer, NULL)) return; + log_debug("%s: %%%u needs to be resized", __func__, wp->id); - - if (evtimer_pending(&wp->resize_timer, NULL)) { - log_debug("%s: %%%u resize timer is running", __func__, wp->id); - return; + TAILQ_FOREACH(r, &wp->resize_queue, entry) { + log_debug("queued resize: %ux%u -> %ux%u", r->osx, r->osy, + r->sx, r->sy); } - server_client_start_resize_timer(wp); - if (~wp->flags & PANE_RESIZEFORCE) { - /* - * The timer is not running and we don't need to force a - * resize, so just resize immediately. - */ - log_debug("%s: resizing %%%u now", __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~PANE_RESIZE; + /* + * There are three cases that matter: + * + * - Only one resize. It can just be applied. + * + * - Multiple resizes and the ending size is different from the + * starting size. We can discard all resizes except the most recent. + * + * - Multiple resizes and the ending size is the same as the starting + * size. We must resize at least twice to force the application to + * redraw. So apply the first and leave the last on the queue for + * next time. + */ + first = TAILQ_FIRST(&wp->resize_queue); + last = TAILQ_LAST(&wp->resize_queue, window_pane_resizes); + if (first == last) { + /* Only one resize. */ + window_pane_send_resize(wp, first->sx, first->sy); + TAILQ_REMOVE(&wp->resize_queue, first, entry); + free(first); + } else if (last->sx != first->osx || last->sy != first->osy) { + /* Multiple resizes ending up with a different size. */ + window_pane_send_resize(wp, last->sx, last->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } } else { /* - * The timer is not running, but we need to force a resize. If - * the force timer has expired, resize to the real size now. - * Otherwise resize to the force size and start the timer. + * Multiple resizes ending up with the same size. There will + * not be more than one to the same size in succession so we + * can just use the last-but-one on the list and leave the last + * for later. We reduce the time until the next check to avoid + * a long delay between the resizes. */ - if (wp->flags & PANE_RESIZENOW) { - log_debug("%s: resizing %%%u after forced resize", - __func__, wp->id); - window_pane_send_resize(wp, 0); - wp->flags &= ~(PANE_RESIZE|PANE_RESIZEFORCE|PANE_RESIZENOW); - } else if (!evtimer_pending(&wp->force_timer, NULL)) { - log_debug("%s: forcing resize of %%%u", __func__, - wp->id); - window_pane_send_resize(wp, 1); - server_client_start_force_timer(wp); + r = TAILQ_PREV(last, window_pane_resizes, entry); + window_pane_send_resize(wp, r->sx, r->sy); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + if (r == last) + break; + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); } + tv.tv_usec = 10000; } + evtimer_add(&wp->resize_timer, &tv); } + /* Check pane buffer size. */ static void server_client_check_pane_buffer(struct window_pane *wp) diff --git a/tmux.h b/tmux.h index a67b30b9..1e496e6f 100644 --- a/tmux.h +++ b/tmux.h @@ -919,7 +919,7 @@ struct window_mode_entry { struct screen *screen; u_int prefix; - TAILQ_ENTRY (window_mode_entry) entry; + TAILQ_ENTRY(window_mode_entry) entry; }; /* Offsets into pane buffer. */ @@ -927,6 +927,18 @@ struct window_pane_offset { size_t used; }; +/* Queued pane resize. */ +struct window_pane_resize { + u_int sx; + u_int sy; + + u_int osx; + u_int osy; + + TAILQ_ENTRY(window_pane_resize) entry; +}; +TAILQ_HEAD(window_pane_resizes, window_pane_resize); + /* Child window structure. */ struct window_pane { u_int id; @@ -951,8 +963,8 @@ struct window_pane { #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 -#define PANE_RESIZE 0x8 -#define PANE_RESIZEFORCE 0x10 +/* 0x8 unused */ +/* 0x10 unused */ #define PANE_FOCUSPUSH 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 @@ -961,7 +973,6 @@ struct window_pane { #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 -#define PANE_RESIZENOW 0x2000 int argc; char **argv; @@ -978,8 +989,8 @@ struct window_pane { struct window_pane_offset offset; size_t base_offset; + struct window_pane_resizes resize_queue; struct event resize_timer; - struct event force_timer; struct input_ctx *ictx; @@ -997,7 +1008,7 @@ struct window_pane { struct screen status_screen; size_t status_size; - TAILQ_HEAD (, window_mode_entry) modes; + TAILQ_HEAD(, window_mode_entry) modes; char *searchstr; int searchregex; @@ -2756,7 +2767,7 @@ void window_redraw_active_switch(struct window *, struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int, int, int); -void window_pane_send_resize(struct window_pane *, int); +void window_pane_send_resize(struct window_pane *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); int window_push_zoom(struct window *, int, int); diff --git a/window.c b/window.c index d8cff590..f21a4d5f 100644 --- a/window.c +++ b/window.c @@ -425,25 +425,18 @@ window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) } void -window_pane_send_resize(struct window_pane *wp, int force) +window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window *w = wp->window; struct winsize ws; - u_int sy; if (wp->fd == -1) return; - if (!force) - sy = wp->sy; - else if (wp->sy <= 1) - sy = wp->sy + 1; - else - sy = wp->sy - 1; - log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, sy); + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy); memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx; + ws.ws_col = sx; ws.ws_row = sy; ws.ws_xpixel = w->xpixel * ws.ws_col; ws.ws_ypixel = w->ypixel * ws.ws_row; @@ -867,29 +860,19 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->argc = 0; - wp->argv = NULL; - wp->shell = NULL; - wp->cwd = NULL; - wp->fd = -1; - wp->event = NULL; wp->fg = 8; wp->bg = 8; TAILQ_INIT(&wp->modes); - wp->layout_cell = NULL; - - wp->xoff = 0; - wp->yoff = 0; + TAILQ_INIT (&wp->resize_queue); wp->sx = sx; wp->sy = sy; wp->pipe_fd = -1; - wp->pipe_event = NULL; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -905,6 +888,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) static void window_pane_destroy(struct window_pane *wp) { + struct window_pane_resize *r; + struct window_pane_resize *r1; + window_pane_reset_mode_all(wp); free(wp->searchstr); @@ -929,8 +915,10 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); - if (event_initialized(&wp->force_timer)) - event_del(&wp->force_timer); + TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -999,9 +987,18 @@ void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; + struct window_pane_resize *r; if (sx == wp->sx && sy == wp->sy) return; + + r = xmalloc (sizeof *r); + r->sx = sx; + r->sy = sy; + r->osx = wp->sx; + r->osy = wp->sy; + TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry); + wp->sx = sx; wp->sy = sy; @@ -1011,14 +1008,6 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); - - /* - * If the pane has already been resized, set the force flag and make - * the application resize twice to force it to redraw. - */ - if (wp->flags & PANE_RESIZE) - wp->flags |= PANE_RESIZEFORCE; - wp->flags |= PANE_RESIZE; } void