OSX:システム全体のキーダウンイベントを検出しますか?
-
13-10-2019 - |
質問
Mac OSXのタイピングチューターアプリケーションに取り組んでおり、アプリケーションが焦点を合わせていない場合でも、キーストロークを転送する必要があります。
おそらくNSDistributedNotificationCenterを介して、システムをアプリにフォワードキーストロークする方法はありますか?私は自分自身を愚かにグーグルで検索しました、そして答えを見つけることができませんでした...
編集: 以下のサンプルコード.
@nsgodに私を正しい方向に向けてくれてありがとう - 私は最終的に追加しました グローバルイベントモニター メソッドを使用します addGlobalMonitorForEventsMatchingMask:handler:
, 、美しく機能します。完全性のために、私の実装は次のようになります:
// register for keys throughout the device...
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent *event){
NSString *chars = [[event characters] lowercaseString];
unichar character = [chars characterAtIndex:0];
NSLog(@"keydown globally! Which key? This key: %c", character);
}];
私にとって、トリッキーな部分はブロックを使用することでしたので、誰でも役立つ場合に備えて少し説明します。
上記のコードについて気付くのは、それがすべてだということです 1つのメソッド呼び出し nsevenで。ブロックは、直接議論として提供されます に 関数。インラインデリゲートメソッドのようなものを考えることができます。これが私のために沈むのにしばらく時間がかかったからといって、私はここで一歩ずつ仕事をするつもりです:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
この最初のビットは問題ありません。 nseventでクラスメソッドを呼び出しており、どのイベントを監視しているか、この場合はnskeydownmaskを監視しようとしていることを伝えています。 a サポートされているイベントタイプのマスクのリスト 見つけることができます ここ.
さて、私たちはトリッキーな部分に来ます:ハンドラーはブロックを期待しています:
handler:^(NSEvent *event){
これを正しくするためにいくつかのコンパイルエラーが必要でしたが、(Appleに感謝します)非常に建設的なエラーメッセージでした。最初に気付くのはカラットです ^。それはブロックの開始を示します。その後、括弧内で、
NSEvent *event
これは、イベントをキャプチャするためにブロック内で使用する変数を宣言します。あなたはそれを呼ぶことができます
NSEvent *someCustomNameForAnEvent
問題ではありません。ブロック内でその名前を使用するだけです。それから、それはほぼすべてです。巻き毛のブレースとブラケットを閉じて、メソッド呼び出しを完了してください。
}];
そして、あなたは終わった!これは本当に「ワンライナー」のようなものです。アプリ内でこの呼び出しを実行する場所は問題ではありません。AppDelegateのApplicationDidFinishLaunchingメソッドで行います。次に、ブロック内で、アプリ内から他のメソッドを呼び出すことができます。
解決
OS X 10.6+の最小要件で大丈夫で、イベントのストリームへの「読み取り専用」アクセスで十分である場合は、COCOAにグローバルイベントモニターをインストールできます。ココアイベント処理ガイド:監視イベント.
OS X 10.5以前をサポートする必要があり、読み取り専用アクセスが問題であり、カーボンイベントマネージャーとの仕事を気にしないでください。基本的にカーボンと同等の使用を行うことができます GetEventMonitorTarget()
. 。 (ただし、その方法に関する(公式)ドキュメントを見つけるのは難しいでしょう)。そのAPIは最初にOS X 10.3で利用可能でした。
イベントストリームへの読み取りワイトアクセスが必要な場合は、Applicationservices> CoreGraphicsの一部であるわずかに低いレベルのAPIを確認する必要があります。CGEventTapCreate()
と友達。これは最初に10.4で利用可能でした。
3つのメソッドすべてでは、ユーザーがシステム設定で有効になっている「アシスタントデバイスへのアクセスを有効にする」ことが必要であることに注意してください。
他のヒント
私は私のケースのために機能したコードを投稿しています。
アプリが起動した後、グローバルイベントハンドラーを追加しています。私のショートカットは、Ctrl+Alt+CMD+Tを私のアプリを開くようにします。
- (void) applicationWillFinishLaunching:(NSNotification *)aNotification
{
// Register global key handler, passing a block as a callback function
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent *event){
// Activate app when pressing cmd+ctrl+alt+T
if([event modifierFlags] == 1835305 && [[event charactersIgnoringModifiers] compare:@"t"] == 0) {
[NSApp activateIgnoringOtherApps:YES];
}
}];
}
私がこれで見つけた問題は、別のアプリによってグローバルに登録されているキーは、または少なくとも私の場合は、おそらく何か間違ったことをしているということです。
たとえば、プログラムが「コマンドシフト3」などのすべてのキーを表示する必要がある場合、OSによって取り上げられているため、それを表示するために進むことはわかりません。
それとも誰かがそれを理解しましたか?知りたいです...
NSGODがすでに指摘しているように、CoreGraphicsも使用できます。
あなたのクラスで(例:-init):
CFRunLoopRef runloop = (CFRunLoopRef)CFRunLoopGetCurrent();
CGEventMask interestedEvents = NSKeyDown;
CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
0, interestedEvents, myCGEventCallback, self);
// by passing self as last argument, you can later send events to this class instance
CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,
eventTap, 0);
CFRunLoopAddSource((CFRunLoopRef)runloop, source, kCFRunLoopCommonModes);
CFRunLoopRun();
クラスの外では、同じ.mファイル:
CGEventRef myCGEventCallback(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void *refcon)
{
if(type == NX_KEYDOWN)
{
// we convert our event into plain unicode
UniChar myUnichar[2];
UniCharCount actualLength;
UniCharCount outputLength = 1;
CGEventKeyboardGetUnicodeString(event, outputLength, &actualLength, myUnichar);
// do something with the key
NSLog(@"Character: %c", *myUnichar);
NSLog(@"Int Value: %i", *myUnichar);
// you can now also call your class instance with refcon
[(id)refcon sendUniChar:*myUnichar];
}
// send event to next application
return event;
}