Xswitcher Layoutkorrektur fir Linux: Schrëtt zwee

zënter virdrun Publikatioun (xswitcher op der Bühn "Beweis vum Konzept") krut zimlech vill konstruktiv Feedback (wat flott ass), Ech hunn weider meng Fräizäit verbruecht fir de Projet z'entwéckelen. Elo wëll ech e bësse vun Ärer ... Den zweete Schrëtt wäert net ganz üblech sinn: Propositioun / Diskussioun iwwer Konfiguratiounsdesign.

Xswitcher Layoutkorrektur fir Linux: Schrëtt zwee

Iergendwéi stellt sech eraus datt normal Programméierer et onheemlech langweileg fannen all dës Kontrollen opzestellen.

Fir net onbegrënnt ze sinn, dobannen ass e Beispill vu wat ech ze dinn hunn.
Insgesamt exzellent konzipéiert (a gutt ëmgesat) Apache Kafka & ZooKeeper.
- Konfiguratioun? Mee et ass langweileg! Domm xml (well et ass "aus der Këscht").
- Oh, wëllt Dir och en ACL? Mee et ass sou langweileg! Tap-blooper... Sou eppes.

Awer a menger Aarbecht ass et genau de Géigendeel. Riets (Och, bal ni déi éischte Kéier) de gebaute Modell erlaabt Iech weider einfach an natierlech weiderzemaachen (bal) en Diagramm zesummestellen.

Ech koum viru kuerzem op en Artikel iwwer Habré iwwer déi haart Aarbecht vun Datewëssenschaftler ...
Et stellt sech eraus datt dëse Moment fir si voll realiséiert ass. An a menger Praxis, wéi se soen, "Liicht Versioun". Multi-Volumen Modeller, erfuerene Programméierer mat OOP am prett, etc. - dëst erschéngt méi spéit wann / wann et ofhëlt. Awer den Designer muss iergendwou hei an elo ufänken.

Gitt zum Punkt. Ech hunn TOML als syntaktesch Basis geholl vun dësem Bierger.

Well hien (TOML) engersäits Mënsch-editable. Op der anerer Säit gëtt et 1: 1 an eng vun de méi heefegste Syntaxen iwwersat: XML, JSON, YAML.
Ausserdeem ass d'Implementatioun déi ech vun "github.com/BurntSushi/toml" benotzt hunn, obwuel net déi modernst (nach ëmmer 1.4 Syntax), ass syntaktesch kompatibel mat deemselwechten ("built-in") JSON.

Dat ass, wann Dir wëllt, kënnt Dir einfach soen "Gitt duerch de Bësch mat deem TOML vun Ärem, ech wëll XXX" an "patch" de Code mat nëmmen enger Zeil.

Also, wann Dir e puer Fënstere wëllt schreiwen fir xswitcher ze konfiguréieren (Ech sinn net sécher) Keng Problemer ginn erwaart "mat dëser verdammt Konfiguratioun vun Iech."

Fir all aner baséiert d'Syntax op "Schlëssel = Wäert" (a wuertwiertlech e puer méi komplizéiert Optiounen, wéi = [e puer, dat, Array]) Ech schätzen
intuitiv bequem.
Wat interessant ass, ass dat "gebrannt" ëm déiselwecht Zäit (ongeféier 2013). Nëmmen, am Géigesaz zu mir, ass den Auteur vum TOML op enger richteger Skala eragaang.

Dofir ass et elo méi einfach fir mech seng Ëmsetzung unzepassen fir mech selwer ze passen, an net vice versa.

Am Allgemengen huelen mir TOML (ganz ähnlech wéi déi al Windows INI). A mir hunn eng Konfiguratioun, an där mir beschreiwen, wéi een eng Serie vun Haken befestegt ofhängeg vum Set vun de leschte Scancodes vun der Tastatur. Drënner, Stéck fir Stéck, ass wat bis elo geschitt ass. An eng Erklärung firwat ech dës Manéier decidéiert hunn.

0. Basis Abstraktiounen

  • Scan Code Bezeechnungen. Eppes muss definitiv doriwwer gemaach ginn, well einfach digital Coden absolut net mënschlech liesbar sinn (dat sinn just ech loloswitcher).
    Ech hunn "ecodes.go" aus "golang-evdev" erausgerappt (ech war ze faul fir d'Originalquell ze kucken, obwuel den Auteur et zimlech kulturell uginn huet). Ech korrigéiert e bëssen (fir elo) eppes wat zimlech ängschtlech war. Wéi "LEFTBRACE" → "L_BRACE".
  • Zousätzlech huet hien d'Konzept vun "Staatsschlësselen" agefouert. Well déi regulär Grammatik benotzt erlaabt net laang Passagen. (Awer et erlaabt Iech mat minimalen Overhead ze kontrolléieren. Wann Dir nëmmen "direkt" Opnam benotzt.)
  • Et gëtt en agebaute "Deduplicator" vun deem wat gedréckt gëtt. Sou gëtt den Zoustand "Wiederhol"=2 geschriwwe ginn один Zäiten.

1. Schablounen Rubrik

[Templates] # "@name@" to simplify expressions
 # Words can consist of these chars (regex)
 "WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"

Aus wat besteet e Mënschsproochegt Wuert mat phonetescher Notatioun? (entweder eng Saach vu Graphemes aka "Hieroglyphen")? Eng Aart vu schrecklechen "Blat". Dofir stellen ech direkt d'Konzept vun "Schabloun" vir.

2. Wat maache wann eppes geklickt gëtt (en anere Scancode ass ukomm)

[ActionKeys]
 # Collect key and do the test for command sequence
 # !!! Repeat codes (code=2) must be collected once per key!
 Add = ["1..0", "=", "BS", "Q..]", "L_CTRL..CAPS", "N_LOCK", "S_LOCK",
        "KP7..KPDOT", "R_CTRL", "KPSLASH", "R_ALT", "KPEQUAL..PAUSE",
        "KPCOMMA", "L_META..COMPOSE", "KPLEFTPAREN", "KPRIGHTPAREN"]

 # Drop all collected keys, including this.  This is default action.
 Drop = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
 # Store extra map for these keys, when any is in "down" state.
 # State is checked via "OFF:"|"ON:" conditions in action.
 # (Also, state of these keys must persist between buffer drops.)
 # ??? How to deal with CAPS and "LOCK"-keys ???
 StateKeys = ["L_CTRL", "L_SHIFT", "L_ALT", "L_META", "CAPS", "N_LOCK", "S_LOCK",
              "R_CTRL", "R_SHIFT", "R_ALT", "R_META"]

 # Test only, but don't collect.
 # E.g., I use F12 instead of BREAK on dumb laptops whith shitty keyboards (new ThinkPads)
 Test = ["F1..F10", "ZENKAKUHANKAKU", "102ND", "F11", "F12",
          "RO..KPJPCOMMA", "SYSRQ", "SCALE", "HANGEUL..YEN",
          "STOP..SCROLLDOWN", "NEW..MAX"]

Et sinn am Ganzen 768 Coden. (Awer "just am Fall" hunn ech "Iwwerraschungen" an den xswitcher Code agefouert).
Bannen hunn ech beschriwwen d'Array ze fëllen mat Linken op Funktiounen "wat ze maachen". Am Golang ass dëst (op eemol) Et huet sech als praktesch an offensichtlech erausgestallt.

  • Ech plangen "Drop" op e Minimum op dëser Plaz ze reduzéieren. Zugonschte vun enger méi flexibeler Veraarbechtung (ech weisen et hei ënnen).

3. Dësch mat Fënster Klassen

# Some behaviour can depend on application currently doing the input.
[[WindowClasses]]
 # VNC, VirtualBox, qemu etc. emulates there input independently, so never intercept.
 # With the exception of some stupid VNC clients, which does high-level (layout-based) keyboard input.
 Regex = "^VirtualBox"
 Actions = "" # Do nothing while focus stays in VirtualBox

[[WindowClasses]]
 Regex = "^konsole"
 # In general, mouse clicks leads to unpredictable (at the low-level where xswitcher resides) cursor jumps.
 # So, it's good choise to drop all buffers after click.
 # But some windows, e.g. terminals, can stay out of this problem.
 MouseClickDrops = 0
 Actions = "Actions"

[[WindowClasses]] # Default behaviour: no Regex (or wildcard like ".")
 MouseClickDrops = 1
 Actions = "Actions"

D'Reihen vum Dësch sinn an duebele Quadratklammern mat hirem Numm. Et hätt direkt net méi einfach gewiescht. Ofhängeg vun der aktueller aktiver Fënster, kënnt Dir déi folgend Optiounen auswielen:

  • Ären eegene Set vu "Hot Keys" "Aktiounen = ...". Wann net / eidel, näischt maachen.
  • Wiesselt "MouseClickDrops" - wat maache wann e Mausklick festgestallt gëtt. Zënter dem Punkt wou xswitcher ageschalt ass, ginn et keng Detailer iwwer "wou se klickt", setzen mir de Puffer als Standard zréck. Awer an Terminaler (zum Beispill) musst Dir dëst net maachen (normalerweis).

4. Eng (oder e puer) Klicksequenzen ausléisen een oder aneren Haken

# action = [ regex1, regex2, ... ]
# "CLEAN" state: all keys are released
[Actions]
# Inverse regex is hard to understand, so extract negation to external condition.
# Expresions will be checked in direct order, one-by-one. Condition succceds when ALL results are True.
 # Maximum key sequence length, extra keys will be dropped. More length - more CPU.
 SeqLength = 8
 # Drop word buffer and start collecting new one
 NewWord = [ "OFF:(CTRL|ALT|META)  SEQ:(((BACK)?SPACE|[LR]_SHIFT):[01],)*(@WORD@:1)", # "@WORD@:0" then collects the char
             "SEQ:(@WORD@:2,@WORD@:0)", # Drop repeated char at all: unlikely it needs correction
             "SEQ:((KP)?MINUS|(KP)?ENTER|ESC|TAB)" ] # Be more flexible: chars line "-" can start new word, but must not completelly invalidate buffer!
 # Drop all buffers
 NewSentence = [ "SEQ:(ENTER:0)" ]

 # Single char must be deleted by single BS, so there is need in compose sequence detector.
 Compose = [ "OFF:(CTRL|L_ALT|META|SHIFT)  SEQ:(R_ALT:1,(R_ALT:2,)?(,@WORD@:1,@WORD@:0){2},R_ALT:0)" ]

 "Action.RetypeWord" = [ "OFF:(CTRL|ALT|META|SHIFT)  SEQ:(PAUSE:0)" ]
 "Action.CyclicSwitch" = [ "OFF:(R_CTRL|ALT|META|SHIFT)  SEQ:(L_CTRL:1,L_CTRL:0)" ] # Single short LEFT CONTROL
 "Action.Respawn" = [ "OFF:(CTRL|ALT|META|SHIFT)  SEQ:(S_LOCK:2,S_LOCK:0)" ] # Long-pressed SCROLL LOCK

 "Action.Layout0" = [ "OFF:(CTRL|ALT|META|R_SHIFT)  SEQ:(L_SHIFT:1,L_SHIFT:0)" ] # Single short LEFT SHIFT
 "Action.Layout1" = [ "OFF:(CTRL|ALT|META|L_SHIFT)  SEQ:(R_SHIFT:1,R_SHIFT:0)" ] # Single short RIGHT SHIFT

 "Action.Hook1" = [ "OFF:(CTRL|R_ALT|META|SHIFT)  SEQ:(L_ALT:1,L_ALT:0)" ]

Haken sinn an zwou Zorte opgedeelt. Built-in, mat "schwätzen" Nimm (NewWord, NewSentence, Compose) a programméierbar.

Programméierbar Nimm fänken mat "Action" un. Well TOML v1.4, Nimm mat Punkte mussen an Zitaten sinn.

All Sektioun soll ënnendrënner beschriwwe ginn mam selwechten Numm.

Fir d'Leit net mat "plakeg" Regelméissegen (aus Erfahrung, hir ze schreiwenvläicht een vun zéng professionell), Ech implementéieren direkt zousätzlech Syntax.

  • "OFF:" (oder "ON:") virun regexp (reegelméissegen Ausdrock) verlaangen, datt déi folgend Knäppercher fräiginn (oder gedréckt).
    Als nächst wäert ech en "ongerechten" regulären Ausdrock maachen. Mat getrennten Iwwerpréiwung vu Stécker tëscht Päifen "|". Fir d'Zuel vun de Rekorder wéi "[LR]_SHIFT" ze reduzéieren (wou dat kloer net néideg ass).
  • "SEQ:" Wann déi viregt Konditioun erfëllt ass (oder fehlend), da kontrolléiere mir géint en "normalen" regulären Ausdrock. Fir Detailer schécken ech direkt un ^W d'"regexp" Bibliothéik. Well ech nach ëmmer net beméit hunn de Grad vun der Kompatibilitéit mat mengem Liiblingspcre ("perl kompatibel") erauszefannen.
  • Den Ausdrock ass an der Form geschriwwen "BUTTON_1: CODE1, BUTTON_2: CODE2" etc., an der Uerdnung an där d'Scannercodes opgeholl ginn.
  • De Scheck gëtt ëmmer um Enn vun der Sequenz "snugged"., also ass et net néideg "$" un de Schwanz ze addéieren.
  • All Kontrollen an enger Zeil ginn een nom aneren duerchgefouert a sinn duerch "ech" kombinéiert. Awer well de Wäert als Array beschriwwe gëtt, kënnt Dir en alternativen Scheck nom Komma schreiwen. Wann dëst aus irgendege Grënn gebraucht gëtt.
  • Wäert "SeqLength = 8" limitéiert d'Gréisst vum Puffer géint deen all Kontrollen duerchgefouert ginn. Well Ech hunn (bis elo) ni endlos Ressourcen a mengem Liewen begéint.

5. Astellung vun den Haken an der viregter Sektioun beschriwwen

# Action is the array, so actions could be chained (m.b., infinitely... Have I to check this?).
# For each action type, extra named parameters could be collected. Invalid parameters will be ignored(?).
[Action.RetypeWord] # Switch layout, drop last word and type it again
 Action = [ "Action.CyclicSwitch", "RetypeWord" ] # Call Switch() between layouts tuned below, then RetypeWord()

[Action.CyclicSwitch] # Cyclic layout switching
 Action = [ "Switch" ] # Internal layout switcher func
 Layouts = [0, 1]

[Action.Layout0] # Direct layout selection
 Action = [ "Layout" ] # Internal layout selection func
 Layout = 0

[Action.Layout1] # Direct layout selection
 Action = [ "Layout" ] # Internal layout selection func
 Layout = 1

[Action.Respawn] # Completely respawn xswitcher. Reload config as well
 Action = [ "Respawn" ]

[Action.Hook1] # Run external commands
  Action = [ "Exec" ]
  Exec = "/path/to/exec -a -b --key_x"
  Wait = 1
  SendBuffer = "Word" # External hook can process collected buffer by it's own means.

Den Haapt Saach hei ass "Aktioun = [Array]". Ähnlech wéi déi vireg Sektioun gëtt et e limitéierten Set vun agebaute Aktiounen. An d'Méiglechkeet vum Docking ass net am Prinzip limitéiert (Schreift "Action.XXX" a sidd net ze faul fir eng aner Rubrik dofir ze schreiwen).
Besonnesch d'Wiedertyping vun engem Wuert am korrigéierte Layout ass an zwee Deeler opgedeelt: "Änneren de Layout wéi do spezifizéiert" и "retype" ("RetypeWord").

Déi reschtlech Parameter ginn an de "Wörterbuch" geschriwwe ("Kaart" op Golang) fir eng ginn Aktioun, hir Lëscht hänkt op wat an "Action" geschriwwen ass.

Verschidde verschidden Aktiounen kënnen an engem Koup beschriwwe ginn (Sektiounen). Oder Dir kënnt et auserneen zéien. Wéi ech uewen gewisen.

Ech hunn direkt d'Aktioun "Exec" gesat fir den externen Skript auszeféieren. Mat der Optioun fir den opgeholle Puffer an stdin ze drécken.

  • "Waart = 1" - waart bis de lafende Prozess fäerdeg ass.
  • Wahrscheinlech, "op de Koup" wëllt Dir zousätzlech Leit an d'Ëmwelt setzen. Informatiounen wéi den Numm vun der Fënsterklass, aus där se ofgefaangen ass.
    "Wëllt Dir Ären Handler verbannen? Dëst ass wou Dir musst goen."

Pff (ausgehal). Et schéngt wéi wann ech näischt vergiess hunn.

Oops! Jo, ech hunn net vergiess ...
Wou ass d'Startkonfiguratioun? Am schwéier Code? Esou:

[ScanDevices]
 # Must exist on start. Self-respawn in case it is younger then 30s
 Test = "/dev/input/event0"
 Respawn = 30
 # Search mask
 Search = "/dev/input/event*"
 # In my thinkPads there are such a pseudo-keyboards whith tons of unnecessary events
 Bypass = "(?i)Video|Camera" # "(?i)" obviously differs from "classic" pcre's.

Wou hunn ech vergiess/e Feeler gemaach? (kee Wee ouni dëst), Ech hoffe wierklech datt opmierksam Lieser net ze faul sinn fir d'Nues ze stiechen.

Vill Gléck!

Source: will.com

Setzt e Commentaire