Last active
July 31, 2021 10:21
-
-
Save honboubao/9db2827c273ac0d4a296ef1651f99c78 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#SingleInstance Force | |
#InstallKeybdHook | |
TAPPING_TERM := 200 | |
keyboard := ["SC029","SC002","SC003","SC004","SC005","SC006","SC007","SC008","SC009","SC00A","SC00B","SC00C","SC00D","SC00E" | |
,"SC00F","SC010","SC011","SC012","SC013","SC014","SC015","SC016","SC017","SC018","SC019","SC01A","SC01B","SC01C" | |
,"SC03A","SC01E","SC01F","SC020","SC021","SC022","SC023","SC024","SC025","SC026","SC027","SC028","SC02B" | |
,"SC02A","SC02C","SC02D","SC02E","SC02F","SC030","SC031","SC032","SC033","SC034","SC035","SC056","SC136" | |
,"SC01D","SC15B","SC038","SC039","SC138","SC11D"] | |
; ^ 1 2 3 4 5 6 7 8 9 0 ß ´ bspc | |
; tab q w e r t z u i o p ü + enter | |
; caps a s d f g h j k l ö ä # | |
; lshift y x c v b n m , . - < rshift | |
; lctrl lwin lalt space ralt rctrl | |
scIndexes := {} | |
shiftMod := { .: "ß", Backspace: "Del", BS: "Del", Enter: "Esc"} | |
modTaps := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "+" , 3 , 4 , 2 , "" , "" , 2 , 4 , 3 , "+" , "" , "" | |
, "" , "^" , "#" , "!" , "" , "" , "" , "" , "!" , "#" , "^" , "^" , "" | |
, "" , "" , "" , 5 , "" , "" ] | |
layers := [] | |
layers[1] := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "" , "" , "ü" , "" , "ä" , "" , "" | |
, "" , "" , "" , "" , "" , "" ] | |
layers[2] := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "0" , "" , "" , "" | |
, "" , "µ" , "²" , "³" , "=" , "/" , "-" , "4" , "5" , "6" , "," , "" , "" | |
, "" , "" , "" , "" , "" , "*" , "+" , "1" , "2" , "3" , "." , "" , "" | |
, "" , "" , "" , "Tab" , "0" , "" ] | |
layers[3] := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "^" , "$" , "#" , "%" , "&" , "|" , "{" , "}" , "[" , "]" , "" , "" , "" | |
, "" , "°" , "´" , "'" , """" , "``" , "?" , "(" , ")" , "<" , ">" , "" , "" | |
, "" , "§" , "~" , "\" , "€" , "@" , "!" , ";" , "," , ":" , "_" , "" , "" | |
, "" , "" , "" , "BS" , "" , "" ] | |
layers[4] := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "F1" , "F2" , "F3" , "F4" , "F5" , "F6" , "F7" , "F8" , "F9" , "F10" , "" , "" | |
, "" , "" , "" , "","Click X1", "F11", "F12" ,"Click X2", "" , "" , "" , "" , "" | |
, "" , "" , "" ,"Enter", "" , "" ] | |
layers[5] := [ "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" | |
, "" , "" , "Home", "Up" , "End" , "PgUp", "^c" , "Home", "Up" , "End" , "" , "" , "" , "" | |
, "" , "" , "Left", "Down","Right", "PgDn", "^v" , "Left", "Down","Right", "" , "" , "" | |
, "" , "" , "" , "" , "" , "" , "^x" , "^z" , "" , "" , "" , "" , "" | |
, "" , "" , "" , "" , "" , "" ] | |
modTapQueue := [] | |
getCurrentLayer() { | |
global modTapQueue, layers | |
currentLayer := 1 | |
for i, state in modTapQueue | |
if (state.hold and layers[state.targetLayer]) | |
currentLayer := state.targetLayer | |
return currentLayer | |
} | |
getKey(sc) { | |
global scIndexes, layers | |
currentLayer := getCurrentLayer() | |
if (layers[currentLayer][scIndexes[sc]]) { | |
return layers[currentLayer][scIndexes[sc]] | |
} | |
if (currentLayer = 1) { | |
return GetKeyName(sc) | |
} | |
return "" | |
} | |
getMods() { | |
return (GetKeyState("Ctrl") ? "^" : "") . (GetKeyState("Alt") ? "!" : "") | |
} | |
class SelfDeletingTimer { | |
__New(period, fn, prms*) { | |
this.fn := IsObject(fn) ? fn : Func(fn) | |
this.prms := prms | |
SetTimer % this, % period | |
} | |
Call() { | |
this.fn.Call(this.prms*) | |
this.Stop() | |
} | |
Stop() { | |
SetTimer % this, Delete | |
} | |
} | |
hasUnresolvedModTap() { | |
global modTapQueue | |
for i, state in modTapQueue | |
if (state.targetLayer and not state.hold) | |
return true | |
} | |
getModTapStateIndex(sc) { | |
global modTapQueue | |
for i, state in modTapQueue | |
if (state.sc = sc) | |
return i | |
} | |
getModTapState(sc) { | |
global modTapQueue | |
return modTapQueue[getModTapStateIndex(sc)] | |
} | |
removeModTapState(sc) { | |
global modTapQueue | |
loop { | |
i := getModTapStateIndex(sc) | |
if (not i) | |
break | |
modTapQueue.removeAt(i) | |
} | |
} | |
modKeyName(mod) { | |
switch mod { | |
case "^": | |
return "Control" | |
case "!": | |
return "Alt" | |
case "+": | |
return "Shift" | |
case "#": | |
return "Win" | |
} | |
} | |
sendModKey(mod, action) { | |
modKey := modKeyName(mod) | |
if (modKey) | |
Send {%modKey% %action%} | |
} | |
holdModTap(sc) { | |
state := getModTapState(sc) | |
if (state and state.targetLayer and not state.hold) { | |
state.hold := true | |
sendModKey(state.targetLayer, "down") | |
} | |
} | |
startModTap(sc, modTapTarget) { | |
global modTapQueue, TAPPING_TERM | |
if (getModTapStateIndex(sc)) { | |
return | |
} | |
state := { sc: sc, timer: new SelfDeletingTimer(TAPPING_TERM, "holdModTap", sc), targetLayer: modTapTarget, tick: A_TickCount } | |
modTapQueue.push(state) | |
} | |
finishModTap(sc) { | |
state := getModTapState(sc) | |
state.timer.stop() | |
if (state.hold) { | |
sendModKey(state.targetLayer, "up") | |
} else { | |
resolveModTapQueue(sc) | |
pressKey(sc) | |
} | |
removeModTapState(sc) | |
} | |
resolveModTapQueue(sc) { | |
global modTapQueue | |
for i, state in modTapQueue { | |
if (state.sc = sc) | |
return | |
if (state.targetLayer and not state.hold) | |
holdModTap(state.sc) | |
else if (not state.targetLayer and not state.triggered) { | |
state.triggered := true | |
sendKey(state.sc, "down") | |
} | |
} | |
} | |
startModTapInterrupt(sc) { | |
global modTapQueue | |
if (getModTapStateIndex(sc)) { | |
return | |
} | |
state := { sc: sc } | |
modTapQueue.push(state) | |
} | |
finishModTapInterrupt(sc) { | |
global modTapQueue | |
state := getModTapState(sc) | |
if (state) { | |
resolveModTapQueue(sc) | |
if (not state.triggered) { | |
state.triggered := true | |
sendKey(state.sc, "down") | |
} | |
} | |
sendKey(sc, "up") | |
removeModTapState(sc) | |
} | |
pressKey(sc) { | |
sendKey(sc, "") | |
} | |
sendKey(sc, action) { | |
global shiftMod | |
key := getKey(sc) | |
if (GetKeyState("Shift") and shiftMod[key]) { | |
mods := getMods() | |
replacementKey := shiftMod[key] | |
Send {Shift up}%mods%{%replacementKey% %action%}{Shift down} | |
return | |
} | |
if (StrLen(key) >= 1) { | |
mods := "" | |
while (StrLen(key) > 1 and RegExMatch(key, "^[#!^+]")) { | |
mods .= SubStr(key, 1, 1) | |
key := SubStr(key, 2) | |
} | |
Send {Blind}%mods%{%key% %action%} | |
} | |
} | |
onKey(sc, action) { | |
global scIndexes, modTaps | |
modTapTarget := modTaps[scIndexes[sc]] | |
if (modTapTarget) { | |
if (action = "down") { | |
startModTap(sc, modTapTarget) | |
} else { | |
finishModTap(sc) | |
} | |
return | |
} | |
if (action = "down" and hasUnresolvedModTap()) { | |
startModTapInterrupt(sc) | |
return | |
} | |
if (action = "up" and getModTapState(sc)) { | |
finishModTapInterrupt(sc) | |
return | |
} | |
sendKey(sc, action) | |
} | |
For k, sc in keyboard { | |
scIndexes[sc] := k | |
dnfn := Func("onKey").Bind(sc, "down") | |
upfn := Func("onKey").Bind(sc, "up") | |
Hotkey, % "*" . sc, % dnfn | |
Hotkey, % "*" . sc . " up", % upfn | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment