Cocoa: No keyboard events will fire while dragging (NSEventTrackingRunLoopMode)

纵然是瞬间 提交于 2020-01-05 07:47:07

问题


I am successfully able to react to keyboard events through my window controller's keyDown: method.
The problem arises while performing a mouse drag:
Keyboard events seem to be delayed and will only fire on mouse up.

To be clear, what I mean is:
• place a log statement in you window controller's keyDown: method
• launch your app, perform some drag operation (on a NSSlider for ex.)
• while maintaining the drag, press any key: nothing logs to the console.
• release drag : logs appear, yay…

The control i am dragging is a custom NSSlider.
I have implemented the dragging mechanism using a 'Mouse-Tracking Loop' Approach. for what I understand, when dragging, NSApplication's main run loop mode is being switched to NSEventTrackingRunLoopMode, thus restricting incoming events.

So, i simply added NSKeyDownMask & NSKeyUpMask in my tracking loop and when encoutered, called self.nextResponder keyDown/up: method accordingly. My problem is solved for this particular custom subclass.

But what about cocoa's native controls ? I can't code that exception...

I had hoped for NSEvent's "addLocalMonitorForEventsMatchingMask:" method but alas, says doc : "will not be called for events that are consumed by nested event-tracking loops such as control tracking, menu tracking, or window dragging".

So, isn't there a straightforward solution to receive keyboard events regardless of the app's runloop mode ?


回答1:


As you found in the documentation for the NSEvent class' addGlobalMonitorForEventsMatchingMask:handler:, this limitation is by design.

However, you can work around it by using the IOKit framework (specifically the IOHID portions) to receive low level device events/interrupts. I just had to do this recently for tracking some specific keypresses during mouse-drags.

The basic gist is to create an IOHID manager with IOHIDManagerCreate(), then add the type(s) of devices to the manager that it should "monitor" with IOHIDManagerSetDeviceMatchingMultiple(), register a callback with the manager via IOHIDManagerRegisterInputValueCallback(), schedule the proper run-loop for the manager with IOHIDManagerScheduleWithRunLoop(), and finally open the manager with IOHIDManagerOpen().

To get these low level events during mouse-drags, perform this setup in a separate thread. When scheduling the run-loop for the manager, use CFRunLoopGetCurrent() to get the run loop for the current thread, and call CFRunLoopRun() after IOHIDManagerOpen().

This guide from Apple can help you get started, along with this Q&A here on Stack Overflow.



来源:https://stackoverflow.com/questions/18047806/cocoa-no-keyboard-events-will-fire-while-dragging-nseventtrackingrunloopmode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!