0

CGEventTapを使用してキーボードイベントタップの監視を停止する正しい方法は何ですか?

特定のキーの出力を変換するシンプルなバックグラウンドアプリを構築しています。CGEventTapに関するこのすばらしい投稿のおかげで、キー変換を有効にすることができました。残念ながら、私はアプリを殺さずにそれを止めることができないようです。

ユーザーがチェックボックスを切り替えて機能をオンまたはオフにすると、次のメソッドが呼び出されます。トグルオンはすぐに発生します。トグルオフは、有効になるまでに1分以上かかる場合があります。ログを見ると、「無効。タップの変換を停止してください」と表示されます。が検出されました。しかし、キー変換は継続します。理由がわかりません。

- (void)watchEventTap
{    
        @autoreleasepool
        {
            CFRunLoopSourceRef runLoopSource = NULL;
            CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(NX_SYSDEFINED), myCGEventCallback, NULL);
            runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

            if (!eventTap)
            {
                NSLog(@"Couldn't create event tap!");
                exit(1);
            }

            if (self.shortcutEnabled) // User default toggled ON
            {
                NSLog(@"Enabled. Convert taps.");
                CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
                CGEventTapEnable(eventTap, true);
                // CFRunLoopRun(); // This blocks rest of app from executing
            }
            else // User default toggled OFF
            {
                NSLog(@"Disabled. Stop converting taps.");
                CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
                CGEventTapEnable(eventTap, false);
                // Clean up the event tap and source after ourselves.
                CFMachPortInvalidate(eventTap);
                CFRunLoopSourceInvalidate(runLoopSource);
                CFRelease(eventTap);
                CFRelease(runLoopSource);
                eventTap = NULL;
                runLoopSource = NULL;
            }
        }
//        exit(0);  // This blocks rest of app from executing
}

提案をありがとう。私はMacOSXアプリを新しく構築しているので、知らないことをしている場合はご容赦ください。

4

1 に答える 1

2

経験豊富な Mac 開発者のおかげで、問題は解決しました。メソッドが呼び出されるたびに、新しい runLoopsSource を作成していました。

これで、tapEvent と runLoop のインスタンス変数が作成されました。eventTap を停止するために必要な行は 1 行だけでした。以下の変更された方法:

- (void)watchEventTap
{

    @autoreleasepool
    {

        if ( [[NSUserDefaults standardUserDefaults] isEnabledNumLockDV] == YES ) // User default toggled ON
        {
            _runLoopSource = NULL;
             _eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(NX_SYSDEFINED), myCGEventCallback, NULL);
            _runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, _eventTap, 0);

            if (!_eventTap)
            {
                NSLog(@"Couldn't create event tap!");
                exit(1);
            }

            NSLog(@"Enabled. Convert taps.");
            CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
            CGEventTapEnable(_eventTap, true);
        }
        else if ( [[NSUserDefaults standardUserDefaults] isEnabledNumLockDV] == NO ) // User default toggled OFF
        {
            NSLog(@"Disabled. Stop converting taps.");
            CGEventTapEnable(_eventTap, false);
        }

    }
}
于 2013-02-10T22:34:27.500 に答える