diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 0983bad0a..a30a880bf 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -68,10 +68,7 @@ static void handle_x11_event(struct wlr_x11_backend *x11, struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { - pixman_region32_union_rect( - &output->exposed, &output->exposed, - ev->x, ev->y, ev->width, ev->height); - wlr_output_update_needs_frame(&output->wlr_output); + handle_x11_expose_event(output, ev); } break; } diff --git a/backend/x11/output.c b/backend/x11/output.c index 0b63a7088..968d7099f 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -100,6 +100,7 @@ static void output_destroy(struct wlr_output *wlr_output) { wlr_pointer_finish(&output->pointer); wlr_touch_finish(&output->touch); + wlr_buffer_unlock(output->current_buffer); struct wlr_x11_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) { @@ -334,6 +335,34 @@ static struct wlr_x11_buffer *get_or_create_x11_buffer( return create_x11_buffer(output, wlr_buffer); } +static xcb_xfixes_region_t create_region(struct wlr_x11_backend *x11, + const pixman_region32_t *region) { + int rects_len = 0; + const pixman_box32_t *rects = pixman_region32_rectangles(region, &rects_len); + + xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); + if (!xcb_rects) { + return XCB_NONE; + } + + for (int i = 0; i < rects_len; i++) { + const pixman_box32_t *box = &rects[i]; + xcb_rects[i] = (struct xcb_rectangle_t){ + .x = box->x1, + .y = box->y1, + .width = box->x2 - box->x1, + .height = box->y2 - box->y1, + }; + } + + xcb_xfixes_region_t x11_region = xcb_generate_id(x11->xcb); + xcb_xfixes_create_region(x11->xcb, x11_region, rects_len, xcb_rects); + + free(xcb_rects); + + return x11_region; +} + static bool output_commit_buffer(struct wlr_x11_output *output, const struct wlr_output_state *state) { struct wlr_x11_backend *x11 = output->x11; @@ -348,29 +377,7 @@ static bool output_commit_buffer(struct wlr_x11_output *output, xcb_xfixes_region_t region = XCB_NONE; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { pixman_region32_union(&output->exposed, &output->exposed, &state->damage); - - int rects_len = 0; - const pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len); - - xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); - if (!xcb_rects) { - goto error; - } - - for (int i = 0; i < rects_len; i++) { - const pixman_box32_t *box = &rects[i]; - xcb_rects[i] = (struct xcb_rectangle_t){ - .x = box->x1, - .y = box->y1, - .width = box->x2 - box->x1, - .height = box->y2 - box->y1, - }; - } - - region = xcb_generate_id(x11->xcb); - xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects); - - free(xcb_rects); + region = create_region(x11, &output->exposed); } pixman_region32_clear(&output->exposed); @@ -386,6 +393,9 @@ static bool output_commit_buffer(struct wlr_x11_output *output, xcb_xfixes_destroy_region(x11->xcb, region); } + wlr_buffer_unlock(output->current_buffer); + output->current_buffer = wlr_buffer_lock(buffer); + return true; error: @@ -768,6 +778,12 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, return; } + if (output->last_msc == complete_notify->msc && complete_notify->serial == 0) { + // This is a present complete event triggered by a synthetic + // present request sent from handle_x11_expose_event() + return; + } + output->last_msc = complete_notify->msc; uint32_t flags = 0; @@ -792,3 +808,39 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); } } + +void handle_x11_expose_event(struct wlr_x11_output *output, + xcb_expose_event_t *ev) { + struct wlr_x11_backend *x11 = output->x11; + + pixman_region32_union_rect(&output->exposed, &output->exposed, + ev->x, ev->y, ev->width, ev->height); + + if (output->current_buffer == NULL) { + return; + } + + // If we have access to the last submitted buffer, re-send the previous + // present request + + struct wlr_x11_buffer *x11_buffer = + get_or_create_x11_buffer(output, output->current_buffer); + if (!x11_buffer) { + return; + } + + xcb_xfixes_region_t region = create_region(x11, &output->exposed); + + pixman_region32_clear(&output->exposed); + + uint32_t serial = 0; + uint32_t options = 0; + uint64_t target_msc = output->last_msc; + xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial, + 0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc, + 0, 0, 0, NULL); + + if (region != XCB_NONE) { + xcb_xfixes_destroy_region(x11->xcb, region); + } +} diff --git a/include/backend/x11.h b/include/backend/x11.h index fcea4d257..c6d3350c9 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -46,6 +46,9 @@ struct wlr_x11_output { pixman_region32_t exposed; + // Last submitted buffer, to be re-submitted on expose events + struct wlr_buffer *current_buffer; + uint64_t last_msc; struct { @@ -143,5 +146,7 @@ void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *event); void handle_x11_present_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event); +void handle_x11_expose_event(struct wlr_x11_output *output, + xcb_expose_event_t *event); #endif