Լայնաձող «хswitcher» համար linux: երկրորդ քայլ

Որպեսզի նախորդ հրապարակումը (հswitcher-ի «proof of concept» փուլում) ստացել է բավական շատ կառուցողական արձագանքներ (ինչպես հաճելի է), ես շարունակել եմ բացահայտ ժամանակս ծախսել նախագծի զարգացման վրա։ Այժմ ցանկանում եմ ծախսել մի քիչ ձեր… Երկրորդ քայլը կլիներ ոչ այնքան սովորական. առաջարկ/քննարկում կոնֆիգուրացիայի մոդելի մասին։

Լայնաձող «хswitcher» համար linux: երկրորդ քայլ

Ինչպես որ ստացվում է, որ սովորական ծրագրավորողներին այդ բոլոր կրիչների ճշգրտումը դուրս է շատ ձանձրալի։

Որպեսզի չլինի դատարկ խոսակցություն, ներսում նույնպես օրինակ մի բան, ինչը ես զբաղվում եմ։
Հիանալի քաղաքակիրթ մտածված (և լավ իրականացված) Apache Kafka & ZooKeeper։
— Կոնֆիգուրացիա? Բայց դա այնքան ձանձրալի է! Ցածր-բարձր xml (քանի որ «ա քուրտի» է):
— Օյ, իսկ դուք էլ ACL ունեք ուզում? Բայց դա այնքան հոգնեցնող է! Թափ-թփ… Ինչ-որ այնպես։

Իմ աշխատանքում՝ միանգամայն հակառակ։ Ճիշտ է (ափսոս, առաջին անգամ առաջ չի գալիս) կառուցված մոդելը թույլ է տալիս հեշտ ու բացարձակապես (պատկերավոր) հավաքել սխեման։

Մոտեցավ վերջերս մի հոդված, որը խոսում էր data-scientist-ների դժվար աշխատանքների մասին…
Դա պարզվեց, որ այդ պահը նրանք լիովին իրականացնում են։ Իսկ իմ պրակտիկայում, ինչպես ասում են, «լոնի տարբերակը»: Շատհատ հրատարակող մոդելներ, մեզ ոչ միներկ մեբող ծրագրավորողներ, OOP-ի հետ և այլն։ Դա ամեն ինչ հետո կհայտնվի, երբ/եթե բարձրանա։ Իսկ կոնստրուկտորը տալիս է այստեղ և հիմա սկսելու գործընթաց։

Դիտենք գործին։ Նիշային հիմքում ես վերցրել եմ TOML այս քաղաքացուց.

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

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

Այսպիսով, ցանկության դեպքում գրելու համար setting hSwitcher ինչ-որ պատուհաններ (ճիշտ չեմ ես) դifficulties «այդ ձեր խալխով կոնֆիգով» — չեն սպասվում։

Շրջաններ բոլոր այլքերով նշված «արտաքին = արժեք» (և բառացի մի քանի ընտրանք՝ ավելի դժվար, օրինակ = [ինչ-որ, զանգված]) ես կարծում եմ
համաձայնաբար հարմար։
Իսկ ինչ-որ հետաքրքիր է, հենց ես «ջերոս» լրիվ դրա շուրջ (մոտ 2013 թվականին): Բայց, զուգահեռ, այլություն՝ TOML-ի հեղինակն շրջել է հաճոյությամբ։

Այսպիսով, հիմա ինձ շատ ավելի հեշտ է հարմարեցնել իր իրականացմանը ինձ, այլ ոչ թե հակառակի։

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

0. Հիմնաբառերն

  • Սկան-կոդերի նշում։ Դա unbedingt պետք է անել ինչ-որ բան, քանի որ ոչ աղյուսակի կոդերը բացարձակապես մարդկային ընթերցելի չեն (այսքան ես այգում) loloswitcher).
    «ecodes.go»-ն ՝ «golang-evdev»-ից (առաջին աղբյուրին մտել եմ ծույլ, թեև հեղինակն ինքը հստակ նշել է)։ Փոքր-ինչ (այժմ) շտկել եմ մասին շատ սարսափելի։ Ենթադրենք «LEFTBRACE» → «L_BRACE»։
  • Լրացուցիչ տեղափոխեցի «ժամանակի» գաղափարը։ Չնայած օգտագործված կանոնական գրագիտությունը չի վհատվեք երկար քայլեր։ (Տարբերաբար թույլ է տալիս ստուգել մինիմալ ծախսերով։ Եթե միայն «ուղղակի» գրառումը օգտագործես։)
  • Կլինի ինտեգրված «դեդուպլիկատոր» սեղմված։ Այսպիսով, «կրկնման» պետությունը = 2 կմիավորում է մեկ միջոց։

1. Ամբողջումներ բաժինը

[Templates] # "@name@" արտահայտություններն արդիականացնելու համար
 # Քաղաքները կարող են բաղկացած լինել այս նշաններից (regexp)
 "WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"

Ինչպիսիք են մարդկային խոսքի բառերը ֆոնետիկ գրանցմամբ (ինչպես գրախաղեր aka «յերոգլիֆներ»)? Какая-то ужасная «простыня». Поэтому сразу закладываю понятие «шаблона».

2. Ինչ անել, երբ ինչ-որ բան սեղմում է (ուժեղացել է նոր սկեն-կոդ)

[ActionKeys]
 # Սահմանել բանալին և կատարել զննություն հրամանների հերթականության համար
 # !!! Կրկնվող կոդերը (code=2) պետք է հավաքվեն մեկ անգամ յուրաքանչյուր բանալիի համար!
 Չափել = ["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"]

 # Գումարել բոլոր հավաքված բանալիները, ներառյալ սա։  Սա է սովորական գործողությունը։
 Սառեցնել = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
 # Խնայել լրացուցիչ քարտեզ այս բանալիների համար, երբ դրանք "ջրհեղեղի" պետությունն են։
 # Պետությունը ստուգվում է ըստ "OFF:"|"ON:" պայմանների գործողության մեջ։
 # (Այնպես էլ, այս բանալիների պետությունը պետք է մնա անվճար աշակերտներից հետո։)
 # ??? Ինչպե՞ս գործել CAPS և "LOCK"-բանալիքների հետ ???
 Պետության բանալիներ = ["L_CTRL", "L_SHIFT", "L_ALT", "L_META", "CAPS", "N_LOCK", "S_LOCK",
              "R_CTRL", "R_SHIFT", "R_ALT", "R_META"]

 # Միայն փորձարկում, բայց մի գումարեք։
 # Օրինակ, ես օգտագործում եմ F12 փոխարեն BREAK հանդուրժելի լապտոպների վրա, որոնց հասակները վատ են (նոր ThinkPads)
 Փորձարկում = ["F1..F10", "ZENKAKUHANKAKU", "102ND", "F11", "F12",
          "RO..KPJPCOMMA", "SYSRQ", "SCALE", "HANGEUL..YEN",
          "STOP..SCROLLDOWN", "NEW..MAX"]

Ընդհանուր առմամբ նախատեսված է 768 ծածկագիր։ (Բայց «հայտարարելու համար» տեղադրեցի կոդում xswitcher պատռելը «surprise»-երի համար)
Մեջ շարադրել եմ զանգվածը ներկառուցելու համար «սկսելու» ֆունկցիաներով։ Այս golang դեպքո՜ւ (հանկարծակի) հայտնվեց հարմար և ակնհայտ։

  • «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"

Աղյուսակի շարքերը ՝ երկակի քառակուսի գծերի մեջ իր անվանումով։ Վաճառքի սկսելով հեշտ չէ ստացվել։ Ներկա ակտիվ օվալից կախված, կարող եք ընտրել տարբերակներ՝

  • Սեփական հավաքածու "առնվազն ծվարեց " 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) և ծրագրավորվող։

Ծրագրավորվող անունները սկսում են «Action.»-ից։ Քանի որ TOML v1.4, անունները պարբերաբար պետք է լինել մեջ դրված։

Ահա ներսում յուրաքանչյուրի մասին բաժինը պետք է նկարագրվի այդ նույն անվանումով.

Որպեսզի մարդկանց ուղեղներն «մերկ» կարգով չպայթեցնեմ (փորձով, դրանց գրեք-ը կարող է մեկ տասից մասնագետների), անմիջապես ներդրում եմ հավելյալ სინտաքսիս:

  • «OFF:» (կամ «ON:») առաջին regexp (կարգավորող արտահայտություն) պահանջում է, որպեսզի կարգված գր.buttons-ը բաց թողնվեն (կամ մուդարեն):
    Հետագայում պատրաստվում եմ անել «անարդար» կարգավորող արտահայտություն: Կտորների առանձնացված ստուգմամբ "|" միջեւ: Ու նպատակը նվազեցնել «[LR]_SHIFT» տիպի գրառումների քանակը (այնտեղ, որտեղ դա ակնհայտորեն չի անհրաժեշտ):
  • «SEQ:» Եթե նախորդ պայմանը կատարված է (կամ բացակայում է), ապա ստուգում ենք «համեմատական» կարգավորող արտահայտության առնչությամբ: Մանրամասնությունների համար անմիջապես ուղարկում եմ պրոցեսոր «regexp»-ին։ Որովհետև ես մինչեւ հիմա չեմ կարողացել պարզել համատեղելիության աստիճանը իմ սիրելի pcre-ների հետ («perl compatible»):
  • Արտահայտությունը գրանցվում է որպես «ԲՈՏՈՆ_1: ԿՈԴ1, ԲՈՏՈՆ_2: ԿՈԴ2» և այլն, ստացված ստացած կոդի հերթականությամբ:
  • Ստուգումը միշտ «պատռում է» հաջորդականության վերջում, այնպես որ "$" ավարտին ավելացնել պետք չէ:
  • Բոլոր ստուգումները մեկ շարքում կատարվում են մեկը մեկի հետևից և միանում են «Երկու». Բայց որովհետեւ արժեքը նկարագրված է որպես զանգված, կարելի է կետից հետո գրել այլ բան. Եթե դա ինչ-որ բան է պետք.
  • Արժեք «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 = [Զանգված]». Ինչպես նախորդ հատվածում, կա սահմանափակ հավաքածու ներմուծված գործողությունների. Եվ առանց սահմանափակումը հնարավորություններից (գրել «Action.XXX» և փութաքառություն կազմելու համար ուրիշը հատված).
Նորից փոխել խոսքը բաժանված ձևակերպումից բաժանվում է երկու մասի՝ «փոփոխեք ձևակերպումը ինչպես գործել է այնտեղ» և «փոխարենը» («RetypeWord»).

Այլ պարամետրերը գրանցվում են «լեքսիկոնում» («map» golang-ում) այս գործողության համար, դրանց ցանկը կախված է «Action»-ում գրածից:

Նշանակել տարբեր գործողություններ մեկ խումբում (սեկտոր). Հնարավոր է քանդել։ Ինչպես ես արդեն ցույց եմ տվել:

Թույլ եմ տալիս գործել «Exec» — կատարել արտաքին սցենար։ Ու հնարավորություն ունենալու այդ buffer-ը ներմուծել stdin:

  • «Wait = 1» — սպասում է մեկնարկված պրոցեսի ավարտին:
  • Հավատալիորեն, «անգամեթ» ցանկանում եք ծածկել հավելյալ տեղեկություն, օրինակ, պատուհանի դասի անունը, որից վերցված է:
    «Արդյո՞ք ցանկանում եք միացնել ձեր մշակողը: Դուք այստեղ եք։»

Ուֆ (քաշեցի շնչառություն): Ստացվում է, թե ոչինչ չենք մոռացել:

Օպ! Այո, չեմ մոռացել…
Դուք որտեղ եք մեկնարկման կոնֆիգուրացիան? Հիմնականում մոտավոր ադապտացիայում:

[ScanDevices]
 # Անհրաժեշտ է գոյություն ունենալ սկզբում։ Ապա ինքնուրույն վերականգնվել, եթե երիտասարդ է 30 վայրկյանից պակաս
 Test = "\/dev\/input\/event0"
 Respawn = 30
 # Փնտրման դիմակը
 Search = "\/dev\/input\/event*"
 # Իմ ThinkPads-ում կան նման իծ-կլյանքներ, որոնցում շատ ավելորդ իրադրություններներ են
 Bypass = "(?i)Video|Camera" # "(?i)" ակնհայտորեն տարբերվում է " clásico" pcre-ներից։

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

Բարև ձեզ!

Ընտանիք: habr.com

Գնել հուսալի հյուրընկալում DDoS պաշտպանությամբ, VPS VDS սերվերներով 🔥 Գնել հուսալի հյուրընկալում DDoS պաշտպանությամբ, VPS VDS սերվերներով | ProHoster