Skip to content

zenity: Fix non-UTF-8 paths, cancel results, filter names and the starting directory - #352

Open
jay-tau wants to merge 4 commits into
PolyMeilex:masterfrom
jay-tau:zenity-fallback-fixes
Open

jay-tau wants to merge 4 commits into
PolyMeilex:masterfrom
jay-tau:zenity-fallback-fixes

Conversation

@jay-tau

@jay-tau jay-tau commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #346.

#347 fixed items 1 and 2 of #346. This fixes the other four, in four commits on top of it:

  1. File names that aren't UTF-8 (item 5). zenity 4 prints the bytes of each path, and rfd decoded them as UTF-8, so such a name made the dialog return None. The output is now kept as bytes. This removes ZenityError::FromUtf8Error, which isn't public.
  2. Cancel (item 4). pick_files and pick_folders return None when the zenity dialog is cancelled, as the portal branch does since Minor xdg portal improvements #323, rather than Some(vec![]).
  3. Filter names with | (item 3). zenity ends a filter's name at the first | and has no way to escape it, so a | in a name is now shown as /. A lookalike such as ∣ (U+2223) would read better, but zenity converts --file-filter arguments from the locale's charset, and with LC_ALL=C, zenity 4.2.2 exited without showing a dialog when a filter name wasn't ASCII.
  4. Starting directory (item 6). The directory is passed as --filename '<dir>/', or joined with the name from set_file_name, rather than as zenity's working directory. With GTK's default file chooser settings, open and folder dialogs could start on Recent instead of the working directory (save dialogs already started there), while zenity opens the folder of a --filename path itself. zenity 3 only does this for an absolute path, so a relative directory is made absolute first, and zenity 4 ignores /, which is its own base name, so the root is passed as /./. Since zenity no longer starts in the directory, one that doesn't exist no longer stops it from starting. On master, the chdir fails and the dialog returns None without opening. A .. in the directory is now resolved by zenity without following symlinks, like cd .. in a shell, where chdir followed them.

Some limits remain. With the newline separator from #347, a selected name that contains a newline is still split: a file named x followed by a newline, selected with y.txt, gives [".../x", "", ".../y.txt"]. A separator such as /./ would avoid that, because GTK gives zenity canonical absolute paths, which have no . components; I can send it separately if you'd like. zenity 3 converts each path with g_filename_to_utf8 before printing it; a failed conversion can print (null), and a successful one from a legacy filename encoding can change the path's bytes. Before GLib 2.86, g_print also converts zenity's piped output when the locale's charset isn't UTF-8; zenity 4.2.2 with GLib 2.90.1 printed the bytes unchanged, also with LC_ALL=C. zenity 4 can also print (null) for a selected file that has no local path.

Testing

I built a small program against this branch and against master (98d6cf2) with default-features = false, features = ["xdg-portal"] and ran it in a headless mutter session on a private session bus without a portal service, so rfd used zenity 4.2.2 (GTK 4.24.1). Keys and clicks were sent with XTest. The program's working directory was the parent of the test folders, except for a second missing-folder check, which ran from a different existing folder to tell the parent folder apart from the working directory. For the first rows, GTK's file chooser was set to startup-mode='cwd' so that master opened the test folder; the set_directory rows use GTK's default, recent.

Case master This branch
pick_files, pick_file: caf\xE9.txt; pick_folder: caf\xE9 None the path
Escape in pick_files, pick_folders Some([]) None
add_filter("Text | Markdown", &["txt", "md"]) filter "Text", file Markdown listed filter "Text / Markdown", Markdown hidden, also with LC_ALL=C
set_directory with pick_files, pick_folder, set_file_name, and a relative path opens Recent opens the folder
set_directory("/") opens / with startup-mode='cwd' opens / with either setting
set_directory with a folder that doesn't exist no dialog, None dialog opens in the parent folder
save_file with set_file_name("report ") and set_directory ".../report" ".../report" (GTK removes the space itself)
pick_files, pick_folders: names with | or a trailing space correct (#347) correct

AsyncFileDialog::pick_files and pick_folders gave the same results as the sync calls for the | names and for Escape.

New unit tests cover reading output that isn't UTF-8 and the zenity arguments; the two argument tests fail on master. With --no-default-features --features xdg-portal, cargo test, cargo clippy and cargo fmt --check pass on each commit with no warnings. The default features, --no-default-features --features gtk3 and --no-default-features --features gtk4 build. The tests also pass with Rust 1.89 (with --ignore-rust-version), so nothing needs more than the 1.92 MSRV.

I didn't run zenity 3; what this says about it comes from its 3.44.5 source. I also didn't test on a BSD.

zenity 4 prints the bytes of each selected path, and rfd decoded them as
UTF-8, so a file name that isn't UTF-8 made the dialog return `None`.
Keep the output as bytes and build the paths from them.
A cancelled zenity prints nothing and exits with 1. `pick_files` and
`pick_folders` turned that into an empty list, which the portal backend
returned as `Some(vec![])`. Return `None` instead, as the portal branch
does since PolyMeilex#323.
rfd passes each filter to zenity as `NAME | PATTERNS`. zenity ends the
name at the first `|` and splits the rest into patterns, with no way to
escape it, so a filter named "Text | Markdown" was shown as "Text" and
matched a file named `Markdown`. Show `|` in the name as `/`.

A lookalike such as `∣` (U+2223) would read better, but zenity converts
`--file-filter` arguments from the locale's charset, and in the C locale
a filter name that isn't ASCII makes zenity exit without showing the
dialog.
PolyMeilex#317 passed the starting directory as zenity's working directory. With
GTK's default file chooser settings, open and folder dialogs could start
on Recent instead; save dialogs already started in the working
directory.

Pass the directory with `--filename` instead, ending in `/`, or joined
with the name from `set_file_name`. zenity opens the folder of a
`--filename` path itself, and zenity 3 only does so for an absolute
path, so a relative directory is made absolute first. zenity 4 ignores
`/`, which is its own base name, so the root is passed as `/./`.

zenity is also no longer started in the directory, so one that doesn't
exist doesn't make starting zenity fail, which returned `None` without
showing a dialog. A `..` in the directory is now resolved by zenity
without following symlinks, like `cd ..` in a shell, where `chdir`
followed them.
@jay-tau
jay-tau force-pushed the zenity-fallback-fixes branch from 0f1e195 to 68f8a0d Compare October 19, 2026 14:10
@jay-tau jay-tau changed the title zenity: Fix paths, cancel results, filter names and the starting directory zenity: Fix non-UTF-8 paths, cancel results, filter names and the starting directory Oct 19, 2026
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.

zenity fallback: incorrect file dialog results, filter names and starting directory

1 participant