Xswitcher-yndielingskorrektor foar Linux: stap twa

sûnt foarige publikaasje (xswitcher op it poadium "proof of concept") krige in soad konstruktive feedback (wat is moai), Ik bleau myn frije tiid te besteegjen oan it ûntwikkeljen fan it projekt. No wol ik in bytsje fan jo... De twadde stap sil net hiel gewoan: útstel / diskusje fan konfiguraasje design.

Xswitcher-yndielingskorrektor foar Linux: stap twa

Op ien of oare manier docht it bliken dat normale programmeurs it ongelooflijk saai fine om al dizze kontrôles yn te stellen.

Om net ûnbegrûn te wêzen, binnen is in foarbyld fan wêr't ik mei dwaande bin.
Oer it algemien poerbêst betocht (en goed ymplementearre) Apache Kafka & ZooKeeper.
- Konfiguraasje? Mar it is saai! Stomme xml (omdat it "út 'e doaze" is).
- Och, wolle jo ek in ACL? Mar it is sa saai! Tap-blooper... Sokssawat.

Mar yn myn wurk is it krekt oarsom. Rjochts (ja, hast nea de earste kear) it konstruearre model lit jo maklik en natuerlik fierder trochgean (Hast) gearstalle in diagram.

Ik kaam koartlyn in artikel oer Habré oer it hurde wurk fan datawittenskippers ...
It docht bliken dat dit momint foar harren folslein realisearre is. En yn myn praktyk, sa't se sizze, "light ferzje". Modellen mei meardere folume, betûfte programmeurs mei OOP klear, ensfh. - dit sil allegear letter ferskine as / as it ôfnimt. Mar de ûntwerper moat hjir en no earne begjinne.

Kom ta de saak. Ik naam TOML as syntaktyske basis fan dizze boarger.

Want hy (TOML) oan 'e iene kant, minsklik bewurkber. Oan 'e oare kant wurdt it 1: 1 oerset yn ien fan' e meast foarkommende syntaksis: XML, JSON, YAML.
Boppedat is de ymplemintaasje dy't ik brûkte fan "github.com/BurntSushi/toml", hoewol net de meast modieuze (noch 1.4-syntaksis), is syntaktysk kompatibel mei deselde ("ynboude") JSON.

Dat is, as jo wolle, kinne jo gewoan sizze "gean troch de bosk mei dy TOML fan jo, ik wol XXX" en "patch" de koade mei mar ien rigel.

Dus, as jo wat finsters wolle skriuwe om xswitcher te konfigurearjen (Ik bin der net wis fan) Der wurde gjin problemen ferwachte "mei dizze ferdomme konfiguraasje fan jo."

Foar alle oaren is de syntaksis basearre op "kaai = wearde" (en letterlik in pear mear komplisearre opsjes, lykas = [guon, dat, array]) Tink ik
yntuïtyf handich.
Wat nijsgjirrich is is dat "ferbaarnd" om deselde tiid hinne (om 2013 hinne). Allinnich, oars as my, gie de skriuwer fan TOML op in goede skaal yn.

Dêrom is it no makliker foar my om de ymplemintaasje oan te passen oan mysels, en net oarsom.

Yn 't algemien nimme wy TOML (heul ferlykber mei de âlde Windows INI). En wy hawwe in konfiguraasje wêryn wy beskriuwe hoe't te heakjen in rige fan heakjes ôfhinklik fan de set fan de nijste scan koades fan it toetseboerd. Hjirûnder, stik foar stik, wat der oant no ta bard is. En in útlis oer wêrom't ik dizze manier besleat.

0. Basic Abstraksjes

  • Scan koade oantsjuttings. Hjir moat perfoarst wat oan dien wurde, om't gewoan digitale koades perfoarst net foar minsken lêsber binne (dat bin ik gewoan loloswitcher).
    Ik skodde "ecodes.go" út "golang-evdev" (ik wie te lui om nei de oarspronklike boarne te sjen, hoewol de skriuwer it frij kultureel oanjûn hat). Ik haw in bytsje (foar no) iets korrizjearre dat nochal bang wie. Like "LEFTBRACE" → "L_BRACE".
  • Dêrneist yntrodusearre hy it konsept fan "steatskaaien". Sûnt de reguliere grammatika brûkt gjin lange passaazjes. (Mar it lit jo kontrolearje mei minimale overhead. As jo ​​allinich "direkte" opname brûke.)
  • D'r sil in ynboude "deduplikator" wêze fan wat yndrukt wurdt. Sa sil de steat "werhelje"=2 skreaun wurde один tiid.

1. Templates seksje

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

Wêr bestiet in minsketaalwurd mei fonetyske notaasje? (of in kwestje fan grafemes aka "hieroglyfen")? In soarte fan ferskriklik "blêd". Dêrom yntrodusearje ik fuortendaliks it konsept fan "sjabloan".

2. Wat te dwaan as der op wat wurdt oanklikt (in oare scankoade is oankommen)

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

D'r binne yn totaal 768 koades. (Mar "foar it gefal" haw ik fangen "ferrassingen" ynfoege yn 'e xswitcher-koade).
Binnen ik beskreau it foljen fan de array mei keppelings nei funksjes "wat te dwaan". Yn golang is dit (ynienen) It die bliken handich en fanselssprekkend.

  • Ik plan te ferminderjen "Drop" ta in minimum op dit plak. Yn it foardiel fan fleksibeler ferwurking (ik sil it hjirûnder sjen litte).

3. Tabel mei finster klassen

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

De rigen fan 'e tafel binne yn dûbele fjouwerkante heakjes mei syn namme. Makliker koe it net west hawwe direkt út 'e bat. Ofhinklik fan it aktive finster kinne jo de folgjende opsjes selektearje:

  • Jo eigen set fan "sneltoetsen" "Actions = ...". As net / leech, neat dwaan.
  • Skeakelje "MouseClickDrops" - wat te dwaan as in mûsklik wurdt ûntdutsen. Sûnt op it punt wêr't xswitcher is ynskeakele, binne d'r gjin details oer "wêr't se klikke," sette wy de buffer standert werom. Mar yn terminals (bygelyks) jo hoege dit net te dwaan (gewoanwei).

4. Ien (of meardere) sekwinsjes fan klikken trigger ien of oare heak

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

Haken binne ferdield yn twa soarten. Ynboud, mei "sprekkende" nammen (NewWord, NewSentence, Compose) en programmeerber.

Programmierbere nammen begjinne mei "Aksje." Omdat TOML v1.4, nammen mei stippen moatte tusken aanhalingstekens stean.

Elke seksje moat hjirûnder beskreaun wurde mei deselde namme.

Om de geasten fan minsken net te blazen mei "neaken" reguliere (út ûnderfining, har skriuwemiskien ien op de tsien professionals), Ik ymplementearje fuortendaliks ekstra syntaksis.

  • "OFF:" (of "ON:") foardat regexp (reguliere útdrukking) fereaskje dat de folgjende knoppen wurde útbrocht (of yndrukt).
    Folgjende sil ik in "ûnearlike" reguliere útdrukking meitsje. Mei aparte kontrôle fan stikken tusken pipen "|". Om it oantal records lykas "[LR]_SHIFT" te ferminderjen (wêr't dit dúdlik net nedich is).
  • "SEQ:" As de foarige betingst foldien is (of ôfwêzich), dan kontrolearje wy tsjin in "normale" reguliere ekspresje. Foar details stjoer ik fuortendaliks nei ^W de "regexp" bibleteek. Om't ik noch net de muoite haw om de graad fan kompatibiliteit te finen mei myn favorite pcre ("perl-kompatibel").
  • De útdrukking is skreaun yn 'e foarm "BUTTON_1: CODE1, BUTTON_2: CODE2" ensfh., yn 'e folchoarder wêryn't de scan koades wurde ûntfongen.
  • De kontrôle wurdt altyd "snugged" oan 'e ein fan' e folchoarder, dus it is net nedich om "$" oan 'e sturt ta te foegjen.
  • Alle kontrôles yn ien rigel wurde ien nei de oare útfierd en wurde kombinearre troch "I". Mar om't de wearde wurdt beskreaun as in array, kinne jo in alternative kontrôle skriuwe nei de komma. As dit om ien of oare reden nedich is.
  • wearde "SeqLength = 8" beheint de grutte fan de buffer tsjin dêr't alle kontrôles wurde útfierd. Omdat Ik haw (oant no ta) nea einleaze boarnen yn myn libben tsjinkommen.

5. It ynstellen fan de heakjes beskreaun yn de foarige paragraaf

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

It wichtichste ding hjir is "Aksje = [Array]". Fergelykber mei de foarige seksje is d'r in beheinde set fan ynboude aksjes. En de mooglikheid fan docking is yn prinsipe net beheind (skriuw "Action.XXX" en wês net te lui om der in oare seksje foar te skriuwen).
Benammen it oertypen fan in wurd yn 'e korrizjearre yndieling is ferdield yn twa dielen: "feroarje de yndieling lykas dêr spesifisearre" и "retype" ("RetypeWord").

De oerbleaune parameters wurde skreaun nei it "wurdboek" ("kaart" yn golang) foar in opjûne aksje, harren list hinget ôf fan wat is skreaun yn "Aksje".

Ferskate ferskillende aksjes kinne wurde beskreaun yn ien heap (seksjes). Of jo kinne it útinoar lûke. Lykas ik hjirboppe toande.

Ik sette fuortendaliks de aksje "Exec" om it eksterne skript út te fieren. Mei de opsje om de opnommen buffer yn stdin te triuwen.

  • "Wachtsje = 1" - wachtsje oant it rinnende proses foltôge is.
  • Wierskynlik, "oan 'e heap" wolle jo ekstra minsken yn 'e omjouwing sette. ynformaasje lykas de namme fan 'e finsterklasse dêr't it ûnderskept waard.
    "Wolle jo jo handler ferbine? Dit is wêr't jo hinne moatte."

Pff (útblaasd). It liket derop dat ik neat fergetten bin.

Oops! Ja, ik fergeat net ...
Wêr is de startkonfiguraasje? Yn hurde koade? Fyn dat leuk:

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

Wêr bin ik fergetten/fersin makke? (gjin manier sûnder dit), Ik hoopje echt dat oandachtige lêzers net te lui wêze sille om har noas te stekken.

Súkses!

Boarne: www.habr.com

Add a comment