From ff1c8ab917a8ed01277dbd81f0b0b16f4516eb74 Mon Sep 17 00:00:00 2001 From: Nathan Monfils Date: Mon, 4 Aug 2025 15:24:49 +0200 Subject: [PATCH 1/2] kitten desktop-ui portal.go: Compat with XDG spec 1. Make sure we coalesce XDG_DATA_HOME as per the spec On my machine /etc/profile.d/flatpak.sh sets XDG_DATA_DIRS=$HOME/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share but XDG_DATA_HOME is unset. 2. Update directory creation logic to find-or-create `enable_portal`'s current behavior is to first find a writable directory and write the portal definition to it, then fall back to creating a directory in the first available configuration directory. This is incorrect in the case where one of the locations in XDG_DATA_DIRS already has a directory, we should still prioritize XDG_DATA_HOME. --- kittens/desktop_ui/portal.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/kittens/desktop_ui/portal.go b/kittens/desktop_ui/portal.go index c9366627c..d8b16d42b 100644 --- a/kittens/desktop_ui/portal.go +++ b/kittens/desktop_ui/portal.go @@ -313,12 +313,24 @@ func show_settings(opts *ShowSettingsOptions) (err error) { } var DataDirs = sync.OnceValue(func() (ans []string) { - d := os.Getenv("XDG_DATA_DIRS") - if d == "" { - d = "/usr/local/share/:/usr/share/" + // $XDG_DATA_DIRS defines the preference-ordered set of base directories + // to search for data files **in addition to the $XDG_DATA_HOME** base + // directory. The directories in $XDG_DATA_DIRS should be separated with + // a colon ':'. + // https://specifications.freedesktop.org/basedir-spec/0.8/#variables + + data_dirs := os.Getenv("XDG_DATA_DIRS") + if data_dirs == "" { + data_dirs = "/usr/local/share/:/usr/share/" } - all := []string{os.Getenv("XDG_DATA_HOME")} - all = append(all, strings.Split(d, ":")...) + + data_home := os.Getenv("XDG_DATA_HOME") + if data_home == "" { + data_home = os.Getenv("HOME") + "/.local/share" + } + + all := []string{data_home} + all = append(all, strings.Split(data_dirs, ":")...) seen := map[string]bool{} for _, x := range all { if !seen[x] { @@ -381,21 +393,13 @@ func enable_portal() (err error) { } portals_dir := "" for _, x := range WritableDataDirs() { + // Find-or-create the first available xdg-desktop-portals/portals directory q := filepath.Join(x, "xdg-desktop-portal", "portals") - if unix.Access(q, unix.W_OK) == nil && IsDir(q) { + if (unix.Access(q, unix.W_OK) == nil && IsDir(q)) || (os.MkdirAll(q, 0o755) == nil) { portals_dir = q break } } - if portals_dir == "" { - for _, x := range WritableDataDirs() { - q := filepath.Join(x, "xdg-desktop-portal", "portals") - if err := os.MkdirAll(q, 0o755); err == nil { - portals_dir = q - break - } - } - } if portals_dir == "" { return fmt.Errorf("Could not find any writable portals directories. Make sure XDG_DATA_HOME is set and point to a directory for which you have write permission.") } From dd7fa6b25adf83453a0da3f705a468d2cb5e3a6d Mon Sep 17 00:00:00 2001 From: Nathan Monfils Date: Mon, 4 Aug 2025 15:43:10 +0200 Subject: [PATCH 2/2] kitten desktop-ui: Do not create an empty *-portals.conf file In the case where a portals.conf file does not exist in the user configuration, there is nothing to patch; we should default to defining the relevant portals for kitty. --- kittens/desktop_ui/portal.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kittens/desktop_ui/portal.go b/kittens/desktop_ui/portal.go index d8b16d42b..faf13d87b 100644 --- a/kittens/desktop_ui/portal.go +++ b/kittens/desktop_ui/portal.go @@ -363,6 +363,7 @@ var AllPortalInterfaces = sync.OnceValue(func() (ans []string) { func patch_portals_conf(text []byte) []byte { lines := []string{} in_preferred := false + patched := false for _, line := range utils.Splitlines(utils.UnsafeBytesToString(text)) { sl := strings.TrimSpace(line) if strings.HasPrefix(sl, "[") { @@ -371,6 +372,7 @@ func patch_portals_conf(text []byte) []byte { for _, iface := range AllPortalInterfaces() { lines = append(lines, iface+"=kitty") } + patched = true } else if in_preferred { remove := false for _, iface := range AllPortalInterfaces() { @@ -384,6 +386,15 @@ func patch_portals_conf(text []byte) []byte { } } } + + if !patched { + // the file was empty or did not contain a section + lines = append(lines, "[preferred]") + for _, iface := range AllPortalInterfaces() { + lines = append(lines, iface+"=kitty") + } + } + return utils.UnsafeStringToBytes(strings.Join(lines, "\n")) } @@ -458,6 +469,7 @@ Exec=%s desktop-ui run-server text := patch_portals_conf(text) if err = os.WriteFile(q, text, 0o644); err == nil { patched_file = q + fmt.Printf("Patched %s to use the kitty portals\n", patched_file) break } } @@ -470,8 +482,8 @@ Exec=%s desktop-ui run-server return err } patched_file = q + fmt.Printf("Created %s to use the kitty portals\n", patched_file) } - fmt.Printf("Patched %s to use the kitty portals\n", patched_file) return }