decompose wl_backend.c: extract wl_extensions.c

This commit is contained in:
Window Maker 2026-06-02 11:49:35 +02:00
parent c60c20bf49
commit a9a3f0ff1d
4 changed files with 198 additions and 164 deletions

View File

@ -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

View File

@ -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(&region);
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(&region, &region, run_start, y, x - run_start, 1);
run_start = -1;
}
}
}
if (run_start >= 0) {
pixman_region32_union_rect(&region, &region, run_start, y, w - run_start, 1);
}
}
/* Set opaque region for the scene buffer */
wlr_scene_buffer_set_opaque_region(fb->scene_buf, &region);
pixman_region32_fini(&region);
}
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 */

View File

@ -0,0 +1,174 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
#include <pixman.h>
#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(&region);
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(&region, &region, run_start, y, x - run_start, 1);
run_start = -1;
}
}
}
if (run_start >= 0) {
pixman_region32_union_rect(&region, &region, run_start, y, w - run_start, 1);
}
}
/* Set opaque region for the scene buffer */
wlr_scene_buffer_set_opaque_region(fb->scene_buf, &region);
pixman_region32_fini(&region);
}
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;
}

View File

@ -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);