xdg-popup: protocol errors and ungrab

This commit is contained in:
Tony Crisci 2017-10-06 08:41:43 -04:00
parent 67ea307753
commit 27ee171d25
1 changed files with 131 additions and 91 deletions

View File

@ -37,9 +37,100 @@ static void resource_destroy(struct wl_client *client,
wl_resource_destroy(resource); wl_resource_destroy(resource);
} }
static struct wlr_xdg_surface_v6 *xdg_popup_grab_get_topmost(
struct wlr_xdg_popup_grab_v6 *grab) {
struct wlr_xdg_popup_v6 *popup;
wl_list_for_each(popup, &grab->popups, grab_link) {
return popup->base;
}
return NULL;
}
static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) {
struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
struct wlr_xdg_popup_v6 *popup, *tmp;
wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) {
zxdg_popup_v6_send_popup_done(popup->resource);
}
wlr_seat_pointer_end_grab(grab->seat);
}
static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab,
struct wlr_surface *surface, double sx, double sy) {
struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
if (wl_resource_get_client(surface->resource) == popup_grab->client) {
wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
} else {
wlr_seat_pointer_clear_focus(grab->seat);
}
}
static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab,
uint32_t time, double sx, double sy) {
wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
}
static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab,
uint32_t time, uint32_t button, uint32_t state) {
uint32_t serial =
wlr_seat_pointer_send_button(grab->seat, time, button, state);
if (serial) {
return serial;
} else {
xdg_pointer_grab_end(grab);
return 0;
}
}
static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab,
uint32_t time, enum wlr_axis_orientation orientation, double value) {
wlr_seat_pointer_send_axis(grab->seat, time, orientation, value);
}
static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) {
xdg_pointer_grab_end(grab);
}
static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = {
.enter = xdg_pointer_grab_enter,
.motion = xdg_pointer_grab_motion,
.button = xdg_pointer_grab_button,
.cancel = xdg_pointer_grab_cancel,
.axis = xdg_pointer_grab_axis,
};
static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat(
struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) {
struct wlr_xdg_popup_grab_v6 *xdg_grab;
wl_list_for_each(xdg_grab, &shell->popup_grabs, link) {
if (xdg_grab->seat == seat) {
return xdg_grab;
}
}
xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab_v6));
if (!xdg_grab) {
return NULL;
}
xdg_grab->pointer_grab.data = xdg_grab;
xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl;
// TODO: keyboard grab
wl_list_init(&xdg_grab->popups);
wl_list_insert(&shell->popup_grabs, &xdg_grab->link);
xdg_grab->seat = seat;
return xdg_grab;
}
static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
wl_signal_emit(&surface->events.destroy, surface); wl_signal_emit(&surface->events.destroy, surface);
wl_resource_set_user_data(surface->resource, NULL);
if (surface->configure_idle) { if (surface->configure_idle) {
wl_event_source_remove(surface->configure_idle); wl_event_source_remove(surface->configure_idle);
@ -56,13 +147,36 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
} }
if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
// TODO: get the current grab and if it has no more popups, end the grab
wl_resource_set_user_data(surface->popup_state->resource, NULL); wl_resource_set_user_data(surface->popup_state->resource, NULL);
if (surface->popup_state->seat) {
struct wlr_xdg_popup_grab_v6 *grab =
xdg_shell_popup_grab_from_seat(surface->client->shell,
surface->popup_state->seat);
struct wlr_xdg_surface_v6 *topmost =
xdg_popup_grab_get_topmost(grab);
if (topmost != surface) {
wl_resource_post_error(surface->client->resource,
ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
"xdg_popup was destroyed while it was not the topmost "
"popup.");
}
wl_list_remove(&surface->popup_state->grab_link);
if (wl_list_empty(&grab->popups) &&
grab->seat->pointer_state.grab == &grab->pointer_grab) {
wlr_seat_pointer_end_grab(grab->seat);
}
}
wl_list_remove(&surface->popup_link); wl_list_remove(&surface->popup_link);
wl_list_remove(&surface->popup_state->grab_link);
free(surface->popup_state); free(surface->popup_state);
} }
wl_resource_set_user_data(surface->resource, NULL);
wl_list_remove(&surface->link); wl_list_remove(&surface->link);
wl_list_remove(&surface->surface_destroy_listener.link); wl_list_remove(&surface->surface_destroy_listener.link);
wl_list_remove(&surface->surface_commit_listener.link); wl_list_remove(&surface->surface_commit_listener.link);
@ -206,98 +320,11 @@ static void xdg_shell_create_positioner(struct wl_client *wl_client,
positioner, xdg_positioner_destroy); positioner, xdg_positioner_destroy);
} }
static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) {
struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
struct wlr_xdg_popup_v6 *popup, *tmp;
wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) {
wl_list_remove(&popup->grab_link);
wl_list_init(&popup->grab_link);
zxdg_popup_v6_send_popup_done(popup->resource);
}
wlr_seat_pointer_end_grab(grab->seat);
}
static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab,
struct wlr_surface *surface, double sx, double sy) {
struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
if (wl_resource_get_client(surface->resource) == popup_grab->client) {
wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
} else {
wlr_seat_pointer_clear_focus(grab->seat);
}
}
static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab,
uint32_t time, double sx, double sy) {
wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
}
static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab,
uint32_t time, uint32_t button, uint32_t state) {
uint32_t serial =
wlr_seat_pointer_send_button(grab->seat, time, button, state);
if (serial) {
return serial;
} else {
xdg_pointer_grab_end(grab);
return 0;
}
}
static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab,
uint32_t time, enum wlr_axis_orientation orientation, double value) {
wlr_seat_pointer_send_axis(grab->seat, time, orientation, value);
}
static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) {
xdg_pointer_grab_end(grab);
}
static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = {
.enter = xdg_pointer_grab_enter,
.motion = xdg_pointer_grab_motion,
.button = xdg_pointer_grab_button,
.cancel = xdg_pointer_grab_cancel,
.axis = xdg_pointer_grab_axis,
};
static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat(
struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) {
struct wlr_xdg_popup_grab_v6 *xdg_grab;
wl_list_for_each(xdg_grab, &shell->popup_grabs, link) {
if (xdg_grab->seat == seat) {
return xdg_grab;
}
}
xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab_v6));
if (!xdg_grab) {
return NULL;
}
xdg_grab->pointer_grab.data = xdg_grab;
xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl;
// TODO: keyboard grab
wl_list_init(&xdg_grab->popups);
wl_list_insert(&shell->popup_grabs, &xdg_grab->link);
xdg_grab->seat = seat;
return xdg_grab;
}
static void xdg_popup_protocol_grab(struct wl_client *client, static void xdg_popup_protocol_grab(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *seat_resource, struct wl_resource *resource, struct wl_resource *seat_resource,
uint32_t serial) { uint32_t serial) {
struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource);
struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource);
//bool parent_is_toplevel =
//surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL;
// TODO: handle make sure the grab is valid for the protocol
if (surface->popup_state->committed) { if (surface->popup_state->committed) {
wl_resource_post_error(surface->popup_state->resource, wl_resource_post_error(surface->popup_state->resource,
@ -310,7 +337,20 @@ static void xdg_popup_protocol_grab(struct wl_client *client,
xdg_shell_popup_grab_from_seat(surface->client->shell, xdg_shell_popup_grab_from_seat(surface->client->shell,
handle->wlr_seat); handle->wlr_seat);
struct wlr_xdg_surface_v6 *topmost = xdg_popup_grab_get_topmost(popup_grab);
bool parent_is_toplevel =
surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL;
if ((topmost == NULL && !parent_is_toplevel) ||
(topmost != NULL && topmost != surface->popup_state->parent)) {
wl_resource_post_error(surface->client->resource,
ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
"xdg_popup was not created on the topmost popup");
return;
}
popup_grab->client = surface->client->client; popup_grab->client = surface->client->client;
surface->popup_state->seat = handle->wlr_seat;
wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link);
@ -423,10 +463,10 @@ static void xdg_surface_get_popup(struct wl_client *client,
} }
surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP; surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP;
surface->popup_state->base = surface;
surface->popup_state->parent = parent; surface->popup_state->parent = parent;
surface->popup_state->geometry = surface->popup_state->geometry =
xdg_positioner_get_geometry(positioner, surface, parent); xdg_positioner_get_geometry(positioner, surface, parent);
wl_list_init(&surface->popup_state->grab_link);
wl_list_insert(&surface->popup_state->parent->popups, wl_list_insert(&surface->popup_state->parent->popups,
&surface->popup_link); &surface->popup_link);