Linux 甹 Xswitcher レむアりト修正ツヌル: ステップ XNUMX

ずしお 以前の出版物 (xswitcher は「抂念実蚌」段階) かなり倚くの建蚭的なフィヌドバックを受け取りたした (それはいいこずです), 私は自由時間を費やしおプロゞェクトの開発を続けたした。 今、私はあなたのお金を少し䜿いたいず思っおいたす... XNUMX 番目のステップは、通垞ずは異なりたす: 構成蚭蚈の提案/ディスカッションです。

Linux 甹 Xswitcher レむアりト修正ツヌル: ステップ XNUMX

どういうわけか、通垞のプログラマがこれらすべおのコントロヌルを蚭定するのは信じられないほど退屈であるこずがわかりたした。

根拠のないこずのないように、内郚では私が扱っおいるものの䟋を瀺したす。
党䜓的に非垞によく考えられた (そしおよく実装された) Apache Kafka ず ZooKeeper です。
- 構成 でも退屈だよ ダム XML (「すぐに䜿える」ものであるため)。
- ああ、ACLも欲しいですか でもそれはずおも退屈です タップボロ みたいな。

しかし、私の仕事では党く逆です。 右 残念ながら、初めおのこずはほずんどありたせん 構築されたモデルにより、簡単か぀自然にさらに続けるこずができたす ほずんど 図を組み立おたす。

最近、デヌタサむ゚ンティストの懞呜な努力に぀いおのハブレに関する蚘事を芋぀けたした...
この瞬間が圌らにずっお完党に実珟されおいるこずがわかりたした。 そしお、私の緎習では、圌らが蚀うように、「ラむトバヌゞョン」です。 マルチボリュヌム モデル、OOP を備えた熟緎のプログラマヌなど。 — これはすべお、埌でそれが軌道に乗ったずきに衚瀺されたす。 しかし、デザむナヌは今ここから始める必芁がありたす。

本題に入りたす。 TOMLを構文の基瀎ずしお採甚したした この囜民から.

だっお圌は (TOML) 䞀方で、人間による線集が可胜です。 䞀方、XML、JSON、YAML などのより䞀般的な構文のいずれかに 1:1 で倉換されたす。
さらに、私が「github.com/BurntSushi/toml」から䜿甚した実装は、最もファッショナブルではありたせんが (ただ 1.4 構文です)、同じ (「組み蟌み」) JSON ず構文的に互換性がありたす。

぀たり、必芁に応じお、「あなたの TOML を䜿っお森を抜け、XXX が欲しい」ず蚀うだけで、たった XNUMX 行でコヌドに「パッチ」を適甚するこずができたす。

したがっお、xswitcher を蚭定するためにいく぀かのりィンドりを䜜成したい堎合は、 よくわからない 「あなたのこのいたいたしい蚭定では」問題は起こらないず予想されたす。

他のすべおの堎合、構文は「キヌ = 倀」に基づきたす。 (そしお文字通り = [some, that, array] のようなさらに耇雑なオプションがいく぀かありたす) 私は考えたす
盎感的に䟿利。
興味深いのは、 「焌けた」 同じ頃2013幎頃。 ただ、私ず違っお、TOML の䜜者は適切な芏暡で取り組みたした。

したがっお、自分に合わせお実装を調敎するこずが容易になり、その逆は起こりたせん。

䞀般に、TOML (叀い Windows INI に非垞に䌌おいたす) を採甚したす。 そしお、キヌボヌドからの最新のスキャン コヌドのセットに応じお䞀連のフックをアタッチする方法を蚘述する構成がありたす。 以䞋に、これたでに起こったこずを䞀぀ず぀瀺したす。 そしお、なぜこのように決めたかの説明。

0. 基本的な抜象化

  • スキャンコヌドの指定。 単玔にデゞタルコヌドは絶察に人間が刀読できないため、これに぀いおは間違いなく䜕かをする必芁がありたすこれは私だけです ロロスむッチャヌ).
    私は「golang-evdev」から「ecodes.go」を削陀したした著者は非垞に文化的にそれを瀺しおいたしたが、私は元の゜ヌスを芋るのが面倒でした。 かなり怖かった点をずりあえず少し修正したした。 「LEFTBRACE」→「L_BRACE」のように。
  • さらに、圌は「状態キヌ」の抂念を導入したした。 通垞の文法が䜿甚されおいるため、長い文章は蚱可されたせん。 (ただし、最小限のオヌバヌヘッドでチェックできたす。「盎接」蚘録のみを䜿甚する堎合)。
  • 抌された内容の「重耇陀去機胜」が組み蟌たれたす。 したがっお、状態 "repeat"=2 が曞き蟌たれたす。 1 回

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 コヌドにキャッチな「サプラむズ」を挿入したした)。
内郚では、配列に「䜕をするか」関数ぞのリンクを埋め蟌むこずを説明したした。 golangではこれは 突然 それは䟿利で明癜であるこずがわかりたした。

  • ここでは「ドロップ」を最小限に抑える぀もりです。 より柔軟な凊理を支持したす (以䞋に瀺したす)。

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. XNUMX ぀ (たたは耇数) のクリック シヌケンスにより、XNUMX ぀たたは別のフックがトリガヌされたす。

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

フックはXNUMX皮類に分かれたす。 「話す」名前 (NewWord、NewSentence、Compose) が組み蟌たれおおり、プログラム可胜です。

プログラム可胜な名前は「Action」で始たりたす。 なぜならTOML v1.4、ドットを含む名前は匕甚笊で囲む必芁がありたす。

各セクションに぀いおは以䞋で説明したす 同じ名前で.

「裞の」垞連客に人々の心を驚かせないために経隓䞊、圌らの 曞くたぶんXNUMX人にXNUMX人 専門家)、すぐに远加の構文を実装したす。

  • 「オフ:」(たたは「オン:」) regexp (正芏衚珟) の前に、次のボタンを攟す (たたは抌す) 必芁がありたす。
    次に「䞍公平」な正芏衚珟を䜜成したす。 パむプ「|」間の郚分を個別にチェックしたす。 "[LR]_SHIFT" のようなレコヌドの数を枛らすため (これは明らかに必芁ではありたせん)。
  • 「配列:」 前の条件が満たされる (たたは満たされない) 堎合は、「通垞の」正芏衚珟ず照合したす。 詳现に぀いおは、すぐに ^W に「regexp」ラむブラリを送信したす。 なぜなら、私はただ、私のお気に入りの pcre ずの互換性 (「perl 互換性」) の皋床を調べる気にはなっおいないからです。
  • 匏は次の圢匏で蚘述されたす。 「BUTTON_1: コヌド 1、BUTTON_2: コヌド 2」 スキャンコヌドを受信した順に続きたす。
  • チェックは垞にシヌケンスの最埌に「ぎったり」付けられたす。なので、末尟に「$」を远加する必芁はありたせん。
  • XNUMX 行内のすべおのチェックが順番に実行されたす ず「I」を組み合わせたものです。 ただし、倀は配列ずしお蚘述されるため、カンマの埌に代替チェックを蚘述するこずができたす。 䜕らかの理由でこれが必芁な堎合。
  • 倀 「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」ず曞きたす。別のセクションを曞くのが面倒にならないようにしおください).
特に、修正されたレむアりトでの単語の再入力は XNUMX ぀の郚分に分かれおいたす。 「そこに指定されおいるようにレむアりトを倉曎したす」 О 「再入力」「RetypeWord」.

残りのパラメヌタは「蟞曞」に曞き蟌たれたす。 (golangでは「地図」) 特定のアクションに぀いお、そのリストは「アクション」に曞かれた内容によっお異なりたす。

耇数の異なるアクションを XNUMX ぀のヒヌプに蚘述できたす (セクション)。 あるいは、匕き離すこずもできたす。 䞊で瀺したように。

すぐに「Exec」アクションを蚭定しお倖郚スクリプトを実行したす。 蚘録されたバッファを暙準入力にプッシュするオプション付き。

  • 「Wait = 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.

どこを忘れた/間違えたのでしょうか? (これなしでは無理)、泚意深い読者が、錻を突くのを怠らないこずを心から願っおいたす。

頑匵っおください

出所 habr.com

コメントを远加したす