Xswitcher išdėstymo korektorius, skirtas Linux: antras žingsnis

Kaip ankstesnis leidinys (xswitcher „koncepcijos įrodymo“ stadijoje) sulaukė gana daug konstruktyvių atsiliepimų (kas yra gražu), ir toliau laisvalaikį leisdavau kurdamas projektą. Dabar noriu išleisti šiek tiek jūsų... Antrasis žingsnis bus ne visai įprastas: pasiūlymas/diskusija dėl konfigūracijos dizaino.

Xswitcher išdėstymo korektorius, skirtas Linux: antras žingsnis

Kažkaip išeina, kad paprastiems programuotojams neįtikėtinai nuobodu nustatyti visus šiuos valdiklius.

Kad nebūtų be pagrindo, viduje yra pavyzdys, su kuo aš turiu reikalų.
Apskritai puikiai sumanyta (ir gerai įgyvendinta) Apache Kafka & ZooKeeper.
- Konfigūracija? Bet tai nuobodu! Kvailas xml (nes jis „iš dėžutės“).
- O, ar tu taip pat nori ACL? Bet tai taip nuobodu! Tap-blooper... Kažkas panašaus.

Bet mano darbe yra visiškai priešingai. Teisingai (deja, beveik niekada pirmą kartą) sukonstruotas modelis leidžia lengvai ir natūraliai tęsti toliau (Beveik) surinkti diagramą.

Neseniai aptikau straipsnį apie Habré apie sunkų duomenų mokslininkų darbą...
Pasirodo, ši akimirka jiems pilnai realizuota. Ir mano praktikoje, kaip sakoma, „lengva versija“. Kelių tūrių modeliai, patyrę programuotojai su paruoštu OOP ir kt. — visa tai pasirodys vėliau, kai/jei jis pakils. Tačiau dizaineris turi pradėti kažkur čia ir dabar.

Eikite į esmę. TOML pasirinkau kaip sintaksinį pagrindą nuo šio piliečio.

Nes jis (TOML) viena vertus, žmogaus redaguojamas. Kita vertus, jis 1:1 verčiamas į bet kurią iš įprastų sintaksių: XML, JSON, YAML.
Be to, įdiegimas, kurį naudojau iš „github.com/BurntSushi/toml“, nors ir nėra pats madingiausias (vis dar 1.4 sintaksė), yra sintaksiškai suderinamas su tuo pačiu („įtaisytuoju“) JSON.

Tai yra, jei norite, galite tiesiog pasakyti „eik per mišką su tuo savo TOML, aš noriu XXX“ ir „pataisyti“ kodą tik viena eilute.

Taigi, jei norite parašyti kai kuriuos langus, kad sukonfigūruotumėte xswitcher (Aš nesu tikras) Nesitikima jokių problemų su šia prakeikta jūsų konfigūracija.

Visiems kitiems sintaksė pagrįsta „raktas = vertė“ (ir pažodžiui keletas sudėtingesnių parinkčių, pvz., = [kai kurie, tai, masyvas]) spėju
intuityviai patogu.
Įdomiausia tai "sudegęs" maždaug tuo pačiu metu (apie 2013 m.). Tik, skirtingai nei aš, TOML autorius įėjo tinkamu mastu.

Todėl dabar man lengviau pritaikyti jo įgyvendinimą sau, o ne atvirkščiai.

Apskritai, mes naudojame TOML (labai panašų į seną Windows INI). Ir mes turime konfigūraciją, kurioje aprašome, kaip pritvirtinti kabliukų seriją, atsižvelgiant į naujausių nuskaitymo kodų rinkinį iš klaviatūros. Žemiau gabalas po gabalo yra tai, kas nutiko iki šiol. Ir paaiškinimas, kodėl taip nusprendžiau.

0. Pagrindinės abstrakcijos

  • Nuskaitykite kodų pavadinimus. Būtinai reikia ką nors padaryti, nes tiesiog skaitmeniniai kodai yra visiškai neįskaitomi žmonėms (tai tik aš loloswitcher).
    Iš "golang-evdev" iškračiau "ecodes.go" (aš tingėjau pažvelgti į pirminį šaltinį, nors autorius tai nurodė gana kultūringai). Aš šiek tiek pataisiau (kol kas) tai, kas buvo gana baisu. Kaip „LEFTBRACE“ → „L_BRACE“.
  • Be to, jis pristatė „būsenos raktų“ sąvoką. Kadangi naudojama įprasta gramatika neleidžia ilgų ištraukų. (Tačiau tai leidžia patikrinti su minimaliomis išlaidomis. Jei naudojate tik „tiesioginį“ įrašymą.)
  • Bus įmontuotas „dedublikatorius“, kas paspausta. Taigi bus parašyta būsena "pakartoti"=2 vienas kartų.

1. Šablonų skyrius

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

Iš ko susideda žmonių kalbos žodis su fonetine žyma? (arba grafemos, dar žinomos kaip „hieroglifai“)? Kažkoks baisus „paklodė“. Todėl iš karto pristatau „šablono“ sąvoką.

2. Ką daryti, kai kažkas paspaudžiamas (gautas kitas nuskaitymo kodas)

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

Iš viso yra 768 kodai. (Bet „tik tuo atveju“ į xswitcher kodą įterpiau gaudymo „staigmenas“).
Viduje aprašiau masyvo užpildymą nuorodomis į funkcijas „ką daryti“. Golange tai yra (staiga) Tai pasirodė patogu ir akivaizdu.

  • Šioje vietoje planuoju „Drop“ sumažinti iki minimumo. Už lankstesnį apdorojimą (tai parodysiu žemiau).

3. Stalas su langų klasėmis

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

Lentelės eilutės yra dvigubuose laužtiniuose skliaustuose su jos pavadinimu. Lengviau iš karto negalėjo būti. Atsižvelgiant į šiuo metu aktyvų langą, galite pasirinkti šias parinktis:

  • Jūsų pačių „karštųjų klavišų“ rinkinys „Veiksmai =…“. Jei ne/tuščia, nieko nedarykite.
  • Perjunkite „MouseClickDrops“ – ką daryti, kai aptinkamas pelės paspaudimas. Kadangi xswitcher įjungimo vietoje nėra informacijos apie tai, kur jie spusteli, buferį nustatome iš naujo pagal numatytuosius nustatymus. Bet terminaluose (pavyzdžiui) to daryti nereikia (dažniausiai).

4. Viena (ar kelios) paspaudimų sekos paleidžia vieną ar kitą kabliuką

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

Kabliukai skirstomi į du tipus. Integruotas, su „kalbančiais“ pavadinimais (NewWord, NewSentence, Compose) ir programuojamas.

Programuojami pavadinimai prasideda žodžiais „Veiksmas“. Nes TOML v1.4, pavadinimai su taškais turi būti kabutėse.

Kiekvienas skyrius turėtų būti aprašytas toliau tuo pačiu vardu.

Kad nepūstų žmonių galvos „nuogiais“ nuolatiniais lankytojais (iš patirties, jų Rašytigal vienas iš dešimties profesionalai), iš karto įdiegiu papildomą sintaksę.

  • „OFF:“ (arba „ON:“) prieš regexp (reguliarioji išraiška) reikalauja atleisti (arba paspausti) šiuos mygtukus.
    Toliau aš padarysiu „nesąžiningą“ reguliarųjį posakį. Atskirai tikrinant dalis tarp vamzdžių "|". Siekiant sumažinti įrašų, pvz., „[LR]_SHIFT“ skaičių (kur tai tikrai nėra būtina).
  • "SEQ:" Jei ankstesnė sąlyga yra įvykdyta (arba jos nėra), patikriname pagal „įprastą“ reguliariąją išraišką. Norėdami gauti daugiau informacijos, iš karto siunčiu į ^W „regexp“ biblioteką. Nes aš vis dar nesivarginau išsiaiškinti suderinamumo su savo mėgstamu pcre ("perl suderinama") laipsnio.
  • Išraiška parašyta forma "BUTTON_1: CODE1, BUTTON_2: CODE2" ir tt nuskaitymo kodų gavimo tvarka.
  • Čekis visada „prigludęs“ prie sekos pabaigos, todėl nereikia pridėti „$“ prie uodegos.
  • Visi patikrinimai vienoje eilutėje atliekami vienas po kito ir yra sujungti „aš“. Tačiau kadangi reikšmė apibūdinama kaip masyvas, po kablelio galite parašyti alternatyvų patikrinimą. Jei dėl kokių nors priežasčių to reikia.
  • Vertė "SeqLength = 8" riboja buferio, pagal kurį atliekami visi patikrinimai, dydį. Nes Aš (iki šiol) niekada gyvenime nesusidūriau su begaliniais ištekliais.

5. Ankstesniame skyriuje aprašytų kabliukų nustatymas

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

Svarbiausia čia yra "Veiksmas = [masyvas]". Panašiai kaip ir ankstesniame skyriuje, yra ribotas integruotų veiksmų rinkinys. O prijungimo galimybė iš esmės nėra ribojama (parašykite „Action.XXX“ ir nepatingėkite parašyti jam kitą skyrių).
Visų pirma, žodžio pakartotinis įvedimas pataisytame išdėstyme yra padalintas į dvi dalis: „pakeisti išdėstymą, kaip nurodyta ten“ и „Retype“ („RetypeWord“).

Likę parametrai įrašomi į „žodyną“ („žemėlapis“ golango k.) tam tikram veiksmui jų sąrašas priklauso nuo to, kas parašyta „Veiksme“.

Vienoje krūvoje galima aprašyti kelis skirtingus veiksmus (skyriai). Arba galite jį atskirti. Kaip parodžiau aukščiau.

Iš karto nustatiau veiksmą „Vykdyti“, kad paleisčiau išorinį scenarijų. Su galimybe perkelti įrašytą buferį į stdin.

  • „Palauk = 1“ – palaukite, kol bus baigtas vykdomas procesas.
  • Tikriausiai „į krūvą“ norėsite į aplinką įdėti papildomų žmonių. informacija, pvz., lango klasės, iš kurios buvo perimta, pavadinimas.
    „Ar norite prijungti savo prižiūrėtoją? Štai kur jums reikia eiti“.

Fu (iškvėpė). Atrodo, nieko nepamiršau.

Oi! Taip, nepamiršau...
Kur yra paleidimo konfigūracija? Kietajame kode? Šitaip:

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

Kur aš pamiršau/padariau klaidą? (be šito jokiu būdu), labai tikiuosi, kad dėmesingi skaitytojai netingės kišti nosies.

Sėkmės!

Šaltinis: www.habr.com

Добавить комментарий