Com (xswitcher a l'etapa de "prova de concepte") va rebre molts comentaris constructius (que és bonic), vaig continuar dedicant el meu temps lliure desenvolupant el projecte. Ara vull gastar una mica del teu... El segon pas no serà del tot habitual: proposta/debat del disseny de la configuració.

D'alguna manera resulta que als programadors normals els resulta increïblement avorrit configurar tots aquests controls.
Per no ser infundat, dins hi ha un exemple del que estic tractant.
En general, Apache Kafka i ZooKeeper excel·lentment concebuts (i ben implementats).
- Configuració? Però és avorrit! XML tonto (perquè és "fora de la caixa").
- Ah, també vols un ACL? Però és tan avorrit! Tap-blooper... Alguna cosa així.
Però en el meu treball és exactament el contrari. Dret (per desgràcia, gairebé mai la primera vegada) el model construït us permet continuar amb facilitat i naturalitat (gairebé) muntar un diagrama.
Recentment em vaig trobar amb un article sobre Habré sobre el treball dur dels científics de dades...
Resulta que aquest moment és plenament realitzat per a ells. I a la meva pràctica, com diuen, "versió lleugera". Models de diversos volums, programadors experimentats amb POO a punt, etc. — tot això apareixerà més tard quan/si surti. Però el dissenyador ha de començar en algun lloc aquí i ara.
Arriba al punt. Vaig prendre TOML com a base sintàctica .
Perquè ell (TOML) d'una banda, editable per humans. D'altra banda, es tradueix 1:1 a qualsevol de les sintaxis més habituals: XML, JSON, YAML.
A més, la implementació que vaig utilitzar des de “github.com/BurntSushi/toml”, encara que no és la més de moda (encara 1.4 sintaxi), és sintàcticament compatible amb el mateix JSON (“incorporat”).
És a dir, si ho desitgeu, simplement podeu dir "anar pel bosc amb aquest TOML vostre, vull XXX" i "pedagar" el codi amb només una línia.
Així, si voleu escriure algunes finestres per configurar xswitcher (No estic segur) No s'esperen problemes "amb aquesta maleïda configuració teva".
Per a tots els altres, la sintaxi es basa en "clau = valor" (i, literalment, un parell d'opcions més complicades, com = [algunes, això, matriu]) suposo
intuïtivament convenient.
El que és interessant és això al mateix temps (al voltant de 2013). Només, a diferència de mi, l'autor de TOML va entrar a una escala adequada.
Per tant, ara em resulta més fàcil ajustar la seva implementació a mi mateix, i no a l'inrevés.
En general, prenem TOML (molt semblant a l'antiga INI de Windows). I tenim una configuració en la qual descrivim com connectar una sèrie de ganxos en funció del conjunt dels últims codis d'escaneig des del teclat. A continuació, peça per peça, el que ha passat fins ara. I una explicació de per què em vaig decidir així.
0. Abstraccions bàsiques
- Designacions de codi d'escaneig. Definitivament, cal fer alguna cosa al respecte, ja que simplement els codis digitals no són absolutament llegibles pels humans (només sóc jo ).
Vaig treure "ecodes.go" de "golang-evdev" (em feia massa mandra mirar la font original, tot i que l'autor ho va indicar molt culturalment). Vaig corregir una mica (de moment) una cosa que era bastant temible. Com "LEFTBRACE" → "L_BRACE". - A més, va introduir el concepte de "claus d'estat". Atès que la gramàtica habitual utilitzada no permet pas llargs. (Però us permet comprovar amb una sobrecàrrega mínima. Si només feu servir la gravació "directa".)
- Hi haurà un "deduplicador" integrat del que es pressiona. Així, s'escriurà l'estat "repetició"=2 01:00 vegades
1. Secció de plantilles
[Templates] # "@name@" to simplify expressions
# Words can consist of these chars (regex)
"WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"
En què consisteix una paraula de llenguatge humà amb notació fonètica? (ja sigui una qüestió de grafemes també coneguts com "jeroglífics")? Una mena de "llençol" terrible. Per tant, de seguida introdueixo el concepte de "plantilla".
2. Què fer quan es fa clic en alguna cosa (ha arribat un altre codi d'escaneig)
[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"]
Hi ha 768 codis en total. (Però "per si de cas" vaig inserir la captura de "sorpreses" al codi xswitcher).
A l'interior vaig descriure omplir la matriu amb enllaços a les funcions "què fer". En golang això és (de sobte) Va resultar ser convenient i evident.
- Tinc la intenció de reduir la "caiguda" al mínim en aquest lloc. A favor d'un processament més flexible (ho mostraré a continuació).
3. Taula amb classes de finestra
# 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"
Les files de la taula estan entre claudàtors dobles amb el seu nom. No podria haver estat més fàcil de seguida. En funció de la finestra activa actualment, podeu seleccionar les opcions següents:
- El vostre propi conjunt de "tecles d'accés ràpid" "Accions = ...". Si no / buit, no feu res.
- Canvia "MouseClickDrops": què fer quan es detecta un clic del ratolí. Com que en el punt on xswitcher està activat no hi ha detalls sobre "on fan clic", restablirem la memòria intermèdia de manera predeterminada. Però als terminals (per exemple) no cal que feu això (generalment).
4. Una (o diverses) seqüències de clics desencadenen un o un altre ganxo
# 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)" ]
Els ganxos es divideixen en dos tipus. Integrat, amb noms "parlants" (NewWord, NewSentence, Compose) i programable.
Els noms programables comencen per "Acció". Perquè TOML v1.4, els noms amb punts han d'estar entre cometes.
Cada apartat s'ha de descriure a continuació amb el mateix nom.
Per no volar la ment de la gent amb habituals "nus" (per experiència, els seus escriurepotser un de cada deu professionals), implemento immediatament una sintaxi addicional.
- "OFF:" (o "ON:") abans que l'expressió regular (expressió regular) requereixi que s'alliberin (o premeu) els botons següents.
A continuació, faré una expressió regular "injusta". Amb control separat de peces entre tubs "|". Per reduir el nombre de registres com "[LR]_SHIFT" (on és evident que això no és necessari). - "SEQ:" Si la condició anterior es compleix (o no), aleshores contrastem amb una expressió regular "normal". Per obtenir més informació, envio immediatament a ^W la biblioteca "regexp". Perquè encara no m'he preocupat d'esbrinar el grau de compatibilitat amb el meu pcre preferit ("compatible perl").
- L'expressió s'escriu en forma "BUTTON_1: CODE1, BUTTON_2: CODE2" etc., en l'ordre en què es reben els codis d'escaneig.
- La comprovació sempre està "acoblada" al final de la seqüència, de manera que no cal afegir "$" a la cua.
- Totes les comprovacions en una línia es realitzen una darrere l'altra i es combinen per "jo". Però com que el valor es descriu com una matriu, podeu escriure una comprovació alternativa després de la coma. Si això és necessari per algun motiu.
- Valor "SeqLength = 8" limita la mida de la memòria intermèdia contra la qual es realitzen totes les comprovacions. Perquè No he trobat mai (fins ara) recursos infinits a la meva vida.
5. Col·locació dels ganxos descrits a l'apartat anterior
# 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.
El principal aquí és "Acció = [Matriu]". De manera similar a la secció anterior, hi ha un conjunt limitat d'accions integrades. I la possibilitat d'acoblament no està limitada en principi (Escriu "Action.XXX" i no siguis massa mandrós per escriure'n una altra secció).
En particular, la reescriptura d'una paraula en el disseny corregit es divideix en dues parts: "canviar el disseny tal com s'especifica allà" и "reescriure" ("Reescriure la paraula").
La resta de paràmetres s'escriuen al "diccionari" ("mapa" en golang) per a una acció determinada, la seva llista depèn del que s'escriu a "Acció".
Es poden descriure diverses accions diferents en un munt (seccions). O pots separar-lo. Com he mostrat més amunt.
Immediatament vaig configurar l'acció "Exec" per executar l'script extern. Amb l'opció d'empènyer el buffer gravat a stdin.
- "Espera = 1": espereu que finalitzi el procés en execució.
- Probablement, "al munt" voldreu posar persones addicionals a l'entorn. informació com ara el nom de la classe de finestra des de la qual s'ha interceptat.
"Vols connectar el teu gestor? Aquí és on has d'anar".
Uf (exhalat). Sembla que no he oblidat res.
Vaja! Sí, no m'he oblidat...
On és la configuració de llançament? En codi dur? Així:
[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.
On he oblidat/he comès un error? (de cap manera sense això), Espero de debò que els lectors atents no siguin massa mandrós per ficar-se el nas.
Bona sort!
Font: www.habr.com
