Xswitcher elrendezés korrektor Linuxhoz: második lépés

Mint korábbi kiadvány (xswitcher a „proof of concept” szakaszban) elég sok építő visszajelzést kapott (ami szép), szabadidőmet továbbra is a projekt fejlesztésével töltöttem. Most el akarok költeni egy kicsit a... A második lépés nem lesz egészen megszokott: javaslat/megbeszélés a konfigurációtervezésről.

Xswitcher elrendezés korrektor Linuxhoz: második lépés

Valahogy úgy alakul, hogy a normál programozók hihetetlenül unalmasnak találják ezeket a vezérlőket beállítani.

Hogy ne legyek alaptalan, belül egy példa, hogy mivel foglalkozom.
Összességében kiválóan kidolgozott (és jól megvalósított) Apache Kafka & ZooKeeper.
- Konfiguráció? De unalmas! Dumb xml (mert „kivételes”).
- Ó, te is akarsz ACL-t? De olyan unalmas! Tap-blooper... Valami ilyesmi.

De az én munkámban ennek pont az ellenkezője. Jobb (sajnos szinte soha nem először) a felépített modell lehetővé teszi a továbblépést egyszerűen és természetesen (Majdnem) összeállít egy diagramot.

Nemrég találkoztam egy cikkel Habréról az adattudósok kemény munkájáról...
Kiderül, hogy ez a pillanat teljesen megvalósult számukra. És az én gyakorlatomban, ahogy mondják, „könnyű változat”. Többkötetes modellek, tapasztalt programozók készenlétben OOP-val stb. — ez mind később megjelenik, amikor/ha felszáll. De a tervezőnek valahol itt és most el kell kezdenie.

Térjen a tárgyra. A TOML-t vettem szintaktikai alapnak ettől a polgártól.

Mert ő (TOML) egyrészt ember által szerkeszthető. Másrészt 1:1 arányban lefordítják bármelyik általánosabb szintaxisra: XML, JSON, YAML.
Ráadásul a „github.com/BurntSushi/toml” oldalról használt implementáció, bár nem a legdivatosabb (még mindig 1.4-es szintaxis), szintaktikailag kompatibilis ugyanazzal a („beépített”) JSON-nal.

Vagyis, ha akarod, egyszerűen azt mondhatod, hogy „menj át az erdőn azzal a TOML-lel, XXX-et akarok”, és egyetlen sorral „patch” a kódot.

Így, ha néhány ablakot szeretne írni az xswitcher konfigurálásához (Nem vagyok benne biztos) Nem várható semmi probléma ezzel az átkozott konfigurációddal.

Az összes többi esetében a szintaxis alapja a „kulcs = érték” (és szó szerint néhány bonyolultabb lehetőség, mint például = [some, that, array]) Feltételezem
intuitíven kényelmes.
Az az érdekes "megégett" nagyjából ugyanebben az időben (2013 körül). Csak velem ellentétben a TOML szerzője megfelelő léptékben ment bele.

Ezért most könnyebben magamhoz igazítom a megvalósítását, és nem fordítva.

Általában a TOML-t használjuk (nagyon hasonló a régi Windows INI-hez). És van egy konfigurációnk, amelyben leírjuk, hogyan kell rögzíteni egy sor horgot a billentyűzet legújabb szkennelési kódjainak készletétől függően. Alább, darabonként, az eddig történtek. És magyarázat arra, hogy miért döntöttem így.

0. Alapvető absztrakciók

  • A kódok beolvasása. Valamit mindenképpen tenni kell ez ellen, mert egyszerűen a digitális kódok egyáltalán nem olvashatók az ember számára (ez csak én vagyok loloswitcher).
    Kiráztam az „ecodes.go”-t a „golang-evdev”-ből (lusta voltam ránézni az eredeti forrásra, bár a szerző elég kulturáltan jelezte). Kijavítottam egy kicsit (egyelőre) valamit, ami meglehetősen félelmetes volt. Például a „BAL BRACE” → „L_BRACE”.
  • Ezenkívül bevezette az „állapotkulcsok” fogalmát. Mivel a használt rendes nyelvtan nem teszi lehetővé a hosszú szövegrészeket. (De lehetővé teszi az ellenőrzést minimális rezsivel. Ha csak „közvetlen” felvételt használ.)
  • Lesz egy beépített „deduplikátor” a lenyomottaknak. Így a "repeat"=2 állapot kerül felírásra egy időben.

1. Sablonok szakasz

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

Miből áll egy emberi nyelv fonetikus jelölésű szó? (akár grafémák, más néven „hieroglifák” kérdése)? Valamiféle szörnyű „lap”. Ezért azonnal bevezetem a „sablon” fogalmát.

2. Mi a teendő, ha valamire kattintottak (másik szkennelési kód érkezett)

[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"]

Összesen 768 kód van. (De „csak abban az esetben” beszúrtam a „meglepetések” elkapását az xswitcher kódjába).
Belül leírtam a tömb kitöltését a „mit csináljunk” függvényekre mutató hivatkozásokkal. A golangban ez van (hirtelen) Kényelmesnek és kézenfekvőnek bizonyult.

  • Azt tervezem, hogy ezen a helyen minimálisra csökkentem a „Drop”-ot. A rugalmasabb feldolgozás mellett (alább megmutatom).

3. Táblázat ablakosztályokkal

# 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"

A táblázat sorai dupla szögletes zárójelben szerepelnek a nevével együtt. Ennél egyszerűbb nem is lehetett volna rögtön. Az aktuálisan aktív ablaktól függően a következő lehetőségek közül választhat:

  • A saját „gyorsbillentyűk” készlete „Műveletek = …”. Ha nem/üres, ne csinálj semmit.
  • Kapcsolja be a „MouseClickDrops” elemet – mi a teendő, ha egérkattintást észlel. Mivel azon a ponton, ahol az xswitcher be van kapcsolva, nincsenek részletek arról, hogy „hova kattintanak”, alapértelmezés szerint visszaállítjuk a puffert. De terminálokban (például) ezt nem kell megtennie (általában).

4. Egy (vagy több) kattintássorozat kivált egy vagy másik horgot

# 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)" ]

A horgokat két típusra osztják. Beépített, „beszélő” nevekkel (NewWord, NewSentence, Compose) és programozható.

A programozható nevek „Action” szóval kezdődnek. Mert TOML v1.4, a pontokkal ellátott neveket idézőjelbe kell tenni.

Az alábbiakban minden szakaszt ismertetni kell ugyanazzal a névvel.

Annak érdekében, hogy ne fújjuk fel az embereket a „meztelen” törzsvendégekkel (tapasztalatból, az övék írnitalán tízből egy szakemberek), azonnal további szintaxist alkalmazok.

  • "OFF:" (vagy "ON:") a reguláris kifejezés (reguláris kifejezés) előtt a következő gombok felengedését (vagy megnyomását) kell megkövetelni.
    Ezután egy „tisztességtelen” reguláris kifejezést fogok készíteni. A "|" csövek közötti darabok külön ellenőrzésével. Az olyan rekordok számának csökkentése érdekében, mint a „[LR]_SHIFT” (ahol ez nyilvánvalóan nem szükséges).
  • "SEQ:" Ha az előző feltétel teljesül (vagy hiányzik), akkor egy „normál” reguláris kifejezéshez hasonlítjuk. A részletekért azonnal elküldöm ^W-nek a „regexp” könyvtárat. Mert még mindig nem foglalkoztam azzal, hogy kiderítsem a kompatibilitás mértékét a kedvenc pcre-mmel ("perl-kompatibilis").
  • A kifejezés a formába van írva "BUTTON_1: CODE1, BUTTON_2: CODE2" stb., a szkennelési kódok beérkezésének sorrendjében.
  • A csekk mindig a szekvencia végére „bújik”., így nem kell „$”-t hozzáadni a farokhoz.
  • Az egyik sorban lévő összes ellenőrzést egymás után hajtják végre és az „én” egyesíti őket. De mivel az érték tömbként van leírva, a vessző után írhat egy alternatív ellenőrzést. Ha erre valamilyen okból szükség van.
  • Érték "SeqLength = 8" korlátozza annak a puffernek a méretét, amelyhez képest minden ellenőrzést végrehajtanak. Mert (Eddig) soha életemben nem találkoztam végtelen erőforrásokkal.

5. Az előző részben leírt horgok beállítása

# 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.

A fő dolog itt az "Action = [tömb]". Az előző szakaszhoz hasonlóan korlátozott számú beépített művelet található. A dokkolás lehetősége pedig elvileg nincs korlátozva (írja az „Action.XXX” címet, és ne legyen túl lusta, hogy másik részt írjon neki).
Különösen a javított elrendezésben lévő szó újragépelése két részre oszlik: „módosítsa az elrendezést az ott megadottak szerint” и "retype" ("RetypeWord").

A többi paraméter a „szótárba” íródik ("térkép" golang nyelven) adott műveletnél ezek listája attól függ, hogy mi van az „Akció” alatt.

Egy kupacban több különböző művelet is leírható (szakaszok). Vagy szét is húzhatod. Ahogy fentebb mutattam.

Azonnal beállítottam az „Exec” műveletet a külső szkript végrehajtására. Lehetőség van a rögzített puffer stdin-be tolására.

  • „Várjon = 1” – várja meg, amíg a futó folyamat befejeződik.
  • Valószínűleg „a halomig” további embereket akar majd a környezetbe helyezni. információk, például annak az ablakosztálynak a neve, amelyből elfogták.
    „Szeretnéd csatlakoztatni a kezelőt? Ide kell menned."

Fú (kilélegzik). Úgy tűnik, nem felejtettem el semmit.

Hoppá! Igen, nem felejtettem el...
Hol van az indító konfiguráció? Kemény kódban? Mint az:

[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.

Hol felejtettem/hibáztam? (e nélkül semmiképp), nagyon remélem, hogy a figyelmes olvasók nem lesznek lusták kiütni az orrukat.

Sok szerencsét!

Forrás: will.com

Hozzászólás