Pembetulan susun atur Xswitcher untuk Linux: langkah dua

Sebagai penerbitan sebelumnya (xswitcher pada peringkat "bukti konsep") menerima banyak maklum balas yang membina (yang bagus), saya terus menghabiskan masa lapang saya membangunkan projek itu. Sekarang saya ingin menghabiskan sedikit daripada anda... Langkah kedua bukanlah perkara biasa: cadangan/perbincangan reka bentuk konfigurasi.

Pembetulan susun atur Xswitcher untuk Linux: langkah dua

Entah bagaimana ternyata pengaturcara biasa mendapati ia amat membosankan untuk menyediakan semua kawalan ini.

Untuk tidak menjadi tidak berasas, di dalamnya adalah contoh apa yang saya hadapi.
Secara keseluruhannya sangat baik (dan dilaksanakan dengan baik) Apache Kafka & ZooKeeper.
- Konfigurasi? Tetapi ia membosankan! xml bodoh (kerana ia "di luar kotak").
- Oh, adakah anda juga mahukan ACL? Tetapi ia sangat membosankan! Ketuk-ketuk... Sesuatu seperti itu.

Tetapi dalam kerja saya ia adalah sebaliknya. Betul (sayangnya, hampir tidak pernah pertama kali) model yang dibina membolehkan anda meneruskan dengan lebih mudah dan semula jadi (Hampir) kumpulkan gambar rajah.

Saya baru-baru ini menjumpai artikel tentang Habré tentang kerja keras saintis data...
Ternyata detik ini direalisasikan sepenuhnya untuk mereka. Dan dalam amalan saya, seperti yang mereka katakan, "versi ringan". Model berbilang volum, pengaturcara berpengalaman dengan OOP sedia, dsb. — ini semua akan muncul kemudian apabila/jika ia berlepas. Tetapi pereka perlu bermula di suatu tempat di sini dan sekarang.

Sampai ke intinya. Saya mengambil TOML sebagai asas sintaksis daripada warganegara ini.

Kerana dia (TOML) di satu pihak, boleh disunting oleh manusia. Sebaliknya, ia diterjemahkan 1:1 ke dalam mana-mana sintaks yang lebih biasa: XML, JSON, YAML.
Selain itu, pelaksanaan yang saya gunakan daripada "github.com/BurntSushi/toml", walaupun bukan yang paling bergaya (masih 1.4 sintaks), serasi secara sintaksis dengan JSON ("terbina dalam") yang sama.

Iaitu, jika anda mahu, anda hanya boleh mengatakan "pergi melalui hutan dengan TOML anda, saya mahu XXX" dan "tampal" kod dengan hanya satu baris.

Oleh itu, jika anda ingin menulis beberapa tingkap untuk mengkonfigurasi xswitcher (Saya tidak pasti) Tiada masalah dijangka "dengan konfigurasi sialan anda ini."

Untuk semua yang lain, sintaks adalah berdasarkan "kunci = nilai" (dan secara literal beberapa pilihan yang lebih rumit, seperti = [beberapa, itu, tatasusunan]) saya agak
mudah secara intuitif.
Apa yang menarik ialah "terbakar" pada masa yang sama (sekitar 2013). Hanya, tidak seperti saya, pengarang TOML masuk pada skala yang betul.

Oleh itu, kini lebih mudah bagi saya untuk menyesuaikan pelaksanaannya agar sesuai dengan diri saya, dan bukan sebaliknya.

Secara umum, kami mengambil TOML (sangat serupa dengan Windows INI lama). Dan kami mempunyai konfigurasi di mana kami menerangkan cara melampirkan satu siri cangkuk bergantung pada set kod imbasan terkini dari papan kekunci. Di bawah, sekeping demi sekeping, adalah apa yang telah berlaku setakat ini. Dan penjelasan mengapa saya memutuskan cara ini.

0. Abstraksi asas

  • Penamaan kod imbasan. Sesuatu yang pasti perlu dilakukan tentang perkara ini, kerana hanya kod digital sama sekali tidak boleh dibaca manusia (itu hanya saya loloswitcher).
    Saya menolak "ecodes.go" daripada "golang-evdev" (saya terlalu malas untuk melihat sumber asal, walaupun pengarang menunjukkannya secara budaya). Saya membetulkan sedikit (buat masa ini) sesuatu yang agak menakutkan. Seperti “LEFTBRACE” → “L_BRACE”.
  • Selain itu, beliau memperkenalkan konsep "kunci negeri". Oleh kerana tatabahasa biasa yang digunakan tidak membenarkan petikan yang panjang. (Tetapi ia membolehkan anda menyemak dengan overhed minimum. Jika anda hanya menggunakan rakaman "langsung".)
  • Akan ada "penyahduplikator" terbina dalam apa yang ditekan. Oleh itu, keadaan "ulang"=2 akan ditulis 1 kali

1. Bahagian 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])"

Apakah kandungan perkataan bahasa manusia dengan tatatanda fonetik? (sama ada masalah grafem aka "hieroglyph")? Beberapa jenis "lembaran" yang mengerikan. Oleh itu, saya segera memperkenalkan konsep "template".

2. Perkara yang perlu dilakukan apabila sesuatu diklik (kod imbasan 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"]

Terdapat 768 kod secara keseluruhan. (Tetapi "sekiranya" saya memasukkan menangkap "kejutan" ke dalam kod xswitcher).
Di dalam saya menerangkan mengisi tatasusunan dengan pautan ke fungsi "apa yang perlu dilakukan". Dalam golang ni (tiba-tiba) Ia ternyata mudah dan jelas.

  • Saya bercadang untuk mengurangkan "Jatuhkan" kepada minimum di tempat ini. Memihak kepada pemprosesan yang lebih fleksibel (saya akan tunjukkan di bawah).

3. Meja dengan kelas tingkap

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

Barisan meja berada dalam kurungan segi empat sama dua dengan namanya. Ia tidak mungkin menjadi lebih mudah dengan segera. Bergantung pada tetingkap yang sedang aktif, anda boleh memilih pilihan berikut:

  • Set "kunci panas" anda sendiri "Tindakan = ...". Jika tidak/kosong, jangan buat apa-apa.
  • Tukar "MouseClickDrops" - perkara yang perlu dilakukan apabila klik tetikus dikesan. Memandangkan pada ketika xswitcher dihidupkan, tiada butiran tentang "tempat mereka mengklik", kami menetapkan semula penimbal secara lalai. Tetapi dalam terminal (contohnya) anda tidak perlu melakukan ini (biasanya).

4. Satu (atau beberapa) urutan klik mencetuskan satu atau satu lagi cangkuk

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

Cangkuk terbahagi kepada dua jenis. Terbina dalam, dengan nama "bercakap" (NewWord, NewSentence, Compose) dan boleh diprogramkan.

Nama boleh atur cara bermula dengan "Tindakan." Kerana TOML v1.4, nama dengan titik mestilah dalam petikan.

Setiap bahagian hendaklah diterangkan di bawah dengan nama yang sama.

Agar tidak meniup fikiran orang dengan orang biasa "telanjang" (dari pengalaman, mereka tulismungkin satu daripada sepuluh profesional), saya segera melaksanakan sintaks tambahan.

  • "MATI:" (atau "HIDUP:") sebelum regexp (ungkapan biasa) memerlukan butang berikut dilepaskan (atau ditekan).
    Seterusnya saya akan membuat ungkapan biasa yang "tidak adil". Dengan pemeriksaan berasingan kepingan antara paip "|". Untuk mengurangkan bilangan rekod seperti "[LR]_SHIFT" (di mana ini jelas tidak perlu).
  • "SEQ:" Jika syarat sebelumnya dipenuhi (atau tidak hadir), maka kami menyemak dengan ungkapan biasa "biasa". Untuk mendapatkan butiran, saya segera menghantar ke ^W perpustakaan "regexp". Kerana saya masih tidak bersusah payah untuk mengetahui tahap keserasian dengan pcre kegemaran saya (“perl compatible”).
  • Ungkapan ditulis dalam bentuk "BUTTON_1: CODE1, BUTTON_2: CODE2" dsb., mengikut susunan kod imbasan diterima.
  • Cek sentiasa "disepit" ke penghujung urutan, jadi tidak perlu menambah "$" pada ekor.
  • Semua semakan dalam satu baris dilakukan satu demi satu dan digabungkan dengan "I". Tetapi memandangkan nilai diterangkan sebagai tatasusunan, anda boleh menulis semakan alternatif selepas koma. Jika ini diperlukan atas sebab tertentu.
  • Nilai "SeqLength = 8" mengehadkan saiz penimbal yang mana semua pemeriksaan dilakukan. Kerana Saya (sehingga kini) tidak pernah menemui sumber yang tidak berkesudahan dalam hidup saya.

5. Menetapkan cangkuk yang diterangkan dalam bahagian 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.

Perkara utama di sini ialah "Tindakan = [Array]". Sama seperti bahagian sebelumnya, terdapat set tindakan terbina dalam yang terhad. Dan kemungkinan dok tidak terhad pada dasarnya (tulis "Action.XXX" dan jangan terlalu malas untuk menulis bahagian lain untuknya).
Khususnya, menaip semula perkataan dalam reka letak yang diperbetulkan dibahagikan kepada dua bahagian: "ubah susun atur seperti yang dinyatakan di sana" и “taip semula” (“Taip Semula”).

Parameter selebihnya ditulis ke "kamus" ("peta" dalam golang) untuk tindakan tertentu, senarai mereka bergantung pada perkara yang ditulis dalam "Tindakan".

Beberapa tindakan berbeza boleh diterangkan dalam satu timbunan (bahagian). Atau anda boleh memisahkannya. Seperti yang saya tunjukkan di atas.

Saya segera menetapkan tindakan "Exec" untuk melaksanakan skrip luaran. Dengan pilihan untuk menolak penimbal yang direkodkan ke dalam stdin.

  • “Tunggu = 1” — tunggu sehingga proses berjalan selesai.
  • Mungkin, "ke timbunan" anda akan mahu meletakkan orang tambahan dalam persekitaran. maklumat seperti nama kelas tetingkap dari mana ia dipintas.
    “Awak nak sambung penangan awak? Di sinilah anda perlu pergi.”

Fuh (dihembus). Nampaknya saya tidak lupa apa-apa.

Aduh! Ya, saya tidak lupa...
Di manakah konfigurasi pelancaran? Dalam kod keras? Macam 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.

Di manakah saya terlupa/membuat kesilapan? (tiada cara tanpa ini), saya sangat berharap agar pembaca yang prihatin tidak terlalu malas untuk mencucuk hidung.

Good luck!

Sumber: www.habr.com

Tambah komen