1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-14 17:53:25 -04:00

tentative fix for #1345

This commit is contained in:
Yuxuan Shui 2024-10-08 19:14:09 +01:00
parent 6e485a050a
commit 6eb45ad2cf
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
3 changed files with 57 additions and 30 deletions

View file

@ -1451,31 +1451,48 @@ static void unredirect(session_t *ps) {
static void handle_x_events(struct session *ps) {
bool wm_was_consistent = wm_is_consistent(ps->wm);
if (ps->vblank_scheduler) {
vblank_handle_x_events(ps->vblank_scheduler);
while (true) {
// Flush because if we go into sleep when there is still requests
// in the outgoing buffer, they will not be sent for an indefinite
// amount of time. Use XFlush here too, we might still use some
// Xlib functions because OpenGL.
//
// Also note, `xcb_flush`/`XFlush` may _read_ more events from the server
// (yes, this is ridiculous, I know). But since we are polling for events
// in a loop, this should be fine - if we read events here, they will be
// handled below; and if some requests is sent later in this loop, which
// means some events must have been received, we will loop once more and
// get here to flush them.
XFlush(ps->c.dpy);
xcb_flush(ps->c.c);
// We have to check for vblank events (i.e. special xcb events) and normal
// events in a loop. This is because both `xcb_poll_for_event` and
// `xcb_poll_for_special_event` will read data from the X connection and
// put it in a buffer. So whichever one we call last, say
// `xcb_poll_for_special_event`, will read data into the buffer that might
// contain events that `xcb_poll_for_event` should handle, and vice versa.
// This causes us to go into sleep with events in the buffer.
//
// We have to keep calling both of them until neither of them return any
// events.
bool has_events = false;
if (ps->vblank_scheduler) {
has_events = vblank_handle_x_events(ps->vblank_scheduler) ==
VBLANK_HANDLE_X_EVENTS_OK;
}
xcb_generic_event_t *ev;
while ((ev = x_poll_for_event(&ps->c))) {
has_events = true;
ev_handle(ps, (xcb_generic_event_t *)ev);
free(ev);
};
if (!has_events) {
break;
}
}
// Flush because if we go into sleep when there is still requests in the
// outgoing buffer, they will not be sent for an indefinite amount of
// time. Use XFlush here too, we might still use some Xlib functions
// because OpenGL.
//
// Also note, after we have flushed here, we won't flush again in this
// function before going into sleep. This is because `xcb_flush`/`XFlush`
// may _read_ more events from the server (yes, this is ridiculous, I
// know). And we can't have that, see the comments above this function.
//
// This means if functions called ev_handle need to send some events,
// they need to carefully make sure those events are flushed, one way or
// another.
XFlush(ps->c.dpy);
xcb_flush(ps->c.c);
xcb_generic_event_t *ev;
while ((ev = x_poll_for_event(&ps->c))) {
ev_handle(ps, (xcb_generic_event_t *)ev);
free(ev);
};
int err = xcb_connection_has_error(ps->c.c);
if (err) {
log_fatal("X11 server connection broke (error %d)", err);

View file

@ -69,7 +69,7 @@ struct vblank_scheduler_ops {
bool (*init)(struct vblank_scheduler *self);
void (*deinit)(struct vblank_scheduler *self);
bool (*schedule)(struct vblank_scheduler *self);
bool (*handle_x_events)(struct vblank_scheduler *self);
enum vblank_handle_x_events_result (*handle_x_events)(struct vblank_scheduler *self);
};
static void
@ -446,10 +446,14 @@ static void handle_present_complete_notify(struct present_vblank_scheduler *self
ev_timer_start(self->base.loop, &self->callback_timer);
}
static bool handle_present_events(struct vblank_scheduler *base) {
static enum vblank_handle_x_events_result
handle_present_events(struct vblank_scheduler *base) {
auto self = (struct present_vblank_scheduler *)base;
xcb_present_generic_event_t *ev;
enum vblank_handle_x_events_result result = VBLANK_HANDLE_X_EVENTS_NO_EVENTS;
while ((ev = (void *)xcb_poll_for_special_event(base->c->c, self->event))) {
result = VBLANK_HANDLE_X_EVENTS_OK;
if (ev->event != self->event_id) {
// This event doesn't have the right event context, it's not meant
// for us.
@ -462,7 +466,7 @@ static bool handle_present_events(struct vblank_scheduler *base) {
next:
free(ev);
}
return true;
return result;
}
static const struct vblank_scheduler_ops vblank_scheduler_ops[LAST_VBLANK_SCHEDULER] = {
@ -573,11 +577,11 @@ vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c, xcb_window_t
return self;
}
bool vblank_handle_x_events(struct vblank_scheduler *self) {
enum vblank_handle_x_events_result vblank_handle_x_events(struct vblank_scheduler *self) {
assert(self->type < LAST_VBLANK_SCHEDULER);
auto fn = vblank_scheduler_ops[self->type].handle_x_events;
if (fn != NULL) {
return fn(self);
}
return true;
return VBLANK_HANDLE_X_EVENTS_NO_EVENTS;
}

View file

@ -29,6 +29,12 @@ enum vblank_callback_action {
VBLANK_CALLBACK_DONE,
};
enum vblank_handle_x_events_result {
VBLANK_HANDLE_X_EVENTS_OK,
VBLANK_HANDLE_X_EVENTS_ERROR,
VBLANK_HANDLE_X_EVENTS_NO_EVENTS,
};
typedef enum vblank_callback_action (*vblank_callback_t)(struct vblank_event *event,
void *user_data);
@ -47,4 +53,4 @@ vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c, xcb_window_t
enum vblank_scheduler_type type, bool use_realtime_scheduling);
void vblank_scheduler_free(struct vblank_scheduler *);
bool vblank_handle_x_events(struct vblank_scheduler *self);
enum vblank_handle_x_events_result vblank_handle_x_events(struct vblank_scheduler *self);