Skip to content

feat(linux): add native Wayland support and generalize compositor gestures - #367

Merged
KnifeLemon merged 8 commits into
KnifeLemon:mainfrom
mahaveergurjar:feat/native-wayland-support
Oct 23, 2026
Merged

KnifeLemon merged 8 commits into
KnifeLemon:mainfrom
mahaveergurjar:feat/native-wayland-support

Conversation

@mahaveergurjar

@mahaveergurjar mahaveergurjar commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR introduces opt-in native Wayland support for Linux desktops (Hyprland, Sway, KDE Wayland, etc.) and extends Wayland gesture support to Hyprland.

Key Changes

  1. Opt-in Avalonia Wayland Backend:
    • Added package reference to Avalonia.Wayland conditional on Linux builds (FileeNativeWayland), keeping Windows and macOS binaries small.
    • Guarded builder.UseWayland() behind #if FILEE_NATIVE_WAYLAND to ensure clean cross-compilation on Windows and macOS.
    • Controlled via the FILEE_USE_WAYLAND=1 environment variable (along with checking WAYLAND_DISPLAY), defaulting to existing XWayland behavior otherwise.
  2. Radial Window Lifecycle on Wayland:
    • In RadialController.cs, when opted into native Wayland, listens to the window's Closed event to re-instantiate it on the next gesture. (In Wayland, hiding an xdg_toplevel destroys the native Wayland surface).
  3. Supported Wayland Compositor Gestures:
    • In LinuxPlatformServices.cs, generalized compositor gesture support to recognize both KDE Plasma and Hyprland (via XDG_CURRENT_DESKTOP or HYPRLAND_INSTANCE_SIGNATURE).
  4. Third-Party Notices:
    • Added NWayland (MIT) to THIRD-PARTY-NOTICES.md.
  5. Documentation & Unit Tests:
    • Added documentation in installer/linux/INSTALL.txt and CONTRIBUTING.md for FILEE_USE_WAYLAND=1 and Hyprland cursor placement (windowrulev2 = move cursor -50% -50%).
    • Added unit tests in tests/Filee.Platform.Tests/WaylandDecisionTests.cs covering the compositor detection and opt-in decision functions across all combinations.

Maintainer changes

  • Merged main into the branch.
  • The two decision functions moved from LinuxPlatformServices (Linux-only, so calling it from RadialController raised CA1416 on Windows and macOS) to a platform-neutral WaylandSupport class in Filee.Platform.Linux: IsNativeWaylandRequested(isLinux, FILEE_USE_WAYLAND, WAYLAND_DISPLAY), IsSupportedCompositor(XDG_CURRENT_DESKTOP, HYPRLAND_INSTANCE_SIGNATURE), and a NativeWaylandRequested property that Program.cs and RadialController both use. The tests call WaylandSupport.
  • THIRD-PARTY-NOTICES.md: NWayland's project link is https://github.com/kekekeks/NWayland (the package's own repository).

…tures

- Add Avalonia.Wayland, Avalonia.Skia, and Avalonia.HarfBuzz packages
- Initialize native Wayland platform backend in Program.cs when WAYLAND_DISPLAY is set
- Re-create RadialWindow if closed to avoid xdg_surface destruction crash on Wayland
- Generalize WaylandGestures check in LinuxPlatformServices.cs beyond KDE Plasma
- Improve initial drag ring visibility in RadialViewModel.cs
- Update Linux installer script for native Wayland deployment
@github-actions github-actions Bot added the feature New features label Oct 21, 2026

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this, @mahaveergurjar! Native Wayland and gestures beyond KDE are both things Linux users want, and getting the donut running under Hyprland without XWayland is a nice result. As it stands, though, the PR changes behaviour on every platform and for every Wayland user, so it needs another pass:

  1. ResetForDrag must keep the donut hidden until files arrive. Setting DonutOpacity = 1 and HitBackground = "Transparent" reverts “Don’t show the donut for a drag gesture without files” on Windows, macOS and X11 too. A Ctrl + drag of text now shows an empty donut. A fully transparent window also doesn’t receive drags on Windows, which is why the near-transparent #01000000 is there (see the comment on _hitBackground). The existing test RenderTests.Donut_toolbar_waits_for_files_in_drag_mode fails with this change (expected opacity 0, got 1). If native Wayland needs something different here, please make it conditional on that backend.
  2. Make the native Wayland backend opt-in. With this change every Linux session that has WAYLAND_DISPLAY (GNOME and KDE included) switches from XWayland to Avalonia’s Wayland backend, which has only been tried on Hyprland and bypasses the X11 paths (gesture toolbar, selected-file queries). When the KDE gesture went in (#142), we agreed to keep such changes opt-in. Please put it behind an environment variable, document it in installer/linux/INSTALL.txt and the Linux section of CONTRIBUTING.md, and reference the Wayland packages only for Linux runtimes so the Windows and macOS packages don’t grow.
  3. Re-creating the donut window: please do it only on the native Wayland backend, decide it from the window’s Closed event rather than IsLoaded (a hidden window may report not loaded elsewhere, which would rebuild it on every gesture), and don’t swallow exceptions with an empty catch.
  4. Other compositors: without the KDE check the gesture turns on everywhere, but only KWin has a script that moves the donut to the cursor. Please keep KDE as is and add other compositors explicitly, with the Hyprland windowrulev2 you mention shipped or documented in INSTALL.txt. On GNOME the donut would open in the wrong place.
  5. Please drop the install.sh changes.
  • pkill -f "Filee" kills every process whose command line contains “Filee”. That includes the shell running Filee-2.0.0-linux-x64/install.sh itself, an editor with the repository open, or a tail -f on Filee’s log.
  • rm -rf "$install_dir" doesn’t check the .filee-package marker.
  • The checks that kept us from overwriting someone else’s ~/.local/bin/filee and desktop file are gone, and the “(Native Wayland)” comment would show up for X11 users too.
  • Upgrading in place is being handled in #360 (install.sh --upgrade with a marker check and rollback).
  1. Tests for whatever stays, please. The fork’s CI run is waiting for approval, and we’ll start it on the next push.

Happy to help work out the opt-in shape if you’d like to discuss it first!

…, and preserve existing installer/drag behaviors
@mahaveergurjar

Copy link
Copy Markdown
Contributor Author

Hi @KnifeLemon,

Thank you for the thorough and thoughtful review! I've addressed all the requested changes in 6b82767:

  1. Reverted ResetForDrag changes: Reverted DonutOpacity and HitBackground back to upstream behavior (0 and #01000000). Verified that RenderTests.Donut_toolbar_waits_for_files_in_drag_mode passes cleanly.
  2. Opt-in Native Wayland: Made Avalonia Wayland backend strictly opt-in via FILEE_USE_WAYLAND=1 (defaulting to existing XWayland behavior otherwise). Added conditions to Filee.App.csproj so the Wayland packages only reference on Linux runtimes, preserving Windows and macOS package sizes.
  3. Radial Window Re-creation: Switched to listening to the window's Closed event (only active when native Wayland is enabled) to reset _window = null without catching or swallowing exceptions or relying on IsLoaded.
  4. Compositor Handling & Documentation: Restored the explicit KDE check and added explicit support for Hyprland (HYPRLAND_INSTANCE_SIGNATURE / XDG_CURRENT_DESKTOP). Documented the Hyprland windowrulev2 rules and the FILEE_USE_WAYLAND=1 variable in both installer/linux/INSTALL.txt and CONTRIBUTING.md.
  5. Dropped install.sh Changes: Completely restored installer/linux/install.sh to its original upstream state.

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mahaveergurjar, and sorry for the wait on CI. 6b82767 covers most of the list: ResetForDrag is back as on main, the native backend is opt-in with FILEE_USE_WAYLAND=1 and documented in INSTALL.txt and CONTRIBUTING.md, the donut window is rebuilt from its Closed event only in that mode and without an empty catch, KDE keeps its check with Hyprland added explicitly, and install.sh is untouched.

What's left:

  1. The build fails on Windows and macOS. The CI run for 7619b7e stops at Program.cs(282,21): error CS1061: 'AppBuilder' does not contain a definition for 'UseWayland' in the Windows build job and in the macOS (Intel) job. $([MSBuild]::IsOSPlatform('Linux')) is the OS that runs the build, not the one the app is built for, and UseWayland() is compiled on every platform. Please decide it from the runtime identifier and compile the call only there, for example:
<PropertyGroup>
  <FileeNativeWayland Condition="$(RuntimeIdentifier.StartsWith('linux')) Or ('$(RuntimeIdentifier)' == '' And $([MSBuild]::IsOSPlatform('Linux')))">true</FileeNativeWayland>
  <DefineConstants Condition="'$(FileeNativeWayland)' == 'true'">$(DefineConstants);FILEE_NATIVE_WAYLAND</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(FileeNativeWayland)' == 'true'">
  <PackageReference Include="Avalonia.Wayland" />
</ItemGroup>

with #if FILEE_NATIVE_WAYLAND around the UseWayland() branch. Avalonia.Skia and Avalonia.HarfBuzz already come with Avalonia.Desktop, so only Avalonia.Wayland is needed and the two extra PackageVersion lines can go.
2. Notices. Avalonia.Wayland brings in NWayland (MIT). Please add it to THIRD-PARTY-NOTICES.md.
3. Tests (point 6 from last time) are still missing. The decisions are easy to make testable: move "use the native backend?" (OS, FILEE_USE_WAYLAND, WAYLAND_DISPLAY) and the compositor check (XDG_CURRENT_DESKTOP, HYPRLAND_INSTANCE_SIGNATURE) into small static functions that take the values, and test them: KDE, Hyprland through either variable, GNOME off, the switch unset, 0 and 1, no WAYLAND_DISPLAY. Please use the same function in Program.cs and RadialController: right now RadialController doesn't check WAYLAND_DISPLAY, so the two can disagree.
4. Hyprland placement. The rules in INSTALL.txt make the donut float without borders, but nothing moves it to the cursor, which is what the KWin script does on KDE. Does it open under the cursor for you? If not, please add a rule that moves it there (Hyprland has move cursor …) to the docs.
5. Please update the PR description. It still mentions the install.sh change, the RadialViewModel change and switching on WAYLAND_DISPLAY alone.

Thanks!

@mahaveergurjar

Copy link
Copy Markdown
Contributor Author

Hi @KnifeLemon,

Thank you for the detailed feedback! All 5 items have been addressed in f6a34b1:

  1. Cross-Platform Compilation:

    • Replaced the platform check in Filee.App.csproj with the recommended <FileeNativeWayland> condition based on RuntimeIdentifier and defined FILEE_NATIVE_WAYLAND.
    • Wrapped UseWayland() under #if FILEE_NATIVE_WAYLAND in Program.cs.
    • Removed Avalonia.Skia and Avalonia.HarfBuzz from Directory.Packages.props and Filee.App.csproj, keeping only Avalonia.Wayland.
    • Tested cross-compilation locally against -r win-x64 and -r osx-x64 — both build with 0 errors.
  2. Notices:

    • Added NWayland (MIT) notice to THIRD-PARTY-NOTICES.md.
  3. Pure Static Decision Functions & Unit Tests:

    • Added pure static methods IsNativeWaylandRequested and IsSupportedWaylandCompositor to LinuxPlatformServices.
    • Both Program.cs and RadialController.cs now use the same IsNativeWaylandRequested check to ensure complete consistency.
    • Added unit test suite in tests/Filee.Platform.Tests/WaylandDecisionTests.cs testing all combinations (KDE, Hyprland via desktop name or signature, GNOME off, switch unset/0/1/invalid, missing WAYLAND_DISPLAY, non-Linux OS). All 72 tests pass.
  4. Hyprland Placement Documentation:

    • Updated installer/linux/INSTALL.txt to include the cursor centering rule:
      windowrulev2 = move cursor -50% -50%, title:^(Filee Donut)$
      
  5. Updated PR Description:

    • Updated the PR title and description to accurately reflect current opt-in Wayland support, documentation, and testing.

KnifeLemon and others added 3 commits October 23, 2026 19:54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The native-backend and compositor checks move to WaylandSupport, which has
  no OS attribute: RadialController calls it on every platform, and the
  Linux-only LinuxPlatformServices raised CA1416 there. Program.cs and
  RadialController use the same NativeWaylandRequested property.
- THIRD-PARTY-NOTICES: NWayland's project is kekekeks/NWayland (the
  package's repository).
- Test names in the repo's sentence style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KnifeLemon
KnifeLemon merged commit e0788de into KnifeLemon:main Oct 23, 2026
10 checks passed
KnifeLemon added a commit to yanscholze/Filee that referenced this pull request Oct 23, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KnifeLemon added a commit to yanscholze/Filee that referenced this pull request Oct 23, 2026
The experimental Wayland notice still said KDE Plasma only. After KnifeLemon#367 it
names both and says how the donut gets to the cursor on each: a KWin
script on KDE Plasma, a window rule in hyprland.conf on Hyprland.

The GNOME notice now follows UsesGnomePointerBridge, so with
FILEE_WAYLAND_GESTURES=0 on GNOME the plain Wayland notice shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants