Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core: fix poll for event loop (i hope)
This partially reverts c57b705, the previous fix for #1345 The previous fix falsely assumed if `x_poll_for_event` returned nothing, it must have not read from the X connection. This is just not true. It worked, probably because it's impossible to have vblank events close together (they are only once per frame). But that's an assumption we probably shouldn't make. _Separately_, we have another problem. We assumed if `x_poll_for_event` returned nothing, we don't need to flush again. This is not true either. There could be pending requests being completed, whose callback function might have sent new requests. This, for example, causes `picom-inspect` to hang, because some requests are stuck in the outgoing buffer. The explanation for the fix is going to be long, because this kind of problems are never simple. First of all, we need a version of `x_poll_for_event` that actually guarantees to not read from X. The current version of `x_poll_for_event` is already extremely complex, I don't want to pile more complexity on top of it. So instead we are now using a different approach, one some might consider a ugly hack. The complexity of `x_poll_for_event` all stems from the lack of `xcb_poll_for_queued_{reply,special_event}` API, so we had to use complex to merge message from different streams (events, special events, replies, and errors) all the while trying our best (and fail) to handle all messages in the xcb buffer before going to sleep. There is a `xcb_poll_for_queued_event` which in theory can be used for this and will make things a lot simpler, the problem is we don't control events, so if there is a large gap between event arrivals, picom would just be sitting there doing nothing. And that's exactly what we do in this commit, that is, controlling events. Every time we wait for replies/errors for some requests, we send a request that are guaranteed to _fail_. This is because unchecked errors will be returned by `xcb_poll_for_queued_event` as an event. This way, we can be sure an event will be received in a bounded amount of time, so we won't hang. This vastly simplifies the logic in `x_poll_for_event`, and made adding a no-read version of it trivial. Now we have that, some care need to be taken to make sure that we _do_ read from X sometimes, otherwise we will go to sleep without reading anything, and be woken up again immediately by the file descriptor. This will result in an infinite loop. This has some ripple effects, for example, now we queue redraw whenever the wm tree changes; before, redraws were only queued when the wm tree becomes consistent. Even with this, we still need the `while(true)` loop in `handle_x_events`, this is because of the other problem we mentioned at the beginning - flushing. The way we fix the flushing problem is by tracking the completion of pending requests - if any requests were completed, we flush conservatively (meaning even when not strictly necessary) - simple enough. But flushing could read (yes, read) from X too! So whenever we flush, we need to call `x_poll_for_event` again, hence the while loop. Fixes: #1345 Signed-off-by: Yuxuan Shui <[email protected]>
- Loading branch information