compositor: introduce wlr_surface_reject_pending()

This commit is contained in:
Kirill Primak 2024-01-27 11:28:37 +03:00 committed by Simon Ser
parent 5ae8ce807a
commit 0052078bd3
8 changed files with 59 additions and 21 deletions

View file

@ -231,6 +231,9 @@ struct wlr_surface {
bool opaque; bool opaque;
bool handling_commit;
bool pending_rejected;
int32_t preferred_buffer_scale; int32_t preferred_buffer_scale;
bool preferred_buffer_transform_sent; bool preferred_buffer_transform_sent;
enum wl_output_transform preferred_buffer_transform; enum wl_output_transform preferred_buffer_transform;
@ -290,6 +293,15 @@ void wlr_surface_map(struct wlr_surface *surface);
*/ */
void wlr_surface_unmap(struct wlr_surface *surface); void wlr_surface_unmap(struct wlr_surface *surface);
/**
* Mark the pending state of a surface as rejected due to a protocol violation,
* preventing it from being cached or committed.
*
* This function must only be used while processing a commit request.
*/
void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource *resource,
uint32_t code, const char *msg, ...);
/** /**
* Whether or not this surface currently has an attached buffer. A surface has * Whether or not this surface currently has an attached buffer. A surface has
* an attached buffer when it commits with a non-null buffer in its pending * an attached buffer when it commits with a non-null buffer in its pending

View file

@ -1,4 +1,5 @@
#include <assert.h> #include <assert.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
@ -200,7 +201,7 @@ static void surface_finalize_pending(struct wlr_surface *surface) {
"is not divisible by scale (%d)", pending->buffer_width, "is not divisible by scale (%d)", pending->buffer_width,
pending->buffer_height, pending->scale); pending->buffer_height, pending->scale);
} else { } else {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(surface, surface->resource,
WL_SURFACE_ERROR_INVALID_SIZE, WL_SURFACE_ERROR_INVALID_SIZE,
"Buffer size (%dx%d) is not divisible by scale (%d)", "Buffer size (%dx%d) is not divisible by scale (%d)",
pending->buffer_width, pending->buffer_height, pending->scale); pending->buffer_width, pending->buffer_height, pending->scale);
@ -567,6 +568,8 @@ static void surface_commit_state(struct wlr_surface *surface,
static void surface_handle_commit(struct wl_client *client, static void surface_handle_commit(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource); struct wlr_surface *surface = wlr_surface_from_resource(resource);
surface->handling_commit = true;
surface_finalize_pending(surface); surface_finalize_pending(surface);
if (surface->role != NULL && surface->role->client_commit != NULL && if (surface->role != NULL && surface->role->client_commit != NULL &&
@ -576,6 +579,11 @@ static void surface_handle_commit(struct wl_client *client,
wl_signal_emit_mutable(&surface->events.client_commit, NULL); wl_signal_emit_mutable(&surface->events.client_commit, NULL);
surface->handling_commit = false;
if (surface->pending_rejected) {
return;
}
if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) {
surface_cache_pending(surface); surface_cache_pending(surface);
} else { } else {
@ -853,6 +861,26 @@ void wlr_surface_unmap(struct wlr_surface *surface) {
} }
} }
void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource *resource,
uint32_t code, const char *msg, ...) {
assert(surface->handling_commit);
if (surface->pending_rejected) {
return;
}
va_list args;
va_start(args, msg);
// XXX: libwayland could expose wl_resource_post_error_vargs() instead
char buffer[128]; // Matches the size of the buffer used in libwayland
vsnprintf(buffer, sizeof(buffer), msg, args);
wl_resource_post_error(resource, code, "%s", buffer);
surface->pending_rejected = true;
va_end(args);
}
bool wlr_surface_set_role(struct wlr_surface *surface, const struct wlr_surface_role *role, bool wlr_surface_set_role(struct wlr_surface *surface, const struct wlr_surface_role *role,
struct wl_resource *error_resource, uint32_t error_code) { struct wl_resource *error_resource, uint32_t error_code) {
assert(role != NULL); assert(role != NULL);

View file

@ -331,7 +331,7 @@ static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) {
} }
if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) { if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(wlr_surface, surface->resource,
ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED, ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED,
"layer_surface has never been configured"); "layer_surface has never been configured");
return; return;
@ -341,7 +341,7 @@ static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) {
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if (surface->pending.desired_width == 0 && if (surface->pending.desired_width == 0 &&
(surface->pending.anchor & horiz) != horiz) { (surface->pending.anchor & horiz) != horiz) {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(wlr_surface, surface->resource,
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE,
"width 0 requested without setting left and right anchors"); "width 0 requested without setting left and right anchors");
return; return;
@ -351,7 +351,7 @@ static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) {
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (surface->pending.desired_height == 0 && if (surface->pending.desired_height == 0 &&
(surface->pending.anchor & vert) != vert) { (surface->pending.anchor & vert) != vert) {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(wlr_surface, surface->resource,
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE,
"height 0 requested without setting top and bottom anchors"); "height 0 requested without setting top and bottom anchors");
return; return;

View file

@ -157,14 +157,14 @@ static void lock_surface_role_client_commit(struct wlr_surface *surface) {
} }
if (!wlr_surface_state_has_buffer(&surface->pending)) { if (!wlr_surface_state_has_buffer(&surface->pending)) {
wl_resource_post_error(lock_surface->resource, wlr_surface_reject_pending(surface, lock_surface->resource,
EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER,
"session lock surface is committed with a null buffer"); "session lock surface is committed with a null buffer");
return; return;
} }
if (!lock_surface->configured) { if (!lock_surface->configured) {
wl_resource_post_error(lock_surface->resource, wlr_surface_reject_pending(surface, lock_surface->resource,
EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK,
"session lock surface has never been configured"); "session lock surface has never been configured");
return; return;
@ -172,7 +172,7 @@ static void lock_surface_role_client_commit(struct wlr_surface *surface) {
if ((uint32_t)surface->pending.width != lock_surface->pending.width || if ((uint32_t)surface->pending.width != lock_surface->pending.width ||
(uint32_t)surface->pending.height != lock_surface->pending.height) { (uint32_t)surface->pending.height != lock_surface->pending.height) {
wl_resource_post_error(lock_surface->resource, wlr_surface_reject_pending(surface, lock_surface->resource,
EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH, EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH,
"committed surface dimensions do not match last acked configure"); "committed surface dimensions do not match last acked configure");
return; return;

View file

@ -151,7 +151,8 @@ static void viewport_handle_surface_client_commit(struct wl_listener *listener,
if (!state->viewport.has_dst && if (!state->viewport.has_dst &&
(floor(state->viewport.src.width) != state->viewport.src.width || (floor(state->viewport.src.width) != state->viewport.src.width ||
floor(state->viewport.src.height) != state->viewport.src.height)) { floor(state->viewport.src.height) != state->viewport.src.height)) {
wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_BAD_SIZE, wlr_surface_reject_pending(viewport->surface,
viewport->resource, WP_VIEWPORT_ERROR_BAD_SIZE,
"wl_viewport.set_source width and height must be integers " "wl_viewport.set_source width and height must be integers "
"when the destination rectangle is unset"); "when the destination rectangle is unset");
return; return;
@ -159,7 +160,8 @@ static void viewport_handle_surface_client_commit(struct wl_listener *listener,
if (state->viewport.has_src && state->buffer != NULL && if (state->viewport.has_src && state->buffer != NULL &&
!check_src_buffer_bounds(state)) { !check_src_buffer_bounds(state)) {
wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_OUT_OF_BUFFER, wlr_surface_reject_pending(viewport->surface,
viewport->resource, WP_VIEWPORT_ERROR_OUT_OF_BUFFER,
"source rectangle out of buffer bounds"); "source rectangle out of buffer bounds");
return; return;
} }

View file

@ -240,9 +240,8 @@ static struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat(
void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup) { void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup) {
if (!popup->parent) { if (!popup->parent) {
wl_resource_post_error(popup->base->resource, wlr_surface_reject_pending(popup->base->surface, popup->base->resource,
XDG_SURFACE_ERROR_NOT_CONSTRUCTED, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_popup has no parent");
"xdg_popup has no parent");
return; return;
} }
} }

View file

@ -261,16 +261,14 @@ static void xdg_surface_role_client_commit(struct wlr_surface *wlr_surface) {
assert(surface != NULL); assert(surface != NULL);
if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) { if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(wlr_surface, surface->resource,
XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured");
"xdg_surface has never been configured");
return; return;
} }
if (surface->role_resource == NULL) { if (surface->role_resource == NULL) {
wl_resource_post_error(surface->resource, wlr_surface_reject_pending(wlr_surface, surface->resource,
XDG_SURFACE_ERROR_NOT_CONSTRUCTED, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role object");
"xdg_surface must have a role object");
return; return;
} }

View file

@ -125,9 +125,8 @@ void handle_xdg_toplevel_client_commit(struct wlr_xdg_toplevel *toplevel) {
pending->max_width < 0 || pending->max_height < 0 || pending->max_width < 0 || pending->max_height < 0 ||
(pending->max_width != 0 && pending->max_width < pending->min_width) || (pending->max_width != 0 && pending->max_width < pending->min_width) ||
(pending->max_height != 0 && pending->max_height < pending->min_height)) { (pending->max_height != 0 && pending->max_height < pending->min_height)) {
wl_resource_post_error(toplevel->resource, wlr_surface_reject_pending(toplevel->base->surface, toplevel->resource,
XDG_TOPLEVEL_ERROR_INVALID_SIZE, XDG_TOPLEVEL_ERROR_INVALID_SIZE, "client provided an invalid min or max size");
"client provided an invalid min or max size");
return; return;
} }
} }