mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
render: throttle xcursor updates
With a bad behaving client (e.g. 'less' with mouse support enabled), we can end up with a *lot* of xcursor updates (so much, that we flooded the wayland socket before we implemented a blocking wayl_flush()). Since there's little point in updating the cursor more than once per frame interval, use frame callbacks to throttle the updates. This works more or lesslike normal terminal refreshes: render_xcursor_set() stores the last terminal (window) that had (and updated) the cursor. The renderer's FDM hook checks if we have such a pending terminal set, and if so, tries to refresh the cursor. This is done by first checking if we're already waiting for a callback from a previous cursor update, and if so we do nothing; the callback will update the cursor for the next frame. If we're *not* already waiting for a callback, we update the cursor immediately.
This commit is contained in:
parent
040a1cd00d
commit
5ec447697c
5 changed files with 136 additions and 76 deletions
145
render.c
145
render.c
|
|
@ -34,25 +34,7 @@ static struct {
|
|||
size_t two; /* commits presented in two or more frame intervals */
|
||||
} presentation_statistics = {0};
|
||||
|
||||
static void
|
||||
fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
||||
{
|
||||
struct renderer *renderer = data;
|
||||
tll_foreach(renderer->wayl->terms, it) {
|
||||
struct terminal *term = it->item;
|
||||
|
||||
if (!term->render.refresh_needed)
|
||||
continue;
|
||||
|
||||
assert(term->window->is_configured);
|
||||
term->render.refresh_needed = false;
|
||||
|
||||
if (term->window->frame_callback == NULL)
|
||||
grid_render(term);
|
||||
else
|
||||
term->render.pending = true;
|
||||
}
|
||||
}
|
||||
static void fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data);
|
||||
|
||||
struct renderer *
|
||||
render_init(struct fdm *fdm, struct wayland *wayl)
|
||||
|
|
@ -601,7 +583,7 @@ static const struct wl_callback_listener frame_listener = {
|
|||
.done = &frame_callback,
|
||||
};
|
||||
|
||||
void
|
||||
static void
|
||||
grid_render(struct terminal *term)
|
||||
{
|
||||
if (term->is_shutting_down)
|
||||
|
|
@ -1110,6 +1092,102 @@ render_resize(struct terminal *term, int width, int height)
|
|||
render_refresh(term);
|
||||
}
|
||||
|
||||
static void xcursor_callback(
|
||||
void *data, struct wl_callback *wl_callback, uint32_t callback_data);
|
||||
|
||||
static const struct wl_callback_listener xcursor_listener = {
|
||||
.done = &xcursor_callback,
|
||||
};
|
||||
|
||||
static void
|
||||
render_xcursor_update(struct wayland *wayl, const struct terminal *term)
|
||||
{
|
||||
wayl->pointer.pending_terminal = NULL;
|
||||
|
||||
/* If called from a frame callback, we may no longer have mouse focus */
|
||||
if (wayl->mouse_focus != term)
|
||||
return;
|
||||
|
||||
wayl->pointer.cursor = wl_cursor_theme_get_cursor(wayl->pointer.theme, term->xcursor);
|
||||
if (wayl->pointer.cursor == NULL) {
|
||||
LOG_ERR("%s: failed to load xcursor pointer '%s'",
|
||||
wayl->pointer.theme_name, term->xcursor);
|
||||
return;
|
||||
}
|
||||
|
||||
wayl->pointer.xcursor = term->xcursor;
|
||||
|
||||
const int scale = term->scale;
|
||||
struct wl_cursor_image *image = wayl->pointer.cursor->images[0];
|
||||
|
||||
wl_surface_attach(
|
||||
wayl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
|
||||
|
||||
wl_pointer_set_cursor(
|
||||
wayl->pointer.pointer, wayl->pointer.serial,
|
||||
wayl->pointer.surface,
|
||||
image->hotspot_x / scale, image->hotspot_y / scale);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
wayl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
|
||||
wl_surface_set_buffer_scale(wayl->pointer.surface, scale);
|
||||
|
||||
assert(wayl->pointer.xcursor_callback == NULL);
|
||||
wayl->pointer.xcursor_callback = wl_surface_frame(wayl->pointer.surface);
|
||||
wl_callback_add_listener(wayl->pointer.xcursor_callback, &xcursor_listener, wayl);
|
||||
|
||||
wl_surface_commit(wayl->pointer.surface);
|
||||
wayl_flush(wayl);
|
||||
}
|
||||
|
||||
static void
|
||||
render_xcursor_refresh(struct wayland *wayl)
|
||||
{
|
||||
if (wayl->pointer.pending_terminal == NULL)
|
||||
return;
|
||||
|
||||
if (wayl->pointer.xcursor_callback == NULL)
|
||||
render_xcursor_update(wayl, wayl->pointer.pending_terminal);
|
||||
else {
|
||||
/* Frame callback will call render_xcursor_update() */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xcursor_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
|
||||
assert(wayl->pointer.xcursor_callback == wl_callback);
|
||||
wl_callback_destroy(wl_callback);
|
||||
wayl->pointer.xcursor_callback = NULL;
|
||||
|
||||
render_xcursor_refresh(wayl);
|
||||
}
|
||||
|
||||
static void
|
||||
fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
||||
{
|
||||
struct renderer *renderer = data;
|
||||
tll_foreach(renderer->wayl->terms, it) {
|
||||
struct terminal *term = it->item;
|
||||
|
||||
if (!term->render.refresh_needed)
|
||||
continue;
|
||||
|
||||
assert(term->window->is_configured);
|
||||
term->render.refresh_needed = false;
|
||||
|
||||
if (term->window->frame_callback == NULL)
|
||||
grid_render(term);
|
||||
else
|
||||
term->render.pending = true;
|
||||
}
|
||||
|
||||
render_xcursor_refresh(renderer->wayl);
|
||||
}
|
||||
|
||||
void
|
||||
render_set_title(struct terminal *term, const char *_title)
|
||||
{
|
||||
|
|
@ -1133,3 +1211,30 @@ render_refresh(struct terminal *term)
|
|||
{
|
||||
term->render.refresh_needed = true;
|
||||
}
|
||||
|
||||
bool
|
||||
render_xcursor_set(struct terminal *term)
|
||||
{
|
||||
struct wayland *wayl = term->wl;
|
||||
|
||||
if (wayl->pointer.theme == NULL)
|
||||
return false;
|
||||
|
||||
if (wayl->mouse_focus == NULL) {
|
||||
wayl->pointer.xcursor = NULL;
|
||||
wayl->pointer.pending_terminal = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wayl->mouse_focus != term) {
|
||||
/* This terminal doesn't have mouse focus */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wayl->pointer.xcursor == term->xcursor)
|
||||
return true;
|
||||
|
||||
/* FDM hook takes care of actual rendering */
|
||||
wayl->pointer.pending_terminal = term;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
2
render.h
2
render.h
|
|
@ -8,10 +8,10 @@ struct renderer;
|
|||
struct renderer *render_init(struct fdm *fdm, struct wayland *wayl);
|
||||
void render_destroy(struct renderer *renderer);
|
||||
|
||||
void grid_render(struct terminal *term);
|
||||
void render_resize(struct terminal *term, int width, int height);
|
||||
void render_set_title(struct terminal *term, const char *title);
|
||||
void render_refresh(struct terminal *term);
|
||||
bool render_xcursor_set(struct terminal *term);
|
||||
|
||||
void render_search_box(struct terminal *term);
|
||||
|
||||
|
|
|
|||
|
|
@ -1674,7 +1674,7 @@ term_xcursor_update(struct terminal *term)
|
|||
selection_enabled(term) ? XCURSOR_TEXT :
|
||||
XCURSOR_HAND2;
|
||||
|
||||
wayl_cursor_set(term->wl, term);
|
||||
render_xcursor_set(term);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
57
wayland.c
57
wayland.c
|
|
@ -32,7 +32,7 @@
|
|||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
static bool wayl_reload_cursor_theme(
|
||||
struct wayland *wayl, const struct terminal *term);
|
||||
struct wayland *wayl, struct terminal *term);
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
|
|
@ -769,6 +769,9 @@ wayl_destroy(struct wayland *wayl)
|
|||
tll_remove(wayl->monitors, it);
|
||||
}
|
||||
|
||||
if (wayl->pointer.xcursor_callback != NULL)
|
||||
wl_callback_destroy(wayl->pointer.xcursor_callback);
|
||||
|
||||
if (wayl->xdg_output_manager != NULL)
|
||||
zxdg_output_manager_v1_destroy(wayl->xdg_output_manager);
|
||||
if (wayl->shell != NULL)
|
||||
|
|
@ -940,56 +943,8 @@ wayl_win_destroy(struct wl_window *win)
|
|||
free(win);
|
||||
}
|
||||
|
||||
bool
|
||||
wayl_cursor_set(struct wayland *wayl, const struct terminal *term)
|
||||
{
|
||||
if (wayl->pointer.theme == NULL)
|
||||
return false;
|
||||
|
||||
if (wayl->mouse_focus == NULL) {
|
||||
wayl->pointer.xcursor = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wayl->mouse_focus != term) {
|
||||
/* This terminal doesn't have mouse focus */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wayl->pointer.xcursor == term->xcursor)
|
||||
return true;
|
||||
|
||||
wayl->pointer.cursor = wl_cursor_theme_get_cursor(wayl->pointer.theme, term->xcursor);
|
||||
if (wayl->pointer.cursor == NULL) {
|
||||
LOG_ERR("%s: failed to load xcursor pointer '%s'",
|
||||
wayl->pointer.theme_name, term->xcursor);
|
||||
return false;
|
||||
}
|
||||
|
||||
wayl->pointer.xcursor = term->xcursor;
|
||||
|
||||
const int scale = term->scale;
|
||||
struct wl_cursor_image *image = wayl->pointer.cursor->images[0];
|
||||
|
||||
wl_surface_attach(
|
||||
wayl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
|
||||
|
||||
wl_pointer_set_cursor(
|
||||
wayl->pointer.pointer, wayl->pointer.serial,
|
||||
wayl->pointer.surface,
|
||||
image->hotspot_x / scale, image->hotspot_y / scale);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
wayl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
|
||||
wl_surface_set_buffer_scale(wayl->pointer.surface, scale);
|
||||
wl_surface_commit(wayl->pointer.surface);
|
||||
wayl_flush(wayl);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
wayl_reload_cursor_theme(struct wayland *wayl, const struct terminal *term)
|
||||
wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term)
|
||||
{
|
||||
if (wayl->pointer.size == 0)
|
||||
return true;
|
||||
|
|
@ -1011,7 +966,7 @@ wayl_reload_cursor_theme(struct wayland *wayl, const struct terminal *term)
|
|||
return false;
|
||||
}
|
||||
|
||||
return wayl_cursor_set(wayl, term);
|
||||
return render_xcursor_set(term);
|
||||
}
|
||||
|
||||
struct terminal *
|
||||
|
|
|
|||
|
|
@ -152,6 +152,9 @@ struct wayland {
|
|||
int size;
|
||||
char *theme_name;
|
||||
const char *xcursor;
|
||||
|
||||
const struct terminal *pending_terminal;
|
||||
struct wl_callback *xcursor_callback;
|
||||
} pointer;
|
||||
|
||||
struct {
|
||||
|
|
@ -184,8 +187,5 @@ void wayl_roundtrip(struct wayland *wayl);
|
|||
struct terminal *wayl_terminal_from_surface(
|
||||
struct wayland *wayl, struct wl_surface *surface);
|
||||
|
||||
/* TODO: pass something other than 'term'? Need scale... */
|
||||
bool wayl_cursor_set(struct wayland *wayl, const struct terminal *term);
|
||||
|
||||
struct wl_window *wayl_win_init(struct terminal *term);
|
||||
void wayl_win_destroy(struct wl_window *win);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue