summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-29 15:21:47 +0900
committerAdam Malczewski <[email protected]>2026-06-29 15:21:47 +0900
commitb66f804cf01a639378bbbf0c5a03ab527853fda6 (patch)
tree4f7186c549671059264a0f0da3f2512f942d714f
parent53ef70aff331c9f95b744809c125af55a230ebfa (diff)
downloadstudy-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.zig13
-rw-r--r--patches/glfw-wayland-dnd-crash.patch80
2 files changed, 93 insertions, 0 deletions
diff --git a/build.zig b/build.zig
index 26891df..333362a 100644
--- a/build.zig
+++ b/build.zig
@@ -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++)