Xswitcher-lay-outcorrector voor Linux: stap twee

Als vorige publicatie (xswitcher in de “proof of concept”-fase) kreeg behoorlijk wat constructieve feedback (wat fijn is), bleef ik mijn vrije tijd besteden aan het ontwikkelen van het project. Nu wil ik een beetje van je geld uitgeven... De tweede stap zal niet helemaal gebruikelijk zijn: voorstel/bespreking van configuratieontwerp.

Xswitcher-lay-outcorrector voor Linux: stap twee

Op de een of andere manier blijkt dat normale programmeurs het ongelooflijk saai vinden om al deze bedieningselementen in te stellen.

Om niet ongegrond te zijn: binnenin staat een voorbeeld van waar ik mee te maken heb.
Over het algemeen uitstekend bedacht (en goed geïmplementeerd) Apache Kafka & ZooKeeper.
- Configuratie? Maar het is saai! Domme xml (omdat het “out of the box” is).
- Oh, wil je ook een kruisband? Maar het is zo saai! Tap-blooper... Zoiets.

Maar in mijn werk is het precies het tegenovergestelde. Rechts (helaas, bijna nooit de eerste keer) het opgebouwde model zorgt ervoor dat je gemakkelijk en natuurlijk verder kunt gaan (Bijna) een diagram samenstellen.

Onlangs kwam ik op Habré een artikel tegen over het harde werk van datawetenschappers...
Het blijkt dat dit moment volledig voor hen is gerealiseerd. En in mijn praktijk, zoals ze zeggen, “light-versie”. Modellen met meerdere volumes, doorgewinterde programmeurs met OOP in de aanslag, enz. – dit zal allemaal later verschijnen wanneer/als het van start gaat. Maar de ontwerper moet hier en nu ergens beginnen.

Kom ter zake. Ik heb TOML als syntactische basis genomen van deze burger.

Omdat hij (TOML) aan de ene kant door mensen bewerkbaar. Aan de andere kant wordt het 1:1 vertaald naar een van de meest voorkomende syntaxis: XML, JSON, YAML.
Bovendien is de implementatie die ik gebruikte van “github.com/BurntSushi/toml”, hoewel niet de meest modieuze (nog steeds 1.4-syntaxis), syntactisch compatibel met dezelfde (“ingebouwde”) JSON.

Dat wil zeggen, als je wilt, kun je eenvoudigweg zeggen "ga door het bos met die TOML van je, ik wil XXX" en de code "patchen" met slechts één regel.

Dus als u enkele vensters wilt schrijven om xswitcher te configureren (Ik weet het niet zeker) Er worden geen problemen verwacht “met deze verdomde configuratie van jou.”

Voor alle anderen is de syntaxis gebaseerd op “sleutel = waarde” (en letterlijk een paar ingewikkelder opties, zoals = [some, that, array]) Ik veronderstel
intuïtief handig.
Wat interessant is, is dat "verbrand" rond dezelfde tijd (rond 2013). Alleen ging de auteur van TOML, in tegenstelling tot mij, op de juiste schaal in.

Daarom is het nu gemakkelijker voor mij om de implementatie ervan aan te passen aan mijzelf, en niet andersom.

Over het algemeen gebruiken we TOML (zeer vergelijkbaar met de oude Windows INI). En we hebben een configuratie waarin we beschrijven hoe we een reeks haken kunnen bevestigen, afhankelijk van de set met de nieuwste scancodes van het toetsenbord. Hieronder volgt stukje voor stukje wat er tot nu toe is gebeurd. En een uitleg waarom ik deze weg heb gekozen.

0. Basale abstracties

  • Codeaanduidingen scannen. Hier moet zeker iets aan gedaan worden, aangezien digitale codes simpelweg absoluut niet voor mensen leesbaar zijn (dat ben ik maar). loloschakelaar).
    Ik schudde "ecodes.go" uit "golang-evdev" (ik was te lui om naar de originele bron te kijken, hoewel de auteur dit nogal cultureel aangaf). Een klein beetje (voorlopig) corrigeerde iets dat heel beangstigend was. Zoals “LEFTBRACE” → “L_BRACE”.
  • Bovendien introduceerde hij het concept van ‘staatssleutels’. Omdat de gebruikte reguliere grammatica geen lange passages toestaat. (Maar u kunt hiermee wel met minimale overhead controleren. Als u alleen “directe” opname gebruikt.)
  • Er zal een ingebouwde “deduplicator” zijn van wat er wordt ingedrukt. De toestand "herhalen"=2 zal dus worden geschreven een tijd.

1. Sjablonensectie

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

Waaruit bestaat een menselijk taalwoord met fonetische notatie? (ofwel een kwestie van grafemen oftewel ‘hiërogliefen’)? Een soort vreselijk "laken". Daarom introduceer ik onmiddellijk het concept van “sjabloon”.

2. Wat te doen als er op iets wordt geklikt (er is weer een scancode binnengekomen)

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

Er zijn in totaal 768 codes. (Maar "voor het geval dat" heb ik "verrassingen" in de xswitcher-code ingevoegd).
Binnenin beschreef ik het vullen van de array met links naar functies "wat te doen". In Golang is dit het geval (plotseling) Het bleek handig en voor de hand liggend.

  • Ik ben van plan om “Drop” op deze plek tot een minimum te beperken. Voorstander van een flexibelere verwerking (ik zal het hieronder laten zien).

3. Tabel met vensterklassen

# 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 rijen van de tabel staan ​​tussen dubbele vierkante haken met de naam ervan. Het kon niet meteen eenvoudiger zijn geweest. Afhankelijk van het momenteel actieve venster kunt u de volgende opties selecteren:

  • Uw eigen set “sneltoetsen” “Acties = …”. Indien niet/leeg, niets doen.
  • Schakel over naar "MouseClickDrops" - wat te doen als een muisklik wordt gedetecteerd. Omdat er op het punt waarop xswitcher wordt ingeschakeld geen details zijn over “waar ze klikken”, resetten we de buffer standaard. Maar in terminals (bijvoorbeeld) hoef je dit niet te doen (gebruikelijk).

4. Eén (of meerdere) reeksen klikken activeren een of andere hook

# 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 zijn verdeeld in twee typen. Ingebouwd, met “sprekende” namen (NewWord, NewSentence, Compose) en programmeerbaar.

Programmeerbare namen beginnen met ‘Actie’. Omdat TOML v1.4, namen met punten moeten tussen aanhalingstekens staan.

Elke sectie moet hieronder worden beschreven met dezelfde naam.

Om de mensen niet te verbazen met ‘naakte’ stamgasten (uit ervaring, hun schrijvenmisschien één op de tien professionals), implementeer ik onmiddellijk aanvullende syntaxis.

  • "UIT:" (of "AAN:") vóór regexp (reguliere expressie) vereisen dat de volgende knoppen worden losgelaten (of ingedrukt).
    Vervolgens ga ik een ‘oneerlijke’ reguliere expressie maken. Met aparte controle van stukken tussen pijpen "|". Om het aantal records te verminderen, zoals "[LR]_SHIFT" (waar dit duidelijk niet nodig is).
  • "SEQ:" Als aan de vorige voorwaarde is voldaan (of afwezig is), controleren we aan de hand van een ‘normale’ reguliere expressie. Voor details stuur ik onmiddellijk de “regexp”-bibliotheek naar ^W. Omdat ik nog steeds niet de moeite heb genomen om de mate van compatibiliteit met mijn favoriete pcre (“perl-compatibel”) te achterhalen.
  • De uitdrukking wordt in de vorm geschreven "BUTTON_1: CODE1, BUTTON_2: CODE2" enz., in de volgorde waarin de scancodes worden ontvangen.
  • De cheque wordt altijd "vastgeklikt" tot het einde van de reeks, dus het is niet nodig om “$” aan de staart toe te voegen.
  • Alle controles op één regel worden na elkaar uitgevoerd en worden gecombineerd door “ik”. Maar aangezien de waarde wordt beschreven als een array, kunt u na de komma een alternatieve controle schrijven. Als dit om wat voor reden dan ook nodig is.
  • Waarde "Volglengte = 8" beperkt de grootte van de buffer waarop alle controles worden uitgevoerd. Omdat Ik ben (tot nu toe) nog nooit in mijn leven eindeloze hulpbronnen tegengekomen.

5. Het instellen van de haken zoals beschreven in de vorige 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.

Het belangrijkste hier is "Actie = [Matrix]". Net als in de vorige sectie is er een beperkte set ingebouwde acties. En de mogelijkheid tot aanmeren is in principe niet beperkt (schrijf “Action.XXX” en wees niet te lui om er nog een sectie voor te schrijven).
Met name het overtypen van een woord in de gecorrigeerde lay-out is verdeeld in twee delen: “wijzig de lay-out zoals daar gespecificeerd” и “opnieuw typen” (“RetypeWord”).

De overige parameters worden naar het “woordenboek” geschreven ("kaart" in Golang) voor een bepaalde actie hangt hun lijst af van wat er in "Actie" staat.

Er kunnen verschillende acties in één hoop worden beschreven (secties). Of je kunt het uit elkaar trekken. Zoals ik hierboven liet zien.

Ik heb onmiddellijk de actie "Exec" ingesteld om het externe script uit te voeren. Met de optie om de opgenomen buffer in stdin te duwen.

  • “Wacht = 1” — wacht tot het lopende proces is voltooid.
  • Waarschijnlijk wil je "op de hoop" extra mensen in de omgeving plaatsen. informatie zoals de naam van de vensterklasse waaruit deze is onderschept.
    “Wilt u uw begeleider koppelen? Dit is waar je heen moet.”

Pfff (uitgeademd). Het lijkt alsof ik niets vergeten ben.

Oeps! Ja, ik was het niet vergeten...
Waar is de startconfiguratie? In harde code? Zoals dat:

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

Waar heb ik het vergeten/een fout gemaakt? (kan niet zonder dit)Ik hoop echt dat aandachtige lezers niet te lui zullen zijn om in hun neus te steken.

Good luck!

Bron: www.habr.com

Voeg een reactie