Korrigjuesi i paraqitjes Xswitcher për Linux: hapi i dytë

Si publikimi i mëparshëm (xswitcher në fazën e "provës së konceptit") mori mjaft reagime konstruktive (e cila eshte e bukur), vazhdova ta kaloj kohën time të lirë duke zhvilluar projektin. Tani dua të shpenzoj pak nga ju... Hapi i dytë nuk do të jetë mjaft i zakonshëm: propozimi/diskutimi i dizajnit të konfigurimit.

Korrigjuesi i paraqitjes Xswitcher për Linux: hapi i dytë

Në një farë mënyre rezulton se programuesit normalë e shohin tepër të mërzitshëm vendosjen e të gjitha këtyre kontrolleve.

Për të mos qenë e pabazë, brenda është një shembull i asaj që kam të bëj.
Në përgjithësi i konceptuar në mënyrë të shkëlqyer (dhe i zbatuar mirë) Apache Kafka & ZooKeeper.
- Konfigurimi? Por është e mërzitshme! Xml memece (sepse është "jashtë kutisë").
- Oh, a do edhe ti një ACL? Por është kaq e mërzitshme! Tap-blooper... Diçka e tillë.

Por në punën time është pikërisht e kundërta. E drejta (mjerisht, pothuajse kurrë herën e parë) modeli i ndërtuar ju lejon të vazhdoni më tej lehtësisht dhe natyrshëm (Pothuajse) mblidhni një diagram.

Kohët e fundit kam hasur në një artikull në Habré rreth punës së palodhur të shkencëtarëve të të dhënave...
Rezulton se ky moment është realizuar plotësisht për ta. Dhe në praktikën time, siç thonë ata, "versioni i lehtë". Modele me shumë vëllime, programues me përvojë me OOP gati, etj. — e gjithë kjo do të shfaqet më vonë kur/nëse hiqet. Por projektuesi duhet të fillojë diku këtu dhe tani.

Shkoni te pika. Kam marrë TOML si bazë sintaksore nga ky shtetas.

Sepse ai (TOML) nga njëra anë, e redaktueshme nga njeriu. Nga ana tjetër, përkthehet 1:1 në ndonjë nga sintaksat më të zakonshme: XML, JSON, YAML.
Për më tepër, zbatimi që përdora nga "github.com/BurntSushi/toml", megjithëse jo më i modës (ende sintaksa 1.4), është sintaksisht i pajtueshëm me të njëjtin JSON ("të integruar").

Kjo do të thotë, nëse dëshironi, thjesht mund të thoni "kaloni nëpër pyll me atë TOML-in tuaj, unë dua XXX" dhe "patch" kodin me vetëm një rresht.

Kështu, nëse doni të shkruani disa dritare për të konfiguruar xswitcher (Nuk jam i sigurt) Nuk priten probleme "me këtë konfigurim të mallkuar tuajin".

Për të gjithë të tjerët, sintaksa bazohet në "çelës = vlerë" (dhe fjalë për fjalë disa opsione më të komplikuara, si = [disa, ajo, grup]) Unë mendoj
i përshtatshëm në mënyrë intuitive.
Ajo që është interesante është se "e djegur" afërsisht në të njëjtën kohë (rreth vitit 2013). Vetëm se, ndryshe nga unë, autori i TOML-it hyri në shkallën e duhur.

Prandaj, tani është më e lehtë për mua të rregulloj zbatimin e tij për t'iu përshtatur vetes, dhe jo anasjelltas.

Në përgjithësi, ne marrim TOML (shumë i ngjashëm me Windows INI të vjetër). Dhe ne kemi një konfigurim në të cilin përshkruajmë se si të bashkojmë një seri grepash në varësi të grupit të kodeve më të fundit të skanimit nga tastiera. Më poshtë, pjesë-pjesë, është ajo që ka ndodhur deri tani. Dhe një shpjegim se pse vendosa në këtë mënyrë.

0. Abstraksionet bazë

  • Skanoni emërtimet e kodeve. Diçka duhet bërë patjetër për këtë, pasi thjesht kodet dixhitale nuk janë absolutisht të lexueshme nga njeriu (ky jam vetëm unë loloswitcher).
    E shkunda "ecodes.go" nga "golang-evdev" (u bë shumë dembel të shikoja burimin origjinal, megjithëse autori e tregoi atë mjaft kulturalisht). E korrigjova pak (për momentin) diçka që ishte shumë e frikshme. Si "LEFTBRACE" → "L_BRACE".
  • Për më tepër, ai prezantoi konceptin e "çelësave të shtetit". Meqenëse gramatika e rregullt e përdorur nuk lejon pasazhe të gjata. (Por ju lejon të kontrolloni me shpenzime minimale. Nëse përdorni vetëm regjistrimin "direkt".)
  • Do të ketë një "zhdukës" të integruar të asaj që shtypet. Kështu do të shkruhet gjendja "përsërit"=2 një kohë.

1. Seksioni i shablloneve

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

Nga se përbëhet një fjalë e gjuhës njerëzore me shënim fonetik? (ose një çështje grafemash të njohura si "hieroglife")? Një lloj "fletë" e tmerrshme. Prandaj, unë prezantoj menjëherë konceptin e "shabllonit".

2. Çfarë duhet të bëni kur klikohet diçka (ka mbërritur një kod tjetër skanimi)

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

Janë 768 kode në total. (Por "vetëm në rast" futa kapjen e "surprizave" në kodin xswitcher).
Brenda përshkrova mbushjen e grupit me lidhje me funksionet "çfarë duhet të bëni". Në golang kjo është (papritur) Doli të ishte e përshtatshme dhe e dukshme.

  • Kam në plan të reduktoj "Drop" në minimum në këtë vend. Në favor të përpunimit më fleksibël (do ta tregoj më poshtë).

3. Tabela me klasat e dritareve

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

Rreshtat e tabelës janë në kllapa të dyfishta katrore me emrin e saj. Nuk mund të ishte më e lehtë menjëherë. Në varësi të dritares aktuale aktive, mund të zgjidhni opsionet e mëposhtme:

  • Seti juaj i "çelësave të nxehtë" "Veprimet = ...". Nëse jo/bosh, mos bëni asgjë.
  • Ndërroni "MouseClickDrops" - çfarë të bëni kur zbulohet një klikim i miut. Meqenëse në pikën ku xswitcher është i ndezur nuk ka detaje rreth "ku klikojnë", ne rivendosim bufferin si parazgjedhje. Por në terminale (për shembull) nuk duhet ta bëni këtë (zakonisht).

4. Një (ose disa) sekuenca klikimesh shkaktojnë një ose një goditje tjetër

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

Grepa ndahen në dy lloje. I integruar, me emra "të folur" (NewWord, NewSentence, Compose) dhe i programueshëm.

Emrat e programueshëm fillojnë me "Veprim". Sepse TOML v1.4, emrat me pika duhet të jenë në thonjëza.

Çdo seksion duhet të përshkruhet më poshtë me të njëjtin emër.

Për të mos i fryrë mendjet njerëzve me rregulltarë "të zhveshur" (nga përvoja, e tyre për të shkruarndoshta një në dhjetë profesionistëve), Unë zbatoj menjëherë sintaksë shtesë.

  • "OFF:" (ose "ON:") përpara se regexp (shprehje e rregullt) të kërkojë që butonat e mëposhtëm të lëshohen (ose të shtypen).
    Më pas do të bëj një shprehje të rregullt "të padrejtë". Me kontroll të veçantë të pjesëve ndërmjet tubave "|". Për të zvogëluar numrin e regjistrimeve si "[LR]_SHIFT" (ku kjo nuk është e nevojshme).
  • "SEQ:" Nëse kushti i mëparshëm plotësohet (ose mungon), atëherë kontrollojmë kundër një shprehjeje të rregullt "normale". Për detaje, dërgoj menjëherë në ^W bibliotekën "regexp". Sepse ende nuk jam shqetësuar të zbuloj shkallën e përputhshmërisë me pcre-n time të preferuar ("perl compatible").
  • Shprehja shkruhet në formë "BUTTON_1: CODE1, BUTTON_2: CODE2" etj., sipas radhës në të cilën janë marrë kodet e skanimit.
  • Kontrolli është gjithmonë "mbytur" në fund të sekuencës, kështu që nuk ka nevojë të shtoni "$" në bisht.
  • Të gjitha kontrollet në një rresht kryhen njëri pas tjetrit dhe kombinohen me "Unë". Por meqenëse vlera përshkruhet si një grup, mund të shkruani një kontroll alternativ pas presjes. Nëse kjo është e nevojshme për ndonjë arsye.
  • Vlerë "Gjatësia seq = 8" kufizon madhësinë e tamponit kundrejt të cilit kryhen të gjitha kontrollet. Sepse Unë (deri më tani) nuk kam hasur kurrë burime të pafundme në jetën time.

5. Vendosja e grepave të përshkruara në seksionin e mëparshëm

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

Gjëja kryesore këtu është "Veprim = [Array]". Ngjashëm me seksionin e mëparshëm, ekziston një grup i kufizuar veprimesh të integruara. Dhe mundësia e dokimit nuk është e kufizuar në parim (shkruani "Action.XXX" dhe mos jini shumë dembel për të shkruar një seksion tjetër për të).
Në veçanti, rishtypja e një fjale në paraqitjen e korrigjuar ndahet në dy pjesë: "Ndrysho paraqitjen siç specifikohet atje" и "Ritype" ("RitypeWord").

Parametrat e mbetur shkruhen në "fjalor" ("hartë" në golang) për një veprim të caktuar, lista e tyre varet nga ajo që shkruhet në "Veprim".

Disa veprime të ndryshme mund të përshkruhen në një grumbull (seksione). Ose mund ta shkëputni. Siç e tregova më lart.

Vendosa menjëherë veprimin "Exec" për të ekzekutuar skriptin e jashtëm. Me opsionin për të shtyrë buferin e regjistruar në stdin.

  • "Prit = 1" - prisni që procesi i ekzekutimit të përfundojë.
  • Ndoshta, "deri në grumbull" do të dëshironi të vendosni njerëz shtesë në mjedis. informacion të tillë si emri i klasës së dritares nga e cila është përgjuar.
    “A doni të lidhni mbajtësin tuaj? Këtu duhet të shkoni”.

Phew (shpirti). Më duket se nuk kam harruar asgjë.

Oops! Po, nuk harrova ...
Ku është konfigurimi i nisjes? Në kod të vështirë? Kështu:

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

Ku harrova/gabova? (nuk ka mundesi pa kete), Unë me të vërtetë shpresoj që lexuesit e vëmendshëm të mos përtojnë të thejnë hundët.

Good luck!

Burimi: www.habr.com

Shto një koment