Linux үчүн Xswitcher макет корректору: экинчи кадам

бери мурунку жарыялоо ("концепциянын далили" баскычында xswitcher) бир топ конструктивдүү пикирлерди алды (бул жакшы), Мен бош убактымды долбоорду иштеп чыгууга жумшадым. Эми мен сенин акчаңдан бир аз сарптагым келет... Экинчи кадам кадимкидей болбойт: конфигурация дизайнын сунуштоо/талкуулоо.

Linux үчүн Xswitcher макет корректору: экинчи кадам

Кандайдыр бир жол менен кадимки программисттерге бул башкаруу элементтеринин бардыгын орнотуу абдан кызыксыз экени белгилүү болду.

Негизсиз болбош үчүн, ичинде мен эмне менен алектенип жатканымдын бир мисалы болуп саналат.
Жалпысынан эң сонун ойлонулган (жана жакшы ишке ашырылган) Apache Kafka & ZooKeeper.
- Конфигурация? Бирок кызыксыз! Dumb xml (анткени ал "кутудан чыккан").
- Ой, сен да ACL каалайсыңбы? Бирок бул абдан кызыксыз! Тап-блопер... Ушундай бир нерсе.

Бирок менин чыгармачылыгымда мунун баары тескерисинче. Туура (Тилекке каршы, дээрлик эч качан биринчи жолу) курулган модели мындан ары да жеңил жана табигый улантууга мүмкүндүк берет (Дээрлик) диаграмма чогултуу.

Мен жакында Хабреде маалымат илимпоздорунун күжүрмөн эмгеги жөнүндө макалага туш болдум ...
Көрсө, бул учур алар үчүн толук ишке ашат экен. Ал эми менин практикамда, алар айткандай, "жеңил версия". Көп томдуу моделдер, даяр OOP менен тажрыйбалуу программисттер ж.б. — мунун баары кийинчерээк ал учуп чыкканда пайда болот. Бирок дизайнер бул жерде жана азыр бир жерден баштоо керек.

пунктуна кел. Мен TOMLди синтаксистик негиз катары алдым бул жарандан.

Анткени ал (TOML) бир жагынан, адам тарабынан түзөтүлөт. Башка жагынан алганда, ал жалпы синтаксистердин каалаганына 1:1 которулат: XML, JSON, YAML.
Анын үстүнө, мен “github.com/BurntSushi/toml” сайтынан колдонгон ишке ашыруу эң модалуу болбосо да (дагы 1.4 синтаксис), синтаксистик жактан ошол эле (“курулган”) JSON менен шайкеш келет.

Башкача айтканда, эгер кааласаңыз, сиз жөн гана "ошол TOML менен токойду аралап өтүңүз, мен ХХХ каалайм" деп айта аласыз жана кодду бир эле сап менен "жамашат".

Ошентип, эгер сиз xswitcherди конфигурациялоо үчүн кээ бир терезелерди жазгыңыз келсе (Мен ишенбейм) "Сиздин бул каргыш конфигурацияңыз менен" эч кандай көйгөйлөр күтүлбөйт.

Башкалардын бардыгы үчүн синтаксис "ачкыч = мааниге" негизделген. (жана түзмө-түз бир нече татаал варианттар, мисалы = [кээ бир, ошол, массив]) Мен ойлойм
интуитивдик ыңгайлуу.
Кызыгы ошол "күйгөн" ошол эле убакта (болжол менен 2013). Болгону, менден айырмаланып, TOML автору тийиштүү масштабда кирди.

Ошондуктан, азыр мага тескерисинче эмес, аны ишке ашырууну өзүмө ылайыкташтыруу оңой.

Жалпысынан алганда, биз TOML алабыз (эски Windows INIге абдан окшош). Жана бизде конфигурация бар, анда клавиатурадан эң акыркы сканерлөө коддорунун топтомуна жараша илгичтерди кантип тиркөө керектиги сүрөттөлгөн. Төмөндө, бөлүк-бөлүк, буга чейин эмне болгон. Анан эмне үчүн ушундай чечимге келгеним тууралуу түшүндүрмө.

0. Негизги абстракциялар

  • Код белгилөөлөрдү сканерлөө. Бул үчүн сөзсүз түрдө бир нерсе кылыш керек, анткени жөн гана санариптик коддорду адам окуй албайт (бул мен элемин loloswitcher).
    Мен “golang-evdev” дегенден “ecodes.go” силкилдедим (автор аны маданияттуу түрдө көрсөтсө да, түпнуска булакты караганга жалкоо болдум). Мен бир аз (азыр) абдан коркунучтуу нерсени оңдоп койдум. "LEFBRACE" → "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 кодуна "сюрприздерди" киргиздим).
Ичинде мен массивди "эмне кылуу керек" функцияларына шилтемелер менен толтурууну сүрөттөп бердим. Голангда бул (капысынан) Бул ыңгайлуу жана ачык-айкын болуп чыкты.

  • Мен бул жерде минимумга чейин "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" ж.б., сканерлөө коддору алынган тартипте.
  • Чек ар дайым ырааттуулуктун акырына чейин "жабылат", ошондуктан куйрукка "$" кошуунун кереги жок.
  • Бир сапта бардык текшерүүлөр биринин артынан бири аткарылат жана "мен" менен бириктирилет. Бирок маани массив катары сүрөттөлгөндүктөн, үтүрдөн кийин альтернатива чекти жазсаңыз болот. Бул кандайдыр бир себептерден улам керек болсо.
  • Наркы "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.

Мен кайда унутуп калдым/ката кетирдим? (ансыз жол жок), Мен чындап эле кунт коюп окурмандар мурдун чукуганга жалкоо болбойт деп ишенем.

Ийгилик!

Source: www.habr.com

Комментарий кошуу