Korektor tata letak Xswitcher untuk Linux: langkah kedua

Sebagai publikasi sebelumnya (xswitcher pada tahap “bukti konsep”) menerima cukup banyak masukan yang membangun (yang bagus), saya terus menghabiskan waktu luang saya untuk mengembangkan proyek. Sekarang aku ingin menghabiskan sedikit waktumu... Langkah kedua tidak biasa: proposal/diskusi desain konfigurasi.

Korektor tata letak Xswitcher untuk Linux: langkah kedua

Ternyata programmer biasa merasa sangat membosankan untuk mengatur semua kontrol ini.

Agar tidak berdasar, di dalamnya ada contoh apa yang saya hadapi.
Secara keseluruhan dirancang dengan sangat baik (dan diterapkan dengan baik) Apache Kafka & ZooKeeper.
- Konfigurasi? Tapi itu membosankan! Xml bodoh (karena "di luar kotak").
- Oh, kamu juga mau ACL? Tapi itu sangat membosankan! Ketuk-blooper... Sesuatu seperti itu.

Namun dalam pekerjaan saya justru sebaliknya. Benar (sayangnya, hampir tidak pernah pertama kali) model yang dibangun memungkinkan Anda melanjutkan lebih jauh dengan mudah dan alami (Hampir) merakit diagram.

Baru-baru ini saya menemukan artikel di Habré tentang kerja keras para ilmuwan data...
Ternyata momen ini terwujud sepenuhnya bagi mereka. Dan dalam praktik saya, seperti yang mereka katakan, "versi ringan". Model multi-volume, pemrogram berpengalaman dengan OOP siap, dll. — ini semua akan muncul nanti ketika/jika lepas landas. Namun perancangnya perlu memulai dari sini dan saat ini.

Langsung ke intinya. Saya menggunakan TOML sebagai dasar sintaksis dari warga ini.

Karena dia (TOML) di satu sisi, dapat diedit manusia. Di sisi lain, ini diterjemahkan 1:1 ke dalam 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 (“bawaan”) yang sama.

Artinya, jika mau, Anda cukup mengatakan “pergilah ke hutan dengan TOML milik Anda, saya ingin XXX” dan “tambal” kode hanya dengan satu baris.

Jadi, jika Anda ingin menulis beberapa jendela untuk mengkonfigurasi xswitcher (Saya tidak yakin) Diperkirakan tidak ada masalah "dengan konfigurasi Anda yang sialan ini".

Untuk yang lainnya, sintaksnya didasarkan pada “key = value” (dan beberapa opsi yang lebih rumit, seperti = [some, that, array]) Saya seharusnya
nyaman secara intuitif.
Yang menarik adalah itu "dibakar" sekitar waktu yang sama (sekitar tahun 2013). Hanya saja, tidak seperti saya, penulis TOML membahasnya dalam skala yang tepat.

Oleh karena itu, sekarang lebih mudah bagi saya untuk menyesuaikan penerapannya dengan diri saya sendiri, dan bukan sebaliknya.

Secara umum, kami mengambil TOML (sangat mirip dengan Windows INI yang lama). Dan kami memiliki konfigurasi di mana kami menjelaskan cara memasang serangkaian kait tergantung pada kumpulan kode pindaian terbaru dari keyboard. Di bawah ini, sepotong demi sepotong, adalah apa yang telah terjadi sejauh ini. Dan penjelasan mengapa saya memutuskan seperti ini.

0. Abstraksi Dasar

  • Pindai penunjukan kode. Sesuatu pasti perlu dilakukan mengenai hal ini, karena kode digital sama sekali tidak dapat dibaca manusia (itu hanya saya loloswitcher).
    Saya menghapus “ecodes.go” dari “golang-evdev” (saya terlalu malas untuk melihat sumber aslinya, meskipun penulis menunjukkannya secara budaya). Saya mengoreksi sedikit (untuk saat ini) sesuatu yang cukup menakutkan. Seperti “LEFTBRACE” → “L_BRACE”.
  • Selain itu, ia memperkenalkan konsep “kunci negara”. Karena tata bahasa biasa yang digunakan tidak memungkinkan adanya bagian yang panjang. (Tetapi ini memungkinkan Anda untuk memeriksa dengan overhead minimal. Jika Anda hanya menggunakan rekaman “langsung”.)
  • Akan ada “deduplikator” bawaan dari apa yang ditekan. Dengan demikian, keadaan "repeat"=2 akan ditulis satu waktu.

1. Bagian templat

[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 apakah kata bahasa manusia dengan notasi fonetik? (baik soal grafem alias “hieroglif”)? Semacam “sprei” yang mengerikan. Oleh karena itu, saya langsung memperkenalkan konsep “template”.

2. Apa yang harus dilakukan jika ada yang diklik (kode pindaian lain 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 ada 768 kode. (Tetapi “untuk berjaga-jaga” saya memasukkan “kejutan” ke dalam kode xswitcher).
Di dalamnya saya jelaskan mengisi array dengan tautan ke fungsi "apa yang harus dilakukan". Di golang ini (Tiba-tiba) Ternyata nyaman dan jelas.

  • Saya berencana untuk mengurangi “Drop” seminimal mungkin di tempat ini. Mendukung pemrosesan yang lebih fleksibel (saya akan menunjukkannya di bawah).

3. Tabel dengan jendela kelas

# 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-baris tabel berada dalam tanda kurung siku ganda beserta namanya. Ini tidak bisa lebih mudah dari awal. Tergantung pada jendela yang sedang aktif, Anda dapat memilih opsi berikut:

  • Kumpulan “tombol pintas” Anda sendiri “Tindakan =…”. Jika tidak/kosong, jangan lakukan apa pun.
  • Ganti “MouseClickDrops” - apa yang harus dilakukan ketika klik mouse terdeteksi. Karena saat xswitcher diaktifkan tidak ada detail tentang "di mana mereka mengklik", kami menyetel ulang buffer secara default. Namun di terminal (misalnya) Anda tidak perlu melakukan ini (biasanya).

4. Satu (atau beberapa) rangkaian klik memicu satu atau beberapa kaitan

# 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. Terintegrasi, dengan nama “berbicara” (NewWord, NewSentence, Compose) dan dapat diprogram.

Nama yang dapat diprogram dimulai dengan “Aksi”. Karena TOML v1.4, nama titik harus diapit tanda petik.

Setiap bagian harus dijelaskan di bawah ini dengan nama yang sama.

Agar tidak mengejutkan orang dengan pelanggan tetap yang “telanjang” (dari pengalaman, mereka untuk menulismungkin satu dari sepuluh profesional), saya segera menerapkan sintaks tambahan.

  • "MATI:" (atau "AKTIF:") sebelum regexp (ekspresi reguler) mengharuskan tombol berikut dilepaskan (atau ditekan).
    Selanjutnya saya akan membuat ekspresi reguler yang “tidak adil”. Dengan pemeriksaan terpisah potongan antar pipa "|". Untuk mengurangi jumlah record seperti "[LR]_SHIFT" (yang jelas tidak diperlukan).
  • "SEQ:" Jika kondisi sebelumnya terpenuhi (atau tidak ada), maka kita periksa dengan ekspresi reguler “normal”. Untuk lebih jelasnya, saya langsung kirim ke ^W perpustakaan “regexp”. Karena saya masih belum repot-repot mencari tahu tingkat kompatibilitasnya dengan pcre favorit saya (“kompatibel dengan Perl”).
  • Ekspresi tersebut ditulis dalam bentuk "BUTTON_1: KODE1, TOMBOL_2: KODE2" dll., sesuai urutan penerimaan kode pindaian.
  • Cek tersebut selalu “dipasang” hingga akhir urutan, jadi tidak perlu menambahkan “$” di bagian ekor.
  • Semua pemeriksaan dalam satu baris dilakukan satu demi satu dan digabungkan dengan "aku". Namun karena nilainya digambarkan sebagai array, Anda dapat menulis tanda centang alternatif setelah koma. Jika ini diperlukan karena alasan tertentu.
  • Nilai "Panjang Urutan = 8" membatasi ukuran buffer yang digunakan untuk melakukan semua pemeriksaan. Karena Saya (sampai sekarang) belum pernah menemukan sumber daya yang tiada habisnya dalam hidup saya.

5. Memasang pengait yang dijelaskan pada bagian sebelumnya

# 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 "Aksi = [Array]". Mirip dengan bagian sebelumnya, ada serangkaian tindakan bawaan yang terbatas. Dan kemungkinan docking pada prinsipnya tidak terbatas (tulis “Action.XXX” dan jangan terlalu malas untuk menulis bagian lain untuk itu).
Secara khusus, pengetikan ulang sebuah kata dalam tata letak yang dikoreksi dibagi menjadi dua bagian: “ubah tata letak seperti yang ditentukan di sana” и “ketik ulang” (“Ketik Ulang Kata”).

Parameter lainnya ditulis ke “kamus” ("peta" dalam golang) untuk tindakan tertentu, daftarnya bergantung pada apa yang tertulis di "Tindakan".

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

Saya segera mengatur tindakan “Exec” untuk menjalankan skrip eksternal. Dengan opsi untuk mendorong buffer yang direkam ke stdin.

  • “Tunggu = 1” — tunggu hingga proses yang berjalan selesai.
  • Mungkin, "ke tumpukan" Anda ingin memasukkan orang tambahan ke dalam lingkungan. informasi seperti nama kelas jendela tempat ia disadap.
    “Apakah Anda ingin menghubungkan pawang Anda? Di sinilah Anda harus pergi.”

Fiuh (menghembuskan napas). Sepertinya saya belum melupakan apa pun.

Ups! Ya, aku tidak lupa...
Di mana konfigurasi peluncurannya? Dalam kode keras? Seperti itu:

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

Dimana saya lupa/melakukan kesalahan? (tidak mungkin tanpa ini), Saya sangat berharap para pembaca yang penuh perhatian tidak akan terlalu malas untuk menyodok.

Good luck!

Sumber: www.habr.com

Tambah komentar