Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ 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`
- Hyprland: Lua keybinds again match the documented shortcuts for window management, screenshots, wallpapers, Waybar, selectors, workspaces and the scratchpad
- 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
Expand Down
7 changes: 7 additions & 0 deletions Configs/.local/lib/hyde/app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
# @cmd.desc: Run a script as a systemd service

if [ -d "/run/systemd/system" ]; then
# 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.
APP2UNIT_DEBUG=${APP2UNIT_DEBUG:-0}
XTE_DEBUG=${XTE_DEBUG:-0}
export APP2UNIT_DEBUG XTE_DEBUG
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
exec app2unit "$@"
fi
# no systemd: drop args before -- and run only the command after --
Expand Down
74 changes: 45 additions & 29 deletions Configs/.local/share/hypr/lua/key_binds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand All @@ -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"}
Expand All @@ -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
Expand All @@ -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]

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -219,33 +225,41 @@ _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)
_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
Expand Down Expand Up @@ -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
---
Expand Down
8 changes: 4 additions & 4 deletions KEYBINDINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Multi-language KEYBINDINGS support

Here are all HyDE specific keybindings listed.

> [!TIP]
> [!TIP]
> <kbd>Super</kbd> + <kbd>/</kbd> shows the keybindings.

<!-- ## <a id=window-management>Window Management</a> -->
Expand All @@ -61,7 +61,7 @@ Here are all HyDE specific keybindings listed.

| Keys | Action |
| :--------------------------------------------------- | :------------------------------ |
| <kbd>CTRL</kbd> + <kbd>Q</kbd> | close focused window |
| <kbd>SUPER</kbd> + <kbd>Q</kbd> | close focused window |
| <kbd>ALT</kbd> + <kbd>F4</kbd> | close focused window |
| <kbd>SUPER</kbd> + <kbd>Delete</kbd> | kill hyprland session |
| <kbd>SUPER</kbd> + <kbd>W</kbd> | toggle float |
Expand All @@ -70,7 +70,7 @@ Here are all HyDE specific keybindings listed.
| <kbd>SUPER</kbd> + <kbd>L</kbd> | lock screen |
| <kbd>SUPER</kbd> + <kbd>SHIFT</kbd> + <kbd>F</kbd> | toggle pin on focused window |
| <kbd>ALT</kbd> + <kbd>CTRL</kbd> + <kbd>Delete</kbd> | logout menu |
| <kbd>ALT</kbd> + <kbd>Control_R</kbd> | toggle waybar and reload config |
| <kbd>SUPER</kbd> + <kbd>CTRL</kbd> + <kbd>B</kbd> | toggle waybar and reload config |
| <kbd>SUPER</kbd> + <kbd>J</kbd> | toggle split |

### Group Navigation
Expand Down Expand Up @@ -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 <kbd>SUPER</kbd> + <kbd>Space</kbd>.
This will bind the **Game Launcher** to <kbd>SUPER</kbd> + <kbd>Space</kbd>.
You can replace `SPACE` with any other key you prefer.

The `gamelauncher.sh` script is included by default and lives at:
Expand Down
1 change: 1 addition & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ is adding a file.

| Case | Checks |
| --- | --- |
| `test_app_wrapper.sh` | Launching through `app.sh` 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 |
Expand Down
59 changes: 59 additions & 0 deletions tests/lua/bind_harness.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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)
Expand All @@ -234,6 +236,63 @@ 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 + 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 + P"] = "hyde-shell screenshot s",
["SUPER + CTRL + P"] = "hyde-shell screenshot sf",
["SUPER + ALT + P"] = "hyde-shell screenshot m",
["Print"] = "hyde-shell screenshot p"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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
Expand Down
39 changes: 39 additions & 0 deletions tests/test_app_wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env sh
# The app wrapper must isolate app2unit from unrelated DEBUG values.

. "$(dirname -- "$0")/lib/common.sh"

wrapper="$REPO_ROOT/Configs/.local/lib/hyde/app.sh"

if [ ! -d /run/systemd/system ]; then
skip "systemd is not available"
finish
fi

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-<unset>}"
printf 'XTE_DEBUG=%s\n' "${XTE_DEBUG-<unset>}"
EOF
chmod +x "$fixture/app2unit"

output=$(
unset APP2UNIT_DEBUG XTE_DEBUG
export DEBUG=release
PATH="$fixture:$PATH" "$wrapper" 2>&1
)
status=$?

if [ "$status" -ne 0 ]; then
fail "app wrapper exited with $status"
fi

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"

finish
Loading