diff --git a/desktopenvs/hyprlua/hypr-usr/input-device-exceptions.lua b/desktopenvs/hyprlua/hypr-usr/input-device-exceptions.lua new file mode 100644 index 0000000..f045cc8 --- /dev/null +++ b/desktopenvs/hyprlua/hypr-usr/input-device-exceptions.lua @@ -0,0 +1,28 @@ +-- Device-specific input overrides — settings here take precedence over global input config. +hl.device({ + name = "zsa-technology-labs-moonlander-mark-i-keyboard", + kb_layout = "de", + kb_variant = "", + kb_model = "", + kb_options = "", + kb_rules = "", + repeat_rate = 15, + repeat_delay = 750, + sensitivity = 0, +}) +hl.device({ + name = "logitech-pro-2-mouse-1", + sensitivity = 0, + accel_profile = "", + left_handed = false, -- left-handed mode handled on the mouse's onboard memory + natural_scroll = false, + scroll_points = "", +}) +hl.device({ + name = "logitech-g903-ls-1", + sensitivity = 0, + accel_profile = "", + left_handed = false, + natural_scroll = false, + scroll_points = "", +}) diff --git a/desktopenvs/hyprlua/hypr-usr/input.lua b/desktopenvs/hyprlua/hypr-usr/input.lua index cc1f6c6..618c643 100644 --- a/desktopenvs/hyprlua/hypr-usr/input.lua +++ b/desktopenvs/hyprlua/hypr-usr/input.lua @@ -14,30 +14,5 @@ hl.config({ }, }, }) -hl.device({ - name = "zsa-technology-labs-moonlander-mark-i-keyboard", - kb_layout = "de", - kb_variant = "", - kb_model = "", - kb_options = "", - kb_rules = "", - repeat_rate = 15, - repeat_delay = 750, - sensitivity = 0, -}) -hl.device({ - name = "logitech-pro-2-mouse-1", - sensitivity = 0, - accel_profile = "", - left_handed = false, -- left-handed mode handled on the mouse's onboard memory - natural_scroll = false, - scroll_points = "", -}) -hl.device({ - name = "logitech-g903-ls-1", - sensitivity = 0, - accel_profile = "", - left_handed = false, - natural_scroll = false, - scroll_points = "", -}) + +require("input-device-exceptions") diff --git a/desktopenvs/hyprlua/hypr-usr/mk-device-exception.sh b/desktopenvs/hyprlua/hypr-usr/mk-device-exception.sh new file mode 100755 index 0000000..3f8c53c --- /dev/null +++ b/desktopenvs/hyprlua/hypr-usr/mk-device-exception.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# TUI for creating/updating device-specific Hyprland input overrides. +# Usage: mk-device-exception.sh +set -euo pipefail + +die() { printf 'error: %s\n' "$*" >&2; exit 1; } + +[[ $# -eq 1 ]] || die "Usage: ${0##*/} " +target="$1" + +command -v hyprctl &>/dev/null || die "hyprctl not found" +command -v dialog &>/dev/null || die "dialog not found" +command -v jq &>/dev/null || die "jq not found" + +# ---------- device selection ---------- + +devices=$(hyprctl devices -j) +entries=$(jq -r ' + (.keyboards[]?.name | "keyboard\t" + .), + (.mice[]?.name | "mouse\t" + .) +' <<<"$devices") + +[[ -z "$entries" ]] && die "No keyboard or mouse devices found" + +if command -v fzf &>/dev/null; then + selected=$(printf '%s\n' "$entries" \ + | fzf --prompt="Select device: " --height=~10 --with-nth=2.. --delimiter=$'\t') +else + echo "Select a device:" >&2 + IFS=$'\n' + select selected in $entries; do [[ -n "$selected" ]] && break; done + unset IFS +fi + +[[ -z "$selected" ]] && exit 1 + +type=${selected%%$'\t'*} +name=${selected#*$'\t'} + +# ---------- parse existing field value from target file ---------- +# Returns the Lua literal for the field (e.g. `"de"`, `15`, `false`). + +get_field() { + local field="$1" default="$2" + [[ -f "$target" ]] || { printf '%s' "$default"; return; } + awk -v dev="$name" -v fld="$field" -v def="$default" ' + /^hl\.device\(\{/ { blk=""; in_blk=1; matched=0 } + in_blk { blk = blk $0 "\n" } + in_blk && /name = / { if ($0 ~ "\"" dev "\"") matched=1 } + in_blk && /^\}\)/ { + if (matched) { + n = split(blk, lines, "\n") + for (i = 1; i <= n; i++) { + line = lines[i] + sub(/^[[:space:]]+/, "", line) # strip indentation + if (line ~ "^" fld "[[:space:]]*=") { + sub(/^[^=]+=/, "", line) # drop "fieldname = " + sub(/^[[:space:]]*/, "", line) # strip leading space + sub(/[[:space:]]*,.*$/, "", line) # drop trailing comma and any inline comment + print line; exit + } + } + print def; exit + } + in_blk = 0 + } + ' "$target" +} + +# Strip surrounding Lua string quotes for display in form ("de" -> de). +unquote() { local v="$1"; v="${v#\"}"; v="${v%\"}"; printf '%s' "$v"; } + +# ---------- show form and collect values ---------- + +case "$type" in +keyboard) + v_layout=$(unquote "$(get_field kb_layout '"de"')") + v_variant=$(unquote "$(get_field kb_variant '""')") + v_model=$(unquote "$(get_field kb_model '""')") + v_options=$(unquote "$(get_field kb_options '""')") + v_rules=$(unquote "$(get_field kb_rules '""')") + v_rate=$(get_field repeat_rate 0) + v_delay=$(get_field repeat_delay 0) + v_sens=$(get_field sensitivity 0) + + # dialog writes its TUI to stdout (/dev/tty) and the result to stderr. + values=$(dialog \ + --title "Keyboard exception: $name" \ + --form "" 16 74 8 \ + "kb_layout" 1 1 "$v_layout" 1 17 42 0 \ + "kb_variant" 2 1 "$v_variant" 2 17 42 0 \ + "kb_model" 3 1 "$v_model" 3 17 42 0 \ + "kb_options" 4 1 "$v_options" 4 17 42 0 \ + "kb_rules" 5 1 "$v_rules" 5 17 42 0 \ + "repeat_rate" 6 1 "$v_rate" 6 17 10 0 \ + "repeat_delay" 7 1 "$v_delay" 7 17 10 0 \ + "sensitivity" 8 1 "$v_sens" 8 17 10 0 \ + 2>&1 >/dev/tty) || exit 0 + + mapfile -t vals <<< "$values" + new_block="hl.device({ + name = \"$name\", + kb_layout = \"${vals[0]:-}\", + kb_variant = \"${vals[1]:-}\", + kb_model = \"${vals[2]:-}\", + kb_options = \"${vals[3]:-}\", + kb_rules = \"${vals[4]:-}\", + repeat_rate = ${vals[5]:-0}, + repeat_delay = ${vals[6]:-0}, + sensitivity = ${vals[7]:-0}, +})" + ;; + +mouse) + v_sens=$(get_field sensitivity 0) + v_accel=$(unquote "$(get_field accel_profile '""')") + v_left=$(get_field left_handed false) + v_natural=$(get_field natural_scroll false) + v_scroll=$(unquote "$(get_field scroll_points '""')") + + values=$(dialog \ + --title "Mouse exception: $name" \ + --form "" 13 74 5 \ + "sensitivity" 1 1 "$v_sens" 1 20 20 0 \ + "accel_profile" 2 1 "$v_accel" 2 20 20 0 \ + "left_handed" 3 1 "$v_left" 3 20 10 0 \ + "natural_scroll" 4 1 "$v_natural" 4 20 10 0 \ + "scroll_points" 5 1 "$v_scroll" 5 20 20 0 \ + 2>&1 >/dev/tty) || exit 0 + + mapfile -t vals <<< "$values" + new_block="hl.device({ + name = \"$name\", + sensitivity = ${vals[0]:-0}, + accel_profile = \"${vals[1]:-}\", + left_handed = ${vals[2]:-false}, + natural_scroll = ${vals[3]:-false}, + scroll_points = \"${vals[4]:-}\", +})" + ;; +esac + +# ---------- replace or append in target file ---------- + +[[ -f "$target" ]] || touch "$target" + +# Remove the existing block for this device (no-op if not present). +tmp=$(mktemp) +awk -v dev="$name" ' + /^hl\.device\(\{/ { blk = $0 "\n"; in_blk=1; matched=0; next } + in_blk { + blk = blk $0 "\n" + if (/name = / && ($0 ~ "\"" dev "\"")) matched=1 + if (/^\}\)/) { + if (!matched) printf "%s", blk + in_blk=0; blk="" + } + next + } + { print } +' "$target" > "$tmp" +mv "$tmp" "$target" + +# Append the new block, separated by a blank line if the file is non-empty. +if [[ -s "$target" ]]; then + [[ "$(tail -c 1 "$target")" != $'\n' ]] && printf '\n' >> "$target" + printf '\n' >> "$target" +fi +printf '%s\n' "$new_block" >> "$target" + +printf 'Written to %s\n' "$target"