Open guijan opened 1 year ago
Edit:
poll()
doesn't have subsecond precision.
poll
's timeout is in miliseconds.
And there are much bigger issues with that code. I'm at the end of my awake hours, so I'll take a closer look tomorrow with fresh eyes before elaborating.
Other place with the same pattern: https://github.com/resurrecting-open-source-projects/scrot/blob/3b281dbf20fb4e4b8bd74c177163c441561db024/src/selection_edge.c#L152-L158
This one seems similar on the surface: https://github.com/resurrecting-open-source-projects/scrot/blob/5ef8a82612a57ebd8b70cc45eaea437cbcfed741/src/scrot_selection.c#L248-L256 But I don't know if there's a way to sleep until the keyboard can be grabbed.
And there are much bigger issues with that code.
scrotXEventVisibility
tries to check if the window becomes visible. However, that event will never come. Because we never subscribed to visibility events. XSelectInput(disp, target, FocusChangeMask);
This subscribes us to focus-change events, not visibility events. For visibility events, VisibilityChangeMask
is needed. The window
member on both visibility and focus-change event happens to line up, which is why that code happened to "work" (for some definition of work).
Just because we received a visibility event doesn't mean the window is visible. Visibility event can be generated due to a window becoming obscured or partially obscured as well.
XCheckIfEvent will only pull out the event for which scrotXEventVisibility
returned true and leave the prior events in the queue. I don't really see why this would be needed or desired.
There might be a race since we call XSelectInput
after raising the window. So we might end up missing the event even though it was generated. (This one I'm not 100% sure about since xlib buffers the output).
There's so much issues with this code that I'd rather not try and fix it, instead I'd much rather figure out what exact problem that code was trying to solve and then solve that from scratch.
I'd much rather figure out what exact problem that code was trying to solve
Related: https://github.com/resurrecting-open-source-projects/scrot/pull/67
My wm doesn't seem to respect the raise request. So it'll be difficult for me to test.
One more bug, if the selected window was already raised, then there won't be any visibility event (as nothing changed) and so scrot will just sleep for that entire duration.
What we ideally want here is something like the following:
But I'm not sure if this can be done.
The only thing close to it in ICCCM/EWMH I can find is this. But it's not exactly what we're looking for, it's about the currently focused window - I'm not sure if there's anything that says that a focused window cannot be obscured.
And then there's this:
the Window Manager may decide to refuse the request (either completely ignore it, or e.g. use _NET_WM_STATE_DEMANDS_ATTENTION)
And in practice, there's plently of WMs that take this route. For example, dwm
only sets the window to be "urgent" when it receives a _NET_ACTIVE_WINDOW
request - it doesn't give it focus.
And in practice, there's plently of WMs that take this route. For example,
dwm
only sets the window to be "urgent" when it receives a_NET_ACTIVE_WINDOW
request - it doesn't give it focus.
If a WM supports _NET_ACTIVE_WINDOW
then we can avoid sleeping if the window already was raised. But I'm not sure if "active" window really means it's "raised" or not, the spec only says it's about which window has focus (and typically the window that has focus is raised, but I don't know if it's required to be).
But I don't know if there's a way to sleep until the keyboard can be grabbed.
This is a different issue altogether.
But yes you can wait for a focus event to come (since grabbing/ungrabbing the keyboard generates focus in/out events). It's what I do in sx4
- but there's no timeout involved.
In order to do a timeout as well, I think you'd need to either setup a sigalarm handler that _exit()
s or mess with poll
or mess with XSync alarm.
Unless there's some pitfall involved, setting up an alarm with a fatal handler seems to be the simplest solution. I'd rather not go XSync
route, it's a lot of code, is inefficient and misses deadlines by larger amount.
Instead of polling for the event (with drift from calling
nanosleep()
in a loop...), this code: https://github.com/resurrecting-open-source-projects/scrot/blob/dc65e9697c0a272029d694efddb1c7cce9e66cf6/src/scrot.c#L373-L379 Should useselect()
orpoll()
to wait for the fd used to communicate with the X server to have some data, then useXCheckIfEvent()
to check if the data is what we're watching for.Edit: Is 300ms enough of a delay? 10ms is often the precision OSes offer to the userland when it comes to timers, there's probably significant drift in polling every 10ms, fixing this might expose an issue with the delay being too short, but I find it unlikely for local X servers. Edit 2: fixed incorrect statement about
poll()