Korektor tata letak Xswitcher untuk linux: langkah kedua

Sejak publikasi sebelumnya (xswitcher pada tahap "proof of concept") menerima banyak ulasan konstruktif (yang bagus) , saya terus menghabiskan waktu luang saya untuk pengembangan proyek. Sekarang saya ingin menghabiskan sedikit dari Anda ... Langkah kedua tidak akan cukup akrab: saran / diskusi tentang desain konfigurasi.



Entah bagaimana ternyata programmer normal sangat bosan untuk memperbaiki semua tikungan ini.

Agar tidak berdasar, di dalam adalah contoh dari apa yang saya hadapi.
( ) Apache Kafka & ZooKeeper.
— ? ! - xml ( « »).
— , ACL ? ! -… - .

Dan dalam pekerjaan saya - justru sebaliknya. Benar (sayangnya, sejak pertama kali hampir tidak pernah) model yang dibangun memungkinkan Anda untuk dengan mudah dan mudah (well, hampir) merakit sirkuit.

Baru-baru ini sebuah artikel tentang Habré menemukan sebuah artikel tentang kerja keras para ilmuwan data ...
, . , , « ». , .. — , / . - .

Langsung ke intinya. Sebagai dasar sintaksis, saya mengambil TOML dari warga ini .

Karena itu (TOML) di satu sisi dapat diedit oleh manusia. Dan di sisi lain - menerjemahkan 1: 1 menjadi salah satu sintaksis yang lebih umum: XML, JSON, YAML.
Selain itu, implementasi yang saya gunakan dari "github.com/BurntSushi/toml", meskipun bukan yang paling modis (masih sintaks 1.4), secara sintaksis kompatibel dengan JSON yang sama ("built-in").

Artinya, jika Anda mau, Anda bisa mengatakan "pergi melalui hutan dengan ini TOML Anda, saya ingin XXX" dan "menambal" kode hanya dengan satu baris.

Jadi, jika Anda ingin menulis beberapa windows (pasti bukan saya) untuk mengkonfigurasi xswitcher, tidak ada masalah "dengan ini konfigurasi sialan Anda".

Untuk semua yang lain, sintaksinya didasarkan pada "key = value" (dan secara harfiah beberapa opsi lebih rumit, ketik = [some, that, array]) Saya kira
nyaman secara intuitif.
, «» ( 2013 ). , , TOML .

, .

Secara umum, kami menggunakan TOML (sangat mirip dengan Windows INI yang lama). Dan kami memiliki konfigurasi di mana kami menjelaskan cara melampirkan serangkaian kait tergantung pada set kode pemindaian terbaru dari keyboard. Di bawah berkeping-keping - apa yang terjadi saat ini. Dan penjelasan dari apa yang saya putuskan begitu.

0. Abstraksi dasar


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

Terdiri dari apa kata bahasa manusia dengan notasi fonetik (atau apakah itu grapheme alias "hieroglif") ? Semacam "lembaran" yang mengerikan. Karena itu, saya langsung meletakkan konsep "template".

2. Apa yang harus dilakukan ketika sesuatu ditekan (kode pindaian berikutnya telah tiba)


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

Total 768 kode disediakan. (Tapi, untuk berjaga-jaga, saya memasukkan tangkapan "kejutan" dalam kode xswitcher).
Di dalamnya saya melukis mengisi array dengan tautan ke fungsi "apa yang harus dilakukan". Di golang, itu (tiba-tiba) ternyata nyaman dan jelas.

  • "Jatuhkan" di tempat ini yang aku rencanakan untuk dikurangi seminimal mungkin. Mendukung pemrosesan yang lebih fleksibel (saya akan tunjukkan di bawah).

3. Label kelas jendela


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

Baris tabel dalam tanda kurung ganda dengan namanya. Lebih mudah untuk pergi tidak berhasil. Bergantung pada jendela aktif saat ini, Anda dapat memilih opsi:

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

Kait dibagi menjadi dua jenis. Built-in, dengan nama "berbicara" (NewWord, NewSentence, Compose) dan dapat diprogram.

Nama yang dapat diprogram dimulai dengan "Aksi." Karena TOML v1.4, nama bertitik harus dalam tanda kutip.

Bagian dengan nama yang sama harus dijelaskan di bawah untuk masing-masing .

Agar tidak meledakkan otak orang dengan pelanggan tetap (dari pengalaman, satu dari sepuluh profesional dapat menulisnya ), saya langsung memperkenalkan sintaks tambahan.

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

Hal utama di sini adalah "Action = [Array]" . Mirip dengan bagian sebelumnya, ada serangkaian tindakan bawaan yang terbatas. Dan tidak terbatas pada prinsipnya kemungkinan docking (tulis "Action.XXX" dan tidak terlalu malas untuk melukis bagian lain untuk itu) .
Secara khusus, kata mengetik ulang di tata letak yang diperbaiki dibagi menjadi dua bagian: "mengubah tata letak seperti yang diatur di sana" dan "ketik ulang" ("RetypeWord") .

Parameter yang tersisa ditulis ke "kamus" ("peta" di golang) untuk tindakan ini, daftar mereka tergantung pada apa yang ditulis dalam "Tindakan".

Beberapa tindakan berbeda dapat dijelaskan dalam satu tumpukan (bagian) . Dan Anda dapat memisahkannya. Seperti yang saya tunjukkan di atas.

Segera letakkan tindakan "Exec" - jalankan skrip eksternal. Dengan opsi untuk mendorong buffer tertulis ke stdin.

  • "Tunggu = 1" - menunggu penyelesaian proses yang sedang berjalan.
  • Mungkin, "ke tumpukan" akan ingin dimasukkan ke dalam lingkungan ekstra. informasi seperti nama kelas jendela dari mana ia dicegat.
    "Ingin mengaitkan pawangmu?" Untuk kamu di sini. "

Fiuh (dihembuskan). Tampaknya tidak melupakan apa pun.

Ups! Ya, saya tidak lupa ...
? -́? :

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


Dan di mana saya lupa / membuat kesalahan (tanpa ini - tidak mungkin) , saya sangat berharap bahwa pembaca yang penuh perhatian tidak terlalu malas untuk menyodok hidung mereka.

Semoga berhasil

All Articles