Question

NSEvent has a method to get the event's subtype:

Getting Custom Event Information
– data1
– data2
– subtype

Can this same subtype be accessed from a CGEvent without first converting it to an NSEvent?

CGEventRef eventCG = ...;
NSEvent *eventNS = [NSEvent eventWithCGEvent:eventCG];

short subtypeNS = eventNS.subtype;
short subtypeCG = ???;

The CGEvent docs mention the mouse event subtypes, with seem to correspond with the mouse subtypes from IOLLEvent.h. However I'm specifically interested in finding the subtypes for System Defined CGEvents.

Those would be the NX_SUBTYPE_AUX_CONTROL_BUTTONS, etc., below or CG alternatives.

/* sub types for mouse and move events */

#define NX_SUBTYPE_DEFAULT                  0
#define NX_SUBTYPE_TABLET_POINT             1
#define NX_SUBTYPE_TABLET_PROXIMITY         2
#define NX_SUBTYPE_MOUSE_TOUCH              3

/* sub types for system defined events */

#define NX_SUBTYPE_POWER_KEY                1
#define NX_SUBTYPE_AUX_MOUSE_BUTTONS        7

/* 
 * NX_SUBTYPE_AUX_CONTROL_BUTTONS usage
 *
 * The incoming NXEvent for other mouse button down/up has event.type 
 * NX_SYSDEFINED and event.data.compound.subtype NX_SUBTYPE_AUX_MOUSE_BUTTONS.
 * Within the event.data.compound.misc.L[0] contains bits for all the buttons 
 * that have changed state, and event.data.compound.misc.L[1] contains the 
 * current button state as a bitmask, with 1 representing down, and 0
 * representing up.  Bit 0 is the left button, bit one is the right button, 
 * bit 2 is the center button and so forth.
 */
#define NX_SUBTYPE_AUX_CONTROL_BUTTONS      8

#define NX_SUBTYPE_EJECT_KEY                10
#define NX_SUBTYPE_SLEEP_EVENT              11
#define NX_SUBTYPE_RESTART_EVENT            12
#define NX_SUBTYPE_SHUTDOWN_EVENT           13

#define NX_SUBTYPE_STICKYKEYS_ON            100
#define NX_SUBTYPE_STICKYKEYS_OFF           101
#define NX_SUBTYPE_STICKYKEYS_SHIFT         102
#define NX_SUBTYPE_STICKYKEYS_CONTROL           103
#define NX_SUBTYPE_STICKYKEYS_ALTERNATE         104
#define NX_SUBTYPE_STICKYKEYS_COMMAND           105
#define NX_SUBTYPE_STICKYKEYS_RELEASE           106
#define NX_SUBTYPE_STICKYKEYS_TOGGLEMOUSEDRIVING    107
Was it helpful?

Solution

For mouse events, you can use CGEventGetIntegerValueField to get the event's kCGMouseEventSubtype property.

System-defined events are trickier. CGEvents don't seem to really expose that.

The easy part is matching the events. As long as CGEvent uses the same event type numbers that NSEvent and IOLLEvent use, and as long as event masks are simply combinations of 1 << eventType, you should be able to use some variation on CGEventMaskBit(NSSystemDefined) for the registration and CGEventGetType(event) == NSSystemDefined for testing.

The hard part is telling what a system-defined event's subtype is without going through NSEvent.

One possibility would be to do the same as for a mouse event (use kCGMouseEventSubtype), but since the layout of NXEventData is different for mouse events and “compound” (which includes system-defined) events, I wouldn't count on that working.

Most probably, there is no way to get a system-defined event's subtype from a CGEvent, so you would have to create an NSEvent.

Have you considered using NSEvent's event monitor API? If you can require 10.6 or later, you can catch events that way, and you'll get them as NSEvents already, ready for asking their subtypes.

OTHER TIPS

So I did a little research on this and it looks like it is possible for subtype, data1 and data2. You probably shouldn't do this, see the update below.

CFDataRef data_ref = CGEventCreateData(kCFAllocatorDefault, event_ref);
if (data_ref == NULL) {
    return;
}

CFIndex len = CFDataGetLength(data_ref);
UInt8 *buffer = malloc(20);
if (buffer == NULL) {
    return;
}

CFDataGetBytes(data_ref, CFRangeMake(len - 68, 20), buffer);

...

int subtype = CFSwapInt32BigToHost(*((UInt32 *) buffer));

#ifdef __LP64__
long data1 = CFSwapInt64BigToHost(*((UInt64 *) (buffer + 4)));
#else
long data1 = CFSwapInt32BigToHost(*((UInt32 *) (buffer + 4)));
#endif
        
#ifdef __LP64__            
long data2 = CFSwapInt64BigToHost(*((UInt64 *) (buffer + 12)));
#else
long data2 = CFSwapInt32BigToHost(*((UInt32 *) (buffer + 8)));
#endif

...

CFRelease(data_ref);
free(buffer);

Update: So here is the correct way to get the info above. It will require linking with -framework AppKit.

#include <objc/objc.h>
#include <objc/objc-runtime.h>

static id auto_release_pool;

...

Class NSAutoreleasePool_class = (Class) objc_getClass("NSAutoreleasePool");
id pool = class_createInstance(NSAutoreleasePool_class, 0);
id (*eventWithoutCGEvent)(id, SEL) = (id (*)(id, SEL)) objc_msgSend;
auto_release_pool = eventWithoutCGEvent(pool, sel_registerName("init"));

...

id (*eventWithCGEvent)(id, SEL, CGEventRef) = (id (*)(id, SEL, CGEventRef)) objc_msgSend;
id event_data = eventWithCGEvent((id) objc_getClass("NSEvent"), sel_registerName("eventWithCGEvent:"), event_ref);

long (*eventWithoutCGEvent)(id, SEL) = (long (*)(id, SEL)) objc_msgSend;
int subtype = (int) eventWithoutCGEvent(event_data, sel_registerName("subtype"));

long data1 = eventWithoutCGEvent(event_data, sel_registerName("data1"));
long data2 = eventWithoutCGEvent(event_data, sel_registerName("data2"));

...

eventWithoutCGEvent(auto_release_pool, sel_registerName("release"));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top