127 lines
5.7 KiB
Bash
Executable File
127 lines
5.7 KiB
Bash
Executable File
#!/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
|