diff options
Diffstat (limited to 'overlays/waybar-systemd-inhibit')
-rw-r--r-- | overlays/waybar-systemd-inhibit/.envrc | 4 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/.gitignore | 2 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/default.nix | 35 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/pyproject.toml | 17 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/uv.lock | 102 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py | 0 | ||||
-rw-r--r-- | overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py | 117 |
7 files changed, 277 insertions, 0 deletions
diff --git a/overlays/waybar-systemd-inhibit/.envrc b/overlays/waybar-systemd-inhibit/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/overlays/waybar-systemd-inhibit/.gitignore b/overlays/waybar-systemd-inhibit/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/overlays/waybar-systemd-inhibit/default.nix b/overlays/waybar-systemd-inhibit/default.nix new file mode 100644 index 00000000..88322ef5 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/default.nix | |||
@@ -0,0 +1,35 @@ | |||
1 | { prev, final, flakeInputs, ... }: | ||
2 | |||
3 | with flakeInputs; | ||
4 | |||
5 | let | ||
6 | workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
7 | overlay = workspace.mkPyprojectOverlay { | ||
8 | sourcePreference = "wheel"; | ||
9 | }; | ||
10 | python = final.python312; | ||
11 | # hacks = final.callPackage pyproject-nix.build.hacks { }; | ||
12 | pythonSet = | ||
13 | (final.callPackage pyproject-nix.build.packages { | ||
14 | inherit python; | ||
15 | }).overrideScope | ||
16 | ( | ||
17 | prev.lib.composeManyExtensions [ | ||
18 | pyproject-build-systems.overlays.default | ||
19 | overlay | ||
20 | # (final: prev: { | ||
21 | # pygobject = hacks.nixpkgsPrebuilt { | ||
22 | # from = python.pkgs.pygobject3; | ||
23 | # }; | ||
24 | # }) | ||
25 | ] | ||
26 | ); | ||
27 | virtualEnv = pythonSet.mkVirtualEnv "waybar-systemd-inhibit-env" workspace.deps.default; | ||
28 | in { | ||
29 | waybar-systemd-inhibit = virtualEnv.overrideAttrs (oldAttrs: { | ||
30 | meta = (oldAttrs.meta or {}) // { | ||
31 | mainProgram = "waybar-systemd-inhibit"; | ||
32 | }; | ||
33 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ final.gobject-introspection final.wrapGAppsHook ]; | ||
34 | }); | ||
35 | } | ||
diff --git a/overlays/waybar-systemd-inhibit/pyproject.toml b/overlays/waybar-systemd-inhibit/pyproject.toml new file mode 100644 index 00000000..6c6240b8 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/pyproject.toml | |||
@@ -0,0 +1,17 @@ | |||
1 | [project] | ||
2 | name = "waybar-systemd-inhibit" | ||
3 | version = "0.1.0" | ||
4 | requires-python = ">=3.12" | ||
5 | dependencies = [ | ||
6 | "asyncclick>=8.1.8", | ||
7 | "asyncio>=3.4.3", | ||
8 | "dbus-next>=0.2.3", | ||
9 | ] | ||
10 | |||
11 | [project.scripts] | ||
12 | waybar-systemd-inhibit = "waybar_systemd_inhibit.__main__:main" | ||
13 | waybar-systemd-inhibit-toggle = "waybar_systemd_inhibit.__main__:toggle" | ||
14 | |||
15 | [build-system] | ||
16 | requires = ["hatchling"] | ||
17 | build-backend = "hatchling.build" | ||
diff --git a/overlays/waybar-systemd-inhibit/uv.lock b/overlays/waybar-systemd-inhibit/uv.lock new file mode 100644 index 00000000..4e10d145 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/uv.lock | |||
@@ -0,0 +1,102 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12" | ||
4 | |||
5 | [[package]] | ||
6 | name = "anyio" | ||
7 | version = "4.9.0" | ||
8 | source = { registry = "https://pypi.org/simple" } | ||
9 | dependencies = [ | ||
10 | { name = "idna" }, | ||
11 | { name = "sniffio" }, | ||
12 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
13 | ] | ||
14 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } | ||
15 | wheels = [ | ||
16 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, | ||
17 | ] | ||
18 | |||
19 | [[package]] | ||
20 | name = "asyncclick" | ||
21 | version = "8.1.8" | ||
22 | source = { registry = "https://pypi.org/simple" } | ||
23 | dependencies = [ | ||
24 | { name = "anyio" }, | ||
25 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
26 | ] | ||
27 | sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/e1e5fdf1c1bb7e6e614987c120a98d9324bf8edfaa5f5cd16a6235c9d91b/asyncclick-8.1.8.tar.gz", hash = "sha256:0f0eb0f280e04919d67cf71b9fcdfb4db2d9ff7203669c40284485c149578e4c", size = 232900, upload-time = "2025-01-06T09:46:52.694Z" } | ||
28 | wheels = [ | ||
29 | { url = "https://files.pythonhosted.org/packages/14/cc/a436f0fc2d04e57a0697e0f87a03b9eaed03ad043d2d5f887f8eebcec95f/asyncclick-8.1.8-py3-none-any.whl", hash = "sha256:eb1ccb44bc767f8f0695d592c7806fdf5bd575605b4ee246ffd5fadbcfdbd7c6", size = 99093, upload-time = "2025-01-06T09:46:51.046Z" }, | ||
30 | { url = "https://files.pythonhosted.org/packages/92/c4/ae9e9d25522c6dc96ff167903880a0fe94d7bd31ed999198ee5017d977ed/asyncclick-8.1.8.0-py3-none-any.whl", hash = "sha256:be146a2d8075d4fe372ff4e877f23c8b5af269d16705c1948123b9415f6fd678", size = 99115, upload-time = "2025-01-06T09:50:52.72Z" }, | ||
31 | ] | ||
32 | |||
33 | [[package]] | ||
34 | name = "asyncio" | ||
35 | version = "3.4.3" | ||
36 | source = { registry = "https://pypi.org/simple" } | ||
37 | sdist = { url = "https://files.pythonhosted.org/packages/da/54/054bafaf2c0fb8473d423743e191fcdf49b2c1fd5e9af3524efbe097bafd/asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", size = 204411, upload-time = "2015-03-10T14:11:26.494Z" } | ||
38 | wheels = [ | ||
39 | { url = "https://files.pythonhosted.org/packages/22/74/07679c5b9f98a7cb0fc147b1ef1cc1853bc07a4eb9cb5731e24732c5f773/asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d", size = 101767, upload-time = "2015-03-10T14:05:10.959Z" }, | ||
40 | ] | ||
41 | |||
42 | [[package]] | ||
43 | name = "colorama" | ||
44 | version = "0.4.6" | ||
45 | source = { registry = "https://pypi.org/simple" } | ||
46 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
47 | wheels = [ | ||
48 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
49 | ] | ||
50 | |||
51 | [[package]] | ||
52 | name = "dbus-next" | ||
53 | version = "0.2.3" | ||
54 | source = { registry = "https://pypi.org/simple" } | ||
55 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
56 | wheels = [ | ||
57 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
58 | ] | ||
59 | |||
60 | [[package]] | ||
61 | name = "idna" | ||
62 | version = "3.10" | ||
63 | source = { registry = "https://pypi.org/simple" } | ||
64 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
65 | wheels = [ | ||
66 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
67 | ] | ||
68 | |||
69 | [[package]] | ||
70 | name = "sniffio" | ||
71 | version = "1.3.1" | ||
72 | source = { registry = "https://pypi.org/simple" } | ||
73 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } | ||
74 | wheels = [ | ||
75 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, | ||
76 | ] | ||
77 | |||
78 | [[package]] | ||
79 | name = "typing-extensions" | ||
80 | version = "4.13.2" | ||
81 | source = { registry = "https://pypi.org/simple" } | ||
82 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
83 | wheels = [ | ||
84 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
85 | ] | ||
86 | |||
87 | [[package]] | ||
88 | name = "waybar-systemd-inhibit" | ||
89 | version = "0.1.0" | ||
90 | source = { editable = "." } | ||
91 | dependencies = [ | ||
92 | { name = "asyncclick" }, | ||
93 | { name = "asyncio" }, | ||
94 | { name = "dbus-next" }, | ||
95 | ] | ||
96 | |||
97 | [package.metadata] | ||
98 | requires-dist = [ | ||
99 | { name = "asyncclick", specifier = ">=8.1.8" }, | ||
100 | { name = "asyncio", specifier = ">=3.4.3" }, | ||
101 | { name = "dbus-next", specifier = ">=0.2.3" }, | ||
102 | ] | ||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py | |||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py new file mode 100644 index 00000000..35cc7fd1 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py | |||
@@ -0,0 +1,117 @@ | |||
1 | import asyncclick as click | ||
2 | from dbus_next.aio import MessageBus | ||
3 | from dbus_next import BusType, Message, PropertyAccess | ||
4 | import asyncio | ||
5 | from functools import update_wrapper | ||
6 | from dbus_next.service import ServiceInterface, method, dbus_property | ||
7 | from dbus_next import Variant, DBusError | ||
8 | import os | ||
9 | import json | ||
10 | |||
11 | class BlockInterface(ServiceInterface): | ||
12 | def __init__(self, system_bus, logind): | ||
13 | super().__init__('li.yggdrasil.WaybarSystemdInhibit') | ||
14 | self.system_bus = system_bus | ||
15 | self.logind = logind | ||
16 | self.fd = None | ||
17 | |||
18 | def Release(self): | ||
19 | if not self.fd: | ||
20 | return | ||
21 | |||
22 | os.close(self.fd) | ||
23 | self.fd = None | ||
24 | self.emit_properties_changed({'IsAcquired': False}) | ||
25 | |||
26 | async def Acquire(self): | ||
27 | if self.fd: | ||
28 | return | ||
29 | |||
30 | res = await self.system_bus.call(Message( | ||
31 | destination='org.freedesktop.login1', | ||
32 | path='/org/freedesktop/login1', | ||
33 | interface='org.freedesktop.login1.Manager', | ||
34 | member='Inhibit', | ||
35 | signature='ssss', | ||
36 | body=[ | ||
37 | "handle-lid-switch", | ||
38 | "waybar-systemd-inhibit", | ||
39 | "User request", | ||
40 | "block", | ||
41 | ], | ||
42 | )) | ||
43 | self.fd = res.unix_fds[res.body[0]] | ||
44 | self.emit_properties_changed({'IsAcquired': True}) | ||
45 | |||
46 | @method() | ||
47 | async def ToggleBlock(self): | ||
48 | if self.fd: | ||
49 | self.Release() | ||
50 | else: | ||
51 | await self.Acquire() | ||
52 | |||
53 | @dbus_property(access=PropertyAccess.READ) | ||
54 | def IsAcquired(self) -> 'b': | ||
55 | return self.fd is not None | ||
56 | |||
57 | |||
58 | @click.command() | ||
59 | async def main(): | ||
60 | system_bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect() | ||
61 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
62 | |||
63 | introspection = await system_bus.introspect('org.freedesktop.login1', '/org/freedesktop/login1') | ||
64 | obj = system_bus.get_proxy_object('org.freedesktop.login1', '/org/freedesktop/login1', introspection) | ||
65 | logind = obj.get_interface('org.freedesktop.login1.Manager') | ||
66 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
67 | |||
68 | def is_blocked_logind(what: str): | ||
69 | return "handle-lid-switch" in what.split(':') | ||
70 | |||
71 | def print_state(is_blocked: bool, is_acquired: bool = False): | ||
72 | icon = "󰌢" if is_blocked else "󰛧" | ||
73 | text = f"<span font=\"Symbols Nerd Font Mono\">{icon}</span>" | ||
74 | if is_acquired: | ||
75 | text = f"<span color=\"#f28a21\">{text}</span>" | ||
76 | elif is_blocked: | ||
77 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
78 | print(json.dumps({'text': text, 'tooltip': ("Manually inhibited" if is_acquired else None)}, separators=(',', ':')), flush=True) | ||
79 | |||
80 | print_state(is_blocked_logind(await logind.get_block_inhibited())) | ||
81 | |||
82 | async def get_inhibit(): | ||
83 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
84 | return session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
85 | |||
86 | async def on_logind_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
87 | if 'BlockInhibited' not in changed_properties: | ||
88 | return | ||
89 | |||
90 | properties = (await get_inhibit()).get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
91 | |||
92 | print_state(is_blocked_logind(changed_properties['BlockInhibited'].value), await properties.get_is_acquired()) | ||
93 | |||
94 | properties.on_properties_changed(on_logind_properties_changed) | ||
95 | |||
96 | session_bus.export('/li/yggdrasil/WaybarSystemdInhibit', BlockInterface(system_bus, logind)) | ||
97 | await session_bus.request_name('li.yggdrasil.WaybarSystemdInhibit') | ||
98 | |||
99 | properties = (await get_inhibit()).get_interface('org.freedesktop.DBus.Properties') | ||
100 | |||
101 | async def on_inhibit_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
102 | if 'IsAcquired' not in changed_properties: | ||
103 | return | ||
104 | |||
105 | print_state(is_blocked_logind(await logind.get_block_inhibited()), changed_properties['IsAcquired'].value) | ||
106 | |||
107 | properties.on_properties_changed(on_inhibit_properties_changed) | ||
108 | |||
109 | await session_bus.wait_for_disconnect() | ||
110 | |||
111 | @click.command() | ||
112 | async def toggle(): | ||
113 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
114 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
115 | obj = session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
116 | interface = obj.get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
117 | await interface.call_toggle_block() | ||