Izvietojumu korektors «хswitcherĀ» Linux: otrais solis

Tā kā iepriekŔējā publikācija (hSwitcher paÅ”laik ir Ā«pierādÄ«juma koncepcijā») saņēma diezgan daudz konstruktÄ«vu atsauksmju (kas ir patÄ«kami), es turpināju tērēt savu brÄ«vo laiku projekta attÄ«stÄ«bai. Tagad vēlos tērēt nedaudz no jÅ«su... Otrais solis bÅ«s nedaudz neierastāks: piedāvājums/apsprieÅ”ana dizaina konfigurācijai.

Izvietojumu korektors «хswitcherĀ» Linux: otrais solis

Kā mūždien, izrādās, normāliem programmētājiem ir ļoti garlaicÄ«gi regulēt visus Å”os slēdžus.

Lai nebÅ«tu balsojumi, iekŔā ir piemērs no tā, ar ko nodarbojos.
Lieliski vispār iecerētie (un labi realizētie) Apache Kafka & ZooKeeper.
— Konfigurācija? Bet tas taču ir garlaicÄ«gi! Ātri – ātri xml (jo Ā«no kastesĀ»).
— Ak, bet vai jÅ«s vēlaties arÄ« ACL? Bet tas ir tik apgrÅ«tinoÅ”i! TÅ«lÄ«t… Kā kaut kā tā.

Bet manā darbā – tieÅ”i pretēji. Pareizi (žēl, ka pirmo reizi gandrÄ«z nekad nav) izveidotā modelis ļauj tālāk viegli un neuzkrÄ«toÅ”i (nu, gandrÄ«z) izveidot shēmu.

Nesen Habrā uzdūros rakstam par datu zinātnieku grūto darbu...
Izrādās, Å”is moments viņiem tiek Ä«stenots pilnā mērā. Un manā praksē, kā saka, Ā«gaismas versijaĀ». Daudzsēriju modeļi, pieredzējuÅ”i programmētāji ar OOP rokās utt. – tas viss parādÄ«sies, kad/ja pacelsies. Bet konstruktors tam ir jāuzsāk tagad.

Tuvāk lietai. Kā sintaktisko bāzi es izvēlējos TOML no Ŕī pilsoņa.

Jo tas (TOML) no vienas puses ir cilvēkiem rediģējams. Bet no otras puses – tiek pārveidots 1:1 jebkurā no izplatÄ«tākajām sintaksēm: XML, JSON, YAML.
Turklāt, mana izmantotā Ä«stenoÅ”ana no Ā«github.com/BurntSushi/tomlĀ» nav ne modÄ«gākā (joprojām sintakse 1.4), tomēr sintaktiski saderÄ«ga ar to paÅ”u (Ā«iebÅ«vētoĀ») JSON.

Tas ir, ja vēlaties, varat vienkārÅ”i teikt Ā«Ei mežā ar Å”o tavu TOML, es vēlos XXXĀ» un Ā«izmainÄ«tĀ» kodu vienā rindā.

Tādējādi, ja vēlaties uzrakstÄ«t kaut kādas logus hSwitcher iestatīŔanai (definitÄ«vi ne es) problēmu Ā«ar Å”o jÅ«su nolādēto konfigurācijuĀ» – nenotiek.

Visiem citiem sintakse, kas balstās uz Ā«atslēga = vērtÄ«baĀ» (un burtiski ar pāris sarežģītākām opcijām, piemēram, = [kādu, masÄ«vu]) uzskatu
par intuitīvi ērtu.
Kas interesanti, manam Ā«uzkarsaĀ» apmēram tajā paŔā laikā (ap 2013. gadu). Tikai, atŔķirÄ«bā no manis, TOML autors piegāja ar atbilstoÅ”u apjomu.

Tāpēc tagad man ir vieglāk pielāgot tā Ä«stenoÅ”anu sev, nevis otrādi.

Tātad, ņemam TOML (ļoti lÄ«dzÄ«gs vecajiem Windows INI). Un sastādam konfigurāciju, kurā aprakstām, kā pievienot sēriju āķu atkarÄ«bā no pēdējo taustiņu kodu kopuma no tastatÅ«ras. Zemāk pa fragmentiem — tas, kas Å”obrÄ«d ir izveidots, un paskaidrojumi, kāpēc es tā izlēmu.

0. Pamata abstrakcijas

  • Taustiņu kodu apzÄ«mējumi. Ar to noteikti jādara kaut kas, jo vienkārÅ”i ciparu kodi ir absolÅ«ti necilvēcÄ«gi lasāmi (to es saku metaforiski). loloswitcher).
    Izguvu Ā«ecodes.goĀ» no Ā«golang-evdevĀ» (uz pirmavotu negribēju iet, lai arÄ« autorā tas ir norādÄ«ts ļoti pieklājÄ«gi). Nedaudz (pagaidām) laboju pavisam briesmÄ«gus piemērus. Piemēram, Ā«LEFTBRACEĀ» → Ā«L_BRACEĀ».
  • Turklāt ieviesu jēdzienu Ā«taustiņi ar stāvokliĀ». Ņemot vērā, ka izmantojamā regulārā gramatika neparedz garus teikumus. (Tā ļauj pārbaudÄ«t ar minimālām izmaksām. Ja izmantot tikai Ā«tieÅ”oĀ» ierakstu.)
  • Tiks iebÅ«vēts Ā«de-deduplicationĀ» uzspiestajam. Tādējādi stāvoklis Ā«atkārtotsĀ»=2 tiks ierakstÄ«ts. vienu reizēm.

1. Šablonu sadalījums

[Templates] # "@name@" lai vienkārŔotu izteiksmes
 # Vārdi var sastāvēt no Ŕīm rakstzÄ«mēm (regex)
 "WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"

No kā sastāv cilvēku valodas vārds ar fonētisko ierakstu. (nav runas par grafēmām jeb Ā«hieroglifiemĀ»)? ŠšŠ°ŠŗŠ°Ń-то ŃƒŠ¶Š°ŃŠ½Š°Ń Ā«ŠæŃ€Š¾ŃŃ‚Ń‹Š½ŃĀ». ŠŸŠ¾ŃŃ‚Š¾Š¼Ńƒ ŃŃ€Š°Š·Ńƒ Š·Š°ŠŗŠ»Š°Š“Ń‹Š²Š°ŃŽ ŠæŠ¾Š½ŃŃ‚ŠøŠµ «шаблона».

2. Ko darīt, kad kaut kas tiek nospiests (ierodas jauns taustiņa kods)

[ActionKeys]
 # Savāc taustiņu un veic testu komandu secībai
 # !!! Atkārtoto kodu (code=2) jāievāc tikai reizi katram taustiņam!
 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"]

 # Izmet visus savākto taustiņu, ieskaitot Å”o. Tas ir noklusējuma darbÄ«ba.
 Drop = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
 # Saglabā papildus karti Å”iem taustiņiem, kad kāds ir Ā«nospiestsĀ» stāvoklÄ«.
 # Stāvoklis tiek pārbaudīts caur «OFF:"|"ON:" nosacījumiem darbībā.
 # (ArÄ«, Å”o taustiņu stāvoklim jānotur starp buferu izmetieniem.)
 # ??? Kā rÄ«koties ar CAPS un Ā«LOCKĀ»-taustiņiem ???
 StateKeys = ["L_CTRL", "L_SHIFT", "L_ALT", "L_META", "CAPS", "N_LOCK", "S_LOCK",
              "R_CTRL", "R_SHIFT", "R_ALT", "R_META"]

 # Testē tikai, bet neievāc.
 # Piemēram, es izmantoju F12, nevis BREAK uz prastiem klēpjdatoriem ar sliktām tastatūrām (jauni ThinkPad modeļi)
 Test = ["F1..F10", "ZENKAKUHANKAKU", "102ND", "F11", "F12",
          "RO..KPJPCOMMA", "SYSRQ", "SCALE", "HANGEUL..YEN",
          "STOP..SCROLLDOWN", "NEW..MAX"]

Kopā ir paredzēti 768 kodi. (Bet Ā«tikai gadÄ«jumā» es ievietoju kodā hswitcher noÄ·erÅ”anu Ā«pārsteigumiemĀ»).
IekŔā es detalizēju masÄ«va aizpildÄ«jumu ar atsaucēm uz funkcijām Ā«ko darÄ«tĀ». Uz golang tas izrādÄ«jās (pavisam negaidÄ«ti) izrādÄ«jās ērti un acÄ«mredzami.

  • Ā«DropĀ» Å”ajā vietā plānoju samazināt lÄ«dz minimumam. Labākai elastÄ«gai apstrādei (skatīŔu zemāk).

3. Logu klaŔu tabula

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

Tabulas rindas — divos kvadrātiekavās ar tās nosaukumu. VienkārÅ”i uzreiz nesanāca. AtkarÄ«bā no paÅ”reizējā aktÄ«vā loga var izvēlēties opcijas:

  • Savs Ā«karsto taustiņuĀ» komplekts Ā«Actions = …». Ja nav / tukÅ”s — nekas nedarÄ«t.
  • Pārslēdzējs Ā«MouseClickDropsĀ» — ko darÄ«t, atklājot peles klikŔķi. Tā kā izvērÅ”anas punktā xswitcher nav sÄ«kākas informācijas Ā«kur tur jāklikŔķinaĀ», pēc noklusējuma atiestata buferi. Taču terminālos (piemēram) var to nemaz nedarÄ«t. (parasti).

4. Viens (vai vairāki) pogu spiedienu secības aktivizē konkrētu lāpstiņu

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

Lāpstiņas sadalÄ«tas divos veidos. IebÅ«vētas, ar Ā«runājoÅ”iemĀ» nosaukumiem (NewWord, NewSentence, Compose) un programmējamas.

Programmējamo nosaukumi sākas ar Ā«Action.Ā». T.k. TOML v1.4, nosaukumiem ar punktiem jābÅ«t pēdiņās.

Zemāk katram jābūt aprakstītas sadaļai ar tādu paŔu nosaukumu.

Lai nesabojātu cilvēku prātus ar Ā«tukÅ”iemĀ» regulāriem izteiksmēm (no pieredzes, tās uzrakstÄ«t- var tikai viens no desmit profesionāļiem), uzreiz ievieÅ”u papildu sintaksi.

  • Ā«OFF:Ā» (vai Ā«ON:Ā») pirms regexp (regulārā izteiksme) prasa, lai nākamās pogas bÅ«tu atbrÄ«votas (vai nospiestas).
    Tālāk plānoju izveidot Ā«negodÄ«guĀ» regulāro izteiksmi. Ar atseviŔķu pārbaudi gabaliem starp caurulēm "|". Ar mērÄ·i samazināt ierakstu skaitu, piemēram, "[LR]_SHIFT" (kur tas skaidri nav nepiecieÅ”ams).
  • Ā«SEQ:Ā» Ja iepriekŔējais nosacÄ«jums ir izpildÄ«ts (vai trÅ«kst), tālāk pārbaudām attiecÄ«bā uz Ā«parastoĀ» regulāro izteiksmi. Par sÄ«kāku informāciju uzreiz sÅ«tu uz^Wbibliotēku Ā«regexpĀ». Jo pats joprojām neesmu rÅ«pējies noskaidrot saderÄ«bu ar manām iecienÄ«tākajām pcre (Ā«perl compatibleĀ»).
  • Izteiksmi uzraksta veidā Ā«POGA_1: KODS1, POGA_2: KODS2Ā» utt., ierakstu secÄ«bā skenēŔanas kodus.
  • Pārbaude vienmēr Ā«spiežasĀ» uz secÄ«bas beigām, tāpēc "$" beigās nav jāliek.
  • Visas pārbaudes vienā rindā tiek veiktas secÄ«gi un apvienotas ar Ā«UNĀ». Bet tā kā vērtÄ«ba aprakstÄ«ta masÄ«vā, pēc komata var uzrakstÄ«t alternatÄ«vu pārbaudi. Ja tas ir kaut kam nepiecieÅ”ams.
  • VērtÄ«ba Ā«SeqLength = 8Ā» ierobežo buferu lielumu, attiecÄ«bā uz kuru tiek veiktas visas pārbaudes. Tā kā manā dzÄ«vē (lÄ«dz Å”im) nav bijusi iespēja sastapt neierobežotas resursus.

5. Sakaru definēŔana, kas aprakstÄ«ta iepriekŔējā sadaļā

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

Galvenais Å”eit — ā€žAction = [MasÄ«vs]ā€œ. LÄ«dzÄ«gi kā iepriekŔējā sadaļā, ir ierobežots iekŔējo rÄ«cÄ«bu kopums. Un principā nav ierobežojumu pieteikÅ”anai (uzrakstÄ«t ā€žAction.XXXā€œ un neakcentēt aprakstÄ«t vēl vienu sadaļu zem tā).
Tostarp, vārdu pārrakstīŔana labotajā izkārtojumā tiek sadalÄ«ta divās daļās: ā€žmainÄ«t izkārtojumu kā tur norādÄ«tsā€œ un ā€žpārrakstÄ«tā€œ (ā€žRetypeWordā€œ).

Pārējie parametri tiek ierakstÄ«ti ā€žvārdnÄ«cÄā€œ (ā€žmapā€œ golangā) attiecÄ«gajam rÄ«cÄ«bai, to saraksts ir atkarÄ«gs no rakstÄ«tā ā€žActionā€œ.

Dažādas rÄ«cÄ«bas var aprakstÄ«t vienā grupā (sadaļās). Un tās var arÄ« izkliedēt. Kā es iepriekÅ” parādÄ«ju.

Uzreiz nosaku darbÄ«bu ā€žExecā€œ — izpildÄ«t ārēju skriptu. Ar iespēju ievietot tam stdin ierakstÄ«to buferi.

  • ā€žWait = 1ā€œ — gaidÄ«t pabeigtā procesa pabeigÅ”anu.
  • Iespējams, ā€žpapildusā€œ vēlos piedāvāt apkārtējo informāciju, piemēram, loga klases nosaukumu, no kura tika nozagts.
    ā€žVai vēlaties pievienot savu apstrādātāju? Jums Å”eit ir.ā€œ

Uf (izelpoju). Izskatās, ka neko neesmu aizmirsis.

Op! Aha, neesmu aizmirsis...
Un kur ir palaiŔanas konfigurācija? Cietajā kodā? Aptuveni tā:

[ScanDevices]
 # Jābūt pastāvīgi. PaŔatjaunoties, ja tas ir jaunāks par 30s.
 Test = "\/dev\/input\/event0"
 Respawn = 30
 # MeklēŔanas maska
 Search = "\/dev\/input\/event*"
 # Manos thinkPads ir Ŕādi pseido-taustiņi ar tonnām nevajadzÄ«gu notikumu
 Bypass = "(?i)Video|Camera" # "(?i)" acÄ«mredzami atŔķiras no ā€žklasiskā" pcre.

Un kur es aizmirsu / kļūdÄ«jos (bez tā — nekā), ļoti ceru, ka apzinÄ«gajiem lasÄ«tājiem nepatiks iespēja uzlikt degunu.

Veiksmi!

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster