From 9a1df7bb036cffbef206a3f2dbe46559b6f15d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 17 Jan 2021 16:12:54 +0100 Subject: [PATCH] render: delay TIOCSWINSZ while doing an interactive resize Instead of disabling content centering, delay the TIOCSWINSZ (a.k.a delay sending the new dimensions to the client) by a small amount while doing an interactive resize. Non-interactive resizes are still immediate. For now, force a resize when the user stops the interactive resize. This ensures the client application receives the new dimensions immediately. It still works without the last, forced, resize, but there typically be a small delay until the client application receives the final dimensions. Closes #301 Closes #283 --- config.c | 14 ++++++++ config.h | 1 + render.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++------- wayland.c | 17 +++++++++ wayland.h | 2 ++ 5 files changed, 126 insertions(+), 12 deletions(-) diff --git a/config.c b/config.c index 81b329cf..7c6b6148 100644 --- a/config.c +++ b/config.c @@ -1745,6 +1745,19 @@ parse_section_tweak( conf->tweak.box_drawing_base_thickness); } + else if (strcmp(key, "resize-delay-ms") == 0) { + unsigned long ms; + if (!str_to_ulong(value, 10, &ms)) { + LOG_AND_NOTIFY_ERR( + "%s:%d: [tweak]: resize-delay-ms: expected an integer, got '%s'", + path, lineno, value); + return false; + } + + conf->tweak.resize_delay_ms = ms; + LOG_WARN("tweak: resize-delay-ms=%hu", conf->tweak.resize_delay_ms); + } + else { LOG_AND_NOTIFY_ERR("%s:%u: [tweak]: %s: invalid key", path, lineno, key); return false; @@ -2166,6 +2179,7 @@ config_load(struct config *conf, const char *conf_path, .render_timer_log = false, .damage_whole_window = false, .box_drawing_base_thickness = 0.04, + .resize_delay_ms = 100, }, .notifications = tll_init(), diff --git a/config.h b/config.h index 1c349f2a..6dadb1a4 100644 --- a/config.h +++ b/config.h @@ -203,6 +203,7 @@ struct config { uint64_t delayed_render_upper_ns; off_t max_shm_pool_size; float box_drawing_base_thickness; + uint16_t resize_delay_ms; } tweak; user_notifications_t notifications; diff --git a/render.c b/render.c index 8ad5ee0c..04986426 100644 --- a/render.c +++ b/render.c @@ -1,10 +1,12 @@ #include "render.h" #include +#include #include #include #include +#include #include #if __has_include() #include @@ -2545,6 +2547,92 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da grid_render(term); } +static void +tiocswinsz(struct terminal *term) +{ + if (term->ptmx >= 0) { + if (ioctl(term->ptmx, (unsigned int)TIOCSWINSZ, + &(struct winsize){ + .ws_row = term->rows, + .ws_col = term->cols, + .ws_xpixel = term->cols * term->cell_width, + .ws_ypixel = term->rows * term->cell_height}) < 0) + { + LOG_ERRNO("TIOCSWINSZ"); + } + } +} + +static bool +fdm_tiocswinsz(struct fdm *fdm, int fd, int events, void *data) +{ + struct terminal *term = data; + + if (events & EPOLLIN) { + tiocswinsz(term); + LOG_WARN("DELAYED"); + } + + fdm_del(fdm, fd); + term->window->resize_timeout_fd = -1; + return true; +} + +static void +send_dimensions_to_client(struct terminal *term) +{ + struct wl_window *win = term->window; + + if (!win->is_resizing || term->conf->tweak.resize_delay_ms == 0) { + /* Send new dimensions to client immediately */ + tiocswinsz(term); + LOG_ERR("IMMEDIATELY"); + + /* And make sure to reset and deallocate a lingering timer */ + if (win->resize_timeout_fd >= 0) { + fdm_del(term->fdm, win->resize_timeout_fd); + win->resize_timeout_fd = -1; + } + } else { + /* Send new dimensions to client “in a while” */ + assert(win->is_resizing && term->conf->tweak.resize_delay_ms > 0); + + int fd = win->resize_timeout_fd; + uint16_t delay_ms = term->conf->tweak.resize_delay_ms; + bool successfully_scheduled = false; + + if (fd < 0) { + /* Lazy create timer fd */ + fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + if (fd < 0) + LOG_ERRNO("failed to create TIOCSWINSZ timer"); + else if (!fdm_add(term->fdm, fd, EPOLLIN, &fdm_tiocswinsz, term)) { + close(fd); + fd = -1; + } + + win->resize_timeout_fd = fd; + } + + if (fd >= 0) { + /* Reset tiemout */ + const struct itimerspec timeout = { + .it_value = {.tv_sec = 0, .tv_nsec = delay_ms * 1000000}, + }; + + if (timerfd_settime(fd, 0, &timeout, NULL) < 0) { + LOG_ERRNO("failed to arm TIOCSWINSZ timer"); + fdm_del(term->fdm, fd); + win->resize_timeout_fd = -1; + } else + successfully_scheduled = true; + } + + if (!successfully_scheduled) + tiocswinsz(term); + } +} + /* Move to terminal.c? */ static bool maybe_resize(struct terminal *term, int width, int height, bool force) @@ -2671,7 +2759,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force) const int total_x_pad = term->width - grid_width; const int total_y_pad = term->height - grid_height; - if (term->conf->center && !term->window->is_resizing) { + if (term->conf->center) { term->margins.left = total_x_pad / 2; term->margins.top = total_y_pad / 2; } else { @@ -2723,17 +2811,6 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->width, term->height, term->cols, term->rows, term->margins.left, term->margins.right, term->margins.top, term->margins.bottom); - /* Signal TIOCSWINSZ */ - if (term->ptmx >= 0 && ioctl(term->ptmx, (unsigned int)TIOCSWINSZ, - &(struct winsize){ - .ws_row = term->rows, - .ws_col = term->cols, - .ws_xpixel = term->cols * term->cell_width, - .ws_ypixel = term->rows * term->cell_height}) == -1) - { - LOG_ERRNO("TIOCSWINSZ"); - } - if (term->scroll_region.start >= term->rows) term->scroll_region.start = 0; @@ -2743,6 +2820,9 @@ maybe_resize(struct terminal *term, int width, int height, bool force) term->render.last_cursor.row = NULL; damage_view: + /* Signal TIOCSWINSZ */ + send_dimensions_to_client(term); + if (!term->window->is_maximized && !term->window->is_fullscreen && !term->window->is_tiled) diff --git a/wayland.c b/wayland.c index 4e8a8ca8..ec113552 100644 --- a/wayland.c +++ b/wayland.c @@ -677,9 +677,22 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, term->window->frame_callback = NULL; } +#if 1 + /* + * TODO: decide if we should to the last “forced” call when ending + * an interactive resize. + * + * Without it, the last TIOCSWINSZ sent to the client will be a + * scheduled one. I.e. there will be a small delay after the user + * has *stopped* resizing, and the client application receives the + * final size. + */ bool resized = was_resizing && !win->is_resizing ? render_resize_force(term, win->configure.width, win->configure.height) : render_resize(term, win->configure.width, win->configure.height); +#else + bool resized = render_resize(term, win->configure.width, win->configure.height); +#endif if (win->configure.is_activated) term_visual_focus_in(term); @@ -1278,6 +1291,7 @@ wayl_win_init(struct terminal *term) win->term = term; win->use_csd = CSD_UNKNOWN; win->csd.move_timeout_fd = -1; + win->resize_timeout_fd = -1; win->surface = wl_compositor_create_surface(wayl->compositor); if (win->surface == NULL) { @@ -1430,6 +1444,9 @@ wayl_win_destroy(struct wl_window *win) wl_surface_destroy(win->surface); wayl_roundtrip(win->term->wl); + + if (win->resize_timeout_fd >= 0) + fdm_del(win->term->wl->fdm, win->resize_timeout_fd); free(win); } diff --git a/wayland.h b/wayland.h index d77e776b..b849249e 100644 --- a/wayland.h +++ b/wayland.h @@ -390,6 +390,8 @@ struct wl_window { bool is_tiled_left:1; bool is_tiled_right:1; } configure; + + int resize_timeout_fd; }; struct config;