Correttore di layout Xswitcher per Linux: u passu dui

siccomu pubblicazione precedente (xswitcher à u stadiu di "prova di cuncettu") hà ricevutu assai feedback constructivu (chì hè bellu), Aghju cuntinuatu à passà u mo tempu liberu à sviluppà u prugettu. Avà vogliu passà un pocu di u vostru... U sicondu passu ùn serà micca abbastanza solitu: pruposta / discussione di cunfigurazione di cunfigurazione.

Correttore di layout Xswitcher per Linux: u passu dui

In ogni modu, risulta chì i programatori normali trovanu incredibbilmente noioso per stallà tutti questi cuntrolli.

Per ùn esse micca infundatu, dentru hè un esempiu di ciò chì aghju trattatu.
In generale cuncepitu (è ben implementatu) Apache Kafka & ZooKeeper.
- Cunfigurazione ? Ma hè noiosa ! Dumb xml (perchè hè "fora di a scatula").
- Oh, vulete ancu un ACL ? Ma hè tantu noiosa ! Tap-blooper... Qualcosa cusì.

Ma in u mo travagliu hè esattamente u cuntrariu. Diritta (Ahimè, quasi mai a prima volta) u mudellu custruitu permette di cuntinuà più facilmente è naturali (quasi) assemble un diagramma.

Recentemente aghju trovu un articulu nantu à Habré nantu à u travagliu duru di i scientifichi di dati ...
Ci hè chì stu mumentu hè cumplettamente realizatu per elli. È in a mo pratica, cum'è dicenu, "versione light". Modelli multi-volumi, programatori stagionati cù OOP in pronta, etc. - tuttu questu appariscerà dopu quandu / s'ellu decolla. Ma u designer hà da principià in un locu quì è avà.

Andate à u puntu. Aghju pigliatu TOML cum'è una basa sintattica da stu citadinu.

Perchè ellu (TOML) da una banda, editable umanu. Per d 'altra banda, hè traduttu 1: 1 in una di e sintassi più cumuni: XML, JSON, YAML.
Inoltre, l'implementazione chì aghju utilizatu da "github.com/BurntSushi/toml", ancu s'ellu ùn hè micca u più di moda (ancora a sintassi 1.4), hè sintatticamente cumpatibile cù u stessu JSON ("built-in").

Questu hè, sè vo vulete, pudete solu dì "passà à u boscu cù quellu TOML di u vostru, vogliu XXX" è "patch" u codice cù una sola linea.

Cusì, sè vo vulete scrive qualchi Windows per cunfigurà xswitcher (Ùn sò micca sicuru) Nisun prublema sò previsti "cù sta maledetta configurazione di a vostra".

Per tutti l'altri, a sintassi hè basatu annantu à "key = value" (è literalmente un paru di opzioni più complicate, cum'è = [alcuni, quellu, array]) Imaginu
intuitivamente convenientu.
Ciò chì hè interessante hè questu "bruciatu" à u listessu tempu (circa 2013). Solu, à u cuntrariu di mè, l'autore di TOML hè andatu in una scala propria.

Dunque, avà hè più faciule per mè per aghjustà a so implementazione per adattà à mè, è micca vice versa.

In generale, pigliamu TOML (assai simile à u vechju Windows INI). È avemu una cunfigurazione in quale descrivemu cumu attaccà una seria di ganci secondu u settore di l'ultimi codici di scansione da u teclatu. Sottu, pezzu per pezzu, hè ciò chì hè accadutu finu à avà. È una spiegazione di perchè aghju decisu questu modu.

0. Astrazzioni basi

  • Scansione di designazioni di codice. Qualcosa di sicuru deve esse fattu nantu à questu, postu chì i codici solu digitale ùn sò assolutamente micca leghjibili da l'omu (hè solu mè loloswitcher).
    Aghju scuzzulatu "ecodes.go" da "golang-evdev" (Eru troppu pigro per fighjà a fonte originale, ancu s'ellu l'autore l'hà indicatu abbastanza culturalmente). Aghju currettu un pocu (per ora) qualcosa chì era abbastanza paura. Cum'è "LEFTBRACE" → "L_BRACE".
  • Inoltre, hà introduttu u cuncettu di "chjavi statali". Siccomu a grammatica regulare utilizata ùn permette micca longu passaghji. (Ma permette di cuntrollà cun un minimu overhead. Se utilizate solu a registrazione "diretta".)
  • Ci sarà un "deduplicator" integratu di ciò chì hè pressatu. Cusì, u statu "ripetizione" = 2 serà scrittu один volte

1. Sezione Templates

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

In cosa consiste una parola di lingua umana con notazione fonetica? (sia una questione di grafemi aka "geroglifi")? Qualchì tipu di "foglia" terribili. Dunque, aghju introduttu immediatamente u cuncettu di "template".

2. Cosa da fà quandu qualcosa hè clicatu (un altru codice di scansione hè ghjuntu)

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

Ci sò 768 codici in totale. (Ma "in casu" aghju inseritu catturà "sorprese" in u codice xswitcher).
Dentru aghju descrittu riempimentu di l'array cù ligami per e funzioni "chì fà". In golang questu hè (di colpu) Risultava cunvene è evidenti.

  • Pensu di riduce "Drop" à u minimu in questu locu. In favore di un prucessu più flexible (l'aghju da mustrà quì sottu).

3. Table cù classi di 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"

E fila di a tavula sò in doppia parentesi quadrate cù u so nome. Ùn puderia micca esse più faciule da u battutu. Sicondu a finestra attualmente attiva, pudete selezziunate e seguenti opzioni:

  • U vostru propiu set di "tasti di scelta rapida" "Azzioni = ...". Se micca / viotu, ùn fate nunda.
  • Cambia "MouseClickDrops" - ciò chì fà quandu un clic di u mouse hè rilevatu. Siccomu à u puntu induve xswitcher hè attivatu ùn ci sò micca dettagli nantu à "induve cliccà", resettamu u buffer per automaticamente. Ma in terminali (per esempiu) ùn avete micca fà questu (di solitu).

4. Una (o parechje) sequenze di clicchi attivanu unu o un altru ganciu

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

I ganci sò spartuti in dui tipi. Custruitu, cù nomi "parlanti" (NewWord, NewSentence, Compose) è programmable.

I nomi programmabili cumincianu cù "Azione". Perchè TOML v1.4, i nomi cù punti devenu esse in virgulette.

Ogni rùbbrica deve esse descrittu quì sottu cù u listessu nome.

Per ùn sbattà a mente di a ghjente cù abitudini "nudi" (da l'esperienza, u so scriveforse unu di deci prufessiunali), I immediatamenti implementatu sintassi supplementari.

  • "OFF:" (o "ON:") prima di regexp (espressione regulare) richiede chì i seguenti buttoni sò liberati (o pressati).
    In seguitu, aghju da fà una espressione regulare "inghjusta". Cù cuntrollu separatu di pezzi trà i tubi "|". Per riduce u numeru di registri cum'è "[LR]_SHIFT" (induve questu hè chjaramente micca necessariu).
  • "SEQ:" Se a cundizione precedente hè cumpleta (o assente), allora cuntrollemu contru una espressione regulare "normale". Per i dettagli, mandu immediatamente à ^W a biblioteca "regexp". Perchè ùn aghju micca sempre scunfittatu di sapè u gradu di cumpatibilità cù u mo pcre preferitu ("perl compatible").
  • L'espressione hè scritta in a forma "BUTTON_1: CODE1, BUTTON_2: CODE2" etc., in l'ordine in quale i codici di scansione sò ricevuti.
  • U cuntrollu hè sempre "snugged" à a fine di a sequenza, cusì ùn ci hè bisognu di aghjunghje "$" à a cuda.
  • Tutti i cuntrolli in una linea sò realizati unu dopu l'altru è sò cumminati da "I". Ma postu chì u valore hè scrittu cum'è un array, pudete scrive un verificatu alternativu dopu à a virgola. S'ellu hè necessariu per una certa ragione.
  • valore "SeqLength = 8" limita a dimensione di u buffer contru à quale tutti i cuntrolli sò realizati. Perchè Aghju (finu à avà) mai scontru risorse infinite in a mo vita.

5. Setting the hooks discrittu in a sezione precedente

# 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 cosa principal hè quì "Azione = [Array]". Simile à a sezione precedente, ci hè un settore limitatu di azzioni integrate. È a pussibilità di docking ùn hè micca limitata in principiu (scrivite "Action.XXX" è ùn sia micca troppu pigro per scrive una altra sezione per questu).
In particulare, a retyping di una parolla in u layout currettu hè divisu in dui parti: "cambià u layout cum'è specificatu quì" и "riscrivite" ("Retype Word").

I paràmetri rimanenti sò scritti à u "dizziunariu" ("mappa" in golang) per una determinata azzione, a so lista dipende di ciò chì hè scrittu in "Azione".

Diversi azzioni diffirenti ponu esse descritte in un munzeddu (sezzioni). O pudete sparisce. Comu aghju dimustratu sopra.

Aghju stabilitu immediatamente l'azzione "Exec" per eseguisce u script esternu. Cù l'opzione di spinghja u buffer registratu in stdin.

  • "Aspittà = 1" - aspettate chì u prucessu in esecuzione finisci.
  • Probabilmente, "à u munzeddu" vi vulete mette più persone in l'ambiente. infurmazione cum'è u nome di a classa di finestra da quale hè stata interceptata.
    "Vulete cunnette u vostru gestore? Questu hè induve duvete andà ".

Phew (espiratu). Sembra chì ùn aghju micca scurdatu di nunda.

Oops! Iè, ùn aghju micca scurdatu ...
Induve hè a cunfigurazione di lanciamentu? In codice duru? Cusì:

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

Induve aghju scurdatu / sbagliatu ? (ùn modu senza questu), Spergu veramente chì i lettori attenti ùn saranu micca troppu pigri per chjappà u nasu.

Bona furtuna!

Source: www.habr.com

Add a comment