wayland: remove persistent X display connection
wlroots manages XWayland internally via its own xcb connection. Our second Xlib connection (XOpenDisplay) interfered with XWayland's event routing, preventing WM_DELETE_WINDOW from reaching X clients and causing slow XWayland startup. Removed: - Persistent wl_state.x_display / x11_event_source - X11 event pump (wl_handle_x11_events) - Xlib-based icon capture, GC creation, key/message forwarding - XSetErrorHandler on our connection - Reentrant wl_event_loop_dispatch in wl_event_flush (caused UAF) Root cursor now set via wlr_xwayland_set_cursor (wlroots API). XWayland close button now works correctly.
This commit is contained in:
parent
e3c19bd103
commit
0e0847b48b
|
|
@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 460 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 502 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 431 KiB |
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
ASAN_OPTIONS=detect_leaks=0 ./src/.libs/wmaker >asan.log
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
|
|
@ -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)");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,48 +1,14 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <X11/Xlib.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@
|
|||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/xwayland.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <stddef.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue