Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hypridle for openSUSE:Factory checked in at 2025-09-22 16:40:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hypridle (Old) and /work/SRC/openSUSE:Factory/.hypridle.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hypridle" Mon Sep 22 16:40:52 2025 rev:4 rq:1306479 version:0.1.7 Changes: -------- --- /work/SRC/openSUSE:Factory/hypridle/hypridle.changes 2024-12-20 23:12:34.481307030 +0100 +++ /work/SRC/openSUSE:Factory/.hypridle.new.27445/hypridle.changes 2025-09-22 16:41:43.562570005 +0200 @@ -1,0 +2,28 @@ +Wed Sep 10 20:04:30 UTC 2025 - Marcus Rueckert <[email protected]> + +- Update to version 0.1.7: + - core: log when ScreenSaver interface is already registered by + @PaideiaDilemma in #133 + - clang-tidy: fix some errors by @Honkazel in #143 + - feat: support source option for additional config files by + @martabal in #144 + - core: guard against dbus logind interface not existing and + check if on_(un)lock_cmd is empty by @PaideiaDilemma in #151 + - Add ability to ignore Wayland idle inhibitors by @ChrisHixon in + #155 + - Ignore idle inhibition per-listener by @ChrisHixon in #158 + - nix: use gcc15 by @FridayFaerie in #159 + - Add a --help option by @ShyAssassin in #160 + +------------------------------------------------------------------- +Tue Jun 3 12:25:40 UTC 2025 - Michal Hrusecky <[email protected]> + +- Update to version 0.1.6: + * Added --version + * Added support for hyprland-lock-notify protocol + * core: implement hyprlock-lock-notify-v1 functionality (gh#hyprwm/hypridle#122) + * clang-tidy and comp fixes (gh#hyprwm/hypridle#126) + * core: fix sleep delay and simplify process spawning (gh#hyprwm/hypridle#127) + * assets: update example.conf (gh#hyprwm/hypridle#137) + +------------------------------------------------------------------- Old: ---- hypridle-0.1.5.obscpio New: ---- hypridle-0.1.7.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hypridle.spec ++++++ --- /var/tmp/diff_new_pack.bkcHsv/_old 2025-09-22 16:41:44.222597737 +0200 +++ /var/tmp/diff_new_pack.bkcHsv/_new 2025-09-22 16:41:44.222597737 +0200 @@ -1,7 +1,7 @@ # # spec file for package hypridle # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: hypridle -Version: 0.1.5 +Version: 0.1.7 Release: 0 Summary: Hyprland's idle daemon License: BSD-3-Clause @@ -28,9 +28,11 @@ BuildRequires: gcc-c++ ## Added for directory ownership BuildRequires: hyprland -BuildRequires: pkg-config +BuildRequires: pkgconfig +BuildRequires: pkgconfig(hyprland-protocols) BuildRequires: pkgconfig(hyprlang) >= 0.4.2 BuildRequires: pkgconfig(hyprutils) +BuildRequires: pkgconfig(hyprwayland-scanner) BuildRequires: pkgconfig(sdbus-c++) BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(wayland-protocols) @@ -51,7 +53,7 @@ %install %cmake_install -install -Dm 0644 %{SOURCE1} %buildroot/%_docdir/%name/hypridle.conf.example +install -Dm 0644 %{SOURCE1} %{buildroot}/%{_docdir}/%{name}/hypridle.conf.example %files %license LICENSE ++++++ hypridle-0.1.5.obscpio -> hypridle-0.1.7.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/.clang-tidy new/hypridle-0.1.7/.clang-tidy --- old/hypridle-0.1.5/.clang-tidy 1970-01-01 01:00:00.000000000 +0100 +++ new/hypridle-0.1.7/.clang-tidy 2025-08-27 12:40:01.000000000 +0200 @@ -0,0 +1,101 @@ +WarningsAsErrors: '*' +HeaderFilterRegex: '.*\.hpp' +FormatStyle: 'file' +Checks: > + -*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-forward-declaration-namespace, + -bugprone-forward-declaration-namespace, + -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -bugprone-branch-clone, + -bugprone-assignment-in-if-condition, + concurrency-*, + -concurrency-mt-unsafe, + cppcoreguidelines-*, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-special-member-functions, + -cppcoreguidelines-explicit-virtual-functions, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-macro-to-enum, + -cppcoreguidelines-init-variables, + -cppcoreguidelines-pro-type-cstyle-cast, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-pro-type-reinterpret-cast, + google-global-names-in-headers, + -google-readability-casting, + google-runtime-operator, + misc-*, + -misc-unused-parameters, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-include-cleaner, + -misc-use-anonymous-namespace, + -misc-const-correctness, + modernize-*, + -modernize-return-braced-init-list, + -modernize-use-trailing-return-type, + -modernize-use-using, + -modernize-use-override, + -modernize-avoid-c-arrays, + -modernize-macro-to-enum, + -modernize-loop-convert, + -modernize-use-nodiscard, + -modernize-pass-by-value, + -modernize-use-auto, + performance-*, + -performance-avoid-endl, + -performance-unnecessary-value-param, + portability-std-allocator-const, + readability-*, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-length, + -readability-magic-numbers, + -readability-uppercase-literal-suffix, + -readability-braces-around-statements, + -readability-redundant-access-specifiers, + -readability-else-after-return, + -readability-container-data-pointer, + -readability-implicit-bool-conversion, + -readability-avoid-nested-conditional-operator, + -readability-redundant-member-init, + -readability-redundant-string-init, + -readability-avoid-const-params-in-decls, + -readability-named-parameter, + -readability-convert-member-functions-to-static, + -readability-qualified-auto, + -readability-make-member-function-const, + -readability-isolate-declaration, + -readability-inconsistent-declaration-parameter-name, + -clang-diagnostic-error, + +CheckOptions: + performance-for-range-copy.WarnOnAllAutoCopies: true + performance-inefficient-string-concatenation.StrictMode: true + readability-braces-around-statements.ShortStatementLines: 0 + readability-identifier-naming.ClassCase: CamelCase + readability-identifier-naming.ClassIgnoredRegexp: I.* + readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!? + readability-identifier-naming.EnumCase: CamelCase + readability-identifier-naming.EnumPrefix: e + readability-identifier-naming.EnumConstantCase: UPPER_CASE + readability-identifier-naming.FunctionCase: camelBack + readability-identifier-naming.NamespaceCase: CamelCase + readability-identifier-naming.NamespacePrefix: N + readability-identifier-naming.StructPrefix: S + readability-identifier-naming.StructCase: CamelCase diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/.github/workflows/nix.yml new/hypridle-0.1.7/.github/workflows/nix.yml --- old/hypridle-0.1.5/.github/workflows/nix.yml 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/.github/workflows/nix.yml 2025-08-27 12:40:01.000000000 +0200 @@ -7,8 +7,35 @@ steps: - uses: actions/checkout@v3 - - uses: DeterminateSystems/nix-installer-action@main - - uses: DeterminateSystems/magic-nix-cache-action@main + - name: Install Nix + uses: nixbuild/nix-quick-install-action@v31 + with: + nix_conf: | + keep-env-derivations = true + keep-outputs = true + + - name: Restore and save Nix store + uses: nix-community/cache-nix-action@v6 + with: + # restore and save a cache using this key + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} + # if there's no cache hit, restore a cache by this prefix + restore-prefixes-first-match: nix-${{ runner.os }}- + # collect garbage until the Nix store size (in bytes) is at most this number + # before trying to save a new cache + # 1G = 1073741824 + gc-max-store-size-linux: 1G + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: nix-${{ runner.os }}- + # created more than this number of seconds ago + purge-created: 0 + # or, last accessed more than this number of seconds ago + # relative to the start of the `Post Restore and save Nix store` phase + purge-last-accessed: 0 + # except any version with the key that is the same as the `primary-key` + purge-primary-key: never # not needed (yet) # - uses: cachix/cachix-action@v12 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/.gitignore new/hypridle-0.1.7/.gitignore --- old/hypridle-0.1.5/.gitignore 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/.gitignore 2025-08-27 12:40:01.000000000 +0200 @@ -1,3 +1,7 @@ .vscode/ build/ -protocols/ \ No newline at end of file +protocols/ +.clangd/ +.direnv/ +.cache/ +compile_commands.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/CMakeLists.txt new/hypridle-0.1.7/CMakeLists.txt --- old/hypridle-0.1.5/CMakeLists.txt 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/CMakeLists.txt 2025-08-27 12:40:01.000000000 +0200 @@ -18,14 +18,17 @@ message(STATUS "Configuring hypridle in Release with CMake") endif() +add_compile_definitions(HYPRIDLE_VERSION="${VERSION}") + include_directories(. "protocols/") include(GNUInstallDirs) # configure set(CMAKE_CXX_STANDARD 23) -add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value - -Wno-missing-field-initializers -Wno-narrowing) +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) +add_compile_options(-Wall -Wextra -Wuseless-cast -Wno-unused-parameter + -Wno-unused-value -Wno-missing-field-initializers) configure_file(systemd/hypridle.service.in systemd/hypridle.service @ONLY) # dependencies @@ -33,13 +36,14 @@ find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) +find_package(hyprwayland-scanner 0.4.4 REQUIRED) pkg_check_modules( deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols - hyprlang>=0.4.0 + hyprlang>=0.6.0 hyprutils>=0.2.0 sdbus-c++>=0.2.0) @@ -48,45 +52,47 @@ 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) +pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") +pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir) +message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}") + +pkg_check_modules(hyprland_protocols_dep REQUIRED IMPORTED_TARGET hyprland-protocols>=0.6.0) +pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir) +message(STATUS "Found hyprland-protocols at ${HYPRLAND_PROTOCOLS}") -function(protocol protoPath protoName external) +function(protocolnew 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) + set(path ${protoPath}) 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) + set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) endif() + message(STATUS "Full proto path: ${path}") + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp + ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp + COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml + ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(hypridle PRIVATE protocols/${protoName}.cpp + protocols/${protoName}.hpp) +endfunction() +function(protocolWayland) + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp + ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp + COMMAND hyprwayland-scanner --wayland-enums --client + ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(hypridle PRIVATE protocols/wayland.cpp protocols/wayland.hpp) 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) + +protocolwayland() + +protocolnew("staging/ext-idle-notify" "ext-idle-notify-v1" false) +protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true) # Installation install(TARGETS hypridle) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/README.md new/hypridle-0.1.7/README.md --- old/hypridle-0.1.5/README.md 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/README.md 2025-08-27 12:40:01.000000000 +0200 @@ -34,15 +34,17 @@ ## Dependencies - wayland - wayland-protocols + - hyprland-protocols - hyprlang >= 0.4.0 - sdbus-c++ + - hyprwayland-scanner ## 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` +cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build +cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` ``` ### Installation: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/VERSION new/hypridle-0.1.7/VERSION --- old/hypridle-0.1.5/VERSION 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/VERSION 2025-08-27 12:40:01.000000000 +0200 @@ -1 +1 @@ -0.1.5 +0.1.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/assets/example.conf new/hypridle-0.1.7/assets/example.conf --- old/hypridle-0.1.5/assets/example.conf 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/assets/example.conf 2025-08-27 12:40:01.000000000 +0200 @@ -2,16 +2,36 @@ # for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hypridle 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) - ignore_systemd_inhibit = false # whether to ignore systemd-inhibit --what=idle inhibitors + lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances. + before_sleep_cmd = loginctl lock-session # lock before suspend. + after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display. } listener { - timeout = 300 # 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. + timeout = 150 # 2.5min. + on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor. + on-resume = brightnessctl -r # monitor backlight restore. +} + +# turn off keyboard backlight, comment out this section if you dont have a keyboard backlight. +listener { + timeout = 150 # 2.5min. + on-timeout = brightnessctl -sd rgb:kbd_backlight set 0 # turn off keyboard backlight. + on-resume = brightnessctl -rd rgb:kbd_backlight # turn on keyboard backlight. +} + +listener { + timeout = 300 # 5min. + on-timeout = loginctl lock-session # lock screen when timeout has passed. +} + +listener { + timeout = 330 # 5.5min. + on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed. + on-resume = hyprctl dispatch dpms on # screen on when activity is detected after timeout has fired. +} + +listener { + timeout = 1800 # 30min. + on-timeout = systemctl suspend # suspend pc. } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/flake.lock new/hypridle-0.1.7/flake.lock --- old/hypridle-0.1.5/flake.lock 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/flake.lock 2025-08-27 12:40:01.000000000 +0200 @@ -1,5 +1,28 @@ { "nodes": { + "hyprland-protocols": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1749046714, + "narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=", + "owner": "hyprwm", + "repo": "hyprland-protocols", + "rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-protocols", + "type": "github" + } + }, "hyprlang": { "inputs": { "hyprutils": [ @@ -13,11 +36,11 @@ ] }, "locked": { - "lastModified": 1721324361, - "narHash": "sha256-BiJKO0IIdnSwHQBSrEJlKlFr753urkLE48wtt0UhNG4=", + "lastModified": 1749145882, + "narHash": "sha256-qr0KXeczF8Sma3Ae7+dR2NHhvG7YeLBJv19W4oMu6ZE=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "adbefbf49664a6c2c8bf36b6487fd31e3eb68086", + "rev": "1bfb84f54d50c7ae6558c794d3cfd5f6a7e6e676", "type": "github" }, "original": { @@ -36,11 +59,11 @@ ] }, "locked": { - "lastModified": 1721324102, - "narHash": "sha256-WAZ0X6yJW1hFG6otkHBfyJDKRpNP5stsRqdEuHrFRpk=", + "lastModified": 1749135356, + "narHash": "sha256-Q8mAKMDsFbCEuq7zoSlcTuxgbIBVhfIYpX0RjE32PS0=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "962582a090bc233c4de9d9897f46794280288989", + "rev": "e36db00dfb3a3d3fdcc4069cb292ff60d2699ccb", "type": "github" }, "original": { @@ -49,13 +72,36 @@ "type": "github" } }, + "hyprwayland-scanner": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1749145760, + "narHash": "sha256-IHaGWpGrv7seFWdw/1A+wHtTsPlOGIKMrk1TUIYJEFI=", + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "rev": "817918315ea016cc2d94004bfb3223b5fd9dfcc6", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1721138476, - "narHash": "sha256-+W5eZOhhemLQxelojLxETfbFbc19NWawsXBlapYpqIA=", + "lastModified": 1748929857, + "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad0b5eed1b6031efaed382844806550c3dcb4206", + "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4", "type": "github" }, "original": { @@ -67,8 +113,10 @@ }, "root": { "inputs": { + "hyprland-protocols": "hyprland-protocols", "hyprlang": "hyprlang", "hyprutils": "hyprutils", + "hyprwayland-scanner": "hyprwayland-scanner", "nixpkgs": "nixpkgs", "systems": "systems" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/flake.nix new/hypridle-0.1.7/flake.nix --- old/hypridle-0.1.5/flake.nix 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/flake.nix 2025-08-27 12:40:01.000000000 +0200 @@ -17,6 +17,18 @@ inputs.nixpkgs.follows = "nixpkgs"; inputs.systems.follows = "systems"; }; + + hyprland-protocols = { + url = "github:hyprwm/hyprland-protocols"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + }; + + hyprwayland-scanner = { + url = "github:hyprwm/hyprwayland-scanner"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + }; }; outputs = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/nix/default.nix new/hypridle-0.1.7/nix/default.nix --- old/hypridle-0.1.5/nix/default.nix 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/nix/default.nix 2025-08-27 12:40:01.000000000 +0200 @@ -1,11 +1,13 @@ { - lib, - stdenv, cmake, - pkg-config, + hyprland-protocols, hyprlang, hyprutils, + hyprwayland-scanner, + lib, + pkg-config, sdbus-cpp, + stdenv, systemd, wayland, wayland-protocols, @@ -19,11 +21,13 @@ nativeBuildInputs = [ cmake + hyprwayland-scanner pkg-config wayland-scanner ]; buildInputs = [ + hyprland-protocols hyprlang hyprutils sdbus-cpp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/nix/overlays.nix new/hypridle-0.1.7/nix/overlays.nix --- old/hypridle-0.1.5/nix/overlays.nix 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/nix/overlays.nix 2025-08-27 12:40:01.000000000 +0200 @@ -13,12 +13,14 @@ default = inputs.self.overlays.hypridle; hypridle = lib.composeManyExtensions [ + inputs.hyprland-protocols.overlays.default inputs.hyprlang.overlays.default inputs.hyprutils.overlays.default + inputs.hyprwayland-scanner.overlays.default inputs.self.overlays.sdbuscpp (final: prev: { hypridle = prev.callPackage ./default.nix { - stdenv = prev.gcc13Stdenv; + stdenv = prev.gcc15Stdenv; version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty"); inherit (final) hyprlang; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/config/ConfigManager.cpp new/hypridle-0.1.7/src/config/ConfigManager.cpp --- old/hypridle-0.1.5/src/config/ConfigManager.cpp 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/src/config/ConfigManager.cpp 2025-08-27 12:40:01.000000000 +0200 @@ -1,6 +1,10 @@ #include "ConfigManager.hpp" +#include "../helpers/Log.hpp" +#include "../helpers/MiscFunctions.hpp" #include <hyprutils/path/Path.hpp> #include <filesystem> +#include <glob.h> +#include <cstring> static std::string getMainConfigPath() { static const auto paths = Hyprutils::Path::findConfig("hypridle"); @@ -13,6 +17,19 @@ CConfigManager::CConfigManager(std::string configPath) : m_config(configPath.empty() ? getMainConfigPath().c_str() : configPath.c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = false}) { ; + configCurrentPath = configPath.empty() ? getMainConfigPath() : configPath; +} + +static Hyprlang::CParseResult handleSource(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; } void CConfigManager::init() { @@ -20,13 +37,24 @@ 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.addSpecialConfigValue("listener", "ignore_inhibit", Hyprlang::INT{0}); m_config.addConfigValue("general:lock_cmd", Hyprlang::STRING{""}); m_config.addConfigValue("general:unlock_cmd", Hyprlang::STRING{""}); + m_config.addConfigValue("general:on_lock_cmd", Hyprlang::STRING{""}); + m_config.addConfigValue("general:on_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.addConfigValue("general:ignore_systemd_inhibit", Hyprlang::INT{0}); + m_config.addConfigValue("general:ignore_wayland_inhibit", Hyprlang::INT{0}); + m_config.addConfigValue("general:inhibit_sleep", Hyprlang::INT{2}); + + // track the file in the circular dependency chain + const auto mainConfigPath = getMainConfigPath(); + alreadyIncludedSourceFiles.insert(std::filesystem::canonical(mainConfigPath)); + + m_config.registerHandler(&::handleSource, "source", {.allowFlags = false}); m_config.commence(); @@ -59,6 +87,8 @@ 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())); + rule.ignoreInhibit = std::any_cast<Hyprlang::INT>(m_config.getSpecialConfigValue("listener", "ignore_inhibit", k.c_str())); + if (timeout == -1) { result.setError("Category has a missing timeout setting"); continue; @@ -68,7 +98,8 @@ } 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); + Debug::log(LOG, "Registered timeout rule for {}s:\n on-timeout: {}\n on-resume: {}\n ignore_inhibit: {}", r.timeout, r.onTimeout, r.onResume, + r.ignoreInhibit); } return result; @@ -78,10 +109,55 @@ return m_vRules; } -std::string CConfigManager::getOnTimeoutCommand() { - return m_vRules.front().onTimeout; -} +std::optional<std::string> CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { + if (rawpath.length() < 2) { + return "source path " + rawpath + " bogus!"; + } + std::unique_ptr<glob_t, void (*)(glob_t*)> glob_buf{new glob_t, [](glob_t* g) { globfree(g); }}; + memset(glob_buf.get(), 0, sizeof(glob_t)); + + const auto CURRENTDIR = std::filesystem::path(configCurrentPath).parent_path().string(); + + if (auto r = glob(absolutePath(rawpath, CURRENTDIR).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { + std::string err = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); + Debug::log(ERR, "{}", err); + return err; + } + + for (size_t i = 0; i < glob_buf->gl_pathc; i++) { + const auto PATH = absolutePath(glob_buf->gl_pathv[i], CURRENTDIR); + + if (PATH.empty() || PATH == configCurrentPath) { + Debug::log(WARN, "source= skipping invalid path"); + continue; + } + + if (std::find(alreadyIncludedSourceFiles.begin(), alreadyIncludedSourceFiles.end(), PATH) != alreadyIncludedSourceFiles.end()) { + Debug::log(WARN, "source= skipping already included source file {} to prevent circular dependency", PATH); + continue; + } + + if (!std::filesystem::is_regular_file(PATH)) { + if (std::filesystem::exists(PATH)) { + Debug::log(WARN, "source= skipping non-file {}", PATH); + continue; + } + + Debug::log(ERR, "source= file doesnt exist"); + return "source file " + PATH + " doesn't exist!"; + } + + // track the file in the circular dependency chain + alreadyIncludedSourceFiles.insert(PATH); + + // allow for nested config parsing + auto backupConfigPath = configCurrentPath; + configCurrentPath = PATH; + + m_config.parseFile(PATH.c_str()); + + configCurrentPath = backupConfigPath; + } -void* const* CConfigManager::getValuePtr(const std::string& name) { - return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr(); + return {}; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/config/ConfigManager.hpp new/hypridle-0.1.7/src/config/ConfigManager.hpp --- old/hypridle-0.1.5/src/config/ConfigManager.hpp 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/src/config/ConfigManager.hpp 2025-08-27 12:40:01.000000000 +0200 @@ -4,6 +4,7 @@ #include <hyprlang.hpp> +#include <set> #include <vector> #include <memory> @@ -13,14 +14,21 @@ void init(); struct STimeoutRule { - uint64_t timeout = 0; - std::string onTimeout = ""; - std::string onResume = ""; + uint64_t timeout = 0; + std::string onTimeout = ""; + std::string onResume = ""; + bool ignoreInhibit = false; }; - std::string getOnTimeoutCommand(); - std::vector<STimeoutRule> getRules(); - void* const* getValuePtr(const std::string& name); + std::vector<STimeoutRule> getRules(); + std::optional<std::string> handleSource(const std::string&, const std::string&); + std::string configCurrentPath; + std::set<std::string> alreadyIncludedSourceFiles; + + template <typename T> + Hyprlang::CSimpleConfigValue<T> getValue(const std::string& name) { + return Hyprlang::CSimpleConfigValue<T>(&m_config, name.c_str()); + } private: Hyprlang::CConfig m_config; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/core/Hypridle.cpp new/hypridle-0.1.7/src/core/Hypridle.cpp --- old/hypridle-0.1.5/src/core/Hypridle.cpp 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/src/core/Hypridle.cpp 2025-08-27 12:40:01.000000000 +0200 @@ -1,14 +1,17 @@ + #include "Hypridle.hpp" #include "../helpers/Log.hpp" #include "../config/ConfigManager.hpp" -#include "signal.h" +#include "csignal" #include <sys/wait.h> #include <sys/poll.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> +#include <algorithm> #include <thread> #include <mutex> +#include <hyprutils/os/Process.hpp> CHypridle::CHypridle() { m_sWaylandState.display = wl_display_connect(nullptr); @@ -18,36 +21,32 @@ } } -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); + m_sWaylandState.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandState.display)); + m_sWaylandState.registry->setGlobal([this](CCWlRegistry* r, 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 = + makeShared<CCExtIdleNotifierV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &ext_idle_notifier_v1_interface, version)); + Debug::log(LOG, " > Bound to {} v{}", IFACE, version); + } else if (IFACE == hyprland_lock_notifier_v1_interface.name) { + m_sWaylandState.lockNotifier = + makeShared<CCHyprlandLockNotifierV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &hyprland_lock_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 = makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_seat_interface, version)); + Debug::log(LOG, " > Bound to {} v{}", IFACE, version); + } + }); - wl_registry_add_listener(m_sWaylandState.registry, ®istryListener, nullptr); + m_sWaylandState.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t name) { Debug::log(LOG, " | removed iface {}", name); }); wl_display_roundtrip(m_sWaylandState.display); @@ -56,23 +55,41 @@ exit(1); } - const auto RULES = g_pConfigManager->getRules(); + static const auto IGNOREWAYLANDINHIBIT = g_pConfigManager->getValue<Hyprlang::INT>("general:ignore_wayland_inhibit"); + + 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; + auto& l = m_sWaylandIdleState.listeners[i]; + const auto& r = RULES[i]; + l.onRestore = r.onResume; + l.onTimeout = r.onTimeout; + l.ignoreInhibit = r.ignoreInhibit; + + if (*IGNOREWAYLANDINHIBIT || r.ignoreInhibit) + l.notification = + makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetInputIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource())); + else + l.notification = + makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource())); + + l.notification->setData(&m_sWaylandIdleState.listeners[i]); - ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l); + l.notification->setIdled([this](CCExtIdleNotificationV1* n) { onIdled((CHypridle::SIdleListener*)n->data()); }); + l.notification->setResumed([this](CCExtIdleNotificationV1* n) { onResumed((CHypridle::SIdleListener*)n->data()); }); } wl_display_roundtrip(m_sWaylandState.display); + if (m_sWaylandState.lockNotifier) { + m_sWaylandState.lockNotification = makeShared<CCHyprlandLockNotificationV1>(m_sWaylandState.lockNotifier->sendGetLockNotification()); + m_sWaylandState.lockNotification->setLocked([this](CCHyprlandLockNotificationV1* n) { onLocked(); }); + m_sWaylandState.lockNotification->setUnlocked([this](CCHyprlandLockNotificationV1* n) { onUnlocked(); }); + } + Debug::log(LOG, "wayland done, registering dbus"); try { @@ -82,7 +99,46 @@ exit(1); } + if (!m_sWaylandState.lockNotifier) + Debug::log(WARN, + "Compositor is missing hyprland-lock-notify-v1!\n" + "general:inhibit_sleep=3, general:on_lock_cmd and general:on_unlock_cmd will not work."); + + static const auto INHIBIT = g_pConfigManager->getValue<Hyprlang::INT>("general:inhibit_sleep"); + static const auto SLEEPCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:before_sleep_cmd"); + static const auto LOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:lock_cmd"); + + switch (*INHIBIT) { + case 0: // disabled + m_inhibitSleepBehavior = SLEEP_INHIBIT_NONE; + break; + case 1: // enabled + m_inhibitSleepBehavior = SLEEP_INHIBIT_NORMAL; + break; + case 2: { // auto (enable, but wait until locked if before_sleep_cmd contains hyprlock, or loginctl lock-session and lock_cmd contains hyprlock.) + if (m_sWaylandState.lockNotifier && std::string{*SLEEPCMD}.contains("hyprlock")) + m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY; + else if (m_sWaylandState.lockNotifier && std::string{*LOCKCMD}.contains("hyprlock") && std::string{*SLEEPCMD}.contains("lock-session")) + m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY; + else + m_inhibitSleepBehavior = SLEEP_INHIBIT_NORMAL; + } break; + case 3: // wait until locked + if (m_sWaylandState.lockNotifier) + m_inhibitSleepBehavior = SLEEP_INHIBIT_LOCK_NOTIFY; + break; + default: Debug::log(ERR, "Invalid inhibit_sleep value: {}", *INHIBIT); break; + } + + switch (m_inhibitSleepBehavior) { + case SLEEP_INHIBIT_NONE: Debug::log(LOG, "Sleep inhibition disabled"); break; + case SLEEP_INHIBIT_NORMAL: Debug::log(LOG, "Sleep inhibition enabled"); break; + case SLEEP_INHIBIT_LOCK_NOTIFY: Debug::log(LOG, "Sleep inhibition enabled - inhibiting until the wayland session gets locked"); break; + } + setupDBUS(); + if (m_inhibitSleepBehavior != SLEEP_INHIBIT_NONE) + inhibitSleep(); enterEventLoop(); } @@ -139,7 +195,7 @@ 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 + if (!m_sEventLoopInternals.shouldProcess) // 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 @@ -187,84 +243,22 @@ 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"); + Hyprutils::OS::CProcess proc("/bin/sh", {"-c", args}); + if (!proc.runAsync()) { + Debug::log(ERR, "Failed run \"{}\"", args); 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); + Debug::log(LOG, "Process Created with pid {}", proc.pid()); } void CHypridle::onIdled(SIdleListener* pListener) { Debug::log(LOG, "Idled: rule {:x}", (uintptr_t)pListener); isIdled = true; - if (g_pHypridle->m_iInhibitLocks > 0) { + if (g_pHypridle->m_iInhibitLocks > 0 && !pListener->ignoreInhibit) { Debug::log(LOG, "Ignoring from onIdled(), inhibit locks: {}", g_pHypridle->m_iInhibitLocks); return; } @@ -281,7 +275,7 @@ void CHypridle::onResumed(SIdleListener* pListener) { Debug::log(LOG, "Resumed: rule {:x}", (uintptr_t)pListener); isIdled = false; - if (g_pHypridle->m_iInhibitLocks > 0) { + if (g_pHypridle->m_iInhibitLocks > 0 && !pListener->ignoreInhibit) { Debug::log(LOG, "Ignoring from onResumed(), inhibit locks: {}", g_pHypridle->m_iInhibitLocks); return; } @@ -304,23 +298,56 @@ } if (m_iInhibitLocks == 0 && isIdled) { - const auto RULES = g_pConfigManager->getRules(); + static const auto IGNOREWAYLANDINHIBIT = g_pConfigManager->getValue<Hyprlang::INT>("general:ignore_wayland_inhibit"); + const auto RULES = g_pConfigManager->getRules(); for (size_t i = 0; i < RULES.size(); ++i) { auto& l = m_sWaylandIdleState.listeners[i]; const auto& r = RULES[i]; - ext_idle_notification_v1_destroy(l.notification); + l.notification->sendDestroy(); + + if (*IGNOREWAYLANDINHIBIT || r.ignoreInhibit) + l.notification = + makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetInputIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource())); + else + l.notification = + makeShared<CCExtIdleNotificationV1>(m_sWaylandIdleState.notifier->sendGetIdleNotification(r.timeout * 1000 /* ms */, m_sWaylandState.seat->resource())); - l.notification = ext_idle_notifier_v1_get_idle_notification(m_sWaylandIdleState.notifier, r.timeout * 1000 /* ms */, m_sWaylandState.seat); + l.notification->setData(&m_sWaylandIdleState.listeners[i]); - ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l); + l.notification->setIdled([this](CCExtIdleNotificationV1* n) { onIdled((CHypridle::SIdleListener*)n->data()); }); + l.notification->setResumed([this](CCExtIdleNotificationV1* n) { onResumed((CHypridle::SIdleListener*)n->data()); }); } } Debug::log(LOG, "Inhibit locks: {}", m_iInhibitLocks); } +void CHypridle::onLocked() { + Debug::log(LOG, "Wayland session got locked"); + m_isLocked = true; + + static const auto LOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:on_lock_cmd"); + if (!std::string{*LOCKCMD}.empty()) + spawn(*LOCKCMD); + + if (m_inhibitSleepBehavior == SLEEP_INHIBIT_LOCK_NOTIFY) + uninhibitSleep(); +} + +void CHypridle::onUnlocked() { + Debug::log(LOG, "Wayland session got unlocked"); + m_isLocked = false; + + if (m_inhibitSleepBehavior == SLEEP_INHIBIT_LOCK_NOTIFY) + inhibitSleep(); + + static const auto UNLOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:on_unlock_cmd"); + if (!std::string{*UNLOCKCMD}.empty()) + spawn(*UNLOCKCMD); +} + CHypridle::SDbusInhibitCookie CHypridle::getDbusInhibitCookie(uint32_t cookie) { for (auto& c : m_sDBUSState.inhibitCookies) { if (c.cookie == cookie) @@ -335,8 +362,7 @@ } bool 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; }); + const auto IT = std::ranges::find_if(m_sDBUSState.inhibitCookies, [&cookie](const CHypridle::SDbusInhibitCookie& item) { return item.cookie == cookie.cookie; }); if (IT == m_sDBUSState.inhibitCookies.end()) return false; @@ -358,8 +384,8 @@ static 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"); + static const auto LOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:lock_cmd"); + static const auto UNLOCKCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:unlock_cmd"); Debug::log(LOG, "Got dbus .Session"); @@ -367,16 +393,16 @@ if (MEMBER == "Lock") { Debug::log(LOG, "Got Lock from dbus"); - if (!std::string{*PLOCKCMD}.empty()) { - Debug::log(LOG, "Locking with {}", *PLOCKCMD); - spawn(*PLOCKCMD); + if (!std::string{*LOCKCMD}.empty()) { + Debug::log(LOG, "Locking with {}", *LOCKCMD); + spawn(*LOCKCMD); } } else if (MEMBER == "Unlock") { Debug::log(LOG, "Got Unlock from dbus"); - if (!std::string{*PUNLOCKCMD}.empty()) { - Debug::log(LOG, "Locking with {}", *PUNLOCKCMD); - spawn(*PUNLOCKCMD); + if (!std::string{*UNLOCKCMD}.empty()) { + Debug::log(LOG, "Unlocking with {}", *UNLOCKCMD); + spawn(*UNLOCKCMD); } } } @@ -390,21 +416,24 @@ 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"); + static const auto SLEEPCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:before_sleep_cmd"); + static const auto AFTERSLEEPCMD = g_pConfigManager->getValue<Hyprlang::STRING>("general:after_sleep_cmd"); Debug::log(LOG, "Got PrepareForSleep from dbus with sleep {}", toSleep); - std::string cmd = toSleep ? *PSLEEPCMD : *PAFTERSLEEPCMD; + std::string cmd = toSleep ? *SLEEPCMD : *AFTERSLEEPCMD; - if (cmd.empty()) - return; + if (!toSleep) + g_pHypridle->handleInhibitOnDbusSleep(toSleep); + + if (!cmd.empty()) + spawn(cmd); - Debug::log(LOG, "Running: {}", cmd); - spawn(cmd); + if (toSleep) + g_pHypridle->handleInhibitOnDbusSleep(toSleep); } -void handleDbusBlockInhibits(const std::string& inhibits) { +static void handleDbusBlockInhibits(const std::string& inhibits) { static auto inhibited = false; // BlockInhibited is a colon separated list of inhibit types. Wrapping in additional colons allows for easier checking if there are active inhibits we are interested in auto inhibits_ = ":" + inhibits + ":"; @@ -455,10 +484,10 @@ else g_pHypridle->onInhibit(false); - static int cookieID = 1337; + static uint32_t cookieID = 1337; if (inhibit) { - auto cookie = CHypridle::SDbusInhibitCookie{uint32_t{cookieID}, app, reason, ownerID}; + auto cookie = CHypridle::SDbusInhibitCookie{.cookie = cookieID, .app = app, .reason = reason, .ownerID = ownerID}; Debug::log(LOG, "Cookie {} sent", cookieID); @@ -484,8 +513,8 @@ } void CHypridle::setupDBUS() { - static auto const IGNORE_DBUS_INHIBIT = **(Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_dbus_inhibit"); - static auto const IGNORE_SYSTEMD_INHIBIT = **(Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_systemd_inhibit"); + static const auto IGNOREDBUSINHIBIT = g_pConfigManager->getValue<Hyprlang::INT>("general:ignore_dbus_inhibit"); + static const auto IGNORESYSTEMDINHIBIT = g_pConfigManager->getValue<Hyprlang::INT>("general:ignore_systemd_inhibit"); auto systemConnection = sdbus::createSystemBusConnection(); auto proxy = sdbus::createProxy(*systemConnection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"}); @@ -496,11 +525,12 @@ m_sDBUSState.connection->addMatch("type='signal',path='" + path + "',interface='org.freedesktop.login1.Session'", ::handleDbusLogin); m_sDBUSState.connection->addMatch("type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.login1.Manager'", ::handleDbusSleep); + m_sDBUSState.login = sdbus::createProxy(*m_sDBUSState.connection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"}); } catch (std::exception& e) { Debug::log(WARN, "Couldn't connect to logind service ({})", e.what()); } Debug::log(LOG, "Using dbus path {}", path.c_str()); - if (!IGNORE_SYSTEMD_INHIBIT) { + if (!*IGNORESYSTEMDINHIBIT) { m_sDBUSState.connection->addMatch("type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.DBus.Properties'", ::handleDbusBlockInhibitsPropertyChanged); try { @@ -509,7 +539,7 @@ } catch (std::exception& e) { Debug::log(WARN, "Couldn't retrieve current systemd inhibits ({})", e.what()); } } - if (!IGNORE_DBUS_INHIBIT) { + if (!*IGNOREDBUSINHIBIT) { // attempt to register as ScreenSaver std::string paths[] = { "/org/freedesktop/ScreenSaver", @@ -537,8 +567,80 @@ m_sDBUSState.screenSaverServiceConnection->addMatch("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'", ::handleDbusNameOwnerChanged); - } catch (std::exception& e) { Debug::log(ERR, "Couldn't connect to session dbus\nerr: {}", e.what()); } + } catch (sdbus::Error& e) { + if (e.getName() == sdbus::Error::Name{"org.freedesktop.DBus.Error.FileExists"}) { + Debug::log(ERR, "Another service is already providing the org.freedesktop.ScreenSaver interface"); + Debug::log(ERR, "Is hypridle already running?"); + } else + Debug::log(ERR, "Failed to connect to ScreenSaver service\nerr: {}", e.what()); + } } systemConnection.reset(); } + +void CHypridle::handleInhibitOnDbusSleep(bool toSleep) { + if (m_inhibitSleepBehavior == SLEEP_INHIBIT_NONE || // + m_inhibitSleepBehavior == SLEEP_INHIBIT_LOCK_NOTIFY // Sleep inhibition handled via onLocked/onUnlocked + ) + return; + + if (!toSleep) + inhibitSleep(); + else + uninhibitSleep(); +} + +void CHypridle::inhibitSleep() { + if (!m_sDBUSState.login) { + Debug::log(WARN, "Can't inhibit sleep. Dbus logind interface is not available."); + return; + } + + if (m_sDBUSState.sleepInhibitFd.isValid()) { + Debug::log(WARN, "Called inhibitSleep, but previous sleep inhibitor is still active!"); + m_sDBUSState.sleepInhibitFd.reset(); + } + + auto method = m_sDBUSState.login->createMethodCall(sdbus::InterfaceName{"org.freedesktop.login1.Manager"}, sdbus::MethodName{"Inhibit"}); + method << "sleep"; + method << "hypridle"; + method << "Hypridle wants to delay sleep until it's before_sleep handling is done."; + method << "delay"; + + try { + auto reply = m_sDBUSState.login->callMethod(method); + + if (!reply || !reply.isValid()) { + Debug::log(ERR, "Failed to inhibit sleep"); + return; + } + + if (reply.isEmpty()) { + Debug::log(ERR, "Failed to inhibit sleep, empty reply"); + return; + } + + sdbus::UnixFd fd; + // This calls dup on the fd, no F_DUPFD_CLOEXEC :( + // There seems to be no way to get the file descriptor out of the reply other than that. + reply >> fd; + + // Setting the O_CLOEXEC flag does not work for some reason. Instead we make our own dupe and close the one from UnixFd. + auto immidiateFD = Hyprutils::OS::CFileDescriptor(fd.release()); + m_sDBUSState.sleepInhibitFd = immidiateFD.duplicate(F_DUPFD_CLOEXEC); + immidiateFD.reset(); // close the fd that was opened with dup + + Debug::log(LOG, "Inhibited sleep with fd {}", m_sDBUSState.sleepInhibitFd.get()); + } catch (const std::exception& e) { Debug::log(ERR, "Failed to inhibit sleep ({})", e.what()); } +} + +void CHypridle::uninhibitSleep() { + if (!m_sDBUSState.sleepInhibitFd.isValid()) { + Debug::log(ERR, "No sleep inhibitor fd to release"); + return; + } + + Debug::log(LOG, "Releasing the sleep inhibitor!"); + m_sDBUSState.sleepInhibitFd.reset(); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/core/Hypridle.hpp new/hypridle-0.1.7/src/core/Hypridle.hpp --- old/hypridle-0.1.5/src/core/Hypridle.hpp 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/src/core/Hypridle.hpp 2025-08-27 12:40:01.000000000 +0200 @@ -2,20 +2,25 @@ #include <memory> #include <vector> -#include <wayland-client.h> #include <sdbus-c++/sdbus-c++.h> +#include <hyprutils/os/FileDescriptor.hpp> #include <condition_variable> -#include "ext-idle-notify-v1-protocol.h" +#include "wayland.hpp" +#include "ext-idle-notify-v1.hpp" +#include "hyprland-lock-notify-v1.hpp" + +#include "../defines.hpp" class CHypridle { public: CHypridle(); struct SIdleListener { - ext_idle_notification_v1* notification = nullptr; - std::string onTimeout = ""; - std::string onRestore = ""; + SP<CCExtIdleNotificationV1> notification = nullptr; + std::string onTimeout = ""; + std::string onRestore = ""; + bool ignoreInhibit = false; }; struct SDbusInhibitCookie { @@ -33,27 +38,43 @@ void onInhibit(bool lock); + void onLocked(); + void onUnlocked(); + SDbusInhibitCookie getDbusInhibitCookie(uint32_t cookie); void registerDbusInhibitCookie(SDbusInhibitCookie& cookie); bool unregisterDbusInhibitCookie(const SDbusInhibitCookie& cookie); bool unregisterDbusInhibitCookies(const std::string& ownerID); + void handleInhibitOnDbusSleep(bool toSleep); + void inhibitSleep(); + void uninhibitSleep(); + private: void setupDBUS(); void enterEventLoop(); bool m_bTerminate = false; bool isIdled = false; + bool m_isLocked = false; int64_t m_iInhibitLocks = 0; + enum { + SLEEP_INHIBIT_NONE, + SLEEP_INHIBIT_NORMAL, + SLEEP_INHIBIT_LOCK_NOTIFY, + } m_inhibitSleepBehavior; + struct { - wl_display* display = nullptr; - wl_registry* registry = nullptr; - wl_seat* seat = nullptr; + wl_display* display = nullptr; + SP<CCWlRegistry> registry = nullptr; + SP<CCWlSeat> seat = nullptr; + SP<CCHyprlandLockNotifierV1> lockNotifier = nullptr; + SP<CCHyprlandLockNotificationV1> lockNotification = nullptr; } m_sWaylandState; struct { - ext_idle_notifier_v1* notifier = nullptr; + SP<CCExtIdleNotifierV1> notifier = nullptr; std::vector<SIdleListener> listeners; } m_sWaylandIdleState; @@ -61,8 +82,10 @@ struct { std::unique_ptr<sdbus::IConnection> connection; std::unique_ptr<sdbus::IConnection> screenSaverServiceConnection; + std::unique_ptr<sdbus::IProxy> login; std::vector<std::unique_ptr<sdbus::IObject>> screenSaverObjects; std::vector<SDbusInhibitCookie> inhibitCookies; + Hyprutils::OS::CFileDescriptor sleepInhibitFd; } m_sDBUSState; struct { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/defines.hpp new/hypridle-0.1.7/src/defines.hpp --- old/hypridle-0.1.5/src/defines.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hypridle-0.1.7/src/defines.hpp 2025-08-27 12:40:01.000000000 +0200 @@ -0,0 +1,8 @@ +#include "wayland.hpp" +#include "ext-idle-notify-v1.hpp" +#include "hyprland-lock-notify-v1.hpp" + +#include <hyprutils/memory/WeakPtr.hpp> +using namespace Hyprutils::Memory; +#define SP CSharedPointer +#define WP CWeakPointer diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/helpers/MiscFunctions.cpp new/hypridle-0.1.7/src/helpers/MiscFunctions.cpp --- old/hypridle-0.1.5/src/helpers/MiscFunctions.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hypridle-0.1.7/src/helpers/MiscFunctions.cpp 2025-08-27 12:40:01.000000000 +0200 @@ -0,0 +1,19 @@ +#include <filesystem> + +#include "MiscFunctions.hpp" + +std::string absolutePath(const std::string& rawpath, const std::string& currentDir) { + std::filesystem::path path(rawpath); + + // Handling where rawpath starts with '~' + if (!rawpath.empty() && rawpath[0] == '~') { + static const char* const ENVHOME = getenv("HOME"); + path = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2); + } + + // Handling e.g. ./, ../ + if (path.is_relative()) + return std::filesystem::weakly_canonical(std::filesystem::path(currentDir) / path); + else + return std::filesystem::weakly_canonical(path); +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/helpers/MiscFunctions.hpp new/hypridle-0.1.7/src/helpers/MiscFunctions.hpp --- old/hypridle-0.1.5/src/helpers/MiscFunctions.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hypridle-0.1.7/src/helpers/MiscFunctions.hpp 2025-08-27 12:40:01.000000000 +0200 @@ -0,0 +1,5 @@ +#pragma once + +#include <string> + +std::string absolutePath(const std::string&, const std::string&); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hypridle-0.1.5/src/main.cpp new/hypridle-0.1.7/src/main.cpp --- old/hypridle-0.1.5/src/main.cpp 2024-11-02 16:29:47.000000000 +0100 +++ new/hypridle-0.1.7/src/main.cpp 2025-08-27 12:40:01.000000000 +0200 @@ -15,6 +15,11 @@ else if (arg == "--quiet" || arg == "-q") Debug::quiet = true; + else if (arg == "--version" || arg == "-V") { + Debug::log(NONE, "hypridle v{}", HYPRIDLE_VERSION); + return 0; + } + else if (arg == "--config" || arg == "-c") { if (i + 1 >= argc) { Debug::log(NONE, "After " + arg + " you should provide a path to a config file."); @@ -32,6 +37,19 @@ return 1; } } + + else if (arg == "--help" || arg == "-h") { + Debug::log(NONE, + "Usage: hypridle [options]\n" + "Options:\n" + " -v, --verbose Enable verbose logging\n" + " -q, --quiet Suppress all output except errors\n" + " -V, --version Show version information\n" + " -c, --config <path> Specify a custom config file path\n" + " -h, --help Show this help message" + ); + return 0; + } } try { ++++++ hypridle.obsinfo ++++++ --- /var/tmp/diff_new_pack.bkcHsv/_old 2025-09-22 16:41:44.366603787 +0200 +++ /var/tmp/diff_new_pack.bkcHsv/_new 2025-09-22 16:41:44.374604123 +0200 @@ -1,5 +1,5 @@ name: hypridle -version: 0.1.5 -mtime: 1730561387 -commit: 26780ac51f6e7273e3934885036b7a7ed1a5af01 +version: 0.1.7 +mtime: 1756291201 +commit: 5430b73ddf148651bcf35fa39ed4d757c7534028
