Parse plain hyprctl binds output to survive Hyprland 0.56's broken JSON
Hyprland 0.56.0 emits invalid JSON from `hyprctl -j binds`: the new allow_input_capture field was added to the format template at slot 16 but its argument at position 8, shifting every value from has_description onward and landing unquoted strings in numeric slots. The jq parse failure emptied the keybindings menu down to the two static web-app entries — and the empty result got cached under a stable key, permanently. Parse the plain-format output instead, which is unaffected (and also immune to the older quotes-in-args JSON breakage), refuse to cache a record set with zero dynamic binds, and bump the cache version so poisoned caches invalidate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
79e9f38c0c
commit
994e4dfa7d
@@ -3,9 +3,8 @@
|
|||||||
# omarchy:summary=Display Hyprland keybindings defined in your configuration using an interactive search menu.
|
# omarchy:summary=Display Hyprland keybindings defined in your configuration using an interactive search menu.
|
||||||
|
|
||||||
# Hyprland's Lua config provider currently reports Lua binds as dispatcher
|
# Hyprland's Lua config provider currently reports Lua binds as dispatcher
|
||||||
# __lua and code:... binds from hl.bind() as key="" and keycode=0 in
|
# __lua in `hyprctl binds`. Keep a lightweight source-derived cache so the
|
||||||
# `hyprctl -j binds`. Keep a lightweight source-derived cache so the menu can
|
# menu can still show and dispatch those bindings.
|
||||||
# still show and dispatch those bindings.
|
|
||||||
declare -A LUA_BIND_KEY_MAP
|
declare -A LUA_BIND_KEY_MAP
|
||||||
declare -A LUA_BIND_DISPATCHER_MAP
|
declare -A LUA_BIND_DISPATCHER_MAP
|
||||||
declare -A LUA_BIND_ARG_MAP
|
declare -A LUA_BIND_ARG_MAP
|
||||||
@@ -65,7 +64,7 @@ parse_keycodes() {
|
|||||||
'
|
'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Supplement `hyprctl -j binds` for Lua-only binds that Hyprland currently
|
# Supplement `hyprctl binds` for Lua-only binds that Hyprland currently
|
||||||
# reports as dispatcher __lua and without their original key for code: binds.
|
# reports as dispatcher __lua and without their original key for code: binds.
|
||||||
build_lua_bind_cache() {
|
build_lua_bind_cache() {
|
||||||
local modmask description key dispatcher arg cache_key
|
local modmask description key dispatcher arg cache_key
|
||||||
@@ -271,9 +270,24 @@ modmask_to_text() {
|
|||||||
dynamic_bindings() {
|
dynamic_bindings() {
|
||||||
local modmask key keycode description dispatcher arg modifiers cache_key
|
local modmask key keycode description dispatcher arg modifiers cache_key
|
||||||
|
|
||||||
hyprctl -j binds |
|
# Parse the plain `hyprctl binds` output rather than `hyprctl -j binds`:
|
||||||
jq -r '.[] | [.modmask, (.key // ""), (.keycode // 0), (.description // ""), (.dispatcher // ""), (.arg // "") | tostring] | join("\u001f")' |
|
# Hyprland 0.56.0 emits invalid JSON for binds (misaligned fields in
|
||||||
|
# bindsRequest), and older versions broke on quotes in bind args.
|
||||||
|
hyprctl binds | awk '
|
||||||
|
function emit() {
|
||||||
|
if (!seen) return
|
||||||
|
seen = 0
|
||||||
|
printf "%s\x1f%s\x1f%s\x1f%s\x1f%s\x1f%s\n", f["modmask"], f["key"], f["keycode"], f["description"], f["dispatcher"], f["arg"]
|
||||||
|
}
|
||||||
|
/^bind/ { emit(); seen = 1; delete f; next }
|
||||||
|
seen && match($0, /^\t[a-z]+: /) { f[substr($0, 2, RLENGTH - 3)] = substr($0, RLENGTH + 1) }
|
||||||
|
END { emit() }
|
||||||
|
' |
|
||||||
while IFS=$'\x1f' read -r modmask key keycode description dispatcher arg; do
|
while IFS=$'\x1f' read -r modmask key keycode description dispatcher arg; do
|
||||||
|
# Lua binds report their full display key ("SUPER + code:20"); the
|
||||||
|
# modifiers are already carried separately in modmask.
|
||||||
|
key="${key##* + }"
|
||||||
|
|
||||||
if [[ -z $key && $keycode != "0" ]]; then
|
if [[ -z $key && $keycode != "0" ]]; then
|
||||||
key="code:$keycode"
|
key="code:$keycode"
|
||||||
fi
|
fi
|
||||||
@@ -421,23 +435,30 @@ prioritize_entries() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
output_binding_records_uncached() {
|
output_binding_records_uncached() {
|
||||||
|
local dynamic
|
||||||
|
|
||||||
build_lua_bind_cache
|
build_lua_bind_cache
|
||||||
|
dynamic=$(dynamic_bindings)
|
||||||
|
|
||||||
{
|
{
|
||||||
dynamic_bindings
|
[[ -n $dynamic ]] && printf '%s\n' "$dynamic"
|
||||||
static_bindings
|
static_bindings
|
||||||
} |
|
} |
|
||||||
sort -u |
|
sort -u |
|
||||||
parse_keycodes |
|
parse_keycodes |
|
||||||
parse_binding_records |
|
parse_binding_records |
|
||||||
prioritize_entries
|
prioritize_entries
|
||||||
|
|
||||||
|
# Fail when Hyprland reported no binds so the fallout of a broken hyprctl
|
||||||
|
# is never cached.
|
||||||
|
[[ -n $dynamic ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
keybindings_cache_key() {
|
keybindings_cache_key() {
|
||||||
{
|
{
|
||||||
printf 'v5\n'
|
printf 'v6\n'
|
||||||
hyprctl -j devices 2>/dev/null | jq -r '.keyboards[].active_keymap' 2>/dev/null
|
hyprctl devices 2>/dev/null | grep -F 'active keymap:'
|
||||||
hyprctl -j binds 2>/dev/null
|
hyprctl binds 2>/dev/null
|
||||||
} | sha256sum | awk '{ print $1 }'
|
} | sha256sum | awk '{ print $1 }'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user