diff --git a/src/backend/backend.h b/src/backend/backend.h index f5eb4d9..4bd528e 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -46,6 +46,18 @@ struct WObjDescriptor; */ typedef struct WMBackend { + /* ------------------------------------------------------------------ */ + /* Backend identity flag (Path 1) */ + /* ------------------------------------------------------------------ */ + + /* + * Non-zero if this is the Wayland compositor backend. + * Allows policy code to take the Wayland fast-path even when dpy != NULL + * (i.e. when WindowMaker's X11 UI is bridged through XWayland). + * X11 backend: is_wayland = 0. Wayland backend: is_wayland = 1. + */ + int is_wayland; + /* ------------------------------------------------------------------ */ /* Display lifecycle (Phase 32) */ /* ------------------------------------------------------------------ */ @@ -95,6 +107,14 @@ typedef struct WMBackend { */ void (*event_loop_run)(void); + /* + * Request the backend event loop to stop. + * X11: no-op (X11 EventLoop exits via exit()). + * Wayland: wl_display_terminate(display) -- causes event_loop_run() to return. + * Called from signal handlers so that cleanup happens outside signal context. + */ + void (*event_loop_terminate)(void); + /* ------------------------------------------------------------------ */ /* Screen lifecycle (Phase 2) */ /* ------------------------------------------------------------------ */ diff --git a/src/event.c b/src/event.c index 66dd341..7a132f7 100644 --- a/src/event.c +++ b/src/event.c @@ -159,7 +159,18 @@ void DispatchEvent(XEvent * event) * WMHandleEvent() can't be called from anything * executed inside here, or we can get in a infinite * recursive loop. + * + * In Wayland mode, calling wl_display_destroy() from a signal + * handler while wl_event_loop_dispatch() is active causes a + * double-free (glibc detects it as SIGABRT). Instead, write a + * byte to the self-pipe; the event loop reads it and calls + * wl_display_terminate() from within loop context. EventLoop() + * then calls Exit(0) after wl_display_run() returns. */ + if (!dpy) { + wm_backend->event_loop_terminate(); + return; + } Shutdown(WSExitMode); } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) { @@ -368,8 +379,14 @@ noreturn void EventLoop(void) int retVal = 0; #endif - /* Wayland path: delegate to the backend's event loop (wl_display_run). */ - if (!dpy) { + /* Wayland path: delegate to the backend's event loop (wl_display_run). + * When SIGTERM is received, event_loop_terminate() writes to a self-pipe + * which causes the event loop to call wl_display_terminate() from within + * loop context; wl_display_run() then returns here, and Exit(0) performs + * the final cleanup safely outside signal-handler and event-loop context. + * Path 1: use wm_backend->is_wayland rather than !dpy because dpy is set + * to the XWayland Display* even in Wayland-compositor mode. */ + if (!dpy || wm_backend->is_wayland) { wm_backend->event_loop_run(); Exit(0); } diff --git a/src/framewin.c b/src/framewin.c index 8d9004c..28a03af 100644 --- a/src/framewin.c +++ b/src/framewin.c @@ -862,6 +862,10 @@ static void remakeTexture(WFrameWindow * fwin, int state) Pixmap tpmap; #endif + /* Phase 19: Wayland — no X11 rendering context; RConvertImage would crash */ + if (!fwin->screen_ptr->rcontext) + return; + if (fwin->title_texture[state] && fwin->titlebar) { FREE_PIXMAP(fwin->title_back[state]); if (wPreferences.new_style == TS_NEW) { @@ -1045,7 +1049,7 @@ void wFrameWindowPaint(WFrameWindow * fwin) fwin->languagebutton_image = scr->b_pixmaps[WBUT_XKBGROUP1 + fwin->languagemode]; #endif - if (fwin->title) { + if (dpy && fwin->title) { Drawable buf; char *title; @@ -1125,10 +1129,12 @@ static void reconfigure(WFrameWindow * fwin, int x, int y, int width, int height int k = (wPreferences.new_style == TS_NEW ? 4 : 3); int resizedHorizontally = 0; - if (dontMove) - XResizeWindow(dpy, fwin->core->window, width, height); - else - XMoveResizeWindow(dpy, fwin->core->window, x, y, width, height); + if (dpy) { + if (dontMove) + XResizeWindow(dpy, fwin->core->window, width, height); + else + XMoveResizeWindow(dpy, fwin->core->window, x, y, width, height); + } if (fwin->core->width != width) { fwin->flags.need_texture_remake = 1; @@ -1144,12 +1150,12 @@ static void reconfigure(WFrameWindow * fwin, int x, int y, int width, int height */ if (fwin->left_button) { if (width < fwin->top_width * k && !fwin->flags.lbutton_dont_fit) { - if (!fwin->flags.hide_left_button) + if (!fwin->flags.hide_left_button && dpy) XUnmapWindow(dpy, fwin->left_button->window); fwin->flags.lbutton_dont_fit = 1; } else if (width >= fwin->top_width * k && fwin->flags.lbutton_dont_fit) { - if (!fwin->flags.hide_left_button) + if (!fwin->flags.hide_left_button && dpy) XMapWindow(dpy, fwin->left_button->window); fwin->flags.lbutton_dont_fit = 0; @@ -1158,12 +1164,12 @@ static void reconfigure(WFrameWindow * fwin, int x, int y, int width, int height #ifdef XKB_BUTTON_HINT if (fwin->language_button) { if (width < fwin->top_width * k && !fwin->flags.languagebutton_dont_fit) { - if (!fwin->flags.hide_language_button) + if (!fwin->flags.hide_language_button && dpy) XUnmapWindow(dpy, fwin->language_button->window); fwin->flags.languagebutton_dont_fit = 1; } else if (width >= fwin->top_width * k && fwin->flags.languagebutton_dont_fit) { - if (!fwin->flags.hide_language_button) + if (!fwin->flags.hide_language_button && dpy) XMapWindow(dpy, fwin->language_button->window); fwin->flags.languagebutton_dont_fit = 0; @@ -1173,27 +1179,29 @@ static void reconfigure(WFrameWindow * fwin, int x, int y, int width, int height if (fwin->right_button) { if (width < fwin->top_width * 2 && !fwin->flags.rbutton_dont_fit) { - if (!fwin->flags.hide_right_button) + if (!fwin->flags.hide_right_button && dpy) XUnmapWindow(dpy, fwin->right_button->window); fwin->flags.rbutton_dont_fit = 1; } else if (width >= fwin->top_width * 2 && fwin->flags.rbutton_dont_fit) { - if (!fwin->flags.hide_right_button) + if (!fwin->flags.hide_right_button && dpy) XMapWindow(dpy, fwin->right_button->window); fwin->flags.rbutton_dont_fit = 0; } } - if (wPreferences.new_style == TS_NEW) { - if (fwin->right_button) - XMoveWindow(dpy, fwin->right_button->window, - width - fwin->right_button->width + 1, 0); - } else { - if (fwin->right_button) - XMoveWindow(dpy, fwin->right_button->window, - width - fwin->right_button->width - 3, - (fwin->titlebar->height - fwin->right_button->height) / 2); + if (dpy) { + if (wPreferences.new_style == TS_NEW) { + if (fwin->right_button) + XMoveWindow(dpy, fwin->right_button->window, + width - fwin->right_button->width + 1, 0); + } else { + if (fwin->right_button) + XMoveWindow(dpy, fwin->right_button->window, + width - fwin->right_button->width - 3, + (fwin->titlebar->height - fwin->right_button->height) / 2); + } } updateTitlebar(fwin); checkTitleSize(fwin); @@ -1237,7 +1245,7 @@ int wFrameWindowChangeTitle(WFrameWindow *fwin, const char *new_title) fwin->title = wstrdup(new_title); if (fwin->titlebar) { - XClearWindow(dpy, fwin->titlebar->window); + if (dpy) XClearWindow(dpy, fwin->titlebar->window); wFrameWindowPaint(fwin); } @@ -1278,6 +1286,12 @@ static void checkTitleSize(WFrameWindow * fwin) return; } + /* Phase 19: Wayland — no wmscreen for WMWidthOfString */ + if (!fwin->screen_ptr->wmscreen) { + fwin->flags.incomplete_title = 0; + return; + } + if (!fwin->titlebar) { fwin->flags.incomplete_title = 1; return; @@ -1309,6 +1323,10 @@ static void paintButton(WCoreWindow * button, WTexture * texture, unsigned long { WScreen *scr = button->screen_ptr; GC copy_gc = scr->copy_gc; + + /* Phase 19: Wayland — no X11 display or GCs for button rendering */ + if (!dpy) + return; int x = 0, y = 0, d = 0; int left = 0, width = 0; diff --git a/src/icon.c b/src/icon.c index f8aa2d0..9040dd1 100644 --- a/src/icon.c +++ b/src/icon.c @@ -220,10 +220,10 @@ void wIconDestroy(WIcon *icon) if (icon->icon_name) XFree(icon->icon_name); - if (icon->pixmap) + if (icon->pixmap && dpy) XFreePixmap(dpy, icon->pixmap); - if (icon->mini_preview) + if (icon->mini_preview && dpy) XFreePixmap(dpy, icon->mini_preview); unset_icon_image(icon); @@ -621,7 +621,7 @@ void set_icon_minipreview(WIcon *icon, RImage *image) wPreferences.minipreview_size - 2 * MINIPREVIEW_BORDER); if (RConvertImage(scr->rcontext, scaled_mini_preview, &tmp)) { - if (icon->mini_preview != None) + if (icon->mini_preview != None && dpy) XFreePixmap(dpy, icon->mini_preview); icon->mini_preview = tmp; } diff --git a/src/main.c b/src/main.c index e2d3b24..31afd13 100644 --- a/src/main.c +++ b/src/main.c @@ -815,7 +815,15 @@ static int real_main(int argc, char **argv) DisplayName = XDisplayName(DisplayName); setenv("DISPLAY", DisplayName, 1); - wXModifierInitialize(); + /* Path 1: skip XGetModifierMapping() when dpy is an XWayland + * display. XWayland can only service X11 requests when the + * Wayland event loop (wl_display_run) is running, which has not + * started yet at this point in main.c. Calling XGetModifierMapping + * before EventLoop() would deadlock waiting for a reply that + * XWayland cannot deliver. Modifier masks are handled via XKB + * in wl_backend (wSetModifierMasks), so this call is not needed. */ + if (!wm_backend->is_wayland) + wXModifierInitialize(); } StartUp(!multiHead); diff --git a/src/misc.c b/src/misc.c index ec91158..6964bf7 100644 --- a/src/misc.c +++ b/src/misc.c @@ -76,6 +76,10 @@ static void eatExpose(void) { XEvent event, foo; + /* Phase 19: Wayland — no X11 event queue to drain */ + if (!dpy) + return; + /* compress all expose events into a single one */ if (XCheckMaskEvent(dpy, ExposureMask, &event)) { @@ -91,6 +95,11 @@ static void eatExpose(void) void move_window(Window win, int from_x, int from_y, int to_x, int to_y) { + /* Phase 19: Wayland — frame windows are virtual; skip X11 move */ + if (!dpy) { + (void) win; (void) from_x; (void) from_y; (void) to_x; (void) to_y; + return; + } #ifdef USE_ANIMATIONS if (wPreferences.no_animations) XMoveWindow(dpy, win, to_x, to_y); @@ -110,6 +119,12 @@ void move_window(Window win, int from_x, int from_y, int to_x, int to_y) * following windows are going to be offset by (ICON_SIZE*i,0) */ void slide_windows(Window wins[], int n, int from_x, int from_y, int to_x, int to_y) { + /* Phase 19: Wayland — frame windows are virtual; skip X11 slide */ + if (!dpy) { + (void)wins; (void)n; (void)from_x; (void)from_y; (void)to_x; (void)to_y; + return; + } + time_t time0 = time(NULL); float dx, dy, x = from_x, y = from_y, px, py; Bool is_dx_nul, is_dy_nul; @@ -291,6 +306,10 @@ static char *getTextSelection(WScreen * screen, Atom selection) { int buffer = -1; + /* Phase 19: Wayland — no X11 selection/clipboard available */ + if (!dpy) + return NULL; + switch (selection) { case WA_CUT_BUFFER0: buffer = 0; @@ -675,15 +694,20 @@ void ExecuteInputCommand(WScreen *scr, const char *cmdline) cmd = ExpandOptions(scr, cmdline); if (cmd) { - XGrabPointer(dpy, scr->root_win, True, 0, - GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime); - XSync(dpy, False); + /* Phase 19: Wayland — skip cursor grab visual feedback */ + if (dpy) { + XGrabPointer(dpy, scr->root_win, True, 0, + GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime); + XSync(dpy, False); + } ExecuteShellCommand(scr, cmd); wfree(cmd); - XUngrabPointer(dpy, CurrentTime); - XSync(dpy, False); + if (dpy) { + XUngrabPointer(dpy, CurrentTime); + XSync(dpy, False); + } } } @@ -790,6 +814,9 @@ char *GetShortcutKey(WShortKey key) } } + /* Phase 19: Wayland — no X11 keycode-to-keysym mapping available */ + if (!dpy) + return NULL; key_name = XKeysymToString(W_KeycodeToKeysym(dpy, key.keycode, 0)); if (!key_name) return NULL; diff --git a/src/pixmap.c b/src/pixmap.c index 1ad0454..8b6c6ee 100644 --- a/src/pixmap.c +++ b/src/pixmap.c @@ -130,11 +130,11 @@ void wPixmapDestroy(WPixmap * pix) { if (!pix->shared) { if (pix->mask && !pix->client_owned_mask) { - XFreePixmap(dpy, pix->mask); + if (dpy) XFreePixmap(dpy, pix->mask); } if (pix->image && !pix->client_owned) { - XFreePixmap(dpy, pix->image); + if (dpy) XFreePixmap(dpy, pix->image); } } wfree(pix); diff --git a/src/resources.c b/src/resources.c index a2cdc60..ab5486f 100644 --- a/src/resources.c +++ b/src/resources.c @@ -35,6 +35,10 @@ int wGetColorForColormap(Colormap colormap, const char *color_name, XColor *color) { + /* Phase 19: Wayland — no X11 colormap available */ + if (!dpy) + return False; + if (!XParseColor(dpy, colormap, color_name, color)) { wwarning(_("could not parse color \"%s\""), color_name); return False; @@ -53,6 +57,9 @@ int wGetColor(WScreen *scr, const char *color_name, XColor *color) void wFreeColor(WScreen * scr, unsigned long pixel) { + if (!dpy) + return; /* Phase 19: Wayland — no colormap to free from */ + if (pixel != scr->white_pixel && pixel != scr->black_pixel) { unsigned long colors[1]; diff --git a/src/screen.c b/src/screen.c index 39e5686..0e444ea 100644 --- a/src/screen.c +++ b/src/screen.c @@ -158,10 +158,11 @@ WScreen *wScreenInit(int screen_number) wm_backend->monitors_select_events(scr); wm_backend->screen_set_root_cursor(scr, WCUR_ROOT); - /* Wayland path: screen_open() sets geometry; X11/wrlib context not needed. - * Allocate workspace 0 directly (wWorkspaceNew is unsafe here because it - * calls wDockCreate which requires X11 rendering resources). */ - if (!dpy) { + /* Pure Wayland path (no XWayland display): skip X11/wrlib context setup + * and allocate workspace 0 directly. When XWayland is available (dpy is + * set, Path 1), fall through to the full X11 init so WINGs, RContext, + * GCs, and menus work correctly on the XWayland display. */ + if (wm_backend->is_wayland && !dpy) { WWorkspace *wspace = wmalloc(sizeof(WWorkspace)); wspace->name = wstrdup("Workspace 1"); wspace->clip = NULL; diff --git a/src/startup.c b/src/startup.c index 1305934..1583d69 100644 --- a/src/startup.c +++ b/src/startup.c @@ -177,8 +177,8 @@ static void getOffendingModifiers(void) Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask }; - if (!dpy) - return; /* Wayland: no modifier mapping to query */ + if (!dpy || wm_backend->is_wayland) + return; /* Wayland: modifier mapping handled by wlroots/XKB */ nlock = wm_backend->keysym_to_keycode(WM_KEY_NUM_LOCK); slock = wm_backend->keysym_to_keycode(WM_KEY_SCROLL_LOCK); @@ -474,8 +474,10 @@ void StartUp(Bool defaultScreenOnly) /* handle X shutdowns and errors via backend */ wm_backend->register_error_handlers(); - /* set hook for our event dispatcher in WINGs event dispatcher (X11 only) */ - if (dpy) + /* set hook for our event dispatcher in WINGs event dispatcher (X11 only). + * Skip for Wayland: we run a wlroots event loop, not a WINGs/X11 loop. + * dpy may be set (XWayland bridge), but X11 event dispatch is not used. */ + if (dpy && !wm_backend->is_wayland) WMHookEventHandler(DispatchEvent); /* initialize defaults stuff */ @@ -550,7 +552,7 @@ void StartUp(Bool defaultScreenOnly) if (dpy) InitializeSwitchMenu(); - /* initialize/restore state for the screens (X11 only for now) */ + /* initialize/restore state for the screens */ if (dpy) { for (j = 0; j < w_global.screen_count; j++) { int lastDesktop; @@ -648,7 +650,7 @@ static void manageAllWindows(WScreen * scr, int crashRecovery) WWindow *wwin; if (!dpy) - return; /* Wayland: no pre-existing X11 clients to adopt */ + return; /* no display — nothing to adopt */ wm_backend->server_grab(); XQueryTree(dpy, scr->root_win, &root, &parent, &children, &nchildren); diff --git a/src/texture.c b/src/texture.c index d656232..e581ca0 100644 --- a/src/texture.c +++ b/src/texture.c @@ -47,6 +47,12 @@ WTexSolid *wTextureMakeSolid(WScreen * scr, XColor * color) texture->type = WTEX_SOLID; texture->subtype = 0; + /* Phase 19: Wayland — skip X11 color allocation and GC creation */ + if (!dpy) { + texture->normal = *color; + return texture; + } + XAllocColor(dpy, scr->w_colormap, color); texture->normal = *color; if (color->red == 0 && color->blue == 0 && color->green == 0) { @@ -127,9 +133,11 @@ void wTextureDestroy(WScreen * scr, WTexture * texture) #define CANFREE(c) (c!=scr->black_pixel && c!=scr->white_pixel && c!=0) switch (texture->any.type) { case WTEX_SOLID: - XFreeGC(dpy, texture->solid.light_gc); - XFreeGC(dpy, texture->solid.dark_gc); - XFreeGC(dpy, texture->solid.dim_gc); + if (dpy) { + XFreeGC(dpy, texture->solid.light_gc); + XFreeGC(dpy, texture->solid.dark_gc); + XFreeGC(dpy, texture->solid.dim_gc); + } if (CANFREE(texture->solid.light.pixel)) colors[count++] = texture->solid.light.pixel; if (CANFREE(texture->solid.dim.pixel)) @@ -160,7 +168,8 @@ void wTextureDestroy(WScreen * scr, WTexture * texture) if (CANFREE(texture->any.color.pixel)) colors[count++] = texture->any.color.pixel; - if (count > 0) { + /* Phase 19: Wayland — skip X11 color/GC cleanup when no display */ + if (dpy && count > 0) { XErrorHandler oldhandler; /* ignore error from buggy servers that don't know how @@ -171,7 +180,8 @@ void wTextureDestroy(WScreen * scr, WTexture * texture) XSync(dpy, 0); XSetErrorHandler(oldhandler); } - XFreeGC(dpy, texture->any.gc); + if (dpy) + XFreeGC(dpy, texture->any.gc); wfree(texture); #undef CANFREE } @@ -192,10 +202,12 @@ WTexGradient *wTextureMakeGradient(WScreen *scr, int style, const RColor *from, texture->normal.green = (from->green + to->green) << 7; texture->normal.blue = (from->blue + to->blue) << 7; - XAllocColor(dpy, scr->w_colormap, &texture->normal); - gcv.background = gcv.foreground = texture->normal.pixel; - gcv.graphics_exposures = False; - texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + if (dpy) { + XAllocColor(dpy, scr->w_colormap, &texture->normal); + gcv.background = gcv.foreground = texture->normal.pixel; + gcv.graphics_exposures = False; + texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + } return texture; } @@ -224,10 +236,12 @@ WTexIGradient *wTextureMakeIGradient(WScreen *scr, int thickness1, const RColor texture->normal.green = (colors2[0].green + colors2[1].green) << 7; texture->normal.blue = (colors2[0].blue + colors2[1].blue) << 7; } - XAllocColor(dpy, scr->w_colormap, &texture->normal); - gcv.background = gcv.foreground = texture->normal.pixel; - gcv.graphics_exposures = False; - texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + if (dpy) { + XAllocColor(dpy, scr->w_colormap, &texture->normal); + gcv.background = gcv.foreground = texture->normal.pixel; + gcv.graphics_exposures = False; + texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + } return texture; } @@ -252,10 +266,12 @@ WTexMGradient *wTextureMakeMGradient(WScreen * scr, int style, RColor ** colors) texture->colors = colors; - XAllocColor(dpy, scr->w_colormap, &texture->normal); - gcv.background = gcv.foreground = texture->normal.pixel; - gcv.graphics_exposures = False; - texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + if (dpy) { + XAllocColor(dpy, scr->w_colormap, &texture->normal); + gcv.background = gcv.foreground = texture->normal.pixel; + gcv.graphics_exposures = False; + texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + } return texture; } @@ -276,10 +292,12 @@ WTexPixmap *wTextureMakePixmap(WScreen *scr, int style, const char *pixmap_file, texture->normal = *color; - XAllocColor(dpy, scr->w_colormap, &texture->normal); - gcv.background = gcv.foreground = texture->normal.pixel; - gcv.graphics_exposures = False; - texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + if (dpy) { + XAllocColor(dpy, scr->w_colormap, &texture->normal); + gcv.background = gcv.foreground = texture->normal.pixel; + gcv.graphics_exposures = False; + texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + } texture->pixmap = image; @@ -309,10 +327,12 @@ WTexTGradient *wTextureMakeTGradient(WScreen *scr, int style, const RColor *from texture->normal.green = (from->green + to->green) << 7; texture->normal.blue = (from->blue + to->blue) << 7; - XAllocColor(dpy, scr->w_colormap, &texture->normal); - gcv.background = gcv.foreground = texture->normal.pixel; - gcv.graphics_exposures = False; - texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + if (dpy) { + XAllocColor(dpy, scr->w_colormap, &texture->normal); + gcv.background = gcv.foreground = texture->normal.pixel; + gcv.graphics_exposures = False; + texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv); + } texture->pixmap = image; @@ -535,6 +555,10 @@ void wDrawBevel(Drawable d, unsigned width, unsigned height, WTexSolid * texture if (relief == WREL_FLAT) return; + /* Phase 19: Wayland — no X11 display, skip bevel drawing */ + if (!dpy) + return; + light = texture->light_gc; dim = texture->dim_gc; dark = texture->dark_gc;