Linux-д зориулсан Xswitcher байршлын засварлагч: хоёр дахь алхам

Оноос хойш өмнөх хэвлэл ("үзэл баримтлалын баталгаа" үе шатанд xswitcher) маш олон бүтээлч санал хүсэлтийг хүлээн авсан (сайхан байна), Би чөлөөт цагаа үргэлжлүүлэн төслийг боловсруулахад зарцуулсан. Одоо би чиний жаахан ч гэсэн зарцуулмаар байна... Хоёр дахь алхам нь ердийн зүйл биш байх болно: тохиргооны дизайны санал/хэлэлцүүлэг.

Linux-д зориулсан Xswitcher байршлын засварлагч: хоёр дахь алхам

Ямар нэгэн байдлаар энгийн програмистуудад эдгээр бүх хяналтыг тохируулах нь уйтгартай санагддаг.

Үндэслэлгүй байхын тулд дотор нь миний харьцаж байгаагийн жишээ юм.
Ерөнхийдөө маш сайн зохиосон (мөн сайн хэрэгжсэн) Apache Kafka & ZooKeeper.
- Тохиргоо? Гэхдээ уйтгартай байна! Дүлий xml (учир нь энэ нь "хайрцагнаас гарсан").
- Өө, чи бас ACL авахыг хүсч байна уу? Гэхдээ энэ үнэхээр уйтгартай юм! Tap-blooper... Тиймэрхүү зүйл.

Харин миний ажилд яг эсрэгээрээ. Зөв (харамсалтай, анх удаа бараг хэзээ ч байгаагүй) бүтээгдсэн загвар нь танд илүү хялбар, байгалийн байдлаар үргэлжлүүлэх боломжийг олгодог (Бараг л) диаграммыг угсарна.

Би саяхан Хабре дээр өгөгдөл судлаачдын шаргуу хөдөлмөрийн тухай нийтлэл олж харлаа...
Энэ мөч тэдний хувьд бүрэн хэрэгжсэн нь харагдаж байна. Миний практикт "хөнгөн хувилбар" гэж хэлдэг. Олон боть загварууд, бэлэн OOP бүхий туршлагатай програмистууд гэх мэт. - энэ бүхэн дараа нь хөөрөх үед/хэрэв гарч ирэх болно. Гэхдээ дизайнер энд, одоо хаа нэгтээ эхлэх хэрэгтэй.

Гол зорилгодоо хүр. Би TOML-ийг синтаксийн үндэс болгон авсан энэ иргэнээс.

Учир нь тэр (TOML) нэг талаас хүний ​​гараар засварлах боломжтой. Нөгөөтэйгүүр, XML, JSON, YAML гэсэн нийтлэг синтаксуудын аль нэгэнд 1:1-ээр орчуулагддаг.
Нэмж дурдахад, миний "github.com/BurntSushi/toml" сайтаас ашигласан хэрэгжүүлэлт нь хамгийн загварлаг биш ч гэсэн (1.4 синтакс) ижил JSON-тай синтаксийн хувьд нийцдэг.

Өөрөөр хэлбэл, хэрэв та хүсвэл "энэ TOML-тэйгээ ой дундуур яваарай, би XXX хүсч байна" гэж хэлээд кодыг зөвхөн нэг мөрөнд "засах" боломжтой.

Тиймээс, хэрэв та xswitcher-ийг тохируулах цонх бичихийг хүсвэл (Би итгэлтэй биш байна) "Таны энэ хараал идсэн тохиргоонд" ямар ч асуудал гарахгүй.

Бусад бүх хүмүүсийн хувьд синтакс нь "түлхүүр = утга" дээр суурилдаг. (мөн = [зарим, тэр, массив] гэх мэт хэд хэдэн илүү төвөгтэй сонголтууд) Би бодож байна
зөн совингийн хувьд тохиромжтой.
Хамгийн сонирхолтой нь тэр "шатсан" ойролцоогоор ижил хугацаанд (ойролцоогоор 2013 он). Зөвхөн надаас ялгаатай нь TOML-ийн зохиогч зохих хэмжээгээр орсон.

Тиймээс одоо түүний хэрэгжилтийг өөртөө тохируулан тохируулах нь надад илүү хялбар болсон, харин эсрэгээр биш.

Ерөнхийдөө бид TOML (хуучин Windows INI-тай маш төстэй) авдаг. Мөн бид гар дээрх хамгийн сүүлийн скан кодуудын багцаас хамааран хэд хэдэн дэгээ хэрхэн холбохыг тайлбарласан тохиргоотой. Өнөөдрийг хүртэл юу болсныг доор, хэсэг хэсгээр нь харуулав. Тэгээд яагаад ингэж шийдсэн тухай тайлбар.

0. Үндсэн хийсвэрлэлүүд

  • Кодын тэмдэглэгээг сканнердах. Зүгээр л дижитал кодууд нь хүн унших боломжгүй байдаг тул энэ талаар ямар нэг зүйл хийх шаардлагатай байна (энэ бол зөвхөн би. loloswitcher).
    Би "golang-evdev"-ээс "ecodes.go"-г сэгсэрлээ (зохиогч үүнийг нэлээд соёлтой зааж өгсөн ч эх сурвалжийг харахаас залхуурсан). Би нэлээд айдас төрүүлсэн зүйлийг бага зэрэг (одоохондоо) зассан. “ЗҮҮН ЗҮҮН” → “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])"

Хүний хэлний авианы тэмдэглэгээтэй үг юунаас бүрддэг вэ? ("иероглиф" гэх графемийн асуудал)? Ямар нэг аймшигтай "хуудас". Тиймээс би "загвар" гэсэн ойлголтыг нэн даруй танилцуулж байна.

2. Ямар нэг зүйл товшвол яах вэ (өөр скан код ирсэн)

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

Нийтдээ 768 код байна. (Гэхдээ би xswitcher код руу "гэнэтийн бэлэг"-ийг оруулсан болно.
Дотор нь би массивыг "юу хийх вэ" функцүүдийн холбоосоор дүүргэхийг тайлбарласан. Голанг хэл дээр ийм байна (гэнэт) Энэ нь тохиромжтой бөгөөд ойлгомжтой болсон.

  • Би энэ газарт "Дусал" -ыг хамгийн бага хэмжээнд хүртэл бууруулахаар төлөвлөж байна. Илүү уян хатан боловсруулалтыг дэмжинэ (би үүнийг доор харуулах болно).

3. Цонхны ангилал бүхий хүснэгт

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

Хүснэгтийн мөрүүд нь түүний нэрээр давхар дөрвөлжин хаалтанд байна. Энэ нь арай хялбар байх байсангүй. Одоогоор идэвхтэй байгаа цонхноос хамааран та дараах сонголтуудыг сонгож болно.

  • Таны "халуун товчлуур" -ын багц "Үйлдэл = ...". Хэрэв үгүй/хоосон бол юу ч хийх хэрэггүй.
  • "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)" ]

Дэгээ нь хоёр төрөлд хуваагдана. Баригдсан, өөрөө тайлбарлах нэртэй (NewWord, NewSentence, Compose) болон програмчлах боломжтой.

Програмчлагдах нэр нь "Үйлдэл" гэж эхэлдэг. Учир нь TOML v1.4, цэгтэй нэрсийг хашилтанд оруулах ёстой.

Хэсэг бүрийг доор тайлбарлах ёстой ижил нэртэй.

Хүмүүсийн оюун санааг "нүцгэн" байнгын хүмүүсээр цохихгүйн тулд (туршлагаас, тэдний бичихмагадгүй арав тутмын нэг нь мэргэжлийн хүмүүс), Би нэн даруй нэмэлт синтаксийг хэрэгжүүлдэг.

  • "OFF:" (эсвэл "ON:") regexp (ердийн илэрхийлэл)-ээс өмнө дараах товчлууруудыг суллах (эсвэл дарах) шаардлагатай.
    Дараа нь би "шударга бус" тогтмол хэллэг хийх болно. Хоолойн хоорондох хэсгүүдийг тусад нь шалгах замаар "|". "[LR]_SHIFT" гэх мэт бичлэгийн тоог багасгахын тулд (энэ нь шаардлагагүй бол).
  • "Дараалал:" Хэрэв өмнөх нөхцөл хангагдсан (эсвэл байхгүй) бол бид "хэвийн" тогтмол илэрхийлэлтэй харьцуулна. Дэлгэрэнгүйг би шууд ^W руу "regexp" номын санг илгээнэ үү. Учир нь би өөрийн дуртай pcre ("perl нийцтэй")-тэй хэр нийцэж байгааг олж мэдэх гэж санаа зовоогүй хэвээр байна.
  • Илэрхийлэл нь хэлбэрээр бичигдсэн байна "BUTTON_1: CODE1, BUTTON_2: CODE2" гэх мэт скан кодыг хүлээн авах дарааллаар.
  • Чек нь үргэлж дарааллын төгсгөлд "нухсан" байдаг, тиймээс сүүл рүү нь "$" нэмэх шаардлагагүй.
  • Нэг мөрөнд байгаа бүх шалгалтыг нэг нэгээр нь хийдэг ба "би"-ээр нэгтгэгддэг. Гэхдээ утгыг массив гэж тодорхойлсон тул таслалын дараа өөр чек бичиж болно. Хэрэв энэ нь ямар нэг шалтгаанаар шаардлагатай бол.
  • үнэ цэнэ "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.

Хамгийн гол нь энд байна "Үйлдэл = [Массив]". Өмнөх хэсэгтэй адил хязгаарлагдмал багц үйлдлүүд бий. Мөн залгах боломж нь зарчмын хувьд хязгаарлагдахгүй ("Action.XXX" гэж бичээд өөр хэсэг бичихээс залхуурах хэрэггүй).
Тодруулбал, зассан байрлал дахь үгийг дахин бичих нь хоёр хэсэгт хуваагдана. "тэнд заасан байршлыг өөрчлөх" и "дахин бичих" ("Дахин бичих").

Үлдсэн параметрүүдийг "толь бичиг" дээр бичсэн болно. (голанг хэлээр "газрын зураг") өгөгдсөн үйлдлийн хувьд тэдгээрийн жагсаалт нь "Үйлдэл" хэсэгт юу бичсэнээс хамаарна.

Хэд хэдэн өөр үйлдлийг нэг багцад дүрсэлж болно (хэсгүүд). Эсвэл та үүнийг салгаж болно. Би дээр үзүүлсэн шиг.

Би гадаад скриптийг гүйцэтгэхийн тулд "Exec" үйлдлийг нэн даруй тохируулсан. Бүртгэгдсэн буферийг stdin руу түлхэх сонголттой.

  • "Хүлээ = 1" - ажиллаж байгаа процесс дуусах хүртэл хүлээнэ үү.
  • Магадгүй "овоолон" та хүрээлэн буй орчинд нэмэлт хүмүүсийг оруулахыг хүсэх байх. хаагдсан цонхны ангийн нэр зэрэг мэдээлэл.
    "Та зохицуулагчаа холбохыг хүсч байна уу? Чи энд л явах хэрэгтэй."

Хөө (амьсгаагаа гаргасан). Би юу ч мартаагүй юм шиг байна.

Өө! Тийм ээ, би мартаагүй ...
Эхлүүлэх тохиргоо хаана байна? Хатуу кодоор уу? Ингэж:

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

Би хаана мартсан/алдаа хийсэн бэ? (үүнгүйгээр ямар ч боломжгүй), Анхааралтай уншигчид хамраа нудрахаас залхуурахгүй гэдэгт би үнэхээр найдаж байна.

Амжилт хүсье!

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх