Xswitcher Layout Corrector für Linux: Schritt zwei

Da die vorherige Veröffentlichung (xswitcher in der Phase des „Proof of Concept“) viele konstruktive Bewertungen erhalten hat (was sehr schön ist) , habe ich meine Freizeit weiterhin mit der Entwicklung des Projekts verbracht. Jetzt möchte ich ein wenig von Ihnen ausgeben ... Der zweite Schritt wird nicht ganz vertraut sein: Vorschlag / Diskussion des Konfigurationsdesigns.



Irgendwie stellt sich heraus, dass normale Programmierer, die all diese Wendungen konfigurieren, wild langweilig sind.

Um nicht unbegründet zu sein, ist innen ein Beispiel dafür, womit ich es zu tun habe.
( ) Apache Kafka & ZooKeeper.
— ? ! - xml ( « »).
— , ACL ? ! -… - .

Und in meiner Arbeit - genau das Gegenteil. Richtig (leider gibt es das erste Mal, dass es so gut wie nie gibt) können Sie mit dem gebauten Modell die Schaltung einfach und problemlos ( na ja, fast) zusammenbauen.

Kürzlich stieß ein Artikel über Habré auf einen Artikel über harte Arbeit von Datenwissenschaftlern ...
, . , , « ». , .. — , / . - .

Komm zum Punkt. Als syntaktische Grundlage habe ich TOML von diesem Bürger genommen .

Weil es (TOML) einerseits von Menschen bearbeitet werden kann. Und andererseits übersetzt es 1: 1 in eine der gängigsten Syntaxen: XML, JSON, YAML.
Darüber hinaus ist die Implementierung, die ich von "github.com/BurntSushi/toml" verwendet habe, zwar nicht die modernste (immer noch Syntax 1.4), aber syntaktisch kompatibel mit demselben ("eingebauten") JSON.

Das heißt, wenn Sie möchten, können Sie einfach sagen: "Gehen Sie mit dieser TOML durch den Wald, ich möchte XXX" und den Code mit nur einer Zeile "patchen".

Wenn Sie also einige Fenster schreiben möchten (definitiv nicht ich) , um xswitcher zu konfigurieren, gibt es keine Probleme "mit dieser verdammten Konfiguration".

Für alle anderen ist die Syntax basierend auf "key = value" (und buchstäblich sind einige Optionen komplizierter, type = [some, then, array]), nehme ich an
intuitiv bequem.
, «» ( 2013 ). , , TOML .

, .

Im Allgemeinen nehmen wir TOML (sehr ähnlich der alten Windows INI). Und wir haben eine Konfiguration, in der wir beschreiben, wie eine Reihe von Haken abhängig von den neuesten Scan-Codes von der Tastatur angebracht werden. Unten in Stücken - was im Moment passiert ist. Und die Erklärung dessen, was ich so entschieden habe.

0. Grundlegende Abstraktionen


  • -. - , - ( loloswitcher).
    «ecodes.go» «golang-evdev» ( , ). () . «LEFTBRACE» → «L_BRACE».
  • « ». . ( . «» .)
  • «» . , «»=2 .

1.


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

Woraus besteht ein menschliches Sprachwort mit phonetischer Notation (oder ist es ein Graphem, auch bekannt als "Hieroglyphen") ? Eine Art schreckliches "Blatt". Deshalb lege ich sofort das Konzept der "Vorlage".

2. Was tun, wenn etwas gedrückt wird (der nächste Scan-Code ist eingetroffen)?


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

Insgesamt werden 768 Codes bereitgestellt. (Aber "nur für den Fall" in den xswitcher-Code eingefügt ein Fang von "Überraschungen").
Im Inneren habe ich das Array mit Links zu den "Was zu tun" -Funktionen gefüllt. Auf Golang stellte sich (plötzlich) heraus, dass es bequem und offensichtlich war.

  • "Drop" an dieser Stelle habe ich vor, auf ein Minimum zu reduzieren. Für eine flexiblere Verarbeitung (ich werde unten zeigen).

3. Fensterklassenbezeichnung


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

Tabellenzeilen stehen in doppelten eckigen Klammern mit ihrem Namen. Einfacher zu gehen hat nicht funktioniert. Abhängig vom aktuell aktiven Fenster können Sie folgende Optionen auswählen:

  • « » «Actions = …». / — .
  • «MouseClickDrops» — . xswitcher « », - . () ( ).

4. ( )


# 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 werden in zwei Typen unterteilt. Eingebaut, mit "sprechenden" Namen (NewWord, NewSentence, Compose) und programmierbar.

Programmierbare Namen beginnen mit "Aktion". weil In TOML v1.4 müssen gepunktete Namen in Anführungszeichen gesetzt werden.

Ein Abschnitt mit demselben Namen sollte unten für jeden Abschnitt beschrieben werden .

Um das Gehirn von Menschen nicht mit „nackten“ Stammgästen in die Luft zu jagen (erfahrungsgemäß kann jeder zehnte Fachmann sie schreiben ), führe ich sofort eine zusätzliche Syntax ein.

  • «OFF:» ( «ON:») regexp ( ) ( ).
    «» . "|". "[LR]_SHIFT" ( ).
  • «SEQ:» ( ), «» . ^W «regexp». pcre («perl compatible»).
  • «_1: 1, _2: 2» .., -.
  • «» , "$" .
  • «». , . - .
  • «SeqLength = 8» , . .. ( ) .

5. ,


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

Die Hauptsache hier ist "Action = [Array]" . Ähnlich wie im vorherigen Abschnitt gibt es eine begrenzte Anzahl integrierter Aktionen. Und im Prinzip unbegrenzt die Möglichkeit des Andockens (schreiben Sie "Action.XXX" und seien Sie nicht zu faul, um einen anderen Abschnitt dafür zu malen) .
Insbesondere ist das Wort, das im korrigierten Layout erneut eingegeben wird, in zwei Teile unterteilt: "Ändern Sie das Layout so, wie es dort eingestellt ist" und "Neu eingeben" ("RetypeWord") .

Die restlichen Parameter werden für diese Aktion in das "Wörterbuch" ("Karte" in Golang) geschrieben. Ihre Liste hängt davon ab, was in "Aktion" geschrieben ist.

In einem Heap (Abschnitt) können mehrere verschiedene Aktionen beschrieben werden . Und du kannst es auseinander ziehen. Wie ich oben gezeigt habe.

Legen Sie sofort die Aktion "Exec" - führen Sie ein externes Skript aus. Mit der Option, den geschriebenen Puffer in stdin zu verschieben.

  • "Wait = 1" - Warten Sie, bis der laufende Prozess abgeschlossen ist.
  • Wahrscheinlich wird "to the heap" die Umgebung extra belasten wollen. Informationen wie der Name der Fensterklasse, von der sie abgefangen wird.
    "Möchten Sie Ihren Handler anschließen?" Für dich hier. "

Puh (ausgeatmet). Es scheint nichts vergessen zu haben.

Hoppla! Ja, ich habe nicht vergessen ...
? -́? :

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


Und wo ich einen Fehler vergessen / gemacht habe (ohne dies - auf keinen Fall) , hoffe ich wirklich, dass aufmerksame Leser nicht zu faul sind, um ihre Nase zu stechen.

Viel Glück

All Articles