diff options
| author | Davi Ribeiro <104164579+Markus328@users.noreply.github.com> | 2025-08-23 07:21:35 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-23 20:21:35 +1000 |
| commit | f747d82b9336bb7c691d39a1f001c0d30d7bfb95 (patch) | |
| tree | 3456b6a9ed002382593e653a48a85b141edcc424 | |
| parent | dashboard: better pfp picker (diff) | |
| download | caelestia-shell-f747d82b9336bb7c691d39a1f001c0d30d7bfb95.tar.gz caelestia-shell-f747d82b9336bb7c691d39a1f001c0d30d7bfb95.tar.bz2 caelestia-shell-f747d82b9336bb7c691d39a1f001c0d30d7bfb95.zip | |
bar: add idle inhibitor (#459)
* bar: add idle inhibitor
* bar: change idle inhibitor button color
* nix: link external scripts instead of install
* services/idleinhibitor: add IPC handler
* better styling
* move cpp scripts to assets/cpp
---------
Co-authored-by: Soramane <61896496+soramanew@users.noreply.github.com>
| -rw-r--r-- | README.md | 4 | ||||
| -rwxr-xr-x | assets/cpp/beat-detector.cpp (renamed from assets/beat_detector.cpp) | 0 | ||||
| -rw-r--r-- | assets/cpp/idle-inhibitor.cpp | 182 | ||||
| -rw-r--r-- | config/BarConfig.qml | 4 | ||||
| -rw-r--r-- | flake.nix | 1 | ||||
| -rw-r--r-- | modules/bar/Bar.qml | 6 | ||||
| -rw-r--r-- | modules/bar/components/IdleInhibitor.qml | 33 | ||||
| -rw-r--r-- | nix/default.nix | 61 | ||||
| -rw-r--r-- | services/IdleInhibitor.qml | 36 |
9 files changed, 318 insertions, 9 deletions
@@ -237,6 +237,10 @@ All configuration options are in `~/.config/caelestia/shell.json`. { "id": "power", "enabled": true + }, + { + "id": "idleInhibitor", + "enabled": false } ], "persistent": true, diff --git a/assets/beat_detector.cpp b/assets/cpp/beat-detector.cpp index 4eb9b48..4eb9b48 100755 --- a/assets/beat_detector.cpp +++ b/assets/cpp/beat-detector.cpp diff --git a/assets/cpp/idle-inhibitor.cpp b/assets/cpp/idle-inhibitor.cpp new file mode 100644 index 0000000..9637b8c --- /dev/null +++ b/assets/cpp/idle-inhibitor.cpp @@ -0,0 +1,182 @@ +#include <csignal> +#include <cstring> +#include <iostream> +#include <unistd.h> +#include <wayland-client-protocol.h> +#include <wayland-client.h> + +// You'll need to generate these headers from the protocol XML files: +// wayland-scanner client-header < idle-inhibit-unstable-v1.xml > +// idle-inhibit-client-protocol.h wayland-scanner private-code < +// idle-inhibit-unstable-v1.xml > idle-inhibit-protocol.c +extern "C" { +#include "idle-inhibitor.h" +} + +class WaylandIdleInhibitor { +private: + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_surface *surface; + struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; + struct zwp_idle_inhibitor_v1 *idle_inhibitor; + + static WaylandIdleInhibitor *instance; + + // Registry listener to get global objects + static void registry_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t version) { + WaylandIdleInhibitor *inhibitor = static_cast<WaylandIdleInhibitor *>(data); + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + inhibitor->compositor = static_cast<struct wl_compositor *>( + wl_registry_bind(registry, id, &wl_compositor_interface, 1)); + } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == + 0) { + inhibitor->idle_inhibit_manager = + static_cast<struct zwp_idle_inhibit_manager_v1 *>(wl_registry_bind( + registry, id, &zwp_idle_inhibit_manager_v1_interface, 1)); + } + } + + static void registry_global_remove(void *data, struct wl_registry *registry, + uint32_t id) {} + + static const struct wl_registry_listener registry_listener; + +public: + WaylandIdleInhibitor() + : display(nullptr), registry(nullptr), compositor(nullptr), + surface(nullptr), idle_inhibit_manager(nullptr), + idle_inhibitor(nullptr) { + instance = this; + } + + ~WaylandIdleInhibitor() { cleanup(); } + + bool initialize() { + display = wl_display_connect(nullptr); + if (!display) { + return false; + } + + registry = wl_display_get_registry(display); + if (!registry) { + return false; + } + + wl_registry_add_listener(registry, ®istry_listener, this); + + // Roundtrip to get all globals + wl_display_roundtrip(display); + + if (!compositor || !idle_inhibit_manager) { + return false; + } + + return true; + } + + bool createInvisibleSurface() { + surface = wl_compositor_create_surface(compositor); + if (!surface) { + return false; + } + + return true; + } + + bool inhibitIdle() { + if (!surface || !idle_inhibit_manager) { + return false; + } + + idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( + idle_inhibit_manager, surface); + + if (!idle_inhibitor) { + std::cerr << "Failed to create idle inhibitor\n"; + return false; + } + + wl_display_roundtrip(display); + + std::cout << "Idle inhibition activated\n"; + return true; + } + + void cleanup() { + if (idle_inhibitor) { + zwp_idle_inhibitor_v1_destroy(idle_inhibitor); + idle_inhibitor = nullptr; + } + + if (surface) { + wl_surface_destroy(surface); + surface = nullptr; + } + + if (idle_inhibit_manager) { + zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager); + idle_inhibit_manager = nullptr; + } + + if (compositor) { + wl_compositor_destroy(compositor); + compositor = nullptr; + } + + if (registry) { + wl_registry_destroy(registry); + registry = nullptr; + } + + if (display) { + wl_display_disconnect(display); + display = nullptr; + } + } + + void run() { + while (wl_display_dispatch(display) != -1) + ; + } + + static WaylandIdleInhibitor *getInstance() { return instance; } +}; + +WaylandIdleInhibitor *WaylandIdleInhibitor::instance = nullptr; + +const struct wl_registry_listener WaylandIdleInhibitor::registry_listener = { + WaylandIdleInhibitor::registry_global, + WaylandIdleInhibitor::registry_global_remove}; + +void signalHandler(int signal) { + + WaylandIdleInhibitor *inhibitor = WaylandIdleInhibitor::getInstance(); + if (inhibitor) { + inhibitor->cleanup(); + } + + exit(0); +} + +int main() { + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + signal(SIGHUP, signalHandler); + + WaylandIdleInhibitor inhibitor; + + if (!(inhibitor.initialize() && inhibitor.createInvisibleSurface() && + inhibitor.inhibitIdle())) { + std::cerr << "Cannot inhibit idle!" << std::endl; + return 1; + } + + inhibitor.run(); + + return 0; +} diff --git a/config/BarConfig.qml b/config/BarConfig.qml index 4aaff8c..1733e47 100644 --- a/config/BarConfig.qml +++ b/config/BarConfig.qml @@ -46,6 +46,10 @@ JsonObject { id: "power", enabled: true }, + { + id: "idleInhibitor", + enabled: false + } ] component Workspaces: JsonObject { @@ -50,6 +50,7 @@ inputsFrom = [shell]; packages = with pkgs; [material-symbols rubik nerd-fonts.caskaydia-cove]; CAELESTIA_BD_PATH = "${shell}/bin/beat_detector"; + CAELESTIA_II_PATH = "${shell}/bin/inhibit_idle"; }; }); diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index 09cebee..518b72b 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -143,6 +143,12 @@ ColumnLayout { } } } + DelegateChoice { + roleValue: "idleInhibitor" + delegate: WrappedLoader { + sourceComponent: IdleInhibitor {} + } + } } } diff --git a/modules/bar/components/IdleInhibitor.qml b/modules/bar/components/IdleInhibitor.qml new file mode 100644 index 0000000..51e09ed --- /dev/null +++ b/modules/bar/components/IdleInhibitor.qml @@ -0,0 +1,33 @@ +import qs.components +import qs.services +import qs.config +import Quickshell +import QtQuick + +StyledRect { + id: root + + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.small * 2 + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, IdleInhibitor.enabled ? 1 : 0) + + StateLayer { + function onClicked(): void { + IdleInhibitor.enabled = !IdleInhibitor.enabled; + } + } + + MaterialIcon { + id: icon + + anchors.centerIn: parent + anchors.horizontalCenterOffset: -1 + + text: "coffee" + color: IdleInhibitor.enabled ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3secondary + font.bold: true + font.pointSize: Appearance.font.size.normal + } +} diff --git a/nix/default.nix b/nix/default.nix index e93f74b..6d0f3c8 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -30,6 +30,9 @@ quickshell, aubio, pipewire, + wayland, + wayland-protocols, + wayland-scanner, caelestia-cli, withCli ? false, extraRuntimeDeps ? [], @@ -61,15 +64,15 @@ fontconfig = makeFontsConf { fontDirectories = [material-symbols rubik nerd-fonts.caskaydia-cove]; }; -in - stdenv.mkDerivation { - pname = "caelestia-shell"; - version = "${rev}"; + + beatDetector = stdenv.mkDerivation { + pname = "beat-detector"; + version = "1.0"; + src = ./..; - nativeBuildInputs = [gcc makeWrapper qt6.wrapQtAppsHook]; - buildInputs = [quickshell aubio pipewire qt6.qtbase]; - propagatedBuildInputs = runtimeDeps; + nativeBuildInputs = [gcc]; + buildInputs = [aubio pipewire]; buildPhase = '' mkdir -p bin @@ -77,22 +80,62 @@ in -I${pipewire.dev}/include/pipewire-0.3 \ -I${pipewire.dev}/include/spa-0.2 \ -I${aubio}/include/aubio \ - assets/beat_detector.cpp \ + assets/cpp/beat-detector.cpp \ -o bin/beat_detector \ -lpipewire-0.3 -laubio ''; installPhase = '' install -Dm755 bin/beat_detector $out/bin/beat_detector + ''; + }; + + idleInhibitor = stdenv.mkDerivation { + pname = "wayland-idle-inhibitor"; + version = "1.0"; + + src = ./..; + + nativeBuildInputs = [gcc wayland-scanner wayland-protocols]; + buildInputs = [wayland]; + + buildPhase = '' + wayland-scanner client-header < ${wayland-protocols}/share/wayland-protocols/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml > idle-inhibitor.h + wayland-scanner private-code < ${wayland-protocols}/share/wayland-protocols/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml > idle-inhibitor.c + cp assets/cpp/idle-inhibitor.cpp . + gcc -o idle-inhibitor.o -c idle-inhibitor.c + g++ -o inhibit_idle idle-inhibitor.cpp idle-inhibitor.o -lwayland-client + ''; + + installPhase = '' + mkdir -p $out/bin + install -Dm755 inhibit_idle $out/bin/inhibit_idle + ''; + }; +in + stdenv.mkDerivation { + pname = "caelestia-shell"; + version = "${rev}"; + src = ./..; + + nativeBuildInputs = [gcc makeWrapper qt6.wrapQtAppsHook]; + buildInputs = [quickshell beatDetector idleInhibitor qt6.qtbase]; + propagatedBuildInputs = runtimeDeps; + + installPhase = '' mkdir -p $out/share/caelestia-shell cp -r ./* $out/share/caelestia-shell makeWrapper ${quickshell}/bin/qs $out/bin/caelestia-shell \ --prefix PATH : "${lib.makeBinPath runtimeDeps}" \ --set FONTCONFIG_FILE "${fontconfig}" \ - --set CAELESTIA_BD_PATH $out/bin/beat_detector \ + --set CAELESTIA_BD_PATH ${beatDetector}/bin/beat_detector \ + --set CAELESTIA_II_PATH ${idleInhibitor}/bin/inhibit_idle \ --add-flags "-p $out/share/caelestia-shell" + + ln -sf ${beatDetector}/bin/beat_detector $out/bin + ln -sf ${idleInhibitor}/bin/inhibit_idle $out/bin ''; meta = { diff --git a/services/IdleInhibitor.qml b/services/IdleInhibitor.qml new file mode 100644 index 0000000..5bad30b --- /dev/null +++ b/services/IdleInhibitor.qml @@ -0,0 +1,36 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + property bool enabled: false + + Process { + id: idleInhibitProc + running: root.enabled + command: [Quickshell.env("CAELESTIA_II_PATH") || "/usr/lib/caelestia/inhibit_idle"] + } + + IpcHandler { + target: "idleInhibitor" + + function isEnabled(): bool { + return root.enabled; + } + + function toggle(): void { + root.enabled = !root.enabled; + } + + function enable(): void { + root.enabled = true; + } + + function disable(): void { + root.enabled = false; + } + } +} |