diff --git a/src/Makefile.am b/src/Makefile.am index 7823c1c..b7f8c2c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,7 +171,8 @@ wmaker_SOURCES += \ backend/wayland/wl_signalfd.c \ backend/wayland/wl_xkb.c \ backend/wayland/wl_badge.c \ - backend/wayland/wl_fakeleader.c + backend/wayland/wl_fakeleader.c \ + backend/wayland/wl_extensions.c endif if USE_NESTED_FUNC diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 1687508..1380927 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -316,7 +316,7 @@ frame_timer_cb(void *data) ((struct wl_toplevel_view *)((char *)(ptr) - offsetof(struct wl_toplevel_view, field))) /* X error handler — defined near the vtable at the bottom of this file. */ -static int wl_catch_xwayland_error(Display *edpy, XErrorEvent *error); +int wl_catch_xwayland_error(Display *edpy, XErrorEvent *error); /* Phase 43: forward declaration — defined in the focus section below. */ static void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp); @@ -3111,7 +3111,7 @@ wl_display_default_screen(void) } -static void +void wl_event_loop_run(void) { if (!wl_state.display) @@ -3303,7 +3303,7 @@ wl_event_loop_run(void) } } -static void +void wl_event_loop_terminate(void) { /* Write a byte to the self-pipe. write(2) is async-signal-safe, so this @@ -6065,166 +6065,6 @@ static void wl_monitors_select_events(WScreen *scr) { (void)scr; } -/* ------------------------------------------------------------------ */ -/* Extensions / cursors / error handlers */ -/* ------------------------------------------------------------------ */ - -static void wl_extensions_init(void) {} -static void wl_cursors_init(void) {} -static WNativeWindow wl_cursor_create_named(const char *name) { (void)name; return WNATIVE_NONE; } -static void -wl_shape_combine_mask(WNativeWindow win, WNativeWindow mask) -{ - struct wl_frame_buf *fb = frame_buf_find(win); - if (!fb) return; - - pixman_image_t *mask_img = (pixman_image_t *)mask; - if (!mask_img) { - /* No mask: clear opaque region (accept all input) */ - wlr_scene_buffer_set_opaque_region(fb->scene_buf, NULL); - return; - } - - /* Build a pixman region from the mask image (non-transparent pixels) */ - int w = pixman_image_get_width(mask_img); - int h = pixman_image_get_height(mask_img); - uint32_t *data = (uint32_t *)pixman_image_get_data(mask_img); - int stride = pixman_image_get_stride(mask_img) / 4; - - pixman_region32_t region; - pixman_region32_init(®ion); - - for (int y = 0; y < h; y++) { - int run_start = -1; - for (int x = 0; x < w; x++) { - uint32_t pixel = data[y * stride + x]; - uint8_t alpha = (pixel >> 24) & 0xFF; - if (alpha > 0) { - if (run_start < 0) run_start = x; - } else { - if (run_start >= 0) { - pixman_region32_union_rect(®ion, ®ion, run_start, y, x - run_start, 1); - run_start = -1; - } - } - } - if (run_start >= 0) { - pixman_region32_union_rect(®ion, ®ion, run_start, y, w - run_start, 1); - } - } - - /* Set opaque region for the scene buffer */ - wlr_scene_buffer_set_opaque_region(fb->scene_buf, ®ion); - - pixman_region32_fini(®ion); -} -static int -wl_catch_xwayland_error(Display *edpy, XErrorEvent *error) -{ - (void)edpy; - /* BadMatch on X_SetInputFocus is benign: the target window may not be - * viewable yet (e.g. during XWayland startup or between map/unmap). - * Silence it so it does not reach Xlib's default handler. */ - if (error->error_code == BadMatch && - error->request_code == X_SetInputFocus) - return 0; - /* BadWindow is also common during window teardown; ignore silently. */ - if (error->error_code == BadWindow) - return 0; - /* Everything else: print via wwarning and continue. */ - char buf[256]; - XGetErrorText(edpy, error->error_code, buf, sizeof(buf)); - wwarning("wl_backend: X error from XWayland: %s (request %d, resource 0x%lx)", - buf, (int)error->request_code, (unsigned long)error->resourceid); - return 0; -} - -/* _XDefaultError interposition ----------------------------------------------- - * libX11's _XDefaultError (called when no user error handler is installed, or - * when the user handler IS _XDefaultError itself) calls exit(1) after printing. - * In the XWayland bridge the error handler can be temporarily displaced by - * WINGs/wrlib save-restore code (selection.c, dragcommon.c, texture.c) between - * the time a request is sent and the time its BadMatch reply is received. - * - * Because libX11 calls _XDefaultError via PLT (confirmed with objdump — it is - * a GLOBAL DEFAULT symbol with no version tag), our definition in the wmaker - * binary takes precedence over the library version via ELF symbol interposition. - * This is the last-resort safety net: even if _XErrorFunction is reset to - * _XDefaultError, we never exit on a recoverable X protocol error while the - * XWayland connection is open. - */ -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); - 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); -} - -static int -wl_catch_xwayland_io_error(Display *edpy) -{ - (void)edpy; - /* XIO error = the X server closed the connection (e.g. XWayland crashed or - * its XWM connection died and it shut down). The default handler calls - * exit(1), which then makes XWayland print "(EE) failed to read Wayland - * events: Broken pipe" — our exit closes the Wayland socket that XWayland's - * client side reads. Intercept it so we can shut down gracefully. */ - wwarning("wl_backend: X IO error — XWayland connection lost, terminating"); - /* Trigger graceful shutdown via the event loop. */ - wl_event_loop_terminate(); - /* XIO error handlers must not return; call exit directly. */ - exit(0); -} - -static void -wl_register_error_handlers(void) -{ - /* Install a permissive X error handler for the XWayland connection. - * The _XDefaultError interposition above provides a last-resort safety - * net if any code temporarily displaces this handler. */ - XSetErrorHandler(wl_catch_xwayland_error); - /* Install an XIO error handler so we get a proper log message instead of - * the silent exit(1) from Xlib's default handler when XWayland disconnects. */ - XSetIOErrorHandler(wl_catch_xwayland_io_error); -} - -static void -wl_texture_alloc_color(WScreen *scr, WMColorRGB *color) -{ - (void) scr; (void) color; -} - -static void -wl_texture_free_colors(WScreen *scr, unsigned long *pixels, int count) -{ - (void) scr; (void) pixels; (void) count; -} - -static GC -wl_texture_create_gc(WScreen *scr, unsigned long pixel) -{ - (void) scr; (void) pixel; - return NULL; -} - -static void -wl_texture_free_gc(GC gc) -{ - (void) gc; -} /* ------------------------------------------------------------------ */ /* Phase 80: no-op stubs for X11-only operations */ diff --git a/src/backend/wayland/wl_extensions.c b/src/backend/wayland/wl_extensions.c new file mode 100644 index 0000000..889a740 --- /dev/null +++ b/src/backend/wayland/wl_extensions.c @@ -0,0 +1,174 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../screen.h" +#include "wl_types.h" + +/* ------------------------------------------------------------------ */ +/* Extensions / cursors / error handlers */ +/* ------------------------------------------------------------------ */ + +void wl_extensions_init(void) {} +void wl_cursors_init(void) {} +WNativeWindow wl_cursor_create_named(const char *name) { (void)name; return WNATIVE_NONE; } +void +wl_shape_combine_mask(WNativeWindow win, WNativeWindow mask) +{ + struct wl_frame_buf *fb = frame_buf_find(win); + if (!fb) return; + + pixman_image_t *mask_img = (pixman_image_t *)mask; + if (!mask_img) { + /* No mask: clear opaque region (accept all input) */ + wlr_scene_buffer_set_opaque_region(fb->scene_buf, NULL); + return; + } + + /* Build a pixman region from the mask image (non-transparent pixels) */ + int w = pixman_image_get_width(mask_img); + int h = pixman_image_get_height(mask_img); + uint32_t *data = (uint32_t *)pixman_image_get_data(mask_img); + int stride = pixman_image_get_stride(mask_img) / 4; + + pixman_region32_t region; + pixman_region32_init(®ion); + + for (int y = 0; y < h; y++) { + int run_start = -1; + for (int x = 0; x < w; x++) { + uint32_t pixel = data[y * stride + x]; + uint8_t alpha = (pixel >> 24) & 0xFF; + if (alpha > 0) { + if (run_start < 0) run_start = x; + } else { + if (run_start >= 0) { + pixman_region32_union_rect(®ion, ®ion, run_start, y, x - run_start, 1); + run_start = -1; + } + } + } + if (run_start >= 0) { + pixman_region32_union_rect(®ion, ®ion, run_start, y, w - run_start, 1); + } + } + + /* Set opaque region for the scene buffer */ + wlr_scene_buffer_set_opaque_region(fb->scene_buf, ®ion); + + pixman_region32_fini(®ion); +} +int +wl_catch_xwayland_error(Display *edpy, XErrorEvent *error) +{ + (void)edpy; + /* BadMatch on X_SetInputFocus is benign: the target window may not be + * viewable yet (e.g. during XWayland startup or between map/unmap). + * Silence it so it does not reach Xlib's default handler. */ + if (error->error_code == BadMatch && + error->request_code == X_SetInputFocus) + return 0; + /* BadWindow is also common during window teardown; ignore silently. */ + if (error->error_code == BadWindow) + return 0; + /* Everything else: print via wwarning and continue. */ + char buf[256]; + XGetErrorText(edpy, error->error_code, buf, sizeof(buf)); + wwarning("wl_backend: X error from XWayland: %s (request %d, resource 0x%lx)", + buf, (int)error->request_code, (unsigned long)error->resourceid); + return 0; +} + +/* _XDefaultError interposition ----------------------------------------------- + * libX11's _XDefaultError (called when no user error handler is installed, or + * when the user handler IS _XDefaultError itself) calls exit(1) after printing. + * In the XWayland bridge the error handler can be temporarily displaced by + * WINGs/wrlib save-restore code (selection.c, dragcommon.c, texture.c) between + * the time a request is sent and the time its BadMatch reply is received. + * + * Because libX11 calls _XDefaultError via PLT (confirmed with objdump — it is + * a GLOBAL DEFAULT symbol with no version tag), our definition in the wmaker + * binary takes precedence over the library version via ELF symbol interposition. + * This is the last-resort safety net: even if _XErrorFunction is reset to + * _XDefaultError, we never exit on a recoverable X protocol error while the + * XWayland connection is open. + */ +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); + 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); +} + +int +wl_catch_xwayland_io_error(Display *edpy) +{ + (void)edpy; + /* XIO error = the X server closed the connection (e.g. XWayland crashed or + * its XWM connection died and it shut down). The default handler calls + * exit(1), which then makes XWayland print "(EE) failed to read Wayland + * events: Broken pipe" — our exit closes the Wayland socket that XWayland's + * client side reads. Intercept it so we can shut down gracefully. */ + wwarning("wl_backend: X IO error — XWayland connection lost, terminating"); + /* Trigger graceful shutdown via the event loop. */ + wl_event_loop_terminate(); + /* XIO error handlers must not return; call exit directly. */ + exit(0); +} + +void +wl_register_error_handlers(void) +{ + /* Install a permissive X error handler for the XWayland connection. + * The _XDefaultError interposition above provides a last-resort safety + * net if any code temporarily displaces this handler. */ + XSetErrorHandler(wl_catch_xwayland_error); + /* Install an XIO error handler so we get a proper log message instead of + * the silent exit(1) from Xlib's default handler when XWayland disconnects. */ + XSetIOErrorHandler(wl_catch_xwayland_io_error); +} + +void +wl_texture_alloc_color(WScreen *scr, WMColorRGB *color) +{ + (void) scr; (void) color; +} + +void +wl_texture_free_colors(WScreen *scr, unsigned long *pixels, int count) +{ + (void) scr; (void) pixels; (void) count; +} + +GC +wl_texture_create_gc(WScreen *scr, unsigned long pixel) +{ + (void) scr; (void) pixel; + return NULL; +} + +void +wl_texture_free_gc(GC gc) +{ + (void) gc; +} diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index d276629..1db5434 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -518,3 +518,22 @@ void wl_workspace_badge_update(WScreen *scr, WNativeWindow badge_win, RImage *im /* Fake leader declarations (extracted to wl_fakeleader.c) */ WNativeWindow wl_fake_leader_create(WScreen *scr); void wl_fake_leader_destroy(WNativeWindow win); + +/* Extensions/cursors/error handlers (extracted to wl_extensions.c) */ +void wl_extensions_init(void); +void wl_cursors_init(void); +WNativeWindow wl_cursor_create_named(const char *name); +void wl_shape_combine_mask(WNativeWindow win, WNativeWindow mask); +int wl_catch_xwayland_error(Display *edpy, XErrorEvent *error); +int wl_catch_xwayland_io_error(Display *edpy); +void wl_register_error_handlers(void); +void wl_texture_alloc_color(WScreen *scr, WMColorRGB *color); +void wl_texture_free_colors(WScreen *scr, unsigned long *pixels, int count); +GC wl_texture_create_gc(WScreen *scr, unsigned long pixel); +void wl_texture_free_gc(GC gc); + +/* Signalfd module (extracted to wl_signalfd.c) */ +int wl_signalfd_handler(int fd, uint32_t mask, void *data); +void wl_handle_inotify_events(void); +void wl_event_loop_run(void); +void wl_event_loop_terminate(void);