-- gui package — the dots keybinds, split out of ./hyprland.lua. Required there -- after the app/start overrides (the launchers read hc.app.*) and before -- ./host.lua. local hc = hyde.config local MOD = hc.modifiers.main local home = os.getenv("HOME") or "" -- ── Keybinds ───────────────────────────────────────────────────────────────── -- Policy: preserve what the dots keybindings.conf did live until the port — -- where a dots bind and a HyDE bind share a combo, the dots action wins and -- the displaced HyDE action is noted. Binds identical in both are not -- repeated here (volume/media/brightness, screenshots, Super+Q/W, -- rofi menus, Super+L lock, Super+Shift+F pin, Super+S scratchpad family, -- Super+Z/X + mouse drag/resize, Super+J togglesplit, Super+T/E/B via the -- app.* overrides in hyprland.lua, Super+Alt+G gamemode, Ctrl+Shift+Esc -- sysmon). -- Removes every live bind on a combo regardless of flags. hl.bind's dedup only -- replaces a bind whose flags match exactly; HyDE binds with locked/transparent/ -- repeating flags would otherwise stay live next to ours and double-fire. local function unbind_all(combo) -- _active/canonicalize are HyDE-private; if an update renames them, degrade -- to "the displaced HyDE binds double-fire" instead of erroring here and -- silently dropping every bind registered below this line. if not (hyde.binds and hyde.binds._active and hyde.binds.canonicalize) then return end local canon = hyde.binds.canonicalize(combo) for id, registered in pairs(hyde.binds._active) do if id:sub(1, #canon + 1) == canon .. "|" then hl.unbind(registered) hyde.binds._active[id] = nil end end end local _F -- Launchers (dots-only combos, no conflict) _F = { description = "[Launcher|Apps] work session (kitty c2)" } hl.bind(MOD .. " + CTRL + I", hl.dsp.exec_cmd("kitty --session " .. home .. "/.config/kitty/c2.conf"), _F) _F = { description = "[Launcher|Apps] work session (kitty runners)" } hl.bind(MOD .. " + CTRL + O", hl.dsp.exec_cmd("kitty --session " .. home .. "/.config/kitty/runners.conf"), _F) _F = { description = "[Launcher|Apps] pyradio (kitty music)" } hl.bind(MOD .. " + R", hl.dsp.exec_cmd("kitty --session " .. home .. "/.config/kitty/music.conf"), _F) _F = { description = "[Launcher|Apps] ferdium" } hl.bind(MOD .. " + Y", hl.dsp.exec_cmd("ferdium"), _F) _F = { description = "[Launcher|Apps] betterbird" } hl.bind(MOD .. " + M", hl.dsp.exec_cmd("betterbird"), _F) _F = { description = "[Launcher|Apps] freetube" } hl.bind(MOD .. " + F", hl.dsp.exec_cmd("freetube"), _F) _F = { description = "[Launcher|Apps] web browser (librewolf)" } hl.bind(MOD .. " + SHIFT + B", hl.dsp.exec_cmd("librewolf"), _F) _F = { description = "[Launcher|Apps] terminal emulator (st)" } hl.bind(MOD .. " + CTRL + T", hl.dsp.exec_cmd("st"), _F) _F = { description = "[Launcher|Apps] terminal emulator" } hl.bind("CTRL + ALT + T", hl.dsp.exec_cmd(hc.app.terminal), _F) -- Rebound fresh so the hc.app.* overrides actually apply: HyDE's binds -- evaluated _apps.browser/_apps.explorer when key_binds.lua ran, baking in the -- hyde-shell open wrappers. _F = { description = "[Launcher|Apps] web browser" } hl.bind(MOD .. " + B", hl.dsp.exec_cmd("zen-browser"), _F) _F = { description = "[Launcher|Apps] file explorer" } hl.bind(MOD .. " + E", hl.dsp.exec_cmd("dolphin"), _F) -- Conflict resolutions (dots action wins; displaced HyDE action noted) _F = { description = "[Launcher|Apps] web browser (firefox)" } -- displaces HyDE: hide waybar hl.bind(MOD .. " + CTRL + B", hl.dsp.exec_cmd("firefox"), _F) _F = { description = "[Launcher|Apps] xmpp (gajim)" } -- displaces HyDE: toggle group (dots keeps it on Super+Shift+G) hl.bind(MOD .. " + G", hl.dsp.exec_cmd("gajim"), _F) _F = { description = "[Window Management] toggle group" } -- displaces HyDE: game launcher hl.bind(MOD .. " + SHIFT + G", hl.dsp.group.toggle(), _F) _F = { description = "[Launcher|Apps] terminal emulator (alacritty)" } -- displaces HyDE: theme select menu hl.bind(MOD .. " + SHIFT + T", hl.dsp.exec_cmd("alacritty"), _F) -- Through `wbar`, never killall: under supervision a killed waybar is back in -- seconds, and a raw relaunch next to it means two bars. _F = { description = "[Theming and Wallpaper] reload hyprland + waybar" } -- displaces HyDE: wallbash mode menu hl.bind(MOD .. " + SHIFT + R", hl.dsp.exec_cmd("hyprctl reload; " .. home .. "/.local/bin/wbar restart"), _F) _F = { description = "[Launcher|Apps] neovim" } -- displaces HyDE: code editor hl.bind(MOD .. " + C", hl.dsp.exec_cmd("kitty nvim"), _F) -- Super+K keepassxc — HyDE's keyboard-layout switch carries locked=true, so a -- plain rebind would leave both live (flags are part of a bind's identity). unbind_all(MOD .. " + K") -- displaces HyDE: keyboard layout switch (locked) _F = { description = "[Launcher|Apps] password manager" } hl.bind(MOD .. " + K", hl.dsp.exec_cmd("keepassxc"), _F) -- Alt+Tab focus-down — HyDE ships a 4-bind alt-tab switcher (transparent/ -- release flags); remove the whole cluster, half a switcher is worse than none. unbind_all("ALT + TAB") -- displaces HyDE: alt-tab switcher (cycle next) unbind_all("ALT + SHIFT + TAB") unbind_all("ALT + ALT_L") unbind_all("ALT + ALT_R") _F = { description = "[Window Management|Change focus] focus down" } hl.bind("ALT + TAB", hl.dsp.focus({ direction = "down" }), _F) -- Waybar toggle (custom layout; the legacy Ctrl+Alt+W). systemctl stop is a -- deliberate stop, so Restart=always does not undo it. _F = { description = "[Window Management] toggle waybar" } hl.bind("CTRL + ALT + W", hl.dsp.exec_cmd(home .. "/.local/bin/wbar toggle"), _F) -- Fullscreen on Alt+Return (Shift+F11 stays HyDE's cycle-fullscreen). -- Native dispatcher, not exec "hyprctl dispatch fullscreen": in Lua mode that -- CLI arg is evaluated as hl.dispatch(fullscreen) — a nil global, dead bind. _F = { description = "[Window Management] toggle fullscreen" } hl.bind("ALT + RETURN", hl.dsp.window.fullscreen(), _F) -- No Super+Delete: HyDE's exit() ends the session on the spot, no confirm, and -- the combo is one slipped finger from Super+Alt+Delete. Ctrl+Alt+Delete (the -- logout menu) stays the way out. unbind_all(MOD .. " + Delete") -- displaces HyDE: exit hyprland session -- ── Focus (arrows + hjkl) ──────────────────────────────────────────────────── -- Super+Ctrl+Up/Down: movefocus — displaces HyDE: nearest-empty (Down), which -- moves to Super+Ctrl+E below. Super+Ctrl+Left/Right move the window to r±1 -- (Workspaces below); r±1 nav is on Super+Left/Right and Super+Ctrl+Shift. for dir, key in pairs({ left = "LEFT", right = "RIGHT", up = "UP", down = "DOWN" }) do _F = { description = "[Window Management|Change focus] focus " .. dir } if key == "UP" or key == "DOWN" then hl.bind(MOD .. " + CTRL + " .. key, hl.dsp.focus({ direction = dir }), _F) end hl.bind("ALT + CTRL + " .. key, hl.dsp.focus({ direction = dir }), _F) end for dir, key in pairs({ left = "H", right = "L", up = "K", down = "J" }) do _F = { description = "[Window Management|Change focus] focus " .. dir } hl.bind("ALT + CTRL + " .. key, hl.dsp.focus({ direction = dir }), _F) end -- Super+Ctrl+H/L carried a deliberate double action in the hyprlang chain: -- changegroupactive (a no-op outside a group) AND movefocus fired together. -- Lua dedup would collapse the pair, so one function does both. -- (Replaces HyDE's plain group-nav binds on the same combos.) Unlike the -- arrows, they do not move the window to r±1 — that is Super+Ctrl+Alt+H/L. _F = { description = "[Window Management|Change focus] group prev + focus left" } hl.bind(MOD .. " + CTRL + H", function() hl.dispatch(hl.dsp.group.prev()) hl.dispatch(hl.dsp.focus({ direction = "left" })) end, _F) _F = { description = "[Window Management|Change focus] group next + focus right" } hl.bind(MOD .. " + CTRL + L", function() hl.dispatch(hl.dsp.group.next()) hl.dispatch(hl.dsp.focus({ direction = "right" })) end, _F) _F = { description = "[Window Management|Change focus] focus up" } hl.bind(MOD .. " + CTRL + K", hl.dsp.focus({ direction = "up" }), _F) _F = { description = "[Window Management|Change focus] focus down" } hl.bind(MOD .. " + CTRL + J", hl.dsp.focus({ direction = "down" }), _F) -- ── Resize (add hjkl to HyDE's arrow set) ──────────────────────────────────── -- HyDE puts a flagless calculator menu on Super+Shift+K; our repeating resize -- would double-fire next to it (flag mismatch), so drop the calculator. unbind_all(MOD .. " + SHIFT + K") -- displaces HyDE: calculator menu for _, m in ipairs({ { "L", 30, 0 }, { "H", -30, 0 }, { "K", 0, -30 }, { "J", 0, 30 } }) do _F = { description = "[Window Management|Resize Active Window] resize window", repeating = true } hl.bind(MOD .. " + SHIFT + " .. m[1], hl.dsp.window.resize({ x = m[2], y = m[3], relative = true }), _F) end -- ── Move active window (Super+Alt, float-aware: moveactive / swapwindow) ───── -- Displaces HyDE: wallpaper next/prev (Alt+Right/Left) and waybar layout -- next/prev (Alt+Up/Down); wallpaper moves to Super+Alt+Ctrl+Shift below, -- as in the dots chain. HyDE's binds carry no flags but ours are repeating, -- so an explicit unbind is needed to keep them from double-firing. unbind_all(MOD .. " + ALT + RIGHT") unbind_all(MOD .. " + ALT + LEFT") unbind_all(MOD .. " + ALT + UP") unbind_all(MOD .. " + ALT + DOWN") local function dots_move_window(dir, pix) local lut = { l = { -1, 0 }, r = { 1, 0 }, u = { 0, -1 }, d = { 0, 1 } } local m = lut[dir] return function() local w = hl.get_active_window() if w and w.floating then hl.dispatch(hl.dsp.window.move({ x = m[1] * pix, y = m[2] * pix, relative = true })) else local names = { l = "left", r = "right", u = "up", d = "down" } hl.dispatch(hl.dsp.window.swap({ direction = names[dir] })) end end end for _, m in ipairs({ { "LEFT", "l" }, { "RIGHT", "r" }, { "UP", "u" }, { "DOWN", "d" }, { "H", "l" }, { "L", "r" }, { "K", "u" }, { "J", "d" }, }) do _F = { description = "[Window Management|Move active window] move " .. m[2], repeating = true } hl.bind(MOD .. " + ALT + " .. m[1], dots_move_window(m[2], 30), _F) end -- ── Wallpaper (moved off Super+Alt by the dots chain) ──────────────────────── _F = { description = "[Theming and Wallpaper] next global wallpaper" } hl.bind(MOD .. " + ALT + CTRL + SHIFT + RIGHT", hl.dsp.exec_cmd(hyde.sh.wallpaper("--next")), _F) _F = { description = "[Theming and Wallpaper] previous global wallpaper" } hl.bind(MOD .. " + ALT + CTRL + SHIFT + LEFT", hl.dsp.exec_cmd(hyde.sh.wallpaper("--prev")), _F) -- ── Workspaces ─────────────────────────────────────────────────────────────── -- r~N = Nth workspace of the FOCUSED monitor — load-bearing on the 3-monitor -- workspace sets (1-10 / 11-20 / 21-30 pinned per monitor in host.lua). -- HyDE's own binds pass "r+1"/"empty" strings the same way; r~N is the one -- thing to verify first in the live session. for i = 1, 10 do local key = (i == 10) and 0 or i _F = { description = "[Workspaces|Navigation] navigate to workspace " .. i .. " (monitor-relative)" } hl.bind(MOD .. " + " .. key, hl.dsp.focus({ workspace = "r~" .. i }), _F) -- displaces HyDE: absolute workspace N _F = { description = "[Workspaces|Move window to workspace] move to workspace " .. i .. " (monitor-relative)" } hl.bind(MOD .. " + CTRL + " .. key, hl.dsp.window.move({ workspace = "r~" .. i }), _F) _F = { description = "[Workspaces|Move window silently] move to workspace " .. i .. " (monitor-relative)" } hl.bind(MOD .. " + ALT + " .. key, hl.dsp.window.move({ workspace = "r~" .. i, follow = false }), _F) -- displaces HyDE: absolute silent move end -- Relative workspace nav on Super+Ctrl+Shift (dots), displacing HyDE's -- move-window-in-workspace family there (repeating flags -> explicit unbind; -- dots does window movement on Super+Alt instead). unbind_all(MOD .. " + SHIFT + CTRL + LEFT") unbind_all(MOD .. " + SHIFT + CTRL + RIGHT") unbind_all(MOD .. " + SHIFT + CTRL + UP") unbind_all(MOD .. " + SHIFT + CTRL + DOWN") _F = { description = "[Workspaces|Navigation|Relative workspace] change active workspace forwards" } hl.bind(MOD .. " + CTRL + SHIFT + RIGHT", hl.dsp.focus({ workspace = "r+1" }), _F) hl.bind(MOD .. " + CTRL + SHIFT + L", hl.dsp.focus({ workspace = "r+1" }), _F) _F = { description = "[Workspaces|Navigation|Relative workspace] change active workspace backwards" } hl.bind(MOD .. " + CTRL + SHIFT + LEFT", hl.dsp.focus({ workspace = "r-1" }), _F) hl.bind(MOD .. " + CTRL + SHIFT + H", hl.dsp.focus({ workspace = "r-1" }), _F) _F = { description = "[Workspaces|Navigation|Relative workspace] change active workspace forwards" } hl.bind(MOD .. " + RIGHT", hl.dsp.focus({ workspace = "r+1" }), _F) _F = { description = "[Workspaces|Navigation|Relative workspace] change active workspace backwards" } hl.bind(MOD .. " + LEFT", hl.dsp.focus({ workspace = "r-1" }), _F) _F = { description = "[Workspaces|Move window to workspace|Relative workspace] move focused window to next workspace" } hl.bind(MOD .. " + CTRL + RIGHT", hl.dsp.window.move({ workspace = "r+1" }), _F) -- displaces HyDE: workspace r+1 _F = { description = "[Workspaces|Move window to workspace|Relative workspace] move focused window to previous workspace", } hl.bind(MOD .. " + CTRL + LEFT", hl.dsp.window.move({ workspace = "r-1" }), _F) -- displaces HyDE: workspace r-1 -- E for "empty" — Super+Ctrl+Down is taken by movefocus above _F = { description = "[Workspaces|Navigation] navigate to the nearest empty workspace" } hl.bind(MOD .. " + CTRL + E", hl.dsp.focus({ workspace = "empty" }), _F) -- Move window to relative workspace: HyDE ships the arrow pair; add hjkl. _F = { description = "[Workspaces|Move window to workspace|Relative workspace] move focused window to next workspace" } hl.bind(MOD .. " + CTRL + ALT + L", hl.dsp.window.move({ workspace = "r+1" }), _F) _F = { description = "[Workspaces|Move window to workspace|Relative workspace] move focused window to previous workspace", } hl.bind(MOD .. " + CTRL + ALT + H", hl.dsp.window.move({ workspace = "r-1" }), _F) -- ── Hardware controls ──────────────────────────────────────────────────────── -- HyDE binds bare F10/F11/F12 to mute / volume down / volume up (key_binds.lua, -- locked + repeating -- a plain rebind would leave both live). That is a laptop -- F-row assumption: the Cantor has no F-row, so F1-F12 only ever arrive from its -- Vial layer 5 and have to reach applications instead of the mixer. The -- XF86Audio* binds below them keep the media keys working, so nothing is lost, -- and Shift+F11 (cycle fullscreen) canonicalizes separately and survives. unbind_all("F10") -- displaces HyDE: un/mute output unbind_all("F11") -- displaces HyDE: decrease volume unbind_all("F12") -- displaces HyDE: increase volume