diff --git a/+cmd~ b/+cmd~ new file mode 100644 index 0000000..b03cad2 --- /dev/null +++ b/+cmd~ @@ -0,0 +1,28 @@ +Xwayland :3 +DISPLAY=':3' gdb -batch -ex 'set confirm off' -ex 'set filename-display absolute' -ex run -ex bt -ex quit --args src/.libs/wmaker --for-real >[2=1] +DISPLAY=':3' ./src/wmaker --for-real >[2=1] +DISPLAY=':3' ./WPrefs.app/WPrefs +killall -9 WPrefs + +WMAKER_USE_WAYLAND='1' WMAKER_WAYLAND_NESTED='1' LD_LIBRARY_PATH='/usr/local/lib' G_SLICE=always-malloc MALLOC_CHECK_=3 valgrind --tool=memcheck --track-origins=yes --leak-check=no ./src/.libs/wmaker + + +WAYLAND_DISPLAY=wayland-0 foot + +./configure --prefix=/usr/local --enable-wayland --disable-x11-backend --disable-x11-wraster --disable-xwayland +make clean +make -j8 CFLAGS='-fsanitize=leak' LDFLAGS='-fsanitize=leak' +echo '' | sudo -S make install +wmaker +WLR_BACKENDS=wayland WLR_WL_OUTPUTS=2 WMAKER_USE_WAYLAND='1' WMAKER_WAYLAND_NESTED=1 LD_LIBRARY_PATH='/usr/local/lib' gdb -batch -ex 'set confirm off' -ex 'set filename-display absolute' -ex run -ex bt -ex quit --args src/.libs/wmaker --for-real >[2=1] + +CFLAGS='-O0 -g -fsanitize=address -fno-omit-frame-pointer' LDFLAGS='-fsanitize=address' ./configure --prefix=/usr/local --enable-wayland; CFLAGS='-O0 -g -fsanitize=address -fno-omit-frame-pointer' LDFLAGS='-fsanitize=address' make -j8 +WMAKER_USE_WAYLAND='1' WMAKER_WAYLAND_NESTED='1' LD_LIBRARY_PATH='/usr/local/lib' ./src/.libs/wmaker + + + +git stash pop +git status +git diff --stat + +export MALLOC_CHECK_=3; gdb -batch -ex 'set confirm off' -ex 'set filename-display absolute' -ex run -ex bt -ex quit --args src/.libs/wmaker --for-real diff --git a/20260626_07h36m55s_grim.png b/20260626_07h36m55s_grim.png new file mode 100644 index 0000000..40b1a58 Binary files /dev/null and b/20260626_07h36m55s_grim.png differ diff --git a/20260626_08h20m35s_grim.png b/20260626_08h20m35s_grim.png new file mode 100644 index 0000000..736e14a Binary files /dev/null and b/20260626_08h20m35s_grim.png differ diff --git a/20260626_08h26m29s_grim.png b/20260626_08h26m29s_grim.png new file mode 100644 index 0000000..5207aaa Binary files /dev/null and b/20260626_08h26m29s_grim.png differ diff --git a/[2=1] b/[2=1] new file mode 100644 index 0000000..9f1fcc3 --- /dev/null +++ b/[2=1] @@ -0,0 +1,28 @@ +[Thread debugging using libthread_db enabled] +Using host libthread_db library "/usr/lib64/libthread_db.so.1". +[New Thread 0x7fffe25ff6c0 (LWP 150586)] +[New Thread 0x7fffe1dfe6c0 (LWP 150587)] +[New Thread 0x7fffe11ff6c0 (LWP 150588)] +[New Thread 0x7fffe09fe6c0 (LWP 150589)] +[Thread 0x7fffe09fe6c0 (LWP 150589) exited] +[Thread 0x7fffe11ff6c0 (LWP 150588) exited] +[New Thread 0x7fffe11ff6c0 (LWP 150590)] +[New Thread 0x7fffe09fe6c0 (LWP 150591)] +[New Thread 0x7fffd3bff6c0 (LWP 150592)] +[New Thread 0x7fffd33fe6c0 (LWP 150593)] +[New Thread 0x7fffd29fd6c0 (LWP 150596)] +[Detaching after fork from child process 150597] +[Detaching after vfork from child process 150598] +[Detaching after fork from child process 150599] + +Thread 10 "[pango] fontcon" received signal SIGINT, Interrupt. +[Switching to Thread 0x7fffd29fd6c0 (LWP 150596)] +0x00007ffff727948d in syscall () from /usr/lib64/libc.so.6 +#0 0x00007ffff727948d in syscall () at /usr/lib64/libc.so.6 +#1 0x00007ffff7a9dbe2 in g_cond_wait () at /usr/lib64/libglib-2.0.so.0 +#2 0x00007ffff7a33474 in ??? () at /usr/lib64/libglib-2.0.so.0 +#3 0x00007ffff7a3393b in g_async_queue_pop () at /usr/lib64/libglib-2.0.so.0 +#4 0x00007ffff7ed29f5 in ??? () at /usr/lib64/libpangoft2-1.0.so.0 +#5 0x00007ffff7a9e306 in ??? () at /usr/lib64/libglib-2.0.so.0 +#6 0x00007ffff720d4ad in ??? () at /usr/lib64/libc.so.6 +#7 0x00007ffff727b68c in ??? () at /usr/lib64/libc.so.6 diff --git a/asan.log b/asan.log new file mode 100644 index 0000000..e69de29 diff --git a/asan.sh b/asan.sh new file mode 100755 index 0000000..5c0acaa --- /dev/null +++ b/asan.sh @@ -0,0 +1 @@ +ASAN_OPTIONS=detect_leaks=0 ./src/.libs/wmaker >asan.log diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..6f07839 Binary files /dev/null and b/screenshot.png differ diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index e287c8f..5050859 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -249,24 +249,7 @@ extern const char *resize_edges_to_cursor(uint32_t edges); -/* Startup pump thread: keeps the Wayland event loop running while the main - * thread initialises WINGs/RContext during StartUp(). XWayland needs the - * Wayland compositor event loop to be responsive so it can answer X11 - * round-trips like XGetModifierMapping (which queries keyboard state from - * wlroots via Wayland protocol). The thread does NOT touch Xlib — the - * x11_event_source fd watcher is registered AFTER this thread exits in - * wl_event_loop_run(), preventing any concurrent Xlib access. */ -static void * -wl_startup_pump_thread(void *arg) -{ - (void)arg; - struct wl_event_loop *loop = wl_display_get_event_loop(wl_state.display); - while (wl_state.startup_pump_running) { - wl_display_flush_clients(wl_state.display); - wl_event_loop_dispatch(loop, 1); - } - return NULL; -} +/* Startup pump thread removed — no persistent X connection. */ #if USE_XWAYLAND extern int wl_handle_x11_events(int fd, uint32_t mask, void *data); @@ -296,8 +279,6 @@ wl_display_open(const char *name) #if USE_XWAYLAND XInitThreads(); - /* Install X error handler: Mesa/EGL via XWayland */ - XSetErrorHandler(wl_catch_xwayland_error); #endif wlr_log_init(WLR_ERROR, NULL); @@ -736,8 +717,6 @@ wl_display_open(const char *name) /* 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). */ /* XWayland is lazy — it starts when the first X11 client connects. * No need to wait for it at startup. Mark as ready so the rest of @@ -746,27 +725,6 @@ wl_display_open(const char *name) if (wl_state.xwayland) { wwarning("wl_backend: XWayland configured (eager mode) — ready at startup"); wl_state.xwayland_ready = true; - if (wl_state.x_display) { - wwarning("wl_backend: XWayland bridge ready — Display*=%p, full WM UI enabled", - (void *)wl_state.x_display); - /* Start a pump thread to keep the Wayland event loop running - * while the main thread calls WMCreateScreenWithRContext() and - * other blocking X11 init in StartUp(). XWayland needs the - * Wayland compositor to be responsive to handle round-trips like - * XGetModifierMapping (which queries keyboard state from wlroots). - * The x11_event_source fd watcher is registered later in - * wl_event_loop_run() after this thread exits, so the pump - * thread never touches Xlib and there is no Xlib contention. */ - wl_state.startup_pump_running = 1; - if (pthread_create(&wl_state.startup_pump_thread, NULL, - wl_startup_pump_thread, NULL) != 0) { - wwarning("wl_backend: pthread_create for pump thread failed — " - "WINGs init may deadlock"); - wl_state.startup_pump_running = 0; - } - } else { - wwarning("wl_backend: XWayland ready but XOpenDisplay failed — pure Wayland mode"); - } } #endif @@ -779,18 +737,9 @@ 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; - } + /* X11 event source and Xlib connection removed — wlroots manages + * XWayland internally. */ #if USE_XWAYLAND - if (wl_state.x_display) { - XCloseDisplay(wl_state.x_display); - wl_state.x_display = NULL; - } -#endif /* shut down XWayland. * @@ -858,6 +807,7 @@ wl_display_close(void) wl_state.xwayland = NULL; unsetenv("DISPLAY"); } +#endif /* Clean up the signalfd source (registered in wl_event_loop_run). */ if (wl_state.sigfd_source) { wl_event_source_remove(wl_state.sigfd_source); @@ -1102,16 +1052,6 @@ wl_event_loop_run(void) /* Register the X11 fd watcher now that StartUp() has completed and * WINGs is initialised. Doing this here (not in wl_display_open) ensures * the pump thread never touches Xlib. */ - if (wl_state.x_display && !wl_state.x11_event_source) { - 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); - if (wl_state.x11_event_source) - wwarning("wl_backend: X11 event source registered for WINGs dispatch"); - } - wwarning("wl_backend: DISPLAY=%s WAYLAND_DISPLAY=%s", getenv("DISPLAY") ? getenv("DISPLAY") : "(unset)", getenv("WAYLAND_DISPLAY") ? getenv("WAYLAND_DISPLAY") : "(unset)"); diff --git a/src/backend/wayland/wl_client.c b/src/backend/wayland/wl_client.c index a553b7b..dfaf137 100644 --- a/src/backend/wayland/wl_client.c +++ b/src/backend/wayland/wl_client.c @@ -602,30 +602,13 @@ void wl_client_kill_impl(WWindow *wwin) #if USE_XWAYLAND void wl_client_message_forward(WMEvent *event, WNativeWindow target_win) { - if (!wl_state.x_display || !event) return; - XEvent *xev = (XEvent *)event->_x11_raw; - if (!xev) return; - xev->xclient.window = (WNativeWindow)target_win; - XSendEvent(wl_state.x_display, (WNativeWindow)target_win, False, 0L, xev); + (void)event; (void)target_win; } void wl_client_send_key(WNativeWindow win, unsigned int keycode, unsigned int modifier_mask, int press) { - if (!wl_state.x_display) return; - XEvent ev; - memset(&ev, 0, sizeof(ev)); - ev.xkey.type = press ? KeyPress : KeyRelease; - ev.xkey.display = wl_state.x_display; - ev.xkey.window = (WNativeWindow)win; - ev.xkey.root = DefaultRootWindow(wl_state.x_display); - ev.xkey.subwindow = WM_NATIVE_NONE; - ev.xkey.same_screen = True; - ev.xkey.time = WM_CURRENT_TIME; - ev.xkey.keycode = keycode; - ev.xkey.state = modifier_mask; - XSendEvent(wl_state.x_display, (WNativeWindow)win, press ? False : True, - press ? WM_EVMASK_KEY_PRESS : WM_EVMASK_KEY_RELEASE, &ev); + (void)win; (void)keycode; (void)modifier_mask; (void)press; } #endif diff --git a/src/backend/wayland/wl_dock.c b/src/backend/wayland/wl_dock.c index 3bd595c..7b05d21 100644 --- a/src/backend/wayland/wl_dock.c +++ b/src/backend/wayland/wl_dock.c @@ -188,10 +188,10 @@ static uint32_t * wm_dock_load_icon(struct wl_toplevel_view *v, int *out_w, int *out_h) { *out_w = *out_h = 0; - if (!v->xw_surface || !wl_state.x_display) + if (!v->xw_surface || !NULL) return NULL; /* XDG native: no X11 icon path */ - RImage *ri = get_window_image_from_x11(wl_state.x_display, (WNativeWindow)v->id); + RImage *ri = get_window_image_from_x11(NULL, (WNativeWindow)v->id); if (!ri) return NULL; diff --git a/src/backend/wayland/wl_event.c b/src/backend/wayland/wl_event.c index 6ca97ce..4ff5877 100644 --- a/src/backend/wayland/wl_event.c +++ b/src/backend/wayland/wl_event.c @@ -16,8 +16,6 @@ void wl_event_flush(void) (!wl_state.session || wl_state.session->active)) { wlr_scene_output_commit(wl_state.scene_output, NULL); wl_display_flush_clients(wl_state.display); - struct wl_event_loop *loop = wl_display_get_event_loop(wl_state.display); - wl_event_loop_dispatch(loop, 0); } } void wl_event_flush_expose(void) {} diff --git a/src/backend/wayland/wl_extensions.c b/src/backend/wayland/wl_extensions.c index af85313..20acd6c 100644 --- a/src/backend/wayland/wl_extensions.c +++ b/src/backend/wayland/wl_extensions.c @@ -144,22 +144,20 @@ wl_catch_xwayland_error(Display *edpy, XErrorEvent *error) */ int _XDefaultError(Display *dpy, XErrorEvent *event) { - /* Only intercept errors on our XWayland Xlib connection. */ - if (wl_state.x_display != NULL) { - char buf[256] = "unknown"; - XGetErrorText(dpy, event->error_code, buf, sizeof(buf)); - wwarning("wl_backend: X error reached _XDefaultError: " - "%s (request %d, resource 0x%lx) — suppressing exit", - buf, (int)event->request_code, - (unsigned long)event->resourceid); + /* XWayland error interposition: suppress protocol errors that are + * non-fatal (BadMatch on SetInputFocus, BadWindow on stale IDs). */ + if (event->error_code == BadMatch && + event->request_code == X_SetInputFocus) return 0; - } - /* Pure X11 mode (no XWayland connection): behave as libX11 normally would. - * x11_backend installs x11_catch_error so _XDefaultError is only reached - * for truly unexpected errors — exiting here is the right thing. */ - fprintf(stderr, "X Error of failed request: error_code=%d request_code=%d\n", - (int)event->error_code, (int)event->request_code); - exit(1); + if (event->error_code == BadWindow) + return 0; + char buf[256] = "unknown"; + XGetErrorText(dpy, event->error_code, buf, sizeof(buf)); + wwarning("wl_backend: X error reached _XDefaultError: " + "%s (request %d, resource 0x%lx) — suppressing exit", + buf, (int)event->request_code, + (unsigned long)event->resourceid); + return 0; } int diff --git a/src/backend/wayland/wl_icon.c b/src/backend/wayland/wl_icon.c index 904aa6a..e294b20 100644 --- a/src/backend/wayland/wl_icon.c +++ b/src/backend/wayland/wl_icon.c @@ -410,59 +410,11 @@ wl_create_rimage_from_drawable(void *rcontext, WMDrawable drawable) if (!drawable) return NULL; - /* X11 window ID (XWayland): capture via XGetImage */ - if ((unsigned long)drawable < 0x10000000 && wl_state.x_display) { - Window xwin = (Window)(unsigned long)drawable; - WScreen *scr = wScreenWithNumber(0); - /* Ensure the window is mapped so we can capture its pixels. - * XSync forces a round-trip so the server processes the map - * and the app receives Expose before we capture. */ - XMapWindow(wl_state.x_display, xwin); - XSync(wl_state.x_display, False); - XWindowAttributes attr; - if (XGetWindowAttributes(wl_state.x_display, xwin, &attr) && - attr.width > 0 && attr.height > 0) { - XImage *ximg = XGetImage(wl_state.x_display, xwin, - 0, 0, attr.width, attr.height, AllPlanes, ZPixmap); - if (ximg) { - RImage *res = RCreateImageFromXImage(scr ? scr->rcontext : NULL, ximg, NULL); - XDestroyImage(ximg); - /* Apply XShape mask: only pixels within the shape - * bounding region are visible (dockapp convention). */ - if (res) { - int count = 0, ordering; - XRectangle *rects = XShapeGetRectangles(wl_state.x_display, - xwin, ShapeBounding, &count, &ordering); - if (rects && count > 0) { - int iw = res->width, ih = res->height; - int bpp = (res->format == RRGBAFormat) ? 4 : 3; - RImage *masked = RCreateImage(iw, ih, 1); - if (masked) { - /* Start fully transparent */ - memset(masked->data, 0, iw * ih * 4); - /* Copy pixels inside shape rects */ - for (int r = 0; r < count; r++) { - int rx = rects[r].x, ry = rects[r].y; - int rw = rects[r].width, rh = rects[r].height; - for (int row = ry; row < ry + rh && row < ih; row++) { - if (row < 0) continue; - for (int col = rx; col < rx + rw && col < iw; col++) { - if (col < 0) continue; - unsigned char *s = res->data + (row * iw + col) * bpp; - unsigned char *d = masked->data + (row * iw + col) * 4; - d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = 255; - } - } - } - RReleaseImage(res); - res = masked; - } - XFree(rects); - } - } - return res; - } - } + /* X11 window ID (XWayland): icon capture disabled — no persistent + * X connection. XWayland dockapp icons use wlr_xwayland_surface + * buffer capture instead. */ + if ((unsigned long)drawable < 0x10000000) { + return NULL; } pixman_image_t *pimg = NULL; diff --git a/src/backend/wayland/wl_screen.c b/src/backend/wayland/wl_screen.c index 517b905..cbe2e1d 100644 --- a/src/backend/wayland/wl_screen.c +++ b/src/backend/wayland/wl_screen.c @@ -118,14 +118,7 @@ wl_screen_open(WScreen *scr) * 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) { - int snum = DefaultScreen(wl_state.x_display); - scr->root_win = DefaultRootWindow(wl_state.x_display); - scr->depth = DefaultDepth(wl_state.x_display, snum); - scr->colormap = DefaultColormap(wl_state.x_display, snum); - } + /* X11 screen values not available — no persistent X connection. */ } void wl_screen_close(WScreen *scr) { (void)scr; } @@ -342,9 +335,6 @@ int wl_parse_color(WScreen *scr, const char *name, WMColorRGB *color_out) void wl_screen_alloc_gcs(WScreen *scr) { - XGCValues gcv; - int gcm; - static const char stipple_data[] = { 0x02, 0x01 }; /* Pre-initialise menu text colors to black so that setMenuTextColor / * setMenuDisabledColor in wReadDefaults can safely compare them before @@ -353,88 +343,16 @@ wl_screen_alloc_gcs(WScreen *scr) scr->mtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True); scr->dtext_color = WMCreateRGBColor(scr->wmscreen, 0, 0, 0, True); - /* Create X11 GCs on the XWayland connection. wmaker's rendering code - * (framewin, icon, menu) uses these regardless of the display backend. - * Mirrors x11_screen_alloc_gcs exactly; without these, CopyArea and - * XRender calls use GC/drawable 0 → BadDrawable → XWayland broken pipe. */ - if (!wl_state.x_display) - return; - - /* Mark the wrlib drawable as override_redirect so XWayland's XWM does not - * manage it as a client window. XWM reads override_redirect from the - * CreateNotify event (already gone), but any subsequent ConfigureNotify - * updates the flag on the wlr_xwayland_surface it created. Combined with - * the explicit window-ID filter in handle_xwayland_new_surface, this - * prevents BadDrawable/RenderBadPicture cascade and the resulting broken - * pipe from the nested Wayland backend. */ -#if USE_XWAYLAND - if (scr->w_win) { - wl_state.rcontext_xwin = (WNativeWindow)scr->w_win; - XSetWindowAttributes or_attr; - or_attr.override_redirect = True; - XChangeWindowAttributes(wl_state.x_display, (WNativeWindow)scr->w_win, CWOverrideRedirect, &or_attr); - XFlush(wl_state.x_display); - } -#endif - - scr->stipple_bitmap = XCreateBitmapFromData(wl_state.x_display, (WNativeWindow)scr->w_win, - stipple_data, 2, 2); - - gcv.stipple = scr->stipple_bitmap; - gcv.foreground = scr->white_pixel; - gcv.fill_style = FillStippled; - gcv.graphics_exposures = False; - gcm = GCForeground | GCStipple | GCFillStyle | GCGraphicsExposures; - scr->stipple_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->w_win, gcm, &gcv); - - gcv.function = GXcopy; - gcv.foreground = scr->white_pixel; - gcv.background = scr->black_pixel; - gcv.line_width = 1; - gcv.line_style = LineDoubleDash; - gcv.fill_style = FillSolid; - gcv.dash_offset = 0; - gcv.dashes = 4; - gcv.graphics_exposures = False; - gcm = GCFunction | GCGraphicsExposures | GCForeground | GCBackground - | GCLineWidth | GCLineStyle | GCFillStyle | GCDashOffset | GCDashList; - scr->icon_select_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->w_win, gcm, &gcv); - - gcv.function = GXxor; - gcv.plane_mask = AllPlanes; - gcv.foreground = scr->white_pixel; - if (gcv.foreground == 0) - gcv.foreground = 1; - gcv.line_width = DEF_FRAME_THICKNESS; - gcv.subwindow_mode = IncludeInferiors; - gcv.graphics_exposures = False; - scr->frame_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->root_win, - GCForeground | GCGraphicsExposures | GCFunction - | GCSubwindowMode | GCLineWidth | GCPlaneMask, &gcv); - - gcv.foreground = scr->white_pixel; - if (gcv.foreground == 0) - gcv.foreground = 17; - gcv.function = GXxor; - gcv.subwindow_mode = IncludeInferiors; - gcv.line_width = 1; - gcv.cap_style = CapRound; - gcv.graphics_exposures = False; - gcm = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth - | GCCapStyle | GCGraphicsExposures; - scr->line_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->root_win, gcm, &gcv); - scr->line_pixel = gcv.foreground; - - gcv.foreground = scr->white_pixel; - gcv.background = scr->black_pixel; - gcv.graphics_exposures = False; - scr->copy_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->w_win, - GCForeground | GCBackground | GCGraphicsExposures, &gcv); - - gcv.graphics_exposures = False; - scr->draw_gc = XCreateGC(wl_state.x_display, (WNativeWindow)scr->w_win, GCGraphicsExposures, &gcv); - - scr->mono_gc = XCreateGC(wl_state.x_display, scr->stipple_bitmap, GCGraphicsExposures, &gcv); + /* No persistent X connection — GCs stubbed to 0. */ + scr->stipple_bitmap = None; + scr->stipple_gc = 0; + scr->icon_select_gc = 0; + scr->frame_gc = 0; + scr->line_gc = 0; + scr->line_pixel = scr->white_pixel ? scr->white_pixel : 1; + scr->copy_gc = 0; + scr->draw_gc = 0; + scr->mono_gc = 0; } #else void wl_screen_alloc_gcs(WScreen *scr) { (void)scr; } @@ -449,27 +367,7 @@ wl_screen_create_internals(WScreen *scr) if (fp) { fprintf(fp, "wl_screen_create_internals: scr=%p\n", (void*)scr); fclose(fp); } } #if USE_XWAYLAND - /* Create off-screen InputOnly X window as focus sink */ - if (wl_state.x_display) { - XSetWindowAttributes attribs; - int vmask = CWEventMask | CWOverrideRedirect; - attribs.event_mask = WM_EVMASK_KEY_PRESS | WM_EVMASK_FOCUS_CHANGE; - attribs.override_redirect = True; - WNativeWindow xwin = XCreateWindow(wl_state.x_display, DefaultRootWindow(wl_state.x_display), - -10, -10, 4, 4, 0, - 0, InputOnly, CopyFromParent, vmask, &attribs); - if (xwin) { - XSelectInput(wl_state.x_display, xwin, WM_EVMASK_KEY_PRESS | WM_EVMASK_KEY_RELEASE); - XMapWindow(wl_state.x_display, xwin); - /* Do NOT call XSetInputFocus here: the window may not be viewable - * yet in XWayland (map is async), triggering BadMatch that perturbs - * XWayland state and causes a Wayland broken-pipe cascade. - * wl_focus_set() will clear Wayland seat focus via - * wlr_seat_keyboard_clear_focus when this window is the target. */ - wl_state.no_focus_xwin = xwin; - scr->no_focus_win = (WNativeWindow)xwin; - } - } + /* X11 focus-sink window removed — no persistent X connection. */ #endif wPreferences.auto_focus = 1; @@ -479,17 +377,7 @@ wl_screen_create_internals(WScreen *scr) wSetModifierMasks(WM_MOD_MOD1, WM_MOD_MOD1, WM_MOD_MOD4, 0); #if USE_XWAYLAND - /* XWayland bridge: create X11 helper window for EWMH */ - if (wl_state.x_display) { - XSetWindowAttributes iattr; - iattr.override_redirect = True; - WNativeWindow xwin = XCreateWindow(wl_state.x_display, DefaultRootWindow(wl_state.x_display), - -2, -2, 1, 1, 0, - 0, InputOutput, CopyFromParent, - CWOverrideRedirect, &iattr); - if (xwin) - scr->info_window = (WNativeWindow)xwin; - } + /* X11 EWMH helper window removed — no persistent X connection. */ #endif /* Create dock_shadow: scene_buffer used as ghost overlay during icon drag */ diff --git a/src/backend/wayland/wl_stubs.c b/src/backend/wayland/wl_stubs.c index c8b4469..61a15d1 100644 --- a/src/backend/wayland/wl_stubs.c +++ b/src/backend/wayland/wl_stubs.c @@ -61,7 +61,7 @@ typedef struct _XDisplay Display; __attribute__((weak)) void wl_xdisplay_flush(void) {} __attribute__((weak)) void wl_xdisplay_sync(int d) { (void)d; } __attribute__((weak)) unsigned long wl_xdisplay_intern_atom(const char *n) { (void)n; return 0; } -__attribute__((weak)) Display *wl_get_x_display(void) { return NULL; } + __attribute__((weak)) RImage *wl_xwayland_capture_snapshot(WScreen *s, struct wlr_xwayland_surface *xw) { (void)s; (void)xw; return NULL; } __attribute__((weak)) float wl_xwayland_get_window_opacity(WNativeWindow cw) { (void)cw; return 1.0f; } __attribute__((weak)) void wl_register_error_handlers(void) {} diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index e423257..4bb44ef 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -202,8 +202,7 @@ struct wl_compositor_state { 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 */ + pthread_t startup_pump_thread; /* pumps Wayland event loop during StartUp() */ volatile int startup_pump_running; /* 1 while pump thread is active */ volatile int terminate; /* set to exit main loop */ @@ -591,7 +590,7 @@ void wm_shm_surface_destroy(struct wm_shm_surface *s); WMScreen *wl_get_wmscreen(void); void wl_wings_dispatch(int timeout_ms, void *ud); #if USE_XWAYLAND -Display *wl_get_x_display(void); + #endif unsigned long wl_xdisplay_intern_atom(const char *name); void wl_xdisplay_flush(void); diff --git a/src/backend/wayland/wl_window.c b/src/backend/wayland/wl_window.c index e6e6294..cd02486 100644 --- a/src/backend/wayland/wl_window.c +++ b/src/backend/wayland/wl_window.c @@ -28,9 +28,9 @@ struct wl_frame_buf *frame_buf_find(WNativeWindow id); extern void *W_RegisterBacking(unsigned long id, void *img); extern void W_UnregisterBacking(unsigned long id); struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id); -__attribute__((weak)) pixman_image_t *get_window_image_from_x11(Display *x_display, WNativeWindow win) +__attribute__((weak)) pixman_image_t *get_window_image_from_x11(void *dpy, WNativeWindow win) { - (void)x_display; (void)win; return NULL; + (void)dpy; (void)win; return NULL; } @@ -72,8 +72,8 @@ wl_window_get_icon_image(WNativeWindow win) /* For XWayland clients, parse _NET_WM_ICON via the Xwayland display. * For native Wayland clients there is no equivalent (the upcoming * xdg-toplevel-icon protocol is not yet wired). */ - if (!wl_state.x_display) return NULL; - return get_window_image_from_x11(wl_state.x_display, (WNativeWindow)win); + if (!NULL) return NULL; + return get_window_image_from_x11(NULL, (WNativeWindow)win); } void wl_window_map(WNativeWindow win) @@ -81,22 +81,7 @@ wl_window_map(WNativeWindow win) struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, true); - /* Ensure XWayland X11 window is mapped so it composites children - * (needed for dockapps whose content is in a child icon_window). */ - /* For XWayland dockapps: map the X11 window and reparent the - * icon_window child into it so XWayland composites the content. */ - if (v->xw_surface && wl_state.x_display && v->xw_surface->hints && - (v->xw_surface->hints->flags & XCB_ICCCM_WM_HINT_STATE) && - v->xw_surface->hints->initial_state == 0 /* WithdrawnState */) { - Window xwin = (Window)v->xw_surface->window_id; - XMapWindow(wl_state.x_display, xwin); - if (v->xw_surface->hints->flags & XCB_ICCCM_WM_HINT_ICON_WINDOW) { - Window icon_xwin = (Window)v->xw_surface->hints->icon_window; - XReparentWindow(wl_state.x_display, icon_xwin, xwin, 0, 0); - XMapWindow(wl_state.x_display, icon_xwin); - } - XFlush(wl_state.x_display); - } + /* XWayland dockapp mapping removed — no persistent X connection. */ return; } if (v) @@ -714,7 +699,7 @@ int wl_window_get_command(WNativeWindow win, char ***argv_out, int *argc_out) * is sufficient for session restore via PATH lookup or a desktop-file * Exec= line. * - * XWayland clients: prefer WM_COMMAND (read from wl_state.x_display) + * XWayland clients: prefer WM_COMMAND (read from NULL) * and fall back to xw_surface->instance / class. */ *argv_out = NULL; *argc_out = 0; @@ -725,20 +710,7 @@ int wl_window_get_command(WNativeWindow win, char ***argv_out, int *argc_out) const char *cmd = NULL; if (v->xw_surface) { #if USE_XWAYLAND - /* XWayland: try WM_COMMAND first via the X server. */ - if (wl_state.x_display && v->xw_surface->window_id) { - char **cmd_argv = NULL; - int cmd_argc = 0; - if (XGetCommand(wl_state.x_display, v->xw_surface->window_id, - &cmd_argv, &cmd_argc) && cmd_argc > 0 && cmd_argv) { - *argv_out = wmalloc(sizeof(char *) * cmd_argc); - for (int i = 0; i < cmd_argc; i++) - (*argv_out)[i] = wstrdup(cmd_argv[i]); - *argc_out = cmd_argc; - XFreeStringList(cmd_argv); - return 1; - } - } + /* WM_COMMAND via X server removed — no persistent X connection. */ #endif cmd = v->xw_surface->instance ? v->xw_surface->instance : v->xw_surface->class; diff --git a/src/backend/wayland/wl_xdisplay.c b/src/backend/wayland/wl_xdisplay.c index 27163d5..5c694fd 100644 --- a/src/backend/wayland/wl_xdisplay.c +++ b/src/backend/wayland/wl_xdisplay.c @@ -1,48 +1,14 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include #include "wl_types.h" - -/* Public accessor for the XWayland Display* — used by screen.c to create - * an RContext for texture rendering without exposing wl_state globally. */ -Display *wl_get_x_display(void) -{ - return wl_state.x_display; -} - -#if USE_XWAYLAND -unsigned long -wl_xdisplay_intern_atom(const char *name) -{ - if (wl_state.x_display) - return (unsigned long)XInternAtom(wl_state.x_display, name, False); - return 0; -} - -void -wl_xdisplay_flush(void) -{ - if (wl_state.x_display) - XFlush(wl_state.x_display); -} - -void -wl_xdisplay_sync(int discard) -{ - if (wl_state.x_display) - XSync(wl_state.x_display, discard ? True : False); -} -#else unsigned long wl_xdisplay_intern_atom(const char *name) { (void)name; return 0; } void wl_xdisplay_flush(void) {} void wl_xdisplay_sync(int discard) { (void)discard; } -#endif RContext *wl_create_rcontext(int screen_number, RContextAttributes *attribs) { (void)screen_number; return RCreateContextWayland(1920, 1080, attribs); } - diff --git a/src/backend/wayland/wl_xwayland.c b/src/backend/wayland/wl_xwayland.c index ee29b4d..7ec4284 100644 --- a/src/backend/wayland/wl_xwayland.c +++ b/src/backend/wayland/wl_xwayland.c @@ -8,10 +8,9 @@ #include #include #include +#include #include #include -#include -#include #include #include "../../screen.h" #include "../../application.h" @@ -324,18 +323,18 @@ handle_xwayland_ready(struct wl_listener *listener, void *data) wwarning("wl_backend: XWayland ready on %s — opening WM Xlib connection", wl_state.xwayland->display_name); - 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); - - /* Set the default root cursor to left_ptr so XWayland clients that - * don't set their own cursor show an arrow instead of the X cursor. */ - if (wl_state.x_display) { - Cursor c = XCreateFontCursor(wl_state.x_display, XC_left_ptr); - XDefineCursor(wl_state.x_display, DefaultRootWindow(wl_state.x_display), c); - XFreeCursor(wl_state.x_display, c); - XFlush(wl_state.x_display); + /* Set root cursor via wlroots XWayland API (no persistent X connection). + * wlr_xwayland_set_cursor uses the internal XWM xcb connection. */ + if (wl_state.xcursor_mgr && wl_state.xwayland) { + struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( + wl_state.xcursor_mgr, "left_ptr", 1); + if (xcursor && xcursor->image_count > 0) { + struct wlr_xcursor_image *img = xcursor->images[0]; + wlr_xwayland_set_cursor(wl_state.xwayland, + img->buffer, img->width * 4, + img->width, img->height, + img->hotspot_x, img->hotspot_y); + } } wl_state.xwayland_ready = true; @@ -454,42 +453,15 @@ handle_xwayland_new_surface(struct wl_listener *listener, void *data) RImage * wl_xwayland_capture_snapshot(WScreen *scr, struct wlr_xwayland_surface *xw) { - if (!wl_state.x_display || !xw) - return NULL; - Window xid = (Window)xw->window_id; - XWindowAttributes attr; - if (!XGetWindowAttributes(wl_state.x_display, xid, &attr)) - return NULL; - XImage *img = XGetImage(wl_state.x_display, xid, - 0, 0, attr.width, attr.height, - AllPlanes, ZPixmap); - if (!img) return NULL; - RImage *res = RCreateImageFromXImage(scr->rcontext, img, NULL); - XDestroyImage(img); - return res; + (void)scr; (void)xw; + return NULL; } float wl_xwayland_get_window_opacity(WNativeWindow client_win) { - if (!wl_state.x_display) - return 1.0f; - WMNativeAtom type; - int format; - unsigned long items, rest; - unsigned char *prop = NULL; - unsigned long val = 0xFFFFFFFF; - WMNativeAtom opacity_atom = XInternAtom(wl_state.x_display, - "_NET_WM_WINDOW_OPACITY", False); - if (XGetWindowProperty(wl_state.x_display, (WNativeWindow)client_win, - opacity_atom, 0L, 1L, False, AnyPropertyType, - &type, &format, &items, &rest, &prop) == Success - && format == 32 && items == 1 && prop) { - val = *(unsigned long *)prop; - } - if (prop) - XFree(prop); - return (float)val / (float)0xFFFFFFFF; + (void)client_win; + return 1.0f; } #else RImage *wl_xwayland_capture_snapshot(WScreen *scr, struct wlr_xwayland_surface *xw) @@ -498,53 +470,10 @@ float wl_xwayland_get_window_opacity(WNativeWindow client_win) { (void)client_win; return 1.0f; } #endif -/* Called by the Wayland event loop whenever the X11 connection fd is readable. */ +/* X11 event handler removed — no persistent X connection. */ 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); - /* Guard: WINGs screen is not initialised until StartUp() completes. */ - if (w_global.screen_count > 0) { - /* Translate XEvent → WMEvent at the XWayland boundary. */ - WMEvent wev; - memset(&wev, 0, sizeof(wev)); - wev._x11_raw = &event; - wev.window = (WNativeWindow)event.xany.window; - switch (event.type) { - case KeyPress: wev.type = WME_KEY_PRESS; break; - case KeyRelease: wev.type = WME_KEY_RELEASE; break; - case ButtonPress: wev.type = WME_BUTTON_PRESS; break; - case ButtonRelease: wev.type = WME_BUTTON_RELEASE; break; - case MotionNotify: wev.type = WME_MOTION; break; - case EnterNotify: wev.type = WME_ENTER; break; - case LeaveNotify: wev.type = WME_LEAVE; break; - case Expose: wev.type = WME_EXPOSE; break; - case ConfigureNotify: wev.type = WME_CONFIGURE; break; - case MapNotify: wev.type = WME_MAP; break; - case UnmapNotify: wev.type = WME_UNMAP; break; - case DestroyNotify: wev.type = WME_DESTROY; break; - case FocusIn: wev.type = WME_FOCUS_IN; break; - case FocusOut: wev.type = WME_FOCUS_OUT; break; - case PropertyNotify: wev.type = WME_PROPERTY; break; - case ClientMessage: wev.type = WME_CLIENT_MESSAGE; break; - case SelectionNotify: wev.type = WME_SELECTION; break; - case SelectionRequest: wev.type = WME_SELECTION_REQUEST; break; - case SelectionClear: wev.type = WME_SELECTION_CLEAR; break; - case MappingNotify: wev.type = WME_MAPPING; break; - case ColormapNotify: wev.type = WME_COLORMAP; break; - case VisibilityNotify: wev.type = WME_VISIBILITY; break; - case ReparentNotify: wev.type = WME_REPARENT; break; - case ConfigureRequest: wev.type = WME_CONFIGURE_REQUEST; break; - case MapRequest: wev.type = WME_MAP_REQUEST; break; - default: wev.type = WME_NONE; break; - } - WMHandleEvent(&wev); - } - } return 0; }