diff --git a/WINGs/WINGs/WINGsP.h.in b/WINGs/WINGs/WINGsP.h.in index 9d77a35..503ff7e 100644 --- a/WINGs/WINGs/WINGsP.h.in +++ b/WINGs/WINGs/WINGsP.h.in @@ -495,6 +495,10 @@ typedef struct W_EventBackend { int *x, int *y, unsigned int *width, unsigned int *height); void (*move_window)(void *ectx, WNativeWindow window, int x, int y); + /* Begin interactive window move (Wayland: xdg_toplevel_move). + * Returns True if the backend will handle the move, False if the + * caller should run its own drag loop (X11). */ + Bool (*window_begin_move)(void *ectx, WNativeWindow window); WNativeWindow (*translate_coordinates)(void *ectx, WNativeWindow src, WNativeWindow dst, int src_x, int src_y, @@ -1024,6 +1028,7 @@ struct W_View { unsigned int root:1; /* is the root window */ unsigned int mapWhenRealized:1; /* map the view when it's realized */ unsigned int alreadyDead:1; /* view was freed */ + unsigned int popup:1; /* popup-style toplevel (no frame, no taskbar) */ unsigned int dontCompressMotion:1; /* motion notify event compress */ unsigned int notifySizeChanged:1; diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index 1ad9718..ce99a8e 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -450,8 +450,13 @@ wl_client_toplevel_realize(struct W_View *view, int width, int height) /* Default app_id; the application can override via xdg_toplevel_set_app_id. * WPrefs and friends ought to call WMSetApplicationIconImage / set_title; * we'll plumb that next. */ - xdg_toplevel_set_app_id(top->xdg_toplevel, "WINGs"); - xdg_toplevel_set_title(top->xdg_toplevel, "WINGs Application"); + if (view->flags.popup) { + xdg_toplevel_set_app_id(top->xdg_toplevel, "wings-popup"); + xdg_toplevel_set_title(top->xdg_toplevel, ""); + } else { + xdg_toplevel_set_app_id(top->xdg_toplevel, "WINGs"); + xdg_toplevel_set_title(top->xdg_toplevel, "WINGs Application"); + } /* Initial commit with no buffer so the compositor sends the first * configure. We attach a real buffer in the configure handler. */ @@ -510,6 +515,25 @@ W_WLClientSetMinMaxSize(W_View *view) wl_surface_commit(top->surface); } +Bool +W_WLClientBeginMove(WNativeWindow window) +{ + extern uint32_t wlc_ptr_button_serial; + extern W_View *wlc_ptr_grab; + extern unsigned int wlc_ptr_button_state; + WLClientCtx *ctx = g_wlclient_ctx; + WLClientToplevel *top = wlc_top_find(window); + if (!ctx || !ctx->seat || !top || !top->xdg_toplevel) return False; + if (wlc_ptr_button_serial == 0) return False; + xdg_toplevel_move(top->xdg_toplevel, ctx->seat, wlc_ptr_button_serial); + /* Release implicit grab — the compositor now owns the input until + * the user releases the button. */ + wlc_ptr_grab = NULL; + /* Clear pressed state so widgets don't think the button is still down */ + wlc_ptr_button_state = 0; + return True; +} + /* ------------------------------------------------------------------ */ /* Input: wl_pointer / wl_keyboard listeners */ /* ------------------------------------------------------------------ */ @@ -540,11 +564,12 @@ wlc_xkb_mod_mask(struct xkb_state *state) /* Pointer state */ static double wlc_ptr_x, wlc_ptr_y; static uint32_t wlc_ptr_focus_serial; +uint32_t wlc_ptr_button_serial; /* serial of most recent button press */ static WLClientToplevel *wlc_ptr_focused; -static unsigned int wlc_ptr_button_state; +unsigned int wlc_ptr_button_state; static unsigned int wlc_ptr_mods; static W_View *wlc_ptr_child_focus; -static W_View *wlc_ptr_grab; /* implicit grab: deliver events here while button held */ +W_View *wlc_ptr_grab; /* implicit grab: deliver events here while button held */ /* * Hit-test: find the deepest child view at (x,y) relative to the toplevel, @@ -706,9 +731,11 @@ static void pointer_handle_button(void *data, struct wl_pointer *p, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - (void)p; (void)serial; + (void)p; WLClientCtx *ctx = data; if (!wlc_ptr_focused) return; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + wlc_ptr_button_serial = serial; /* Linux input event codes -> X-style button numbers. */ unsigned int wmb; diff --git a/WINGs/wevent_wl.c b/WINGs/wevent_wl.c index 297f93f..05e5b5b 100644 --- a/WINGs/wevent_wl.c +++ b/WINGs/wevent_wl.c @@ -219,6 +219,16 @@ static Bool wl_get_window_geometry(void *ectx, WNativeWindow w, static void wl_move_window(void *ectx, WNativeWindow w, int x, int y) { (void)ectx; (void)w; (void)x; (void)y; } +/* Implemented in wclient_wl.c — calls xdg_toplevel_move on the client + * connection so the compositor handles the drag. */ +extern Bool W_WLClientBeginMove(WNativeWindow window); + +static Bool wl_window_begin_move(void *ectx, WNativeWindow window) +{ + (void)ectx; + return W_WLClientBeginMove(window); +} + static WNativeWindow wl_translate_coordinates(void *ectx, WNativeWindow src, WNativeWindow dst, int sx, int sy, int *dx, int *dy) { (void)ectx; (void)src; (void)dst; (void)sx; (void)sy; @@ -317,6 +327,7 @@ const W_EventBackend W_EventBackendWL = { .query_pointer = wl_query_pointer, .get_window_geometry = wl_get_window_geometry, .move_window = wl_move_window, + .window_begin_move = wl_window_begin_move, .translate_coordinates = wl_translate_coordinates, .get_root_window = wl_get_root_window, .query_tree = wl_query_tree, diff --git a/WINGs/wevent_x11.c b/WINGs/wevent_x11.c index cf310ea..07f6f31 100644 --- a/WINGs/wevent_x11.c +++ b/WINGs/wevent_x11.c @@ -435,6 +435,14 @@ x11_move_window(void *ectx, WNativeWindow window, int x, int y) XMoveWindow(ctx->dpy, (Window)window, x, y); } +static Bool +x11_window_begin_move(void *ectx, WNativeWindow window) +{ + (void)ectx; (void)window; + /* X11 uses manual drag loop with grab_pointer; no backend assist. */ + return False; +} + static WNativeWindow x11_translate_coordinates(void *ectx, WNativeWindow src, WNativeWindow dst, int src_x, int src_y, int *dst_x, int *dst_y) @@ -759,6 +767,7 @@ const W_EventBackend W_EventBackendX11 = { .query_pointer = x11_query_pointer, .get_window_geometry = x11_get_window_geometry, .move_window = x11_move_window, + .window_begin_move = x11_window_begin_move, .translate_coordinates = x11_translate_coordinates, .get_root_window = x11_get_root_window, .query_tree = x11_query_tree, diff --git a/WPrefs.app/editmenu.c b/WPrefs.app/editmenu.c index 38b9114..a68b4ab 100644 --- a/WPrefs.app/editmenu.c +++ b/WPrefs.app/editmenu.c @@ -383,6 +383,8 @@ static WEditMenu *makeEditMenu(WMScreen * scr, WMWidget * parent, const char *ti } else { mPtr->view = W_CreateTopView(scr); mPtr->flags.standalone = 1; + if (mPtr->view) + mPtr->view->flags.popup = 1; } if (!mPtr->view) { wfree(mPtr); @@ -1050,6 +1052,17 @@ static void dragMenu(WEditMenu * menu) unsigned blau; WNativeWindow blaw; + if (menu->parent) + WTearOffEditMenu(menu->parent, menu); + + /* Wayland: ask compositor to drive the move, then return. X11: + * fall through to manual drag loop. */ + if (scr->eventBackend->window_begin_move + && scr->eventBackend->window_begin_move(scr->eventBackendCtx, + W_VIEW_DRAWABLE(menu->view))) { + return; + } + scr->eventBackend->get_window_geometry(scr->eventBackendCtx, W_VIEW_DRAWABLE(menu->view), &dx, &dy, &blau, &blau); @@ -1060,9 +1073,6 @@ static void dragMenu(WEditMenu * menu) scr->eventBackend->grab_pointer(scr->eventBackendCtx, scr->rootWin, scr->defaultCursor); - if (menu->parent) - WTearOffEditMenu(menu->parent, menu); - while (!done) { WMNextEvent(scr, &ev); diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index b651134..2b66994 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -1138,12 +1138,34 @@ handle_toplevel_map(struct wl_listener *listener, void *data) view->xdg_surface->toplevel->app_id ? view->xdg_surface->toplevel->app_id : "(none)", view->xdg_surface->toplevel->title ? view->xdg_surface->toplevel->title : "(none)"); + /* Detect popup-style WINGs windows (menu editor items, etc.) — these + * need borderless presentation, no taskbar entry, and free positioning. */ + const char *app_id_check = view->xdg_surface->toplevel->app_id; + int is_popup = (app_id_check && strcmp(app_id_check, "wings-popup") == 0); + /* Phase 39/41: register the surface as a managed window. */ { WScreen *scr = wScreenWithNumber(0); if (scr) { WWindow *wwin = wManageWindow(scr, view->id); if (wwin) { + if (is_popup) { + /* Set borderless + skip-taskbar attributes */ + wwin->client_flags.no_border = 1; + wwin->client_flags.no_titlebar = 1; + wwin->client_flags.no_closable = 1; + wwin->client_flags.no_miniaturizable = 1; + wwin->client_flags.no_resizable = 1; + wwin->client_flags.no_close_button = 1; + wwin->client_flags.no_miniaturize_button = 1; + wwin->client_flags.no_resizebar = 1; + wwin->client_flags.skip_window_list = 1; + wwin->client_flags.skip_switchpanel = 1; + wwin->user_flags = wwin->client_flags; + /* Rebuild frame without decorations */ + extern void wWindowConfigureBorders(WWindow *wwin); + wWindowConfigureBorders(wwin); + } int pid = wl_client_get_pid(view->id); /* Phase 62: focus delivered by wManageWindow via wSetFocusTo * (auto_focus=1 set in wl_screen_create_internals); the