Xswitcher diseinu-zuzentzailea Linuxerako: bigarren urratsa

Bezala aurreko argitalpena (xswitcher-ek "kontzeptuaren froga" fasean) iritzi eraikitzaile asko jaso zituen (polita da), nire denbora librea proiektua garatzen jarraitu nuen. Orain zure zati apur bat pasa nahi dut... Bigarren urratsa ez da guztiz ohikoa izango: konfigurazio diseinuaren proposamena/eztabaida.

Xswitcher diseinu-zuzentzailea Linuxerako: bigarren urratsa

Nolabait, programatzaile arruntei izugarri aspergarria iruditzen zaie kontrol horiek guztiak konfiguratzea.

Oinarririk gabekoa izan ez dadin, barnean ari naizenaren adibidea da.
Orokorrean bikain pentsatuta (eta ondo inplementatuta) Apache Kafka eta ZooKeeper.
- Konfigurazioa? Baina aspergarria da! Xml mutu ("kutxatik kanpo" dagoelako).
- Ai, zuk ere nahi al duzu ACL bat? Baina hain da aspergarria! Tap-blooper... Horrelako zerbait.

Baina nire lanean guztiz kontrakoa da. Eskuin (Ai, ia inoiz ez lehen aldia) eraikitako ereduak errazago eta naturaltasunez jarraitzeko aukera ematen du (Ia) diagrama bat muntatu.

Duela gutxi Habré-ri buruzko artikulu bat topatu dut datu-zientzialarien lan gogorrari buruz...
Ematen du momentu hau guztiz gauzatzen dela haientzat. Eta nire praktikan, esaten den bezala, “bertsio arina”. Bolumen anitzeko ereduak, OOP prest duten programatzaile onduak, etab. — hau guztia geroago agertuko da aireratzen denean/abiatzen bada. Baina diseinatzaileak hemen eta orain nonbait hasi behar du.

Lortu puntura. TOML hartu nuen oinarri sintaktiko gisa herritar honengandik.

Berak duelako (TOML) alde batetik, gizakiak editagarriak. Bestalde, 1:1 itzultzen da ohiko sintaxietako edozeinetara: XML, JSON, YAML.
Gainera, “github.com/BurntSushi/toml”-tik erabili dudan inplementazioa, modan dagoena ez den arren (oraindik 1.4 sintaxia), sintaktikoki bateragarria da JSON berarekin (“integratua”).

Hau da, nahi baduzu, besterik gabe, "joa zaitez basotik zure TOML horrekin, XXX nahi dut" eta "adabaki" kodea lerro bakarrarekin.

Horrela, xswitcher konfiguratzeko leiho batzuk idatzi nahi badituzu (Ez nago ziur) Ez da arazorik espero "zure konfigurazio madarikatu honekin".

Beste guztientzat, sintaxia "gakoa = balioa"n oinarritzen da (eta literalki pare bat aukera konplikatuago, adibidez = [batzuk, hori, array]) suposatzen dut
intuitiboki erosoa.
Interesgarria dena hori da "erre" garai berean (2013 inguruan). Bakarrik, ni ez bezala, TOMLren egileak eskala egokian sartu zuen.

Hori dela eta, orain errazagoa zait bere ezarpena neure buruari egokitzea, eta ez alderantziz.

Orokorrean, TOML hartzen dugu (Windows INI zaharraren oso antzekoa). Eta konfigurazio bat dugu, zeinetan kako sorta bat nola lotu deskribatzen dugun teklatuaren azken eskaneatu kodeen multzoaren arabera. Jarraian, piezaz pieza, orain arte gertatu dena. Eta horrela erabaki nuen zergatik erabaki nuen.

0. Oinarrizko abstrakzioak

  • Eskaneatu kodearen izendapenak. Behin betiko zerbait egin behar da honi buruz, kode digitalak ez baitira erabat gizakiek irakurtzen (ni naiz loloswitcher).
    “Golang-evdev”-tik “ecodes.go” astindu nuen (jatorrizko iturriari begiratzeko alferra nengoen, egileak nahiko kulturalki adierazi zuen arren). Apur bat zuzendu nuen (oraingoz) nahiko beldurgarria zen zerbait. "LEFTBRACE" → "L_BRACE" bezala.
  • Gainera, "estatuko gakoak" kontzeptua sartu zuen. Erabilitako gramatika arruntak ez baitu pasarte luzerik onartzen. (Baina kostu minimoarekin egiaztatzea ahalbidetzen du. Grabaketa "zuzena" soilik erabiltzen baduzu.)
  • Sakatzen denaren "desduplikatu" bat egongo da. Horrela, "errepikatu"=2 egoera idatziko da bat denbora.

1. Txantiloiak atala

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

Zertan datza idazkera fonetikoa duen giza hizkuntza-hitz bat? (Edo grafema kontua edo "hieroglifoak")? Ikaragarrizko "orria" moduko bat. Horregatik, berehala sartzen dut “txantiloi” kontzeptua.

2. Zer egin zerbait sakatzen denean (beste eskaneatu kode bat iritsi da)

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

768 kode daude guztira. (Baina "badaezpada" xswitcher kodean "sorpresa" harrapatzeko txertatu nuen).
Barruan array-a "zer egin" funtzioetarako estekekin betetzea deskribatu nuen. Golan hau da (bat-batean) Erosoa eta bistakoa izan zen.

  • Leku honetan "Drop" gutxienera murrizteko asmoa dut. Prozesamendu malguagoaren alde (behean erakutsiko dut).

3. Leiho klaseekin taula

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

Taularen errenkadak kortxete bikoitz artean daude bere izenarekin. Ezin zen errazagoa izan hasieratik. Une honetan aktibo dagoen leihoaren arabera, aukera hauek hauta ditzakezu:

  • Zure "laster-tekla" multzoa "Ekintzak =...". Ez bada/hutsik, ez egin ezer.
  • Aldatu "MouseClickDrops" - zer egin saguaren klik bat hautematen denean. Xswitcher aktibatuta dagoen unean "non klik egiten duten" buruzko xehetasunik ez dagoenez, lehenespenez buffera berrezartzen dugu. Baina terminaletan (adibidez) ez duzu hau egin beharrik (normalean).

4. Klik-sekuentzia batek (edo hainbat) amu bat edo beste abiarazten du

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

Amuak bi motatan banatzen dira. Inkorporatua, "ahozko" izenekin (NewWord, NewSentence, Compose) eta programagarria.

Izen programagarriak "Ekintza"rekin hasten dira. Zeren TOML v1.4, puntuak dituzten izenak komatxo artean egon behar dira.

Atal bakoitza jarraian deskribatu behar da izen berarekin.

Ohiko "biluziak" (esperientziatik, beren idaztekoagian hamarretik bat profesionalak), berehala inplementatzen dut sintaxi gehigarria.

  • "OFF:" (edo "ON:") adierazpen erregularra (adierazpen erregularra) hurrengo botoiak askatu (edo sakatu) behar baino lehen.
    Jarraian, adierazpen erregular "bidegabea" egingo dut. Hodien arteko piezak bereizita egiaztatzearekin "|". "[LR]_SHIFT" bezalako erregistro kopurua murrizteko (non hau beharrezkoa ez den).
  • "SEQ:" Aurreko baldintza betetzen bada (edo ez badago), orduan adierazpen erregular "normal" batekin egiaztatuko dugu. Xehetasunak lortzeko, berehala bidaltzen dut ^W-ra "regexp" liburutegia. Oraindik ez naizelako kezkatu nire pcre gogokoenarekin («perl compatible») zein den bateragarritasun-maila jakiteko.
  • Adierazpena forman idatzita dago "BUTTON_1: CODE1, BUTTON_2: CODE2" etab., eskaneatzeko kodeak jasotzen diren ordenan.
  • Egiaztapena sekuentziaren amaierara arte beti "eskuratuta" dago, beraz, ez dago "$" gehitu beharrik isatsari.
  • Lerro bateko egiaztapen guztiak bata bestearen atzetik egiten dira eta “I”-rekin konbinatzen dira. Baina balioa array gisa deskribatzen denez, kontrol alternatibo bat idatz dezakezu komaren ondoren. Hau arrazoiren bategatik behar bada.
  • Balio "SeqLength = 8" egiaztapen guztiak egiten diren buffer-aren tamaina mugatzen du. Zeren Nire bizitzan ez dut inoiz (orain arte) baliabide amaigaberik topatu.

5. Aurreko atalean azaldutako kakoen ezarpena

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

Hemen gauza nagusia da "Ekintza = [Matrizea]". Aurreko atalaren antzera, barne-ekintzen multzo mugatua dago. Eta atrakatzeko aukera ez da printzipioz mugatua (idatzi "Action.XXX" eta ez izan alferrak horren beste atal bat idazteko).
Bereziki, zuzendutako diseinuan hitz bat berriro idaztea bi zatitan banatzen da: "aldatu diseinua bertan zehazten den moduan" и "berriro idatzi" ("Berri idatziHitza").

Gainerako parametroak "hiztegian" idazten dira ("mapa" golang-en) ekintza jakin baterako, haien zerrenda "Ekintza"-n idatzitakoaren araberakoa da.

Hainbat ekintza ezberdin deskriba daitezke pila batean (atalak). Edo kendu dezakezu. Goian erakutsi dudan bezala.

Berehala "Exec" ekintza ezarri dut kanpoko scripta exekutatzeko. Grabatutako bufferra stdin-era bultzatzeko aukerarekin.

  • "Itxaron = 1" - itxaron exekutatzen ari den prozesua amaitu arte.
  • Seguruenik, "pilaraino" ingurunean pertsona gehigarriak jarri nahi dituzu. atzeman den leiho-klasearen izena bezalako informazioa.
    "Zure kudeatzailea konektatu nahi duzu? Hona joan behar duzu».

Uf (arnasa). Badirudi ez dudala ezer ahaztu.

Aupa! Bai, ez nuen ahaztu...
Non dago abiarazteko konfigurazioa? Kode gogorrean? Horrelako:

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

Non ahaztu/egin dut akats bat? (hori gabe inola ere ez), Benetan espero dut irakurle adiak ez direla alferrak izango sudurra sartzeko.

Zorte on!

Iturria: www.habr.com

Gehitu iruzkin berria