diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 5050859..8b29afa 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -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, diff --git a/src/backend/wayland/wl_handlers.c b/src/backend/wayland/wl_handlers.c index 8420e34..daf8d37 100644 --- a/src/backend/wayland/wl_handlers.c +++ b/src/backend/wayland/wl_handlers.c @@ -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); diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index 51ee671..fb881d1 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -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; diff --git a/src/backend/wayland/wl_xdg.c b/src/backend/wayland/wl_xdg.c index 4633970..5aa7e7e 100644 --- a/src/backend/wayland/wl_xdg.c +++ b/src/backend/wayland/wl_xdg.c @@ -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); +}