Corretor de layout Xswitcher para linux: etapa dois

Como a publicação anterior (xswitcher na etapa "prova de conceito") recebeu muitas críticas construtivas (o que é bom) , continuei a gastar meu tempo livre no desenvolvimento do projeto. Agora eu quero gastar um pouco do seu ... O segundo passo não será muito familiar: sugestão / discussão sobre o design da configuração.



De alguma forma, os programadores normais estão muito entediados em sintonizar todas essas reviravoltas.

Para não ser infundado, é um exemplo do que estou lidando.
( ) Apache Kafka & ZooKeeper.
— ? ! - xml ( « »).
— , ACL ? ! -… - .

E no meu trabalho - exatamente o oposto. Corretamente (infelizmente, desde a primeira vez quase nunca), o modelo construído permite montar fácil e facilmente (ou quase) o circuito.

Recentemente, um artigo sobre Habré encontrou um artigo sobre o trabalho árduo de cientistas de dados ...
, . , , « ». , .. — , / . - .

Vá direto ao ponto. Como base sintática, peguei o TOML desse cidadão .

Porque (TOML) é, por um lado, editável por humanos. E por outro lado - traduz 1: 1 em qualquer uma das sintaxes mais comuns: XML, JSON, YAML.
Além disso, a implementação que usei no “github.com/BurntSushi/toml”, embora não seja a mais atual (sintaxe atual 1.4), é sintaticamente compatível com o mesmo JSON (“interno”).

Ou seja, se desejar, você pode simplesmente dizer "vá pela floresta com esse seu TOML, eu quero XXX" e "remende" o código com apenas uma linha.

Portanto, se você deseja escrever algumas janelas (definitivamente não eu) para configurar o xswitcher, não problemas "com essa sua porra de configuração".

Para todas as outras sintaxes com base em "key = value" (e literalmente em algumas opções mais complexas, como = [a, então a matriz]) acredita
intuitivamente conveniente.
, «» ( 2013 ). , , TOML .

, .

Em geral, utilizamos o TOML (muito semelhante ao antigo INI do Windows). E temos uma configuração na qual descrevemos como conectar uma série de ganchos, dependendo do conjunto dos códigos de verificação mais recentes do teclado. Abaixo em pedaços - o que aconteceu no momento. E a explicação do que eu decidi.

0. Abstrações básicas


  • -. - , - ( 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])"

Em que consiste uma palavra da linguagem humana com uma notação fonética (ou é um grafema conhecido como "hieróglifo") ? Algum tipo de "folha" terrível. Portanto, eu imediatamente coloquei o conceito de "modelo".

2. O que fazer quando algo é pressionado (o próximo código de digitalização chegou)


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

São fornecidos um total de 768 códigos. (Mas, apenas no caso, eu inseri uma captura de "surpresas" no código do xswitcher).
No interior, pintei preenchendo a matriz com links para as funções "o que fazer". No golang, (de repente) acabou sendo conveniente e óbvio.

  • "Largar" neste local pretendo reduzir ao mínimo. A favor de um processamento mais flexível (mostrarei abaixo).

3. Etiqueta de classe da janela


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

As linhas da tabela estão entre colchetes duplos com seu nome. Mais fácil de ir não funcionou. Dependendo da janela ativa atual, você pode escolher as opções:

  • « » «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)" ]

Ganchos são divididos em dois tipos. Incorporado, com nomes "falantes" (NewWord, NewSentence, Compose) e programável.

Os nomes programáveis ​​começam com "Ação". Porque TOML v1.4, os nomes pontilhados devem estar entre aspas.

Uma seção com o mesmo nome deve ser descrita abaixo para cada um .

Para não explodir o cérebro das pessoas com regulares "nus" (na experiência, um em cada dez profissionais pode escrevê- los ), eu imediatamente apresento uma sintaxe adicional.

  • «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.

O principal aqui é "Ação = [Matriz]" . Semelhante à seção anterior, há um conjunto limitado de ações internas. E, em princípio, ilimitada a possibilidade de atracar (escreva "Action.XXX" e não tenha preguiça de pintar outra seção) .
Em particular, a palavra redigitação no layout corrigido é dividida em duas partes: “alterar o layout conforme definido lá” e “redigitar” (“RetypeWord”) .

Os parâmetros restantes são gravados no "dicionário" ("mapa" em golang) para esta ação, sua lista depende do que está escrito em "Ação".

Várias ações diferentes podem ser descritas em uma pilha (seção) . E você pode separá-lo. Como mostrei acima.

Coloque imediatamente a ação "Exec" - execute um script externo. Com a opção de enviar o buffer escrito para o stdin.

  • “Wait = 1” - aguarde a conclusão do processo em execução.
  • Provavelmente, "para a pilha" vai querer colocar no ambiente extra. informações como o nome da classe de janela da qual é interceptada.
    "Quer ligar seu manipulador?" Para você aqui.

Ufa (exalado). Parece não ter esquecido nada.

Opa! Sim, eu não esqueci ...
? -́? :

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


E onde eu esqueci / cometi um erro (sem isso - de jeito nenhum) , realmente espero que os leitores atentos não tenham preguiça de cutucar o nariz.

Boa sorte

All Articles