(hopefully ergonomic) thumb-heavy keyboard layout

Post your working scripts, libraries and tools for AHK v1.1 and older
42td
Posts: 4
Joined: 20 Oct 2018, 11:15

(hopefully ergonomic) thumb-heavy keyboard layout

20 Oct 2018, 15:23

I have used so-called ergonomic keyboards for years (mostly by Microsoft). IMO they get the angle right for normal shoulder widths, but I can't stand the 19th century-style row staggering anymore. Since all modern ergonomic keyboards that I know of are too expensive, require DIY, or do not have enough keys, I switched back to a normal keyboard, but try to use the staggering to my advantage, treating F/T/6 and J/U/7 as the left and right index fingers' "home column", resp. And so on for the other fingers (D/R/5 = left middle finger). (Speaking in QUERTZ labels here.) That results in a similar forearm angle as the split angled keyboards, but compensates for the column staggering.

That alone would also make the pinky shift keys an even worse torture than before. So, to get shift (and other modifier) keys for the thumbs, I replaced the space bar with a small (single unit wide) key, so the thumbs can freely reach the next row (X/C/V/B/N/M). There's no row for dedicated number keys anymore, but that's a small price to pay IMO. OTOH, function keys closer to the new home row is another advantage.

I'm sharing this script here in QUERTZified form. Personally, I actually relocated the letters, too.

The utility functions here might be useful in other scripts which require a lot of remapping, as they allow configuration via visual keyboard "ASCII art".

Tested with the Windows keyboard layout set to one similar to German default, but without dead keys (in the base layer at least).

"Installation": This script includes and overwrites another script %A_ScriptFullPath%.generated (i.e. called like the name under which you save it with ".generated" appended). You have to initially create it, with encoding UTF-8 with BOM (byte order mark), and write a linefeed in it. (Other contents which Autohotkey accepts should obviously also work, but an empty file somehow does not.)

EDIT: I meant row staggering, not column.

Code: Select all

;Emacs: -*- tab-width: 16 -*-
; Keyboard layout for thumbs on the bottom letter row, no number key row.

; MIT License
; 
; Copyright (c) 2018 42td
; 
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
; 
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.


#SingleInstance force
#Warn, , MsgBox
#NoEnv
SetBatchLines -1
ListLines Off
Process, Priority, , High
SendMode Input
EnvGet, SystemRoot, SystemRoot
Menu Tray, Icon, %SystemRoot%\system32\shell32.dll, 45

aliases := {}
aliases["Spc*"  ] := "send {u+00a0}"
aliases["-*"    ] := "send {u+2011}"	; non-breaking hyphen
aliases["%"     ] := "send {``%}"
aliases["\"     ] := "send {u+005c}"
aliases["{"     ] := "send {{}"
aliases["}"     ] := "send {}}"
aliases["FF"    ] := "send {u+240c}"
aliases["FS"    ] := "send {u+241c}"
aliases["GS"    ] := "send {u+241d}"
aliases["RS"    ] := "send {u+241e}"
aliases["US"    ] := "send {u+241f}"
aliases["("")"  ] := "send {u+0308}"	; dead diaeresis
aliases["(́́´)"   ] := "send {u+0301}"	; dead acute
aliases["(́́``)"  ] := "send {u+0300}"	; dead grave
aliases["(́́^)"   ] := "send {u+0302}"	; dead circumflex
aliases["(~)"   ] := "send {u+0303}"	; dead tilde
aliases["(́°)"   ] := "send {u+030a}"	; dead circle
aliases["(,)"   ] := "send {u+0327}"	; dead cedille
aliases["𝛑"	] := "send {𝛑}"
aliases["╔·"	] := "┌"	; because single-line box drawing characters
aliases["╦·"	] := "┬"	; are used for pretty-printing, these aliases
aliases["╗·"	] := "┐"	; exist to denote those characters when
aliases["╠·"	] := "├"	; mapping them
aliases["╬·"	] := "┼"	;
aliases["╣·"	] := "┤"	;
aliases["╚·"	] := "└"	;
aliases["╩·"	] := "┴"	;
aliases["╝·"	] := "┘"	;
aliases["║·"	] := "│"	;
aliases["═·"	] := "─"	;

; === layers ===
modS  := [ "v", "m" ]
modL  := [ "b", "n" ]
modR  := [ "c", "sc033" ]
modK  := [ "LAlt", "Ralt" ]
modM  := [ "sc039" ]	;[ "LCtrl" ]
modWN := [ "LCtrl" ]
code := disableKeysCode(modL, modR, modM)

keys := unpretty(["║│", "║"], " ═╔╦╗╚╩╝╠╬╣╤╧"
,"╔═══════╦═══════╦═══════╦═══════╦═══════╦═══════╦═══════╦╤══════╦═══════╦═══════╦═══════╦═══════╦═══════╦═══════════════╗"
,"║ sc029 ║   1   ║   2   ║   3   ║   4   ║   5   ║   6   ║│  7   ║   8   ║   9   ║   0   ║ sc00c ║ sc00d ║            BS ║"
,"╠═══════╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩╧══╦╤══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══════════╣"
,"║ Tab       ║   q   ║   w   ║   e   ║   r   ║   t   ║   z   ║│  u   ║   i   ║   o   ║   p   ║ sc01a ║ sc01b ║     Enter ║"
,"╠═══════════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩╧╦╤════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╗         ║"
,"║ CapsLock    ║   a   ║   s   ║   d   ║   f   ║   g   ║   h   ║│  j   ║   k   ║   l   ║ sc027 ║ sc028 ║ sc02b ║         ║"
,"╠═════════╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦╤══╩╧══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══════╩═════════╣"
,"║ LShift  ║ sc056 ║   y   ║   x   ║       ║       ║       ║│      ║       ║       ║ sc034 ║ sc035 ║              RShift ║"
,"╚═════════╩═══════╩═══════╩═══════╩═══════╩═══════╩═══════╩╧══════╩═══════╩═══════╩═══════╩═══════╩═════════════════════╝")

code .= hotkeysCode([], [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│   ^   │   /   │   q   │   w   │   e   │   r   │   t   ║   z   │   u   │   i   │   o   │   p   │   ß   │   ≈           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│ LAlt      │ LCtrl │   a   │   s   │   d   │   f   │   g   ║   h   │   j   │   k   │   l   │   x   │ RCtrl │      LAlt │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│ Tab         │ Enter │   BS  │ Space │   c   │   v   │   b   ║   n   │   m   │   ,   │   .   │   y   │  Tab  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│ CapsLock│   -   │   +   │  ``   │       │       │       ║       │       │       │  Ins  │  Del  │                 Esc │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modS, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│   °   │   \   │  ---  │  ---  │  ---  │  ---  │  ---  ║  ---  │  ---  │  ---  │  ---  │  ---  │   ẞ   │   ≉           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  ---  │  ---  │  ---  │  ---  │  ---  │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │+Enter │  +BS  │+Space │  ---  │  ---  │  ---  ║  ---  │  ---  │  ---  │  ---  │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   _   │  ---  │   ʼ   │       │       │       ║       │       │       │  ---  │  ---  │                 --- │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modL, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │   ∃   │   ∀   │   '   │  ""   │  ---  ║   ☐   │   {   │   }   │  ---  │   ∅   │   ≤   │   ≥           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   -   │   +   │   \   │   /   │   |   ║   *   │   (   │   )   │   ~   │   $   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   <   │   >   │   &   │  ---  ║   @   │   [   │   ]   │   …   │   !   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   ‒   │   ×   │  (,)  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modS, modL,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │   ∄   │  ---  │  ---  │   ©   │  ---  ║   ☒   │   ☑   │  ---  │  ---  │   ⦰   │   «   │   »           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │   ∞   │   ∧   │  ---  │   ∨   ║  ---  │   ¬   │  ---  │  (~)  │   ÷   │---    │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   ≤   │   ≥   │  ---  │  ---  ║   •   │  ---  │  ---  │   ‥   │   ‼   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -*  │   ✕   │ ("")  │       │       │       ║       │       │       │  ---  │  ---  │               ---   │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modR, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  (^)  │  ---  │  ---  │  ---  │  ---  │   _   │  ---  ║  ---  │   7   │   8   │   9   │   ¿   │   “   │  ”            │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   ü   │   ö   │   ä   │   ?   │   ‖   ║   #   │   4   │   5   │   6   │   .   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   =   │   %   │   !   │  ---  ║   0   │   1   │   2   │   3   │   ,   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   —   │   ±   │ (``)  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modS, modR,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  (°)  │  ---  │  ---  │  ---  │  ---  │   ¢   │   ¤   ║  ---  │   ⁷   │   ⁸   │   ⁹   │   ⁿ   │   „   │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   Ü   │   Ö   │   Ä   │   €   │   ¦   ║   ⁽   │   ⁴   │   ⁵   │   ⁶   │   ⁾   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   ≠   │   £   │  ---  │  ---  ║   ⁰   │   ¹   │   ²   │   ³   │   §   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   –   │   ⋆   │  (́́´)  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modK, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │   ¼   │   ⅓   │   ½   │   ⅔   │   ¾   ║   ✛   │   ↖   │   ↑   │   ↗   │   ⁈   │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │   ∈   │  ---  │  ---  │   ∖   ║   ∑   │   ←   │   ↓   │   →   │   ⁄    │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   ≙   │   ¶   │  ---  │   ♫   ║   ↕   │   ↙   │   ↔   │   ↘   │   ✓   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ⸺  │   ✻   │  ---  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modS, modK,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │  ---  │   ♣   │   ♠   │   ♥   │   ♦   ║   ☩   │   ⇖   │   ⇑   │   ⇗   │   ⁉   │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │   ∉   │  ---  │  ---  │  ---  ║  ---  │   ⇐   │   ⇓   │   ⇒   │   ∕   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │   ≟   │   ‣   │  ---  │   ♬   ║   ⇕   │   ⇙   │   ⇔   │   ⇘   │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │ ⸻ │   ★   │  ---  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modM, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║ ^Home │  Home │   Up  │  End  │ ^End  │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  Ins  │  Left │  Down │ Right │  PgUp │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║ +Ins  │ ^Left │  Del  │^Right │ PgDn  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modK, modL,	keys, unpretty(["│", "·║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │   ⊆   │   ∪   │  ---  │  ---  │   ⊇  ·║  ---  │   ╔·  │   ╦·  │   ╗·  │  ---  │   ≪   │   ≫          │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   ⊂   │  ---  │  ---  │  ---  │   ⊃  ·║   ∩   │   ╠·  │   ╬·  │   ╣·  │   ═·  │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  Spc* │  ---  │  ---  │  --- ·║  ---  │   ╚·  │   ╩·  │   ╝·  │   ║·  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │       │       │      ·║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modK, modR,	keys, unpretty(["│", "·║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  FF   │  FS   │  GS   │  RS   │  US   │  --- ·║  ---  │   ╔   │   ╦   │   ╗   │  ---  │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │  ---  │  ---  │  ---  │  --- ·║  ---  │   ╠   │   ╬   │   ╣   │   ═   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │  ---  │  ---  │  --- ·║  ---  │   ╚   │   ╩   │   ╝   │   ║   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │       │       │      ·║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
code .= hotkeysCode(modL, modR,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │  ---  │  ---  │  ---  │   γ   │  ---  ║  ---  │   ⌂   │  ---  │  ---  │  ---  │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │  ---  │   α   │   ε   │  ---  ║  ---  │   ✗   │   ®   │   ™   │   δ   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │   𝛑   │  ---  │  ---  ║   β   │   µ   │  ---  │   ·   │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

code .= hotkeysCode(modWN, [],	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┬───────┬───────┬───────┬───────┬───────┬───────╥───────┬───────┬───────┬───────┬───────┬───────┬───────────────┐"
,"│  ---  │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  ---  │  #7   │  #8   │  #9   │  ---  │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  ---  │  #4   │  #5   │  #6   │  ---  │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  #0   │  #1   │  #2   │  #3   │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │       │       │       ║       │       │       │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))

; assigns keys to a NOOP
; each parameter may be a single key string or a list thereof
disableKeysCode(keys*) {
  local i, e, code := ""
  log(A_ThisFunc, A_ThisFunc . "(" . arrayToString(keys) . ")")
  for i, e in keys {
    code .= isObject(e) ? disableKeysCode(e*) : (e . "::`treturn`n")
  }
  return code
}

; parameters modifierKeys1, modifierKeys2: list of alternative modifier keys (e.g. left and right kana);
; - both empty to define base layer
; - one of them empty to define a layer reachable with a single modifier
; - both non-empty to define a layer reachable with two modifiers
hotkeysCode(modifierKeys1, modifierKeys2, fromKeys, toKeys) {
    ; two modifiers: use each in both (#If and prefix) roles, e.g.
    ; modifierKeys1 == [ "a", "b" ], modifierKeys2 == [ "c", "d" ] =>
    ; #If (GetKeyState("a", "P") || GetKeyState("b", "P"))
    ; c & from1:: to1
    ; c & from2:: to2
    ; #If (GetKeyState("a", "P") || GetKeyState("b", "P"))
    ; d & from1:: to1
    ; d & from2:: to2
    ; #If (GetKeyState("c", "P") || GetKeyState("d", "P"))
    ; a & from1:: to1
    ; a & from2:: to2
    ; #If (GetKeyState("c", "P") || GetKeyState("d", "P"))
    ; b & from1:: to1
    ; b & from2:: to2
    ; This is required because the former half recognizes a & c & from1 with a pressed before c etc,
    ; and the latter half recognizes c & a & from1 with a pressed before c etc.
  return modifierKeys1.length() > 0 && modifierKeys2.length() > 0 ? (hotkeysCodeImpl(modifierKeys1, modifierKeys2, fromKeys, toKeys) . hotkeysCodeImpl(modifierKeys2, modifierKeys1, fromKeys, toKeys))
    ; only one modifier: use as #If
    : modifierKeys2.length() > 0 ? hotkeysCodeImpl(modifierKeys2, modifierKeys1, fromKeys, toKeys)
    ; one modifier (already given as #If) or none
    : hotkeysCodeImpl(modifierKeys1, modifierKeys2, fromKeys, toKeys)
}

hotkeysCodeImpl(modifierIfKeys, modifierPrefixKeys, fromKeys, toKeys) {
  local i, k, code, comment
  if (modifierPrefixKeys.length() == 0) {
    return hotkeysCodeImplIf(modifierIfKeys) . hotkeysCodeImplHotkeys("", fromKeys, toKeys)
  } else {
    comment := " && (" . arrayToString(modifierPrefixKeys, " || ", "", 0) . ")"
    code := hotkeysCodeImplIf(modifierIfKeys, comment)
    for i, k in modifierPrefixKeys {
      code .= hotkeysCodeImplHotkeys(k . " & ", fromKeys, toKeys)
    }
    return code
  }
}

; Map e.g. [ "a", "b" ] to `#If (GetKeyState("a", "P") || GetKeyState("b", "P"))'
hotkeysCodeImplIf(modifierKeys, descriptionSuffix = "") {
  local i, k
  local code := ";`t____________________/ ("
  code .= arrayToString(modifierKeys, " || ", "", 0) . ")" . descriptionSuffix
  code .= " \____________________"

  for i, k in modifierKeys {
    code .= (i == modifierKeys.minIndex()) ? "`n#If " : " || "
    code .= "GetKeyState(""" . k . """, ""P"")"
  }
  code .= "`n"
  return code
}  

; Builds the hotkey definition code, e.g.
; | pk & fk1:: tk1
; | pk & fk2:: tk2
; if prefix == "pk & ", fromKeys == [ "fk1", "fk2" ] and toKeys == [ "tk1", "tk2" ]
; prefix can be empty; otherwise it must include the " & "
hotkeysCodeImplHotkeys(prefix, fromKeys, toKeys) {
  local i, k, kt, code := ""
  if (fromKeys.length() != toKeys.length()) {
    throw "key count mismatch: " . arrayToString(fromKeys) . " / " . arrayToString(toKeys)
  }

  for i, k in fromKeys {
    kt := toKeys[i]
    if (neq(kt, "---")) {
      kt := eq(aliases[kt], "") ? kt : aliases[kt]
      if (eq(prefix, "") && eq(k, kt)) { ; comment out identity mapping
        code .= ";;"
      }
      code .= prefix . k . "::`t" . kt . "`n"
    }
  }
;  log(A_ThisFunc, code)
  return code
}

code .= "#If"
log("---------------------------------- generated code ----------------------------------")
log(code)
log("---------------------------------- end of generated code ----------------------------------")
if (fileReplaceInPlace(A_ScriptFullPath . ".generated", ".*", "", code)) {
  fileAppend, edited %A_ScriptFullPath%.generated`, re-starting`n, *
  sleep 1000
  reload
}

; Extracts substrings from a given string, discarding some characters
; (e.g. to extract keys from a pretty-printed keyboard layout).
; This function undoes the pretty-printing by
; 1. discarding all characters in ignoredChars;
; 2. splitting each param at separators (a string or array of strings, see standard function strSplit);
; 3. removing empty split results;
; 4. then joining the split results into one array.
; Example: unpretty("|", " _"
;   , "|_q_|_w_|_e_|_r_|"
;   , "|   |F10| d |")
; == ["q", "w", "e", "r", "F10", "d"]
unpretty(separators, ignoredChars, decoratedKeys*) {
	local results := []
	local decoratedKey, part
	for uu, decoratedKey in decoratedKeys {
		local parts := strSplit(decoratedKey, separators, ignoredChars)
		for uu, part in parts {
			if (neq(part, "")) {
				results.push(part)
			}
		}
	}
	log(A_ThisFunc, "unpretty(..) == " . arrayToString(results))	;?
	return results
}

arrayToString(array, separator = ", ", elemToStringFunc = "", withIndex = 1) {
	local i, e, s
	s := ""
	for i, e in array {
		s .= eq(s, "") ? "" : separator
		if (withIndex) {
			s .= "[" . i . "] "
		}
		s .= (elemToStringFunc == "" ? e : elemToStringFunc.call(e))
	}
	return s
}

log(arg0, arg1 = "") {
	local e, arg0nl
	if (neq(arg1, "")) {
		log(format("{:-22.22s}:{}", arg0, arg1))
	} else {
		try {
			arg0nl := arg0 . "`n"
			FileAppend, %arg0nl%, **
		} catch e {
			OutputDebug %arg0%
		}
	}
}

; compares values for strict (string) equality
; see https://autohotkey.com/board/topic/28987-compare-two-variables-as-strings/
eq(val1, vals*) {
	local i, val2
	for i, val2 in vals {
		if (("" . val1) != ("" . val2)) {
			return 0
		}
	}
	return 1
}
neq(val1, vals*) {
	return !eq(val1, vals*)
}

fileReplaceInPlace(fileName, startMarkRegex, endMarkRegex, newLines*) {
	local file := fileOpen(fileName, "rw-")
	if (!isObject(file)) {
		throw "failed to open file " . file . ": " . A_LastError
	}
	local section := 0 ; 0 / 1 / 2: before / in / after the section to replace
	local i, startPos, crlf, oldLines := [], linesAfter := []
	while (!file.atEOF) {
		local line := file.readLine()
		local chompedLine := regexReplace(line, "`r?`n$", "")
		crlf := regexMatch(line, "`r`n$")
;		outputDebug % "input line:`t" . line
		if (section == 0) {
			if (regexMatch(chompedLine, startMarkRegex)) {
				section := 1
				startPos := file.tell()
			}
		} else if (section == 1) {
			if (neq(endMarkRegex, "") && regexMatch(chompedLine, endMarkRegex)) {
				section := 2
				linesAfter.push(line)
			} else {
				oldLines.push(chompedLine)
			}
		} else {
			linesAfter.push(line)
		}
	}

	outputDebug % "old lines: " . arrayToString(oldLines)
	outputDebug % "new lines: " . arrayToString(newLines)
	local sectionChanged := neq(join(oldLines, "`n"), join(newLines, "`n"))
	if (section > 0 ; section to replace found
			&& sectionChanged) {
		; write replacement
		local eol := crlf ? "`r`n" : "`n"
		file.seek(startPos)
		for i, line in newLines {
			file.write(line)
			file.write(eol)
		}

		; Write remaining input lines after replacement;
		; The end marker is optional; if not found, replace until EOF.
		for i, line in linesAfter {
			file.write(line)
		}

		; truncate
		file.length := file.tell()
	}
	file.close()
	return section > 0 && sectionChanged
}

join(array, separator = ", ", elemToStringFunc = "") {
	return arrayToString(array, separator, elemToStringFunc, 0)
}


; === misc ===
NumLock & Esc::	exitApp
NumLock & F1::	suspend
NumLock & F2::	reload
NumLock::	NumLock
v::	RShift
m::	LShift
LControl::	return

; === sub thumb row ===
; LCtrl::	navigation modifier
; LWin::	
; LAlt::	
; RAlt::	
; AppsKey::	
; RWin::	
; RCtrl::	


; === navigation block, ... ===
; PrintScreen::	
; ScrollLock::	
; Pause::	
; 
; Ins::	
; Home::
; PgUp::	
; Del::	
; End::	
; PgDn::	
; 
; Up::	
; Left::	
; Down::	
; Right::	


; === number pad ===
; NumLock::	
; NumpadDiv::	
; NumpadMult::	
; NumpadHome::	
; NumpadUp::	
; NumpadPgUp::	
; 
; NumpadLeft::	
; NumpadClear::	
; NumpadRight::	
; NumpadEnd::	
; NumpadDown::	
; NumpadPgDn::	
; 
; NumpadIns::	
; NumpadDel::	
; NumpadEnter::	
; NumpadAdd::	
; NumpadSub::	


#Include %A_ScriptFullPath%.generated
42td
Posts: 4
Joined: 20 Oct 2018, 11:15

Re: (hopefully ergonomic) thumb-heavy keyboard layout

02 Feb 2019, 17:11

That layout turned out to be very good for my little fingers, but I did not like how much I had to move the thumbs between three keys each to reach all important characters.

So I placed three layers on just the two thumb home keys (V and M in QUERTY): For normal shift (mostly capital letters, of course), one must now use the shift key of the other hand than the combined key, as one normally does. When combining a key with the same hand shift key ("odd shift"), it's now a different layer. And double shift is yet another. Now the thumbs can rest on there home position for all frequently used characters. Similarly, I placed three layers on thumb keys B and N for the more exotic characters. The other thumb keys C and comma are no longer needed as modifiers, so I put Backspace and Enter on them. The navigation layer is still on space.

Typing in caps by holding a single shift is no longer possible, so I need to get used to using CapsLock more often. But otherwise, I really like this layout.

Keyboard photo: https://imgur.com/a/hwyji6M

Here's the updated code. But this time, I'm too lazy to change letters back to QUERTZ. This is NEO-inspired, how I actually use it.

The installation caveat still applies.

Code: Select all

; MIT License
; 
; Copyright (c) 2019 42td
; 
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
; 
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.


; Keyboard layout for thumbs on the bottom letter row, no number key row.
; Also maps German QUERTZ layout to approximately Neo.

; TODO
; - map a key to moving the mouse pointer to the focussed control (to make scroll wheel mapping more useful)
; - hotkey to show image of layout

#SingleInstance force
#Warn, , MsgBox
#NoEnv
SetBatchLines -1
ListLines Off
Process, Priority, , High
SendMode Input
EnvGet, SystemRoot, SystemRoot
Menu Tray, Icon, %SystemRoot%\system32\shell32.dll, 45

; assigns keys to a NOOP
; each parameter may be a single key string or a list thereof
disableKeysCode(keys*) {
  local i, e, code := ""
  log(A_ThisFunc, A_ThisFunc . "(" . arrayToString(keys) . ")")
  for i, e in keys {
    code .= isObject(e) ? disableKeysCode(e*) : (e . "::`treturn`n")
  }
  return code
}

; 1st pair of parameters is for keys in group "L", 2nd pair for "R", third pair for "-"
hotkeysCodeG(modifierKeysL1, modifierKeysL2, modifierKeysR1, modifierKeysR2, modifierKeysO1, modifierKeysO2, keyToGroup, fromKeys, toKeys) {
  local code := "", selectedKeys
  selectedKeys := filterByGroup(fromKeys, toKeys, "L", keyToGroup)
  code .= hotkeysCode(modifierKeysL1, modifierKeysL2, selectedKeys[1], selectedKeys[2])
  selectedKeys := filterByGroup(fromKeys, toKeys, "R", keyToGroup)
  code .= hotkeysCode(modifierKeysR1, modifierKeysR2, selectedKeys[1], selectedKeys[2])
  selectedKeys := filterByGroup(fromKeys, toKeys, "-", keyToGroup)
  code .= hotkeysCode(modifierKeysO1, modifierKeysO2, selectedKeys[1], selectedKeys[2])
  return code
}

; parameters modifierKeys1, ..2: each is a map of alternative modifier keys
; (e.g. left and right kana) to the group of keys to which it should apply (or
; group "-" for all);
; - both NONE to define base layer
; - one of them NONE to define a layer reachable with a single modifier
; - both non-NONE to define a layer reachable with two modifiers
; - more modifiers not supported
hotkeysCode(modifierKeys1, modifierKeys2, fromKeys, toKeys) {
    ; two modifiers: use each in both (#If and prefix) roles, e.g.
    ; modifierKeys1 == [ "a", "b" ], modifierKeys2 == [ "c", "d" ]
    ; meaning ("a" or "b") and ("c" or "d") => generate code:
    ; #If (GetKeyState("a", "P") || GetKeyState("b", "P"))
    ; c & from1:: to1
    ; c & from2:: to2
    ; d & from1:: to1
    ; d & from2:: to2
    ; #If (GetKeyState("c", "P") || GetKeyState("d", "P"))
    ; a & from1:: to1
    ; a & from2:: to2
    ; b & from1:: to1
    ; b & from2:: to2
    ; This is required because the former half recognizes a & c & from1 with a pressed before c etc,
    ; and the latter half recognizes c & a & from1 with a pressed before c etc.
  return modifierKeys1.length() > 0 && modifierKeys2.length() > 0
    ? (   hotkeysCodeImpl(modifierKeys1, modifierKeys2, fromKeys, toKeys)
        . hotkeysCodeImpl(modifierKeys2, modifierKeys1, fromKeys, toKeys))
    ; only one modifier: use as prefix
    : modifierKeys2.length() > 0
    ? hotkeysCodeImpl(modifierKeys1, modifierKeys2, fromKeys, toKeys)
    ; one modifier (already given as prefix) or none
    : hotkeysCodeImpl(modifierKeys2, modifierKeys1, fromKeys, toKeys)
}

hotkeysCodeImpl(modifierIfKeys, modifierPrefixKeys, fromKeys, toKeys) {
  local i, k, code, comment
  log(A_ThisFunc, A_ThisFunc . "(" . arrayToString(modifierIfKeys) . ", " . arrayToString(modifierPrefixKeys) . ", " . arrayToString(fromKeys) . ", " . arrayToString(toKeys) . ")")
  if (modifierPrefixKeys.length() == 0) {
    return hotkeysCodeImplIf(modifierIfKeys) . hotkeysCodeImplMapping("", fromKeys, toKeys) . "#If`n"
  } else {
    comment := " && (" . arrayToString(modifierPrefixKeys, " || ", "", 0) . ")"
    code := hotkeysCodeImplIf(modifierIfKeys, comment)
    for i, k in modifierPrefixKeys {
      code .= hotkeysCodeImplMapping(k . " & ", fromKeys, toKeys)
    }
    code .= "#If`n"
    return code
  }
}

; Map e.g. [ "a", "b" ] to `#If (GetKeyState("a", "P") || GetKeyState("b", "P"))'
hotkeysCodeImplIf(modifierKeys, descriptionSuffix = "") {
  local i, k
  local code := ";`t--------------------- ("
  log(A_ThisFunc, A_ThisFunc . "(" . arrayToString(modifierKeys) . ", " . descriptionSuffix . ")")
  code .= arrayToString(modifierKeys, " || ", "", 0) . ")" . descriptionSuffix
  code .= " ---------------------"

  for i, k in modifierKeys {
    code .= (i == modifierKeys.minIndex()) ? "`n#If " : " || "
    code .= "GetKeyState(""" . k . """, ""P"")"
  }
  code .= "`n"
  return code
}  

; Builds the hotkey definition code, e.g.
; | pk & fk1:: tk1
; | pk & fk2:: tk2
; if prefix == "pk & ", fromKeys == [ "fk1", "fk2" ] and toKeys == [ "tk1", "tk2" ]
; prefix can be empty; otherwise it must include the " & "
hotkeysCodeImplMapping(prefix, fromKeys, toKeys) {
  local i, k, kt, code := ""
  log(A_ThisFunc, A_ThisFunc . "(" . prefix . ", " . arrayToString(fromKeys) . ", " . arrayToString(toKeys) . ")")
  if (fromKeys.length() != toKeys.length()) {
    throw Exception("key count mismatch: " . fromKeys.length() . " / " . toKeys.length() . " (" . arrayToString(fromKeys) . ") / (" . arrayToString(toKeys) . ")")
  }

  for i, k in fromKeys {
    kt := toKeys[i]
    if (neq(kt, "---")) {
      kt := eq(aliases[kt], "") ? kt : aliases[kt]
      if (eq(prefix, "") && eq(k, kt)) { ; comment out identity mapping
        code .= ";;"
      }
      code .= prefix . k . "::`t" . kt . "`n"
    }
  }
;  log(A_ThisFunc, code)
  return code
}

filterByGroup(fromKeys, toKeys, selectedKeyGroup, keyToGroup) {
  local selectedFromKeys := [], selectedToKeys := [], i, k
  for i, k in fromKeys {
    if (eq(keyToGroup[k], selectedKeyGroup)) {
      selectedFromKeys.push(k)
      selectedToKeys.push(toKeys[i])
    }
  }
  log(A_ThisFunc, A_ThisFunc . "([" . arrayToString(fromKeys) . "], [" . arrayToString(toKeys) . "], " . selectedKeyGroup . ", ..) == [[" . arrayToString(selectedFromKeys) . "], [" . arrayToString(selectedToKeys) . "]]")
  return [ selectedFromKeys, selectedToKeys ]
}

log(arg0, arg1 = "") {
	local e, arg0nl
	if (neq(arg1, "")) {
		log(format("{:-22.22s}:{}", arg0, arg1))
	} else {
		try {
			arg0nl := arg0 . "`n"
			FileAppend, %arg0nl%, **
		} catch e {
			OutputDebug %arg0%
		}
	}
}

; compares values for strict (string) equality
; see https://autohotkey.com/board/topic/28987-compare-two-variables-as-strings/
eq(val1, vals*) {
	local i, val2
	for i, val2 in vals {
		if (("" . val1) != ("" . val2)) {
			return 0
		}
	}
	return 1
}
neq(val1, vals*) {
	return !eq(val1, vals*)
}

; Extracts substrings from a given string, discarding some characters
; (e.g. to extract keys from a pretty-printed keyboard layout).
; This function undoes the pretty-printing by
; 1. discarding all characters in ignoredChars;
; 2. splitting each param at separators (a string or array of strings, see standard function strSplit);
; 3. removing empty split results;
; 4. then joining the split results into one array.
; Example: unpretty("|", " _"
;   , "|_q_|_w_|_e_|_r_|"
;   , "|   |F10| d |")
; == ["q", "w", "e", "r", "F10", "d"]
unpretty(separators, ignoredChars, decoratedKeys*) {
	local results := []
	local decoratedKey, part
	for uu, decoratedKey in decoratedKeys {
		local parts := strSplit(decoratedKey, separators, ignoredChars)
		for uu, part in parts {
			if (neq(part, "")) {
				results.push(part)
			}
		}
	}
	log(A_ThisFunc, "unpretty(..) == " . arrayToString(results))	;?
	return results
}

toMap(keysArray, valuesArray) {
	local map := {}, i, k
	if (keysArray.length() != valuesArray.length()) {
		throw Exception("array length mismatch: " . keysArray.length() . " != " . valuesArray.length() . " " . arrayToString(keysArray) . " / " . arrayToString(valuesArray))
	}
	for i, k in keysArray {
		map[k] := valuesArray[i]
	}
	return map
}

mapToString(map, ignoreValue = "") {
	local k, v, s
	s := ""
	for k, v in map {
		if (neq(v, ignoreValue)) {
			s .= eq(s, "") ? "" : ", "
			s .= k . ":" . v
		}
	}
	return s
}

join(array, separator = ", ", elemToStringFunc = "") {
	return arrayToString(array, separator, elemToStringFunc, 0)
}

fileReplaceInPlace(fileName, startMarkRegex, endMarkRegex, newLines*) {
	local file := fileOpen(fileName, "rw-")
	if (!isObject(file)) {
		throw "failed to open file " . file . ": " . A_LastError
	}
	local section := 0 ; 0 / 1 / 2: before / in / after the section to replace
	local i, startPos, crlf, oldLines := [], linesAfter := []
	while (!file.atEOF) {
		local line := file.readLine()
		local chompedLine := regexReplace(line, "`r?`n$", "")
		crlf := regexMatch(line, "`r`n$")
;		outputDebug % "input line:`t" . line
		if (section == 0) {
			if (regexMatch(chompedLine, startMarkRegex)) {
				section := 1
				startPos := file.tell()
			}
		} else if (section == 1) {
			if (neq(endMarkRegex, "") && regexMatch(chompedLine, endMarkRegex)) {
				section := 2
				linesAfter.push(line)
			} else {
				oldLines.push(chompedLine)
			}
		} else {
			linesAfter.push(line)
		}
	}

	outputDebug % "old lines: " . arrayToString(oldLines)
	outputDebug % "new lines: " . arrayToString(newLines)
	local sectionChanged := neq(join(oldLines, "`n"), join(newLines, "`n"))
	if (section > 0 ; section to replace found
			&& sectionChanged) {
		; write replacement
		local eol := crlf ? "`r`n" : "`n"
		file.seek(startPos)
		for i, line in newLines {
			file.write(line)
			file.write(eol)
		}

		; Write remaining input lines after replacement;
		; The end marker is optional; if not found, replace until EOF.
		for i, line in linesAfter {
			file.write(line)
		}

		; truncate
		file.length := file.tell()
	}
	file.close()
	return section > 0 && sectionChanged
}

arrayToString(array, separator = ", ", elemToStringFunc = "", withIndex = 1) {
	local i, e, s
	s := ""
	for i, e in array {
		s .= eq(s, "") ? "" : separator
		if (withIndex) {
			s .= "[" . i . "] "
		}
		s .= (elemToStringFunc == "" ? e : elemToStringFunc.call(e))
	}
	return s
}

aliases := { "": ""
  , "#"	:	"send {#}"
  , "WheelL"	:	"send {WheelLeft}"
  , "WheelR"	:	"send {WheelRight}"
  , "WheelU"	:	"send {WheelUp}"
  , "WheelD"	:	"send {WheelDown}"
  , "WheelUm"	:	"send {WheelUp 8}"
  , "WheelDm"	:	"send {WheelDown 8}"
  , "Spc*"	:	"send {u+00a0}"
  , "-h"	:	"send {u+2010}"	; hyphen
  , "-*"	:	"send {u+2011}"	; non-breaking hyphen
  , "-f"	:	"send {u+2012}"	; figure dash
  , "-n"	:	"send {u+2013}"	; en dash
  , "-m"	:	"send {u+2014}"	; em dash
  , "-2m"	:	"send {u+2e3a}"	; two-em dash
  , "-3m"	:	"send {u+2e3b}"	; three-em dash
  , "%"	:	"send {``%}"
  , "/f"	:	"send {u+2044}" ; fraction slash
  , "/d"	:	"send {u+2215}" ; division slash
  , "FF"	:	"send {u+240c}"
  , "FS"	:	"send {u+241c}"
  , "GS"	:	"send {u+241d}"
  , "RS"	:	"send {u+241e}"
  , "US"	:	"send {u+241f}"
  , "("")"	:	"send {u+0308}"	; dead diaeresis
  , "(́́´)"	:	"send {u+0301}"	; dead acute
  , "(́́``)"	:	"send {u+0300}"	; dead grave
  , "(́́^)"	:	"send {u+0302}"	; dead circumflex
  , "(~)"	:	"send {u+0303}"	; dead tilde
  , "(́°)"	:	"send {u+030a}"	; dead circle
  , "(,)"	:	"send {u+0327}"	; dead cedille
  , "𝛑"	:	"send {u+03c0}"
  ;
  ; because single-line box drawing characters are used for pretty-printing,
  ; these aliases exist to denote those characters when mapping them
  , "╔·"	:	"┌"
  , "╦·"	:	"┬"
  , "╗·"	:	"┐"
  , "╠·"	:	"├"
  , "╬·"	:	"┼"
  , "╣·"	:	"┤"
  , "╚·"	:	"└"
  , "╩·"	:	"┴"
  , "╝·"	:	"┘"
  , "║·"	:	"│"
  , "═·"	:	"─"
  ;
  ; When the left side of a hotkey contains shift, the resulting char is not
  ; the right side, but the char that the keyboard layout assigns to shift +
  ; that char; these aliases counter that effect.
  , "<"	:	"send <"
  , "\"	:	"send \"
  , "0"	:	"send 0"
  , "1"	:	"send 1"
  , "2"	:	"send 2"
  , "3"	:	"send 3"
  , "4"	:	"send 4"
  , "5"	:	"send 5"
  , "6"	:	"send 6"
  , "7"	:	"send 7"
  , "8"	:	"send 8"
  , "9"	:	"send 9"
  ;
  , "":"" }

keys := unpretty(["║│", "║"], " ═╔╦╗╚╩╝╠╬╣╤╧"
,"╔═══════╗       ╔═══════╦═══════╦═══════╦═══════╗   ╔═══════╦╤══════╦═══════╦═══════╗   ╔═══════╦═══════╦═══════╦═══════╗"
,"║       ║       ║   F1  ║   F2  ║   F3  ║   F4  ║   ║   F5  ║│  F6  ║   F7  ║   F8  ║   ║   F9  ║  F10  ║  F11  ║  F12  ║"
,"╠═══════╬═══════╬═══════╬═══════╬═══════╬═══════╬═══╩═══╦╤══╩╧══╦═══╩═══╦═══╩═══╦═══╩═══╬═══════╬═══════╬═══════╩═══════╣"
,"║ sc029 ║   1   ║   2   ║   3   ║   4   ║   5   ║   6   ║│  7   ║   8   ║   9   ║   0   ║ sc00c ║ sc00d ║            BS ║"
,"╠═══════╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩╧══╦╤══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══════════╣"
,"║ Tab       ║   q   ║   w   ║   e   ║   r   ║   t   ║   z   ║│  u   ║   i   ║   o   ║   p   ║ sc01a ║ sc01b ║     Enter ║"
,"╠═══════════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩╧╦╤════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╦═════╩═╗         ║"
,"║ CapsLock    ║   a   ║   s   ║   d   ║   f   ║   g   ║   h   ║│  j   ║   k   ║   l   ║ sc027 ║ sc028 ║ sc02b ║         ║"
,"╠═════════╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦╤══╩╧══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══╦═══╩═══════╩═════════╣"
,"║ LShift  ║ sc056 ║   y   ║   x   ║   c   ║       ║       ║│      ║       ║ sc033 ║ sc034 ║ sc035 ║              RShift ║"
,"╚═════════╩═══════╩═══════╩═══════╩═══════╩═══════╩═══════╩╧══════╩═══════╩═══════╩═══════╩═══════╩═════════════════════╝")
keyToGroup := toMap(keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   L   │   L   │   L   │   L   │   │   L   ║   R   │   R   │   R   │   │   R   │   R   │   R   │   R   │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│   L   │   L   │   L   │   L   │   L   │   L   │   L   ║   R   │   R   │   R   │   R   │   R   │   R   │           R   │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│   -       │   -   │   L   │   L   │   L   │   L   │   L   ║   R   │   R   │   R   │   R   │   R   │   -   │       -   │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│   -         │   L   │   L   │   L   │   L   │   L   │   L   ║   R   │   R   │   R   │   R   │   R   │   -   │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│   -     │   L   │   L   │   L   │   -   │       │       ║       │       │   -   │   -   │   -   │                 -   │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
log("keyToGroup == " . mapToString(keyToGroup))

; === layers ===
NONE  := []
modSL := [ "v" ]
modSR := [ "m" ]
modS  := [ "v", "m" ]
modKL := [ "b" ]
modKR := [ "n" ]
modK  := [ "b", "n" ]
modN  := [ "sc039" ]
code := disableKeysCode(modKL, modKR, modN)

; base
code .= hotkeysCode(NONE,NONE,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   F1  │   F2  │   F3  │   F4  │   │   F5  ║   F6  │   F7  │   F8  │   │   F9  │  F10  │  F11  │  F12  │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│   ^   │   /   │   x   │   v   │   l   │   c   │   w   ║   k   │   h   │   g   │   f   │   q   │   ß   │   ≈           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│ LAlt      │ LCtrl │   u   │   i   │   a   │   e   │   o   ║   s   │   n   │   r   │   t   │   d   │ RCtrl │      LAlt │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│ CapsLock    │   -   │   ?   │ Space │   p   │   y   │   z   ║   b   │   m   │   ,   │   .   │   j   │  Tab  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│ LShift  │   -   │   +   │  ``   │   BS  │       │       ║       │       │ Enter │  Ins  │  Del  │                 Esc │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; normal shift
code .= hotkeysCodeG(modSR,NONE, modSL,NONE, modS,NONE,	keyToGroup, keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │  +F1  │  +F2  │  +F3  │  +F4  │   │  +F5  ║  +F6  │  +F7  │  +F8  │   │  +F9  │ +F10  │ +F11  │ +F12  │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│   °   │   \   │   X   │   V   │   L   │   C   │   W   ║   K   │   H   │   G   │   F   │   Q   │   ẞ   │  ---          │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│ +LAlt     │+LCtrl │   U   │   I   │   A   │   E   │   O   ║   S   │   N   │   R   │   T   │   D   │+RCtrl │     +LAlt │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   _   │   !   │+Space │   P   │   Y   │   Z   ║   B   │   M   │  +,   │   :   │   J   │ +Tab  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -h  │   *   │   ʼ   │  +BS  │       │       ║       │       │+Enter │ +Ins  │ +Del  │                 --- │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; same-hand shift
; │       │       │   x   │   v   │   l   │   c   │   w   ║   k   │   h   │   g   │   f   │   q   │   ß   │   <=          │
code .= hotkeysCodeG(modSL,NONE, modSR,NONE, modS,NONE,	keyToGroup, keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   α   │   β   │   γ   │   δ   │   │   ε   ║   ∩   │   ∪   │   ∖   │   │   ⊂   │   ⊃   │   ⊆   │   ⊇   │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│   “   │   „   │   ”   │  ---  │   '   │  ""   │  ---  ║  ---  │   {   │   }   │   ≈   │   +   │   -   │   Space       │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   ü   │   |   │   ä   │   /   │   ö   ║   *   │   (   │   )   │   ~   │   $   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   +   │   =   │+Space │   \   │   &   │  ---  ║   @   │   [   │   ]   │   …   │   §   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -f  │   ±   │  ---  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; double shift
code .= hotkeysCode(modSL,modSR,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   ♣   │   ♠   │   ♥   │   ♦   │   │   ¼   ║   ⅔   │   ½   │   ⅔   │   │   ¾   │  ---  │  ---  │  ---  │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  ---  │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  ---  │   #   │   ≙   │   ≉   │  ---  │   «   │   »           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   Ü   │   ∞   │   Ä   │  ---  │   Ö   ║  ---  │   <   │   >   │  ---  │   ÷   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   *   │   ≠   │  Spc* │   %   │  ---  │  ---  ║   •   │   ♫   │   ♬   │   ‥   │   ·   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -n  │   ✻   │  ---  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; normal kana
code .= hotkeysCodeG(modKR,NONE, modKL,NONE, modK,NONE,	keyToGroup, keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   1   │   2   │   3   │   4   │   │   5   ║   6   │   7   │   8   │   │   9   │   0   │   .   │   ,   │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  (^)  │  ---  │   ∃   │   ∀   │  ---  │   ¢   │   ¤   ║   +   │   7   │   8   │   9   │   +   │   -   │   Space       │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │   ∈   │  ---  │   €   │  ---  ║   -   │   4   │   5   │   6   │   .   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   ¬   │   ∧   │   ∨   │   £   │   ¥   │   ⌂   ║   0   │   1   │   2   │   3   │   ,   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -*  │   ×   │  (`)  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │               ---   │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; same-hand kana
; │       │       │   x   │   v   │   l   │   c   │   w   ║   k   │   h   │   g   │   f   │   q   │   ß   │   <=          │
code .= hotkeysCodeG(modKL,NONE, modKR,NONE, modK,NONE,	keyToGroup, keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   1   │   2   │   3   │   4   │   │   5   ║   6   │   7   │   8   │   │   9   │   0   │   .   │   ,   │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  ---  │  ---  │   ∄   │  ---  │   ╔·  │   ╦·  │   ╗·  ║   ✛   │   ↖   │   ↑   │   ↗   │   ⁉   │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   ═·  │   ╠·  │   ╬·  │   ╣·  │   ¦   ║  ---  │   ←   │   ↓   │   →   │   ∅   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   ║·  │   ╚·  │   ╩·  │   ╝·  │  ---  │  ---  ║   ↕   │   ↙   │   ↔   │   ↘   │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │   -m  │   ✕   │  ---  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; double kana
code .= hotkeysCode(modKL,modKR,	keys, unpretty(["│"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   FS  │   GS  │   RS  │   US  │   │   FF  │  ---  │  ---  │  ---  │   │  ---  │  ---  │  ---  │  ---  │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  ---  │  ---  │  ---  │  ---  │   ╔   │   ╦   │   ╗   │   ☩   │   ⇖   │   ⇑   │   ⇗   │   ⁈   │   ≪   │  ≫           │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │   ═   │   ╠   │   ╬   │   ╣   │   ‖   │  ---  │   ⇐   │   ⇓   │   ⇒   │   ⦰   │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │   ║   │   ╚   │   ╩   │   ╝   │  ---  │  ---  │   ⇕   │   ⇙   │   ⇔   │   ⇘   │  ---  │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  -2m  │   ⋆   │  ---  │  ---  │       │       │       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; shift+kana
;                     x       v       l       c       w       k       h       g       f       q       ß
code .= hotkeysCode(modS,modK,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │   ¹   │   ²   │   ³   │   ⁴   │   │   ⁵   ║   ⁶   │   ⁷   │   ⁸   │   │   ⁹   │   ⁰   │   ⁽   │   ⁾   │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  (°)  │   ⁿ   │   ✗   │  ---  │   ®   │   ©   │   ☐   ║   ☒   │   ☑   │   ≟   │  ---  │   ¿   │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │   ∉   │   ¶   │  ---  │  ---  ║   ∑   │   ≤   │   ≥   │   ™   │   /d  │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │   𝛑   │  ---  │  ---  ║   /f  │   µ   │  (,)  │  ("") │   ✓   │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  -3m  │   ★   │  ---  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))
; navigation
code .= hotkeysCode(modN,NONE,	keys, unpretty(["│", "║"], " ─┌┬┐└┴┘├┼┤╓╥╖╙╨╜╟╫╢"
,"┌───────┐       ┌───────┬───────┬───────┬───────┐   ┌───────╥───────┬───────┬───────┐   ┌───────┬───────┬───────┬───────┐"
,"│       │       │  #1   │  #2   │  #3   │  #4   │   │  #5   ║  #6   │  #7   │  #8   │   │  #9   │  #0   │  ---  │  ---  │"
,"├───────┼───────┼───────┼───────┼───────┼───────┼───┴───╥───╨───┬───┴───┬───┴───┬───┴───┼───────┼───────┼───────┴───────┤"
,"│  ---  │  ---  │  ---  │  ---  │WheelUm│ WheelU│WheelDm║ ^Home │  Home │   Up  │  End  │ ^End  │  ---  │           --- │"
,"├───────┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───╨───╥───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───────────┤"
,"│  ---      │  ---  │  ---  │ WheelL│ WheelD│ WheelR│  ---  ║ +Ins  │  Left │  Down │ Right │  PgUp │  ---  │       --- │"
,"├───────────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────╨─╥─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┬─────┴─┐         │"
,"│  ---        │  ---  │  ---  │  ---  │  ---  │  ---  │  ---  ║  Ins  │ ^Left │  Del  │^Right │  PgDn │  ---  │         │"
,"├─────────┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───╥───╨───┬───┴───┬───┴───┬───┴───┬───┴───┬───┴───────┴─────────┤"
,"│  ---    │  ---  │  ---  │  ---  │  ---  │       │       ║       │       │  ---  │  ---  │  ---  │                ---  │"
,"└─────────┴───────┴───────┴───────┴───────┴───────┴───────╨───────┴───────┴───────┴───────┴───────┴─────────────────────┘"))


log("---------------------------------- generated code ----------------------------------")
log(code)
log("---------------------------------- end of generated code ----------------------------------")
if (fileReplaceInPlace(A_ScriptFullPath . ".generated", ".*", "", code)) {
  fileAppend, edited %A_ScriptFullPath%.generated`, re-starting`n, *
  sleep 1000
  reload
}

; #InputLevel 0
#Include %A_ScriptFullPath%.generated
; #InputLevel 1

; === misc ===
NumLock & NumpadSub::	exitApp
NumLock & NumpadDiv::	suspend
NumLock & NumpadMult::	reload
NumLock::	NumLock
v::	RShift
m::	LShift

; === sub thumb row ===
; LCtrl::	Space
; LWin::	
; LAlt::	LShift
RAlt::	RShift
; AppsKey::	
; RWin::	
; RCtrl::	


; === navigation block, ... ===
; PrintScreen::	
; ScrollLock::	
; Pause::	
; 
; Ins::	
; Home::
; PgUp::	
; Del::	
; End::	
; PgDn::	
; 
; Up::	
; Left::	
; Down::	
; Right::	


; === number pad ===
; NumLock::	
; NumpadDiv::	
; NumpadMult::	
; NumpadHome::	
; NumpadUp::	
; NumpadPgUp::	
; 
; NumpadLeft::	
; NumpadClear::	
; NumpadRight::	
; NumpadEnd::	
; NumpadDown::	
; NumpadPgDn::	
; 
; NumpadIns::	
; NumpadDel::	
; NumpadEnter::	
; NumpadAdd::	
; NumpadSub::	

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: furqan, Spawnova, TheNaviator and 80 guests