Linuxでキーボード(make / break)イベントをフックし、インターセプトし、生成してから、アプリケーションに配信したいと思います。より正確には、キーイベントストリーム内のパターンを検出し、検出されたパターンに応じてイベントを破棄/ストリームに挿入できるようにしたいです。
SOに関するいくつかの関連する質問を見ましたが、次のとおりです。
- キーイベント(キーロガーなど)を取得する方法のみを扱い、それらの伝播を操作する方法は扱いません(リッスンするだけで、傍受/生成はしません)。
- または、Xでパッシブ/アクティブグラブを使用します(詳細については、以下を参照してください)。
小さなDSL
以下で問題を説明しますが、もう少しコンパクトでわかりやすくするために、最初に小さなDSL定義を使用します。
A_
:make(押す)キーAの場合A^
:ブレーク(リリース)キーAの場合A^->[C_,C^,U_,U^]
:A^
CとUのmake / breakコンボを処理チェーンのさらに下流に(そして最後にアプリケーションに)送信します。ない場合は->
何も送信されません(ただし、後続のイベントを検出するために内部状態が変更される場合があります)。$X
:任意のアクションを実行します。これは、いくつかの構成可能なキーイベントシーケンス(おそらくC-x C-s
emacsのようなもの)を送信するか、関数を実行することができます。キーイベントのみを送信できる場合は、アクティブなアプリケーションに応じてウィンドウマネージャーでこれらをさらに処理できるため、これで十分です。
問題の説明
さて、この表記法で、検出したいパターンと、処理チェーンに渡したいイベントを次に示します。
A_, A^->[A_,A^]
:expl。上記を参照してください。送信はで行われることに注意してくださいA^
。A_, B_, A^->[A_,A^], B^->[B_,B^]
:基本的に1と同じですが、重複するイベントによって処理フローが変更されることはありません。A_, B_, B^->[$X], A^
:別のキーが保持されている間に(B)キーの完全なメイク/ブレークがあった場合(A)、Xが実行され(上記を参照)、Aのブレークは破棄されます。
(これは原則として、キーイベント上に実装された単純なステートマシンであり、出力として(複数の)キーイベントを生成できます)。
その他の注意事項
- ソリューションはタイピング速度で機能する必要があります。
- 変更されたキーイベントストリームのコンシューマーは、Linux上のXで実行されます(コンソール、ブラウザー、エディターなど)。
- キーボードイベントのみが処理に影響します(マウスなどはありません)
- マッチングは、keysyms(少し簡単)またはkeycode(少し難しい)で発生する可能性があります。後者の場合、コードからkeysymに変換するには、マッピングを読み取る必要があります。
- 可能であれば、USBキーボードと仮想マシン内の両方で機能するソリューションをお勧めします(ドライバーレイヤーで作業している場合は問題になる可能性がありますが、他のレイヤーでも問題ありません)。
- 私は実装言語についてかなりオープンです。
考えられる解決策と質問
したがって、基本的な質問は、これをどのように実装するかです。
XGrabKey
パッシブグラブ( )とを使用してウィンドウマネージャーにソリューションを実装しましたXSendEvent
。B^
残念ながら、パッシブグラブは、上記の2番目のパターンでは正しくキャプチャされないため、この場合は機能しません。その理由は、変換されたグラブがで終了しA^
、継続されないためB^
です。新しいグラブは、保持されている場合はキャプチャBに変換されますが、約1秒後になります。それ以外の場合は、プレーンB^
がアプリケーションに送信されます。これはで確認できますxev
。
アクティブなグラブ(XGrabKeyboard
)を使用するように実装を変換することはできますが、ウィンドウマネージャーが常にキーボードにアクティブなグラブを持っている場合、他のアプリケーションへの影響についてはわかりません。Xのドキュメントでは、アクティブグラブは煩わしく、短期間の使用向けに設計されていると言及しています。誰かがこれを経験していて、長期のアクティブグラブに大きな欠点がない場合、私はこれを解決策と見なします。
ウィンドウマネージャー(Xクライアントとして動作する)以外の主要なイベント処理の他のレイヤーを見ていきたいと思います。キーボードドライバまたはマッピングは、上記の問題を解決できる限り可能です。これは、ソリューションが個別のアプリケーションである必要がないことも意味します。ドライバーまたはカーネルモジュールにこれを実行してもらっても問題ありません。ただし、カーネルやドライバーのプログラミングは行ったことがないので、いくつかの優れたリソースをいただければ幸いです。
ポインタをありがとう!