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>
This commit is contained in:
parent
b10c9a8006
commit
f747d82b93
9 changed files with 318 additions and 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,
|
||||
|
|
|
|||
182
assets/cpp/idle-inhibitor.cpp
Normal file
182
assets/cpp/idle-inhibitor.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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";
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,12 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "idleInhibitor"
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: IdleInhibitor {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
33
modules/bar/components/IdleInhibitor.qml
Normal file
33
modules/bar/components/IdleInhibitor.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {
|
||||
|
|
|
|||
36
services/IdleInhibitor.qml
Normal file
36
services/IdleInhibitor.qml
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue