diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index 04bb811..dc69713 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -66,6 +66,8 @@ typedef struct WLClientCtx { struct xdg_wm_base *wm_base; /* wmaker-popup-v1: client-driven popup positioning */ struct wmaker_popup_manager_v1 *wmaker_popup_mgr; + /* First non-popup toplevel — used as parent for popup positioning. */ + struct xdg_toplevel *main_toplevel; struct wl_pointer *pointer; struct wl_keyboard *keyboard; @@ -466,6 +468,11 @@ wl_client_toplevel_realize(struct W_View *view, int width, int height) wwarning("wclient_wl: wrapped popup toplevel id=0x%lx pos=(%d,%d) wrapper=%p", (unsigned long)view->window, view->pos.x, view->pos.y, (void*)top->wmaker_popup); + /* Anchor positioning to the application's main toplevel. */ + if (top->wmaker_popup && ctx->main_toplevel) { + wmaker_popup_v1_set_parent(top->wmaker_popup, + ctx->main_toplevel); + } /* Apply any position recorded in view->pos before realize. */ if (top->wmaker_popup && (view->pos.x || view->pos.y)) { wmaker_popup_v1_set_position(top->wmaker_popup, @@ -477,6 +484,10 @@ wl_client_toplevel_realize(struct W_View *view, int width, int height) } else { xdg_toplevel_set_app_id(top->xdg_toplevel, "WINGs"); xdg_toplevel_set_title(top->xdg_toplevel, "WINGs Application"); + /* Remember the first regular toplevel as the "main" window so + * subsequent popups can position relative to it. */ + if (!ctx->main_toplevel) + ctx->main_toplevel = top->xdg_toplevel; } /* Initial commit with no buffer so the compositor sends the first diff --git a/protocols/wmaker-popup-v1.xml b/protocols/wmaker-popup-v1.xml index 0692811..4132190 100644 --- a/protocols/wmaker-popup-v1.xml +++ b/protocols/wmaker-popup-v1.xml @@ -74,7 +74,10 @@ Wrapper around an xdg_toplevel that lets the client drive screen - placement. Position is in compositor-global coordinates. + placement. Position is in coordinates relative to the parent + toplevel's top-left corner, or compositor-global if no parent is + set. The parent is set via set_parent() and is typically the + application's main toplevel (e.g., the WPrefs main window). @@ -90,14 +93,26 @@ - - - Place the popup's top-left corner at (x, y) in compositor-global - coordinates. Takes effect on the next surface commit if the - toplevel is not yet mapped, or immediately if already mapped. + + + Subsequent set_position calls are interpreted as offsets from + the parent toplevel's top-left corner. Pass NULL to revert to + compositor-global coordinates. - - + + + + + + Place the popup at (x, y). If a parent has been set via + set_parent, coordinates are relative to the parent's top-left + corner. Otherwise, coordinates are compositor-global. + Takes effect on the next surface commit if the toplevel is not + yet mapped, or immediately if already mapped. + + + diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index e5de810..e5fdadc 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -277,6 +277,7 @@ struct wl_toplevel_view { /* wmaker-popup-v1: client-driven popup position (only valid if popup != NULL) */ struct wl_resource *popup; /* wmaker_popup_v1 resource or NULL */ int popup_x, popup_y; + struct wlr_xdg_toplevel *popup_parent; /* parent toplevel or NULL */ long *wm_state; /* Phase 56: saved WM state or NULL */ int wm_state_n; /* Phase 56: element count */ char **wm_command; /* Phase 57: WM_COMMAND argv or NULL */ @@ -1390,6 +1391,23 @@ wmaker_popup_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } +static void +wmaker_popup_handle_set_parent(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent_resource) +{ + (void)client; + struct wl_toplevel_view *view = wl_resource_get_user_data(resource); + if (!view) return; + if (parent_resource) { + view->popup_parent = wlr_xdg_toplevel_from_resource(parent_resource); + } else { + view->popup_parent = NULL; + } + wwarning("wmaker_popup: set_parent view=%p parent=%p", (void*)view, + (void*)view->popup_parent); +} + static void wmaker_popup_handle_set_position(struct wl_client *client, struct wl_resource *resource, @@ -1406,6 +1424,7 @@ wmaker_popup_handle_set_position(struct wl_client *client, static const struct wmaker_popup_v1_interface wmaker_popup_impl = { .destroy = wmaker_popup_handle_destroy, + .set_parent = wmaker_popup_handle_set_parent, .set_position = wmaker_popup_handle_set_position, }; @@ -1422,14 +1441,27 @@ wmaker_popup_apply(struct wl_toplevel_view *view) { if (!view || !view->popup) return; - int dx = view->popup_x - view->x; - int dy = view->popup_y - view->y; + /* If a parent was set, translate the requested position into screen + * coordinates by adding the parent toplevel's current position. */ + int target_x = view->popup_x; + int target_y = view->popup_y; + if (view->popup_parent) { + struct wl_toplevel_view *parent_view = + wl_find_view_by_xdg_surface(view->popup_parent->base); + if (parent_view) { + target_x += parent_view->x; + target_y += parent_view->y; + } + } + + int dx = target_x - view->x; + int dy = target_y - view->y; wwarning("wmaker_popup_apply: view=%p (%d,%d)->(%d,%d) frame_id=0x%lx scene_node=%p", - (void*)view, view->x, view->y, view->popup_x, view->popup_y, + (void*)view, view->x, view->y, target_x, target_y, (unsigned long)view->frame_id, (void*)view->scene_node); - view->x = view->popup_x; - view->y = view->popup_y; + view->x = target_x; + view->y = target_y; /* If a frame_buf wraps this view, move it via the WM's window-move * path so the frame and its tree node update consistently. */