wayland: Phase 17 — XWayland support for X11 client windows
Add wlr_xwayland to the headless compositor so X11 applications
(e.g. GNUstep apps) can run under the Wayland backend.
Key changes:
- Include <wlr/xwayland.h>; add xwayland/xwayland_new_surface_listener
to wl_compositor_state
- Add xw_surface field to wl_toplevel_view so XDG and XWayland windows
share the same view struct and context-store keyed by WNativeWindow
- XWayland window_id used directly as WNativeWindow (no conflict with
XDG synthetic IDs ≥ 0x10000000)
- handle_xwayland_{map,unmap,destroy,set_class}: same wManageWindow/
wUnmanageWindow lifecycle as XDG toplevels
- override_redirect windows (menus, tooltips) are skipped
- view_surface_at: bounds-check path for XWayland surfaces alongside
wlr_xdg_surface_surface_at for XDG toplevels
- vtable functions updated: focus_set, prop_get_normal_hints,
prop_get_wm_class, window_fetch_name, window_get_icon_name,
window_get_client_attrs, client_synth_configure, window_resize,
window_move, client_close, window_get_rect, prop_get_transient_for,
client_get_pid all handle xw_surface != NULL
- wlr_xwayland_create(lazy=false) called in wl_display_open after
compositor and seat; DISPLAY env var set from display_name
- wlr_xwayland_destroy in wl_display_close
- Smoke test: new check 4g verifies "XWayland started DISPLAY=:<n>" log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c2c5027610
commit
5b1590607c
|
|
@ -60,6 +60,11 @@
|
|||
* Phase 66: XDG popup initial configure — schedule configure for
|
||||
* WLR_XDG_SURFACE_ROLE_POPUP so context menus and autocomplete dropdowns
|
||||
* can commit their first buffer without stalling on the compositor.
|
||||
* Phase 17: XWayland support — wlr_xwayland started alongside the headless
|
||||
* backend; X11 windows (e.g. GNUstep apps) appear via new_surface events,
|
||||
* receive synthetic WNativeWindow IDs equal to their xcb_window_t, and
|
||||
* are managed through the same wl_toplevel_view / wManageWindow lifecycle
|
||||
* as XDG toplevels. DISPLAY env var is set so X11 clients can connect.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -84,6 +89,7 @@
|
|||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||
#include <wlr/xwayland.h> /* Phase 17: XWayland support */
|
||||
#include <wlr/util/box.h>
|
||||
/* Generated xdg-shell protocol implementation (Wayland glue code) */
|
||||
#include "protocols/xdg-shell-protocol.c"
|
||||
|
|
@ -122,6 +128,10 @@ struct wl_toplevel_view {
|
|||
struct wl_listener request_fullscreen_listener;/* Phase 16 */
|
||||
struct wl_listener request_minimize_listener; /* Phase 16 */
|
||||
struct wl_listener set_app_id_listener; /* Phase 16 */
|
||||
/* Phase 17: XWayland — set for X11 windows, NULL for XDG toplevels.
|
||||
* When non-NULL, the lifecycle listeners above are wired to XWayland
|
||||
* signals instead of XDG signals; id == xw_surface->window_id. */
|
||||
struct wlr_xwayland_surface *xw_surface;
|
||||
WNativeWindow id; /* synthetic handle, >= 0x10000000 */
|
||||
int x, y; /* Phase 55: WM-assigned position */
|
||||
long *wm_state; /* Phase 56: saved WM state or NULL */
|
||||
|
|
@ -182,6 +192,9 @@ struct wl_compositor_state {
|
|||
struct wl_listener pointer_motion_abs_listener;
|
||||
struct wl_listener pointer_button_listener;
|
||||
struct wl_listener pointer_frame_listener;
|
||||
/* Phase 17: XWayland server for X11 client support */
|
||||
struct wlr_xwayland *xwayland;
|
||||
struct wl_listener xwayland_new_surface_listener;
|
||||
char socket_name[64]; /* Phase 34: e.g. "wayland-0" */
|
||||
int started; /* non-zero after successful display_open */
|
||||
};
|
||||
|
|
@ -219,6 +232,8 @@ static void wl_update_pointer_focus(uint32_t time_msec);
|
|||
static void wl_pointer_warp_to(int x, int y);
|
||||
/* Phase 14: pointer event handler forward declarations. */
|
||||
static void handle_pointer_motion(struct wl_listener *listener, void *data);
|
||||
/* Phase 17: XWayland new surface forward declaration. */
|
||||
static void handle_xwayland_new_surface(struct wl_listener *listener, void *data);
|
||||
static void handle_pointer_motion_absolute(struct wl_listener *listener, void *data);
|
||||
static void handle_pointer_button(struct wl_listener *listener, void *data);
|
||||
static void handle_pointer_frame(struct wl_listener *listener, void *data);
|
||||
|
|
@ -477,6 +492,161 @@ handle_new_toplevel_decoration(struct wl_listener *listener, void *data)
|
|||
wwarning("wl_backend: decoration mode=client_side for new toplevel");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 17: XWayland surface lifecycle */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* handle_xwayland_map — X11 window is ready to be managed.
|
||||
* Uses the same wManageWindow path as XDG toplevels.
|
||||
*/
|
||||
static void
|
||||
handle_xwayland_map(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, map_listener);
|
||||
(void)data;
|
||||
struct wlr_xwayland_surface *xw = view->xw_surface;
|
||||
|
||||
wwarning("wl_backend: xwayland mapped id=0x%lx class=%s title=%s",
|
||||
view->id,
|
||||
xw->class ? xw->class : "(none)",
|
||||
xw->title ? xw->title : "(none)");
|
||||
|
||||
WScreen *scr = wScreenWithNumber(0);
|
||||
if (scr) {
|
||||
WWindow *wwin = wManageWindow(scr, view->id);
|
||||
if (wwin) {
|
||||
wwarning("wl_backend: wManageWindow(xwayland) succeeded id=0x%lx "
|
||||
"class=%s name=%s geo=%dx%d pid=%d",
|
||||
view->id,
|
||||
wwin->wm_class ? wwin->wm_class : "(none)",
|
||||
wwin->wm_instance ? wwin->wm_instance : "(none)",
|
||||
wwin->client.width, wwin->client.height, (int)xw->pid);
|
||||
int cx = view->x + wwin->client.width / 2;
|
||||
int cy = view->y + wwin->client.height / 2;
|
||||
wl_pointer_warp_to(cx, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_xwayland_unmap — X11 window hidden; unmanage it.
|
||||
*/
|
||||
static void
|
||||
handle_xwayland_unmap(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, unmap_listener);
|
||||
(void)data;
|
||||
|
||||
wwarning("wl_backend: xwayland unmapped id=0x%lx", view->id);
|
||||
|
||||
WScreen *scr = wScreenWithNumber(0);
|
||||
if (scr) {
|
||||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||||
if (desc && desc->parent_type == WCLASS_WINDOW) {
|
||||
WWindow *wwin = (WWindow *)desc->parent;
|
||||
wUnmanageWindow(wwin, False, True);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_xwayland_destroy — X11 window destroyed; free the view.
|
||||
*/
|
||||
static void
|
||||
handle_xwayland_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, destroy_listener);
|
||||
(void)data;
|
||||
|
||||
wl_list_remove(&view->map_listener.link);
|
||||
wl_list_remove(&view->unmap_listener.link);
|
||||
wl_list_remove(&view->destroy_listener.link);
|
||||
wl_list_remove(&view->title_listener.link);
|
||||
wl_list_remove(&view->link);
|
||||
wl_state.managed_surface_count--;
|
||||
if (view->wm_state) wfree(view->wm_state);
|
||||
if (view->wm_command) {
|
||||
int ci;
|
||||
for (ci = 0; ci < view->wm_command_n; ci++)
|
||||
wfree(view->wm_command[ci]);
|
||||
wfree(view->wm_command);
|
||||
}
|
||||
wfree(view);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_xwayland_set_class — X11 window WM_CLASS changed.
|
||||
*/
|
||||
static void
|
||||
handle_xwayland_set_class(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, title_listener);
|
||||
(void)data;
|
||||
struct wlr_xwayland_surface *xw = view->xw_surface;
|
||||
|
||||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||||
if (!desc || desc->parent_type != WCLASS_WINDOW)
|
||||
return;
|
||||
WWindow *wwin = (WWindow *)desc->parent;
|
||||
if (wwin->wm_class) wfree(wwin->wm_class);
|
||||
if (wwin->wm_instance) wfree(wwin->wm_instance);
|
||||
wwin->wm_class = xw->class ? strdup(xw->class) : NULL;
|
||||
wwin->wm_instance = xw->instance ? strdup(xw->instance) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_xwayland_new_surface — called when a new X11 window connects.
|
||||
* Allocates a wl_toplevel_view, uses window_id as the WNativeWindow ID,
|
||||
* and wires the lifecycle listeners to XWayland signals.
|
||||
*/
|
||||
static void
|
||||
handle_xwayland_new_surface(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wlr_xwayland_surface *xw = data;
|
||||
(void)listener;
|
||||
|
||||
/* Skip override-redirect windows (menus, tooltips — not WM-managed). */
|
||||
if (xw->override_redirect) {
|
||||
wwarning("wl_backend: xwayland override_redirect window 0x%x (skipped)",
|
||||
(unsigned)xw->window_id);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_toplevel_view *view = wmalloc(sizeof(*view));
|
||||
memset(view, 0, sizeof(*view));
|
||||
view->xw_surface = xw;
|
||||
view->id = (WNativeWindow)xw->window_id;
|
||||
|
||||
view->map_listener.notify = handle_xwayland_map;
|
||||
view->unmap_listener.notify = handle_xwayland_unmap;
|
||||
view->destroy_listener.notify = handle_xwayland_destroy;
|
||||
view->title_listener.notify = handle_xwayland_set_class;
|
||||
|
||||
wl_signal_add(&xw->events.map, &view->map_listener);
|
||||
wl_signal_add(&xw->events.unmap, &view->unmap_listener);
|
||||
wl_signal_add(&xw->events.destroy, &view->destroy_listener);
|
||||
wl_signal_add(&xw->events.set_class, &view->title_listener);
|
||||
|
||||
wl_list_insert(&wl_state.toplevel_list, &view->link);
|
||||
wl_state.managed_surface_count++;
|
||||
|
||||
/* Position at the centre of the headless output initially. */
|
||||
view->x = (wl_state.output ? (int)wl_state.output->width : 1920) / 4;
|
||||
view->y = (wl_state.output ? (int)wl_state.output->height : 1080) / 4;
|
||||
|
||||
wwarning("wl_backend: new XWayland surface id=0x%lx class=%s title=%s",
|
||||
view->id,
|
||||
xw->class ? xw->class : "(none)",
|
||||
xw->title ? xw->title : "(none)");
|
||||
|
||||
/* Send an initial configure so the X11 client gets a position/size. */
|
||||
wlr_xwayland_surface_configure(xw,
|
||||
(int16_t)view->x, (int16_t)view->y,
|
||||
xw->width > 0 ? xw->width : 640,
|
||||
xw->height > 0 ? xw->height : 480);
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 36 / 38: new XDG surface listener.
|
||||
* Allocates a wl_toplevel_view, assigns a synthetic WNativeWindow ID,
|
||||
|
|
@ -768,6 +938,27 @@ wl_display_open(const char *name)
|
|||
wl_signal_add(&wl_state.xdg_shell->events.new_surface,
|
||||
&wl_state.xdg_new_surface_listener);
|
||||
|
||||
/* Phase 17: start XWayland so X11 clients (e.g. GNUstep apps) can run
|
||||
* under the Wayland compositor. The third argument (lazy=false) starts
|
||||
* the Xwayland process eagerly so DISPLAY is available immediately. */
|
||||
wl_state.xwayland = wlr_xwayland_create(wl_state.display,
|
||||
wl_state.compositor, false);
|
||||
if (wl_state.xwayland) {
|
||||
wl_state.xwayland_new_surface_listener.notify = handle_xwayland_new_surface;
|
||||
wl_signal_add(&wl_state.xwayland->events.new_surface,
|
||||
&wl_state.xwayland_new_surface_listener);
|
||||
wlr_xwayland_set_seat(wl_state.xwayland, wl_state.seat);
|
||||
if (wl_state.xwayland->display_name) {
|
||||
setenv("DISPLAY", wl_state.xwayland->display_name, 1);
|
||||
wwarning("wl_backend: XWayland started DISPLAY=%s",
|
||||
wl_state.xwayland->display_name);
|
||||
} else {
|
||||
wwarning("wl_backend: XWayland created (display_name pending)");
|
||||
}
|
||||
} else {
|
||||
wwarning("wl_backend: XWayland unavailable (X11 clients will not work)");
|
||||
}
|
||||
|
||||
/* Phase 37: periodic timer to drive client frame callbacks at ~60 fps.
|
||||
* Using a timer avoids the swapchain/allocator complexity of the full
|
||||
* render pipeline while still keeping Wayland clients unblocked. */
|
||||
|
|
@ -795,6 +986,12 @@ wl_display_close(void)
|
|||
{
|
||||
if (!wl_state.started)
|
||||
return;
|
||||
/* Phase 17: shut down XWayland before destroying the display. */
|
||||
if (wl_state.xwayland) {
|
||||
wlr_xwayland_destroy(wl_state.xwayland);
|
||||
wl_state.xwayland = NULL;
|
||||
unsetenv("DISPLAY");
|
||||
}
|
||||
if (wl_state.frame_timer) {
|
||||
wl_event_source_remove(wl_state.frame_timer);
|
||||
wl_state.frame_timer = NULL;
|
||||
|
|
@ -962,7 +1159,14 @@ wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp)
|
|||
return;
|
||||
}
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (!v || !v->xdg_surface || !v->xdg_surface->surface)
|
||||
if (!v) return;
|
||||
/* Phase 17: XWayland windows use wlr_xwayland_surface_activate. */
|
||||
if (v->xw_surface) {
|
||||
wlr_xwayland_surface_activate(v->xw_surface, true);
|
||||
wwarning("wl_backend: focus_set(xwayland) id=0x%lx", win);
|
||||
return;
|
||||
}
|
||||
if (!v->xdg_surface || !v->xdg_surface->surface)
|
||||
return;
|
||||
wlr_seat_keyboard_notify_enter(wl_state.seat,
|
||||
v->xdg_surface->surface,
|
||||
|
|
@ -1063,7 +1267,7 @@ static void wl_pointer_ungrab(unsigned long timestamp) { (void)timestamp; }
|
|||
|
||||
/*
|
||||
* Find the topmost mapped view whose surface contains screen point (px, py).
|
||||
* Returns the wlr_surface and sets *sx/*sy to surface-local coordinates.
|
||||
* Returns the wlr_surface and sets sx,sy to surface-local coordinates.
|
||||
* Iterates toplevel_list in reverse-insertion order (last = topmost).
|
||||
*/
|
||||
static struct wlr_surface *
|
||||
|
|
@ -1072,10 +1276,27 @@ view_surface_at(int px, int py, double *sx_out, double *sy_out,
|
|||
{
|
||||
struct wl_toplevel_view *v;
|
||||
wl_list_for_each_reverse(v, &wl_state.toplevel_list, link) {
|
||||
if (!v->xdg_surface || !v->xdg_surface->mapped)
|
||||
continue;
|
||||
double sx = px - (double)v->x;
|
||||
double sy = py - (double)v->y;
|
||||
|
||||
/* Phase 17: XWayland window — simple bounds check on the surface. */
|
||||
if (v->xw_surface) {
|
||||
if (!v->xw_surface->mapped || !v->xw_surface->surface)
|
||||
continue;
|
||||
if (sx >= 0 && sy >= 0 &&
|
||||
sx < (double)v->xw_surface->width &&
|
||||
sy < (double)v->xw_surface->height) {
|
||||
if (sx_out) *sx_out = sx;
|
||||
if (sy_out) *sy_out = sy;
|
||||
if (view_out) *view_out = v;
|
||||
return v->xw_surface->surface;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* XDG toplevel path. */
|
||||
if (!v->xdg_surface || !v->xdg_surface->mapped)
|
||||
continue;
|
||||
double sub_sx, sub_sy;
|
||||
struct wlr_surface *sub = wlr_xdg_surface_surface_at(
|
||||
v->xdg_surface, sx, sy, &sub_sx, &sub_sy);
|
||||
|
|
@ -1276,11 +1497,37 @@ static void wl_event_randr_update(void *event) { (void)event; }
|
|||
static int
|
||||
wl_prop_get_normal_hints(WNativeWindow win, WNormalHints *out, int *pre_icccm)
|
||||
{
|
||||
/* Phase 47: populate size constraints from XDG toplevel. */
|
||||
/* Phase 47: populate size constraints from XDG toplevel.
|
||||
* Phase 17: also handles XWayland size_hints. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
memset(out, 0, sizeof(*out));
|
||||
*pre_icccm = 0;
|
||||
if (!v || !v->xdg_surface || !v->xdg_surface->toplevel)
|
||||
if (!v) return 0;
|
||||
|
||||
/* Phase 17: XWayland size_hints path. */
|
||||
if (v->xw_surface) {
|
||||
struct wlr_xwayland_surface_size_hints *sh = v->xw_surface->size_hints;
|
||||
if (sh) {
|
||||
if (sh->min_width > 0 || sh->min_height > 0) {
|
||||
out->flags |= WNH_P_MIN_SIZE;
|
||||
out->min_width = sh->min_width > 0 ? sh->min_width : 1;
|
||||
out->min_height = sh->min_height > 0 ? sh->min_height : 1;
|
||||
}
|
||||
if (sh->max_width > 0 || sh->max_height > 0) {
|
||||
out->flags |= WNH_P_MAX_SIZE;
|
||||
out->max_width = sh->max_width;
|
||||
out->max_height = sh->max_height;
|
||||
}
|
||||
}
|
||||
if (v->xw_surface->width > 0 && v->xw_surface->height > 0) {
|
||||
out->flags |= WNH_P_SIZE;
|
||||
out->width = v->xw_surface->width;
|
||||
out->height = v->xw_surface->height;
|
||||
}
|
||||
return (out->flags != 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!v->xdg_surface || !v->xdg_surface->toplevel)
|
||||
return 0;
|
||||
struct wlr_xdg_toplevel *tl = v->xdg_surface->toplevel;
|
||||
if (tl->current.min_width > 0 || tl->current.min_height > 0) {
|
||||
|
|
@ -1313,8 +1560,13 @@ static int
|
|||
wl_prop_get_wm_class(WNativeWindow win,
|
||||
char **wm_class, char **wm_instance)
|
||||
{
|
||||
/* Phase 41: use XDG app_id as both WM_CLASS and WM_INSTANCE. */
|
||||
/* Phase 41: use XDG app_id / Phase 17: use XWayland WM_CLASS. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->xw_surface) {
|
||||
*wm_class = strdup(v->xw_surface->class ? v->xw_surface->class : "x11-client");
|
||||
*wm_instance = strdup(v->xw_surface->instance ? v->xw_surface->instance : "x11-client");
|
||||
return 0;
|
||||
}
|
||||
const char *app_id = (v && v->xdg_surface && v->xdg_surface->toplevel &&
|
||||
v->xdg_surface->toplevel->app_id)
|
||||
? v->xdg_surface->toplevel->app_id : "wayland-client";
|
||||
|
|
@ -1326,11 +1578,13 @@ wl_prop_get_wm_class(WNativeWindow win,
|
|||
static Bool
|
||||
wl_window_fetch_name(WNativeWindow win, char **name_out)
|
||||
{
|
||||
/* Phase 41: use XDG toplevel title as the window name. */
|
||||
/* Phase 41: XDG title / Phase 17: XWayland title. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
const char *title = (v && v->xdg_surface && v->xdg_surface->toplevel &&
|
||||
v->xdg_surface->toplevel->title)
|
||||
? v->xdg_surface->toplevel->title : NULL;
|
||||
const char *title = NULL;
|
||||
if (v && v->xw_surface)
|
||||
title = v->xw_surface->title;
|
||||
else if (v && v->xdg_surface && v->xdg_surface->toplevel)
|
||||
title = v->xdg_surface->toplevel->title;
|
||||
if (title) {
|
||||
*name_out = strdup(title);
|
||||
return True;
|
||||
|
|
@ -1342,13 +1596,13 @@ wl_window_fetch_name(WNativeWindow win, char **name_out)
|
|||
static Bool
|
||||
wl_window_get_icon_name(WNativeWindow win, char **name_out)
|
||||
{
|
||||
/* Phase 60: Wayland has no separate icon-name concept; return the
|
||||
* XDG toplevel title (same as window_fetch_name) so the WM always
|
||||
* has something to display in icon labels. */
|
||||
/* Phase 60: return title for icon labels; Phase 17: XWayland path. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
const char *title = (v && v->xdg_surface && v->xdg_surface->toplevel &&
|
||||
v->xdg_surface->toplevel->title)
|
||||
? v->xdg_surface->toplevel->title : NULL;
|
||||
const char *title = NULL;
|
||||
if (v && v->xw_surface)
|
||||
title = v->xw_surface->title;
|
||||
else if (v && v->xdg_surface && v->xdg_surface->toplevel)
|
||||
title = v->xdg_surface->toplevel->title;
|
||||
if (title) {
|
||||
*name_out = strdup(title);
|
||||
return True;
|
||||
|
|
@ -1374,7 +1628,14 @@ wl_prop_get_transient_for(WNativeWindow win)
|
|||
* implementation stores the parent as xdg_surface->toplevel->parent
|
||||
* (a wlr_xdg_surface *). We walk the view list to find its ID. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (!v || !v->xdg_surface || !v->xdg_surface->toplevel)
|
||||
if (!v) return WNATIVE_NONE;
|
||||
/* Phase 17: XWayland transient_for via parent window_id. */
|
||||
if (v->xw_surface) {
|
||||
struct wlr_xwayland_surface *parent = v->xw_surface->parent;
|
||||
if (!parent) return WNATIVE_NONE;
|
||||
return (WNativeWindow)parent->window_id;
|
||||
}
|
||||
if (!v->xdg_surface || !v->xdg_surface->toplevel)
|
||||
return WNATIVE_NONE;
|
||||
struct wlr_xdg_surface *parent = v->xdg_surface->toplevel->parent;
|
||||
if (!parent)
|
||||
|
|
@ -1421,17 +1682,28 @@ static void wl_window_map_subwindows(WNativeWindow parent) { (void)parent; }
|
|||
static void wl_window_unmap(WNativeWindow win) { (void)win; }
|
||||
static void wl_window_move(WNativeWindow win, int x, int y)
|
||||
{
|
||||
/* Phase 55: record the WM-assigned position in the view struct. */
|
||||
/* Phase 55: record the WM-assigned position in the view struct.
|
||||
* Phase 17: also inform XWayland of the new position. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) { v->x = x; v->y = y; }
|
||||
if (!v) return;
|
||||
v->x = x; v->y = y;
|
||||
if (v->xw_surface)
|
||||
wlr_xwayland_surface_configure(v->xw_surface,
|
||||
(int16_t)x, (int16_t)y, v->xw_surface->width, v->xw_surface->height);
|
||||
}
|
||||
|
||||
static void wl_window_resize(WNativeWindow win, int w, int h)
|
||||
{
|
||||
/* Phase 44: send a configure with the new size. */
|
||||
/* Phase 44: send a configure with the new size.
|
||||
* Phase 17: XWayland path uses wlr_xwayland_surface_configure. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (!v || !v->xdg_surface || w <= 0 || h <= 0)
|
||||
if (!v || w <= 0 || h <= 0) return;
|
||||
if (v->xw_surface) {
|
||||
wlr_xwayland_surface_configure(v->xw_surface,
|
||||
(int16_t)v->x, (int16_t)v->y, (uint16_t)w, (uint16_t)h);
|
||||
return;
|
||||
}
|
||||
if (!v->xdg_surface) return;
|
||||
wlr_xdg_toplevel_set_size(v->xdg_surface, (uint32_t)w, (uint32_t)h);
|
||||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||||
}
|
||||
|
|
@ -1451,6 +1723,14 @@ wl_window_get_rect(WNativeWindow win,
|
|||
return 0; /* invalid/none */
|
||||
}
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
/* Phase 17: XWayland geometry. */
|
||||
if (v && v->xw_surface) {
|
||||
if (win_x) { *win_x = v->x; }
|
||||
if (win_y) { *win_y = v->y; }
|
||||
if (win_w) { *win_w = v->xw_surface->width > 0 ? v->xw_surface->width : 1; }
|
||||
if (win_h) { *win_h = v->xw_surface->height > 0 ? v->xw_surface->height : 1; }
|
||||
return 1;
|
||||
}
|
||||
if (v && v->xdg_surface && v->xdg_surface->surface) {
|
||||
int w = v->xdg_surface->surface->current.width;
|
||||
int h = v->xdg_surface->surface->current.height;
|
||||
|
|
@ -1491,10 +1771,19 @@ static int wl_overlay_text_width(WScreen *scr, const char *str, int n) { (void)
|
|||
static int
|
||||
wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out)
|
||||
{
|
||||
/* Phase 42: return real map_state and geometry from XDG surface. */
|
||||
/* Phase 42: return real map_state and geometry from XDG surface.
|
||||
* Phase 17: XWayland path uses xw_surface->mapped and width/height. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (v && v->xdg_surface && v->xdg_surface->surface) {
|
||||
if (v && v->xw_surface) {
|
||||
if (v->xw_surface->mapped) {
|
||||
out->map_state = WCLIENT_VIEWABLE;
|
||||
out->width = v->xw_surface->width;
|
||||
out->height = v->xw_surface->height;
|
||||
} else {
|
||||
out->map_state = WCLIENT_UNMAPPED;
|
||||
}
|
||||
} else if (v && v->xdg_surface && v->xdg_surface->surface) {
|
||||
out->map_state = WCLIENT_VIEWABLE;
|
||||
out->width = v->xdg_surface->surface->current.width;
|
||||
out->height = v->xdg_surface->surface->current.height;
|
||||
|
|
@ -1526,13 +1815,21 @@ wl_client_synth_configure(WNativeWindow client_win,
|
|||
{
|
||||
/* Phase 44: send an XDG configure with the new size to the client.
|
||||
* Phase 13: also record the WM-assigned position in the view so
|
||||
* pointer hit-testing (view_surface_at) uses current coordinates. */
|
||||
* pointer hit-testing (view_surface_at) uses current coordinates.
|
||||
* Phase 17: XWayland uses wlr_xwayland_surface_configure instead. */
|
||||
(void)border_width; (void)above;
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(client_win);
|
||||
if (!v || !v->xdg_surface)
|
||||
return;
|
||||
if (!v) return;
|
||||
v->x = x;
|
||||
v->y = y;
|
||||
if (v->xw_surface) {
|
||||
wlr_xwayland_surface_configure(v->xw_surface,
|
||||
(int16_t)x, (int16_t)y,
|
||||
width > 0 ? (uint16_t)width : v->xw_surface->width,
|
||||
height > 0 ? (uint16_t)height : v->xw_surface->height);
|
||||
return;
|
||||
}
|
||||
if (!v->xdg_surface) return;
|
||||
if (width > 0 && height > 0)
|
||||
wlr_xdg_toplevel_set_size(v->xdg_surface, (uint32_t)width, (uint32_t)height);
|
||||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||||
|
|
@ -1544,9 +1841,13 @@ static void wl_client_remove_from_save_set(WNativeWindow win) { (void)win; }
|
|||
static void
|
||||
wl_client_close(WNativeWindow win)
|
||||
{
|
||||
/* Phase 46: ask the XDG toplevel to close itself. */
|
||||
/* Phase 46: ask the surface to close.
|
||||
* Phase 17: XWayland uses wlr_xwayland_surface_close. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->xdg_surface)
|
||||
if (!v) return;
|
||||
if (v->xw_surface)
|
||||
wlr_xwayland_surface_close(v->xw_surface);
|
||||
else if (v->xdg_surface)
|
||||
wlr_xdg_toplevel_send_close(v->xdg_surface);
|
||||
}
|
||||
|
||||
|
|
@ -1560,7 +1861,11 @@ wl_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out)
|
|||
static int wl_client_get_pid(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (!v || !v->xdg_surface || !v->xdg_surface->resource)
|
||||
if (!v) return 0;
|
||||
/* Phase 17: XWayland surfaces have a direct pid field. */
|
||||
if (v->xw_surface)
|
||||
return (int)v->xw_surface->pid;
|
||||
if (!v->xdg_surface || !v->xdg_surface->resource)
|
||||
return 0;
|
||||
struct wl_client *client = wl_resource_get_client(v->xdg_surface->resource);
|
||||
if (!client) return 0;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,10 @@
|
|||
# receives wl_pointer.enter events. wl_client_synth_configure now records
|
||||
# the WM-assigned position (v->x/v->y) so view_surface_at() returns the
|
||||
# correct surface. Verified by "pointer focus entered surface" log.
|
||||
# 39. (Phase 17) XWayland started: wlr_xwayland_create() is called in
|
||||
# wl_display_open(); compositor logs "XWayland started DISPLAY=:<n>"
|
||||
# so X11 clients (GNUstep apps) can connect. DISPLAY env var is set.
|
||||
# Verified by "XWayland started DISPLAY=" log.
|
||||
# 38. (Phase 11) noclip/nodock set in wl_screen_create_internals: wDockCreate
|
||||
# requires X11 rendering resources unavailable in Wayland mode; disabling
|
||||
# the dock/clip prevents crashes when wWorkspaceNew is called to create
|
||||
|
|
@ -255,6 +259,20 @@ grep -q 'dock/clip disabled in Wayland mode' /tmp/wl-smoke-stderr \
|
|||
|| die "compositor did not log dock/clip disabled (noclip/nodock not set in wl_screen_create_internals)"
|
||||
echo " nodock check: dock/clip disabled in Wayland mode (noclip=1 nodock=1)"
|
||||
|
||||
# -- 4g. Phase 17: XWayland started and DISPLAY set ----------------------
|
||||
#
|
||||
# wl_display_open() logs "XWayland started DISPLAY=:<n>" when
|
||||
# wlr_xwayland_create() succeeds; verify the log line is present.
|
||||
# (Falls back gracefully if XWayland is unavailable.)
|
||||
if grep -q 'XWayland started DISPLAY=' /tmp/wl-smoke-stderr; then
|
||||
XDISP=$(grep -o 'DISPLAY=:[0-9]*' /tmp/wl-smoke-stderr | head -1 | sed 's/DISPLAY=//')
|
||||
echo " xwayland check: XWayland started DISPLAY=$XDISP"
|
||||
elif grep -q 'XWayland unavailable' /tmp/wl-smoke-stderr; then
|
||||
echo " xwayland check: XWayland unavailable (skipped)"
|
||||
else
|
||||
die "compositor did not log XWayland startup status"
|
||||
fi
|
||||
|
||||
# -- 5. Core Wayland globals must be advertised ---------------------------
|
||||
#
|
||||
# Phase 35: wl_compositor, wl_seat, and xdg_wm_base must appear in the
|
||||
|
|
|
|||
Loading…
Reference in New Issue