Xswitcher-asettelukorjaus linuxille: vaihe kaksi

Kuin edellinen julkaisu (xswitcher proof of concept -vaiheessa) sai melko paljon rakentavaa palautetta (mikä on kivaa), jatkoin vapaa-ajan viettämistä projektin kehittämiseen. Nyt haluan viettää osan... Toinen vaihe ei ole aivan tuttu: ehdotus / keskustelu kokoonpanon suunnittelusta.

Xswitcher-asettelukorjaus linuxille: vaihe kaksi

Jotenkin käy ilmi, että tavallisille ohjelmoijille on hurjan tylsää tehdä kaikkia näitä käänteitä.

Jotta ei olisi perusteetonta, sisällä on esimerkki siitä, mitä olen tekemisissä.
Upea kokonaisuus suunniteltu (ja hyvin toteutettu) Apache Kafka & ZooKeeper.
— Kokoonpano? Mutta se on tylsää! Tyap-bloop xml (koska "out of the box").
- Ai, haluatko myös ACL:n? Mutta se on niin ärsyttävää! Napautusvirhe... Jotain sellaista.

Omassa työssäni asia on juuri päinvastoin. Oikein (valitettavasti ensimmäistä kertaa lähes koskaan) rakennettu malli mahdollistaa eteenpäin helposti ja luonnollisesti (Melkein) koota kaavio.

Äskettäin törmäsin Habrén artikkeliin datatieteilijöiden kovasta työstä ...
Osoittautuu, että tällä hetkellä ne ovat täysin toteutuneet. Ja käytännössä, kuten sanotaan, "kevyt versio". Moniosaiset mallit, kokeneet ohjelmoijat OOP:lla valmiina jne. - kaikki tämä näkyy myöhemmin, kun / jos se lähtee liikkeelle. Ja suunnittelijan on aloitettava jostain tässä ja nyt.

Mennä asiaan. Syntaktiseksi pohjaksi otin TOML:n tältä kansalaiselta.

Koska hän (TOML) toisaalta ihmisen muokattavissa. Toisaalta se käännetään 1:1 mihin tahansa yleisimmistä syntakseista: XML, JSON, YAML.
Lisäksi osoitteesta "github.com/BurntSushi/toml" käyttämäni toteutus, vaikka se ei ole muodikkain (vielä syntaksi 1.4), on syntaktisesti yhteensopiva saman ("upotetun") JSON:n kanssa.

Eli jos haluat, voit yksinkertaisesti sanoa "mene metsään tällä TOML:lläsi, haluan XXX" ja "paikata" koodin yhdellä rivillä.

Joten, jos haluat, kirjoita joitain ikkunoita xswitcherin määrittämiseksi (En ole varma) ongelmia "vitun konfiguraatiosi kanssa" ei odoteta.

Kaikille muille syntaksi perustuu "avain = arvo" (ja kirjaimellisesti pari monimutkaisempaa vaihtoehtoa, kuten = [jonkinlainen array]) Luulen
intuitiivisesti kätevä.
Kummallista kyllä, aivan "palanut" suunnilleen samaan aikaan (noin 2013). Vain, toisin kuin minä, TOML:n kirjoittaja meni suuressa mittakaavassa.

Siksi nyt minun on helpompi säätää sen toteutusta itselleni, eikä päinvastoin.

Yleensä otamme TOML:n (hyvin samanlainen kuin vanha Windows INI). Ja meillä on kokoonpano, jossa kuvailemme kuinka kiinnittää sarja koukkuja näppäimistön uusimpien skin-koodien mukaan. Alla palasina - mitä tapahtui tällä hetkellä. Ja selitys miksi päätin niin.

0. Perusabstraktiot

  • Skannaa koodimerkinnät. Tälle on tehtävä jotain, koska pelkät digitaaliset koodit eivät ole ihmisen luettavissa (tämä olen minä puutarhassa loloswitcher).
    Puristin sanan "ecodes.go" sanasta "golang-evdev" (olin liian laiska menemään lähteeseen, vaikka kirjoittajalla on se varsin kulttuurisesti merkitty). Hieman (toistaiseksi) korjannut erittäin pelottavaa. Kirjoita "LEFTBRACE" → "L_BRACE".
  • Lisäksi hän esitteli käsitteen "avaimet tilalla". Koska käytetty säännöllinen kielioppi ei edistä pitkiä kohtia. (Mutta sen avulla voit tarkistaa minimaalisella lisäkululla. Jos käytät vain "suoraa" tallennusta.)
  • Siellä on sisäänrakennettu "vähennys" napsautetun yhden. Joten tila "toista" = 2 kirjoitettaisiin yksi aikaa.

1. Malliosio

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

Mistä foneettisella merkinnällä varustettu ihmiskielen sana koostuu? (Onko kyse grafeemista eli "hieroglyfeistä")? Jonkinlainen kauhea "lakka". Siksi luon heti "mallin" käsitteen.

2. Mitä tehdä, kun jotain napsautetaan (toinen skannauskoodi on saapunut)

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

Koodeja on yhteensä 768. (Mutta "varmuuden vuoksi" lisäsin xswitcher-koodiin "yllätyksiä").
Sisälle maalasin täyttämällä taulukon linkeillä "mitä tehdä" -toimintoihin. Golangissa tämä on (yhtäkkiä) osoittautui käteväksi ja ilmeiseksi.

  • "Drop" tässä paikassa aion vähentää minimiin. Joustavamman käsittelyn puolesta (näytän alla).

3. Taulukko ikkunaluokineen

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

Taulukon rivit ovat kaksoishakasulkeissa sen nimen kanssa. Se ei sujunut yhtään helpommin. Tällä hetkellä aktiivisesta ikkunasta riippuen voit valita vaihtoehdot:

  • Omat "pikanäppäimet" "Toiminnot = ...". Jos ei/tyhjä, älä tee mitään.
  • MouseClickDrops-kytkin – mitä tehdä, kun hiiren napsautus havaitaan. Koska xswitcherin käynnistyspiste ei sisällä mitään tietoja siitä, missä sitä napsautetaan, oletusarvoisesti nollaamme puskurin. Mutta terminaaleissa (esimerkiksi) et voi tehdä tätä (yleensä).

4. Yksi (tai useampi) napsautussarja laukaisee yhden tai toisen koukun

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

Koukut on jaettu kahteen tyyppiin. Sisäänrakennettu, "puhuvilla" nimillä (NewWord, NewSentence, Compose) ja ohjelmoitava.

Ohjelmoitavat nimet alkavat sanalla "Action". Koska TOML v1.4, pisteillä varustettujen nimien tulee olla lainausmerkeissä.

Jokainen osa tulee kuvata alla. samalla nimellä.

Jotta ihmisten aivot ei räjähtäisi "alastomilla" säännöllisillä kausilla (kokemuksen mukaan heidän kirjoittaaehkä yksi kymmenestä ammattilaisille), ota välittömästi käyttöön lisäsyntaksi.

  • "OFF:" (tai "ON:") ennen regexp (säännöllinen lauseke) edellyttävät, että seuraavat painikkeet vapautetaan (tai painetaan).
    Seuraavaksi aion tehdä "epärehellisen" säännöllisen lausekkeen. Erillinen kappaleiden tarkistus "|" putkien välissä. Tietueiden määrän vähentämiseksi, kuten "[LR]_SHIFT" (jos se ei selvästikään ole välttämätöntä).
  • SEQ: Jos edellinen ehto täyttyy (tai puuttuu), tarkistamme "tavanomaisen" säännöllisen lausekkeen. Yksityiskohtia varten lähetän välittömästi osoitteeseen ^W "regexp"-kirjastoon. Koska hän ei ole vieläkään vaivautunut selvittämään yhteensopivuusastetta suosikkini pcre:n kanssa ("perl-yhteensopiva").
  • Lauseke kirjoitetaan muodossa "BUTTON_1: CODE1, BUTTON_2: CODE2" jne. skannauskoodien saapumisjärjestyksessä.
  • Validointia "painataan" aina sekvenssin loppuun, joten "$" ei tarvitse lisätä häntään.
  • Kaikki yhden rivin tarkistukset suoritetaan peräkkäin ja niitä yhdistää "minä". Mutta koska arvo on kuvattu taulukkona, voit kirjoittaa vaihtoehtoisen tarkistuksen pilkun jälkeen. Jos se on jostain syystä tarpeen.
  • Arvo "SeqLength=8" rajoittaa puskurin kokoa, jota vastaan ​​kaikki tarkistukset suoritetaan. Koska En ole (toistaiseksi) nähnyt loputtomia resursseja elämässäni.

5. Edellisessä osiossa kuvattu koukkujen asettaminen

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

Pääasia tässä on "Toiminto = [Matriisi]". Kuten edellisessä osiossa, sisäänrakennettuja toimintoja on rajoitettu määrä. Ja telakointimahdollisuus, jota ei periaatteessa rajoiteta (kirjoita "Action.XXX" äläkä ole liian laiska maalaamaan sille toista osaa).
Erityisesti sanojen kirjoittaminen korjatussa asettelussa on jaettu kahteen osaan: "Muuta asettelua tuolla annetulla tavalla" и "kirjoita uudelleen" ("RetypeWord").

Loput parametrit kirjoitetaan "sanakirjaan" ("kartta" golangilla) tietyn toiminnon osalta niiden luettelo riippuu siitä, mitä "Toiminto" on kirjoitettu.

Yhdessä kasassa voidaan kuvata useita eri toimintoja (osioita). Ja voit purkaa sen osiin. Kuten edellä esitin.

Aseta välittömästi toiminto "Exec" - suorita ulkoinen komentosarja. Mahdollisuus työntää kirjoitettu puskuri stdiniin.

  • "Odota = 1" - odota käynnissä olevan prosessin päättymistä.
  • Todennäköisesti "kasaan" haluat laittaa lisää ympäristöön. tietoja, kuten sen ikkunan luokan nimi, josta se siepattiin.
    "Haluatko yhdistää käsittelijän? Täällä sinä olet."

Huh (hengitän ulos). En näköjään unohtanut mitään.

Op! Joo, ei unohtunut...
Missä käynnistysasetukset ovat? Kovakoodissa? Näin:

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

Missä unohdin/erehdyin? (ei mitenkään ilman sitä), Toivon todella, että tarkkaavaiset lukijat eivät ole liian laiskoja pistämään nenään.

Onnea!

Lähde: will.com

Lisää kommentti