From 26f9510e0774b328b7befad78b44d26ecba7fcbe Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 17:05:58 +0200 Subject: [PATCH 01/11] backend/wayland: extract layer test to separate function This is more readable. --- backend/wayland/output.c | 58 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bf668cc4e..291e98122 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -247,6 +247,40 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, return create_wl_buffer(wl, wlr_buffer); } +static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_state *layer_state) { + if (layer_state->buffer == NULL) { + return true; + } + + int x = layer_state->dst_box.x; + int y = layer_state->dst_box.y; + int width = layer_state->dst_box.width; + int height = layer_state->dst_box.height; + + // We can't display sub-surfaces out of the primary surface's bounds + int output_width = output->wlr_output.width; + int output_height = output->wlr_output.height; + if (x < 0 || y < 0 || x + width > output_width || y + height > output_height) { + return false; + } + + // We need viewporter for scaling and cropping + bool needs_viewport = width != layer_state->buffer->width || + height != layer_state->buffer->height; + if (!wlr_fbox_empty(&layer_state->src_box)) { + needs_viewport = needs_viewport || + layer_state->src_box.x != 0 || + layer_state->src_box.y != 0 || + layer_state->src_box.width != width || + layer_state->src_box.height != height; + } + if (output->backend->viewporter == NULL && needs_viewport) { + return false; + } + + return test_buffer(output->backend, layer_state->buffer); +} + static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = @@ -288,29 +322,7 @@ static bool output_test(struct wlr_output *wlr_output, bool supported = output->backend->subcompositor != NULL; for (ssize_t i = state->layers_len - 1; i >= 0; i--) { struct wlr_output_layer_state *layer_state = &state->layers[i]; - if (layer_state->buffer != NULL) { - int x = layer_state->dst_box.x; - int y = layer_state->dst_box.y; - int width = layer_state->dst_box.width; - int height = layer_state->dst_box.height; - bool needs_viewport = width != layer_state->buffer->width || - height != layer_state->buffer->height; - if (!wlr_fbox_empty(&layer_state->src_box)) { - needs_viewport = needs_viewport || - layer_state->src_box.x != 0 || - layer_state->src_box.y != 0 || - layer_state->src_box.width != width || - layer_state->src_box.height != height; - } - if (x < 0 || y < 0 || - x + width > wlr_output->width || - y + height > wlr_output->height || - (output->backend->viewporter == NULL && needs_viewport)) { - supported = false; - } - supported = supported && - test_buffer(output->backend, layer_state->buffer); - } + supported = supported && test_layer(output, layer_state); layer_state->accepted = supported; } } From 09c7af0bd4511047f1fed801945336c5315ac010 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 17:11:58 +0200 Subject: [PATCH 02/11] backend/wayland: extract layer viewport check to a helper Improves readability, and we'll need to use it from another function. --- backend/wayland/output.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 291e98122..1896e5088 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -247,6 +247,23 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, return create_wl_buffer(wl, wlr_buffer); } +static bool layer_needs_viewport(struct wlr_output_layer_state *layer_state) { + if (layer_state->buffer == NULL) { + return false; + } + if (layer_state->dst_box.width != layer_state->buffer->width || + layer_state->dst_box.height != layer_state->buffer->height) { + return true; + } + if (layer_state->src_box.x != 0 || + layer_state->src_box.y != 0 || + layer_state->src_box.width != layer_state->dst_box.width || + layer_state->src_box.height != layer_state->dst_box.height) { + return true; + } + return false; +} + static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_state *layer_state) { if (layer_state->buffer == NULL) { return true; @@ -265,16 +282,7 @@ static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_sta } // We need viewporter for scaling and cropping - bool needs_viewport = width != layer_state->buffer->width || - height != layer_state->buffer->height; - if (!wlr_fbox_empty(&layer_state->src_box)) { - needs_viewport = needs_viewport || - layer_state->src_box.x != 0 || - layer_state->src_box.y != 0 || - layer_state->src_box.width != width || - layer_state->src_box.height != height; - } - if (output->backend->viewporter == NULL && needs_viewport) { + if (output->backend->viewporter == NULL && layer_needs_viewport(layer_state)) { return false; } From 2bd587995084009be50e0ae089803b0bae72c5af Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 19:58:29 +0200 Subject: [PATCH 03/11] backend/wayland: do not require viewporter for empty layer src_box --- backend/wayland/output.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 1896e5088..0fc5c38dd 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -255,10 +255,11 @@ static bool layer_needs_viewport(struct wlr_output_layer_state *layer_state) { layer_state->dst_box.height != layer_state->buffer->height) { return true; } - if (layer_state->src_box.x != 0 || + if (!wlr_fbox_empty(&layer_state->src_box) && + (layer_state->src_box.x != 0 || layer_state->src_box.y != 0 || layer_state->src_box.width != layer_state->dst_box.width || - layer_state->src_box.height != layer_state->dst_box.height) { + layer_state->src_box.height != layer_state->dst_box.height)) { return true; } return false; From a863b82bdbefc9bf18b586ee25fcd69ba310826b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Feb 2023 10:46:08 +0100 Subject: [PATCH 04/11] output: add cursor output layers --- backend/wayland/output.c | 4 ++++ include/wlr/types/wlr_output_layer.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 0fc5c38dd..e950ce931 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -270,6 +270,10 @@ static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_sta return true; } + if (layer_state->layer->cursor) { + return false; // TODO: add support for cursor layers + } + int x = layer_state->dst_box.x; int y = layer_state->dst_box.y; int width = layer_state->dst_box.width; diff --git a/include/wlr/types/wlr_output_layer.h b/include/wlr/types/wlr_output_layer.h index f4aef0582..3db0aefc9 100644 --- a/include/wlr/types/wlr_output_layer.h +++ b/include/wlr/types/wlr_output_layer.h @@ -43,6 +43,7 @@ struct wlr_output_layer { struct wl_list link; // wlr_output.layers struct wlr_addon_set addons; + bool cursor; struct { struct wl_signal feedback; // struct wlr_output_layer_feedback_event @@ -71,6 +72,9 @@ struct wlr_output_layer_state { // Damaged region since last commit in buffer-local coordinates. Leave NULL // to damage the whole buffer. const pixman_region32_t *damage; + struct { + int x, y; + } cursor_hotspot; // Populated by the backend after wlr_output_test() and wlr_output_commit(), // indicates whether the backend has acknowledged and will take care of From 4157715e263d7728f235b801e9d192d08a70a864 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Feb 2023 10:46:25 +0100 Subject: [PATCH 05/11] output: add output_state_get_cursor_layer() helper --- include/types/wlr_output.h | 3 +++ types/output/state.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 22a5c4639..253f116a4 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -18,4 +18,7 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, float scale, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y); +struct wlr_output_layer_state *output_state_get_cursor_layer( + const struct wlr_output_state *state); + #endif diff --git a/types/output/state.c b/types/output/state.c index 41610f035..a5e00cc75 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "types/wlr_output.h" void wlr_output_state_init(struct wlr_output_state *state) { @@ -150,3 +151,19 @@ err: wlr_output_state_finish(©); return false; } + +struct wlr_output_layer_state *output_state_get_cursor_layer( + const struct wlr_output_state *state) { + if (!(state->committed & WLR_OUTPUT_STATE_LAYERS)) { + return NULL; + } + if (state->layers_len == 0) { + return NULL; + } + + struct wlr_output_layer_state *layer_state = &state->layers[state->layers_len - 1]; + if (!layer_state->layer->cursor) { + return NULL; + } + return layer_state; +} From bf4c20a05d1711bec50621b4efd8a4ff512f39e7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 11:18:23 +0200 Subject: [PATCH 06/11] output: add output_prepare_state() Doesn't do much yet, but will soon. --- types/output/output.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/types/output/output.c b/types/output/output.c index 4bd2f243c..7ec05fcd4 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -659,6 +659,15 @@ static bool output_basic_test(struct wlr_output *output, return true; } +static bool output_prepare_state(struct wlr_output *output, + struct wlr_output_state *state, bool *new_buffer) { + if (!output_ensure_buffer(output, state, new_buffer)) { + return false; + } + + return true; +} + bool wlr_output_test_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); @@ -676,7 +685,7 @@ bool wlr_output_test_state(struct wlr_output *output, } bool new_back_buffer = false; - if (!output_ensure_buffer(output, ©, &new_back_buffer)) { + if (!output_prepare_state(output, ©, &new_back_buffer)) { return false; } @@ -714,7 +723,7 @@ bool wlr_output_commit_state(struct wlr_output *output, } bool new_back_buffer = false; - if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { + if (!output_prepare_state(output, &pending, &new_back_buffer)) { return false; } From 9c442c7fd83efa348dce66236ffefc2b0b366a62 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 20:02:50 +0200 Subject: [PATCH 07/11] output: pass cursor as layer when set_cursor is unimplemented --- include/types/wlr_output.h | 3 ++ include/wlr/types/wlr_output.h | 2 + types/output/cursor.c | 78 ++++++++++++++++++++++++++++++---- types/output/output.c | 27 ++++++++++-- 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 253f116a4..58b547810 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -17,6 +17,9 @@ bool output_ensure_buffer(struct wlr_output *output, bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, float scale, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y); +bool output_get_cursor_layer_state(struct wlr_output *output, + struct wlr_buffer *buffer, int x, int y, int hotspot_x, int hotspot_y, + struct wlr_output_layer_state *out); struct wlr_output_layer_state *output_state_get_cursor_layer( const struct wlr_output_state *state); diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index f3ed7c105..1d0ecdff8 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -189,6 +189,8 @@ struct wlr_output { struct wlr_swapchain *cursor_swapchain; struct wlr_buffer *cursor_front_buffer; int software_cursor_locks; // number of locks forcing software cursors + struct wlr_output_layer *cursor_layer; + bool cursor_layer_changed; struct wl_list layers; // wlr_output_layer.link diff --git a/types/output/cursor.c b/types/output/cursor.c index eb6ac6f2c..aed7d5ccb 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -6,19 +6,72 @@ #include #include #include +#include #include #include #include "render/allocator/allocator.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" -static bool output_set_hardware_cursor(struct wlr_output *output, +bool output_get_cursor_layer_state(struct wlr_output *output, + struct wlr_buffer *buffer, int x, int y, int hotspot_x, int hotspot_y, + struct wlr_output_layer_state *out) { + if (output->cursor_layer == NULL) { + output->cursor_layer = wlr_output_layer_create(output); + if (output->cursor_layer == NULL) { + return false; + } + output->cursor_layer->cursor = true; + } + + *out = (struct wlr_output_layer_state){ + .layer = output->cursor_layer, + .buffer = buffer, + .dst_box = { + .x = x, + .y = y, + .width = buffer->width, + .height = buffer->height, + }, + .cursor_hotspot = { + .x = hotspot_x, + .y = hotspot_y, + }, + }; + return true; +} + +static bool output_test_hardware_cursor_layer(struct wlr_output *output, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { - if (!output->impl->set_cursor) { + if (buffer == NULL) { + return true; + } + if (!output->impl->test) { return false; } - if (!output->impl->set_cursor(output, buffer, hotspot_x, hotspot_y)) { + struct wlr_output_layer_state layer_state = {0}; + if (!output_get_cursor_layer_state(output, buffer, 0, 0, hotspot_x, hotspot_y, &layer_state)) { + return false; + } + + struct wlr_output_state output_state = { + .committed = WLR_OUTPUT_STATE_LAYERS, + .layers = &layer_state, + .layers_len = 1, + }; + return output->impl->test(output, &output_state) && layer_state.accepted; +} + +static bool output_set_hardware_cursor(struct wlr_output *output, + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { + bool ok; + if (!output->impl->set_cursor) { + ok = output_test_hardware_cursor_layer(output, buffer, hotspot_x, hotspot_y); + } else { + ok = output->impl->set_cursor(output, buffer, hotspot_x, hotspot_y); + } + if (!ok) { return false; } @@ -29,6 +82,11 @@ static bool output_set_hardware_cursor(struct wlr_output *output, output->cursor_front_buffer = wlr_buffer_lock(buffer); } + if (!output->impl->set_cursor) { + output->cursor_layer_changed = true; + wlr_output_update_needs_frame(output); + } + return true; } @@ -348,8 +406,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; - if (!output->impl->set_cursor || - output->software_cursor_locks > 0) { + if (output->software_cursor_locks > 0) { return false; } @@ -362,8 +419,9 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { // If the cursor was hidden or was a software cursor, the hardware // cursor position is outdated - output->impl->move_cursor(cursor->output, - (int)cursor->x, (int)cursor->y); + if (output->impl->move_cursor) { + output->impl->move_cursor(cursor->output, (int)cursor->x, (int)cursor->y); + } struct wlr_buffer *buffer = NULL; if (texture != NULL) { @@ -485,7 +543,11 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, return true; } - assert(cursor->output->impl->move_cursor); + if (!cursor->output->impl->move_cursor) { + cursor->output->cursor_layer_changed = true; + wlr_output_update_needs_frame(cursor->output); + return true; + } return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); } diff --git a/types/output/output.c b/types/output/output.c index 7ec05fcd4..59f139dd3 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -660,11 +660,28 @@ static bool output_basic_test(struct wlr_output *output, } static bool output_prepare_state(struct wlr_output *output, - struct wlr_output_state *state, bool *new_buffer) { + struct wlr_output_state *state, bool *new_buffer, + struct wlr_output_layer_state *cursor_state) { if (!output_ensure_buffer(output, state, new_buffer)) { return false; } + if (output->hardware_cursor != NULL && output->impl->set_cursor == NULL && + output->cursor_layer_changed) { + assert(output->cursor_layer != NULL); + + struct wlr_output_cursor *cursor = output->hardware_cursor; + if (!output_get_cursor_layer_state(output, output->cursor_front_buffer, + cursor->x, cursor->y, cursor->hotspot_x, cursor->hotspot_y, cursor_state)) { + return false; + } + + assert((state->committed & WLR_OUTPUT_STATE_LAYERS) == 0); + state->committed |= WLR_OUTPUT_STATE_LAYERS; + state->layers = cursor_state; + state->layers_len = 1; + } + return true; } @@ -685,7 +702,8 @@ bool wlr_output_test_state(struct wlr_output *output, } bool new_back_buffer = false; - if (!output_prepare_state(output, ©, &new_back_buffer)) { + struct wlr_output_layer_state cursor_state; + if (!output_prepare_state(output, ©, &new_back_buffer, &cursor_state)) { return false; } @@ -723,7 +741,8 @@ bool wlr_output_commit_state(struct wlr_output *output, } bool new_back_buffer = false; - if (!output_prepare_state(output, &pending, &new_back_buffer)) { + struct wlr_output_layer_state cursor_state; + if (!output_prepare_state(output, &pending, &new_back_buffer, &cursor_state)) { return false; } @@ -837,6 +856,8 @@ bool wlr_output_commit_state(struct wlr_output *output, } } + output->cursor_layer_changed = false; + struct wlr_output_event_commit event = { .output = output, .committed = pending.committed, From 6c89732d8b12cadbab082a39dc3659bf3f6b8e41 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 20:03:14 +0200 Subject: [PATCH 08/11] backend/x11: switch to cursor layers --- backend/x11/output.c | 99 +++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/backend/x11/output.c b/backend/x11/output.c index 175204fa3..711401f38 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -18,9 +18,11 @@ #include #include #include +#include #include #include "backend/x11.h" +#include "types/wlr_output.h" #include "util/time.h" static const uint32_t SUPPORTED_OUTPUT_STATE = @@ -112,6 +114,8 @@ static void output_destroy(struct wlr_output *wlr_output) { static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { + struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); + uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, @@ -138,6 +142,11 @@ static bool output_test(struct wlr_output *wlr_output, } } + struct wlr_output_layer_state *cursor_state = output_state_get_cursor_layer(state); + if (cursor_state != NULL) { + cursor_state->accepted = output->x11->argb32; + } + return true; } @@ -342,43 +351,6 @@ error: return false; } -static bool output_commit(struct wlr_output *wlr_output, - const struct wlr_output_state *state) { - struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); - struct wlr_x11_backend *x11 = output->x11; - - if (!output_test(wlr_output, state)) { - return false; - } - - if (state->committed & WLR_OUTPUT_STATE_ENABLED) { - if (state->enabled) { - xcb_map_window(x11->xcb, output->win); - } else { - xcb_unmap_window(x11->xcb, output->win); - } - } - - if (state->committed & WLR_OUTPUT_STATE_MODE) { - if (!output_set_custom_mode(wlr_output, - state->custom_mode.width, - state->custom_mode.height, - state->custom_mode.refresh)) { - return false; - } - } - - if (state->committed & WLR_OUTPUT_STATE_BUFFER) { - if (!output_commit_buffer(output, state)) { - return false; - } - } - - xcb_flush(x11->xcb); - - return true; -} - 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; @@ -394,7 +366,6 @@ static void update_x11_output_cursor(struct wlr_x11_output *output, 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); @@ -461,16 +432,43 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, return true; } -static bool output_set_cursor(struct wlr_output *wlr_output, - struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) { +static bool output_commit(struct wlr_output *wlr_output, + const struct wlr_output_state *state) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; - if (x11->argb32 == XCB_NONE) { + if (!output_test(wlr_output, state)) { return false; } - if (buffer != NULL) { + if (state->committed & WLR_OUTPUT_STATE_ENABLED) { + if (state->enabled) { + xcb_map_window(x11->xcb, output->win); + } else { + xcb_unmap_window(x11->xcb, output->win); + } + } + + if (state->committed & WLR_OUTPUT_STATE_MODE) { + if (!output_set_custom_mode(wlr_output, + state->custom_mode.width, + state->custom_mode.height, + state->custom_mode.refresh)) { + return false; + } + } + + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + if (!output_commit_buffer(output, state)) { + return false; + } + } + + struct wlr_output_layer_state *cursor_state = output_state_get_cursor_layer(state); + if (cursor_state != NULL) { + struct wlr_buffer *buffer = cursor_state->buffer; + int hotspot_x = cursor_state->cursor_hotspot.x; + int hotspot_y = cursor_state->cursor_hotspot.y; if (hotspot_x < 0) { hotspot_x = 0; } @@ -483,17 +481,16 @@ static bool output_set_cursor(struct wlr_output *wlr_output, if (hotspot_y > buffer->height) { hotspot_y = buffer->height; } + + bool ok = output_cursor_to_picture(output, buffer); + update_x11_output_cursor(output, hotspot_x, hotspot_y); + if (!ok) { + return false; + } } - bool success = output_cursor_to_picture(output, buffer); + xcb_flush(x11->xcb); - 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; } @@ -514,8 +511,6 @@ static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .test = output_test, .commit = output_commit, - .set_cursor = output_set_cursor, - .move_cursor = output_move_cursor, .get_primary_formats = output_get_primary_formats, }; From e0a69804ed23cadaba888e3d38bb84d46c419961 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jun 2023 20:03:26 +0200 Subject: [PATCH 09/11] backend/wayland: switch to cursor layers --- backend/wayland/output.c | 102 +++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index e950ce931..4b72bc331 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -19,6 +19,7 @@ #include "backend/wayland.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" +#include "types/wlr_output.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" @@ -270,10 +271,6 @@ static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_sta return true; } - if (layer_state->layer->cursor) { - return false; // TODO: add support for cursor layers - } - int x = layer_state->dst_box.x; int y = layer_state->dst_box.y; int width = layer_state->dst_box.width; @@ -294,6 +291,13 @@ static bool test_layer(struct wlr_wl_output *output, struct wlr_output_layer_sta return test_buffer(output->backend, layer_state->buffer); } +static bool test_cursor(struct wlr_wl_output *output, struct wlr_output_layer_state *layer_state) { + if (layer_state->buffer == NULL) { + return true; + } + return !layer_needs_viewport(layer_state) && test_buffer(output->backend, layer_state->buffer); +} + static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = @@ -330,10 +334,17 @@ static bool output_test(struct wlr_output *wlr_output, } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { + size_t layers_len = state->layers_len; + struct wlr_output_layer_state *cursor_state = output_state_get_cursor_layer(state); + if (cursor_state != NULL) { + cursor_state->accepted = test_cursor(output, cursor_state); + layers_len--; + } + // If we can't use a sub-surface for a layer, then we can't use a // sub-surface for any layer underneath bool supported = output->backend->subcompositor != NULL; - for (ssize_t i = state->layers_len - 1; i >= 0; i--) { + for (ssize_t i = layers_len - 1; i >= 0; i--) { struct wlr_output_layer_state *layer_state = &state->layers[i]; supported = supported && test_layer(output, layer_state); layer_state->accepted = supported; @@ -363,6 +374,7 @@ static const struct wlr_addon_interface output_layer_addon_impl = { static struct wlr_wl_output_layer *get_or_create_output_layer( struct wlr_wl_output *output, struct wlr_output_layer *wlr_layer) { assert(output->backend->subcompositor != NULL); + assert(!wlr_layer->cursor); struct wlr_wl_output_layer *layer; struct wlr_addon *addon = wlr_addon_find(&wlr_layer->addons, output, @@ -501,6 +513,10 @@ static bool commit_layers(struct wlr_wl_output *output, struct wlr_wl_output_layer *prev_layer = NULL; for (size_t i = 0; i < layers_len; i++) { + if (layers[i].layer->cursor) { + continue; + } + struct wlr_wl_output_layer *layer = get_or_create_output_layer(output, layers[i].layer); if (layer == NULL) { @@ -525,6 +541,36 @@ static bool commit_layers(struct wlr_wl_output *output, return true; } +static bool commit_cursor(struct wlr_wl_output *output, + const struct wlr_output_layer_state *cursor_state) { + struct wlr_wl_backend *backend = output->backend; + + output->cursor.hotspot_x = cursor_state->cursor_hotspot.x; + output->cursor.hotspot_y = cursor_state->cursor_hotspot.y; + + if (output->cursor.surface == NULL) { + output->cursor.surface = wl_compositor_create_surface(backend->compositor); + } + struct wl_surface *surface = output->cursor.surface; + + if (cursor_state->buffer != NULL) { + struct wlr_wl_buffer *buffer = get_or_create_wl_buffer(backend, cursor_state->buffer); + if (buffer == NULL) { + return false; + } + + wl_surface_attach(surface, buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); + } else { + wl_surface_attach(surface, NULL, 0, 0); + } + + wl_surface_commit(surface); + + update_wl_output_cursor(output); + return true; +} + static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = @@ -556,6 +602,11 @@ static bool output_commit(struct wlr_output *wlr_output, damage_surface(output->surface, damage); } + struct wlr_output_layer_state *cursor_state = output_state_get_cursor_layer(state); + if (cursor_state != NULL && !commit_cursor(output, cursor_state)) { + return false; + } + if ((state->committed & WLR_OUTPUT_STATE_LAYERS) && !commit_layers(output, state->layers, state->layers_len)) { return false; @@ -604,40 +655,6 @@ static bool output_commit(struct wlr_output *wlr_output, return true; } -static bool output_set_cursor(struct wlr_output *wlr_output, - struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) { - struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); - struct wlr_wl_backend *backend = output->backend; - - output->cursor.hotspot_x = hotspot_x; - output->cursor.hotspot_y = hotspot_y; - - if (output->cursor.surface == NULL) { - output->cursor.surface = - wl_compositor_create_surface(backend->compositor); - } - struct wl_surface *surface = output->cursor.surface; - - if (wlr_buffer != NULL) { - struct wlr_wl_buffer *buffer = - get_or_create_wl_buffer(output->backend, wlr_buffer); - if (buffer == NULL) { - return false; - } - - wl_surface_attach(surface, buffer->wl_buffer, 0, 0); - wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_commit(surface); - } else { - wl_surface_attach(surface, NULL, 0, 0); - wl_surface_commit(surface); - } - - update_wl_output_cursor(output); - wl_display_flush(backend->remote_display); - return true; -} - static const struct wlr_drm_format_set *output_get_formats( struct wlr_output *wlr_output, uint32_t buffer_caps) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); @@ -694,17 +711,10 @@ void update_wl_output_cursor(struct wlr_wl_output *output) { } } -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, .test = output_test, .commit = output_commit, - .set_cursor = output_set_cursor, - .move_cursor = output_move_cursor, .get_cursor_formats = output_get_formats, .get_primary_formats = output_get_formats, }; From ccf04f38d55d00061834530e0279de2909acdb8e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 7 Apr 2023 11:43:30 +0200 Subject: [PATCH 10/11] backend/drm: use output layers for cursors --- backend/drm/drm.c | 232 +++++++++++++++++++++++++--------------------- 1 file changed, 126 insertions(+), 106 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 8c639379f..d4597b66e 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -27,6 +27,7 @@ #include "render/pixel_format.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" +#include "types/wlr_output.h" #include "util/env.h" #include "config.h" @@ -534,17 +535,131 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn return true; } +static bool test_cursor_layer_state(struct wlr_drm_connector *conn, + struct wlr_output_layer_state *layer_state) { + struct wlr_drm_backend *drm = conn->backend; + + if (layer_state->buffer == NULL) { + return true; + } + + struct wlr_buffer *buffer = layer_state->buffer; + if ((uint64_t)buffer->width != drm->cursor_width || + (uint64_t)buffer->height != drm->cursor_height) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); + return false; + } + + if (buffer->width != layer_state->dst_box.width || + buffer->height != layer_state->dst_box.height) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor doesn't support scaling"); + return false; + } + + return true; +} + +static void drm_connector_set_pending_cursor_fb(struct wlr_drm_connector *conn, + const struct wlr_output_state *state) { + struct wlr_drm_backend *drm = conn->backend; + + struct wlr_drm_crtc *crtc = conn->crtc; + if (crtc == NULL) { + return; + } + struct wlr_drm_plane *plane = crtc->cursor; + if (plane == NULL) { + return; + } + + conn->cursor_enabled = false; + conn->cursor_width = 0; + conn->cursor_height = 0; + + struct wlr_output_layer_state *layer_state = output_state_get_cursor_layer(state); + if (layer_state == NULL || layer_state->buffer == NULL) { + return; + } + + struct wlr_buffer *buffer = layer_state->buffer; + + if (!test_cursor_layer_state(conn, layer_state)) { + return; + } + + struct wlr_buffer *local_buf; + if (drm->parent) { + struct wlr_drm_format format = {0}; + if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { + wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); + return; + } + + bool ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, + buffer->width, buffer->height, &format); + wlr_drm_format_finish(&format); + if (!ok) { + return; + } + + local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); + if (local_buf == NULL) { + return; + } + } else { + local_buf = wlr_buffer_lock(buffer); + } + + bool ok = drm_fb_import(&conn->cursor_pending_fb, drm, local_buf, &plane->formats); + wlr_buffer_unlock(local_buf); + if (!ok) { + return; + } + + // Update cursor hotspot + int hotspot_x = layer_state->cursor_hotspot.x; + int hotspot_y = layer_state->cursor_hotspot.y; + if (conn->cursor_hotspot_x != hotspot_x || conn->cursor_hotspot_y != hotspot_y) { + conn->cursor_x -= hotspot_x - conn->cursor_hotspot_x; + conn->cursor_y -= hotspot_y - conn->cursor_hotspot_y; + conn->cursor_hotspot_x = hotspot_x; + conn->cursor_hotspot_y = hotspot_y; + } + + conn->cursor_enabled = true; + conn->cursor_width = buffer->width; + conn->cursor_height = buffer->height; + + struct wlr_box box = { + .x = layer_state->dst_box.x, + .y = layer_state->dst_box.y, + }; + + int width, height; + wlr_output_transformed_resolution(&conn->output, &width, &height); + + enum wl_output_transform transform = + wlr_output_transform_invert(conn->output.transform); + wlr_box_transform(&box, &box, transform, width, height); + + conn->cursor_x = box.x - hotspot_x; + conn->cursor_y = box.y - hotspot_y; + + layer_state->accepted = true; +} + static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, const struct wlr_output_state *state) { struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; - if (!crtc || drm->parent) { + if (crtc == NULL) { return false; } - if (!crtc->liftoff) { - return true; // libliftoff is disabled + if (drm->parent != NULL || !crtc->liftoff) { + drm_connector_set_pending_cursor_fb(conn, state); + return true; } assert(state->committed & WLR_OUTPUT_STATE_LAYERS); @@ -628,6 +743,14 @@ static bool drm_connector_test(struct wlr_output *output, // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. ok = true; + + if (state->committed & WLR_OUTPUT_STATE_LAYERS) { + struct wlr_output_layer_state *cursor_layer_state = output_state_get_cursor_layer(state); + if (cursor_layer_state != NULL && test_cursor_layer_state(conn, cursor_layer_state)) { + cursor_layer_state->accepted = true; + } + } + goto out; } @@ -897,107 +1020,6 @@ const drmModeModeInfo *wlr_drm_mode_get_info(struct wlr_output_mode *wlr_mode) { return &mode->drm_mode; } -static bool drm_connector_set_cursor(struct wlr_output *output, - struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { - struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = conn->backend; - struct wlr_drm_crtc *crtc = conn->crtc; - - if (!crtc) { - return false; - } - - struct wlr_drm_plane *plane = crtc->cursor; - if (plane == NULL) { - return false; - } - - if (conn->cursor_hotspot_x != hotspot_x || - conn->cursor_hotspot_y != hotspot_y) { - // Update cursor hotspot - conn->cursor_x -= hotspot_x - conn->cursor_hotspot_x; - conn->cursor_y -= hotspot_y - conn->cursor_hotspot_y; - conn->cursor_hotspot_x = hotspot_x; - conn->cursor_hotspot_y = hotspot_y; - } - - conn->cursor_enabled = false; - if (buffer != NULL) { - if ((uint64_t)buffer->width != drm->cursor_width || - (uint64_t)buffer->height != drm->cursor_height) { - wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); - return false; - } - - struct wlr_buffer *local_buf; - if (drm->parent) { - struct wlr_drm_format format = {0}; - if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { - wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); - return false; - } - - bool ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, - buffer->width, buffer->height, &format); - wlr_drm_format_finish(&format); - if (!ok) { - return false; - } - - local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); - if (local_buf == NULL) { - return false; - } - } else { - local_buf = wlr_buffer_lock(buffer); - } - - bool ok = drm_fb_import(&conn->cursor_pending_fb, drm, local_buf, - &plane->formats); - wlr_buffer_unlock(local_buf); - if (!ok) { - return false; - } - - conn->cursor_enabled = true; - conn->cursor_width = buffer->width; - conn->cursor_height = buffer->height; - } - - wlr_output_update_needs_frame(output); - return true; -} - -static bool drm_connector_move_cursor(struct wlr_output *output, - int x, int y) { - struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - if (!conn->crtc) { - return false; - } - struct wlr_drm_plane *plane = conn->crtc->cursor; - if (!plane) { - return false; - } - - struct wlr_box box = { .x = x, .y = y }; - - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, width, height); - - box.x -= conn->cursor_hotspot_x; - box.y -= conn->cursor_hotspot_y; - - conn->cursor_x = box.x; - conn->cursor_y = box.y; - - wlr_output_update_needs_frame(output); - return true; -} - bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) { return conn->cursor_enabled && conn->cursor_x < conn->output.width && @@ -1073,8 +1095,6 @@ static const struct wlr_drm_format_set *drm_connector_get_primary_formats( } static const struct wlr_output_impl output_impl = { - .set_cursor = drm_connector_set_cursor, - .move_cursor = drm_connector_move_cursor, .destroy = drm_connector_destroy_output, .test = drm_connector_test, .commit = drm_connector_commit, From dd39fa5bd6ebaa0bd95c3b7c8de608d45f96226a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 3 Jul 2023 17:51:17 +0200 Subject: [PATCH 11/11] fixup! output: pass cursor as layer when set_cursor is unimplemented --- types/output/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index aed7d5ccb..432e2630f 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -82,7 +82,7 @@ static bool output_set_hardware_cursor(struct wlr_output *output, output->cursor_front_buffer = wlr_buffer_lock(buffer); } - if (!output->impl->set_cursor) { + if (!output->impl->set_cursor && output->cursor_layer != NULL) { output->cursor_layer_changed = true; wlr_output_update_needs_frame(output); }