Skip to content

✨ Add Kotlin/Native Linux file picker via XDG desktop portal - #636

Open
Enaium wants to merge 3 commits into
vinceglb:mainfrom
Enaium:feature/linux-native-xdg-portal-picker
Open

✨ Add Kotlin/Native Linux file picker via XDG desktop portal#636
Enaium wants to merge 3 commits into
vinceglb:mainfrom
Enaium:feature/linux-native-xdg-portal-picker

Conversation

@Enaium

@Enaium Enaium commented Aug 10, 2026

Copy link
Copy Markdown

Summary

Adds file picker, directory picker, and file saver dialogs for Kotlin/Native Linux targets (linuxX64, linuxArm64) to filekit-dialogs, backed by the GNOME XDG Desktop Portal (org.freedesktop.portal.FileChooser).

On GNOME desktops the portal renders the Nautilus-style GNOME file dialog (the native portal is planned to move into Nautilus itself), so this is the Nautilus/GNOME-native picking experience for native Linux apps.

What changed

filekit-dialogs

  • Enable linuxX64/linuxArm64 targets in the module convention.
  • src/linuxMain: platform actuals for openFilePicker, openDirectoryPicker, openFileSaver, and openFileWithDefaultApplication (via xdg-open), plus FileKitDialogSettings/FileKitOpenFileSettings.
  • src/linuxDbusMain: a libdbus cinterop client implementing the portal OpenFile/SaveFile requests (a{sv} options: multiple, directory, filters, current_folder, current_name) and the org.freedesktop.portal.Request.Response signal handling, compiled into both Linux targets (the shared linuxMain metadata compilation cannot see cinterop bindings).
  • linuxMain keeps the protocol model and pure helpers (URI decoding, filter building, response resolution) so they are unit-testable.
  • Operational failures are wrapped in FileKitPickerException/FileKitDialogException with LINUX_* messages, mirroring the existing Windows/macOS conventions.

Build

  • dbus.def cinterop with pkg-config-resolved compiler flags and a -L link path for the distro libdir; consumers link against libdbus-1 (present on every D-Bus enabled system).

Tests

  • linuxTest: pure helper tests (URI decoding, filters, response resolution) and failure-mapping tests.
  • linuxX64Test: portal response parsing regression tests using real (ua{sv}) D-Bus messages (covers the read path that previously aborted on dbus_message_iter_open_container), a portal connectivity smoke test (skips when no session bus is available), and a manual picker test gated behind FILEKIT_MANUAL_PICKER_TEST=1 that opens the real dialog for click-through verification.

Docs

  • docs/installation.mdx and docs/dialogs/setup.mdx updated: dialogs now supported on Kotlin/Native Linux via the XDG portal.

Verification

  • ./gradlew assemble and :filekit-dialogs:check pass.
  • ktlint clean.
  • End-to-end click-through test performed on GNOME (Wayland): dialog opens, multi-select works, paths are returned and printed correctly.
  • Note: on this machine running native tests requires a libcrypt.so.1 compatibility shim (pre-existing toolchain/sysroot issue, unrelated to this change).

Affected platforms

  • Kotlin/Native Linux (linuxX64, linuxArm64): added.
  • filekit-dialogs-compose is unchanged: Compose Multiplatform has no Linux native artifacts yet.

@vinceglb vinceglb 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.

Hi @Enaium, thanks a lot for working on native Linux dialog support!

I reviewed the implementation and found three important issues to address before merging:

  • assemble currently fails on macOS because the Linux interop requires pkg-config and the D-Bus development headers from the host machine.
  • The current_folder option should contain the raw filesystem path. It currently includes a file:// prefix, which can prevent the dialog from opening in the requested directory.
  • Percent-encoded paths should be decoded as UTF-8. The current implementation corrupts filenames containing non-ASCII characters such as é.

Can you take a look? Thanks again!

@Enaium

Enaium commented Aug 10, 2026

Copy link
Copy Markdown
Author

Thanks for the review! All three issues are addressed:

  1. macOS assemble failurefilekit-dialogs now only creates its Linux targets when building on a Linux host (HostManager.hostIsLinux), since the libdbus cinterop requires the host's D-Bus development headers. filekit-core keeps its Linux targets everywhere (it has no cinterop). The Linux CI jobs (build and test-linux) now install libdbus-1-dev so assemble/native tests keep working there.

  2. current_folder — now sends the raw filesystem path (null-terminated bytestring) instead of a file:// URI, matching the JVM XdgFilePickerPortal implementation.

  3. Percent-decoding — collected bytes are now decoded as UTF-8 (previously each byte was converted to a Latin-1 char, corrupting non-ASCII names like café.png). Added regression tests for percent-encoded and literal UTF-8 paths.

Verified locally: assemble, :filekit-dialogs:check, and the full native test suite (52 tests) pass, and I confirmed via -Dos.name simulation that the Linux targets are absent on non-Linux hosts.

@Enaium
Enaium marked this pull request as draft August 10, 2026 16:05
@Enaium
Enaium force-pushed the feature/linux-native-xdg-portal-picker branch from 57ba73f to 2681176 Compare August 10, 2026 16:27
@Enaium
Enaium marked this pull request as ready for review August 10, 2026 16:29
@Enaium
Enaium force-pushed the feature/linux-native-xdg-portal-picker branch from 2681176 to d45177e Compare August 10, 2026 16:37
@Enaium

Enaium commented Aug 10, 2026

Copy link
Copy Markdown
Author

Publishing has been verified on both platforms without issues:

  • macOS: ./gradlew :filekit-dialogs:publishToMavenLocal completes successfully, publishing all variants (jvm, macosArm64, iosArm64, iosSimulatorArm64, mingwX64, js, wasmJs, android), with the configuration cache stored cleanly.
  • Linux: the build (assemble) and test-linux (linuxX64Test) CI jobs pass, covering the Linux native targets including the libdbus cinterop.

@Enaium
Enaium requested a review from vinceglb August 10, 2026 17:02
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