wayland: implement xdg_popup support for context menus
- Listen for xdg_shell new_popup signal - Create scene tree for popups as children of parent toplevel - Unconstrain popup geometry on initial commit - Store xdg_surface->data for parent lookup by nested popups - Remove redundant wl_update_pointer_focus from button handler, let wlr_seat manage implicit grabs internally
This commit is contained in:
parent
4a58fd90eb
commit
6011f4226f
|
|
@ -630,6 +630,11 @@ wl_display_open(const char *name)
|
|||
wl_signal_add(&wl_state.xdg_shell->events.new_toplevel,
|
||||
&wl_state.xdg_new_surface_listener);
|
||||
|
||||
extern void handle_xdg_new_popup(struct wl_listener *listener, void *data);
|
||||
wl_state.xdg_new_popup_listener.notify = handle_xdg_new_popup;
|
||||
wl_signal_add(&wl_state.xdg_shell->events.new_popup,
|
||||
&wl_state.xdg_new_popup_listener);
|
||||
|
||||
#ifdef USE_XWAYLAND
|
||||
/* Create XWayland server for X11 client support */
|
||||
wl_state.xwayland = wlr_xwayland_create(wl_state.display,
|
||||
|
|
|
|||
|
|
@ -350,13 +350,6 @@ handle_pointer_button(struct wl_listener *listener, void *data)
|
|||
W_WL_EnqueueEvent(wmscr, &wev, target);
|
||||
}
|
||||
|
||||
/* Refresh pointer focus before delivering button event.
|
||||
* The last wl_update_pointer_focus call was during the most recent
|
||||
* motion event. If no motion happened since then, the XWayland
|
||||
* surface might not have pointer focus, causing
|
||||
* wlr_seat_pointer_notify_button to be a no-op. */
|
||||
wl_update_pointer_focus(ev->time_msec);
|
||||
|
||||
/* Suppress forwarding to the client when the WM modifier key is held
|
||||
* on a button press over a client surface. In this case frameMouseDown
|
||||
* will handle the event (move or resize), and the app must not see it. */
|
||||
|
|
@ -372,9 +365,7 @@ handle_pointer_button(struct wl_listener *listener, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
if (forward_to_client &&
|
||||
(wl_state.seat->pointer_state.focused_surface ||
|
||||
ev->state == WLR_BUTTON_RELEASED)) {
|
||||
if (forward_to_client) {
|
||||
wlr_seat_pointer_notify_button(wl_state.seat, ev->time_msec,
|
||||
ev->button, ev->state);
|
||||
wlr_seat_pointer_notify_frame(wl_state.seat);
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ struct wl_compositor_state {
|
|||
struct wl_listener decoration_new_listener;
|
||||
/* Phase 36: XDG toplevel tracking */
|
||||
struct wl_listener xdg_new_surface_listener;
|
||||
struct wl_listener xdg_new_popup_listener;
|
||||
int managed_surface_count; /* surfaces seen */
|
||||
/* Phase 37: periodic timer that drives client frame callbacks */
|
||||
struct wl_event_source *frame_timer;
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
|
|||
view->scene_node = wlr_scene_xdg_surface_create(
|
||||
_vp, view->xdg_surface);
|
||||
if (view->scene_node) {
|
||||
view->xdg_surface->data = view->scene_node;
|
||||
view_attach_scene_destroy_listener(view);
|
||||
/* Deferred reparent: move client surface under its
|
||||
* frame grouping tree now that the node exists. */
|
||||
|
|
@ -684,3 +685,115 @@ handle_xdg_new_toplevel(struct wl_listener *listener, void *data)
|
|||
toplevel->app_id ? toplevel->app_id : "(none)",
|
||||
toplevel->title ? toplevel->title : "(none)");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* XDG Popup handling */
|
||||
|
||||
static void handle_popup_commit(struct wl_listener *listener, void *data);
|
||||
static void handle_popup_destroy(struct wl_listener *listener, void *data);
|
||||
|
||||
struct wl_popup_ctx {
|
||||
struct wlr_xdg_popup *popup;
|
||||
struct wl_listener commit;
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
static void
|
||||
handle_popup_commit(struct wl_listener *listener, void *data)
|
||||
{
|
||||
(void)data;
|
||||
struct wl_popup_ctx *ctx = wl_container_of(listener, ctx, commit);
|
||||
struct wlr_xdg_popup *popup = ctx->popup;
|
||||
|
||||
if (!popup->base->initialized)
|
||||
return;
|
||||
|
||||
/* Unconstrain popup on initial configure so it fits on screen.
|
||||
* The box is in the root parent toplevel's coordinate space. */
|
||||
if (!popup->base->configured) {
|
||||
/* Walk up to the root toplevel via parent surfaces */
|
||||
struct wlr_xdg_surface *parent =
|
||||
wlr_xdg_surface_try_from_wlr_surface(popup->parent);
|
||||
while (parent && parent->role == WLR_XDG_SURFACE_ROLE_POPUP) {
|
||||
parent = wlr_xdg_surface_try_from_wlr_surface(parent->popup->parent);
|
||||
}
|
||||
|
||||
int toplevel_x = 0, toplevel_y = 0;
|
||||
if (parent && parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
||||
struct wl_toplevel_view *pv =
|
||||
wl_find_view_by_xdg_surface(parent);
|
||||
if (pv) {
|
||||
toplevel_x = pv->x;
|
||||
toplevel_y = pv->y;
|
||||
}
|
||||
}
|
||||
|
||||
WScreen *scr = wScreenWithNumber(0);
|
||||
struct wlr_box box = {
|
||||
.x = -toplevel_x,
|
||||
.y = -toplevel_y,
|
||||
.width = scr ? scr->scr_width : 1920,
|
||||
.height = scr ? scr->scr_height : 1080,
|
||||
};
|
||||
wlr_xdg_popup_unconstrain_from_box(popup, &box);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_popup_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
(void)data;
|
||||
struct wl_popup_ctx *ctx = wl_container_of(listener, ctx, destroy);
|
||||
wl_list_remove(&ctx->commit.link);
|
||||
wl_list_remove(&ctx->destroy.link);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
handle_xdg_new_popup(struct wl_listener *listener, void *data)
|
||||
{
|
||||
(void)listener;
|
||||
struct wlr_xdg_popup *popup = data;
|
||||
|
||||
wwarning("wl_backend: NEW XDG POPUP parent_surface=%p base=%p",
|
||||
(void*)popup->parent, (void*)popup->base);
|
||||
|
||||
/* Find the parent's scene tree to attach the popup to.
|
||||
* Walk up the parent chain to find the toplevel view. */
|
||||
struct wlr_xdg_surface *parent_xdg =
|
||||
wlr_xdg_surface_try_from_wlr_surface(popup->parent);
|
||||
struct wlr_scene_tree *parent_tree = NULL;
|
||||
|
||||
if (parent_xdg) {
|
||||
if (parent_xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
||||
struct wl_toplevel_view *pv =
|
||||
wl_find_view_by_xdg_surface(parent_xdg);
|
||||
if (pv && pv->scene_node)
|
||||
parent_tree = pv->scene_node;
|
||||
} else if (parent_xdg->role == WLR_XDG_SURFACE_ROLE_POPUP) {
|
||||
/* Nested popup — use the parent popup's scene tree.
|
||||
* wlr_scene_xdg_surface_create on the parent popup set
|
||||
* data on that surface's scene tree. */
|
||||
parent_tree = parent_xdg->data;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent_tree) {
|
||||
struct wlr_scene_tree *popup_tree =
|
||||
wlr_scene_xdg_surface_create(parent_tree, popup->base);
|
||||
if (popup_tree) {
|
||||
popup->base->data = popup_tree;
|
||||
}
|
||||
} else {
|
||||
wwarning("wl_backend: popup has no parent scene tree!");
|
||||
}
|
||||
|
||||
struct wl_popup_ctx *ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return;
|
||||
|
||||
ctx->popup = popup;
|
||||
ctx->commit.notify = handle_popup_commit;
|
||||
wl_signal_add(&popup->base->surface->events.commit, &ctx->commit);
|
||||
ctx->destroy.notify = handle_popup_destroy;
|
||||
wl_signal_add(&popup->events.destroy, &ctx->destroy);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue