diff options
| author | Adam Malczewski <[email protected]> | 2026-06-29 15:21:47 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-29 15:21:47 +0900 |
| commit | b66f804cf01a639378bbbf0c5a03ab527853fda6 (patch) | |
| tree | 4f7186c549671059264a0f0da3f2512f942d714f | |
| parent | 53ef70aff331c9f95b744809c125af55a230ebfa (diff) | |
| download | study-player-b66f804cf01a639378bbbf0c5a03ab527853fda6.tar.gz study-player-b66f804cf01a639378bbbf0c5a03ab527853fda6.zip | |
fix: reliable Wayland drag-and-drop (root cause: GLFW 3.4 wl_data_offer NULL listener)
The rewrite builds raylib Wayland-only (X11/GLX segfaults on WSLg), but
raylib 6.0 vendors GLFW 3.4 (release) which crashes the moment a file is
dragged over the window on Wayland (glfw/glfw#2835, #2562):
struct wl_data_offer_listener dataOfferListener = { dataOfferHandleOffer };
// source_actions (opcode 1) + action (opcode 2) handlers are NULL
wl_data_offer is v3; modern compositors (labwc/GNOME/KDE) emit
source_actions/action during a drag -> libwayland-client wl_abort()s on
the NULL listener -> app dies on drag-enter, before any drop registers.
This is why drag-and-drop is 'unreliable' in the rewrite but reliable in
the original 'dev' app, which uses the X11/Xdnd backend (-D_GLFW_X11) — a
separate code path with none of these bugs.
Fix: backport the upstream GLFW-master fix as a committed patch applied
idempotently in build.zig before (marker = dataOfferHandleAction;
forces a lib rebuild when newly applied). Adds no-op source_actions/action
handlers so the events are safely consumed (GLFW only needs the
mime-types to accept a drop), plus guards two NULL derefs in the same
data-device path (dataDeviceHandleEnter / dataDeviceHandleDrop).
This keeps the Wayland-only build (no WSLg GLX regression) and fixes the
crash on both WSLg and a real Wayland desktop (cyberdeck/labwc). Drop the
patch once raylib vendors a GLFW release containing the upstream fix.
Verified: build green; desktop binary launches, loads an mp3 from argv
(132300 frames), enters the loop with no segfault. Interactive drag-drop
itself still needs user testing (it's a user action; the WSLg
non-interactive shell stalls the Wayland window before render).
| -rw-r--r-- | build.zig | 13 | ||||
| -rw-r--r-- | patches/glfw-wayland-dnd-crash.patch | 80 |
2 files changed, 93 insertions, 0 deletions
@@ -22,9 +22,22 @@ pub fn build(b: *std.Build) void { // Desktop raylib -> build/desktop/libraylib.a. raylib shares .o files in src/ // across platforms, so `make clean` first to avoid picking up wasm objects // from a prior web build. Guarded on the desktop lib so it only builds once. + // + // Before building, apply the GLFW Wayland drag-and-drop crash fix (see + // patches/glfw-wayland-dnd-crash.patch). GLFW 3.4 (vendored in raylib 6.0) + // leaves the wl_data_offer source_actions/action listener handlers NULL, + // so libwayland wl_abort()s when a compositor sends them during a drag -> + // the app crashes the moment a file is dragged over the window. Idempotent: + // if the marker (dataOfferHandleAction) is already in the file, skip; else + // apply the patch and delete any stale lib so `make` actually rebuilds. const raylib_lib = b.addSystemCommand(&.{ "sh", "-c", "r=\"$PWD\"; mkdir -p \"$r/build/desktop\"; " ++ + "wlw=vendor/raylib/src/external/glfw/src/wl_window.c; " ++ + "if ! grep -q 'dataOfferHandleAction' \"$wlw\" 2>/dev/null; then " ++ + "(cd vendor/raylib && patch -p1 --forward < \"$r/patches/glfw-wayland-dnd-crash.patch\" >/dev/null); " ++ + "rm -f \"$r/build/desktop/libraylib.a\"; " ++ + "fi; " ++ "[ -f \"$r/build/desktop/libraylib.a\" ] || (" ++ "cd vendor/raylib/src && make clean >/dev/null 2>&1; " ++ "make PLATFORM=PLATFORM_DESKTOP RAYLIB_LIBTYPE=STATIC " ++ diff --git a/patches/glfw-wayland-dnd-crash.patch b/patches/glfw-wayland-dnd-crash.patch new file mode 100644 index 0000000..8a9bf1b --- /dev/null +++ b/patches/glfw-wayland-dnd-crash.patch @@ -0,0 +1,80 @@ +From: study-player rewrite (backport of upstream GLFW master fix) +Subject: [Wayland] Fix drag-and-drop crash on data offer source_actions/action + +raylib 6.0 vendors GLFW 3.4 (release), which has an unfixed Wayland +drag-and-drop bug that crashes the host app the moment a file is dragged +over the window (glfw/glfw#2835, glfw/glfw#2562). Fixed in GLFW master but +NOT in the 3.4 release: + +`struct wl_data_offer_listener dataOfferListener` (wl_window.c) only +initialises the `offer` (opcode 0) handler. `source_actions` (opcode 1) +and `action` (opcode 2) are NULL (wl_data_offer is version 3; those events +were added in v3). Modern compositors (labwc, GNOME, KDE, ...) emit +source_actions/action during a drag, and libwayland-client then calls +`wl_abort("listener function for opcode N of wl_data_offer is NULL")` -> +the app dies on drag-enter, before any drop can happen. Provide no-op +handlers so the events are safely consumed. (GLFW only needs the `offer` +mime-types to decide what to accept on drop.) + +Also guard two NULL derefs in the same data-device path (both fixed in +GLFW master, changelog: "A drag entering a non-GLFW surface could cause a +segfault"): `dataDeviceHandleEnter` dereferenced `window` even when the +drag entered a non-GLFW surface, and `dataDeviceHandleDrop` passed a +possibly-NULL `dragFocus` to `_glfwInputDrop` (asserts are off in release). + +The X11/Xdnd backend (used by the original 'dev' app, -D_GLFW_X11) is a +separate code path and is unaffected, which is why drag-and-drop is +reliable there. + +Drop this patch once raylib vendors a GLFW release containing these fixes. + +diff --git a/src/external/glfw/src/wl_window.c b/src/external/glfw/src/wl_window.c +index 505166e..d68faf8 100644 +--- a/src/external/glfw/src/wl_window.c ++++ b/src/external/glfw/src/wl_window.c +@@ -1935,9 +1935,26 @@ static void dataOfferHandleOffer(void* userData, + } + } + ++// wl_data_offer v3 events. The compositor sends source_actions / action during ++// a drag-and-drop; without handlers libwayland-client wl_abort()s on the NULL ++// listener function pointer (glfw/glfw#2835). No-op handlers are sufficient. ++static void dataOfferHandleSourceActions(void* userData, ++ struct wl_data_offer* offer, ++ uint32_t source_actions) ++{ ++} ++ ++static void dataOfferHandleAction(void* userData, ++ struct wl_data_offer* offer, ++ uint32_t dnd_action) ++{ ++} ++ + static const struct wl_data_offer_listener dataOfferListener = + { +- dataOfferHandleOffer ++ dataOfferHandleOffer, ++ dataOfferHandleSourceActions, ++ dataOfferHandleAction + }; + + static void dataDeviceHandleDataOffer(void* userData, +@@ -1987,7 +2004,7 @@ static void dataDeviceHandleEnter(void* userData, + window = wl_surface_get_user_data(surface); + } + +- if (surface == window->wl.surface && _glfw.wl.offers[i].text_uri_list) ++ if (window && surface == window->wl.surface && _glfw.wl.offers[i].text_uri_list) + { + _glfw.wl.dragOffer = offer; + _glfw.wl.dragFocus = window; +@@ -2042,7 +2059,7 @@ static void dataDeviceHandleDrop(void* userData, + { + int count; + char** paths = _glfwParseUriList(string, &count); +- if (paths) ++ if (paths && _glfw.wl.dragFocus) + _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths); + + for (int i = 0; i < count; i++) |
