diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 26c56cab..9a7387ce 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -50,16 +51,6 @@ static void handle_x11_event(struct wlr_x11_backend *x11, } break; } - case XCB_CONFIGURE_NOTIFY: { - xcb_configure_notify_event_t *ev = - (xcb_configure_notify_event_t *)event; - struct wlr_x11_output *output = - get_x11_output_from_window_id(x11, ev->window); - if (output != NULL) { - handle_x11_configure_notify(output, ev); - } - break; - } case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event; if (ev->data.data32[0] == x11->atoms.wm_delete_window) { @@ -73,7 +64,10 @@ static void handle_x11_event(struct wlr_x11_backend *x11, } case XCB_GE_GENERIC: { xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event; - if (ev->extension == x11->xinput_opcode) { + if (ev->extension == x11->present_opcode) { + handle_x11_present_event(x11, + (xcb_present_generic_event_t *)ev); + } else if (ev->extension == x11->xinput_opcode) { handle_x11_xinput_event(x11, ev); } } @@ -224,6 +218,30 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const xcb_query_extension_reply_t *ext; + /* Present extension */ + + ext = xcb_get_extension_data(x11->xcb, &xcb_present_id); + if (!ext || !ext->present) { + wlr_log(WLR_ERROR, "X11 does not support Present extension"); + goto error_display; + } + x11->present_opcode = ext->major_opcode; + + xcb_present_query_version_cookie_t present_cookie = + xcb_present_query_version(x11->xcb, 1, 2); + xcb_present_query_version_reply_t *present_reply = + xcb_present_query_version_reply(x11->xcb, present_cookie, NULL); + + if (!present_reply || (present_reply->major_version <= 1 && + present_reply->minor_version < 2)) { + wlr_log(WLR_ERROR, "X11 does not support required Present version"); + free(present_reply); + goto error_display; + } + free(present_reply); + + /* Xfixes extension */ + ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xfixes extension"); @@ -242,6 +260,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } free(fixes_reply); + /* Xinput extension */ + ext = xcb_get_extension_data(x11->xcb, &xcb_input_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xinput extension"); diff --git a/backend/x11/meson.build b/backend/x11/meson.build index 19e873ab..62b280e5 100644 --- a/backend/x11/meson.build +++ b/backend/x11/meson.build @@ -2,8 +2,9 @@ x11_libs = [] x11_required = [ 'x11-xcb', 'xcb', - 'xcb-xinput', + 'xcb-present', 'xcb-xfixes', + 'xcb-xinput', ] foreach lib : x11_required diff --git a/backend/x11/output.c b/backend/x11/output.c index 1307f785..c65e39cc 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -15,13 +16,6 @@ #include "backend/x11.h" #include "util/signal.h" -static int signal_frame(void *data) { - struct wlr_x11_output *output = data; - wlr_output_send_frame(&output->wlr_output); - wl_event_source_timer_update(output->frame_timer, output->frame_delay); - return 0; -} - static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb) { const xcb_setup_t *xcb_setup = xcb_get_setup(xcb); @@ -40,26 +34,11 @@ static struct wlr_x11_output *get_x11_output_from_output( return (struct wlr_x11_output *)wlr_output; } -static void output_set_refresh(struct wlr_output *wlr_output, int32_t refresh) { - struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); - - if (refresh <= 0) { - refresh = X11_DEFAULT_REFRESH; - } - - wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width, - wlr_output->height, refresh); - - output->frame_delay = 1000000 / refresh; -} - static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; - output_set_refresh(&output->wlr_output, refresh); - const uint32_t values[] = { width, height }; xcb_void_cookie_t cookie = xcb_configure_window_checked( x11->xcb, output->win, @@ -84,7 +63,6 @@ static void output_destroy(struct wlr_output *wlr_output) { wlr_input_device_destroy(&output->touch_dev); wl_list_remove(&output->link); - wl_event_source_remove(output->frame_timer); wlr_egl_destroy_surface(&x11->egl, output->surf); xcb_destroy_window(x11->xcb, output->win); xcb_flush(x11->xcb); @@ -140,19 +118,15 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { struct wlr_output *wlr_output = &output->wlr_output; wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); - wlr_output->width = 1024; - wlr_output->height = 768; - - output_set_refresh(&output->wlr_output, 0); + wlr_output_update_custom_mode(&output->wlr_output, 1024, 768, 0); + wlr_output_update_enabled(wlr_output, true); snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", ++x11->last_output_num); parse_xcb_setup(wlr_output, x11->xcb); uint32_t mask = XCB_CW_EVENT_MASK; - uint32_t values[] = { - XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY - }; + uint32_t values[] = { XCB_EVENT_MASK_EXPOSURE }; output->win = xcb_generate_id(x11->xcb); xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win, x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1, @@ -176,6 +150,11 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { }; xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head); + output->present_context = xcb_generate_id(x11->xcb); + xcb_present_select_input(x11->xcb, output->present_context, output->win, + XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY | + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); + output->surf = wlr_egl_create_surface(&x11->egl, &output->win); if (!output->surf) { wlr_log(WLR_ERROR, "Failed to create EGL surface"); @@ -189,17 +168,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_x11_output_set_title(wlr_output, NULL); - xcb_map_window(x11->xcb, output->win); - xcb_flush(x11->xcb); - - struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display); - output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); - wl_list_insert(&x11->outputs, &output->link); - wl_event_source_timer_update(output->frame_timer, output->frame_delay); - wlr_output_update_enabled(wlr_output, true); - wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER, &input_device_impl, "X11 pointer", 0, 0); wlr_pointer_init(&output->pointer, &pointer_impl); @@ -217,22 +187,60 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev); wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev); + // Start the vsync loop + xcb_present_notify_msc(x11->xcb, output->win, 0, 0, 1, 0); + xcb_map_window(x11->xcb, output->win); + xcb_flush(x11->xcb); + return wlr_output; } -void handle_x11_configure_notify(struct wlr_x11_output *output, - xcb_configure_notify_event_t *ev) { - // ignore events that set an invalid size: - if (ev->width > 0 && ev->height > 0) { - wlr_output_update_custom_mode(&output->wlr_output, ev->width, - ev->height, output->wlr_output.refresh); +void handle_x11_present_event(struct wlr_x11_backend *x11, + xcb_present_generic_event_t *base) { + struct wlr_x11_output *output; + + switch (base->evtype) { + case XCB_PRESENT_CONFIGURE_NOTIFY: { + xcb_present_configure_notify_event_t *ev = + (xcb_present_configure_notify_event_t *)base; + + output = get_x11_output_from_window_id(x11, ev->window); + if (!output || output->present_context != ev->event) { + break; + } + + if (ev->width <= 0 || ev->height <= 0) { + break; + } + + /* + * We don't need to resize anything here ourselves. + * The EGL/OpenGL driver does that automatically. + */ + wlr_output_update_custom_mode(&output->wlr_output, + ev->width, ev->height, 0); // Move the pointer to its new location update_x11_pointer_position(output, output->x11->time); - } else { - wlr_log(WLR_DEBUG, - "Ignoring X11 configure event for height=%d, width=%d", - ev->width, ev->height); + + break; + } + case XCB_PRESENT_COMPLETE_NOTIFY: { + xcb_present_complete_notify_event_t *ev = + (xcb_present_complete_notify_event_t *)base; + + output = get_x11_output_from_window_id(x11, ev->window); + if (!output || output->present_context != ev->event) { + break; + } + + wlr_output_send_frame(&output->wlr_output); + + xcb_present_notify_msc(x11->xcb, output->win, 0, 0, 1, 0); + xcb_flush(x11->xcb); + + break; + } } } diff --git a/include/backend/x11.h b/include/backend/x11.h index e628e4cb..59cb8e44 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,8 +17,6 @@ #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f -#define X11_DEFAULT_REFRESH (60 * 1000) // 60 Hz - struct wlr_x11_backend; struct wlr_x11_output { @@ -35,8 +34,7 @@ struct wlr_x11_output { struct wlr_input_device touch_dev; struct wl_list touchpoints; // wlr_x11_touchpoint::link - struct wl_event_source *frame_timer; - int frame_delay; + xcb_present_event_t present_context; bool cursor_hidden; }; @@ -78,6 +76,7 @@ struct wlr_x11_backend { xcb_timestamp_t time; uint8_t xinput_opcode; + uint8_t present_opcode; struct wl_listener display_destroy; }; @@ -97,7 +96,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, void update_x11_pointer_position(struct wlr_x11_output *output, xcb_timestamp_t time); -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_present_generic_event_t *ev); #endif