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

 main.py

View raw Download
text/plain • 5.91 kiB
Python script, ASCII text executable
        
            
1
"""
2
Implements the org.freedesktop.StatusNotifierWatcher and nothing else.
3
Copyright 2025, roundabout-host.com <vlad@roundabout-host.com>
4
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public Licence as published by
7
the Free Software Foundation, either version 3 of the Licence, or
8
(at your option) any later version.
9
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public Licence for more details.
14
15
You should have received a copy of the GNU General Public Licence
16
along with this program. If not, see <https://www.gnu.org/licenses/>.
17
"""
18
19
import gi
20
gi.require_version("Gtk", "4.0")
21
from gi.repository import GLib, Gio, GObject, Gtk
22
from pydbus import SessionBus
23
import pydbus.generic
24
25
26
class StatusNotifierWatcher:
27
StatusNotifierItemRegistered = pydbus.generic.signal()
28
StatusNotifierHostRegistered = pydbus.generic.signal()
29
StatusNotifierItemUnregistered = pydbus.generic.signal()
30
31
def __init__(self, bus):
32
super().__init__()
33
self.hosts = set()
34
self.items = set()
35
self.bus = bus
36
37
dbus_proxy = self.bus.get("org.freedesktop.DBus", "/org/freedesktop/DBus")
38
dbus_proxy.NameOwnerChanged.connect(self._on_name_owner_changed)
39
40
def _on_name_owner_changed(self, name, old_owner, new_owner):
41
if new_owner == "":
42
removed = False
43
for lookup in (name, old_owner):
44
if lookup in self.items:
45
self.items.discard(lookup)
46
self.StatusNotifierItemUnregistered(lookup)
47
if lookup in self.hosts:
48
self.hosts.discard(lookup)
49
50
def RegisterStatusNotifierItem(self, service_name):
51
self.items.add(service_name)
52
self.StatusNotifierItemRegistered(service_name)
53
54
def RegisterStatusNotifierHost(self, service_name):
55
self.hosts.add(service_name)
56
self.StatusNotifierHostRegistered(service_name)
57
58
@property
59
def RegisteredStatusNotifierItems(self):
60
return sorted(self.items)
61
62
@property
63
def IsStatusNotifierHostRegistered(self):
64
return bool(self.hosts)
65
66
@property
67
def ProtocolVersion(self):
68
return 0
69
70
71
class KDEStatusNotifierWatcher(StatusNotifierWatcher):
72
"""
73
<node>
74
<interface name="org.kde.StatusNotifierWatcher">
75
<method name="RegisterStatusNotifierItem">
76
<arg type="s" name="service_name" direction="in"/>
77
</method>
78
<method name="RegisterStatusNotifierHost">
79
<arg type="s" name="service_name" direction="in"/>
80
</method>
81
<property name="RegisteredStatusNotifierItems" type="as" access="read"/>
82
<property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
83
<property name="ProtocolVersion" type="i" access="read"/>
84
<signal name="StatusNotifierItemRegistered">
85
<arg type="s" name="service" direction="out"/>
86
</signal>
87
<signal name="StatusNotifierItemUnregistered">
88
<arg type="s" name="service" direction="out"/>
89
</signal>
90
<signal name="StatusNotifierHostRegistered">
91
<arg type="s" name="service" direction="out"/>
92
</signal>
93
</interface>
94
</node>
95
"""
96
97
98
class FDStatusNotifierWatcher(StatusNotifierWatcher):
99
"""
100
<node>
101
<interface name="org.freedesktop.StatusNotifierWatcher">
102
<method name="RegisterStatusNotifierItem">
103
<arg type="s" name="service_name" direction="in"/>
104
</method>
105
<method name="RegisterStatusNotifierHost">
106
<arg type="s" name="service_name" direction="in"/>
107
</method>
108
<property name="RegisteredStatusNotifierItems" type="as" access="read"/>
109
<property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
110
<property name="ProtocolVersion" type="i" access="read"/>
111
<signal name="StatusNotifierItemRegistered">
112
<arg type="s" name="service" direction="out"/>
113
</signal>
114
<signal name="StatusNotifierItemUnregistered">
115
<arg type="s" name="service" direction="out"/>
116
</signal>
117
<signal name="StatusNotifierHostRegistered">
118
<arg type="s" name="service" direction="out"/>
119
</signal>
120
</interface>
121
</node>
122
"""
123
124
125
class WatcherApplication(Gtk.Application):
126
def __init__(self):
127
super().__init__(application_id="com.roundabout_host.roundabout.PanoramaIndicatorService",
128
flags=Gio.ApplicationFlags.FLAGS_NONE)
129
self.bus = None
130
self.kde_publishing = None
131
self.fd_publishing = None
132
self.kde_watcher = None
133
self.fd_watcher = None
134
135
def do_startup(self):
136
Gtk.Application.do_startup(self)
137
self.bus = SessionBus()
138
self.kde_watcher = KDEStatusNotifierWatcher(self.bus)
139
self.fd_watcher = FDStatusNotifierWatcher(self.bus)
140
self.kde_publishing = self.bus.publish(
141
f"org.kde.StatusNotifierWatcher",
142
("/StatusNotifierWatcher", self.kde_watcher)
143
)
144
self.fd_publishing = self.bus.publish(
145
f"org.freedesktop.StatusNotifierWatcher",
146
("/StatusNotifierWatcher", self.fd_watcher)
147
)
148
149
def do_activate(self):
150
print("StatusNotifierWatcher running...")
151
self.hold()
152
153
def do_shutdown(self):
154
if self.bus:
155
if self.kde_publishing:
156
self.bus.unpublish(self.kde_publishing)
157
if self.fd_publishing:
158
self.bus.unpublish(self.fd_publishing)
159
print("Unregistered")
160
Gtk.Application.do_shutdown(self)
161
162
163
if __name__ == "__main__":
164
app = WatcherApplication()
165
app.run(None)
166