Merge branch 'cursor-layer' into 'master'

Draft: Use output layers for cursors

See merge request wlroots/wlroots!4032
This commit is contained in:
Simon Ser 2023-07-15 13:29:57 +00:00
commit 6dfcdb0a31
9 changed files with 404 additions and 233 deletions

View file

@ -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"
@ -536,17 +537,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);
@ -630,6 +745,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;
}
@ -899,107 +1022,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 &&
@ -1075,8 +1097,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,

View file

@ -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"
@ -248,6 +249,56 @@ 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 (!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)) {
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;
}
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
if (output->backend->viewporter == NULL && layer_needs_viewport(layer_state)) {
return false;
}
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 =
@ -284,34 +335,19 @@ 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];
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;
}
}
@ -339,6 +375,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,
@ -477,6 +514,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) {
@ -501,6 +542,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 =
@ -532,6 +603,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;
@ -580,40 +656,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);
@ -670,17 +712,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,
};

View file

@ -18,9 +18,11 @@
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layer.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "types/wlr_output.h"
#include "util/time.h"
static const uint32_t SUPPORTED_OUTPUT_STATE =
@ -113,6 +115,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,
@ -139,6 +143,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;
}
@ -343,43 +352,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;
@ -395,7 +367,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);
@ -462,16 +433,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;
}
@ -484,17 +482,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;
}
@ -515,8 +512,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,
};

View file

@ -17,5 +17,11 @@ 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);
#endif

View file

@ -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

View file

@ -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

View file

@ -6,19 +6,72 @@
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layer.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#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 != NULL) {
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);
}

View file

@ -661,6 +661,32 @@ 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,
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;
}
bool wlr_output_test_state(struct wlr_output *output,
const struct wlr_output_state *state) {
uint32_t unchanged = output_compare_state(output, state);
@ -678,7 +704,8 @@ bool wlr_output_test_state(struct wlr_output *output,
}
bool new_back_buffer = false;
if (!output_ensure_buffer(output, &copy, &new_back_buffer)) {
struct wlr_output_layer_state cursor_state;
if (!output_prepare_state(output, &copy, &new_back_buffer, &cursor_state)) {
return false;
}
@ -716,7 +743,8 @@ bool wlr_output_commit_state(struct wlr_output *output,
}
bool new_back_buffer = false;
if (!output_ensure_buffer(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;
}
@ -830,6 +858,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,

View file

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_output_layer.h>
#include "types/wlr_output.h"
void wlr_output_state_init(struct wlr_output_state *state) {
@ -150,3 +151,19 @@ err:
wlr_output_state_finish(&copy);
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;
}