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 int efd;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render(struct bm_menu *menu)
|
render_windows_if_pending(const struct bm_menu *menu, struct wayland *wayland) {
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct window *window;
|
struct window *window;
|
||||||
wl_list_for_each(window, &wayland->windows, link) {
|
wl_list_for_each(window, &wayland->windows, link) {
|
||||||
if (window->render_pending)
|
if (window->render_pending)
|
||||||
bm_wl_window_render(window, wayland->display, menu);
|
bm_wl_window_render(window, wayland->display, menu);
|
||||||
}
|
}
|
||||||
wl_display_flush(wayland->display);
|
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];
|
struct epoll_event ep[16];
|
||||||
uint32_t num = epoll_wait(efd, ep, 16, -1);
|
uint32_t num = epoll_wait(efd, ep, 16, -1);
|
||||||
@ -42,13 +43,33 @@ render(struct bm_menu *menu)
|
|||||||
bm_wl_repeat(wayland);
|
bm_wl_repeat(wayland);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (menu->dirty) {
|
static void
|
||||||
menu->dirty = false;
|
schedule_windows_render_if_dirty(struct bm_menu *menu, struct wayland *wayland) {
|
||||||
wl_list_for_each(window, &wayland->windows, link) {
|
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);
|
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
|
static enum bm_key
|
||||||
|
Loading…
Reference in New Issue
Block a user