diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5025e539a3..129023f8a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,9 +9,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
## Unreleased
### Fixed
+- Core: app launchers no longer show a false error when an unrelated `DEBUG` variable contains a non-boolean value such as `release`
+- Desktop: the generated battery notification startup command now launches `batterynotify.lua` instead of the removed shell implementation
+- Hyprland: Lua keybinds again match the documented shortcuts for window management, screenshots, wallpapers, Waybar, selectors, workspaces and the scratchpad
+- Screenshot: area capture now uses the fixed upstream Grimblast selector instead of prompting for the region twice
+- Screenshot: Satty defaults to the compatible GTK GL renderer when no explicit `GSK_RENDERER` is configured
+- Screenshot: the "print all monitors" keybind now invokes a full-output capture instead of capturing only the focused monitor
- Waybar `hyprland/workspaces` module adapted to use lua dispatchers
- Hyprland: `Super + Ctrl + arrows` no longer changes the group and the workspace at the same time.
- Group navigation stays on `Super + Alt + arrows`
+ Group navigation uses `Super + Ctrl + H/L`
- Hyprland: the right Control key no longer hides Waybar on its own. Hiding moved to `Super + Ctrl + B`
- Hyprland: workspaces 11-20 on the numpad respond again
- OCR: the language list in the result notification is no longer split across arguments
diff --git a/Configs/.local/lib/hyde/app.sh b/Configs/.local/lib/hyde/app.sh
index f26ebf956a..3f3c1d5bf2 100755
--- a/Configs/.local/lib/hyde/app.sh
+++ b/Configs/.local/lib/hyde/app.sh
@@ -5,6 +5,14 @@
# @cmd: service
# @cmd.desc: Run a script as a systemd service
+# app2unit and xdg-terminal-exec fall back to the generic DEBUG variable,
+# which may contain non-boolean build modes such as "release".
+# Give both helpers an explicit, scoped default while preserving any
+# user-provided values on both execution paths.
+APP2UNIT_DEBUG=${APP2UNIT_DEBUG:-0}
+XTE_DEBUG=${XTE_DEBUG:-0}
+export APP2UNIT_DEBUG XTE_DEBUG
+
if [ -d "/run/systemd/system" ]; then
exec app2unit "$@"
fi
diff --git a/Configs/.local/lib/hyde/screenshot.sh b/Configs/.local/lib/hyde/screenshot.sh
index ce6c01533d..9148f2d437 100755
--- a/Configs/.local/lib/hyde/screenshot.sh
+++ b/Configs/.local/lib/hyde/screenshot.sh
@@ -90,6 +90,14 @@ fi
[[ -n ${SCREENSHOT_ANNOTATION_ARGS[*]} ]] && annotation_args+=("${SCREENSHOT_ANNOTATION_ARGS[@]}")
+run_annotation_tool() {
+ if [[ $annotation_tool == "satty" ]]; then
+ GSK_RENDERER="${GSK_RENDERER:-gl}" "$annotation_tool" "${annotation_args[@]}"
+ else
+ "$annotation_tool" "${annotation_args[@]}"
+ fi
+}
+
take_screenshot() {
local mode=$1
shift
@@ -102,7 +110,7 @@ take_screenshot() {
print_log -g "Executing screenshot command: ${command[*]}"
if eval "${command[*]}"; then
[[ ${SCREENSHOT_ANNOTATION_ENABLED} == false ]] && return 0
- if ! "$annotation_tool" "${annotation_args[@]}"; then
+ if ! run_annotation_tool; then
send_notifs -r 9 -a "HyDE Alert" "Screenshot Error" "Failed to open annotation tool"
return 1
fi
diff --git a/Configs/.local/share/hyde/schema/config.md b/Configs/.local/share/hyde/schema/config.md
index 1aa1a23fdf..fa381f9632 100644
--- a/Configs/.local/share/hyde/schema/config.md
+++ b/Configs/.local/share/hyde/schema/config.md
@@ -8,7 +8,7 @@ Users are encouraged to use an editor that support schema validation to ensure t
---
### [battery.notify]
-batterynotify.sh configuration.
+Battery notification configuration.
| Key | Description | Default |
| --- | ----------- | ------- |
@@ -155,7 +155,7 @@ Generic desktop autostart configuration. Use this header instead of hyprland-sta
| applet_removable_media | Removable media applet. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-removable-media-applet.service -t service -- udiskie --no-automount --smart-tray |
| auth_dialogue | Authentication dialogue. | hyde-shell app -t service -- polkitkdeauth.sh |
| bar | Bar. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-bar.scope -t scope -- waybar.py --watch |
-| battery_notify | Battery notification script. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.sh |
+| battery_notify | Battery notification script. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.lua |
| blue_light_filter_daemon | Blue-light filter daemon. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-blue-light-filter.service -t service -- hyprsunset |
| clipboard_persist | Clipboard persist daemon. | hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-clipboard-persist.service -t service wl-clip-persist --clipboard regular |
| dbus_share_picker | DBus share picker (for XDG portal / XDPH). | dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE XDG_SESSION_DESKTOP XDG_CONFIG_HOME QT_QPA_PLATFORMTHEME |
diff --git a/Configs/.local/share/hyde/schema/config.toml b/Configs/.local/share/hyde/schema/config.toml
index cf3743464e..37588cbebb 100644
--- a/Configs/.local/share/hyde/schema/config.toml
+++ b/Configs/.local/share/hyde/schema/config.toml
@@ -98,7 +98,7 @@ font = "JetBrainsMono Nerd Font" # Font for bookmarks.
[wlogout]
style = 2 # Style for wlogout.
-# batterynotify.sh configuration.
+# Battery notification configuration.
[battery.notify]
timer = 120 # Timer for battery notifications.
notify = 1140 # Notification threshold.
@@ -321,7 +321,7 @@ clipboard_persist = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-clipboard-persi
wallpaper = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-wallpaper.service -t service -- wallpaper.sh --start --global" # Wallpaper script.
bar = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-bar.scope -t scope -- waybar.py --watch" # Bar.
notifications = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-notifications.service -t service -- dunst" # Notification daemon.
-battery_notify = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.sh" # Battery notification script.
+battery_notify = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.lua" # Battery notification script.
applet_network_manager = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-network-manager-applet.service -t service -- nm-applet --indicator" # Network manager applet.
applet_removable_media = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-removable-media-applet.service -t service -- udiskie --no-automount --smart-tray" # Removable media applet.
applet_bluetooth = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-bluetooth-applet.service -t service -- blueman-applet" # Bluetooth applet.
diff --git a/Configs/.local/share/hyde/schema/config.toml.json b/Configs/.local/share/hyde/schema/config.toml.json
index ace7cb5899..73701fb369 100644
--- a/Configs/.local/share/hyde/schema/config.toml.json
+++ b/Configs/.local/share/hyde/schema/config.toml.json
@@ -402,7 +402,7 @@
}
},
"battery.notify": {
- "description": "batterynotify.sh configuration.",
+ "description": "Battery notification configuration.",
"type": "object",
"properties": {
"timer": {
@@ -1345,7 +1345,7 @@
"type": "string"
},
"battery_notify": {
- "default": "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.sh",
+ "default": "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.lua",
"description": "Battery notification script.",
"type": "string"
},
diff --git a/Configs/.local/share/hyde/schema/schema.toml b/Configs/.local/share/hyde/schema/schema.toml
index 819c7d4cda..006af3ab8a 100644
--- a/Configs/.local/share/hyde/schema/schema.toml
+++ b/Configs/.local/share/hyde/schema/schema.toml
@@ -362,7 +362,7 @@ options = [1, 2]
type = "integer"
[properties."battery.notify"]
-description = "batterynotify.sh configuration."
+description = "Battery notification configuration."
type = "object"
[properties."battery.notify".properties.timer]
@@ -1186,7 +1186,7 @@ description = "Notification daemon."
type = "string"
[properties."desktop.start".properties.battery_notify]
-default = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.sh"
+default = "hyde-shell app -u hyde-$XDG_SESSION_DESKTOP-battery-notify.service -t service -- batterynotify.lua"
description = "Battery notification script."
type = "string"
diff --git a/Configs/.local/share/hypr/lua/key_binds.lua b/Configs/.local/share/hypr/lua/key_binds.lua
index 2503159f3d..f5eb8d2e24 100644
--- a/Configs/.local/share/hypr/lua/key_binds.lua
+++ b/Configs/.local/share/hypr/lua/key_binds.lua
@@ -50,7 +50,7 @@ end
_F = {description = "[Launcher|Apps] terminal emulator"}
hl.bind(MOD .. " + T", hl.dsp.exec_cmd(_apps.terminal), _F)
_F = {description = "[Launcher|Apps] dropdown terminal"}
-hl.bind(MOD .. " + grave", hl.dsp.exec_cmd("hyde-shell pypr toggle console"), _F)
+hl.bind(MOD .. " + ALT + T", hl.dsp.exec_cmd("hyde-shell pypr toggle console"), _F)
_F = {description = "[Launcher|Apps] file explorer"}
hl.bind(MOD .. " + E", hl.dsp.exec_cmd(_apps.explorer), _F)
_F = {description = "[Launcher|Apps] browser"}
@@ -63,10 +63,12 @@ hl.bind("CTRL + SHIFT + ESCAPE", hl.dsp.exec_cmd("hyde-shell system.monitor.sh")
local _wm = "Window Management"
_F = {description = "[Window Management] close focused window"}
hl.bind(MOD .. " + Q", hl.dsp.window.close(), _F)
+_F = {description = "[Window Management] close focused window"}
+hl.bind("ALT + F4", hl.dsp.window.close(), _F)
_F = {description = "[Window Management] kill focused window"}
hl.bind(MOD .. "+ ALT + F4", hl.dsp.window.kill(), _F)
_F = {description = "[Window Management] exit hyprland session"}
-hl.bind("CTRL + Delete", hl.dsp.exit(), _F)
+hl.bind(MOD .. " + Delete", hl.dsp.exit(), _F)
_F = {description = "[Window Management] toggle float true"}
hl.bind(MOD .. " + W", hl.dsp.window.float({action = "toggle"}), _F)
_F = {description = "[Window Management] toggle group"}
@@ -76,9 +78,9 @@ hl.bind("ALT + P", hl.dsp.window.pseudo(), _F)
-- bindd = $mainMod, G, $d toggle group,exec, hydectl tabs
_F = {description = "[Window Management] cycle fullscreen"}
-hl.bind(MOD .. " + F11", cycle_fullscreen, _F)
+hl.bind("SHIFT + F11", cycle_fullscreen, _F)
_F = {description = "[Window Management] toggle pin"}
-hl.bind(MOD .. " + F", hl.dsp.exec_cmd(hyde.sh.window.pin()), _F)
+hl.bind(MOD .. " + SHIFT + F", hl.dsp.exec_cmd(hyde.sh.window.pin()), _F)
_F = {description = "[Window Management] logout menu"}
hl.bind("CTRL + ALT + DELETE", hl.dsp.exec_cmd(hyde.sh.session.logout.launcher()), _F)
-- ALT_R is a keysym, not a modifier: "ALT_R + CONTROL_R" resolves to a bare
@@ -88,12 +90,10 @@ hl.bind(MOD .. " + CTRL + B", hl.dsp.exec_cmd(hyde.sh.waybar("--hide")), _F)
_F = {description = "[Window Management] lock session"}
hl.bind(MOD .. " + L", hl.dsp.exec_cmd(hyde.sh.session.lock()), _F)
--- Group navigation lives on ALT only. The CTRL variant collided with relative
--- workspace navigation further down, which binds the same combo.
_F = {description = "[Window Management|Group Navigation] change active group backwards"}
-hl.bind(MOD .. " + ALT + Left", hl.dsp.group.prev(), _F)
+hl.bind(MOD .. " + CTRL + H", hl.dsp.group.prev(), _F)
_F = {description = "[Window Management|Group Navigation] change active group forwards"}
-hl.bind(MOD .. " + ALT + Right", hl.dsp.group.next(), _F)
+hl.bind(MOD .. " + CTRL + L", hl.dsp.group.next(), _F)
-- $d=[$wm|Change focus]
@@ -122,15 +122,15 @@ hl.bind("ALT + ALT_L", hl.dsp.exec_cmd(hyde.sh.altab("--apply")), _F)
-- # Resize kwindows
_F = {description = "[Window Management|Resize Active Window] resize window right", repeating = true}
-hl.bind(MOD .. " + EQUAL", hl.dsp.window.resize({x = 30, y = 0, relative = true}), _F)
+hl.bind(MOD .. " + SHIFT + RIGHT", hl.dsp.window.resize({x = 30, y = 0, relative = true}), _F)
_F = {description = "[Window Management|Resize Active Window] resize window left", repeating = true}
-hl.bind(MOD .. " + MINUS", hl.dsp.window.resize({x = -30, y = 0, relative = true}), _F)
+hl.bind(MOD .. " + SHIFT + LEFT", hl.dsp.window.resize({x = -30, y = 0, relative = true}), _F)
_F = {description = "[Window Management|Resize Active Window] resize window up", repeating = true}
-hl.bind(MOD .. " + SHIFT + EQUAL", hl.dsp.window.resize({x = 0, y = -30, relative = true}), _F)
+hl.bind(MOD .. " + SHIFT + UP", hl.dsp.window.resize({x = 0, y = -30, relative = true}), _F)
_F = {description = "[Window Management|Resize Active Window] resize window down", repeating = true}
-hl.bind(MOD .. " + SHIFT + MINUS", hl.dsp.window.resize({x = 0, y = 30, relative = true}), _F)
+hl.bind(MOD .. " + SHIFT + DOWN", hl.dsp.window.resize({x = 0, y = 30, relative = true}), _F)
-- TEXT = hl.get_active_window().floating
@@ -158,7 +158,7 @@ hl.bind(MOD .. " + X", hl.dsp.window.resize(), _F)
-- $d=[$wm]
_F = {description = "[Layout Management|Dwindle] toggle split"}
-_F = {description = "[Layout Management|Scrolling] toggle fit"}
+hl.bind(MOD .. " + J", hl.dsp.layout("togglesplit"), _F)
_F = {description = "[Launcher|Rofi menus] application finder"}
hl.bind(MOD .. " + A", hl.dsp.exec_cmd(hyde.sh.menu.apps()), _F)
@@ -190,6 +190,12 @@ hl.bind(MOD .. " + SHIFT + slash", hl.dsp.exec_cmd(hyde.sh.menu.search()), _F)
-- # binddel = , F11, $d decrease volume , exec, hyde-shell volumecontrol.sh -o d # decrease volume
-- # binddel = , F12, $d increase volume , exec, hyde-shell volumecontrol.sh -o i # increase volume
+_F = {description = "[Hardware Controls|Audio] un/mute output", locked = true}
+hl.bind("F10", hl.dsp.exec_cmd(hyde.sh.volumecontrol("-o", "m")), _F)
+_F = {description = "[Hardware Controls|Audio] decrease volume", locked = true, repeating = true}
+hl.bind("F11", hl.dsp.exec_cmd(hyde.sh.volumecontrol("-o", "d")), _F)
+_F = {description = "[Hardware Controls|Audio] increase volume", locked = true, repeating = true}
+hl.bind("F12", hl.dsp.exec_cmd(hyde.sh.volumecontrol("-o", "i")), _F)
_F = {description = "[Hardware Controls|Audio] un/mmute output", locked = true}
hl.bind("XF86AudioMute", hl.dsp.exec_cmd(hyde.sh.volumecontrol("-o", "m")), _F)
_F = {description = "[Hardware Controls|Audio] un/mute microphone", locked = true}
@@ -219,26 +225,30 @@ _F = {description = "[Utilities] toggle keyboard layout", locked = true}
hl.bind(MOD .. " + K", hl.dsp.exec_cmd(hyde.sh.kb.switch()), _F)
_F = {description = "[Utilities] game mode", locked = true}
hl.bind(MOD .. " + ALT + G", hl.dsp.exec_cmd(hyde.sh.gamemode()), _F) -- TODO
+_F = {description = "[Utilities] game launcher"}
+hl.bind(MOD .. " + SHIFT + G", hl.dsp.exec_cmd("hyde-shell gamelauncher"), _F)
_F = {description = "[Utilities] screen capture] color picker", locked = true}
hl.bind(MOD .. " + SHIFT + P", hl.dsp.exec_cmd("hyprpicker -an"), _F)
_F = {description = "[Utilities] partial screenshot capture", locked = true}
-hl.bind(MOD .. " + S", hl.dsp.exec_cmd(hyde.sh.screenshot.snip()), _F)
+hl.bind(MOD .. " + P", hl.dsp.exec_cmd(hyde.sh.screenshot.snip()), _F)
_F = {description = "[Utilities] freeze and snip screen", locked = true}
-hl.bind(MOD .. " + SHIFT + S", hl.dsp.exec_cmd(hyde.sh.screenshot.freeze()), _F)
+hl.bind(MOD .. " + CONTROL + P", hl.dsp.exec_cmd(hyde.sh.screenshot.freeze()), _F)
_F = {description = "[Utilities] print monitor", locked = true}
-hl.bind(MOD .. " + ALT + S", hl.dsp.exec_cmd(hyde.sh.screenshot.monitor()), _F)
+hl.bind(MOD .. " + ALT + P", hl.dsp.exec_cmd(hyde.sh.screenshot.monitor()), _F)
_F = {description = "[Utilities] print all monitors", locked = true}
-hl.bind(MOD .. " + CONTROL + A", hl.dsp.exec_cmd(hyde.sh.screenshot.monitor()), _F)
+hl.bind("Print", hl.dsp.exec_cmd(hyde.sh.screenshot.full()), _F)
_F = {description = "[Utilities] OCR scanner", locked = true}
hl.bind(MOD .. " + CONTROL + S", hl.dsp.exec_cmd(hyde.sh.screenshot.ocr()), _F)
--- _F = { description = "[Theming and Wallpaper] next global wallpaper"}
--- hl.bind(MOD .. "+ ALT + Right", hl.dsp.exec_cmd(hyde.sh.wallpaper("--next")), _F)
--- _F = { description = "[Theming and Wallpaper] previous global wallpaper"}
--- hl.bind(MOD .. "+ ALT + Left", hl.dsp.exec_cmd(hyde.sh.wallpaper("--prev")), _F)
--- _F = { description = "[Theming and Wallpaper] next waybar layout"}
--- hl.bind(MOD .. "+ CONTROL + ALT + Right", hl.dsp.exec_cmd(hyde.sh.waybar("--next")), _F)
+_F = {description = "[Theming and Wallpaper] next global wallpaper"}
+hl.bind(MOD .. "+ ALT + Right", hl.dsp.exec_cmd(hyde.sh.wallpaper("--next")), _F)
+_F = {description = "[Theming and Wallpaper] previous global wallpaper"}
+hl.bind(MOD .. "+ ALT + Left", hl.dsp.exec_cmd(hyde.sh.wallpaper("--prev")), _F)
+_F = {description = "[Theming and Wallpaper] next Waybar layout"}
+hl.bind(MOD .. "+ ALT + Up", hl.dsp.exec_cmd("hyde-shell waybar --next"), _F)
+_F = {description = "[Theming and Wallpaper] previous Waybar layout"}
+hl.bind(MOD .. "+ ALT + Down", hl.dsp.exec_cmd("hyde-shell waybar --prev"), _F)
_F = {description = "[Theming and Wallpaper] select a global wallpaper"}
hl.bind(MOD .. "+ SHIFT + W", hl.dsp.exec_cmd(hyde.sh.menu.wallpapers()), _F)
@@ -246,6 +256,10 @@ _F = {description = "[Theming and Wallpaper] wallbash mode selector"}
hl.bind(MOD .. "+ SHIFT + R", hl.dsp.exec_cmd(hyde.sh.menu.wallbash()), _F)
_F = {description = "[Theming and Wallpaper] select a theme"}
hl.bind(MOD .. "+ SHIFT + T", hl.dsp.exec_cmd(hyde.sh.menu.themes()), _F)
+_F = {description = "[Theming and Wallpaper] select animations"}
+hl.bind(MOD .. "+ SHIFT + Y", hl.dsp.exec_cmd("hyde-shell animations --select"), _F)
+_F = {description = "[Theming and Wallpaper] select Hyprlock layout"}
+hl.bind(MOD .. "+ SHIFT + U", hl.dsp.exec_cmd("hyde-shell hyprlock --select"), _F)
-- # TODO Make a main rofi menu for these selectors
-- $rice=Theming and Wallpaper
@@ -320,15 +334,17 @@ _F = {
hl.bind(MOD .. " + CONTROL + ALT + LEFT", hl.dsp.window.move({workspace = "r-1"}), _F)
_F = {description = "[Workspaces|Navigation|Mouse] next workspace"}
-hl.bind(MOD .. " + SHIFT + mouse_down", hl.dsp.focus({workspace = "r+1"}), _F)
+hl.bind(MOD .. " + mouse_down", hl.dsp.focus({workspace = "e+1"}), _F)
_F = {description = "[Workspaces|Navigation|Mouse] previous workspace"}
-hl.bind(MOD .. " + SHIFT + mouse_up", hl.dsp.focus({workspace = "r-1"}), _F)
+hl.bind(MOD .. " + mouse_up", hl.dsp.focus({workspace = "e-1"}), _F)
-- # Move/Switch to special workspace (scratchpad)
--- $d=[$ws|Navigation|Special workspace]
--- # bindd = $mainMod, grave, $d toggle scratchpad , togglespecialworkspace
--- # bindd = $mainMod SHIFT, grave, $d move to scratchpad , movetoworkspace, special
--- # bindd = $mainMod Alt, grave, $d move to scratchpad (silent) , movetoworkspacesilent, special
+_F = {description = "[Workspaces|Navigation|Special workspace] toggle scratchpad"}
+hl.bind(MOD .. " + S", hl.dsp.workspace.toggle_special(), _F)
+_F = {description = "[Workspaces|Navigation|Special workspace] move focused window to scratchpad"}
+hl.bind(MOD .. " + SHIFT + S", hl.dsp.window.move({workspace = "special"}), _F)
+_F = {description = "[Workspaces|Navigation|Special workspace] move focused window silently to scratchpad"}
+hl.bind(MOD .. " + ALT + S", hl.dsp.window.move({workspace = "special", follow = false}), _F)
--- Move silent
---
diff --git a/KEYBINDINGS.md b/KEYBINDINGS.md
index f41e7db24a..dd09fec158 100644
--- a/KEYBINDINGS.md
+++ b/KEYBINDINGS.md
@@ -52,7 +52,7 @@ Multi-language KEYBINDINGS support
Here are all HyDE specific keybindings listed.
-> [!TIP]
+> [!TIP]
> Super + / shows the keybindings.
@@ -61,7 +61,7 @@ Here are all HyDE specific keybindings listed.
| Keys | Action |
| :--------------------------------------------------- | :------------------------------ |
-| CTRL + Q | close focused window |
+| SUPER + Q | close focused window |
| ALT + F4 | close focused window |
| SUPER + Delete | kill hyprland session |
| SUPER + W | toggle float |
@@ -70,7 +70,7 @@ Here are all HyDE specific keybindings listed.
| SUPER + L | lock screen |
| SUPER + SHIFT + F | toggle pin on focused window |
| ALT + CTRL + Delete | logout menu |
-| ALT + Control_R | toggle waybar and reload config |
+| SUPER + CTRL + B | toggle waybar and reload config |
| SUPER + J | toggle split |
### Group Navigation
@@ -301,7 +301,7 @@ For example, to create a keybinding that launches the **HyDE Game Launcher**, ad
bind = $mainMod, SPACE, exec, $HOME/.local/lib/hyde/gamelauncher.sh
```
-This will bind the **Game Launcher** to SUPER + Space.
+This will bind the **Game Launcher** to SUPER + Space.
You can replace `SPACE` with any other key you prefer.
The `gamelauncher.sh` script is included by default and lives at:
diff --git a/Scripts/dots/hyde.toml b/Scripts/dots/hyde.toml
index c66edfcdde..33db2c2bb0 100644
--- a/Scripts/dots/hyde.toml
+++ b/Scripts/dots/hyde.toml
@@ -95,8 +95,7 @@ target_root = "${HOME}/.local/bin"
paths = ["hyde-shell", "hydectl"]
[grimblast]
-# source = "blob+https://raw.githubusercontent.com/hyprwm/contrib/refs/heads/main/grimblast/grimblast"
-source = "blob+https://raw.githubusercontent.com/zspher/contrib/refs/heads/feature-grimblast-toplevel-handle/grimblast/grimblast"
+source = "blob+https://raw.githubusercontent.com/hyprwm/contrib/refs/heads/main/grimblast/grimblast"
action = "sync"
target_root = "${HOME}/.local/lib/hyde/screenshot"
paths = "grimblast"
diff --git a/tests/README.md b/tests/README.md
index 6be9f57cb6..c0a1f5f803 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -19,10 +19,13 @@ is adding a file.
| Case | Checks |
| --- | --- |
+| `test_app_wrapper.sh` | Launching through either `app.sh` execution path does not leak an unrelated non-boolean `DEBUG` value into app2unit or xdg-terminal-exec |
| `test_binds.sh` | Loads the Lua keybinds against a stubbed Hyprland API: no two binds share a combination once modifiers are folded to what Hyprland matches on, no keysym sits in a modifier position, no bind uses the `code:NN` form the Lua parser rejects, every bind has a description, every `hyde-shell` command it runs exists. Touchpad gestures are checked in the same pass: a valid finger count, a direction and action Hyprland accepts, and no two gestures on the same finger count and direction |
| `test_git.sh` | The tree holds no gitlink without a matching `.gitmodules` entry, which would break `git submodule` and anything walking submodules |
-| `test_dots.sh` | Every installer metafile under `Scripts/dots` parses, declares the keys the installer needs, uses a known action, and points at a source directory that exists |
+| `test_dots.sh` | Every installer metafile under `Scripts/dots` parses, declares the keys the installer needs, uses a known action, and points at a source directory that exists. It also keeps Grimblast on the fixed official source |
| `test_lua_syntax.sh` | Every shipped Lua file parses |
+| `test_schema.sh` | Generated schema artifacts use the current Lua battery notification daemon rather than the removed shell implementation |
+| `test_screenshot_wrapper.sh` | Satty receives a compatible default GTK renderer while preserving explicit renderer overrides |
| `test_shell.sh` | Every shipped shell script parses, and shellcheck finds no error-severity problem |
## Dependencies
diff --git a/tests/lua/bind_harness.lua b/tests/lua/bind_harness.lua
index f2bd48837a..671aec362c 100644
--- a/tests/lua/bind_harness.lua
+++ b/tests/lua/bind_harness.lua
@@ -203,6 +203,7 @@ local function check(condition, message)
end
local seen = {}
+local by_combo = {}
for _, bind in ipairs(binds) do
local id = canonical(bind.combo)
local previous = seen[id]
@@ -212,6 +213,7 @@ for _, bind in ipairs(binds) do
string.format("combo %s is bound twice, as %q and %q", id, tostring(previous), bind.combo)
)
seen[id] = bind.combo
+ by_combo[id] = bind
local tokens = split(bind.combo)
local key = table.remove(tokens)
@@ -234,6 +236,70 @@ for _, bind in ipairs(binds) do
)
end
+-- The Lua migration must preserve the public keymap documented in
+-- KEYBINDINGS.md. Structural checks alone cannot catch a valid bind moving to
+-- an unexpected key or pointing at the wrong helper.
+local required_defaults = {
+ "SUPER + SHIFT + G",
+ "SUPER + SHIFT + P",
+ "SUPER + CTRL + S",
+ "SUPER + SHIFT + W",
+ "SUPER + SHIFT + R",
+ "SUPER + SHIFT + T",
+ "SUPER + ALT + T",
+ "ALT + F4",
+ "SUPER + DELETE",
+ "SHIFT + F11",
+ "SUPER + SHIFT + F",
+ "SUPER + CTRL + H",
+ "SUPER + CTRL + L",
+ "SUPER + SHIFT + RIGHT",
+ "SUPER + SHIFT + LEFT",
+ "SUPER + SHIFT + UP",
+ "SUPER + SHIFT + DOWN",
+ "SUPER + J",
+ "F10",
+ "F11",
+ "F12",
+ "SUPER + ALT + RIGHT",
+ "SUPER + ALT + LEFT",
+ "SUPER + ALT + UP",
+ "SUPER + ALT + DOWN",
+ "SUPER + SHIFT + Y",
+ "SUPER + SHIFT + U",
+ "SUPER + mouse_down",
+ "SUPER + mouse_up",
+ "SUPER + S",
+ "SUPER + SHIFT + S",
+ "SUPER + ALT + S"
+}
+
+for _, combo in ipairs(required_defaults) do
+ check(by_combo[canonical(combo)] ~= nil, string.format("documented combo %s is missing", combo))
+end
+
+local screenshot_commands = {
+ ["SUPER + CTRL + S"] = "hyde-shell screenshot sc",
+ ["SUPER + P"] = "hyde-shell screenshot s",
+ ["SUPER + CTRL + P"] = "hyde-shell screenshot sf",
+ ["SUPER + ALT + P"] = "hyde-shell screenshot m",
+ ["Print"] = "hyde-shell screenshot p"
+}
+
+for combo, expected in pairs(screenshot_commands) do
+ local bind = by_combo[canonical(combo)]
+ check(bind ~= nil, string.format("documented screenshot combo %s is missing", combo))
+
+ if bind then
+ local args = type(bind.action) == "table" and bind.action.args or nil
+ local command = args and args[1]
+ check(
+ command == expected,
+ string.format("%s runs %q, expected %q", combo, tostring(command), expected)
+ )
+ end
+end
+
-- Every hyde-shell command reachable from a bind has to exist in the tree.
for _, bind in ipairs(binds) do
local args = type(bind.action) == "table" and bind.action.args or nil
diff --git a/tests/test_app_wrapper.sh b/tests/test_app_wrapper.sh
new file mode 100755
index 0000000000..ebac5f7138
--- /dev/null
+++ b/tests/test_app_wrapper.sh
@@ -0,0 +1,77 @@
+#!/usr/bin/env sh
+# The app wrapper must isolate app2unit and xdg-terminal-exec from unrelated
+# DEBUG values on both its systemd and direct-exec paths.
+
+. "$(dirname -- "$0")/lib/common.sh"
+
+wrapper="$REPO_ROOT/Configs/.local/lib/hyde/app.sh"
+
+fixture=$(mktemp -d)
+trap 'rm -rf "$fixture"' EXIT HUP INT TERM
+
+cat >"$fixture/app2unit" <<'EOF'
+#!/usr/bin/env sh
+printf 'APP2UNIT_DEBUG=%s\n' "${APP2UNIT_DEBUG-}"
+printf 'XTE_DEBUG=%s\n' "${XTE_DEBUG-}"
+while [ "$#" -gt 0 ] && [ "$1" != "--" ]; do
+ shift
+done
+[ "$#" -gt 0 ] && shift
+exec "$@"
+EOF
+
+cat >"$fixture/xdg-terminal-exec" <<'EOF'
+#!/usr/bin/env sh
+printf 'APP2UNIT_CONSUMER_DEBUG=%s\n' "${APP2UNIT_DEBUG-${DEBUG-0}}"
+printf 'XTE_CONSUMER_DEBUG=%s\n' "${XTE_DEBUG-${DEBUG-0}}"
+EOF
+chmod +x "$fixture/app2unit" "$fixture/xdg-terminal-exec"
+
+output=$(
+ unset APP2UNIT_DEBUG XTE_DEBUG
+ export DEBUG=release
+ PATH="$fixture:$PATH" "$wrapper" -- xdg-terminal-exec 2>&1
+)
+status=$?
+
+if [ "$status" -ne 0 ]; then
+ fail "app wrapper exited with $status"
+fi
+
+printf '%s\n' "$output" | grep -qx 'APP2UNIT_CONSUMER_DEBUG=0' ||
+ fail "app wrapper leaked DEBUG through APP2UNIT_DEBUG"
+printf '%s\n' "$output" | grep -qx 'XTE_CONSUMER_DEBUG=0' ||
+ fail "app wrapper leaked DEBUG into xdg-terminal-exec"
+
+if [ -d /run/systemd/system ]; then
+ printf '%s\n' "$output" | grep -qx 'APP2UNIT_DEBUG=0' ||
+ fail "app wrapper did not default APP2UNIT_DEBUG to 0"
+ printf '%s\n' "$output" | grep -qx 'XTE_DEBUG=0' ||
+ fail "app wrapper did not default XTE_DEBUG to 0"
+fi
+
+export_line=$(grep -n '^export APP2UNIT_DEBUG XTE_DEBUG$' "$wrapper" | cut -d: -f1)
+systemd_line=$(grep -n '^if \[ -d "/run/systemd/system" \]; then$' "$wrapper" | cut -d: -f1)
+
+if [ -z "$export_line" ] || [ -z "$systemd_line" ] || [ "$export_line" -ge "$systemd_line" ]; then
+ fail "scoped debug defaults are not exported before the execution-path branch"
+fi
+
+override_output=$(
+ export DEBUG=release
+ export APP2UNIT_DEBUG=1
+ export XTE_DEBUG=yes
+ PATH="$fixture:$PATH" "$wrapper" -- xdg-terminal-exec 2>&1
+)
+override_status=$?
+
+if [ "$override_status" -ne 0 ]; then
+ fail "app wrapper with explicit debug values exited with $override_status"
+fi
+
+printf '%s\n' "$override_output" | grep -qx 'APP2UNIT_CONSUMER_DEBUG=1' ||
+ fail "app wrapper did not preserve APP2UNIT_DEBUG"
+printf '%s\n' "$override_output" | grep -qx 'XTE_CONSUMER_DEBUG=yes' ||
+ fail "app wrapper did not preserve XTE_DEBUG"
+
+finish
diff --git a/tests/test_dots.sh b/tests/test_dots.sh
index 980934ab38..60afbee5b5 100755
--- a/tests/test_dots.sh
+++ b/tests/test_dots.sh
@@ -10,4 +10,14 @@ fi
python3 "$TESTS_DIR/python/check_dots.py" || fail "check_dots reported defects"
+hyde_metafile="$REPO_ROOT/Scripts/dots/hyde.toml"
+official_grimblast='source = "blob+https://raw.githubusercontent.com/hyprwm/contrib/refs/heads/main/grimblast/grimblast"'
+
+grep -Fqx "$official_grimblast" "$hyde_metafile" ||
+ fail "grimblast is not sourced from the fixed upstream main branch"
+
+if grep -q 'feature-grimblast-toplevel-handle' "$hyde_metafile"; then
+ fail "grimblast still uses the double-selection experimental branch"
+fi
+
finish
diff --git a/tests/test_schema.sh b/tests/test_schema.sh
new file mode 100755
index 0000000000..2164d640cc
--- /dev/null
+++ b/tests/test_schema.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+# Generated schema artifacts must not restore commands removed by migrations.
+
+. "$(dirname -- "$0")/lib/common.sh"
+
+schema_dir="$REPO_ROOT/Configs/.local/share/hyde/schema"
+artifacts="
+schema.toml
+config.toml
+config.toml.json
+config.md
+"
+
+for artifact in $artifacts; do
+ path="$schema_dir/$artifact"
+
+ if grep -q 'batterynotify\.sh' "$path"; then
+ fail "$artifact still references the removed batterynotify.sh"
+ fi
+
+ if ! grep -q 'batterynotify\.lua' "$path"; then
+ fail "$artifact does not reference batterynotify.lua"
+ fi
+done
+
+finish
diff --git a/tests/test_screenshot_wrapper.sh b/tests/test_screenshot_wrapper.sh
new file mode 100755
index 0000000000..a607b03d4a
--- /dev/null
+++ b/tests/test_screenshot_wrapper.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# Satty needs a compatible GTK renderer when launched from Hyprland's
+# environment, but explicit user choices must still win.
+
+# shellcheck source=tests/lib/common.sh
+. "$(dirname -- "$0")/lib/common.sh"
+
+wrapper="$REPO_ROOT/Configs/.local/lib/hyde/screenshot.sh"
+fixture=$(mktemp -d)
+trap 'rm -rf "$fixture"' EXIT HUP INT TERM
+
+mkdir -p "$fixture/bin" "$fixture/lib/hyde/screenshot" "$fixture/runtime" "$fixture/pictures"
+
+cat >"$fixture/bin/hyde-shell" </dev/null 2>&1; }
+print_log() { :; }
+send_notifs() { :; }
+INIT
+fi
+EOF
+
+cat >"$fixture/lib/hyde/screenshot/grimblast" <<'EOF'
+#!/usr/bin/env sh
+for target_file in "$@"; do :; done
+: >"$target_file"
+EOF
+
+cat >"$fixture/bin/satty" <<'EOF'
+#!/usr/bin/env sh
+printf 'GSK_RENDERER=%s\n' "${GSK_RENDERER-}"
+EOF
+
+chmod +x "$fixture/bin/hyde-shell" "$fixture/bin/satty" "$fixture/lib/hyde/screenshot/grimblast"
+
+default_output=$(PATH="$fixture/bin:$PATH" "$wrapper" s --no-notify 2>&1)
+default_status=$?
+
+if [ "$default_status" -ne 0 ]; then
+ fail "screenshot wrapper with default renderer exited with $default_status"
+fi
+
+printf '%s\n' "$default_output" | grep -qx 'GSK_RENDERER=gl' ||
+ fail "Satty did not receive the compatible GL renderer default"
+
+override_output=$(GSK_RENDERER=cairo PATH="$fixture/bin:$PATH" "$wrapper" s --no-notify 2>&1)
+override_status=$?
+
+if [ "$override_status" -ne 0 ]; then
+ fail "screenshot wrapper with explicit renderer exited with $override_status"
+fi
+
+printf '%s\n' "$override_output" | grep -qx 'GSK_RENDERER=cairo' ||
+ fail "screenshot wrapper did not preserve the explicit renderer"
+
+finish