#!/bin/sh
# GPL_3+
cat << 'EEE' > /dev/null
/* Copyright (C) 2018 Momi-g
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
EEE
cat << 'EEE' > /dev/null
#SH_doc
title=xkmp section=1 repnl=\040
@name xkmp
@_brief X11 keyconfig, xkbcomp setting helper
@_syno
	xkmp [-hHVls] [-f basefile] [-i inifile] [-a outfile]

@tl;dr
		@(code)@
	~$ xkbcomp $DISPLAY ./keymap.dfl
	~$ xkmp -f keymap.dfl -i setting.ini	#output xkmp.out
	~$ xkmp -a xkmp.out		#apply
	( ~$ xkbcomp keymap.dfl $DISPLAY		#rollback)
		@()
@_opt
	@(list_o)
	-hHV: usage, version
	-l: list for ini setting symbols/keycode etc
	-s: output sample inifile
	-f basefile: basefile made by ~$ 'xkbcomp $DISPLAY -'
	-i inifile: keymap setting file. see '-s' and edit
	@()
@_desc
	'xkmp' makes keymap config file for X11. 
	X11 has standard keyboard setting system `xkbcomp`, but this is
	very complex so noone can handle it. --
	'xkmp' helps to make `xkbcomp` setting with more general	syntax/files. 
	keymap.ini accepts the below commands: --
	'  'define, modclear, keymv, keycp, keydef, modset, red, led, repeat --
	--
	see 'xkmp -s' about keymap.ini detail setting.
@_eg
	@(code)@
	~$ cat << 'EEE' > mykey.ini
	keymv <CAPS> :: <LCTL>	#swap caps<->ctrl
	keymv <LCTL> :: <CAPS>
	keymv <INS>  :: null	#kill ins
	EEE
	
	~$ xkbcomp $DISPLAY - > keymap.dfl
	~$ xkmp -f keymap.dfl -i mykey.ini	#>> output xkmp.out
	~$ xkmp -a xkmp.out		#>> apply keymap
	~$ xev	#>> test keysetting. push ins etc
	~$ xkbcomp keymap.dfl $DISPLAY	#>> rollback to default
	@()@
@exit_status
	suc/fail == 0/not 0

@notes
	I used AutoHotKey(AHK) for keymappting. this app is the result of 
	anger spill over for X11/xkbcomp.
@conforming_to	posix-shell
@copyright	Copyright (C) 2018 Momi-g
@_ver 2021-09-07 v2.0.0
@_see 'xkbcomp(1), xmodmap(1), xev(1), xbindkeys(1), xdotool(1)' --
	'wmctrl(1), bind(bash), zenity(1), dialog(1)' --
	@(code)@
	
	---recommend info
	https://www.charvolant.org/doug/xkb/html/node5.html
	http://pascal.tsu.ru/en/xkb/
	https://wiki.archlinux.jp/index.php/X_KeyBoard_extension
	https://qiita.com/ZeptByteS/items/db1de2cbd940c65b8d1f
	https://github.com/mooz/xkeysnail
	---official, but not recommend
	https://www.x.org/releases/current/doc/libX11/XKB/xkblib.html
	@()@
#SH_docE
EEE
f_usage(){
cat << 'EEE'
HowTo (xkmp, xkbcomp setting helper)
opt: -f(basefile), -i(nifile), -a(pply), -s(ample ini), -l(lbl/sym list) -hHV
------
you *SHOULD* save your dfl key setting for recovery. keymap is very sensitive.
	xkbcomp $DISPLAY ./keymap.dfl	#... save
	xkbcomp ./keymap.dfl $DISPLAY	#... rollback
--
1. make basic file
	~$ xkbcomp $DISPLAY base.file
	
	# mymap.ini
	red {+s} <LatJ> :: {} <UP>	# shift+J -> up

2. run xkmp generate keymap file for 'xkbcomp'
	~$ xkmp -f base.file -i mymap.ini	# -f: xkmp needs pure basefile.

3. apply 'xkmp.out' to 'xkmp -a' or 'xkbcomp'
	~$ xkmp -a xkmp.out	..or..	~$ xkbcomp xkmp.out $DISPLAY

4. check working or recover
	~$ xev	 ..or.. ~$ xkbcomp base.file $DISPLAY (rollback)
...see 'xkmp -s/H'
EEE
	exit 0
}

f_usage_H(){
 cat << 'E E'|sed -e'1d;$d'
/*--copyfrom xkmp.1.txt*/
XKMP(1)                     General Commands Manual                    XKMP(1)



NAME
       xkmp - X11 keyconfig, xkbcomp setting helper

SYNOPSIS
       xkmp [-hHVls] [-f basefile] [-i inifile] [-a outfile]

TL;DR
       ~$ xkbcomp $DISPLAY ./keymap.dfl
       ~$ xkmp -f keymap.dfl -i setting.ini    #output xkmp.out
       ~$ xkmp -a xkmp.out      #apply
       ( ~$ xkbcomp keymap.dfl $DISPLAY        #rollback)


OPTIONS
       -hHV   usage, version

       -l     list for ini setting symbols/keycode etc

       -s     output sample inifile

       -f basefile
              basefile made by ~$ xkbcomp $DISPLAY -

       -i inifile
              keymap setting file. see -s and edit

DESCRIPTION
       xkmp  makes keymap config file for X11.  X11 has standard keyboard set‐
       ting system `xkbcomp`, but this is very complex so noone can handle it.
       xkmp helps to make `xkbcomp`  setting  with  more  generalsyntax/files.
       keymap.ini accepts the below commands:
         define, modclear, keymv, keycp, keydef, modset, red, led, repeat

       see xkmp -s about keymap.ini detail setting.

EXSAMPLE
       ~$ cat << 'EEE' > mykey.ini
       keymv <CAPS> :: <LCTL>   #swap caps<->ctrl
       keymv <LCTL> :: <CAPS>
       keymv <INS>  :: null     #kill ins
       EEE

       ~$ xkbcomp $DISPLAY - > keymap.dfl
       ~$ xkmp -f keymap.dfl -i mykey.ini #>> output xkmp.out
       ~$ xkmp -a xkmp.out      #>> apply keymap
       ~$ xev    #>> test keysetting. push ins etc
       ~$ xkbcomp keymap.dfl $DISPLAY     #>> rollback to default


EXIT_STATUS
       suc/fail == 0/not 0

NOTES
       I  used  AutoHotKey(AHK)  for  keymappting.  this  app is the result of
       anger spill over for X11/xkbcomp.

CONFORMING_TO
       posix-shell

COPYRIGHT
       Copyright (C) 2018 Momi-g

VERSION
       2021-09-07 v2.0.0

SEE_ALSO
       xkbcomp(1), xmodmap(1), xev(1), xbindkeys(1), xdotool(1)
       wmctrl(1), bind(bash), zenity(1), dialog(1)

       ---recommend info
       https://www.charvolant.org/doug/xkb/html/node5.html
       http://pascal.tsu.ru/en/xkb/
       https://wiki.archlinux.jp/index.php/X_KeyBoard_extension
       https://qiita.com/ZeptByteS/items/db1de2cbd940c65b8d1f
       https://github.com/mooz/xkeysnail
       ---official, but not recommend
       https://www.x.org/releases/current/doc/libX11/XKB/xkblib.html




                                                                       XKMP(1)
/*--copyend xkmp.1.txt*/
E E
exit 0
}
f_version_info(){
cat << 'EEE'
xkmp v2.0.0
Copyright (C) 2018 Momi-g
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
EEE
exit 0
}

#	X11 keymapping helper
# ... xkbcomp $DISPLAY base.file
# ...  ./xkmp.dash -f base.file -i mykey.ini
# ... xkbcomp xkmp.dash.out $DISPLAY
# ... xkbcomp base.file $DISPLAY	...reload

f_err(){ echo "`basename $0`: $*" >/dev/stderr; exit 1;}
f_fileck(){ [ -f "$1" ]||f_err "ERR: ${2-""} file $1 not found" ;}
f_abs()( buf="$1"; [ "${1#/}" = "$1" ] && buf="$PWD/$buf"; printf '%s' "$buf")
f_s0path()( b="$0"; while :;do [ "${b##/*}" = "" ]||b="$PWD/$b"; cd "${b%/*}"
 [ -L "$b" ]||break; b="$b: symbolic link to @";b=`file "${b%:*}"|cut -b ${#b}-
 printf @`; b=${b%??}; done; printf '%s/%s'_ "$PWD" "${b##*/}"
)

xkmp_main()(
 tdir="/tmp"
 cdir="$PWD"
 # cd "$bdir"	#<< argでfile指定があるからここでcdするとnot foundになる 終わってから。
 b="$0"
 while :;do
   [ "${b##/*}" = "" ]||b="$PWD/$b"
   cd "${b%/*}"
   [ -L "$b" ]||break
   b="$b: symbolic link to @"
   b=`file "${b%:*}"|cut -b ${#b}-; printf @`
   b=${b%??}
 done
 cd "$cdir"
 bdir=`dirname "$b"`
 bname=`basename "$b"`
 PATH="$bdir:$PATH"
 
 cmd=$(cat <<- 'END'   # 'END'.. not expand $foo etc. 
    # opt dfl  type	( +add command you want to test input opt)	
      -h   0	bool	f_usage
      -H   0	bool	f_usage_H
      -V   0	bool	f_version_info
      -l   0	bool	'f_list_info "$bdir"'
      -s   0	bool	'f_sample "$bdir"'
      -i   nofile	str
      -f   nofile	str
      -a   0	str		'f_apply "$opt_a"'
	END
 )
 buf=`$bdir/ckopt "$cmd"`  #..or.. "$cmd"  (colon, quiet err mode as getopts )
 eval "$buf"
 
 f_fileck "$opt_i" "inifile"
 f_fileck "$opt_f" "baseini"
 sfile=`f_abs "$opt_f"`
 inifile=`f_abs "$opt_i"`

 cd "$bdir"
 tmpf_1="$tdir/$bname.buf1"
 tmpf_2="$tdir/$bname.buf2"
 tmpf_3="$tdir/$bname.buf3"
 tmpf_4="$tdir/$bname.buf4"
 tmpf_out="$tdir/$bname.out"
 tmpf_base="$tdir/$bname.base"

 echo "bin dir: $bdir" >/dev/stderr
 pwd>/dev/stderr

 #--main start
 cp "$sfile" "$tmpf_base"

 #0. 途中経過表示
 echo "$0: show status to stderr ..." > /dev/stderr

 #1. iniのコメント削除とラベルreplace		lnick @a :: <LatA>
 ./replace_xa.sh -i "$inifile" > "$tmpf_4"
 inifile="$tmpf_4"
 # 色々使う仮想modの情報取得. 下準備. default状態の奴を準備 両方必要なので纏めない
 # symbol2real list, read-vmk list
 s2rmods=`./improvemodifier_xa.sh "$tmpf_base" ` # modifier_map Control { Shift_L };
 rvmods=`./vmod2rmod_xa.sh "$tmpf_base" "$s2rmods"`	# alt alt_l alt_r ...etc real-vmk list

 #2. デフォから指定mod削除	modclear {+s+c+m1}
 cat "$inifile" | awk '$1 == "modclear" {print $0}' > "$tmpf_1"
 ./modclear_xa.sh  -f "$tmpf_base" -i "$tmpf_1" -s "$s2rmods" -m "$rvmods" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #3. keyset系再配置 keymv,keycp 
 ./keyset_xa.sh -f "$tmpf_base" -i "$inifile" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #4. defkeyキーコード作成。ブロック系なのでini直接読み込み。
 ./deflbl_xa.sh -f "$tmpf_base" -i "$inifile" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #5. モディファイヤの指定。	 modset <LatA> <LatB> :: @shift/set (Shift_L)	とか。
 cat "$inifile" | awk '$1 == "modset" {print $0}' > "$tmpf_1"
 ./modset_xa.sh  -f "$tmpf_base" -i "$tmpf_1" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

#define <mKey> :: {
#	kc=255
#	mod=[base, +s, +s+c]
#	sym=[g, Shift_L, A]
#	repeat=off
#}

 #5. remap実行	remap <LatA> :: <LatB>  / or null	>> omitした。remap自体はkeycpで内部で使用してる。
 cat "$inifile" | awk '$1 == "remap" {print $0}' > "$tmpf_1"
 ./remap_xa.sh -f "$tmpf_base" -i "$tmpf_1" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #6. red指定	red {+s+c} <LatT> :: {+s} <LatU>
 cat "$inifile" | awk '$1 == "red" {print $0}' > "$tmpf_1"
 ./red_xa.sh -f "$tmpf_base" -i "$tmpf_1" -m "$rvmods" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #7. led追加
 cat "$inifile" | awk '$1 ~ /led/ {print $0}' > "$tmpf_1"
 ./led_xa.sh -f "$tmpf_base" -i "$tmpf_1" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 #8.	最後にxset向けにrepeatキーコードをコメント	repeat <LatA> <LFSH> :: off >> 並列は面倒くさいので止め。
 cat "$inifile" | awk '$1 == "repeat" {print $0}' > "$tmpf_1"
 ./repeat_xa.sh -f "$tmpf_base" -i "$tmpf_1" > "$tmpf_3"
 cp "$tmpf_3" "$tmpf_base"

 cp "$tmpf_base" "$tmpf_out"
 cp -f "$tmpf_out" "$cdir"

 outname=${tmpf_out##*/}
 echo
 echo "--- complete. run '$bname -a $outname'
 ...or 'xkbcomp $tmpf_out \$DISPLAY' + 'xset r ...' "
)


# apply outputfile
f_apply(){
	f_fileck "$1" "apply"
	fl="$1"
	replist=`cat "$fl"|awk '$1 != "" {print $0}'| awk '{print NR " " $0 }' |
		sort -nr | awk '
		$2 == "//keyrepeat" {print $0}
		$2 != "//keyrepeat" {exit 0}' | sort -n | awk '{$1="";$2="";print $0}' `
	#list of  xset r 66 # define <CAPS> , ...

	cmd="xkbcomp \"$fl\" \$DISPLAY"
	echo "run command:  $cmd"
	eval "$cmd"		# xkbcomp outfile $diSPLAY
	[ $? = 0 ] || f_err "xkbcomp failed..."

	echo "xkbcomp suc. run xset:"
	echo "$replist"
	eval "$replist"
	[ $? = 0 ]||f_err "xset failed. recommend to reset 'xkbcomp keymap.bak \$DISPLAY'"
	exit 0
}

f_list_info(){
# search lbl(+alias) from xkmcomp
 cd "$1"
 kc_lbl=`xkbcomp $DISPLAY - |
 ./getblock_xa.sh '$1 == "xkb_keycodes"' | tr '=;' ' ' | awk '
	$1 ~/^</ {print $2 " " $1}
	$1 == "alias" {print $2 " " $3}
	' | ./ljoin -k 2 | awk '{print $0 "\t"}'`

#echo "$0: $lbl_kc" >/dev/stderr ; sleep 1000
 kc_sym=`xmodmap -pke | tr '=' ' ' | awk '{$1=""; print $0}'  `

 buf=`printf '%s\n%s\n' "$kc_lbl" "$kc_sym"`
 echo "$buf" | ./ljoin -k 1
 echo "keycode -- label(+alias label) -- keysymbols"
 echo "make from 'xkbcomp \$DISPLAY -' and 'xmodmap -pke'. check 'xev' too"

 cat << 'EEE'
keylabels. see 'xev', 'cat /usr/include/X11/keysymdef.h'

<FK01> - <FK12>
<ESC>,<HZTG>,<TAB>,<CAPS>,<LFSH>,<LCTL>
<LWIN>,<LALT>,<MUHE>,<SPCE>,<HENK>,<HKTG>,<RALT>,<MENU>,<RCTL>
<RTSH>,<RTRN>,<BKSP>

<PRSC>,<SCLK>,<PAUS>
<INS>,<DELE>,<HOME>,<END>,<PGUP>,<PGDN>
<UP>,<DOWN>,<LEFT>,<RGHT>

<KP0> - <KP9>
<NMLK>, <KPDV>, <KPMU>, <KPSU>, <KPAD>, <KPEN>, <KPDL>

1-0 <AE01>-<AE10>
a-z <LatA> - <LatZ>

-^\ ...AE11-13
@[ ... AD11-12
;:] ... AC10-12
,./\ ... AB08-11
EEE
 exit 0
}

f_sample(){ cat $1/dropsample.ini; exit 0; }

# set -e is stupid command. fuckin rule is added between posix-2008 to posix-2013 
# https://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside-subshells-with-parenthesis-followed-by-an-or
xkmp_main "$@" || exit 	#SH_MAIN



cat<<'EEE'>/dev/null
 change log
 --
2021-09-07  Momi-g	<dmy@dmy.dmy>

	* xkmp(all): change ini syntax define, lnick, etc
	
	* xkmp(all): new pkg. v2.0.0

2018-09-19  Momi-g	<dmy@dmy.dmy>

	* xkmp.dash(all): publish v1.0.0

EEE
