README and upload xkbhyprmodfix

This commit is contained in:
Moririn 2023-03-15 18:00:00 +00:00
parent 312f39b4e1
commit 462aecc59c
2 changed files with 133 additions and 1 deletions

View File

@ -1,3 +1,9 @@
# xkbhyprmodfix
Replace CapsLock with Hyper_L without keybinds assigned to Hyper clashing with other modifiers on Mod4.
Replace CapsLock with Hyper_L without keybinds assigned to Hyper clashing with those on Super.
Creates a new xkb keymap, "HyperCaps", identical to the user's current keyboard layout, except with CapsLock replaced with Hyper and Hyper_L moved to Mod3. The symbol file for the current layout and the xkb rules files in `/usr/share/X11/xkb` are cloned to `/usr/local/share/X11/xkb/`, edited, and then symlinked to. The original unmodified files are backed up.
This is a workaround to desktop environments such as KDE overwriting any keyboard setup done with xinit configuration files.
Must be run as root (to grant access to `/usr/share` and `/usr/local/share`).

126
xkbhyprmodfix.sh Executable file
View File

@ -0,0 +1,126 @@
#!/bin/sh
# test for root
[ $(id -u) = 0 ] || { printf "%s\n" "xkbhyprmodfix: must be run as root user"; exit 2; }
rundate="$(date '+%s')" # UNIX timestamp for backups
# get keyboard layout info
xkbinfo=$(setxkbmap -query)
layouts=$(echo "$xkbinfo" | grep "^layout:" | sed -E "s/layout:[[:space:]]+//g")
variants=$(echo "$xkbinfo" | grep "^variant:" | sed -E "s/variant:[[:space:]]+//g")
# extract layout and variant settings if only one setting, otherwise ask the user to select which layout they want to modify
echo "$layouts" | grep -q "," || { layout="$layouts"; variant="$variants"; }
while ! [ "$layout" ]; do
layoutcount=$(echo "$layouts" | tr ',' ' ' | wc -w)
printf "%s " "Multiple keyboard layouts detected ($layouts). Select one to proceed (1-$layoutcount)"
read -r selection
case "$selection" in
''|*[!0-9]*)
printf "%s\n" "Invalid selection '$selection'."
continue
;;
esac
{ [ "$selection" -gt 0 ] && [ "$selection" -le "$layoutcount" ]; } || { printf "%s\n" "Invalid selection '$selection'."; continue; }
layout=$(echo "$layouts" | cut -d"," -f"$selection")
variant=$(echo "$variants" | cut -d"," -f"$selection")
done
# new keyboard will duplicate the existing one
# the default layout of that language is not a variant, meaning $variant will be empty, but is referred to as "basic" in the symbol file
# $variant is copied into a new variable which is set to "basic" if blank
# similarly, "basic" is a valid variant but is considered as no variant in the .lst files
# $variant is set to blank if it is "basic"
variant_inherit="$variant"
[ -n "$variant_inherit" ] || variant_inherit="basic"
[ "$variant" = "basic" ] && variant=
xkbdir=/usr/share/X11/xkb
localxkbdir=/usr/local/share/X11/xkb_hypercaps
locallayout="$localxkbdir"/symbols/"$layout"
[ -e "$localxkbdir" ] && {
printf "%s " "Filepath '$localxkbdir' exists. Files found at this path or its subdirectories may be overwritten. Continue? (y/n)"
read -r selection
[ "$selection" = "y" ] || [ "$selection" = "Y" ] || { printf "%s\n" "Exiting."; exit 2; }
}
# make sure the layout exists in symbols and hypercaps layout is not already applied
[ -f "$xkbdir"/symbols/"$layout" ] || { printf "%s\n" "Error: $xkbdir/symbols/$layout not found. Exiting."; exit 2; }
grep -q 'xkb_symbols "hypercaps" {' "$xkbdir"/symbols/"$layout" && { printf "%s\n" "Error: hypercaps layout for layout '$layout' already exists."; exit 2; }
# Get layout title
lstfile=$(wc -l "$xkbdir"/rules/*.lst | grep -v "total$" | sort -b -r -h | sed 's/^[[:space:]]\+[0-9]\+ //g; 1q') # Use the longest .lst file
[ -n "$variant" ] \
&& lstpattern="^[[:space:]]{2}${variant}[[:space:]]+$layout:[[:space:]]" \
|| lstpattern="^[[:space:]]{2}${layout}[[:space:]]{8,}"
layouttitle=$(grep -E "$lstpattern" "$lstfile" | head -n 1 | sed -E "s/$lstpattern//g")
[ -n "$layouttitle" ] || { printf "%s\n" "Warning: Keyboard layout name not found in '$lstfile'. Defaulting to '$layout (HyperCaps)' as name."; layouttitle="$layout ()"; }
hypercapstitle=$(echo "$layouttitle" | sed -E 's/\((.+)\)$/\(\1, HyperCaps\)/; s/\(\)/(HyperCaps)/')
echo "$hypercapstitle" | grep -q "HyperCaps)" || hypercapstitle="$layouttitle (HyperCaps)"
# file setup
mkdir -p "$localxkbdir"/symbols
mkdir -p "$localxkbdir"/rules
cp -f "$xkbdir"/symbols/"$layout" "$locallayout"
cp -f -d -t "$localxkbdir"/rules/ "$xkbdir"/rules/base.xml "$xkbdir"/rules/base.lst \
"$xkbdir"/rules/evdev.xml "$xkbdir"/rules/evdev.lst \
"$xkbdir"/rules/xorg.xml "$xkbdir"/rules/xorg.lst
# append new keyboard layout to copied symbol file if it does not already exist
grep -q 'xkb_symbols "hypercaps" {' "$locallayout" || \
printf "\n%s\n%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n%s\n" \
"partial alphanumeric_keys" \
"xkb_symbols \"hypercaps\" {" \
"include \"$layout($variant_inherit)\"" \
"replace key <HYPR> { [ Super_L, Super_L ] };" \
"name[Group1]=\"$hypercapstitle\";" \
"key <CAPS> { [ Hyper_L ] };" \
"modifier_map Mod3 { <CAPS> };" \
"};" >> "$locallayout"
# modify copied rule files if layout is not already applied
grep -q "<description>$hypercapstitle</description>" "$localxkbdir"/rules/*.xml || \
sed -i '/<description>'"$layouttitle"'<\/description>/,/<\/layout>/{/<\/variantList>/i\
<variant>\
<configItem>\
<name>hypercaps</name>\
<description>'"$hypercapstitle"'</description>\
</configItem>\
</variant>
}' "$localxkbdir"/rules/base.xml "$localxkbdir"/rules/evdev.xml
grep -q "<description>$hypercapstitle</description>" "$localxkbdir"/rules/*.xml || \
sed -i '/<description>'"$layouttitle"'<\/description>/,/<\/layout>/{/<\/configItem>/a\
<variantList>\
<variant>\
<configItem>\
<name>hypercaps</name>\
<description>'"$hypercapstitle"'</description>\
</configItem>\
</variant>\
</variantList>
}' "$localxkbdir"/rules/base.xml "$localxkbdir"/rules/evdev.xml
grep -q "hypercaps $layout: $hypercapstitle" "$localxkbdir"/rules/*.lst || \
sed -i "/^! variant$/a \ \ hypercaps $layout: $hypercapstitle" "$localxkbdir"/rules/*.lst
#
# danger zone
#
# backup original files and alias modified copies
cp -d -f "$xkbdir"/symbols/"$layout" "$xkbdir"/symbols/"${layout}"."${rundate}".bak \
&& ln -s -f "$locallayout" "$xkbdir"/symbols/"$layout" \
|| { printf "%s\n" "Unable to apply modified symbol layout to $xkbdir/symbols/$layout"; exit 2; }
for ext in xml lst; do
for file in base evdev xorg; do
cp -d -f "$xkbdir"/rules/"${file}"."${ext}" "$xkbdir"/rules/"${file}"."${rundate}"."${ext}" \
&& ln -s -f "$localxkbdir"/rules/"${file}"."${ext}" "$xkbdir"/rules/"${file}"."${ext}" \
|| { printf "%s\n" "Unable to replace rule file '${file}.${ext}"; exit 2; }
done
done