问题
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