75 lines
2.6 KiB
Python
Executable File
75 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Opens the xdg-desktop-portal file chooser and prints the picked file's
|
|
# path. Quickshell has no usable file dialog of its own (QtQuick.Dialogs'
|
|
# fallback renders inside the calling window, i.e. the tiny bar popup), and
|
|
# no zenity/kdialog is installed — the portal goes through
|
|
# xdg-desktop-portal-gtk, which is.
|
|
#
|
|
# Usage: PickFile.py [title] [glob ...]
|
|
# Exits 0 and prints the path on a pick, 1 on cancel, 2 on error.
|
|
|
|
import os
|
|
import sys
|
|
from urllib.parse import unquote, urlparse
|
|
|
|
from gi.repository import Gio, GLib
|
|
|
|
title = sys.argv[1] if len(sys.argv) > 1 else "Open File"
|
|
globs = sys.argv[2:]
|
|
|
|
try:
|
|
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
|
except GLib.Error as e:
|
|
print(f"No session bus: {e.message}", file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
# The portal answers on a Request object whose path is derived from our
|
|
# unique bus name and handle_token, so subscribe to it before calling —
|
|
# otherwise a fast response could arrive before we're listening.
|
|
token = f"quickshell_{os.getpid()}"
|
|
sender = bus.get_unique_name().lstrip(":").replace(".", "_")
|
|
handle = f"/org/freedesktop/portal/desktop/request/{sender}/{token}"
|
|
|
|
loop = GLib.MainLoop()
|
|
result = {"code": 2, "uris": []}
|
|
|
|
|
|
def on_response(_conn, _sender, _path, _iface, _signal, params):
|
|
code, results = params.unpack()
|
|
result["code"] = code
|
|
result["uris"] = results.get("uris", [])
|
|
loop.quit()
|
|
|
|
|
|
bus.signal_subscribe("org.freedesktop.portal.Desktop", "org.freedesktop.portal.Request",
|
|
"Response", handle, None, Gio.DBusSignalFlags.NONE, on_response)
|
|
|
|
options = {
|
|
"handle_token": GLib.Variant("s", token),
|
|
"modal": GLib.Variant("b", True),
|
|
}
|
|
filters = []
|
|
if globs:
|
|
filters.append((", ".join(globs), [(0, g) for g in globs]))
|
|
filters.append(("All files", [(0, "*")]))
|
|
options["filters"] = GLib.Variant("a(sa(us))", filters)
|
|
downloads = os.path.expanduser("~/Downloads")
|
|
if os.path.isdir(downloads):
|
|
options["current_folder"] = GLib.Variant("ay", downloads.encode() + b"\0")
|
|
|
|
try:
|
|
bus.call_sync("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop",
|
|
"org.freedesktop.portal.FileChooser", "OpenFile",
|
|
GLib.Variant("(ssa{sv})", ("", title, options)),
|
|
GLib.VariantType("(o)"), Gio.DBusCallFlags.NONE, -1, None)
|
|
except GLib.Error as e:
|
|
print(f"File chooser portal unavailable: {e.message}", file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
loop.run()
|
|
|
|
# Response codes: 0 = picked, 1 = cancelled, 2 = other failure.
|
|
if result["code"] != 0 or not result["uris"]:
|
|
sys.exit(1 if result["code"] == 1 else 2)
|
|
print(unquote(urlparse(result["uris"][0]).path))
|