Kuna (хswitcher on "proof of concept" staadiumis) sai piisavalt palju konstruktiivset tagasisidet (mis on meeldiv), jätkasin oma vaba aja kulutamist projekti arendamiseks. Nüüd tahan kulutada natuke teie… Teine samm on veidi ebatavaline: konfiguratsiooni disaini ettepanek/arutelu.

Kuna tavaliselt tundub, et normaalsetele programmeerijatele on nende kõikide keerukate seadistuste tegemine äärmiselt igav.
Kuna ei ole mõtet olla sõnaselge, siin on näide sellest, millega ma kokku puutun.
Suurepäraselt kujundatud (ja hästi teostatud) Apache Kafka & ZooKeeper.
— Konfiguratsioon? Aga see on ju igav! Tõmba-tõmba xml (sest "karbist välja").
— Oh, ja teie tahate ka ACL-i? Aga see on ju nii tüütu! Tõmba-tõmba… Nii see on.
Ja minu töös on see täiesti vastupidine. Õigesti (kahjuks ei juhtu see peaaegu kunagi esimesel katsel) koostatud mudel võimaldab hiljem lihtsalt ja vaevata (noh, peaaegu) koos skeemi kokku panna.
Hiljuti nägin Habré artiklit andeteadlastest, kelle töö ei ole kerge…
Selgub, et see aspekt on neil täielikult realiseeritud. Ja minu praktikas, nagu öeldakse, on see "kerge versioon". Mitmeosalisemad mudelid, kogenud programmeerijad OOP-iga ja nii edasi – see kõik tuleb hiljem, kui/ja kui tõusma hakkab. A konstruktor peab siin ja praegu millegagi alustama.
Asi on tõsine. Süntaktilise aluseks võtsin TOML .
Sest ta (TOML) on ühelt poolt inimese redigeerimiseks sobiv. Teiselt poolt – edastatakse 1:1 igasse levinumasse süntaksisse: XML, JSON, YAML.
Lisaks on minu kasutatavad rakendused ‘github.com/BurntSushi/toml’ kuigi mitte kõige moes (ikka veel süntaks 1.4), kuid süntaktiliselt ühilduv sama (‘sisseehitatud’) JSON-iga.
See tähendab, et soovi korral võib lihtsalt öelda "kasu meetod sellest TOMList, tahan XXX" ja "patcheerida" kogu koodi ühe reaga.
Seega, kui soovite kirjutada mingisuguseid aknaid xswitcher’ile (kindlasti mitte mina) probleeme „selle teie kuradi konfiguratsiooniga” ei oodata.
Kogu ülejäänud süntaks „võti = väärtus” (ja mõne paari keerukama valikuga, nagu = [mõni, massiiv]) ma arvan
intuitiivselt mugav.
Mis on huvitav, mul endal umbes samal ajal (aasta paiku 2013). Ainult, erinevalt minust, tuli TOMLi autor loomulikult asjast läbi.
Seega on mul nüüd lihtsam kohandada selle rakendust enda järgi, mitte vastupidi.
Üldiselt võtame TOML (väga sarnane vana Windowsi INI-le). Ja seadistame konfiguratsiooni, kus kirjeldame, kuidas siduda rida hook'e olenevalt viimaste klaviatuurikoodide kogumist. Allpool tükkhaaval — see, mis hetkel välja tuli. Ja selgitused, miks ma nii otsustasin.
0. Põhialused
- Klaviatuurikoodide tähistamine. Sellega tuleb kindlasti midagi teha, kuna lihtsalt numbrilised koodid on täiesti mittetäielikult loetavad (ma pole siinkohal aiamaastikul). ).
Võtsin "ecodes.go" failist "golang-evdev" (originaali järgi ei viitsinud minna, kuigi autor on selle kenasti välja toonud). Natuke (praegu) muutsin täiesti kohutavat. Näiteks "LEFTBRACE" → "L_BRACE". - Tõin täiendavalt sisse mõisted "klahvid seisundiga". Kuna kasutatud regulaarne grammatika ei võimalda pikki jutustusi. (Aga see võimaldab kontrollida minimaalsete kuludega. Kui kasutada ainult "otsest" salvestust.)
- Sisse tuleb ehitatud "deduplicaator" vajutatud klahvide jaoks. Nii et seisund "kordus"=2 salvestatakse. ühte Mis puutub reaalsesse tööprotsessi, siis rohkem kui poole aasta jooksul ja, tähelepanu, regulaarsete kuudepikkuste värskendustega, on operatsioonisüsteemi taaskäivitamine toimunud vaid üks kord, samas kui sel perioodil Windows Server 2019 GUI oli taaskäivitamisel iga kuu.
1. Mallide jaotamine
[Templates] # "@name@" väljendite lihtsustamiseks
# Sõnad võivad koosneda järgmistes märkidest (regex)
"WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"
Millest koosneb sõna inimkeeles koos fonoloogilise kirjutamisega. (berreak kevadest aka "hieroglüüfid")? Какая-то ужасная «простыня». Поэтому сразу закладываю понятие «шаблона».
2. Mida teha, kui midagi vajutatakse (järjekordne klaviatuurikood saabus)
[ActionKeys]
# Kogu klahv ja testi käsu järjestus
# !!! Korduvad koodid (code=2) tuleb koguda ainult ühe klahvi kohta!
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"]
# Viska kõik kogutud klahvid, sealhulgas see. See on vaikimisi toiming.
Drop = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
# Salvestage ekstra kaart nende klahvide jaoks, kui mõni on "all" seisundis.
# Seisundit kontrollitakse "OFF:"|"ON:" tingimuste kaudu tegevuses.
# (Samuti peavad nende klahvide seisundid püsima puhverlagede 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"]
# Testimiseks, kuid ära kogu.
# Näiteks kasutan F12 asemel BREAK odavatel sülearvutitel, millel on kehvad klaviatuurid (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 "iga juhuks" lisasin koodi xswitcher'i üllatuste püüdmiseks).
Sisemiselt kirjeldasin massiivi täitmist linkidega funktsioonide "mida teha". Go keeles on see (üllatuslikult) osutunud mugavaks ja ilmseks.
- Kavasin plaanin "Drop" selle koha pealt minimeerida. Paindlikuma töötlemise kasuks (näitan allpool).
3. Akende klasside tabel
# 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 — topelt-konditsioneerivate nurkade vahel koos selle nimega. Lihtsalt esimesel katsel ei õnnestunud. Sõltuvalt aktiivsest aknast saad valida valikud:
- Oma komplekt "kiirklahve" "Actions = …". Kui pole / tühi — midagi ei toimu.
- Lüliti "MouseClickDrops" — mida teha, kui hiirekliki tuvastatakse. Kuna xswitcheri sisselülitamise punktis pole mingeid üksikasju "kuhu klikkida", lähtestame vaikimisi vahemälu. Kuid terminalides (näiteks) ei pruugi seda teha. (üldiselt).
4. Üks (või mitu) vajutust käivitavad selle või teise hook'i
# 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)" ]
Hoope jagasin kaheks tüübiks. Sissetoodud, "rääkivate" nimede (NewWord, NewSentence, Compose) ja programmeeritavad.
Programmeeritavate nimed algavad "Action.". TOML v1.4 alusel peavad punktidega nimed olema jutumärkides.
Allpool peab igaühe jaoks olema kirjeldatud sektsioon sama nimega.
Et mitte inimestele "paljaste" regulaarmärkide tõttu pead lõhkuda (kogemus näitab, et kirjutada- võib olla üks kümnest professionaalist), tutvustan kohe täiendavat süntaksit.
- "OFF:" (või "ON:") enne regexp (regulaarmärk) nõuavad, et edasi antud nupud oleksid vabastatud (või vajutatud).
Edasi plaanin teha "ebaregulaarse" regulaarmüügi. Eraldi kontrolliga osade vahel pipe'ide "|". Eesmärgiga vähendada salvestuste arvu, nagu "[LR]_SHIFT" (seal, kus see ei ole selgelt vajalik). - "SEQ:" Kui eelmised tingimused on täidetud (või puuduvad), kontrollime edasi "tavalise" regulaarmüügi suhtes. Üksikasjade jaoks saadan koheselt raamatukogusse "regexp." Sest ma pole endiselt viitsinud välja selgitada ühilduvuse taset minu lemmikute pcre-dega ("perl compatible").
- Väljend sõnastatakse kujul "BUTTON_1: CODE1, BUTTON_2: CODE2" jne, saabumise järjekorras sken-koodide järgi.
- Kontroll toimub alati järelduse lõpus, seega ei pea lõppu "$" lisama.
- Kõik kontrollid ühes reas toimuvad järjestikku ja ühendatakse "JA". Kuid kuna väärtus on määratletud massiivina, võib pärast koma kirjutada alternatiivse kontrolli. Kui see on mingil põhjusel vajalik.
- Tähendus "SeqLength = 8" piirab vahemälu suurust, mille suhtes kõik kontrollid toimuvad. Kuna mul pole elus (kuni praeguseni) olnud piiramatu ressursi kogemust.
5. Eelmises osas kirjeldatud hookide seadistamine
# 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 — „Action = [Massiiv]“. Nagu eelnevas osas, on olemas piiratud hulk sisseehitatud tegevusi. Ja põhimõtteliselt pole piiratud võimalus siduda (kirjuta „Action.XXX“ ja ära unusta kirjutada sellele veel üks sektsioon).
Sealhulgas, õigekirjakorrektuuri sõna ülekirjutamine jaguneb kaheks osaks: „vaheta klaviatuur sarnaselt seal määratletule“ ja „ülekirjuta“ („RetypeWord“).
Ülejäänud parameetrid salvestatakse „sõnastikku“ („map“ golangis) selle tegevuse jaoks, nende loetelu sõltub „Actionis“ kirjutatust.
Mõningaid erinevaid tegevusi saab kirjeldada ühes kastmes (sektsioonid). Ja neid saab ka laiali tassida. Nagu ma eespool näitasin.
Kohe seadistan tegevuse „Exec“ — teosta välise skripti. Valikuga, et tal oleks stdin'is kirja pandud puhver.
- „Wait = 1“ — oota, kuni käivitatud protsess lõpetatakse.
- Tõenäoliselt tahad „lisaks“ keskkonda visata täiendavat teavet, nagu akna klassi nimi, millest on kinni peetud.
„Tahtsid oma töötlejat lisada? Siin on sul koht.”
Uf (hingasin välja). Näib, et midagi pole ununenud.
Ops! Jah, ei unustanud…
Ja kus on käivituskonfiguratsioon? Kõvakoodis? Umbes nii:
[ScanDevices]
# Peab olemas olema käivitamisel. Enda taastamine, kui see on noorem kui 30s
Test = "\/dev\/input\/event0"
Respawn = 30
# Otsimismask
Search = "\/dev\/input\/event*"
# Minu ThinkPadidel on sellised pseudo-klaviatuurid, kus on tohutult üleliigseid sündmusi
Bypass = "(?i)Video|Camera" # "(?i)" erineb ilmselgelt „klassikalisest“ pcre'st.
Ja kus ma unustasin/vääratustasin (ilma selleta — ei saa), loodan tõeliselt, et tähelepanelikud lugejad ei viitsi nina sisse torgata.
Edu!
Allikas: habr.com
