forked from RepoMirrors/bemenu
Fix Wayland event loop order to avoid missed renders
After the changes of the previous commit, the event loop flow on Wayland is: 1. Render windows if window->render_pending is set 2. Wait for events [NOTE: this includes the frame callback] 3. Schedule render if menu->dirty is set 4. Handle events (return from render) and repeat This can still miss renders since the menu->dirty flag is set in step (4), but the menu->dirty flag is checked in step (3). So if the event loop only does a single iteration (quite unusual as most user actions cause multiple events), we can get stuck on step (2) for a while. In order to avoid this problem, this changes the event loop order to: 1. Schedule render if menu->dirty is set 2. Wait for events [NOTE: this includes the frame callback] 3. Render windows if window->render_pending is set 4. Handle events (return from render) and repeat Script (for Sway) to reproduce the issue / verify the fix: #!/usr/bin/env sh mousesety() { swaymsg seat - cursor set 200 "$1" >/dev/null; sleep 0.2; } { while true; do mousesety 200; mousesety 300; mousesety 400; done } & trap 'kill $!' EXIT export BEMENU_BACKEND=wayland BEMENU_OPTS='--list 40 --hb #0000FF' yes | head -30 | bemenu Fixes: #274 Fixes: #275
This commit is contained in:
parent
7d2c189865
commit
04b0d83d56
@ -14,22 +14,23 @@
|
||||
static int efd;
|
||||
|
||||
static void
|
||||
render(struct bm_menu *menu)
|
||||
{
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
wl_display_dispatch_pending(wayland->display);
|
||||
|
||||
if (wl_display_flush(wayland->display) < 0 && errno != EAGAIN) {
|
||||
wayland->input.sym = XKB_KEY_Escape;
|
||||
return;
|
||||
}
|
||||
|
||||
render_windows_if_pending(const struct bm_menu *menu, struct wayland *wayland) {
|
||||
struct window *window;
|
||||
wl_list_for_each(window, &wayland->windows, link) {
|
||||
if (window->render_pending)
|
||||
bm_wl_window_render(window, wayland->display, menu);
|
||||
}
|
||||
wl_display_flush(wayland->display);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_for_events(struct wayland *wayland) {
|
||||
wl_display_dispatch_pending(wayland->display);
|
||||
|
||||
if (wl_display_flush(wayland->display) < 0 && errno != EAGAIN) {
|
||||
wayland->input.sym = XKB_KEY_Escape;
|
||||
return;
|
||||
}
|
||||
|
||||
struct epoll_event ep[16];
|
||||
uint32_t num = epoll_wait(efd, ep, 16, -1);
|
||||
@ -42,13 +43,33 @@ render(struct bm_menu *menu)
|
||||
bm_wl_repeat(wayland);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menu->dirty) {
|
||||
menu->dirty = false;
|
||||
wl_list_for_each(window, &wayland->windows, link) {
|
||||
static void
|
||||
schedule_windows_render_if_dirty(struct bm_menu *menu, struct wayland *wayland) {
|
||||
struct window *window;
|
||||
wl_list_for_each(window, &wayland->windows, link) {
|
||||
if (window->render_pending) {
|
||||
// This does not happen during normal execution, but only when the windows need to
|
||||
// be(re)created. We need to do the render ASAP (not schedule it) because otherwise,
|
||||
// since we lack a window, we may not receive further events and will get deadlocked
|
||||
render_windows_if_pending(menu, wayland);
|
||||
} else if (menu->dirty) {
|
||||
bm_wl_window_schedule_render(window);
|
||||
}
|
||||
}
|
||||
|
||||
menu->dirty = false;
|
||||
}
|
||||
|
||||
static void
|
||||
render(struct bm_menu *menu)
|
||||
{
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
|
||||
schedule_windows_render_if_dirty(menu, wayland);
|
||||
wait_for_events(wayland);
|
||||
render_windows_if_pending(menu, wayland);
|
||||
}
|
||||
|
||||
static enum bm_key
|
||||
|
Loading…
Reference in New Issue
Block a user