Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missed rendering regressions on Wayland (fixes --grab flag) #273

Merged
merged 2 commits into from
Jun 30, 2022
Merged

Fix missed rendering regressions on Wayland (fixes --grab flag) #273

merged 2 commits into from
Jun 30, 2022

Conversation

joanbm
Copy link
Contributor

@joanbm joanbm commented Jun 29, 2022

This PR contains two closely related regression fixes (introduced by commit 5a09570 and released in versions 0.6.5+), which affect exclusively the Wayland backend.
Both are due to some missed edge cases when managing the dirty flag that was introduced in that commit, and for the most part just cause bemenu not to re-render after the items are loaded when using the --grab, so bemenu stays at "Loading..." until an unrelated event happens (e.g. a key press) that causes an actual re-render.

The issue fixed by the first commit can be reproduced with:

{ echo one; sleep 1; echo two; } | BEMENU_BACKEND=wayland bemenu --grab

And causes bemenu to pause at "Loading..." almost 100% of the time.

The issue fixed by the second commit can be reproduced with:

{ echo one; echo two; } | BEMENU_BACKEND=wayland bemenu --grab

And causes bemenu to sometimes (~15% on my PC) pause at "Loading...". Reproduces more often when having some background activity.

@@ -516,6 +516,7 @@ menu_with_options(struct client *client)
// bm_menu_grab_keyboard(menu, true);
bm_menu_render(menu);
bm_menu_set_filter(menu, NULL);
menu->dirty = true; // Ensure a redraw on the first call to bm_menu_render after the items are loaded
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dirty is not considered public API. I think you should set this in this function instead

bm_menu_set_filter(struct bm_menu *menu, const char *filter)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Note that most bm_menu_set_* are missing the dirtiness check, which is what initially led me to try to just set the dirty flag and call it a day, but I agree that the ideal approach going forward should be to set it on all bm_menu_set_* functions that cause a change on the UI.

Currently, on the Wayland backend, the following command:
    { echo one; sleep 1; echo two; } | BEMENU_BACKEND=wayland bemenu --grab
Will keep showing the 'Loading...' text even after all items are available.
The items will only be shown after some input, e.g. a key press, happens.

This was a regression introduced by 5a09570
(versions 0.6.5+). A dirty flag was added to avoid unnecessary redraws,
however, this flag is not reset between the renders before/after the items are
loaded, so the bm_menu_render call after the items are loaded is ignored
(on Wayland, which is the only renderer that currently uses the dirty flag).

Fix the issue by setting the dirty flag when the filter changes, so it will be
reset when changing from "Loading..." to empty and cause a redraw.
Despite the fix in the previous commit (3d7e47c), the following command:
    { echo one; echo two; } | BEMENU_BACKEND=wayland bemenu --grab
Will likely still show the 'Loading...' text after all items are available.

A related problem (also on Wayland) is that when pressing two keys (for the
filter) almost simultaneously, sometimes only one of the keys will be rendered.
The other key will only be shown on the next render. For example:
    - Filter shows "s"
    - User presses the "o" and "n" keys simultaneously
    - Filter shows "so" ["n" appears to have been lost]
    - User presses the "g" key
    - Filter shows "song" [now the "n" has been rendered]

Both problems have the same root cause: If two events that cause a render happen
"close enough" to each other, often the second event will not cause a render.

As far as I can tell, the cause is that the "dirty && render_pending" render
check should not check the dirty flag at all, just "render_pending".

With "dirty && render_pending", if two events happen in close succession:
- On the first event, generally dirty==true, render_pending==true, a render
  will happen, the frame callback will be set, and both flags will be cleared.
- For the second event, generally dirty==true and render_pending==false.
  dirty will be unset and nothing will happen.
- When the frame triggers, render_pending is set, but no render will happen
  because dirty==false

With just "render_pending" (the change in this commit):
- On the first event, generally dirty==true, render_pending==false, the frame
  callback will be set, and dirty will be cleared.
- For the second event, generally dirty==true and render_pending==false.
  dirty will be unset and nothing will happen.
- When the frame triggers, render_pending is set, then a render will be done.
free(menu->filter);
menu->filter_size = (filter ? strlen(filter) : 0);
menu->filter = (menu->filter_size > 0 ? bm_strdup(filter) : NULL);
menu->curses_cursor = (menu->filter ? bm_utf8_string_screen_width(menu->filter) : 0);
menu->dirty |= (menu->cursor != menu->filter_size);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This is not necessary to fix the issue at hand, but just not to leave the dirtiness check incomplete).

@Cloudef Cloudef merged commit c7a5812 into Cloudef:master Jun 30, 2022
@Cloudef
Copy link
Owner

Cloudef commented Jun 30, 2022

Thanks merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants