Phase 83: frame decorations for native Wayland clients

- Create WM frames (titlebar, buttons, resizebar) for native XDG
  toplevels — remove the is_wayland guard that skipped frame creation
- Set decoration mode to SERVER_SIDE so clients don't draw their own
  titlebar (CSD)
- Skip client_reparent for Wayland (no X11 reparenting needed; the
  client scene node is independent of the frame scene buffers)
- Set frame_border_width=0 in wl_screen_create_internals (Wayland has
  no X11-style window border; the frame_buf renders all decoration)
- Use wlr_scene_subsurface_tree_create for client surface (avoids XDG
  geometry offset issues)

Known issue: transient size mismatch on initial map when saved session
state or size constraints differ from the client's committed buffer.
Resolves after the client processes the configure event.
This commit is contained in:
Levi Neely 2026-05-14 12:18:13 +02:00
parent 288d2ad3a7
commit ac1dbd3b8e
2 changed files with 22 additions and 23 deletions

View File

@ -995,8 +995,8 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
/* Phase 18: add XDG surface to the scene graph. */
if (wl_state.scene && !view->scene_node) {
view->scene_node = wlr_scene_xdg_surface_create(
&wl_state.scene->tree, view->xdg_surface);
view->scene_node = wlr_scene_subsurface_tree_create(
&wl_state.scene->tree, view->xdg_surface->surface);
if (view->scene_node)
wlr_scene_node_set_position(&view->scene_node->node,
view->x, view->y);
@ -1134,7 +1134,7 @@ handle_deferred_decoration_commit(struct wl_listener *listener, void *data)
wl_list_remove(&dd->commit_listener.link);
wlr_xdg_toplevel_decoration_v1_set_mode(dd->deco,
WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
wfree(dd);
}
@ -1147,7 +1147,7 @@ handle_new_toplevel_decoration(struct wl_listener *listener, void *data)
struct wlr_xdg_surface *surface = deco->toplevel->base;
if (surface->initialized) {
wlr_xdg_toplevel_decoration_v1_set_mode(deco,
WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
} else {
struct wl_deferred_decoration *dd = wmalloc(sizeof(*dd));
dd->deco = deco;
@ -3575,6 +3575,11 @@ wl_screen_open(WScreen *scr)
scr->root_win = (WNativeWindow)0x0FFFFFFF; /* synthetic root window ID */
scr->colormap = 0;
/* Wayland has no X11-style window border; the frame_buf renders the
* full decoration. Set border width to 0 so policy code does not
* offset the client position by a non-existent border. */
scr->frame_border_width = 0;
/* XWayland bridge: fill in real X11 screen values so RCreateContext
* and WINGs can use the display connection for drawing and fonts. */
if (wl_state.x_display) {
@ -6246,6 +6251,10 @@ wl_screen_create_internals(WScreen *scr)
* compositor's single-threaded event loop is running.
* NOTE: native dock panel disabled — WM dock uses XWayland windows now. */
/* wl_state.dock_pending_scr = scr; */
/* Wayland has no X11-style window border; force to 0 so policy code
* does not offset client positions by a non-existent border. */
scr->frame_border_width = 0;
}
static void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; }
static void

View File

@ -1083,8 +1083,8 @@ WWindow *wManageWindow(WScreen *scr, WNativeWindow window)
wwin->flags.omnipresent ^= 1;
}
/* Create frame, borders and do reparenting (X11 only) */
if (!wm_backend->is_wayland) {
/* Create frame, borders and do reparenting */
{
foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
#ifdef XKB_BUTTON_HINT
if (wPreferences.modelock)
@ -1147,11 +1147,13 @@ WWindow *wManageWindow(WScreen *scr, WNativeWindow window)
wwin->frame->on_dblclick_titlebar = titlebarDblClick;
wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
wm_backend->window_select_events(wwin->client_win,
wwin->event_mask & ~StructureNotifyMask);
wm_backend->client_reparent(wwin->client_win, wwin->frame->core->window,
0, wwin->frame->top_width);
wm_backend->window_select_events(wwin->client_win, wwin->event_mask);
if (!wm_backend->is_wayland) {
wm_backend->window_select_events(wwin->client_win,
wwin->event_mask & ~StructureNotifyMask);
wm_backend->client_reparent(wwin->client_win, wwin->frame->core->window,
0, wwin->frame->top_width);
wm_backend->window_select_events(wwin->client_win, wwin->event_mask);
}
{
int gx, gy;
@ -1171,18 +1173,6 @@ WWindow *wManageWindow(WScreen *scr, WNativeWindow window)
/* to make sure the window receives it's new position after reparenting */
wWindowSynthConfigureNotify(wwin);
} else {
/* Wayland: record geometry from normal hints; frame stays NULL.
* Phase 67: store workspace in wwin->workspace since there is no
* frame->workspace slot; wWorkspaceForceChange uses this field
* when frame is NULL to avoid NULL dereferences on WS switch. */
wwin->client.x = x;
wwin->client.y = y;
wwin->client.width = width;
wwin->client.height = height;
wwin->frame_x = x;
wwin->frame_y = y;
wwin->workspace = workspace;
}
/* Setup descriptors and save window to internal lists */