From bae81c96f1d5fa0d2f190ecb0b2d31957da65d3d Mon Sep 17 00:00:00 2001 From: janhesters Date: Mon, 17 Aug 2026 16:38:03 +0200 Subject: [PATCH] Use layout-independent clipboard shortcuts --- default/hypr/bindings/clipboard.lua | 43 ++++---------------- test/shell.d/hyprland-default-config-test.sh | 16 ++++++++ 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/default/hypr/bindings/clipboard.lua b/default/hypr/bindings/clipboard.lua index f095ea5cd64..ae51e2fa9c3 100644 --- a/default/hypr/bindings/clipboard.lua +++ b/default/hypr/bindings/clipboard.lua @@ -3,46 +3,19 @@ -- layer-shell surfaces such as Omarchy panels. A virtual keyboard (wtype) won't -- do: the physically held SUPER merges into the injected chord at the seat. -- The down/up split works around Hyprland send_shortcut sometimes leaving --- synthetic key state stuck/repeating. +-- synthetic key state stuck/repeating. Keep both events in the same callback +-- so Hyprland resolves them against the same keyboard device and layout. Use +-- Insert/Delete chords so letter keys cannot translate through another XKB +-- layout in Electron/Chromium applications. -- https://github.com/hyprwm/Hyprland/discussions/14099 local function send_shortcut_once(mods, key) return function() hl.dispatch(hl.dsp.send_key_state({ mods = mods, key = key, state = "down" })) - - hl.timer(function() - hl.dispatch(hl.dsp.send_key_state({ mods = mods, key = key, state = "up" })) - end, { timeout = 50, type = "oneshot" }) - end -end - --- Lean on the terminal tag from default/hypr/apps/terminals.lua so there's one --- definition of what counts as a terminal. Dynamic tags carry a trailing "*". -local function active_window_is_terminal() - local window = hl.get_active_window() - if not window then - return false - end - - for _, tag in ipairs(window.tags or {}) do - if tag:gsub("%*$", "") == "terminal" then - return true - end - end - - return false -end - -local function universal_clipboard_shortcut(default_mods, default_key, terminal_mods, terminal_key) - return function() - if active_window_is_terminal() then - send_shortcut_once(terminal_mods, terminal_key)() - else - send_shortcut_once(default_mods, default_key)() - end + hl.dispatch(hl.dsp.send_key_state({ mods = mods, key = key, state = "up" })) end end -o.bind("SUPER + C", "Universal copy", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert")) -o.bind("SUPER + V", "Universal paste", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert")) -o.bind("SUPER + X", "Universal cut", send_shortcut_once("CTRL", "X")) +o.bind("SUPER + C", "Universal copy", send_shortcut_once("CTRL", "Insert")) +o.bind("SUPER + V", "Universal paste", send_shortcut_once("SHIFT", "Insert")) +o.bind("SUPER + X", "Universal cut", send_shortcut_once("SHIFT", "Delete")) o.bind("SUPER + CTRL + V", "Clipboard manager", "omarchy-shell shell toggle omarchy.clipboard") diff --git a/test/shell.d/hyprland-default-config-test.sh b/test/shell.d/hyprland-default-config-test.sh index e37ef931656..a187edc72d5 100644 --- a/test/shell.d/hyprland-default-config-test.sh +++ b/test/shell.d/hyprland-default-config-test.sh @@ -109,6 +109,22 @@ grep -F 'hl.dsp.send_key_state({ mods = mods, key = key, state = "down" })' "$RO fail "universal clipboard shortcuts send explicit mods to the focused surface" pass "universal clipboard shortcuts send explicit mods to the focused surface" +grep -F 'hl.dsp.send_key_state({ mods = mods, key = key, state = "up" })' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null || + fail "universal clipboard shortcuts release the injected key" + +if grep -F 'hl.timer' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null; then + fail "universal clipboard shortcuts resolve press and release in the same keymap" +fi +pass "universal clipboard shortcuts resolve press and release in the same keymap" + +grep -F 'o.bind("SUPER + C", "Universal copy", send_shortcut_once("CTRL", "Insert"))' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null || + fail "universal copy uses a layout-independent chord" +grep -F 'o.bind("SUPER + V", "Universal paste", send_shortcut_once("SHIFT", "Insert"))' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null || + fail "universal paste uses a layout-independent chord" +grep -F 'o.bind("SUPER + X", "Universal cut", send_shortcut_once("SHIFT", "Delete"))' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null || + fail "universal cut uses a layout-independent chord" +pass "universal clipboard shortcuts avoid layout-sensitive letter keys" + if grep -E 'send_key_state\(\{[^}]*window' "$ROOT/default/hypr/bindings/clipboard.lua" >/dev/null; then fail "universal clipboard shortcuts do not target only normal windows" fi