Skip to content

Commit b472119

Browse files
committed
niri's file picker never opened, because the backend that claims it delegates
Every "attach a file" or "upload" button in every app on niri did nothing at all. No dialog, no error, no crash. The only trace anywhere was one line in the *user* journal: xdg-desktop-portal-gnome: Delegated FileChooser call failed: The name is not activatable niri's own portals.conf sets `default=gnome;gtk;`, so FileChooser goes to the GNOME backend, and that backend does not implement FileChooser. It delegates to org.gnome.Nautilus. kuma ships Thunar, nothing provides that name, and the request dies one level below where anything is watching. The `;gtk` fallback cannot help, and that is the part worth keeping in mind for the next portal: fallback is resolved from the .portal files before any call is made, gnome.portal *advertises* FileChooser, and the delegation only fails afterwards, inside the backend. A backend that lies in its manifest is invisible to the router. So the interface has to be named to gtk explicitly, which implements it for real and was already installed and running for Access and Notification. The config is derived from niri's file rather than copied beside it. The highest-precedence portals.conf wins outright instead of merging, so a hand-copy would silently freeze whatever niri's other defaults were the day it was written, which is the same staleness trap that moved the terminal off foot. Two greps fail the build instead of shipping a dead picker: one if niri stops shipping the file this derives from, one if the gtk backend stops implementing the interface being routed to it. Both verified by sabotage, and neither writes a file when it trips. COSMIC is deliberately untouched: cosmic.portal implements FileChooser natively, confirmed by reading it out of a built COSMIC image, so routing it to gtk would swap a working native picker for a worse one. Verified on the running laptop before baking: with the interface pointed at gtk, an OpenFile call over D-Bus put a real picker on screen, where the same call previously logged the delegation failure and showed nothing. 131 tests, fmt and clippy clean.
1 parent fd7e0ca commit b472119

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

‎src/containerfile.rs‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,33 @@ pub fn generate(config: &Config) -> String {
12621262
out.push_str(
12631263
"RUN grep -q '\"alacritty\"' /usr/share/doc/niri/default-config.kdl \\\n && mkdir -p /etc/niri \\\n && sed -e 's/alacritty/kitty/g' -e '/starts waybar/d' -e '/^spawn-at-startup \"waybar\"$/d' -e '/XF86Audio/d' -e '/XF86MonBrightness/d' -e '/^binds {/r /usr/lib/kuma/niri-binds.kdl' /usr/share/doc/niri/default-config.kdl > /etc/niri/config.kdl \\\n && cat /usr/lib/kuma/niri-extras.kdl >> /etc/niri/config.kdl \\\n && niri validate --config /etc/niri/config.kdl\n",
12641264
);
1265+
// Every "attach a file" button in every app did nothing, silently.
1266+
//
1267+
// niri's own portals.conf prefers the GNOME backend for everything
1268+
// it does not name, and that backend does not implement FileChooser:
1269+
// it delegates to org.gnome.Nautilus. kuma ships Thunar, so the name
1270+
// is not activatable and the request dies inside the backend. The
1271+
// only trace anywhere is "Delegated FileChooser call failed: The
1272+
// name is not activatable" in the *user* journal, which is why this
1273+
// survived two desktops and a bare-metal install unnoticed.
1274+
//
1275+
// The `default=gnome;gtk;` fallback cannot save it. Fallback is
1276+
// resolved from the .portal files, gnome.portal advertises
1277+
// FileChooser, and the failure only happens later, one level down,
1278+
// where the router cannot see it. So the interface has to be
1279+
// pointed at gtk by name. gtk implements it for real, is already
1280+
// installed for Access and Notification, and is already running.
1281+
//
1282+
// Derived from niri's file rather than copied beside it: the
1283+
// highest-precedence file wins outright instead of merging, so a
1284+
// hand-copy would silently freeze whatever niri's defaults were the
1285+
// day it was written. Both greps fail the build instead of shipping
1286+
// a dead picker: the first if niri stops shipping the file this is
1287+
// derived from, the second if the gtk backend stops implementing
1288+
// the one interface being routed to it.
1289+
out.push_str(
1290+
"RUN grep -q '^\\[preferred\\]' /usr/share/xdg-desktop-portal/niri-portals.conf \\\n && grep -q 'org.freedesktop.impl.portal.FileChooser' /usr/share/xdg-desktop-portal/portals/gtk.portal \\\n && mkdir -p /etc/xdg-desktop-portal \\\n && { cat /usr/share/xdg-desktop-portal/niri-portals.conf; echo 'org.freedesktop.impl.portal.FileChooser=gtk;'; } > /etc/xdg-desktop-portal/niri-portals.conf\n",
1291+
);
12651292
// Upstream niri-session imports the ENTIRE greeter environment into
12661293
// the systemd user manager — deprecated (warns in the journal every
12671294
// login) and indiscriminate. Scope it: the XDG_* trio is how
@@ -2449,6 +2476,48 @@ mod tests {
24492476
assert!(pinned.iter().any(|p| p == "/etc/hostname"));
24502477
}
24512478

2479+
/// A file picker that never appears, which is what niri shipped until
2480+
/// this line existed. The GNOME portal backend advertises FileChooser
2481+
/// and then delegates it to org.gnome.Nautilus, a package kuma does
2482+
/// not install, so the interface has to be named to the gtk backend
2483+
/// rather than left to `default=gnome;gtk;` to resolve.
2484+
///
2485+
/// COSMIC is deliberately not given the same treatment: its own
2486+
/// backend implements FileChooser, verified by reading cosmic.portal
2487+
/// out of a built COSMIC image. Routing it to gtk there would replace
2488+
/// a working native picker with a worse one.
2489+
#[test]
2490+
fn niri_routes_the_file_chooser_at_a_backend_that_implements_it() {
2491+
let out = generate(&config("schema_version = 1\n[system]\ndesktop = \"niri\"\n"));
2492+
let conf = "/etc/xdg-desktop-portal/niri-portals.conf";
2493+
assert!(out.contains("org.freedesktop.impl.portal.FileChooser=gtk;"));
2494+
assert!(out.contains(conf));
2495+
2496+
// Derived from niri's own file, never a hand-copy: the winning
2497+
// config file replaces rather than merges, so a copy would freeze
2498+
// whatever the other defaults were the day it was written.
2499+
assert!(out.contains("cat /usr/share/xdg-desktop-portal/niri-portals.conf"));
2500+
2501+
// Both halves of the routing are guarded, because both can rot
2502+
// without anything else failing: the file this derives from, and
2503+
// the backend it hands the interface to.
2504+
assert!(out.contains("grep -q '^\\[preferred\\]'"));
2505+
assert!(out.contains(
2506+
"grep -q 'org.freedesktop.impl.portal.FileChooser' \
2507+
/usr/share/xdg-desktop-portal/portals/gtk.portal"
2508+
));
2509+
2510+
// Written to /etc, so the drift check owns it like any other file
2511+
// kuma has an opinion about.
2512+
let owned = etc_paths(&config("schema_version = 1\n[system]\ndesktop = \"niri\"\n"));
2513+
assert!(owned.iter().any(|p| p == conf));
2514+
2515+
// COSMIC's own backend implements it; leave that alone.
2516+
let cosmic = generate(&config("schema_version = 1\n[system]\ndesktop = \"cosmic\"\n"));
2517+
assert!(!cosmic.contains("niri-portals.conf"));
2518+
assert!(!cosmic.contains("FileChooser"));
2519+
}
2520+
24522521
/// The cheap tier of the smoke tests, and the only one that runs
24532522
/// everywhere: every committed example must compile to an image that
24542523
/// keeps the promises kuma makes about *all* images, plus the ones

0 commit comments

Comments
 (0)