This commit is contained in:
Maximilian Seidler 2026-04-13 23:43:52 -07:00 committed by GitHub
commit 8d2354ecca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 975 additions and 90 deletions

View file

@ -1,8 +1,13 @@
name: Build
name: Build (Nix)
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
on: [push, pull_request, workflow_dispatch]
jobs:
nix:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -42,7 +47,6 @@ jobs:
# with:
# name: hyprland
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
#
- name: Build
run: nix flake check --print-build-logs --keep-going
run: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}' -L --extra-substituters "https://hyprland.cachix.org"

15
.github/workflows/nix-ci.yml vendored Normal file
View file

@ -0,0 +1,15 @@
name: Nix
on: [push, pull_request, workflow_dispatch]
jobs:
hyprlock:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
uses: ./.github/workflows/nix-build.yml
secrets: inherit
test:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
needs: hyprlock
uses: ./.github/workflows/nix-test.yml
secrets: inherit

66
.github/workflows/nix-test.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: Test (Nix)
on:
workflow_call:
secrets:
CACHIX_AUTH_TOKEN:
required: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- 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 }}
# 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: 5G
# 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
#- uses: cachix/cachix-action@v15
# with:
# name: hyprland
# authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Run test VM
run: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#checks.x86_64-linux.tests' -L --extra-substituters "https://hyprland.cachix.org"
- name: Check exit status
run: grep 0 result/exit_status
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: logs
path: result/logs
- name: Upload traces
if: always()
uses: actions/upload-artifact@v4
with:
name: traces
path: result/traces

View file

@ -163,3 +163,13 @@ install(
FILES ${CMAKE_SOURCE_DIR}/assets/example.conf
DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/hypr
RENAME hyprlock.conf)
if(TESTS)
include(CTest)
message(STATUS "Building hyprlock test meta package")
enable_testing()
add_custom_target(tests)
add_subdirectory(tests)
endif()

12
flake.lock generated
View file

@ -62,11 +62,11 @@
]
},
"locked": {
"lastModified": 1772459870,
"narHash": "sha256-xxkK2Cvqxpf/4UGcJ/TyCwrvmiNWsKsJfFzHMp2bxis=",
"lastModified": 1774534854,
"narHash": "sha256-grFWyjph17nSuIGE+eh9oPEW5prXRzyWsQ4xCvXTzWc=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "e63f3a79334dec49f8eb1691f66f18115df04085",
"rev": "762166b516432ce4b02bfbae365f1daa6f88f76d",
"type": "github"
},
"original": {
@ -100,11 +100,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1772433332,
"narHash": "sha256-izhTDFKsg6KeVBxJS9EblGeQ8y+O8eCa6RcW874vxEc=",
"lastModified": 1774386573,
"narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cf59864ef8aa2e178cccedbe2c178185b0365705",
"rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
"type": "github"
},
"original": {

View file

@ -32,39 +32,42 @@
};
};
outputs =
{
self,
nixpkgs,
systems,
...
}@inputs:
let
inherit (nixpkgs) lib;
eachSystem = lib.genAttrs (import systems);
pkgsFor = eachSystem (
system:
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; [ hyprlock-with-deps ];
overlays = with self.overlays; [hyprlock-with-deps];
}
);
in
{
overlays = import ./nix/overlays.nix { inherit inputs lib self; };
packages = eachSystem (system: {
default = self.packages.${system}.hyprlock;
inherit (pkgsFor.${system}) hyprlock;
);
pkgsDebugFor = eachSystem (system:
import nixpkgs {
localSystem = system;
overlays = with self.overlays; [hyprlock-debug hyprlock-with-deps];
});
in {
overlays = import ./nix/overlays.nix {inherit inputs lib self;};
homeManagerModules = {
default = self.homeManagerModules.hyprlock;
hyprlock = builtins.throw "hyprlock: the flake HM module has been removed. Use the module from Home Manager upstream.";
};
packages = eachSystem (system: {
default = self.packages.${system}.hyprlock;
inherit (pkgsFor.${system}) hyprlock;
inherit (pkgsDebugFor.${system}) hyprlock-debug hyprlock-test-meta;
});
checks = eachSystem (system: self.packages.${system});
formatter = eachSystem (system: pkgsFor.${system}.nixfmt-tree);
homeManagerModules = {
default = self.homeManagerModules.hyprlock;
hyprlock = builtins.throw "hyprlock: the flake HM module has been removed. Use the module from Home Manager upstream.";
};
checks = eachSystem (system: self.packages.${system} // (import ./nix/tests/default.nix inputs pkgsFor.${system}));
formatter = eachSystem (system: pkgsFor.${system}.nixfmt-tree);
};
}

View file

@ -1,6 +1,7 @@
{
lib,
stdenv,
stdenvAdapters,
cmake,
pkg-config,
cairo,
@ -19,49 +20,73 @@
wayland,
wayland-protocols,
wayland-scanner,
debug ? false,
version ? "git",
shortRev ? "",
}:
stdenv.mkDerivation {
pname = "hyprlock";
inherit version;
}: let
inherit (builtins) foldl';
inherit (lib.lists) flatten;
inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.strings) hasSuffix optionalString;
src = ../.;
nativeBuildInputs = [
cmake
pkg-config
hyprwayland-scanner
wayland-scanner
adapters = flatten [
stdenvAdapters.useMoldLinker
(lib.optional debug stdenvAdapters.keepDebugInfo)
];
buildInputs = [
cairo
libdrm
libGL
libxkbcommon
libgbm
hyprgraphics
hyprlang
hyprutils
pam
pango
sdbus-cpp_2
systemdLibs
wayland
wayland-protocols
];
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
in
customStdenv.mkDerivation {
pname = "hyprlock${optionalString debug "-debug"}";
inherit version;
cmakeFlags = lib.mapAttrsToList lib.cmakeFeature {
HYPRLOCK_COMMIT = shortRev;
HYPRLOCK_VERSION_COMMIT = ""; # Intentionally left empty (hyprlock --version will always print the commit)
};
src = cleanSourceWith {
filter = name: _type: let
baseName = baseNameOf (toString name);
in
! (hasSuffix ".nix" baseName);
src = cleanSource ../.;
};
meta = {
homepage = "https://github.com/hyprwm/hyprlock";
description = "A gpu-accelerated screen lock for Hyprland";
license = lib.licenses.bsd3;
platforms = lib.platforms.linux;
mainProgram = "hyprlock";
};
}
nativeBuildInputs = [
cmake
pkg-config
hyprwayland-scanner
wayland-scanner
];
buildInputs = [
cairo
libdrm
libGL
libxkbcommon
libgbm
hyprgraphics
hyprlang
hyprutils
pam
pango
sdbus-cpp_2
systemdLibs
wayland
wayland-protocols
];
cmakeFlags = lib.mapAttrsToList lib.cmakeFeature {
HYPRLOCK_COMMIT = shortRev;
HYPRLOCK_VERSION_COMMIT = ""; # Intentionally left empty (hyprlock --version will always print the commit)
};
cmakeBuildType =
if debug
then "Debug"
else "Release";
meta = {
homepage = "https://github.com/hyprwm/hyprlock";
description = "A gpu-accelerated screen lock for Hyprland";
license = lib.licenses.bsd3;
platforms = lib.platforms.linux;
mainProgram = "hyprlock";
};
}

View file

@ -2,19 +2,15 @@
lib,
inputs,
self,
}:
let
mkDate =
longDate:
(lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
}: let
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
in
{
in {
default = inputs.self.overlays.hyprlock;
hyprlock-with-deps = lib.composeManyExtensions [
@ -37,4 +33,18 @@ in
shortRev = self.sourceInfo.shortRev or "dirty";
};
};
hyprlock-debug = lib.composeManyExtensions [
self.overlays.hyprlock
# Dependencies
(final: prev: {
hyprutils = prev.hyprutils.override {debug = true;};
hyprgraphics = prev.hyprgraphics.override {debug = true;};
hyprlock-debug = prev.hyprlock.override {debug = true;};
hyprlock-test-meta = prev.callPackage ./test-meta.nix {
stdenv = prev.gcc15Stdenv;
version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
};
})
];
}

45
nix/test-meta.nix Normal file
View file

@ -0,0 +1,45 @@
{
cmake,
egl-wayland,
hyprland-protocols,
hyprlock,
hyprwayland-scanner,
lib,
pkg-config,
stdenv,
stdenvAdapters,
wayland-scanner,
version ? "git",
}: let
inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.strings) hasSuffix;
in
stdenv.mkDerivation (finalAttrs: {
pname = "hyprlock-test-meta";
inherit version;
src = cleanSourceWith {
filter = name: _type: let
baseName = baseNameOf (toString name);
in
! (hasSuffix ".nix" baseName);
src = cleanSource ../tests;
};
nativeBuildInputs = [
cmake
hyprland-protocols
hyprwayland-scanner
pkg-config
wayland-scanner
];
buildInputs = hyprlock.buildInputs;
meta = {
homepage = "https://github.com/hyprwm/hyprlock";
description = "Hyprlock testing utility";
license = lib.licenses.bsd3;
platforms = hyprlock.meta.platforms;
};
})

164
nix/tests/default.nix Normal file
View file

@ -0,0 +1,164 @@
inputs: pkgs: let
inherit (pkgs) lib;
inherit (lib.lists) flatten;
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
env = {
#"AQ_TRACE" = "1";
#"HYPRLAND_TRACE" = "1";
"HYPRLAND_HEADLESS_ONLY" = "1";
"XDG_RUNTIME_DIR" = "/tmp";
"XDG_CACHE_HOME" = "/tmp";
};
envAddToSystemdRun = lib.concatStringsSep " " (
lib.mapAttrsToList (k: v: "--setenv ${k}=${v} ") env
);
APITRACE_RECORD = true;
APITRACE_RECORD_PY = if APITRACE_RECORD then "True" else "False";
in {
tests = pkgs.testers.runNixOSTest {
name = "hyprlock-tests";
nodes.machine = {pkgs, ...}: {
environment.systemPackages = with pkgs; flatten [
# Programs needed for tests
coreutils # date command
procps # pidof
(lib.optional APITRACE_RECORD apitrace)
];
# Enabled by default for some reason
services.speechd.enable = false;
environment.variables = env;
programs.hyprland = {
enable = true;
#withUWSM = true
};
programs.hyprlock = {
enable = true;
package = flake.hyprlock;
};
networking.dhcpcd.enable = false;
# Disable portals
xdg.portal.enable = lib.mkForce false;
# Autologin root into tty
services.getty.autologinUser = "alice";
system.stateVersion = "24.11";
environment.etc."hyprlock/assets".source = "${flake.hyprlock-test-meta}/share/hypr/assets/";
users.users.alice = {
isNormalUser = true;
# password: abcdefghijklmnopqrstuvwxyz1234567890-=!@#$%^&*()_+[]{};\':\\"]\\|,./<>?`~äöüćńóśź
hashedPassword = "$y$j9T$s.atBE5..ISB2OoPWrXnU1$.8yaRmR9iBV9e.Q9wM1hG0ciMMYLGhpmDqsJo8Sjiv2";
};
virtualisation = {
cores = 4;
# Might crash with less
memorySize = 8192;
resolution = {
x = 1920;
y = 1080;
};
# Doesn't seem to do much, thought it would fix XWayland crashing
qemu.options = ["-vga none -device virtio-gpu-pci"];
};
};
testScript = ''
from pathlib import Path
# Wait for tty to be up
machine.wait_for_unit("multi-user.target")
# Startup Hyprland as the test compositor for hyprlock
print("Running Hyprland")
machine.execute("systemd-run -q -u hyprland --uid $(id -u alice) -p RuntimeMaxSec=60 ${envAddToSystemdRun} --setenv PATH=$PATH ${pkgs.hyprland}/bin/Hyprland -c ${flake.hyprlock-test-meta}/share/hypr/hyprland.conf")
machine.wait_for_file("/tmp/hyprland_exec_once_notification")
machine.execute("sleep 1") # slack just to be save
_, systeminfo = machine.execute("hyprctl --instance 0 systeminfo")
print(systeminfo)
test_files = [Path("${flake.hyprlock}/share/hypr/hyprlock.conf")] # also test the example configuration
test_files += list(Path("${flake.hyprlock-test-meta}/share/hypr/configs/").iterdir())
for hyprlock_config in test_files:
print(f"Testing configuration file {hyprlock_config}")
log_file_path = "/tmp/hyprlock_test_" + hyprlock_config.stem
hyprlock_cmd = f"hyprlock --config {str(hyprlock_config)} -v 2>&1 >{log_file_path}; echo $? > /tmp/exit_status"
if ${APITRACE_RECORD_PY}:
hyprlock_cmd = f"${lib.getExe' pkgs.apitrace "apitrace"} trace --output {log_file_path}.trace --api egl {hyprlock_cmd}"
machine.execute(f"hyprctl --instance 0 dispatch exec '{hyprlock_cmd}'")
wait_for_lock_exit_status, out = machine.execute("WAYLAND_DISPLAY=wayland-1 ${lib.getExe' flake.hyprlock-test-meta "wait-for-lock"}")
print(f"Wait for lock exit code: {wait_for_lock_exit_status}")
if wait_for_lock_exit_status != 0:
break
_, hyprlock_pid = machine.execute("pidof hyprlock")
print(f"Hyprlock pid {hyprlock_pid}")
# wrong password
machine.send_chars("asdf\n")
machine.execute("sleep 3") # default fail_timeout is 2 seconds
# correct password
machine.send_chars("abcdefghijklmnopqrstuvwxyz1234567890-=!@#$%^&*()_+[]{};':\"]\\|,./<>?`~")
machine.send_key("alt_r-a")
machine.send_key("alt_r-o")
machine.send_key("alt_r-u")
machine.send_key("alt_r-apostrophe")
machine.send_key("c")
machine.send_key("alt_r-apostrophe")
machine.send_key("n")
machine.send_key("alt_r-apostrophe")
machine.send_key("o")
machine.send_key("alt_r-apostrophe")
machine.send_key("s")
machine.send_key("alt_r-apostrophe")
machine.send_key("z")
machine.send_chars("\n")
machine.execute(f"waitpid {hyprlock_pid}")
_, exit_status = machine.execute("cat /tmp/exit_status")
print(f"Hyprlock exited with {exit_status}")
machine.copy_from_vm(log_file_path, "logs")
if ${APITRACE_RECORD_PY}:
machine.copy_from_vm(log_file_path + ".trace", "traces")
_, out = machine.execute(f"cat {log_file_path}")
print(f"Hyprlock log:\n{out}")
_, out = machine.execute(f"cat {log_file_path}")
if not exit_status or int(exit_status) != 0:
break
machine.execute("hyprctl --instance 0 dispatch exit")
_, exit_status = machine.execute("cat /tmp/exit_status")
# For the github runner, just to make sure wen don't accidentally succeed
if not exit_status.strip():
_, __ = machine.execute("echo 99 >/tmp/exit_status")
exit_status = "99"
machine.copy_from_vm("/tmp/exit_status")
assert int(exit_status) == 0, f"hyprlock exit code != 0 (exited with {exit_status})"
# Finally - shutdown
machine.shutdown()
'';
};
}

57
tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.27)
project(hyprlock-test-meta DESCRIPTION "Package files used for hyprlock's integration tests")
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 23)
find_package(PkgConfig REQUIRED)
find_package(hyprwayland-scanner 0.4.4 REQUIRED)
pkg_check_modules(wfldeps REQUIRED IMPORTED_TARGET
hyprland-protocols>=0.6.0
hyprutils>=0.11.0
wayland-client
wayland-protocols>=1.35
)
add_executable(wait-for-lock "waitForLock.cpp")
target_link_libraries(wait-for-lock PRIVATE PkgConfig::wfldeps)
# protocols
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "Found hyprland-protocols at ${HYPRLAND_PROTOCOLS}")
make_directory(${CMAKE_SOURCE_DIR}/protocols)
target_include_directories(wait-for-lock PRIVATE ${CMAKE_SOURCE_DIR}/protocols)
# wayland client
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/)
target_sources(wait-for-lock PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp)
# hyprland-lock-notify-v1
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/hyprland-lock-notify-v1.cpp
${CMAKE_SOURCE_DIR}/protocols/hyprland-lock-notify-v1.hpp
COMMAND hyprwayland-scanner --client ${HYPRLAND_PROTOCOLS}/protocols/hyprland-lock-notify-v1.xml
${CMAKE_SOURCE_DIR}/protocols/)
target_sources(wait-for-lock PRIVATE ${CMAKE_SOURCE_DIR}/protocols/hyprland-lock-notify-v1.cpp)
install(TARGETS wait-for-lock)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/hyprland.conf
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
file(GLOB_RECURSE TESTCONFIGS CONFIGURE_DEPENDS "configs/*.conf")
install(FILES ${TESTCONFIGS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr/configs)
file(GLOB_RECURSE TESTCONFIGS CONFIGURE_DEPENDS "assets/*.png")
install(FILES ${TESTCONFIGS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr/assets)

BIN
tests/assets/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
tests/assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

64
tests/configs/images.conf Normal file
View file

@ -0,0 +1,64 @@
background {
monitor=
path=/etc/hyprlock/assets/background.png
}
image {
monitor=
path=/etc/hyprlock/assets/avatar.png
size=150
position=0, 50
halign=center
valign=center
border_size=3
shadow_passes=1
shadow_size=5
shadow_boost=0.5
}
general {
hide_cursor=true
}
input-field {
monitor=
size=50, 50
capslock_color=rgb(CB7459)
dots_rounding=0
dots_size=0.35
dots_spacing=0.1
dots_text_format=*
fade_on_empty=true
font_color=rgb(8F8F8F)
font_family=Noto Sans
inner_color=rgba(00000000)
outer_color=rgba(FFF7EDa0)
outline_thickness=4
position=0, -5%
rounding=-1
halign=center
valign=center
}
label {
monitor=
color=rgb(FFF7ED)
font_family=Noto Sans
font_size=40
position=0, 10%
text=$TIME $FAIL
halign=center
valign=center
}
label {
monitor=
color=rgba(BEBEBEA0)
font_family=Noto Sans
font_size=24
position=0, 15%
text=cmd[update:10000] date '+%A %d %B %Y'
halign=center
valign=center
}

View file

@ -0,0 +1,90 @@
background {
color=rgba(255, 255, 255, 0)
}
shape {
size = 90%, 90%
position = 0, 0
color = rgba(0, 0, 0, 0.5)
border_color = rgba(255, 255, 0, 1.0)
rounding = 5
border_size = 10
halign = center
valign = center
}
shape {
size = 50%, 50%
position = -10, -10
color = rgb(0, 0, 255)
rounding = 5
border_size = 0
halign = center
valign = center
}
shape {
size = 50%, 50%
position = +10, +10
color = rgb(00ff00)
color = rgba(00ff00ff)
rounding = 5
border_size = 0
halign = center
valign = center
}
# Top left corner
shape {
size = 10%, 10%
position = 10, -10
color = rgba(0, 255, 0, 1.0)
rounding = 5
border_size = 0
halign = left
valign = top
}
# Top right corner
shape {
size = 10%, 10%
position = -10, -10
color = rgba(0, 255, 0, 1.0)
rounding = 5
border_size = 0
halign = right
valign = top
}
# Bottom left corner
shape {
size = 10%, 10%
position = 10, 10
color = rgba(0, 255, 0, 1.0)
rounding = 5
border_size = 0
halign = left
valign = bottom
}
# Bottom right corner
shape {
size = 10%, 10%
position = -10, 10
color = rgba(0, 255, 0, 1.0)
rounding = 5
border_size = 0
halign = right
valign = bottom
}
# Origin of shape centered
shape {
size = 10%, 10%
position = 50%, 50%
color = rgb(ff0000)
rounding = 5
border_size = 0
halign = left
valign = bottom
}

View file

@ -0,0 +1,221 @@
background {
monitor=
blur_passes=2
blur_size=10
path=screenshot
}
general {
hide_cursor=true
}
input-field {
monitor=
size=50, 50
capslock_color=rgb(CB7459)
dots_rounding=0
dots_size=0.35
dots_spacing=0.1
dots_text_format=*
fade_on_empty=false
font_color=rgb(8F8F8F)
font_family=Noto Sans
inner_color=rgba(00000000)
outer_color=rgba(FFF7EDa0)
outline_thickness=4
placeholder_text=$PROMPT
fail_text=$FAIL <b>($ATTEMPTS)</b>
position=0, -5%
rounding=-1
halign=center
valign=center
}
label {
monitor=
color=rgb(FFF7ED)
font_family=Noto Sans
font_size=40
position=0, 10%
text=$TIME $FAIL
halign=center
valign=center
}
label {
monitor=
color=rgba(BEBEBEA0)
font_family=Noto Sans
font_size=24
position=0, 15%
text=cmd[update:10000] date '+%A %d %B %Y'
halign=center
valign=center
}
label {
text = cmd[update:200] date +'%H:%M:%S:%N'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, -200
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S:%N'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, -150
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S:%N'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, -100
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S:%N'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, -50
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 0
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 50
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 100
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 150
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 200
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 250
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 300
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 350
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 400
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 450
halign = left
valign = center
}
label {
text = cmd[update:200] date +'%H:%M:%S'
color = rgba(129, 162, 190, 1.0)
font_size = 35
font_family = Noto Sans
position = 150, 500
halign = left
valign = center
}

41
tests/hyprland.conf Normal file
View file

@ -0,0 +1,41 @@
monitor = Virtual-1,1920x1080@60,auto-right,1
monitor = ,disabled
input {
# to type german and polish specific letters via compose keys
kb_layout = eu
}
render {
ctm_animation = 0
cm_enabled = 0
cm_fs_passthrough = 0
}
animations {
enabled = 0
}
decoration {
shadow {
enabled = 0
}
}
xwayland {
enabled = 0
}
misc {
disable_hyprland_logo = 1
disable_splash_rendering = 1
force_default_wallpaper = 0
key_press_enables_dpms = 1
}
debug {
disable_logs = 0
}
# we use this in nix/tests/default.nix to be able to wait for hyprland startup
exec-once = echo "startup" > /tmp/hyprland_exec_once_notification

70
tests/waitForLock.cpp Normal file
View file

@ -0,0 +1,70 @@
// This program exits when the wayland session gets locked, or 10 seconds have passed.
// In case it is already locked, it shall return immediatly.
// It uses hyprland-lock-notify to accomplish that.
#include "hyprland-lock-notify-v1.hpp"
#include "wayland.hpp"
#include <fcntl.h>
#include <print>
#include <hyprutils/memory/SharedPtr.hpp>
#include <thread>
using namespace Hyprutils::Memory;
#define SP CSharedPointer
struct SSessionLockState {
SP<CCHyprlandLockNotifierV1> m_lockNotifier = nullptr;
SP<CCHyprlandLockNotificationV1> m_lockNotification = nullptr;
bool m_didLock = false;
};
int main(int argc, char** argv) {
auto wlDisplay = wl_display_connect(nullptr);
if (!wlDisplay) {
std::println(stderr, "Failed to connect to Wayland display");
return -1;
}
auto state = makeShared<SSessionLockState>();
auto wlRegistry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(wlDisplay));
wlRegistry->setGlobal([state](CCWlRegistry* r, uint32_t name, const char* interface, uint32_t version) {
const std::string IFACE = interface;
if (IFACE == hyprland_lock_notifier_v1_interface.name)
state->m_lockNotifier =
makeShared<CCHyprlandLockNotifierV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &hyprland_lock_notifier_v1_interface, version));
});
wl_display_roundtrip(wlDisplay);
if (!state->m_lockNotifier) {
std::print(stderr, "Failed to bind to lock notifier\n");
return -1;
}
state->m_lockNotification = makeShared<CCHyprlandLockNotificationV1>(state->m_lockNotifier->sendGetLockNotification());
state->m_lockNotification->setLocked([state](auto) { state->m_didLock = true; });
wl_display_flush(wlDisplay);
const auto STARTTP = std::chrono::system_clock::now();
while (!state->m_didLock) {
if (wl_display_prepare_read(wlDisplay) == 0) {
wl_display_read_events(wlDisplay);
wl_display_dispatch_pending(wlDisplay);
} else {
wl_display_dispatch(wlDisplay);
}
if (std::chrono::system_clock::now() - STARTTP > std::chrono::seconds(10)) {
std::print(stderr, "Timeout waiting for the lock event\n");
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}