diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 8584d6a66..1b09320a6 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -11,6 +11,7 @@ packages: - wayland-dev - wayland-protocols - xcb-util-image-dev + - xcb-util-renderutil-dev - xcb-util-wm-dev sources: - https://github.com/swaywm/wlroots diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index ab0e48177..c43461790 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -11,6 +11,7 @@ packages: - wayland-protocols - xcb-util-errors - xcb-util-image + - xcb-util-renderutil - xcb-util-wm - seatd sources: diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 7789a0a79..aaa4eb13a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -18,6 +18,7 @@ packages: - x11/libxkbcommon - x11/pixman - x11/xcb-util-errors + - x11/xcb-util-renderutil - x11/xcb-util-wm - sysutils/seatd - gmake diff --git a/backend/x11/backend.c b/backend/x11/backend.c index fe66bdd8b..482524c98 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -355,6 +357,29 @@ static bool query_dri3_formats(struct wlr_x11_backend *x11) { return true; } +static void x11_get_argb32(struct wlr_x11_backend *x11) { + xcb_render_query_pict_formats_cookie_t cookie = + xcb_render_query_pict_formats(x11->xcb); + xcb_render_query_pict_formats_reply_t *reply = + xcb_render_query_pict_formats_reply(x11->xcb, cookie, NULL); + if (!reply) { + wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats"); + return; + } + + xcb_render_pictforminfo_t *format = + xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32); + + if (format == NULL) { + wlr_log(WLR_DEBUG, "No ARGB_32 render format"); + free(reply); + return; + } + + x11->argb32 = format->id; + free(reply); +} + struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display) { wlr_log(WLR_INFO, "Creating X11 backend"); @@ -629,13 +654,15 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, xcb_rectangle_t rect = { .x = 0, .y = 0, .width = 1, .height = 1 }; xcb_poly_fill_rectangle(x11->xcb, blank, gc, 1, &rect); - x11->cursor = xcb_generate_id(x11->xcb); - xcb_create_cursor(x11->xcb, x11->cursor, blank, blank, + x11->transparent_cursor = xcb_generate_id(x11->xcb); + xcb_create_cursor(x11->xcb, x11->transparent_cursor, blank, blank, 0, 0, 0, 0, 0, 0, 0, 0); xcb_free_gc(x11->xcb, gc); xcb_free_pixmap(x11->xcb, blank); + x11_get_argb32(x11); + return &x11->backend; error_event: diff --git a/backend/x11/meson.build b/backend/x11/meson.build index 51c6dd04e..b9f22a4a7 100644 --- a/backend/x11/meson.build +++ b/backend/x11/meson.build @@ -4,6 +4,8 @@ x11_required = [ 'xcb', 'xcb-dri3', 'xcb-present', + 'xcb-render', + 'xcb-renderutil', 'xcb-xfixes', 'xcb-xinput', ] diff --git a/backend/x11/output.c b/backend/x11/output.c index 07317637d..73723fc4e 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -7,12 +7,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "backend/x11.h" @@ -79,6 +81,11 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_list_remove(&output->link); wlr_buffer_unlock(output->back_buffer); wlr_swapchain_destroy(output->swapchain); + wlr_swapchain_destroy(output->cursor.swapchain); + if (output->cursor.pic != XCB_NONE) { + xcb_render_free_picture(x11->xcb, output->cursor.pic); + } + // A zero event mask deletes the event context xcb_present_select_input(x11->xcb, output->present_event_id, output->win, 0); xcb_destroy_window(x11->xcb, output->win); @@ -336,12 +343,177 @@ static void output_rollback_render(struct wlr_output *wlr_output) { wlr_renderer_bind_buffer(x11->renderer, NULL); } +static void update_x11_output_cursor(struct wlr_x11_output *output, + int32_t hotspot_x, int32_t hotspot_y) { + struct wlr_x11_backend *x11 = output->x11; + + xcb_cursor_t cursor = x11->transparent_cursor; + + if (output->cursor.pic != XCB_NONE) { + cursor = xcb_generate_id(x11->xcb); + xcb_render_create_cursor(x11->xcb, cursor, output->cursor.pic, + hotspot_x, hotspot_y); + } + + uint32_t values[] = {cursor}; + xcb_change_window_attributes(x11->xcb, output->win, + XCB_CW_CURSOR, values); + xcb_flush(x11->xcb); + + if (cursor != x11->transparent_cursor) { + xcb_free_cursor(x11->xcb, cursor); + } +} + +static bool output_cursor_to_picture(struct wlr_x11_output *output, + struct wlr_texture *texture, enum wl_output_transform transform, + int width, int height) { + struct wlr_x11_backend *x11 = output->x11; + int depth = 32; + int stride = width * 4; + + if (output->cursor.pic != XCB_NONE) { + xcb_render_free_picture(x11->xcb, output->cursor.pic); + } + output->cursor.pic = XCB_NONE; + + if (texture == NULL) { + return true; + } + + if (output->cursor.swapchain == NULL || + output->cursor.swapchain->width != width || + output->cursor.swapchain->height != height) { + wlr_swapchain_destroy(output->cursor.swapchain); + output->cursor.swapchain = wlr_swapchain_create( + x11->allocator, width, height, + x11->drm_format); + if (output->cursor.swapchain == NULL) { + return false; + } + } + + struct wlr_buffer *wlr_buffer = + wlr_swapchain_acquire(output->cursor.swapchain, NULL); + if (wlr_buffer == NULL) { + return false; + } + + if (!wlr_renderer_bind_buffer(x11->renderer, wlr_buffer)) { + return false; + } + + uint8_t *data = malloc(width * height * 4); + if (data == NULL) { + return false; + } + + struct wlr_box cursor_box = { + .width = width, + .height = height, + }; + + float projection[9]; + wlr_matrix_projection(projection, width, height, output->wlr_output.transform); + + float matrix[9]; + wlr_matrix_project_box(matrix, &cursor_box, transform, 0, projection); + + wlr_renderer_begin(x11->renderer, width, height); + wlr_renderer_clear(x11->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); + wlr_render_texture_with_matrix(x11->renderer, texture, matrix, 1.0); + wlr_renderer_end(x11->renderer); + + bool result = wlr_renderer_read_pixels( + x11->renderer, WL_SHM_FORMAT_ARGB8888, NULL, + width * 4, width, height, 0, 0, 0, 0, + data); + + wlr_renderer_bind_buffer(x11->renderer, NULL); + + wlr_buffer_unlock(wlr_buffer); + + if (!result) { + free(data); + return false; + } + + xcb_pixmap_t pix = xcb_generate_id(x11->xcb); + xcb_create_pixmap(x11->xcb, depth, pix, output->win, + width, height); + + output->cursor.pic = xcb_generate_id(x11->xcb); + xcb_render_create_picture(x11->xcb, output->cursor.pic, + pix, x11->argb32, 0, 0); + + xcb_gcontext_t gc = xcb_generate_id(x11->xcb); + xcb_create_gc(x11->xcb, gc, pix, 0, NULL); + + xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP, + pix, gc, width, height, 0, 0, 0, depth, + stride * height * sizeof(uint8_t), + data); + free(data); + xcb_free_gc(x11->xcb, gc); + xcb_free_pixmap(x11->xcb, pix); + + return true; +} + +static bool output_set_cursor(struct wlr_output *wlr_output, + struct wlr_texture *texture, float scale, + enum wl_output_transform transform, + int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { + struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); + struct wlr_x11_backend *x11 = output->x11; + int width = 0, height = 0; + + if (x11->argb32 == XCB_NONE) { + return false; + } + + if (texture != NULL) { + width = texture->width * wlr_output->scale / scale; + height = texture->height * wlr_output->scale / scale; + } + + struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; + wlr_box_transform(&hotspot, &hotspot, + wlr_output_transform_invert(wlr_output->transform), + width, height); + + if (!update_texture) { + // This means we previously had a failure of some sort. + if (texture != NULL && output->cursor.pic == XCB_NONE) { + return false; + } + + // Update hotspot without changing cursor image + update_x11_output_cursor(output, hotspot.x, hotspot.y); + return true; + } + + bool success = output_cursor_to_picture(output, texture, transform, + width, height); + + update_x11_output_cursor(output, hotspot.x, hotspot.y); + + return success; +} + +static bool output_move_cursor(struct wlr_output *_output, int x, int y) { + // TODO: only return true if x == current x and y == current y + return true; +} + static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .attach_render = output_attach_render, .test = output_test, .commit = output_commit, .rollback_render = output_rollback_render, + .set_cursor = output_set_cursor, + .move_cursor = output_move_cursor, }; struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { @@ -390,7 +562,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { 0, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, x11->colormap, - x11->cursor, + x11->transparent_cursor, }; output->win = xcb_generate_id(x11->xcb); xcb_create_window(x11->xcb, x11->depth->depth, output->win, diff --git a/include/backend/x11.h b/include/backend/x11.h index 1b440d2a5..3dfe2fe8a 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -51,6 +51,11 @@ struct wlr_x11_output { pixman_region32_t exposed; uint64_t last_msc; + + struct { + struct wlr_swapchain *swapchain; + xcb_render_picture_t pic; + } cursor; }; struct wlr_x11_touchpoint { @@ -70,7 +75,8 @@ struct wlr_x11_backend { xcb_depth_t *depth; xcb_visualid_t visualid; xcb_colormap_t colormap; - xcb_cursor_t cursor; + xcb_cursor_t transparent_cursor; + xcb_render_pictformat_t argb32; uint32_t dri3_major_version, dri3_minor_version; size_t requested_outputs;