From 80c6b487dc05662efd675695b7db93563e582f7c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 31 May 2017 10:15:51 +0000 Subject: Because we defer actually resizing applications (calling TIOCSWINSZ) until the end of the server loop, tmux may have gone through several internal resizes in between. This can be a problem if the final size is the same as the initial size (what the application things it currently is), because the application may choose not to redraw, assuming the screen state is unchanged, when in fact tmux has thrown away parts of the screen, assuming the application will redraw them. To avoid this, do an extra resize if the new size is the same size as the initial size. This should force the application to redraw when tmux needs it to, while retaining the benefits of deferring (so we now resize at most two times instead of at most one - and only two very rarely). Fixes a problem with break-pane and zoomed panes reported by Michal Mazurek. --- server-client.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'server-client.c') diff --git a/server-client.c b/server-client.c index 8de4f6bd..673f1642 100644 --- a/server-client.c +++ b/server-client.c @@ -1050,14 +1050,36 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) if (!(wp->flags & PANE_RESIZE)) return; + /* + * If we are resizing to the same size as when we entered the loop + * (that is, to the same size the application currently thinks it is), + * tmux may have gone through several resizes internally and thrown + * away parts of the screen. So we need the application to actually + * redraw even though its final size has not changed. + */ + if (wp->sx == wp->osx && + wp->sy == wp->osy && + wp->sx > 1 && + wp->sy > 1) { + memset(&ws, 0, sizeof ws); + ws.ws_col = wp->sx - 1; + ws.ws_row = wp->sy - 1; + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) + fatal("ioctl failed"); + log_debug("%s: %%%u forcing resize", __func__, wp->id); + } + memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; ws.ws_row = wp->sy; - if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) fatal("ioctl failed"); + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); wp->flags &= ~PANE_RESIZE; + + wp->osx = wp->sx; + wp->osy = wp->sy; } /* Check if pane should be resized. */ @@ -1068,6 +1090,7 @@ server_client_check_resize(struct window_pane *wp) if (!(wp->flags & PANE_RESIZE)) return; + log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_event, wp); -- cgit From ea6428a5d2c3c753c3123b3ecace7357e97eae50 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 31 May 2017 10:29:15 +0000 Subject: It is not OK to ignore SIGWINCH if SIOCGWINSZ reports the size has unchanged, because it may have changed and changed back in the time between us getting the signal and calling ioctl(). Always redraw when we see SIGWINCH. --- server-client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'server-client.c') diff --git a/server-client.c b/server-client.c index 673f1642..15b0d2dc 100644 --- a/server-client.c +++ b/server-client.c @@ -1451,10 +1451,9 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->flags & CLIENT_CONTROL) break; - if (tty_resize(&c->tty)) { - recalculate_sizes(); - server_redraw_client(c); - } + tty_resize(&c->tty); + recalculate_sizes(); + server_redraw_client(c); if (c->session != NULL) notify_client("client-resized", c); break; -- cgit From d60663ea8664d1c71def883bd64d97af3f791f89 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 31 May 2017 11:00:00 +0000 Subject: Some applications like vi(1) and tmux until 10 minutes or so ago, do not redraw on SIGWINCH if the size returns to the original size between the original SIGWINCH and when they get around to calling TIOCGWINSZ. So use the existing resize timer to introduce a small delay between the two resizes. --- server-client.c | 59 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 19 deletions(-) (limited to 'server-client.c') diff --git a/server-client.c b/server-client.c index 15b0d2dc..e8faedc7 100644 --- a/server-client.c +++ b/server-client.c @@ -1038,6 +1038,44 @@ server_client_loop(void) } } +/* Check if we need to force a resize. */ +static int +server_client_resize_force(struct window_pane *wp) +{ + struct timeval tv = { .tv_usec = 100000 }; + struct winsize ws; + + /* + * If we are resizing to the same size as when we entered the loop + * (that is, to the same size the application currently thinks it is), + * tmux may have gone through several resizes internally and thrown + * away parts of the screen. So we need the application to actually + * redraw even though its final size has not changed. + */ + + if (wp->flags & PANE_RESIZEFORCE) { + wp->flags &= ~PANE_RESIZEFORCE; + return (0); + } + + if (wp->sx != wp->osx || + wp->sy != wp->osy || + wp->sx <= 1 || + wp->sy <= 1) + return (0); + + memset(&ws, 0, sizeof ws); + ws.ws_col = wp->sx; + ws.ws_row = wp->sy - 1; + if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) + fatal("ioctl failed"); + log_debug("%s: %%%u forcing resize", __func__, wp->id); + + evtimer_add(&wp->resize_timer, &tv); + wp->flags |= PANE_RESIZEFORCE; + return (1); +} + /* Resize timer event. */ static void server_client_resize_event(__unused int fd, __unused short events, void *data) @@ -1049,25 +1087,8 @@ server_client_resize_event(__unused int fd, __unused short events, void *data) if (!(wp->flags & PANE_RESIZE)) return; - - /* - * If we are resizing to the same size as when we entered the loop - * (that is, to the same size the application currently thinks it is), - * tmux may have gone through several resizes internally and thrown - * away parts of the screen. So we need the application to actually - * redraw even though its final size has not changed. - */ - if (wp->sx == wp->osx && - wp->sy == wp->osy && - wp->sx > 1 && - wp->sy > 1) { - memset(&ws, 0, sizeof ws); - ws.ws_col = wp->sx - 1; - ws.ws_row = wp->sy - 1; - if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) - fatal("ioctl failed"); - log_debug("%s: %%%u forcing resize", __func__, wp->id); - } + if (server_client_resize_force(wp)) + return; memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; -- cgit