| 1 | #!/usr/bin/env python3 |
| 2 | # org.freedesktop.FileManager1 for lf: browsers' "Show in Folder", Electron apps, portals... call |
| 3 | # ShowItems/ShowFolders over D-Bus. Each opens lf in foot (a file = its folder with the file selected). |
| 4 | # D-Bus activated via ~/.local/share/dbus-1/services/org.freedesktop.FileManager1.service. |
| 5 | import os |
| 6 | import subprocess |
| 7 | from urllib.parse import unquote, urlparse |
| 8 | |
| 9 | from gi.repository import Gio, GLib |
| 10 | |
| 11 | XML = """ |
| 12 | <node> |
| 13 | <interface name="org.freedesktop.FileManager1"> |
| 14 | <method name="ShowFolders"><arg type="as" name="URIs" direction="in"/><arg type="s" name="StartupId" direction="in"/></method> |
| 15 | <method name="ShowItems"><arg type="as" name="URIs" direction="in"/><arg type="s" name="StartupId" direction="in"/></method> |
| 16 | <method name="ShowItemProperties"><arg type="as" name="URIs" direction="in"/><arg type="s" name="StartupId" direction="in"/></method> |
| 17 | </interface> |
| 18 | </node> |
| 19 | """ |
| 20 | |
| 21 | |
| 22 | def path_of(uri): |
| 23 | if uri.startswith("file://"): |
| 24 | return unquote(urlparse(uri).path) |
| 25 | return uri if uri.startswith("/") else None |
| 26 | |
| 27 | |
| 28 | def lf(path): |
| 29 | subprocess.Popen(["foot", "-e", "lf", path], start_new_session=True, |
| 30 | stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
| 31 | |
| 32 | |
| 33 | def call(conn, sender, obj, iface, method, params, inv): |
| 34 | uris, _startup = params.unpack() |
| 35 | # one window per folder: ShowItems of several files in the same folder opens it once |
| 36 | seen = set() |
| 37 | for uri in uris: |
| 38 | p = path_of(uri) |
| 39 | if not p or not os.path.exists(p): |
| 40 | continue |
| 41 | key = p if method == "ShowFolders" else os.path.dirname(p) |
| 42 | if key in seen: |
| 43 | continue |
| 44 | seen.add(key) |
| 45 | lf(p) |
| 46 | inv.return_value(None) |
| 47 | |
| 48 | |
| 49 | def acquired(conn, name): |
| 50 | node = Gio.DBusNodeInfo.new_for_xml(XML) |
| 51 | conn.register_object("/org/freedesktop/FileManager1", node.interfaces[0], call, None, None) |
| 52 | |
| 53 | |
| 54 | loop = GLib.MainLoop() |
| 55 | Gio.bus_own_name(Gio.BusType.SESSION, "org.freedesktop.FileManager1", Gio.BusNameOwnerFlags.NONE, |
| 56 | acquired, None, lambda *a: loop.quit()) |
| 57 | loop.run() |