Korektor tata letak Xswitcher kanggo Linux: langkah loro

Wiwit publikasi sadurungé (xswitcher ing tataran "bukti konsep") nampa cukup akèh saran mbangun (sing apik), Aku terus nglampahi wektu luang ngembangaken proyek. Saiki aku pengin mbuwang sethithik sampeyan ... Langkah kapindho bakal ora cukup biasanipun: proposal / rembugan desain konfigurasi.

Korektor tata letak Xswitcher kanggo Linux: langkah loro

Piye wae dadi metu sing programer normal nemokake iku luar biasa boring kanggo nyetel kabeh kontrol iki.

Supaya ora tanpa dhasar, ing njero ana conto sing dakkarepake.
Sakabèhé disusun kanthi apik (lan ditindakake kanthi apik) Apache Kafka & ZooKeeper.
- Konfigurasi? Nanging mboseni! Bodho xml (amarga "metu saka kothak").
- Oh, sampeyan uga pengin ACL? Nanging dadi mboseni! Tap-blooper... Mergo.

Nanging ing karyaku pancen kosok balene. bener (Alah, meh ora pisanan) model dibangun ngijini sampeyan kanggo terus luwih gampang lan alamiah (meh) ngumpulake diagram.

Aku bubar nemokake artikel babagan Habré babagan kerja keras para ilmuwan data ...
Pranyata wektu iki wis rampung kanggo wong-wong mau. Lan ing praktikku, kaya sing diucapake, "versi ringan". Model multi-volume, programer berpengalaman kanthi OOP siap, lsp. - iki kabeh bakal katon mengko nalika / yen njupuk mati. Nanging desainer kudu miwiti nang endi wae ing kene lan saiki.

Njaluk menyang titik. Aku njupuk TOML minangka basis sintaksis saka warga iki.

Amarga dheweke (TOML) ing tangan siji, manungsa-editable. Ing sisih liya, diterjemahake 1: 1 menyang sintaksis sing luwih umum: XML, JSON, YAML.
Menapa malih, implementasine aku digunakake saka "github.com/BurntSushi/toml", senajan ora paling modis (isih 1.4 sintaksis), sintaksis kompatibel karo padha ("built-in") JSON.

Yaiku, yen sampeyan pengin, sampeyan mung bisa ngomong "liwat alas nganggo TOML sampeyan, aku pengin XXX" lan "patch" kode mung siji baris.

Mangkono, yen sampeyan pengin nulis sawetara windows kanggo ngatur xswitcher (Aku ora yakin) Ora ana masalah sing diarepake "karo konfigurasi sampeyan."

Kanggo kabeh liyane, sintaks adhedhasar "kunci = nilai" (lan secara harfiah sawetara opsi sing luwih rumit, kaya = [sawetara, sing, array]) ngaranku
intuisi trep.
Sing menarik iku "bakar" ing wektu sing padha (sekitar 2013). Mung, ora kaya aku, penulis TOML mlebu ing skala sing tepat.

Mula, saiki luwih gampang aku nyetel implementasine supaya cocog karo aku, lan ora kosok balene.

Umumé, kita njupuk TOML (padha karo Windows INI lawas). Lan kita duwe konfigurasi sing kita njlèntrèhaké carane masang seri pancingan gumantung ing pesawat saka kode scan paling anyar saka keyboard. Ing ngisor iki, potongan-potongan, apa sing kedadeyan nganti saiki. Lan panjelasan kenapa aku mutusake kanthi cara iki.

0. abstraksi dhasar

  • Jeneng kode scan. Ana sing kudu ditindakake babagan iki, amarga kode digital pancen ora bisa diwaca manungsa (mung aku loloswitcher).
    Aku guncang metu "ecodes.go" saka "golang-evdev" (Aku kesed kanggo katon ing sumber asli, sanajan penulis dituduhake cukup budaya). Aku mbenerake sethithik (saiki) sing cukup wedi. Kaya "LEFTBRACE" → "L_BRACE".
  • Kajaba iku, dheweke ngenalake konsep "tombol negara". Amargi tata basa ingkang dipunginakaken boten pareng wacan ingkang panjang. (Nanging ngidini sampeyan mriksa kanthi overhead minimal. Yen sampeyan mung nggunakake rekaman "langsung".)
  • Bakal ana "deduplikator" sing dibangun saka apa sing ditekan. Mangkono, negara "baleni" = 2 bakal ditulis один kaping.

1. bagean Cithakan

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

Tembung basa manungsa kanthi notasi fonetis kalebu apa? (salah siji masalah graphemes alias "hieroglyphs")? Sawetara jenis "sheet" elek. Mulane, aku langsung ngenalake konsep "template".

2. Apa sing kudu ditindakake nalika ana sing diklik (kode pindai liyane wis teka)

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

Ana 768 kode. (Nanging "mung ing kasus" Aku dipasang nyekel "kagetan" menyang kode xswitcher).
Nang aku diterangake ngisi array kanthi pranala menyang fungsi "apa sing kudu ditindakake". Ing golang iki (tiba-tiba) Pranyata trep lan ketok.

  • Aku rencana kanggo ngurangi "Drop" kanggo minimal ing panggonan iki. Ing sih saka Processing luwih fleksibel (Aku bakal nuduhake ing ngisor iki).

3. Tabel karo kelas jendhela

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

Larik meja kasebut ana ing kurung kotak dobel kanthi jeneng. Iku ora bisa wis luwih gampang langsung saka bat. Gumantung ing jendhela sing lagi aktif, sampeyan bisa milih opsi ing ngisor iki:

  • Set dhewe "tombol panas" "Tindakan = ...". Yen ora / kosong, ora nindakake apa-apa.
  • Ganti "MouseClickDrops" - apa sing kudu ditindakake nalika klik mouse dideteksi. Wiwit ing titik ing ngendi xswitcher diuripake ora ana rincian babagan "ing ngendi padha klik," kita ngreset buffer minangka standar. Nanging ing terminal (contone) sampeyan ora kudu nindakake iki (biasane).

4. Siji (utawa sawetara) urutan klik micu siji utawa liyane pancing

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

Pancing dipérang dadi rong jinis. Dibangun ing, kanthi jeneng "speaking" (NewWord, NewSentence, Compose) lan bisa diprogram.

Jeneng sing bisa diprogram diwiwiti kanthi "Tindakan". Amarga TOML v1.4, jeneng karo titik kudu ing kuotasi.

Saben bagean kudu diterangake ing ngisor iki kanthi jeneng sing padha.

Supaya ora nyebul pikirane wong kanthi rutin "wuda" (saka pengalaman, dheweke kanggo nulisMungkin siji saka sepuluh profesional), Aku langsung ngleksanakake sintaks tambahan.

  • "OFF:" (utawa "ON:") sadurunge regexp (ekspresi biasa) mbutuhake tombol ing ngisor iki dirilis (utawa dipencet).
    Sabanjure aku bakal nggawe ekspresi reguler "ora adil". Kanthi mriksa kapisah saka bêsik antarane pipo "|". Kanggo nyuda jumlah rekaman kaya "[LR] _SHIFT" (ing ngendi iki ora perlu).
  • "SEQ:" Yen kondisi sadurunge wis ketemu (utawa ora ana), banjur kita mriksa marang "normal" expression biasa. Kanggo rincian, aku langsung ngirim menyang ^W perpustakaan "regexp". Amarga aku isih durung keganggu kanggo mangerteni tingkat kompatibilitas karo pcre favorit ("perl kompatibel").
  • Ekspresi kasebut ditulis ing wangun "BUTTON_1: KODE1, TOMBOL_2: KODE2" etc., ing urutan kang kode scan ditampa.
  • Priksa tansah "snugged" kanggo mburi urutan, dadi ora perlu nambah "$" ing buntut.
  • Kabeh mriksa ing siji baris dileksanakake siji sawise liyane lan digabungake karo "Aku". Nanging amarga nilai kasebut diterangake minangka array, sampeyan bisa nulis cek alternatif sawise koma. Yen iki perlu kanggo sawetara alasan.
  • Nilai "SeqLength = 8" matesi ukuran buffer marang kang kabeh mriksa dileksanakake. Amarga Aku (nganti saiki) ora nate nemoni sumber daya tanpa wates.

5. Nyetel pancingan sing diterangake ing bagean sadurunge

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

Sing utama ing kene yaiku "Tindakan = [Array]". Kaya ing bagean sadurunge, ana sawetara tumindak sing diwatesi. Lan kamungkinan docking ora winates ing asas (tulis "Tindakan.XXX" lan aja kesed nulis bagean liyane).
Utamane, ngetik maneh tembung ing tata letak sing didandani dipérang dadi rong bagéan: "Ganti tata letak kaya sing wis ditemtokake ing kono" и "Ketik maneh" ("RetypeWord").

Parameter sing isih ana ditulis menyang "kamus" ("map" in golang) kanggo tumindak tartamtu, dhaftar sing gumantung apa ditulis ing "Tindakan".

Sawetara tumindak beda bisa diterangake ing siji tumpukan (bagean). Utawa sampeyan bisa narik loro. Kaya sing daktuduhake ing ndhuwur.

Aku langsung nyetel "Exec" tumindak kanggo nglakokaké script external. Kanthi pilihan kanggo push buffer direkam menyang stdin.

  • "Enteni = 1" - ngenteni proses mlaku rampung.
  • Mbokmenawa, "menyang tumpukan" sampeyan pengin nambah wong ing lingkungan. informasi kayata jeneng kelas jendhela saka sing dicegat.
    “Kowe arep nyambung pawangmu? Iki ngendi sampeyan kudu lunga."

Phew (ambegan). Kayane aku ora lali apa-apa.

Adhuh! Ya, aku ora lali ...
Ing endi konfigurasi peluncuran? Ing kode hard? Koyo kui:

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

Ngendi aku lali / nggawe kesalahan? (ora ana cara tanpa iki), Muga-muga sing maca sing ati-ati ora kesed nyopot irung.

Good luck!

Source: www.habr.com

Add a comment