From 3d99ab4bb1bee1f57a61ed6e78b0ce313bbb40a2 Mon Sep 17 00:00:00 2001 From: NAX LAB <64031920+NAXLAB@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:25:59 -0400 Subject: [PATCH] Appease X11 users with niri/wayland specific postfixup wrapper (#212) * Appease X11 users with niri/wayland specific postfixup wrapper * Linux: stay on native Wayland under niri in the app, not the Nix wrapper Move the niri check from the flake's wrapper into apps/solvecraft/src/main.rs next to the XWayland default, so it applies to every niri user, not only Nix builds (flake.nix is back to main's, which also clears the conflict with #210). SOLVECRAFT_WAYLAND=1 still keeps native Wayland, and =0 now forces XWayland. docs/nix.md notes the trade-off: no file drag-and-drop on native Wayland. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT --------- Co-authored-by: bherbruck Co-authored-by: Claude Opus 5.5 --- apps/solvecraft/src/main.rs | 49 ++++++++++++++++++++++++++++++++++--- docs/nix.md | 13 ++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/apps/solvecraft/src/main.rs b/apps/solvecraft/src/main.rs index f8fb1a4a..a1a56dcb 100644 --- a/apps/solvecraft/src/main.rs +++ b/apps/solvecraft/src/main.rs @@ -175,10 +175,12 @@ fn main() -> eframe::Result { #[cfg(all(unix, not(target_os = "macos")))] let mut options = options; #[cfg(all(unix, not(target_os = "macos")))] - // winit has no file drag-and-drop on Wayland (only on X11), so dropping files from the file - // manager showed a "no" cursor. Run through XWayland when it's there; SOLVECRAFT_WAYLAND=1 - // keeps the native Wayland backend. - if std::env::var_os("DISPLAY").is_some() && std::env::var_os("SOLVECRAFT_WAYLAND").is_none() { + if prefer_x11( + std::env::var_os("DISPLAY").is_some(), + std::env::var_os("WAYLAND_DISPLAY").is_some(), + std::env::var("XDG_CURRENT_DESKTOP").ok().as_deref(), + std::env::var("SOLVECRAFT_WAYLAND").ok().as_deref(), + ) { options.event_loop_builder = Some(Box::new(|b| { use winit::platform::x11::EventLoopBuilderExtX11; b.with_x11(); @@ -236,6 +238,45 @@ fn main() -> eframe::Result { ) } +/// Whether to run through XWayland rather than native Wayland. winit has no file drag-and-drop +/// on Wayland (only on X11), so dropping files from the file manager showed a "no" cursor: use +/// XWayland when it's there. Except on niri, whose XWayland (xwayland-satellite) breaks window +/// management (maximize/restore), so it stays on native Wayland and gives up file drops. +/// `SOLVECRAFT_WAYLAND=1` keeps native Wayland, `SOLVECRAFT_WAYLAND=0` forces XWayland. +#[cfg(all(unix, not(target_os = "macos")))] +fn prefer_x11(display: bool, wayland_display: bool, desktop: Option<&str>, wayland_override: Option<&str>) -> bool { + match wayland_override.map(str::trim) { + Some("0") => return display, + Some(_) => return false, + None => {} + } + let niri = desktop.is_some_and(|d| d.split(':').any(|name| name.trim().eq_ignore_ascii_case("niri"))); + display && !(niri && wayland_display) +} + +#[cfg(all(test, unix, not(target_os = "macos")))] +mod backend_tests { + use super::prefer_x11; + + #[test] + fn xwayland_by_default_native_wayland_on_niri_and_the_override_wins() { + // XWayland whenever there's an X display, for file drag-and-drop. + assert!(prefer_x11(true, true, Some("GNOME"), None)); + assert!(prefer_x11(true, false, None, None)); + assert!(!prefer_x11(false, true, Some("KDE"), None)); + // niri (alone or in a desktop list) under Wayland stays native. + assert!(!prefer_x11(true, true, Some("niri"), None)); + assert!(!prefer_x11(true, true, Some("niri:GNOME"), None)); + // niri without a Wayland socket (nested X session) still uses X11. + assert!(prefer_x11(true, false, Some("niri"), None)); + // SOLVECRAFT_WAYLAND: anything but 0 keeps Wayland, 0 forces XWayland (when present). + assert!(!prefer_x11(true, true, Some("GNOME"), Some("1"))); + assert!(!prefer_x11(true, true, Some("GNOME"), Some(""))); + assert!(prefer_x11(true, true, Some("niri"), Some("0"))); + assert!(!prefer_x11(false, true, Some("niri"), Some("0"))); + } +} + #[cfg(test)] mod icon_tests { #[test] diff --git a/docs/nix.md b/docs/nix.md index bca4dfd7..7ea73a69 100644 --- a/docs/nix.md +++ b/docs/nix.md @@ -87,6 +87,19 @@ For older builds, including 0.3.0, set Anti-aliasing to 1x and restart when usin If that window cannot show the settings dialog, edit only the `msaa` field to `1` in `~/.local/share/solvecraft/graphics.json`; preserve any other settings. +## Wayland, XWayland and niri + +On Linux the app runs through XWayland whenever an X display is available, because +winit only supports dragging files from a file manager into the window on X11. On +niri (detected from `XDG_CURRENT_DESKTOP` with a Wayland socket present) it stays on +native Wayland instead, since window management such as maximize/restore misbehaves +under niri's XWayland. The trade-off: **on native Wayland, dropping files onto the +window does not work**; use File > Open or pass the file on the command line. +This applies to every build, not only the Nix package. + +Override the choice with `SOLVECRAFT_WAYLAND=1` (always native Wayland) or +`SOLVECRAFT_WAYLAND=0` (XWayland whenever an X display is available). + ## Development shells `nix develop` supplies runtime libraries and uses the Rust/C toolchain already on