Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
X11:Wayland
hypridle
hypridle-0.1.2.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File hypridle-0.1.2.obscpio of Package hypridle
07070100000000000081A4000000000000000000000001662183E20000070E000000000000000000000000000000000000001D00000000hypridle-0.1.2/.clang-format--- Language: Cpp BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveMacros: true AlignConsecutiveAssignments: true AlignEscapedNewlines: Right AlignOperands: false AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BreakBeforeBraces: Attach BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon ColumnLimit: 180 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false IncludeBlocks: Preserve IndentCaseLabels: true IndentWidth: 4 PointerAlignment: Left ReflowComments: false SortIncludes: false SortUsingDeclarations: false SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 4 UseTab: Never AllowShortEnumsOnASingleLine: false BraceWrapping: AfterEnum: false AlignConsecutiveDeclarations: AcrossEmptyLines NamespaceIndentation: All 07070100000001000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001700000000hypridle-0.1.2/.github07070100000002000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000002100000000hypridle-0.1.2/.github/workflows07070100000003000081A4000000000000000000000001662183E2000001EE000000000000000000000000000000000000002900000000hypridle-0.1.2/.github/workflows/nix.ymlname: Build on: [push, pull_request, workflow_dispatch] jobs: nix: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main # not needed (yet) # - uses: cachix/cachix-action@v12 # with: # name: hyprland # authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build run: nix flake check --print-build-logs --keep-going 07070100000004000081A4000000000000000000000001662183E20000001A000000000000000000000000000000000000001A00000000hypridle-0.1.2/.gitignore.vscode/ build/ protocols/07070100000005000081A4000000000000000000000001662183E200000B41000000000000000000000000000000000000001E00000000hypridle-0.1.2/CMakeLists.txtcmake_minimum_required(VERSION 3.19) set(VERSION 0.1.2) project(hypridle DESCRIPTION "An idle management daemon for Hyprland" VERSION ${VERSION} ) set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring hypridle in Debug with CMake") add_compile_definitions(HYPRLAND_DEBUG) else() add_compile_options(-O3) message(STATUS "Configuring hypridle in Release with CMake") endif() include_directories( . "protocols/" ) # configure set(CMAKE_CXX_STANDARD 23) add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing) configure_file(systemd/hypridle.service.in systemd/hypridle.service @ONLY) # dependencies message(STATUS "Checking deps...") find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols hyprlang>=0.4.0 sdbus-c++) file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") add_executable(hypridle ${SRCFILES}) target_link_libraries(hypridle PRIVATE rt Threads::Threads PkgConfig::deps) # protocols find_program(WaylandScanner NAMES wayland-scanner) message(STATUS "Found WaylandScanner at ${WaylandScanner}") execute_process( COMMAND pkg-config --variable=pkgdatadir wayland-protocols WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") function(protocol protoPath protoName external) if (external) execute_process( COMMAND ${WaylandScanner} client-header ${protoPath} protocols/${protoName}-protocol.h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) execute_process( COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) target_sources(hypridle PRIVATE protocols/${protoName}-protocol.c) else() execute_process( COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) execute_process( COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) target_sources(hypridle PRIVATE protocols/${protoName}-protocol.c) endif() endfunction() make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so the dir won't be there protocol("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false) # Installation install(TARGETS hypridle) install(FILES ${CMAKE_BINARY_DIR}/systemd/hypridle.service DESTINATION "lib/systemd/user") 07070100000006000081A4000000000000000000000001662183E2000005DF000000000000000000000000000000000000001700000000hypridle-0.1.2/LICENSEBSD 3-Clause License Copyright (c) 2024, Hypr Development Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 07070100000007000081A4000000000000000000000001662183E20000087E000000000000000000000000000000000000001900000000hypridle-0.1.2/README.md# hypridle Hyprland's idle daemon ## Features - based on the `ext-idle-notify-v1` wayland protocol - support for dbus' loginctl commands (lock / unlock / before-sleep) - support for dbus' inhibit (used by e.g. firefox / steam) ## Configuration Configuration is done via `~/.config/hypr/hypridle.conf` in the standard hyprland syntax. ```ini general { lock_cmd = notify-send "lock!" # dbus/sysd lock command (loginctl lock-session) unlock_cmd = notify-send "unlock!" # same as above, but unlock before_sleep_cmd = notify-send "Zzz" # command ran before sleep after_sleep_cmd = notify-send "Awake!" # command ran after sleep ignore_dbus_inhibit = false # whether to ignore dbus-sent idle-inhibit requests (used by e.g. firefox or steam) } listener { timeout = 500 # in seconds on-timeout = notify-send "You are idle!" # command to run when timeout has passed on-resume = notify-send "Welcome back!" # command to run when activity is detected after timeout has fired. } ``` You can add as many listeners as you please. Omitting `on-timeout` or `on-resume` (or leaving them empty) will make those events ignored. ## Dependencies - wayland - wayland-protocols - hyprlang >= 0.4.0 - sdbus-c++ ## Building & Installation ### Building: ```sh cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build cmake --build ./build --config Release --target hypridle -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` ``` ### Installation: ```sh sudo cmake --install build ``` ### Usage: Hypridle should ideally be launched after logging in. This can be done by your compositor or by systemd. For example, for Hyprland, use the following in your `hyprland.conf`. ```hyprlang exec-once = hypridle ``` If, instead, you want to have systemd do this for you, you'll just need to enable the service using ```sh systemctl --user enable --now hypridle.service ``` ## Flags ``` -c <config_path>, --config <config_path>: specify a config path, by default set to ${XDG_CONFIG_HOME}/hypr/hypridle.conf -q, --quiet -v, --verbose ``` 07070100000008000081A4000000000000000000000001662183E2000007D6000000000000000000000000000000000000001A00000000hypridle-0.1.2/flake.lock{ "nodes": { "hyprlang": { "inputs": { "nixpkgs": [ "nixpkgs" ], "systems": "systems" }, "locked": { "lastModified": 1713121246, "narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=", "owner": "hyprwm", "repo": "hyprlang", "rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706", "type": "github" }, "original": { "owner": "hyprwm", "repo": "hyprlang", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1712963716, "narHash": "sha256-WKm9CvgCldeIVvRz87iOMi8CFVB1apJlkUT4GGvA0iM=", "owner": "NixOS", "repo": "nixpkgs", "rev": "cfd6b5fc90b15709b780a5a1619695a88505a176", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "hyprlang": "hyprlang", "nixpkgs": "nixpkgs", "systems": "systems_2" } }, "systems": { "locked": { "lastModified": 1689347949, "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", "owner": "nix-systems", "repo": "default-linux", "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default-linux", "type": "github" } }, "systems_2": { "locked": { "lastModified": 1689347949, "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", "owner": "nix-systems", "repo": "default-linux", "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default-linux", "type": "github" } } }, "root": "root", "version": 7 } 07070100000009000081A4000000000000000000000001662183E20000042A000000000000000000000000000000000000001900000000hypridle-0.1.2/flake.nix{ description = "Hyprland's idle daemon"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; systems.url = "github:nix-systems/default-linux"; hyprlang = { url = "github:hyprwm/hyprlang"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { self, nixpkgs, systems, ... } @ inputs: let inherit (nixpkgs) lib; eachSystem = lib.genAttrs (import systems); pkgsFor = eachSystem (system: import nixpkgs { localSystem.system = system; overlays = with self.overlays; [default]; }); in { overlays = import ./nix/overlays.nix {inherit inputs lib;}; packages = eachSystem (system: { default = self.packages.${system}.hypridle; inherit (pkgsFor.${system}) hypridle; }); homeManagerModules = { default = self.homeManagerModules.hypridle; hypridle = import ./nix/hm-module.nix self; }; checks = eachSystem (system: self.packages.${system}); formatter = eachSystem (system: pkgsFor.${system}.alejandra); }; } 0707010000000A000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001300000000hypridle-0.1.2/nix0707010000000B000081A4000000000000000000000001662183E200000251000000000000000000000000000000000000001F00000000hypridle-0.1.2/nix/default.nix{ lib, stdenv, cmake, pkg-config, hyprlang, sdbus-cpp, systemd, wayland, wayland-protocols, version ? "git", }: stdenv.mkDerivation { pname = "hypridle"; inherit version; src = ../.; nativeBuildInputs = [ cmake pkg-config ]; buildInputs = [ hyprlang sdbus-cpp systemd wayland wayland-protocols ]; meta = { homepage = "https://github.com/hyprwm/hypridle"; description = "An idle management daemon for Hyprland"; license = lib.licenses.bsd3; platforms = lib.platforms.linux; mainProgram = "hypridle"; }; } 0707010000000C000081A4000000000000000000000001662183E200000C21000000000000000000000000000000000000002100000000hypridle-0.1.2/nix/hm-module.nixself: { config, pkgs, lib, ... }: let inherit (builtins) toString; inherit (lib.types) bool int listOf package str submodule; inherit (lib.modules) mkIf; inherit (lib.options) mkOption mkEnableOption; inherit (lib.meta) getExe; cfg = config.services.hypridle; in { options.services.hypridle = { enable = mkEnableOption "Hypridle, Hyprland's idle daemon"; package = mkOption { description = "The hypridle package"; type = package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.hypridle; }; listeners = mkOption { description = "The hypridle listeners"; type = listOf (submodule { options = { timeout = mkOption { description = "The timeout for the hypridle service, in seconds"; type = int; default = 500; }; onTimeout = mkOption { description = "The command to run when the timeout is reached"; type = str; default = "echo 'timeout reached'"; }; onResume = mkOption { description = "The command to run when the service resumes"; type = str; default = "echo 'service resumed'"; }; }; }); default = []; }; lockCmd = mkOption { description = "The command to run when the service locks"; type = str; default = "echo 'lock!'"; }; unlockCmd = mkOption { description = "The command to run when the service unlocks"; type = str; default = "echo 'unlock!'"; }; afterSleepCmd = mkOption { description = "The command to run after the service sleeps"; type = str; default = "echo 'Awake...'"; }; beforeSleepCmd = mkOption { description = "The command to run before the service sleeps"; type = str; default = "echo 'Zzz...'"; }; ignoreDbusInhibit = mkOption { description = "Whether to ignore dbus-sent idle-inhibit requests (used by e.g. firefox or steam)"; type = bool; default = false; }; }; config = mkIf cfg.enable { xdg.configFile."hypr/hypridle.conf".text = '' general { lock_cmd = ${cfg.lockCmd} unlock_cmd = ${cfg.unlockCmd} before_sleep_cmd = ${cfg.beforeSleepCmd} after_sleep_cmd = ${cfg.afterSleepCmd} ignore_dbus_inhibit = ${ if cfg.ignoreDbusInhibit then "true" else "false" } } ${builtins.concatStringsSep "\n" (map (listener: '' listener { timeout = ${toString listener.timeout} on-timeout = ${listener.onTimeout} on-resume = ${listener.onResume} } '') cfg.listeners)} ''; systemd.user.services.hypridle = { Unit = { Description = "Hypridle"; After = ["graphical-session.target"]; }; Service = { ExecStart = "${getExe cfg.package}"; Restart = "always"; RestartSec = "10"; }; Install.WantedBy = [ "default.target" ]; }; }; } 0707010000000D000081A4000000000000000000000001662183E20000026C000000000000000000000000000000000000002000000000hypridle-0.1.2/nix/overlays.nix{ lib, inputs, }: let mkDate = longDate: (lib.concatStringsSep "-" [ (builtins.substring 0 4 longDate) (builtins.substring 4 2 longDate) (builtins.substring 6 2 longDate) ]); in { default = inputs.self.overlays.hypridle; hypridle = lib.composeManyExtensions [ inputs.hyprlang.overlays.default (final: prev: { hypridle = prev.callPackage ./default.nix { stdenv = prev.gcc13Stdenv; version = "0.pre" + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty"); inherit (final) hyprlang; }; }) ]; } 0707010000000E000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001300000000hypridle-0.1.2/src0707010000000F000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001A00000000hypridle-0.1.2/src/config07070100000010000081A4000000000000000000000001662183E200000C9A000000000000000000000000000000000000002C00000000hypridle-0.1.2/src/config/ConfigManager.cpp#include "ConfigManager.hpp" #include <filesystem> static std::string getConfigDir() { static const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); if (xdgConfigHome && std::filesystem::path(xdgConfigHome).is_absolute()) return xdgConfigHome; return getenv("HOME") + std::string("/.config"); } static std::string getMainConfigPath() { return getConfigDir() + "/hypr/hypridle.conf"; } CConfigManager::CConfigManager(std::string configPath) : m_config(configPath.empty() ? getMainConfigPath().c_str() : configPath.c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = false}) { ; } void CConfigManager::init() { m_config.addSpecialCategory("listener", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("listener", "timeout", Hyprlang::INT{-1}); m_config.addSpecialConfigValue("listener", "on-timeout", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("listener", "on-resume", Hyprlang::STRING{""}); m_config.addConfigValue("general:lock_cmd", Hyprlang::STRING{""}); m_config.addConfigValue("general:unlock_cmd", Hyprlang::STRING{""}); m_config.addConfigValue("general:before_sleep_cmd", Hyprlang::STRING{""}); m_config.addConfigValue("general:after_sleep_cmd", Hyprlang::STRING{""}); m_config.addConfigValue("general:ignore_dbus_inhibit", Hyprlang::INT{0}); m_config.commence(); auto result = m_config.parse(); if (result.error) Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); result = postParse(); if (result.error) Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); } Hyprlang::CParseResult CConfigManager::postParse() { const auto KEYS = m_config.listKeysForSpecialCategory("listener"); Hyprlang::CParseResult result; if (KEYS.empty()) { result.setError("No rules configured"); return result; } for (auto& k : KEYS) { STimeoutRule rule; Hyprlang::INT timeout = std::any_cast<Hyprlang::INT>(m_config.getSpecialConfigValue("listener", "timeout", k.c_str())); rule.timeout = timeout; rule.onTimeout = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("listener", "on-timeout", k.c_str())); rule.onResume = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("listener", "on-resume", k.c_str())); if (timeout == -1) { result.setError("Category has a missing timeout setting"); continue; } m_vRules.emplace_back(rule); } for (auto& r : m_vRules) { Debug::log(LOG, "Registered timeout rule for {}s:\n on-timeout: {}\n on-resume: {}", r.timeout, r.onTimeout, r.onResume); } return result; } std::vector<CConfigManager::STimeoutRule> CConfigManager::getRules() { return m_vRules; } std::string CConfigManager::getOnTimeoutCommand() { return m_vRules.front().onTimeout; } void* const* CConfigManager::getValuePtr(const std::string& name) { return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr(); } 07070100000011000081A4000000000000000000000001662183E2000002C0000000000000000000000000000000000000002C00000000hypridle-0.1.2/src/config/ConfigManager.hpp#pragma once #include "../helpers/Log.hpp" #include <hyprlang.hpp> #include <vector> #include <memory> class CConfigManager { public: CConfigManager(std::string configPath); void init(); struct STimeoutRule { uint64_t timeout = 0; std::string onTimeout = ""; std::string onResume = ""; }; std::string getOnTimeoutCommand(); std::vector<STimeoutRule> getRules(); void* const* getValuePtr(const std::string& name); private: Hyprlang::CConfig m_config; std::vector<STimeoutRule> m_vRules; Hyprlang::CParseResult postParse(); }; inline std::unique_ptr<CConfigManager> g_pConfigManager; 07070100000012000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001800000000hypridle-0.1.2/src/core07070100000013000081A4000000000000000000000001662183E200003EF4000000000000000000000000000000000000002500000000hypridle-0.1.2/src/core/Hypridle.cpp#include "Hypridle.hpp" #include "../helpers/Log.hpp" #include "../config/ConfigManager.hpp" #include "signal.h" #include <sys/wait.h> #include <sys/poll.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <thread> #include <mutex> CHypridle::CHypridle() { m_sWaylandState.display = wl_display_connect(nullptr); if (!m_sWaylandState.display) { Debug::log(CRIT, "Couldn't connect to a wayland compositor"); exit(1); } } void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { g_pHypridle->onGlobal(data, registry, name, interface, version); } void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) { g_pHypridle->onGlobalRemoved(data, registry, name); } inline const wl_registry_listener registryListener = { .global = handleGlobal, .global_remove = handleGlobalRemove, }; void handleIdled(void* data, ext_idle_notification_v1* ext_idle_notification_v1) { g_pHypridle->onIdled((CHypridle::SIdleListener*)data); } void handleResumed(void* data, ext_idle_notification_v1* ext_idle_notification_v1) { g_pHypridle->onResumed((CHypridle::SIdleListener*)data); } inline const ext_idle_notification_v1_listener idleListener = { .idled = handleIdled, .resumed = handleResumed, }; void CHypridle::run() { m_sWaylandState.registry = wl_display_get_registry(m_sWaylandState.display); wl_registry_add_listener(m_sWaylandState.registry, ®istryListener, nullptr); wl_display_roundtrip(m_sWaylandState.display); if (!m_sWaylandIdleState.notifier) { Debug::log(CRIT, "Couldn't bind to ext-idle-notifier-v1, does your compositor support it?"); exit(1); } const auto RULES = g_pConfigManager->getRules(); m_sWaylandIdleState.listeners.resize(RULES.size()); Debug::log(LOG, "found {} rules", RULES.size()); for (size_t i = 0; i < RULES.size(); ++i) { auto& l = m_sWaylandIdleState.listeners[i]; const auto& r = RULES[i]; l.notification = ext_idle_notifier_v1_get_idle_notification(m_sWaylandIdleState.notifier, r.timeout * 1000 /* ms */, m_sWaylandState.seat); l.onRestore = r.onResume; l.onTimeout = r.onTimeout; ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l); } wl_display_roundtrip(m_sWaylandState.display); Debug::log(LOG, "wayland done, registering dbus"); try { m_sDBUSState.connection = sdbus::createSystemBusConnection(); } catch (std::exception& e) { Debug::log(CRIT, "Couldn't create the dbus connection ({})", e.what()); exit(1); } setupDBUS(); enterEventLoop(); } void CHypridle::enterEventLoop() { pollfd pollfds[] = { { .fd = m_sDBUSState.connection->getEventLoopPollData().fd, .events = POLLIN, }, { .fd = wl_display_get_fd(m_sWaylandState.display), .events = POLLIN, }, { .fd = m_sDBUSState.screenSaverServiceConnection ? m_sDBUSState.screenSaverServiceConnection->getEventLoopPollData().fd : 0, .events = POLLIN, }, }; std::thread pollThr([this, &pollfds]() { while (1) { int ret = poll(pollfds, m_sDBUSState.screenSaverServiceConnection ? 3 : 2, 5000 /* 5 seconds, reasonable. It's because we might need to terminate */); if (ret < 0) { Debug::log(CRIT, "[core] Polling fds failed with {}", errno); m_bTerminate = true; exit(1); } for (size_t i = 0; i < 3; ++i) { if (pollfds[i].revents & POLLHUP) { Debug::log(CRIT, "[core] Disconnected from pollfd id {}", i); m_bTerminate = true; exit(1); } } if (m_bTerminate) break; if (ret != 0) { Debug::log(TRACE, "[core] got poll event"); std::lock_guard<std::mutex> lg(m_sEventLoopInternals.loopRequestMutex); m_sEventLoopInternals.shouldProcess = true; m_sEventLoopInternals.loopSignal.notify_all(); } } }); while (1) { // dbus events // wait for being awakened m_sEventLoopInternals.loopRequestMutex.unlock(); // unlock, we are ready to take events std::unique_lock lk(m_sEventLoopInternals.loopMutex); if (m_sEventLoopInternals.shouldProcess == false) // avoid a lock if a thread managed to request something already since we .unlock()ed m_sEventLoopInternals.loopSignal.wait(lk, [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events m_sEventLoopInternals.loopRequestMutex.lock(); // lock incoming events if (m_bTerminate) break; m_sEventLoopInternals.shouldProcess = false; std::lock_guard<std::mutex> lg(m_sEventLoopInternals.eventLock); if (pollfds[0].revents & POLLIN /* dbus */) { Debug::log(TRACE, "got dbus event"); while (m_sDBUSState.connection->processPendingRequest()) { ; } } if (pollfds[1].revents & POLLIN /* wl */) { Debug::log(TRACE, "got wl event"); wl_display_flush(m_sWaylandState.display); if (wl_display_prepare_read(m_sWaylandState.display) == 0) { wl_display_read_events(m_sWaylandState.display); wl_display_dispatch_pending(m_sWaylandState.display); } else { wl_display_dispatch(m_sWaylandState.display); } } if (pollfds[2].revents & POLLIN /* dbus2 */) { Debug::log(TRACE, "got dbus event"); while (m_sDBUSState.screenSaverServiceConnection->processPendingRequest()) { ; } } // finalize wayland dispatching. Dispatch pending on the queue int ret = 0; do { ret = wl_display_dispatch_pending(m_sWaylandState.display); wl_display_flush(m_sWaylandState.display); } while (ret > 0); } Debug::log(ERR, "[core] Terminated"); } void CHypridle::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { const std::string IFACE = interface; Debug::log(LOG, " | got iface: {} v{}", IFACE, version); if (IFACE == ext_idle_notifier_v1_interface.name) { m_sWaylandIdleState.notifier = (ext_idle_notifier_v1*)wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, version); Debug::log(LOG, " > Bound to {} v{}", IFACE, version); } else if (IFACE == wl_seat_interface.name) { if (m_sWaylandState.seat) { Debug::log(WARN, "Hypridle does not support multi-seat configurations. Only binding to the first seat."); return; } m_sWaylandState.seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, version); Debug::log(LOG, " > Bound to {} v{}", IFACE, version); } } void CHypridle::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) { ; } static void spawn(const std::string& args) { Debug::log(LOG, "Executing {}", args); int socket[2]; if (pipe(socket) != 0) { Debug::log(LOG, "Unable to create pipe for fork"); } pid_t child, grandchild; child = fork(); if (child < 0) { close(socket[0]); close(socket[1]); Debug::log(LOG, "Fail to create the first fork"); return; } if (child == 0) { // run in child sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); grandchild = fork(); if (grandchild == 0) { // run in grandchild close(socket[0]); close(socket[1]); execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); // exit grandchild _exit(0); } close(socket[0]); write(socket[1], &grandchild, sizeof(grandchild)); close(socket[1]); // exit child _exit(0); } // run in parent close(socket[1]); read(socket[0], &grandchild, sizeof(grandchild)); close(socket[0]); // clear child and leave grandchild to init waitpid(child, NULL, 0); if (grandchild < 0) { Debug::log(LOG, "Failed to create the second fork"); return; } Debug::log(LOG, "Process Created with pid {}", grandchild); } void CHypridle::onIdled(SIdleListener* pListener) { Debug::log(LOG, "Idled: rule {:x}", (uintptr_t)pListener); isIdled = true; if (g_pHypridle->m_iInhibitLocks > 0) { Debug::log(LOG, "Ignoring from onIdled(), inhibit locks: {}", g_pHypridle->m_iInhibitLocks); return; } if (pListener->onTimeout.empty()) { Debug::log(LOG, "Ignoring, onTimeout is empty."); return; } Debug::log(LOG, "Running {}", pListener->onTimeout); spawn(pListener->onTimeout); } void CHypridle::onResumed(SIdleListener* pListener) { Debug::log(LOG, "Resumed: rule {:x}", (uintptr_t)pListener); isIdled = false; if (g_pHypridle->m_iInhibitLocks > 0) { Debug::log(LOG, "Ignoring from onResumed(), inhibit locks: {}", g_pHypridle->m_iInhibitLocks); return; } if (pListener->onRestore.empty()) { Debug::log(LOG, "Ignoring, onRestore is empty."); return; } Debug::log(LOG, "Running {}", pListener->onRestore); spawn(pListener->onRestore); } void CHypridle::onInhibit(bool lock) { m_iInhibitLocks += lock ? 1 : -1; if (m_iInhibitLocks < 0) { // what would be safer appending one or setting to 0? // what if would be equal -2? // you have been warned. m_iInhibitLocks = 0; Debug::log(WARN, "BUG THIS: inhibit locks < 0. Brought back to 0."); } else if (m_iInhibitLocks > 0) { Debug::log(LOG, "Inhibit locks: {}", m_iInhibitLocks); } else { Debug::log(LOG, "Inhibit locks: {}", m_iInhibitLocks); if (isIdled && lock) { Debug::log(LOG, "Running from onInhibit() isIdled = true {}", g_pConfigManager->getOnTimeoutCommand()); spawn(g_pConfigManager->getOnTimeoutCommand()); } } } CHypridle::SDbusInhibitCookie CHypridle::getDbusInhibitCookie(uint32_t cookie) { for (auto& c : m_sDBUSState.inhibitCookies) { if (c.cookie == cookie) return c; } return {}; } void CHypridle::registerDbusInhibitCookie(CHypridle::SDbusInhibitCookie& cookie) { m_sDBUSState.inhibitCookies.push_back(cookie); } void CHypridle::unregisterDbusInhibitCookie(const CHypridle::SDbusInhibitCookie& cookie) { const auto IT = std::find_if(m_sDBUSState.inhibitCookies.begin(), m_sDBUSState.inhibitCookies.end(), [&cookie](const CHypridle::SDbusInhibitCookie& item) { return item.cookie == cookie.cookie; }); if (IT == m_sDBUSState.inhibitCookies.end()) Debug::log(WARN, "BUG THIS: attempted to unregister unknown cookie"); else m_sDBUSState.inhibitCookies.erase(IT); } void handleDbusLogin(sdbus::Message& msg) { // lock & unlock static auto* const PLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:lock_cmd"); static auto* const PUNLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:unlock_cmd"); Debug::log(LOG, "Got dbus .Session"); const auto MEMBER = msg.getMemberName(); if (MEMBER == "Lock") { Debug::log(LOG, "Got Lock from dbus"); if (!std::string{*PLOCKCMD}.empty()) { Debug::log(LOG, "Locking with {}", *PLOCKCMD); spawn(*PLOCKCMD); } } else if (MEMBER == "Unlock") { Debug::log(LOG, "Got Unlock from dbus"); if (!std::string{*PUNLOCKCMD}.empty()) { Debug::log(LOG, "Locking with {}", *PUNLOCKCMD); spawn(*PUNLOCKCMD); } } } void handleDbusSleep(sdbus::Message& msg) { const auto MEMBER = msg.getMemberName(); if (MEMBER != "PrepareForSleep") return; bool toSleep = true; msg >> toSleep; static auto* const PSLEEPCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:before_sleep_cmd"); static auto* const PAFTERSLEEPCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:after_sleep_cmd"); Debug::log(LOG, "Got PrepareForSleep from dbus with sleep {}", toSleep); std::string cmd = toSleep ? *PSLEEPCMD : *PAFTERSLEEPCMD; if (cmd.empty()) return; Debug::log(LOG, "Running: {}", cmd); spawn(cmd); } void handleDbusScreensaver(sdbus::MethodCall call, bool inhibit) { std::string app = "?", reason = "?"; if (inhibit) { call >> app; call >> reason; } else { uint32_t cookie = 0; call >> cookie; Debug::log(TRACE, "Read uninhibit cookie: {}", cookie); const auto COOKIE = g_pHypridle->getDbusInhibitCookie(cookie); if (COOKIE.cookie == 0) { Debug::log(WARN, "No cookie in uninhibit"); } else { app = COOKIE.app; reason = COOKIE.reason; g_pHypridle->unregisterDbusInhibitCookie(COOKIE); } } Debug::log(LOG, "ScreenSaver inhibit: {} dbus message from {} with content {}", inhibit, app, reason); static auto* const PIGNORE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_dbus_inhibit"); if (!**PIGNORE) { if (inhibit) g_pHypridle->onInhibit(true); else g_pHypridle->onInhibit(false); } static int cookieID = 1337; if (inhibit) { auto cookie = CHypridle::SDbusInhibitCookie{uint32_t{cookieID}, app, reason}; auto reply = call.createReply(); reply << uint32_t{cookieID++}; reply.send(); Debug::log(LOG, "Cookie {} sent", cookieID - 1); g_pHypridle->registerDbusInhibitCookie(cookie); } else { auto reply = call.createReply(); reply.send(); Debug::log(TRACE, "Uninhibit response sent"); } } void CHypridle::setupDBUS() { auto proxy = sdbus::createProxy("org.freedesktop.login1", "/org/freedesktop/login1"); auto method = proxy->createMethodCall("org.freedesktop.login1.Manager", "GetSession"); method << "auto"; sdbus::ObjectPath path; try { auto reply = proxy->callMethod(method); reply >> path; } catch (std::exception& e) { Debug::log(CRIT, "Couldn't connect to logind service ({})", e.what()); exit(1); } Debug::log(LOG, "Using dbus path {}", path.c_str()); m_sDBUSState.connection->addMatch("type='signal',path='" + path + "',interface='org.freedesktop.login1.Session'", handleDbusLogin, sdbus::floating_slot_t{}); m_sDBUSState.connection->addMatch("type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.login1.Manager'", handleDbusSleep, sdbus::floating_slot_t{}); // attempt to register as ScreenSaver try { m_sDBUSState.screenSaverServiceConnection = sdbus::createSessionBusConnection("org.freedesktop.ScreenSaver"); m_sDBUSState.screenSaverObject = sdbus::createObject(*m_sDBUSState.screenSaverServiceConnection, "/org/freedesktop/ScreenSaver"); m_sDBUSState.screenSaverObject->registerMethod("org.freedesktop.ScreenSaver", "Inhibit", "ss", "u", [&](sdbus::MethodCall c) { handleDbusScreensaver(c, true); }); m_sDBUSState.screenSaverObject->registerMethod("org.freedesktop.ScreenSaver", "UnInhibit", "u", "", [&](sdbus::MethodCall c) { handleDbusScreensaver(c, false); }); m_sDBUSState.screenSaverObject->finishRegistration(); } catch (std::exception& e) { Debug::log(ERR, "Failed registering for /org/freedesktop/ScreenSaver, perhaps taken?\nerr: {}", e.what()); } } 07070100000014000081A4000000000000000000000001662183E2000008D3000000000000000000000000000000000000002500000000hypridle-0.1.2/src/core/Hypridle.hpp#pragma once #include <memory> #include <vector> #include <wayland-client.h> #include <sdbus-c++/sdbus-c++.h> #include <condition_variable> #include "ext-idle-notify-v1-protocol.h" class CHypridle { public: CHypridle(); struct SIdleListener { ext_idle_notification_v1* notification = nullptr; std::string onTimeout = ""; std::string onRestore = ""; }; struct SDbusInhibitCookie { uint32_t cookie = 0; std::string app, reason; }; void run(); void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version); void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name); void onIdled(SIdleListener*); void onResumed(SIdleListener*); void onInhibit(bool lock); SDbusInhibitCookie getDbusInhibitCookie(uint32_t cookie); void registerDbusInhibitCookie(SDbusInhibitCookie& cookie); void unregisterDbusInhibitCookie(const SDbusInhibitCookie& cookie); private: void setupDBUS(); void enterEventLoop(); bool m_bTerminate = false; bool isIdled = false; int64_t m_iInhibitLocks = 0; struct { wl_display* display = nullptr; wl_registry* registry = nullptr; wl_seat* seat = nullptr; } m_sWaylandState; struct { ext_idle_notifier_v1* notifier = nullptr; std::vector<SIdleListener> listeners; } m_sWaylandIdleState; struct { std::unique_ptr<sdbus::IConnection> connection; std::unique_ptr<sdbus::IConnection> screenSaverServiceConnection; std::unique_ptr<sdbus::IObject> screenSaverObject; std::vector<SDbusInhibitCookie> inhibitCookies; } m_sDBUSState; struct { std::condition_variable loopSignal; std::mutex loopMutex; std::atomic<bool> shouldProcess = false; std::mutex loopRequestMutex; std::mutex eventLock; } m_sEventLoopInternals; }; inline std::unique_ptr<CHypridle> g_pHypridle; 07070100000015000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001B00000000hypridle-0.1.2/src/helpers07070100000016000081A4000000000000000000000001662183E20000093F000000000000000000000000000000000000002300000000hypridle-0.1.2/src/helpers/Log.hpp#pragma once #include <format> #include <iostream> #include <string> enum eLogLevel { TRACE = 0, INFO, LOG, WARN, ERR, CRIT, NONE }; #define RASSERT(expr, reason, ...) \ if (!(expr)) { \ Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ std::format(reason, ##__VA_ARGS__), __LINE__, \ ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \ printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \ *((int*)nullptr) = 1; /* so that we crash and get a coredump */ \ } #define ASSERT(expr) RASSERT(expr, "?") namespace Debug { inline bool quiet = false; inline bool verbose = false; template <typename... Args> void log(eLogLevel level, const std::string& fmt, Args&&... args) { if (!verbose && level == TRACE) return; if (quiet) return; if (level != NONE) { std::cout << '['; switch (level) { case TRACE: std::cout << "TRACE"; break; case INFO: std::cout << "INFO"; break; case LOG: std::cout << "LOG"; break; case WARN: std::cout << "WARN"; break; case ERR: std::cout << "ERR"; break; case CRIT: std::cout << "CRITICAL"; break; default: break; } std::cout << "] "; } std::cout << std::vformat(fmt, std::make_format_args(args...)) << "\n"; } };07070100000017000081A4000000000000000000000001662183E2000003CA000000000000000000000000000000000000001C00000000hypridle-0.1.2/src/main.cpp #include "config/ConfigManager.hpp" #include "core/Hypridle.hpp" int main(int argc, char** argv, char** envp) { std::string configPath; for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--verbose" || arg == "-v") Debug::verbose = true; else if (arg == "--quiet" || arg == "-q") Debug::quiet = true; else if (arg == "--config" || arg == "-c") { configPath = argv[++i]; } } try { g_pConfigManager = std::make_unique<CConfigManager>(configPath); g_pConfigManager->init(); } catch (const char* err) { Debug::log(CRIT, "ConfigManager threw: {}", err); std::string strerr = err; if (strerr.contains("File does not exist")) Debug::log(NONE, " Make sure you have a config."); return 1; } g_pHypridle = std::make_unique<CHypridle>(); g_pHypridle->run(); return 0; } 07070100000018000041ED000000000000000000000002662183E200000000000000000000000000000000000000000000001700000000hypridle-0.1.2/systemd07070100000019000081A4000000000000000000000001662183E200000153000000000000000000000000000000000000002B00000000hypridle-0.1.2/systemd/hypridle.service.in[Unit] Description=Hyprland's idle daemon Documentation=https://wiki.hyprland.org/Hypr-Ecosystem/hypridle PartOf=graphical-session.target After=graphical-session.target ConditionEnvironment=WAYLAND_DISPLAY [Service] Type=simple ExecStart=@CMAKE_INSTALL_PREFIX@/bin/hypridle Restart=on-failure [Install] WantedBy=graphical-session.target 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!91 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor