By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

introduce indicators

roundabout,
created on Saturday, 18 October 2025, 10:27:19 (1760783239), received on Saturday, 18 October 2025, 10:27:22 (1760783242)
Author identity: Vlad <vlad.muntoiu@gmail.com>

531b5fb8e89f1288ea5bbde374ce1372235aa578

applets/indicators/__init__.py

@@ -0,0 +1,204 @@

                                
                                
                                
                            
                                
                                    
                                        
                                        #!/usr/bin/env python3
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        D-Bus app indicator/system tray for the Panorama panel.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Copyright 2025, roundabout-host.com <vlad@roundabout-host.com>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        This program is free software: you can redistribute it and/or modify
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        it under the terms of the GNU General Public Licence as published by
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        the Free Software Foundation, either version 3 of the Licence, or
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        (at your option) any later version.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        This program is distributed in the hope that it will be useful,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        but WITHOUT ANY WARRANTY; without even the implied warranty of
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        GNU General Public Licence for more details.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        You should have received a copy of the GNU General Public Licence
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        along with this program. If not, see <https://www.gnu.org/licenses/>.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from __future__ import annotations
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import os
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import traceback
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import uuid
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import locale
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from pathlib import Path
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import gi
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from ctypes import CDLL
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        CDLL("libdbusmenu-glib.so")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        gi.require_version("Dbusmenu", "0.4")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import panorama_panel
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from gi.repository import Gtk, GLib, Gio
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from . import dbusmenu
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        module_directory = Path(__file__).resolve().parent
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        locale.bindtextdomain("panorama-indicator-applet", module_directory / "locale")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        _ = lambda x: locale.dgettext("panorama-indicator-applet", x)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class StatusNotifierService:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            <node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                              <interface name="org.kde.StatusNotifierHost">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                              </interface>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            </node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self, applet: IndicatorApplet):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.applet = applet
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.xml = """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                <node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <interface name="org.kde.StatusNotifierHost">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  </interface>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                </node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.node_info = Gio.DBusNodeInfo.new_for_xml(self.xml)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.iface_info = self.node_info.interfaces[0]
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def export(self, connection: Gio.DBusConnection):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                connection.register_object(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    "/StatusNotifierHost",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.iface_info,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.handle_method_call,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def handle_method_call(self, conn, sender, obj_path, iface_name, method, params, invocation):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                # It has no methods
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                invocation.return_value(None)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class StatusIcon(Gtk.MenuButton):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self, applet: IndicatorApplet, service: str):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                super().__init__()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.applet = applet
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.service = service
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                label = Gtk.Label(label=service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.set_child(label)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                print(self.service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.popover_menu = Gtk.PopoverMenu(flags=Gtk.PopoverMenuFlags.NESTED)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.set_popover(self.popover_menu)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                connection, _0, path = self.service.rpartition(":")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.menu = None
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                try:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    proxy = Gio.DBusProxy.new_sync(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.applet.connection,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        Gio.DBusProxyFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        connection,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        path,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "org.kde.StatusNotifierItem",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.applet.item_id += 1
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    icon_name = proxy.get_cached_property("IconName")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    if icon_name:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        icon_name = icon_name.unpack()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.icon = Gtk.Image.new_from_icon_name(icon_name)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.set_child(self.icon)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    menu = proxy.get_cached_property("Menu")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    if menu:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.menu = dbusmenu.CanonicalMenuModel()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.menu.connect_dbus(connection, menu.get_string(), f"ac{self.applet.item_id}")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.insert_action_group(f"ac{self.applet.item_id}", self.menu.actions)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.popover_menu.set_menu_model(self.menu.menu)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                except Exception as e:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    print(f"Could not get properties from {service}: {e}")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    traceback.print_exc()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class IndicatorApplet(panorama_panel.Applet):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            name = _("Indicators")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            description = _("Show application status icons")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                super().__init__(orientation=orientation, config=config or {})
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.items = {}
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.item_id = 0
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.connection = Gio.bus_get_sync(Gio.BusType.SESSION)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                # Make the host name unique
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.bus_name = f"org.kde.StatusNotifierHost-{os.getpid()}-pan-{uuid.uuid4()}"
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.service = StatusNotifierService(self)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.service.export(self.connection)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                Gio.bus_own_name(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    Gio.BusType.SESSION,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.bus_name,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    Gio.BusNameOwnerFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                try:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.watcher = Gio.DBusProxy.new_sync(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.connection,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        Gio.DBusProxyFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "org.kde.StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "/StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "org.kde.StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                except Exception as e:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    print("Could not connect to StatusNotifierWatcher:", e)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.watcher = None
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if self.watcher:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    try:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.watcher.call_sync(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            "RegisterStatusNotifierHost",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            GLib.Variant("(s)", (self.bus_name,)),
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            Gio.DBusCallFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            -1,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    except Exception as e:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        print("Failed to register host:", e)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    # Watch for new items
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.connection.signal_subscribe(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "org.kde.StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "StatusNotifierItemRegistered",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "/StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        Gio.DBusSignalFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.on_item_registered_signal,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    # Watch for vanishing items
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.connection.signal_subscribe(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "org.kde.StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "StatusNotifierItemUnregistered",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        "/StatusNotifierWatcher",
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        None,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        Gio.DBusSignalFlags.NONE,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.on_item_unregistered_signal,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    # TODO: watch for things such as icon changes.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def on_item_registered_signal(self, conn, sender_name, obj_path, iface_name, signal_name, params):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                (service,) = params.unpack()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.on_item_registered(service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def on_item_unregistered_signal(self, conn, sender_name, obj_path, iface_name, signal_name, params):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                (service,) = params.unpack()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.on_item_unregistered(service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def on_item_registered(self, service):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                icon = StatusIcon(self, service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.append(icon)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.items[service] = icon
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def on_item_unregistered(self, service):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if service in self.items:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.remove(self.items[service])
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    del self.items[service]
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                            
                                

applets/indicators/dbusmenu.py

@@ -0,0 +1,151 @@

                                
                                
                                
                            
                                
                                    
                                        
                                        #!/usr/bin/env python3
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Translates the com.canonical.dbusmenu protocol to Gio menus.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Based on a C++ implementation.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Thanks to https://github.com/WayfireWM/wf-shell/blob/master/src/panel/widgets/tray/item.hpp
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        for the C++ version which is Copyright 2025, trigg <https://github.com/trigg>,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        MIT/Expat Licence.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Python version: Copyright 2025, roundabout-host.com <vlad@roundabout-host.com>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        This file (and no other files, unless otherwise stated) is licensed under the
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        MIT/Expat Licence:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Permission is hereby granted, free of charge, to any person obtaining a copy
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        of this software and associated documentation files (the "Software"), to deal
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        in the Software without restriction, including without limitation the rights
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        copies of the Software, and to permit persons to whom the Software is
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        furnished to do so, subject to the following conditions:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        The above copyright notice and this permission notice shall be included in all
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        copies or substantial portions of the Software.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        SOFTWARE.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import typing
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import uuid
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from gi.repository import GLib, Gio, GObject, Dbusmenu
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class CanonicalMenuModel(GObject.Object):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """Implements com.canonical.dbusmenu protocol.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            Provides a `menu` attribute to get a Gio.Menu for the specified DBus object, and an
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            `actions` attribute that is a Gio.ActionGroup with the menu actions to insert where needed.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """Generate a disconnected instance."""
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                super().__init__()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.prefix = None
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.client = None
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.prefix = ""
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.actions = Gio.SimpleActionGroup()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.menu: typing.Optional[Gio.Menu] = Gio.Menu()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def connect_dbus(self, dbus_name: str, menu_path: str, prefix: str) -> None:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """Connect this menu with the specified DBus object.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                :param dbus_name: The bus name to use, like ":1.360" or "org.gnome.Fractal"
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                :param menu_path: The DBus path to the menu. It can be found in the Menu attribute of an
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                org.kde.StatusNotifierItem.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                :param prefix: The prefix to use for action names; use this name for inserting the
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                action group.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.prefix = prefix
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.client = Dbusmenu.Client.new(dbus_name, menu_path)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.client.connect("layout-updated", self._layout_updated)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                root = self.client.get_root()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.reconstitute(root)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def reconstitute(self, root_item: Dbusmenu.Menuitem):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """Regenerate the menu.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                :param root_item: The item to recursively generate from.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.menu.remove_all()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                for action_name in self.actions.list_actions():
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.actions.remove_action(action_name)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self._iterate_children(self.menu, root_item, 0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def _iterate_children(self, parent_menu, parent, count):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if isinstance(parent, Dbusmenu.Menuitem):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    current_section = Gio.Menu()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    for child in parent.get_children():
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        label = child.property_get("label")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        if not label:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            parent_menu.append_section(None, current_section)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            current_section = Gio.Menu()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            continue
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        menu_type = ""
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        icon_name = ""
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        toggle_type = ""
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        if child.property_exist("type"):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            menu_type = child.property_get("type")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        enabled = child.property_get_bool("enabled")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        if child.property_exist("icon-name"):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            icon_name = child.property_get("icon-name")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        toggle_state = child.property_get_int("toggle-state")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        if child.property_exist("toggle-type"):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            icon_name = child.property_get("toggle-type")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        has_children = bool(child.get_children())
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        if has_children:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            submenu = Gio.Menu()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            count = self._iterate_children(submenu, child, count)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            current_section.append_submenu(label, submenu)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        else:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            item = Gio.MenuItem.new(label, "a")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            if enabled:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                # Difference from C++ version: we don't use the label, to make it easier
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                # to translate
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                action_name = f"mi{count}"
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                action_string = f"{self.prefix}.{action_name}"
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                if toggle_type == "radio":
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    item.set_action_and_target_value(action_string, GLib.Variant.new_string(action_name))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    boolean_action = Gio.SimpleAction(name=action_name, state=GLib.Variant.new_string(action_name if toggle_state else ""))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    # A default argument is needed, otherwise the last item will be used
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    def on_activate(action, vb, item=child):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        data = GLib.Variant.new_int32(0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        item.handle_event("clicked", data, 0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    boolean_action.connect("activate", on_activate)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    self.actions.add_action(boolean_action)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                elif toggle_type == "check":
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    item.set_action_and_target_value(action_string, GLib.Variant.new_boolean(toggle_state))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    boolean_action = Gio.SimpleAction(name=action_name, state=GLib.Variant.new_boolean(toggle_state))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    def on_activate(action, vb, item=child):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        data = GLib.Variant.new_int32(0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        item.handle_event("clicked", data, 0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    boolean_action.connect("activate", on_activate)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    self.actions.add_action(boolean_action)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                else:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    item.set_detailed_action(action_string)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    action = Gio.SimpleAction(name=action_name)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    def on_activate(action, vb, item=child):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        data = GLib.Variant.new_int32(0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                        item.handle_event("clicked", data, 0)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    action.connect("activate", on_activate)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                                    self.actions.add_action(action)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                            current_section.append_item(item)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        count += 1
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    parent_menu.append_section(None, current_section)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                return count
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def _layout_updated(self, item):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if self.client is None:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    return
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                root = self.client.get_root()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.reconstitute(root)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                            
                                

config.yaml

@@ -69,6 +69,7 @@ panels:

                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                    icon_size: 24
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                    scroll_direction: 1
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                    volume_step: 0.05
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            - IndicatorApplet: {}
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                - ClockApplet:
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                    formatting: '%T, %a %-d %b %Y'
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            - position: bottom
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                            
                                

main.py

@@ -47,7 +47,7 @@ faulthandler.enable()

                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            os.environ["GI_TYPELIB_PATH"] = "/usr/local/lib/x86_64-linux-gnu/girepository-1.0"
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            from ctypes import CDLL
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        CDLL('libgtk4-layer-shell.so')
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        CDLL("libgtk4-layer-shell.so")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            import gi
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                            gi.require_version("Gtk", "4.0")
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        

@@ -638,10 +638,13 @@ def load_packages_from_dir(dir_path: Path):

                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            module_name = path.name
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            spec = importlib.util.spec_from_file_location(module_name, path / "__init__.py")
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            module = importlib.util.module_from_spec(spec)
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        module.__package__ = module_name
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        sys.modules[module_name] = module
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            spec.loader.exec_module(module)
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            loaded_modules.append(module)
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                        except Exception as e:
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                            print(f"Failed to load applet module {path.name}: {e}", file=sys.stderr)
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        traceback.print_exc()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                    else:
                                        
                                        
                                            
                                            
                                            
                                            
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            
                                                        continue