Xswitcher դասավորության ուղղիչ Linux-ի համար. քայլ երկրորդ

Ինչպես նախորդ հրապարակումը (xswitcher-ը «հայեցակարգի ապացույց» փուլում) բավականին շատ կառուցողական արձագանքներ ստացավ (ինչը հաճելի է), ազատ ժամանակս շարունակեցի տրամադրել նախագծի մշակմանը։ Հիմա ուզում եմ մի քիչ ծախսել քո... Երկրորդ քայլը այնքան էլ սովորական չի լինի՝ կոնֆիգուրացիայի դիզայնի առաջարկ/քննարկում:

Xswitcher դասավորության ուղղիչ Linux-ի համար. քայլ երկրորդ

Ինչ-որ կերպ պարզվում է, որ նորմալ ծրագրավորողների համար աներևակայելի ձանձրալի է այս բոլոր հսկիչները տեղադրելը:

Որպեսզի անհիմն չլինեմ, ներսում օրինակ է, թե ինչի հետ գործ ունեմ։
Ընդհանուր առմամբ, գերազանց մտածված (և լավ իրականացված) Apache Kafka & ZooKeeper:
- Կազմաձևո՞ւմ: Բայց դա ձանձրալի է! Համր xml (քանի որ այն «դուրս է»):
-Հա, դու նո՞ւյնպես ACL ես ուզում: Բայց դա այնքան ձանձրալի է: Tap-blooper... Նման մի բան։

Բայց իմ աշխատանքում ճիշտ հակառակն է. Ճիշտ (ավաղ, գրեթե երբեք առաջին անգամ) Կառուցված մոդելը թույլ է տալիս շարունակել ավելի հեշտ և բնական (Գրեթե) հավաքել դիագրամ.

Վերջերս ես հանդիպեցի Habré-ի մասին մի հոդվածի տվյալների գիտնականների քրտնաջան աշխատանքի մասին...
Ստացվում է, որ այս պահը նրանց համար լիովին իրացված է։ Իսկ իմ պրակտիկայում, ինչպես ասում են, «թեթեւ տարբերակ»։ Բազմածավալ մոդելներ, պատրաստված ծրագրավորողներ OOP-ով և այլն: — այս ամենը կհայտնվի ավելի ուշ, երբ/եթե այն հանվի: Բայց դիզայները պետք է ինչ-որ տեղից սկսի այստեղ և հիմա:

Անցեք կետին: Որպես շարահյուսական հիմք վերցրեցի TOML-ը այս քաղաքացուց.

Քանի որ նա (TOML) մի կողմից՝ մարդու կողմից խմբագրվող։ Մյուս կողմից, այն թարգմանվում է 1:1 առավել տարածված շարահյուսություններից որևէ մեկով՝ XML, JSON, YAML:
Ավելին, «github.com/BurntSushi/toml»-ից իմ օգտագործած իրականացումը, թեև ամենանորաձևը չէ (դեռ 1.4 շարահյուսություն), շարահյուսականորեն համատեղելի է նույն («ներկառուցված») JSON-ի հետ:

Այսինքն, եթե ցանկանում եք, կարող եք պարզապես ասել «անցիր անտառով քո այդ TOML-ով, ես ուզում եմ XXX» և «կարկատել» կոդը ընդամենը մեկ տողով։

Այսպիսով, եթե ցանկանում եք գրել որոշ պատուհաններ xswitcher-ը կարգավորելու համար (Ես վստահ չեմ) «Ձեր այս անիծյալ կոնֆիգուրացիայի հետ կապված խնդիրներ չեն սպասվում»:

Բոլոր մյուսների համար շարահյուսությունը հիմնված է «բանալին = արժեքի» վրա (և բառացիորեն մի քանի ավելի բարդ տարբերակներ, ինչպիսիք են = [որոշ, այն, զանգված]) կարծում եմ
ինտուիտիվ հարմար:
Հետաքրքիրն այն է «այրված» մոտավորապես նույն ժամանակահատվածում (մոտ 2013 թ.): Միայն թե, ի տարբերություն ինձ, ԹՈՄԼ-ի հեղինակը պատշաճ մասշտաբով մտավ։

Հետևաբար, հիմա ինձ համար ավելի հեշտ է հարմարեցնել դրա իրականացումը ինձ հարմարեցնելու համար, և ոչ հակառակը:

Ընդհանուր առմամբ, մենք վերցնում ենք TOML (շատ նման է հին Windows INI-ին): Եվ մենք ունենք կոնֆիգուրացիա, որտեղ մենք նկարագրում ենք, թե ինչպես կարելի է կցել մի շարք կեռիկներ՝ կախված ստեղնաշարի վերջին սկանավորման կոդերից: Ստորև, մաս առ մաս, ներկայացնում ենք այն, ինչ տեղի է ունեցել մինչ այժմ։ Եվ բացատրություն, թե ինչու ես այսպես որոշեցի.

0. Հիմնական աբստրակցիաներ

  • Սկան կոդի նշանակումները: Անպայման ինչ-որ բան պետք է արվի սրա հետ կապված, քանի որ պարզապես թվային կոդերը բացարձակապես ընթեռնելի չեն մարդու կողմից (դա միայն ես եմ loloswitcher).
    Ես ջնջեցի «ecodes.go»-ն «golang-evdev»-ից (չափազանց ծույլ էի նայել սկզբնաղբյուրը, թեև հեղինակը բավականին մշակութային է նշել): Ես մի փոքր ուղղեցի (առայժմ) մի բան, որը բավականին վախեցնող էր: Ինչպես «LEFTBRACE» → «L_BRACE»:
  • Բացի այդ, նա ներկայացրեց «պետական ​​բանալիներ» հասկացությունը: Քանի որ օգտագործված կանոնավոր քերականությունը թույլ չի տալիս երկար հատվածներ: (Բայց դա թույլ է տալիս ստուգել նվազագույն ծախսերով: Եթե օգտագործում եք միայն «ուղիղ» ձայնագրություն):
  • Կլինի սեղմվածի ներկառուցված «դյուպլիկատոր»: Այսպիսով, կգրվի «repeat»=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 կոդի մեջ):
Ներսում ես նկարագրեցի զանգվածը լրացնելը «ինչ անել» գործառույթների հղումներով: Գոլանգում սա է (հանկարծ) Պարզվեց, որ հարմար է և ակնհայտ։

  • Ես նախատեսում եմ այս վայրում նվազագույնի հասցնել «Drop»-ը: Ավելի ճկուն մշակման օգտին (ես ցույց կտամ ստորև):

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»-ի նման գրառումների քանակը նվազեցնելու համար (որտեղ դա ակնհայտորեն անհրաժեշտ չէ):
  • "SEQ:" Եթե ​​նախորդ պայմանը բավարարված է (կամ բացակայում է), ապա մենք ստուգում ենք «նորմալ» կանոնավոր արտահայտությունը: Մանրամասների համար ես անմիջապես ուղարկում եմ ^W «regexp» գրադարանը։ Քանի որ ես դեռ չեմ անհանգստացել պարզել իմ սիրելի pcre-ի հետ համատեղելիության աստիճանը («perl compatible»):
  • Արտահայտությունը գրված է ձևով «BUTTON_1. CODE1, BUTTON_2: CODE2» և այլն, ըստ սկանավորման կոդերի ստացման հերթականության:
  • Չեկը միշտ «կցվում է» հաջորդականության վերջում, ուստի կարիք չկա պոչին «$» ավելացնել։
  • Բոլոր ստուգումները մեկ տողում կատարվում են մեկը մյուսի հետևից և միավորվում են «ես»-ով: Բայց քանի որ արժեքը նկարագրված է որպես զանգված, կարող եք ստորակետից հետո գրել այլընտրանքային ստուգում: Եթե ​​դա ինչ-ինչ պատճառներով անհրաժեշտ է:
  • Արժեք «Հաջորդական երկարություն = 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» և մի ծույլ մի՛ եղեք դրա համար մեկ այլ բաժին գրել).
Մասնավորապես, շտկված դասավորության մեջ բառի վերատպումը բաժանված է երկու մասի. «փոխել դասավորությունը, ինչպես նշված է այնտեղ» и «վերատիպել» («RetypeWord»).

Մնացած պարամետրերը գրված են «բառարան» («քարտեզ» գոլանգով) տվյալ գործողության համար դրանց ցանկը կախված է նրանից, թե ինչ է գրված «Գործողություն»-ում:

Մի քանի տարբեր գործողություններ կարելի է նկարագրել մեկ կույտում (հատվածներ). Կամ դուք կարող եք այն քանդել: Ինչպես ցույց տվեցի վերևում.

Ես անմիջապես դրեցի «Exec» գործողությունը՝ արտաքին սկրիպտը գործարկելու համար: Ձայնագրված բուֆերը stdin-ի մեջ մղելու հնարավորությամբ:

  • «Սպասեք = 1» - սպասեք, մինչև ընթացիկ գործընթացն ավարտվի:
  • Հավանաբար, «դեպի կույտ» դուք կցանկանաք լրացուցիչ մարդկանց շրջապատել: տեղեկատվություն, ինչպիսին է պատուհանի դասի անունը, որտեղից այն գաղտնալսվել է:
    «Ցանկանու՞մ եք միացնել ձեր կառավարիչը: Սա այն վայրն է, որտեղ դուք պետք է գնաք»:

Phew (արտաշնչել): Կարծես ոչինչ չեմ մոռացել։

Վա՜յ Այո, ես չեմ մոռացել ...
Որտե՞ղ է մեկնարկի կոնֆիգուրացիան: Կոշտ կոդով? Դրա նման:

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

Որտե՞ղ եմ մոռացել/սխալվել: (առանց սրա ոչ մի կերպ), իսկապես հուսով եմ, որ ուշադիր ընթերցողները շատ չեն ծույլ քիթը խոթել։

Good luck!

Source: www.habr.com

Добавить комментарий