diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 50e360c..addb43b 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -307,6 +307,9 @@ struct wl_compositor_state { struct wl_listener xwayland_new_surface_listener; struct wl_listener xwayland_ready_listener; struct wl_listener xwayland_server_destroy_listener; + bool xwayland_ready; /* set by handle_xwayland_ready */ + Display *x_display; /* WM's Xlib connection to own XWayland */ + struct wl_event_source *x11_event_source; /* wl_event_loop fd watcher for x_display */ /* Phase 20: real input devices from nested backend + their listeners */ struct wlr_input_device *real_keyboard_dev; struct wlr_input_device *real_pointer_dev; @@ -1063,7 +1066,10 @@ handle_xwayland_map(struct wl_listener *listener, void *data) } - /* Pure Wayland mode: manage as WM client. */ + /* Manage as WM client. Guard: wScreenWithNumber asserts screen_count > 0, + * so skip if StartUp() hasn't initialised the screen yet. */ + if (w_global.screen_count == 0) + return; WScreen *scr = wScreenWithNumber(0); if (scr) { WWindow *wwin = wManageWindow(scr, view->id); @@ -1189,13 +1195,24 @@ handle_xwayland_ready(struct wl_listener *listener, void *data) if (!wl_state.xwayland || !wl_state.xwayland->display_name) { wwarning("wl_backend: XWayland ready but display_name unavailable"); + wl_state.xwayland_ready = true; return; } /* Advertise the XWayland display so X11 client apps can connect. */ setenv("DISPLAY", wl_state.xwayland->display_name, 1); - wwarning("wl_backend: XWayland ready on %s (X11 clients can now connect)", + wwarning("wl_backend: XWayland ready on %s — opening WM Xlib connection", wl_state.xwayland->display_name); + + /* Open the WM's own Xlib connection to its XWayland instance. + * This Display* is returned from wl_display_open so WINGs, title bars, + * root menus, and the dock all work via the X11 code path. */ + wl_state.x_display = XOpenDisplay(wl_state.xwayland->display_name); + if (!wl_state.x_display) + wwarning("wl_backend: XOpenDisplay(%s) failed — WM UI limited to native Wayland", + wl_state.xwayland->display_name); + + wl_state.xwayland_ready = true; } /* @@ -1567,6 +1584,23 @@ handle_backend_new_output(struct wl_listener *listener, void *data) /* Display lifecycle (Phase 32 / Phase 34) */ /* ------------------------------------------------------------------ */ +/* Called by the Wayland event loop whenever the X11 connection fd is readable. + * Drains all pending X11 events and dispatches them through WINGs so that + * menus, dialogs, and other WINGs widgets work during wl_display_run(). */ +static int +wl_handle_x11_events(int fd, uint32_t mask, void *data) +{ + (void)fd; (void)mask; (void)data; + if (!wl_state.x_display) + return 0; + XEvent event; + while (XPending(wl_state.x_display)) { + XNextEvent(wl_state.x_display, &event); + WMHandleEvent(&event); + } + return 0; +} + static void * wl_display_open(const char *name) { @@ -2117,12 +2151,36 @@ wl_display_open(const char *name) wl_state.started = 1; - /* XWayland is started but we do not block on its ready signal here. - * The ready callback (handle_xwayland_ready) will set DISPLAY when it - * fires so X11 client apps can connect. The WM's own UI renders via - * native Wayland surfaces — no X11 Display* needed by the WM itself. */ + /* Block until XWayland fires the "ready" signal — pump the Wayland server + * event loop so the XWayland process can complete its handshake. + * handle_xwayland_ready() calls XOpenDisplay and stores the result in + * wl_state.x_display, then sets xwayland_ready = true to break the loop. + * If XWayland is not available, return NULL (pure Wayland path). */ + if (wl_state.xwayland) { + wwarning("wl_backend: waiting for XWayland ready signal..."); + struct wl_event_loop *ev_loop = + wl_display_get_event_loop(wl_state.display); + while (!wl_state.xwayland_ready) { + wl_display_flush_clients(wl_state.display); + wl_event_loop_dispatch(ev_loop, 10); + } + if (wl_state.x_display) { + wwarning("wl_backend: XWayland bridge ready — Display*=%p, full WM UI enabled", + (void *)wl_state.x_display); + /* Integrate X11 events into the Wayland event loop so WINGs + * menus, dialogs, and keyboard events are processed during + * wl_display_run(). ConnectionNumber(dpy) is the Xlib socket fd. */ + wl_state.x11_event_source = wl_event_loop_add_fd( + wl_display_get_event_loop(wl_state.display), + ConnectionNumber(wl_state.x_display), + WL_EVENT_READABLE, + wl_handle_x11_events, NULL); + } else { + wwarning("wl_backend: XWayland ready but XOpenDisplay failed — pure Wayland mode"); + } + } - return NULL; /* pure Wayland: no X11 Display* used by the WM */ + return wl_state.x_display; } static void @@ -2130,6 +2188,18 @@ wl_display_close(void) { if (!wl_state.started) return; + + /* Remove the X11 event source and close the Xlib bridge connection + * before killing XWayland so we don't get stale readable events. */ + if (wl_state.x11_event_source) { + wl_event_source_remove(wl_state.x11_event_source); + wl_state.x11_event_source = NULL; + } + if (wl_state.x_display) { + XCloseDisplay(wl_state.x_display); + wl_state.x_display = NULL; + } + /* Phase 17: shut down XWayland. * * wlroots 0.15 bug: wlr_xwayland_server_destroy() calls wl_client_destroy() @@ -2970,8 +3040,17 @@ wl_screen_open(WScreen *scr) scr->scr_height = wl_state.output ? (int)wl_state.output->height : 1080; scr->depth = 24; - scr->root_win = WNATIVE_NONE; + scr->root_win = WNATIVE_NONE; scr->colormap = 0; + + /* XWayland bridge: fill in real X11 screen values so RCreateContext + * and WINGs can use the display connection for drawing and fonts. */ + if (dpy) { + int snum = DefaultScreen(dpy); + scr->root_win = DefaultRootWindow(dpy); + scr->depth = DefaultDepth(dpy, snum); + scr->colormap = DefaultColormap(dpy, snum); + } } static void wl_screen_close(WScreen *scr) { (void)scr; } @@ -4753,6 +4832,15 @@ wl_screen_create_internals(WScreen *scr) wSetModifierMasks(Mod1Mask, Mod1Mask, Mod4Mask, 0); + /* XWayland bridge: create a real X11 helper window for EWMH + * (_NET_SUPPORTING_WM_CHECK) and clipboard selection ownership. */ + if (dpy) { + Window xwin = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), + 0, 0, 1, 1, 0, 0, 0); + if (xwin) + scr->info_window = (WNativeWindow)xwin; + } + /* Phase 30: the WDock X11 code path is still disabled (it needs X11 GCs / * pixmaps we don't have in pure Wayland mode). The visual dock panel is * created below via wm_dock_create() which uses wm_shm_surface directly. */