Täpse paigutuse korrektor „xswitcher” Linuxile: teine samm

Kuna eelmine postitus (хswitcher tõestamise kontseptsiooni faasis) sai üsna palju konstruktiivset tagasisidet (mis on meeldiv), jätkasin oma vaba aja kulutamist projekti arendamisele. Nüüd tahan kulutada natuke teie… Teine samm ei ole päris harjumuspärane: konfiguratsiooni disaini pakkumine/arutelu.

Täpse paigutuse korrektor „xswitcher” Linuxile: teine samm

Kuid kuidagi tundub, et tavalistele programmeerijatele on kõigi nende keerukate seadmete seadmine äärmiselt igav.

Et mitte olla põhjendamata, siin on näide sellest, millega ma tegelen.
Fantastiliselt hästi läbi mõeldud (ja hästi teostatud) Apache Kafka & ZooKeeper.
— Konfiguratsioon? Aga see on ju igav! Tjaap-tyap xml (sest «karbist välja»).
— Oh, kas te tahate ka ACL? Aga see on nii tüütu! Tap-lyap… Noh, kuidagi nii.

Aga mu töös on täpselt vastupidi. Õigesti (kahjuks harva toimib esimese korraga) ehitatud mudel võimaldab edaspidi kergesti ja muretult (noh, peaaegu) genereerida skeemi.

Viimasel ajal sattusin Habrisse artiklile data-scientist'ide mitte kerge töö kohta...
Selgub, et see hetk on neil täielikult realiseeritud. Aga minu praktikas, nagu öeldakse, "kerge versioon". Mitmeosalised mudelid, kogenud programmeerijad OOP-ga varustatud jne – see kõik tuleb hiljem, kui/ja kui see tõuseb. Konstruktoril on aga vaja millegagi alustada siin ja praegu.

Asume lähemale sisule. Sünktaktilise alusena kasutasin TOML-i. siit selle isiku käest..

Sest ta on (TOML) ühelt poolt inimredigeeritav. Teiselt poolt – edastatakse 1:1 mis tahes levinumates süntaksites: XML, JSON, YAML.
Lisaks on minu kasutatud rakendus "github.com/BurntSushi/toml" ehkki mitte kõige moodsam (ikka veel süntaks 1.4), aga süntaksiliselt ühilduv sama ("sisseehitatud") JSON-iga.

Nii et, kui soovite võite lihtsalt öelda "mine metsas selle sinu TOML-iga, ma tahan XXX" ja "patchida" koodi ühe rea abil.

Seega, kui soovite kirjutada konfigureerimiseks hswitcher mingid aknad (kindlasti mitte mina) probleeme "selle teie kuradi konfigiga" – ei oodata.

Kõigi teiste süntaks, mis põhineb "võti = väärtus" (ja sisuliselt paar keerulisemat valikut, nagu = [mingi, massiiv]) pean ma
intuitiivselt mugavaks.
Kuidas huvitav, et see juhtus «põles läbi» umbes samal ajal (umbes 2013. aastal). Ainult et, erinevalt minust, autor TOML läks teele nõuetekohaselt.

Seetõttu on mul nüüd lihtsam oma rakendust selle järgi kohandada, mitte vastupidi.

Üldiselt võtame TOML'i (mis sarnaneb väga vanale Windowsi INI-le). Ja loome konfiguratsiooni, kus kirjeldame, kuidas külge panna seeria hüpikuid sõltuvalt viimasest sken-koodide komplektist klaviatuurilt. Allpool on tükkide kaupa – see, mis hetkel valmis on. Ja selgitused, miks ma nii otsustasin.

0. Põhiabstraktsioonid

  • Sken-koodide tähistamine. Sellega peab kindlasti midagi ette võtma, kuna lihtsalt numbrilised koodid ei ole absoluutselt inimloetavad (see on minu aed) loloswitcher).
    Võtsin «ecodes.go» «golang-evdev'ist» (originaallähte juurde minemine tundus liiga vaevaline, kuigi autor on selle kenasti viidatud). Natuke (praegu) kohandasin selle koletise paremaks. Nagu «LEFTBRACE» → «L_BRACE».
  • Täpsemalt sisse viidud mõisted «olekus klahvid». Kuna kasutatud regulaarne grammatik ei soosinud pikki lauseid. (Kuid see võimaldab kontrollida minimaalse koormusega. Kui kasutada ainult «otse» salvestamist.)
  • Sisaldab sisseehitatud "deduplikaator"-i. Seega salvestatakse olek "kordus"=2. ühe korral.

1. Mallide jagamine

[Templates] # "@name@" väljendite lihtsustamiseks
 # Sõnad võivad koosneda järgmistest tähemärkidest (regex)
 "WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"

Mida sisaldab inimkeele sõna koos fonoloogilise tähendusega (kas see on grafemide ehk "hieroglüüfide" küsimus)? Какая-то ужасная «простыня». Поэтому сразу закладываю понятие «шаблона».

2. Mida teha, kui midagi vajutatakse (saabus uus skännikood)

[ActionKeys]
 # Kogu klahv ja tee test käskude järjestusele
 # !!! Korduskoodid (kood=2) peavad olema kogutud iga klahvi kohta ainult korra!
 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"]

 # Kasta kõik kogutud klahvid, sealhulgas see. See on vaikimisi toiming.
 Drop = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
 # Salvestage ekstra kaart nende klahvide jaoks, kui need on "alla" olekus.
 # Oleku kontrollimiseks kasutatakse "OFF:"|"ON:" tingimusi tegevuses.
 # (Lisaks peab nende klahvide olek püsima puhverdamise vahel.)
 # ??? Kuidas tegeleda CAPS ja "LOCK"-klahvidega ???
 StateKeys = ["L_CTRL", "L_SHIFT", "L_ALT", "L_META", "CAPS", "N_LOCK", "S_LOCK",
              "R_CTRL", "R_SHIFT", "R_ALT", "R_META"]

 # Ainult testimiseks, kuid ärge koguge.
 # Nt kasutan F12 BREAK asemel halbadelt sülearvutitelt (uued ThinkPadid)
 Test = ["F1..F10", "ZENKAKUHANKAKU", "102ND", "F11", "F12",
          "RO..KPJPCOMMA", "SYSRQ", "SCALE", "HANGEUL..YEN",
          "STOP..SCROLLDOWN", "NEW..MAX"]

Kokku on ette nähtud 768 koodi. (Aga "igal juhu" lisasin koodisse xswitcher, et kinni püüda "üllatused").
Sisem olen kirjeldanud massiivi täitmist funktsioonide linkidega "mida teha". Golangis on see (üllatuslikult) mugav ja ilmekas.

  • "Drop" kavas selles kohas vähendada miinimumini, et eelistada paindlikumat töötlemist (näitan allpool).

3. Aken klasside tabeliga

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

Tabeli read on kahequadraatlise sulgudes koos tema nimetusega. Lihtsamalt ei õnnestunud. Sõltuvalt hetkel aktiivsest aknast saab valida valikuid:

  • Oma "kuumade klahvikombinatsioonide" komplekt "Actions = ...". Kui ei ole/tühi - siis mitte midagi teha.
  • Lüliti "MouseClickDrops" - mida teha hiirekliki tuvastamise korral. Kuna xswitcheri aktiivimispunktis ei ole mingeid üksikasju "kuhu seal klikitakse", siis lähtume vaikimisi puhvri nullimisest. Kuid terminalides (näiteks) seda ei pruugi teha. (tavaliselt).

4. Üks (või mitu) nupuvajutuste järjestust aktiveerib vastava hüppe

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

Hüppelaadijad jagasin kaheks tüübiks. Sisseehitatud, "rääkivate" nimedega (NewWord, NewSentence, Compose) ja programmeeritavad.

Programmeeritavate nimed algavad "Action.". Kuna TOML v1.4, peavad punktidega nimed olema ümbritsetud jutumärkidega.

Allpool peaks igaühe kohta olema kirjeldatud segment sama nimega.

Ettei inimestele aju plahvataks „paljaste” regulaarsete väljenditega (kogemuse põhjal, nende kirjutadahulgas võib üks kümnest professionaalidest), rakendan kohe lisasüntaksit.

  • „OFF:” (või „ON:”) eelnevalt regexp (regulaarne väljend) nõuab, et järgnevad nupud oleksid vabastatud (või vajutatud).
    Edasi plaanin teha „e-baasi” regulaarset väljendit. Eraldi kontrollides osi torude "|" vahel. Eesmärgiga vähendada kirjeid, mis näevad välja nagu "[LR]_SHIFT" (seal, kus see selgelt ei ole vajalik).
  • „SEQ:” Kui eelmine tingimus on täidetud (või puudub), kontrollime edasi tavapärase regulaarse väljendi suhtes. Üksikasjade osas suunan kohe^Wraamatukokku „regexp”. Sest ma pole veel viitsinud välja selgitada ühilduvuse määr minu lemmikute pcre („perl compatible”) suhtes.
  • Väljend kirjutatakse kujul „NUPP_1: KOOD1, NUPP_2: KOOD2” jne, saabumise järjekorras sken-koodide.
  • Kontrollimine toimub alati sekvenzi lõpule, seega "$" sabasse lisama ei pea.
  • Kõik kontrollid ühes reas viiakse läbi järjestikku. ja ühendatakse «И» kaudu. Kuid kuna väärtus on kirjeldatud massiivina, saab pärast kommat kirjutada alternatiivse kontrolli. Kui see mingil põhjusel vajalik on.
  • Väärtus «SeqLength = 8» piirab puhvri suurust, mille suhtes kõik kontrollid toimuvad. Kuna mul (kuni praeguseni) pole kunagi olnud piiramatu ressursse.

5. Klambrite määramine, nagu eelmises sektsioonis on kirjas

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

Peamine siin on — «Action = [Massiiv]». Sarnaselt eelmisele sektsioonile on olemas piiratud hulk sisseehitatud toiminguid. Ja põhimõtteliselt piiramatu võimalus ühendamiseks (kirjutada «Action.XXX» ja mitte unustada kirjutada selle alla veel üks sektsioon).
Sealhulgas, sõna ümbervormistus parandatud klaviatuuriseade jaguneb kaheks osaks: «muuda seade nagu seal on määratud» ja «korrata» («RetypeWord»).

Ülejäänud parameetrid salvestatakse «sõnastikku» («map» golangis) antud toimingu jaoks, nende loetelu sõltub sellest, mis on kirjutatud «Actionis».

Mitu erinevat toimingut saab kirjeldada ühes grupis (sektsioonides). Ja neid saab hajutada. Kuidas ma eespool näitasin.

Koheselt plaanin toimingu «Exec» — välise skripti käitamiseks. Temale stdin'sse kirjutatud puhvri andmise võimalusega.

  • «Wait = 1» — oota, kuni käivitatud protsess on lõppenud.
  • Tõenäoliselt soovite „kõrval“ jagada keskkonda täiendava teave, näiteks selle akna klassinime, kust see on kinni püüdnud.
    „Kas soovite oma töötlejat ühendada? Siin on link.”

Uf (hingasin välja). Tundub, et midagi pole unustatud.

Opp! Aha, ei unustanud…
Aga kus on käivitusk конфигурация? Kõva koodiga? Umbes nii:

[ScanDevices]
 # Peab olema alguses. Iseseisev taaskäivitus, kui see on noorem kui 30s
 Test = "/dev/input/event0"
 Respawn = 30
 # Otsingu mask
 Search = "/dev/input/event*"
 # Minu ThinkPadides on sellised pseudo-klaviatuurid, kus on palju tarbetuid sündmusi
 Bypass = "(?i)Video|Camera" # "(?i)" erineb kindlasti "klassikalisest" pcre-st.

Aga kus unustasin/eksisin (ilma selleta – mitte mingil juhul), loodan väga, et tähelepanelikud lugejad ei viitsi nina nurgast lohistada.

Edu!

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster