Change resize timers and flags into one timer and a queue which is
authornicm <nicm@openbsd.org>
Thu, 10 Jun 2021 07:33:41 +0000 (07:33 +0000)
committernicm <nicm@openbsd.org>
Thu, 10 Jun 2021 07:33:41 +0000 (07:33 +0000)
simpler and fixes problems with vim when resized multiple times. GitHub
issue 2677.

usr.bin/tmux/server-client.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/window.c

index 03b622e..5e51221 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server-client.c,v 1.373 2021/06/10 07:21:46 nicm Exp $ */
+/* $OpenBSD: server-client.c,v 1.374 2021/06/10 07:33:41 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1450,84 +1450,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)
 {
-       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);
+       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 (~wp->flags & PANE_RESIZE)
+       if (TAILQ_EMPTY(&wp->resize_queue))
                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);
+       if (!event_initialized(&wp->resize_timer))
+               evtimer_set(&wp->resize_timer, server_client_resize_timer, wp);
+       if (evtimer_pending(&wp->resize_timer, NULL))
                return;
+
+       log_debug("%s: %%%u needs to be resized", __func__, wp->id);
+       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)
index a89476f..eb927b1 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.837 2021/06/10 07:28:45 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.838 2021/06/10 07:33:41 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -2953,6 +2953,8 @@ Ctrl keys may be prefixed with
 .Ql C-
 or
 .Ql ^ ,
+Shift keys with
+.Ql S-
 and Alt (meta) with
 .Ql M- .
 In addition, the following special key names are accepted:
index 82c8771..21e522a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1104 2021/06/10 07:28:45 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1105 2021/06/10 07:33:41 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -918,7 +918,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. */
@@ -926,6 +926,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;
@@ -950,8 +962,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
@@ -960,7 +972,6 @@ struct window_pane {
 #define PANE_STATUSDRAWN 0x400
 #define PANE_EMPTY 0x800
 #define PANE_STYLECHANGED 0x1000
-#define PANE_RESIZENOW 0x2000
 
        int              argc;
        char           **argv;
@@ -977,8 +988,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;
 
@@ -996,7 +1007,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;
@@ -2755,7 +2766,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);
index d0c4e2d..c267b43 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.271 2021/06/10 07:24:45 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.272 2021/06/10 07:33:41 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -427,25 +427,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;
@@ -860,29 +853,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;
@@ -898,6 +881,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);
 
@@ -919,8 +905,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);
 
@@ -989,9 +977,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;
 
@@ -1001,14 +998,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