Skip to content

Code Quality: Refactor the Linux port to prepare for a cross-platform desktop head - #202

Open
lextm wants to merge 2 commits into
MemerGamer:mainfrom
lextudio:linux/cross-platform-seam
Open

lextm wants to merge 2 commits into
MemerGamer:mainfrom
lextudio:linux/cross-platform-seam

Conversation

@lextm

@lextm lextm commented Oct 17, 2026 •

Copy link
Copy Markdown

Resolved / Related Issues

  • Closes #

No linked issue. This is a refactor with no intended behavior change.

What changed

  • Extracts the OS-neutral desktop code into a new Files.Platform.Posix project that references only Files.Platform.Abstractions. Files.Platform.Linux keeps the Linux-only services and references it.
  • Renames Linux-prefixed types, files and namespaces to Desktop*/Posix*, so the Linux head is one implementation of a shared desktop layer.
  • Adds a FilesDesktopOS selector (defaults to Linux) and gates the Wayland/X11 targets on it. Same Uno packages, define and output for Linux.
  • Gathers the app layer's Linux entry points behind DesktopPlatform (DI registration, user directories, drag file list, display scale, FileManager1) and moves the Linux startup, single instance and Uno host code from Program.cs into Program.Linux.cs.
  • Routes the root/elevation environment through a Posix-level hook that the Linux assembly installs, and moves the "open beneath a directory handle" primitive into the Posix native layer.
  • CredentialsHelpers uses any non-Windows ISecretStore.
  • Fixes the tests left stale by the move and keeps the /usr/lib/linuxfiles helper, state and cache paths that packaging, the polkit policy and launchers use.

Steps used to test these changes

  1. dotnet build src/Files.App -f net10.0-desktop builds with 0 errors.
  2. The Files CI (Linux) workflow passes on the same commit, including the platform test projects step: https://github.com/lextudio/macOS-Files/actions/runs/39047066444

Remaining limitations

  • I did not run the app by hand, so the split Program startup, single-instance forwarding and drag and drop are only covered by the build and CI, not by a runtime check.
  • Some Linux-specific code is still in Files.Platform.Posix (for example /proc/self/fd in TrustedNativeDirectory and the freedesktop MIME and icon helpers). Moving it is left for a follow-up.
  • FilesDesktopOS=macOS is still scaffolding: Program.Linux.cs, DesktopPlatform.cs and MainWindow.Linux.cs reference Files.Platform.Linux unconditionally, so that selection does not compile yet.
  • AddLinuxAppData() no longer registers ILocalSettingsStore by itself; AddPosixPlatform() owns that registration, so callers need both.
  • The rename is partial. LinuxMediaPreview / LinuxMedia* resource keys and StorageNetworkService.Linux.cs keep their names because nothing depends on them being platform-neutral yet.

🤖 Generated with Claude Code

@MemerGamer MemerGamer 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 this, Lex. The split looks careful: startup order in Program.Linux.cs, OpenBeneath/openat2 semantics, native flag values, packaging paths and resource keys all check out as equivalent, and it merges cleanly. Locally src/Files.App builds with 0 errors (net10.0-desktop, Release), and Files.Platform.Tests passes apart from 3 TrustedNativeDirectory tests that most likely fail only because of my local temp path; CI is green.

A few things to fix before merging, since the PR says there is no intended behavior change:

  1. System dark/light detection regression (src/Files.App/Services/App/AppThemeModeService.cs). Before, the code called LinuxColorScheme.Initialize() synchronously and read LinuxColorScheme.IsDark, which checks the FILES_COLOR_SCHEME override, then the portal, GTK_THEME, kdeglobals and gsettings. Now it reads ISystemAppearanceService.Current.IsDark == true, which comes only from the portal color-scheme, is bool?, and stays null until the fire-and-forget RefreshAsync() completes. With the theme set to Default, startup resolves to Light first, and setups without a portal (KDE-only, GTK_THEME, gsettings) plus the FILES_COLOR_SCHEME override no longer affect this path. LinuxColorScheme.Initialize()/.Changed are also left with no callers, so that watcher never starts. Either keep the LinuxColorScheme call behind the desktop seam, or fall back to PaletteIsDark / the full chain when IsDark is null.
  2. Stale path in a repo test: scripts/linux/tests/test_install_root_helper.py:127 still reads src/Files.Platform.Linux/Native/PosixNative.cs, which now lives under src/Files.Platform.Posix/Native/. CI doesn't run this test, so it isn't caught there. Please update the path, including the split if the #if ELEVATION_HELPER branches moved to another file.
  3. Root env hook installed by [ModuleInitializer] (TracedProcessRegistration.cs): safe today because Program.Main loads the Linux assembly first, but if Posix code ever starts a process before that assembly loads, the root child-environment sanitizing is skipped without any error. Please install it explicitly from Program/AddDesktopPlatform, or at least document the load-order invariant next to PrepareStartInfo.
  4. Minor: FilesDesktopOS=macOS can't compile yet (Program.Linux.cs, DesktopPlatform.cs and MainWindow.Linux.cs still reference Files.Platform.Linux unconditionally). That's fine for scaffolding, but please mention it in the PR description. AddLinuxAppData() on its own no longer registers ILocalSettingsStore, since AddPosixPlatform owns it now. The renames are also partial (LinuxMediaPreview / LinuxMedia* resw keys, StorageNetworkService.Linux.cs).

Happy to re-test once 1 and 2 are addressed.

🤖 Generated with Claude Code

@lextm

lextm commented Nov 2, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed review. Pushed 3afee5022 for 1–3 and updated the description for 4.

  1. Startup colour scheme. LinuxSystemAppearanceService now calls LinuxColorScheme.Initialize() synchronously in its constructor, seeds Current.PaletteIsDark from it and re-raises Changed when LinuxColorScheme.Changed fires, so the portal-less watcher runs again. ResolveRootTheme uses IsDark ?? PaletteIsDark, which keeps the GTK_THEME / kdeglobals / gsettings chain for both startup (before the portal answers) and desktops without a portal. FILES_COLOR_SCHEME is applied in RefreshAsync as well, so it still wins over a portal value. Added two StartupRegressionTests rows covering the override seeding, with the static _isDark restored afterwards.
  2. Stale test path. Updated test_install_root_helper.py to src/Files.Platform.Posix/Native/PosixNative.cs. The #if ELEVATION_HELPER / #if !ELEVATION_HELPER branches moved with the file, so there is no split to follow.
  3. Process hook. Removed the [ModuleInitializer]. AddLinuxPlatform() now calls TracedProcessRegistration.Register() before registering any service; ConfigureHost is the first thing startup does after AppModel construction, and every TracedProcess caller is resolved from that container, so nothing can start a process before the hook is in place.
  4. Description. Added the macOS scaffold (FilesDesktopOS=macOS does not compile yet), the AddLinuxAppData() / ILocalSettingsStore ownership note and the partial renames to the remaining-limitations section.

Local checks: dotnet build src/Files.App -f net10.0-desktop and the platform test project build with 0 errors; Files.Platform.Tests passes locally apart from the 1 installer test that needs Linux's install -D. CI is green on the new head: https://github.com/lextudio/macOS-Files/actions/runs/41460799284

lextm and others added 2 commits November 3, 2026 01:47
Extract the OS-neutral desktop code into a new Files.Platform.Posix project
that references only Files.Platform.Abstractions, and rename the Linux-prefixed
types, files and namespaces to Desktop*/Posix* so the Linux head is one
implementation of a shared desktop layer instead of the layer itself.

* Add a FilesDesktopOS selector (defaults to Linux) and gate the desktop
  Wayland/X11 targets on it. Linux behaviour is unchanged: the same Uno
  packages, the same define, the same output.
* Move the shared POSIX primitives (stat, enumeration, watching, file
  operations, permissions, archives, launching, icons, previews, search and
  settings) into Files.Platform.Posix; Files.Platform.Linux keeps the
  Linux-only services and references it.
* Route the root/elevation environment applied when starting a process through
  a Posix-level hook that the Linux assembly installs, and move the safe
  "open beneath a directory handle" primitive into the Posix native layer.
* Gather the app layer's Linux entry points behind DesktopPlatform (DI
  registration, user directories, drag file list, display scale, FileManager1)
  and move the Linux startup, single instance and Uno host code from
  Program.cs into Program.Linux.cs.
* Let CredentialsHelpers use any non-Windows ISecretStore.
* Fix the tests left stale by the move and restore the /usr/lib/linuxfiles
  helper, state and cache paths that the packaging, polkit policy and
  launchers still use.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…itly

- Seed LinuxSystemAppearanceService's palette synchronously from LinuxColorScheme, keep its
  Changed watcher alive, and let FILES_COLOR_SCHEME win over portal values. A default theme now
  resolves correctly before the portal answers and on desktops that only expose GTK_THEME,
  kdeglobals or gsettings.
- Install the root child-environment hook from AddLinuxPlatform instead of a module initializer,
  so the load-order requirement is explicit.
- Point the root-helper test at PosixNative's new location.
@lextm
lextm force-pushed the linux/cross-platform-seam branch from 3afee50 to f40aa7c Compare November 3, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants