wlroots-hyprland/types/xdg_shell/wlr_xdg_surface.c

626 lines
18 KiB
C
Raw Permalink Normal View History

2018-05-14 23:28:45 +02:00
#include <assert.h>
#include <stdlib.h>
#include <string.h>
2021-07-01 22:36:01 +02:00
#include <wlr/util/box.h>
2018-05-14 23:28:45 +02:00
#include <wlr/util/log.h>
#include "types/wlr_xdg_shell.h"
static void xdg_surface_configure_destroy(
struct wlr_xdg_surface_configure *configure) {
if (configure == NULL) {
return;
}
wl_list_remove(&configure->link);
free(configure->toplevel_configure);
2018-05-14 23:28:45 +02:00
free(configure);
}
2023-07-26 10:36:24 +02:00
// An xdg_surface implementation is reset, when:
// 1) a surface is unmapped due to a commit with NULL buffer, or
// 2) the xdg_surface role object is destroyed, or
// 3) wlr_xdg_surface is destroyed
// An xdg_surface role object implementation is reset, when:
// 1) a surface is unmapped due to a commit with NULL buffer, or
// 2) the xdg_surface role object implementation is destroyed
static void reset_xdg_surface(struct wlr_xdg_surface *surface) {
surface->configured = false;
surface->initialized = false;
2018-05-14 23:28:45 +02:00
struct wlr_xdg_popup *popup, *popup_tmp;
wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) {
wlr_xdg_popup_destroy(popup);
}
2023-07-26 10:36:24 +02:00
struct wlr_xdg_surface_configure *configure, *tmp;
wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
xdg_surface_configure_destroy(configure);
}
if (surface->configure_idle) {
wl_event_source_remove(surface->configure_idle);
surface->configure_idle = NULL;
}
}
static void reset_xdg_surface_role_object(struct wlr_xdg_surface *surface) {
2018-05-14 23:28:45 +02:00
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel != NULL) {
reset_xdg_toplevel(surface->toplevel);
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup != NULL) {
reset_xdg_popup(surface->popup);
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_NONE:
break;
2018-05-14 23:28:45 +02:00
}
}
static void xdg_surface_handle_ack_configure(struct wl_client *client,
struct wl_resource *resource, uint32_t serial) {
struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource);
if (surface == NULL) {
return;
}
2018-05-14 23:28:45 +02:00
if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) {
wl_resource_post_error(surface->resource,
XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
"xdg_surface must have a role");
return;
}
// First find the ack'ed configure
2018-05-14 23:28:45 +02:00
bool found = false;
struct wlr_xdg_surface_configure *configure, *tmp;
wl_list_for_each(configure, &surface->configure_list, link) {
if (configure->serial == serial) {
2018-05-14 23:28:45 +02:00
found = true;
break;
}
}
if (!found) {
wl_resource_post_error(surface->client->resource,
XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE,
"wrong configure serial: %u", serial);
return;
}
// Then remove old configures from the list
wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
if (configure->serial == serial) {
break;
}
2022-08-18 13:16:16 +02:00
wl_signal_emit_mutable(&surface->events.ack_configure, configure);
xdg_surface_configure_destroy(configure);
}
2018-05-14 23:28:45 +02:00
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_NONE:
assert(0 && "not reached");
break;
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel != NULL) {
handle_xdg_toplevel_ack_configure(surface->toplevel,
configure->toplevel_configure);
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup != NULL) {
handle_xdg_popup_ack_configure(surface->popup,
configure->popup_configure);
}
2018-05-14 23:28:45 +02:00
break;
}
surface->configured = true;
surface->pending.configure_serial = serial;
2018-05-14 23:28:45 +02:00
2022-08-18 13:16:16 +02:00
wl_signal_emit_mutable(&surface->events.ack_configure, configure);
2018-05-14 23:28:45 +02:00
xdg_surface_configure_destroy(configure);
}
static void surface_send_configure(void *user_data) {
struct wlr_xdg_surface *surface = user_data;
surface->configure_idle = NULL;
struct wlr_xdg_surface_configure *configure = calloc(1, sizeof(*configure));
2018-05-14 23:28:45 +02:00
if (configure == NULL) {
wl_client_post_no_memory(surface->client->client);
return;
}
wl_list_insert(surface->configure_list.prev, &configure->link);
configure->serial = surface->scheduled_serial;
2018-06-10 12:15:26 +02:00
configure->surface = surface;
2018-05-14 23:28:45 +02:00
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_NONE:
assert(0 && "not reached");
break;
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel != NULL) {
configure->toplevel_configure =
send_xdg_toplevel_configure(surface->toplevel);
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup != NULL) {
configure->popup_configure =
send_xdg_popup_configure(surface->popup);
}
2018-05-14 23:28:45 +02:00
break;
}
2022-08-18 13:16:16 +02:00
wl_signal_emit_mutable(&surface->events.configure, configure);
2018-06-10 12:15:26 +02:00
2018-05-14 23:28:45 +02:00
xdg_surface_send_configure(surface->resource, configure->serial);
}
uint32_t wlr_xdg_surface_schedule_configure(struct wlr_xdg_surface *surface) {
2018-05-14 23:28:45 +02:00
struct wl_display *display = wl_client_get_display(surface->client->client);
struct wl_event_loop *loop = wl_display_get_event_loop(display);
if (!surface->initialized) {
wlr_log(WLR_ERROR, "A configure is scheduled for an uninitialized xdg_surface %p",
surface);
}
if (surface->configure_idle == NULL) {
surface->scheduled_serial = wl_display_next_serial(display);
2018-05-14 23:28:45 +02:00
surface->configure_idle = wl_event_loop_add_idle(loop,
surface_send_configure, surface);
if (surface->configure_idle == NULL) {
wl_client_post_no_memory(surface->client->client);
}
2018-05-14 23:28:45 +02:00
}
return surface->scheduled_serial;
2018-06-10 12:15:26 +02:00
}
2018-05-14 23:28:45 +02:00
static void xdg_surface_handle_get_popup(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *parent_resource,
struct wl_resource *positioner_resource) {
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_from_resource(resource);
assert(xdg_surface != NULL);
struct wlr_xdg_surface *parent = NULL;
if (parent_resource != NULL) {
parent = wlr_xdg_surface_from_resource(parent_resource);
}
struct wlr_xdg_positioner *positioner =
wlr_xdg_positioner_from_resource(positioner_resource);
2018-05-14 23:28:45 +02:00
create_xdg_popup(xdg_surface, parent, positioner, id);
}
static void xdg_surface_handle_get_toplevel(struct wl_client *client,
struct wl_resource *resource, uint32_t id) {
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_from_resource(resource);
assert(xdg_surface != NULL);
2018-05-14 23:28:45 +02:00
create_xdg_toplevel(xdg_surface, id);
}
static void xdg_surface_handle_set_window_geometry(struct wl_client *client,
struct wl_resource *resource, int32_t x, int32_t y, int32_t width,
int32_t height) {
struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource);
assert(surface != NULL);
2018-05-14 23:28:45 +02:00
if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) {
wl_resource_post_error(surface->resource,
XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
"xdg_surface must have a role");
return;
}
if (width <= 0 || height <= 0) {
2022-11-04 19:56:11 +01:00
wl_resource_post_error(resource,
XDG_SURFACE_ERROR_INVALID_SIZE,
"Tried to set invalid xdg-surface geometry");
return;
}
surface->pending.geometry.x = x;
surface->pending.geometry.y = y;
surface->pending.geometry.width = width;
surface->pending.geometry.height = height;
2018-05-14 23:28:45 +02:00
}
static void xdg_surface_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource);
if (surface == NULL) {
return;
}
2018-05-14 23:28:45 +02:00
2023-07-26 10:36:24 +02:00
if (surface->role_resource != NULL) {
wl_resource_post_error(resource,
XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT,
"surface was destroyed before its role object");
2018-05-14 23:28:45 +02:00
return;
}
wl_resource_destroy(resource);
}
static const struct xdg_surface_interface xdg_surface_implementation = {
.destroy = xdg_surface_handle_destroy,
.get_toplevel = xdg_surface_handle_get_toplevel,
.get_popup = xdg_surface_handle_get_popup,
.ack_configure = xdg_surface_handle_ack_configure,
.set_window_geometry = xdg_surface_handle_set_window_geometry,
};
static void xdg_surface_role_client_commit(struct wlr_surface *wlr_surface) {
2023-07-26 10:36:24 +02:00
struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface);
assert(surface != NULL);
2018-05-14 23:28:45 +02:00
if (wlr_surface_state_has_buffer(&wlr_surface->pending) && !surface->configured) {
wlr_surface_reject_pending(wlr_surface, surface->resource,
XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured");
2018-05-14 23:28:45 +02:00
return;
}
2023-07-26 10:36:24 +02:00
if (surface->role_resource == NULL) {
wlr_surface_reject_pending(wlr_surface, surface->resource,
XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role object");
return;
}
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_NONE:
assert(0 && "not reached");
return;
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel != NULL) {
handle_xdg_toplevel_client_commit(surface->toplevel);
}
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup != NULL) {
handle_xdg_popup_client_commit(surface->popup);
}
break;
}
}
static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) {
struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface);
assert(surface != NULL);
if (surface->surface->unmap_commit) {
2023-07-26 10:36:24 +02:00
reset_xdg_surface_role_object(surface);
reset_xdg_surface(surface);
assert(!surface->initial_commit);
surface->initial_commit = false;
} else {
surface->initial_commit = !surface->initialized;
surface->initialized = true;
2023-07-26 10:36:24 +02:00
}
2018-05-14 23:28:45 +02:00
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_NONE:
2023-07-26 10:36:24 +02:00
assert(0 && "not reached");
return;
2018-05-14 23:28:45 +02:00
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel == NULL) {
2023-07-26 10:36:24 +02:00
return;
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup == NULL) {
2023-07-26 10:36:24 +02:00
return;
}
2018-05-14 23:28:45 +02:00
break;
}
2023-07-26 10:36:24 +02:00
if (wlr_surface_has_buffer(wlr_surface)) {
2023-03-04 20:14:50 +01:00
wlr_surface_map(wlr_surface);
2018-05-14 23:28:45 +02:00
}
}
2023-07-26 10:36:24 +02:00
static void xdg_surface_role_destroy(struct wlr_surface *wlr_surface) {
struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface);
2023-07-26 10:36:24 +02:00
if (surface == NULL) {
// This is the only time xdg_surface can be inert
return;
}
2023-07-26 10:36:24 +02:00
destroy_xdg_surface(surface);
}
static const struct wlr_surface_role xdg_surface_role = {
2023-07-26 10:36:24 +02:00
.name = "xdg_surface",
.client_commit = xdg_surface_role_client_commit,
2023-07-26 10:36:24 +02:00
.commit = xdg_surface_role_commit,
.destroy = xdg_surface_role_destroy,
};
static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_xdg_surface_state),
};
2023-07-26 10:36:24 +02:00
struct wlr_xdg_surface *wlr_xdg_surface_try_from_wlr_surface(
struct wlr_surface *surface) {
if (surface->role != &xdg_surface_role || surface->role_resource == NULL) {
return NULL;
}
return wlr_xdg_surface_from_resource(surface->role_resource);
2018-05-14 23:28:45 +02:00
}
void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_surface,
2018-05-14 23:28:45 +02:00
uint32_t id) {
2023-07-26 10:36:24 +02:00
if (!wlr_surface_set_role(wlr_surface, &xdg_surface_role, client->resource,
XDG_WM_BASE_ERROR_ROLE)) {
return;
}
if (wlr_surface_has_buffer(wlr_surface)) {
wl_resource_post_error(client->resource,
XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER,
"xdg_surface must not have a buffer at creation");
return;
}
struct wlr_xdg_surface *surface = calloc(1, sizeof(*surface));
if (surface == NULL) {
2018-05-14 23:28:45 +02:00
wl_client_post_no_memory(client->client);
return;
2018-05-14 23:28:45 +02:00
}
if (!wlr_surface_synced_init(&surface->synced, wlr_surface,
&surface_synced_impl, &surface->pending, &surface->current)) {
goto error_surface;
}
surface->client = client;
surface->role = WLR_XDG_SURFACE_ROLE_NONE;
surface->surface = wlr_surface;
surface->resource = wl_resource_create(client->client,
2018-05-14 23:28:45 +02:00
&xdg_surface_interface, wl_resource_get_version(client->resource),
id);
if (surface->resource == NULL) {
goto error_synced;
2018-05-14 23:28:45 +02:00
}
wl_list_init(&surface->configure_list);
wl_list_init(&surface->popups);
2018-05-14 23:28:45 +02:00
wl_signal_init(&surface->events.destroy);
wl_signal_init(&surface->events.ping_timeout);
wl_signal_init(&surface->events.new_popup);
wl_signal_init(&surface->events.configure);
wl_signal_init(&surface->events.ack_configure);
2018-05-14 23:28:45 +02:00
wlr_log(WLR_DEBUG, "new xdg_surface %p (res %p)", surface,
surface->resource);
wl_resource_set_implementation(surface->resource,
2023-07-26 10:36:24 +02:00
&xdg_surface_implementation, surface, NULL);
wl_list_insert(&client->surfaces, &surface->link);
2023-07-26 10:36:24 +02:00
wlr_surface_set_role_object(wlr_surface, surface->resource);
wl_signal_emit_mutable(&surface->client->shell->events.new_surface, surface);
return;
error_synced:
wlr_surface_synced_finish(&surface->synced);
error_surface:
free(surface);
wl_client_post_no_memory(client->client);
2018-05-14 23:28:45 +02:00
}
2023-07-26 10:36:24 +02:00
bool set_xdg_surface_role(struct wlr_xdg_surface *surface, enum wlr_xdg_surface_role role) {
assert(role != WLR_XDG_SURFACE_ROLE_NONE);
static const char *role_names[] = {
[WLR_XDG_SURFACE_ROLE_TOPLEVEL] = "xdg_toplevel",
[WLR_XDG_SURFACE_ROLE_POPUP] = "xdg_popup",
};
if (surface->role != WLR_XDG_SURFACE_ROLE_NONE && surface->role != role) {
wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_ROLE,
"Cannot assign role %s to xdg_surface@%" PRIu32 ", already has role %s",
role_names[role], wl_resource_get_id(surface->resource),
role_names[surface->role]);
return false;
}
if (surface->role_resource != NULL) {
wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_ROLE,
"Cannot reassign role %s to xdg_surface@%" PRIu32 ", role object still exists",
role_names[role], wl_resource_get_id(surface->resource));
return false;
2018-05-14 23:28:45 +02:00
}
2023-07-26 10:36:24 +02:00
surface->role = role;
return true;
}
static void destroy_xdg_surface_role_object(struct wlr_xdg_surface *surface) {
if (surface->role_resource == NULL) {
return;
xdg_shell: destroy children popups with parent surface popups have a link in parent's surface->popups list and needs to be freed before: ==6902==ERROR: AddressSanitizer: heap-use-after-free on address 0x6120001a0300 at pc 0x7fc1447acb50 bp 0x7fffd396e680 sp 0x7fffd396e670 WRITE of size 8 at 0x6120001a0300 thread T0 #0 0x7fc1447acb4f in wl_list_remove ../util/signal.c:55 #1 0x7fc14477d206 in destroy_xdg_popup_v6 ../types/xdg_shell_v6/wlr_xdg_popup_v6.c:162 #2 0x7fc1447816e0 in destroy_xdg_surface_v6 ../types/xdg_shell_v6/wlr_xdg_surface_v6.c:108 #3 0x7fc144a1c025 in destroy_resource src/wayland-server.c:688 #4 0x7fc144a1c091 in wl_resource_destroy src/wayland-server.c:705 #5 0x7fc14477fd6f in xdg_client_v6_handle_resource_destroy ../types/xdg_shell_v6/wlr_xdg_shell_v6.c:72 #6 0x7fc144a1c025 in destroy_resource src/wayland-server.c:688 #7 0x7fc144a20851 (/lib64/libwayland-server.so.0+0xc851) #8 0x7fc144a20d92 (/lib64/libwayland-server.so.0+0xcd92) #9 0x7fc144a1c140 in wl_client_destroy src/wayland-server.c:847 #10 0x7fc144a1c21c in destroy_client_with_error src/wayland-server.c:307 #11 0x7fc144a1c21c in wl_client_connection_data src/wayland-server.c:330 #12 0x7fc144a1df01 in wl_event_loop_dispatch src/event-loop.c:641 #13 0x7fc144a1c601 in wl_display_run src/wayland-server.c:1260 #14 0x40a2f4 in main ../sway/main.c:433 #15 0x7fc143ef718a in __libc_start_main ../csu/libc-start.c:308 #16 0x40b749 in _start (/opt/wayland/bin/sway+0x40b749) 0x6120001a0300 is located 64 bytes inside of 264-byte region [0x6120001a02c0,0x6120001a03c8) freed by thread T0 here: #0 0x7fc14690d880 in __interceptor_free (/lib64/libasan.so.5+0xee880) #1 0x7fc1447acce8 in wlr_signal_emit_safe ../util/signal.c:29 #2 0x7fc1447a3cac in surface_handle_resource_destroy ../types/wlr_surface.c:576 #3 0x7fc144a1c025 in destroy_resource src/wayland-server.c:688 previously allocated by thread T0 here: #0 0x7fc14690de50 in calloc (/lib64/libasan.so.5+0xeee50) #1 0x7fc144781d38 in create_xdg_surface_v6 ../types/xdg_shell_v6/wlr_xdg_surface_v6.c:415 #2 0x7fc14147503d in ffi_call_unix64 (/lib64/libffi.so.6+0x603d) Alternative would be to have popups listen to the parent's surface destroy event and remove themselves from the list at this point OR on their own destroy, whichever happens first, but that seems more complicated for little benefit.
2018-06-25 00:35:13 +02:00
}
switch (surface->role) {
2023-07-26 10:36:24 +02:00
case WLR_XDG_SURFACE_ROLE_NONE:
assert(0 && "not reached");
break;
2018-05-14 23:28:45 +02:00
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
if (surface->toplevel != NULL) {
destroy_xdg_toplevel(surface->toplevel);
}
2018-05-14 23:28:45 +02:00
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup != NULL) {
destroy_xdg_popup(surface->popup);
}
2018-05-14 23:28:45 +02:00
break;
}
2023-07-26 10:36:24 +02:00
surface->role_resource = NULL;
wl_list_remove(&surface->role_resource_destroy.link);
wl_list_init(&surface->role_resource_destroy.link);
}
static void xdg_surface_handle_role_resource_destroy(struct wl_listener *listener, void *data) {
struct wlr_xdg_surface *surface = wl_container_of(listener, surface, role_resource_destroy);
destroy_xdg_surface_role_object(surface);
reset_xdg_surface(surface);
}
void set_xdg_surface_role_object(struct wlr_xdg_surface *surface,
struct wl_resource *role_resource) {
assert(surface->role != WLR_XDG_SURFACE_ROLE_NONE);
assert(surface->role_resource == NULL);
assert(role_resource != NULL);
surface->role_resource = role_resource;
surface->role_resource_destroy.notify = xdg_surface_handle_role_resource_destroy;
wl_resource_add_destroy_listener(role_resource, &surface->role_resource_destroy);
}
void destroy_xdg_surface(struct wlr_xdg_surface *surface) {
destroy_xdg_surface_role_object(surface);
reset_xdg_surface(surface);
wl_signal_emit_mutable(&surface->events.destroy, NULL);
2023-07-26 10:36:24 +02:00
wl_list_remove(&surface->link);
wlr_surface_synced_finish(&surface->synced);
2023-07-26 10:36:24 +02:00
wl_resource_set_user_data(surface->resource, NULL);
free(surface);
}
2018-05-14 23:28:45 +02:00
struct wlr_xdg_surface *wlr_xdg_surface_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &xdg_surface_interface,
&xdg_surface_implementation));
return wl_resource_get_user_data(resource);
}
void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface) {
if (surface->client->ping_serial != 0) {
// already pinged
return;
}
surface->client->ping_serial =
wl_display_next_serial(wl_client_get_display(surface->client->client));
wl_event_source_timer_update(surface->client->ping_timer,
surface->client->shell->ping_timeout);
xdg_wm_base_send_ping(surface->client->resource,
surface->client->ping_serial);
}
void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup,
2018-05-14 23:28:45 +02:00
double *popup_sx, double *popup_sy) {
struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(popup->parent);
assert(parent != NULL);
struct wlr_box parent_geo;
wlr_xdg_surface_get_geometry(parent, &parent_geo);
*popup_sx = parent_geo.x + popup->current.geometry.x -
popup->base->current.geometry.x;
*popup_sy = parent_geo.y + popup->current.geometry.y -
popup->base->current.geometry.y;
2018-05-14 23:28:45 +02:00
}
struct wlr_surface *wlr_xdg_surface_surface_at(
struct wlr_xdg_surface *surface, double sx, double sy,
double *sub_x, double *sub_y) {
struct wlr_surface *sub = wlr_xdg_surface_popup_surface_at(surface, sx, sy,
sub_x, sub_y);
if (sub != NULL) {
return sub;
}
return wlr_surface_surface_at(surface->surface, sx, sy, sub_x, sub_y);
}
struct wlr_surface *wlr_xdg_surface_popup_surface_at(
struct wlr_xdg_surface *surface, double sx, double sy,
double *sub_x, double *sub_y) {
struct wlr_xdg_popup *popup;
wl_list_for_each(popup, &surface->popups, link) {
2023-03-04 20:14:50 +01:00
if (!popup->base->surface->mapped) {
continue;
}
2018-05-14 23:28:45 +02:00
double popup_sx, popup_sy;
wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy);
2018-05-14 23:28:45 +02:00
struct wlr_surface *sub = wlr_xdg_surface_surface_at(
popup->base, sx - popup_sx, sy - popup_sy,
2018-05-14 23:28:45 +02:00
sub_x, sub_y);
if (sub != NULL) {
return sub;
}
}
return NULL;
2018-05-14 23:28:45 +02:00
}
struct xdg_surface_iterator_data {
wlr_surface_iterator_func_t user_iterator;
void *user_data;
int x, y;
};
static void xdg_surface_iterator(struct wlr_surface *surface,
int sx, int sy, void *data) {
struct xdg_surface_iterator_data *iter_data = data;
iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy,
iter_data->user_data);
}
static void xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface,
int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) {
struct wlr_xdg_popup *popup;
wl_list_for_each(popup, &surface->popups, link) {
2023-03-04 20:14:50 +01:00
if (!popup->base->surface->mapped) {
continue;
}
double popup_sx, popup_sy;
wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy);
struct xdg_surface_iterator_data data = {
.user_iterator = iterator,
.user_data = user_data,
.x = x + popup_sx, .y = y + popup_sy,
};
wlr_surface_for_each_surface(popup->base->surface,
xdg_surface_iterator, &data);
xdg_surface_for_each_popup_surface(popup->base,
x + popup_sx, y + popup_sy, iterator, user_data);
}
}
2018-05-14 23:28:45 +02:00
void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface,
wlr_surface_iterator_func_t iterator, void *user_data) {
wlr_surface_for_each_surface(surface->surface, iterator, user_data);
xdg_surface_for_each_popup_surface(surface, 0, 0, iterator, user_data);
2018-05-14 23:28:45 +02:00
}
void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface,
wlr_surface_iterator_func_t iterator, void *user_data) {
xdg_surface_for_each_popup_surface(surface, 0, 0, iterator, user_data);
}
void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface,
struct wlr_box *box) {
wlr_surface_get_extends(surface->surface, box);
/* The client never set the geometry */
if (wlr_box_empty(&surface->current.geometry)) {
return;
}
wlr_box_intersection(box, &surface->current.geometry, box);
}