summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavi Ribeiro <104164579+Markus328@users.noreply.github.com>2025-08-23 07:21:35 -0300
committerGitHub <noreply@github.com>2025-08-23 20:21:35 +1000
commitf747d82b9336bb7c691d39a1f001c0d30d7bfb95 (patch)
tree3456b6a9ed002382593e653a48a85b141edcc424
parentdashboard: better pfp picker (diff)
downloadcaelestia-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.md4
-rwxr-xr-xassets/cpp/beat-detector.cpp (renamed from assets/beat_detector.cpp)0
-rw-r--r--assets/cpp/idle-inhibitor.cpp182
-rw-r--r--config/BarConfig.qml4
-rw-r--r--flake.nix1
-rw-r--r--modules/bar/Bar.qml6
-rw-r--r--modules/bar/components/IdleInhibitor.qml33
-rw-r--r--nix/default.nix61
-rw-r--r--services/IdleInhibitor.qml36
9 files changed, 318 insertions, 9 deletions
diff --git a/README.md b/README.md
index 3be0a67..d5e4025 100644
--- a/README.md
+++ b/README.md
@@ -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, &registry_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 {
diff --git a/flake.nix b/flake.nix
index 31172f8..4e18178 100644
--- a/flake.nix
+++ b/flake.nix
@@ -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;
+ }
+ }
+}