Merge branch 'delay-tiocswinsz-on-interactive-resize'

Closes #283
Closes #301
This commit is contained in:
Daniel Eklöf 2021-01-26 20:32:04 +01:00
commit c1675af103
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
8 changed files with 169 additions and 26 deletions

View file

@ -63,6 +63,11 @@
* The scrollback search box no longer accepts non-printable characters.
* Non-formatting C0 control characters, `BS`, `HT` and `DEL` are now
stripped from pasted text.
* While doing an interactive resize of a foot window, foot now
requires 100ms of idle time (where the window size does not change)
before sending the new dimensions to the client application. The
timing can be tweaked, or completely disabled, by setting
`resize-delay-ms` (https://codeberg.org/dnkl/foot/issues/301).
### Deprecated

View file

@ -523,6 +523,19 @@ parse_section_main(const char *key, const char *value, struct config *conf,
conf->center = center;
}
else if (strcmp(key, "resize-delay-ms") == 0) {
unsigned long ms;
if (!str_to_ulong(value, 10, &ms)) {
LOG_AND_NOTIFY_ERR(
"%s:%d: [default]: resize-delay-ms: "
"expected an integer, got '%s'",
path, lineno, value);
return false;
}
conf->resize_delay_ms = ms;
}
else if (strcmp(key, "bold-text-in-bright") == 0)
conf->bold_in_bright = str_to_bool(value);
@ -2080,6 +2093,7 @@ config_load(struct config *conf, const char *conf_path,
},
.pad_x = 2,
.pad_y = 2,
.resize_delay_ms = 100,
.bold_in_bright = false,
.bell_action = BELL_ACTION_NONE,
.startup_mode = STARTUP_WINDOWED,

View file

@ -77,6 +77,7 @@ struct config {
unsigned pad_x;
unsigned pad_y;
bool center;
uint16_t resize_delay_ms;
bool bold_in_bright;
enum {

View file

@ -131,6 +131,25 @@ in this order:
Default: _2x2_.
*resize-delay-ms*
Time, in milliseconds, of "idle time" "before foot sends the new
window dimensions to the client application while doing an
interactive resize of a foot window. Idle time in this context is
a period of time where the window size is not changing.
In other words, while you are fiddling with the window size, foot
does not send the updated dimensions to the client. Only when you
pause the fiddling for *relay-size-ms* milliseconds is the client
updated.
Emphasis is on _while_ here; as soon as the interactive resize
ends (i.e. when you let go of the window border), the final
dimensions is sent to the client, without any delays.
Setting it to 0 disables the delay completely.
Default: _100_.
*initial-window-size-pixels*
Initial window width and height in _pixels_ (subject to output
scaling), on the form _WIDTHxHEIGHT_. The height _includes_ the

View file

@ -18,6 +18,7 @@
# initial-window-size-chars=<COLSxROWS>
# initial-window-mode=windowed
# pad=2x2 # optionally append 'center'
# resize-delay-ms=100
# bold-text-in-bright=no
# bell=none

101
render.c
View file

@ -1,10 +1,12 @@
#include "render.h"
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <pthread.h>
#if __has_include(<pthread_np.h>)
#include <pthread_np.h>
@ -2545,6 +2547,89 @@ 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);
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->resize_delay_ms == 0) {
/* Send new dimensions to client immediately */
tiocswinsz(term);
/* 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->resize_delay_ms > 0);
int fd = win->resize_timeout_fd;
uint16_t delay_ms = term->conf->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 timeout */
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 +2756,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) {
if (term->conf->center && !term->window->is_resizing) {
term->margins.left = total_x_pad / 2;
term->margins.top = total_y_pad / 2;
} else {
@ -2723,17 +2808,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 +2817,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)

View file

@ -535,6 +535,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
bool is_activated = false;
bool is_fullscreen = false;
bool is_maximized = false;
bool is_resizing = false;
bool is_tiled_top = false;
bool is_tiled_bottom = false;
bool is_tiled_left = false;
@ -566,11 +567,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
case XDG_TOPLEVEL_STATE_TILED_RIGHT: is_tiled_right = true; break;
case XDG_TOPLEVEL_STATE_TILED_TOP: is_tiled_top = true; break;
case XDG_TOPLEVEL_STATE_TILED_BOTTOM: is_tiled_bottom = true; break;
case XDG_TOPLEVEL_STATE_RESIZING:
/* Ignored */
/* TODO: throttle? */
break;
case XDG_TOPLEVEL_STATE_RESIZING: is_resizing = true; break;
}
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
@ -614,6 +611,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
win->configure.is_activated = is_activated;
win->configure.is_fullscreen = is_fullscreen;
win->configure.is_maximized = is_maximized;
win->configure.is_resizing = is_resizing;
win->configure.is_tiled_top = is_tiled_top;
win->configure.is_tiled_bottom = is_tiled_bottom;
win->configure.is_tiled_left = is_tiled_left;
@ -646,8 +644,11 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
struct terminal *term = win->term;
bool wasnt_configured = !win->is_configured;
bool was_resizing = win->is_resizing;
win->is_configured = true;
win->is_maximized = win->configure.is_maximized;
win->is_resizing = win->configure.is_resizing;
win->is_tiled_top = win->configure.is_tiled_top;
win->is_tiled_bottom = win->configure.is_tiled_bottom;
win->is_tiled_left = win->configure.is_tiled_left;
@ -676,8 +677,25 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
term->window->frame_callback = NULL;
}
bool resized = render_resize(
term, win->configure.width, win->configure.height);
#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.
*
* Note: if we also disable content centering while resizing, then
* the last, forced, resize *is* necessary.
*/
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);
@ -1276,6 +1294,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) {
@ -1428,6 +1447,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);
}

View file

@ -372,22 +372,26 @@ struct wl_window {
bool is_configured;
bool is_fullscreen;
bool is_maximized;
bool is_resizing;
bool is_tiled_top;
bool is_tiled_bottom;
bool is_tiled_left;
bool is_tiled_right;
bool is_tiled; /* At least one of is_tiled_{top,bottom,left,right} is true */
struct {
bool is_activated;
bool is_fullscreen;
bool is_maximized;
bool is_tiled_top;
bool is_tiled_bottom;
bool is_tiled_left;
bool is_tiled_right;
int width;
int height;
bool is_activated:1;
bool is_fullscreen:1;
bool is_maximized:1;
bool is_resizing:1;
bool is_tiled_top:1;
bool is_tiled_bottom:1;
bool is_tiled_left:1;
bool is_tiled_right:1;
} configure;
int resize_timeout_fd;
};
struct config;