Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Saphrosit

macrumors newbie
Original poster
Oct 10, 2010
12
0
Hi everybody,

I'm experiencing a strange problem: in my application I wait for keyDown events, using this code

Code:
machPort =  CGEventTapCreate(kCGSessionEventTap,
					 kCGTailAppendEventTap,
					 kCGEventTapOptionDefault,
					 CGEventMaskBit(kCGEventKeyDown),
					 (CGEventTapCallBack) eventTapFunction,
					 self );
		
CFRunLoopSourceRef mKeyboardEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, machPort, 0);
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(runLoop,  mKeyboardEventSrc, kCFRunLoopDefaultMode);
I need to show a window after the pressure of some keys. I need also the program to stop while the user makes a choice in that window, so I call

Code:
[[NSApplication sharedApplication] runModalForWindow:myWindow];
To stop runModal I linked a button in the window to a method that runs:
Code:
[myWindow orderOut:self];
[[NSApplication sharedApplication] stopModal];
The problem is that I receive a
Code:
Assertion failure in -[NSEvent _initWithCGSEvent:eventRef:], /SourceCache/AppKit/AppKit-1038.35/AppKit.subproj/NSEvent.m:1260
Invalid parameter not satisfying: cgsEvent.type > 0 && cgsEvent.type <= kCGSLastEventType
error when trying to `stopModal`. Can someone explain me why and how to solve it?
Thanks in advance
 
Don't use the CoreFoundation run loop functions with NSApplication. Use NSRunLoop instead.

Code:
@interface MYAppDelegate : NSObject <NSApplicationDelegate> {
@private
    CFMachPortRef machPort;
    NSMachPort *machPortWrapper;
}
@end

@implementation MYAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    machPort = CGEventTapCreate(kCGSessionEventTap,
                                kCGTailAppendEventTap,
                                kCGEventTapOptionDefault,
                                CGEventMaskBit(kCGEventKeyDown),
                                eventTapFunction,
                                self);
    if (!machPort) {
        NSException *e =
            [NSException exceptionWithName:@"FailedToCreateKeyDownEventTap"
                                    reason:@"Failed to create an event tap for key down events"
                                  userInfo:[NSDictionary dictionary]];
        @throw e;
    }
    
    /* 
       Wrap the Mach port behind machPort into an NSMachPort object.
       Note the NSMachPortDeallocateNone option is critical.  We don't
       want NSMachPort to invalidate or destroy the mach port.   That
       responsibility lies with machPort.
    */
    machPortWrapper = 
        [[NSMachPort alloc] 
                initWithMachPort:CFMachPortGetPort(machPort) 
                         options:NSMachPortDeallocateNone];
    
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:machPortWrapper forMode:NSDefaultRunLoopMode];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    if (machPortWrapper) {
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop removePort:machPortWrapper forMode:NSDefaultRunLoopMode];
        [machPortWrapper release];
        machPortWrapper = NULL;
    }
    if (machPort) {
        CFRelease(machPort);
        machPort = NULL;
    }
}

@end
 
Nothing changed...

I think the problem is due to an illegal CGEventType passed to eventTapFunction:

Code:
CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
	
	NSEvent* sysEvent = [NSEvent eventWithCGEvent:event];
	Controller *app = (Controller*) refcon;
#if DEBUG_EVENT	
	NSLog(@"event received: %@, type = %lu",sysEvent,[sysEvent type]);
#endif
	if (type == NX_KEYDOWN && [sysEvent type] == NSKeyDown) {	
              [app myFunction];
	}
	return event;
}

I found that, when app crashes, type is -2. I tried to add
Code:
	if (type != NX_KEYDOWN)
		return event;

so I do not receive the assertion failure but the application stops to handle events. Not sure this is the right way to solve the problem...
 
I don't know why you're getting that. I don't get that in my test project which shows a modal dialog whenever the user hits Ctrl+Cmd+A.

Note that you should have only been getting key down events in your eventTapFunction so testing for it shouldn't have been necessary.

Just returning the event is fine. The event will continue to be processed as though your tap wasn't in place at all. But are you saying that when you do this, your app stops receiving further events?

Me being me, I'd want to know what this spurious event is, in case it's a symptom of another problem.

EDIT: Actually an event type CGEventType is defined in CGEventType.h as unsigned 32-bit integer. So your -2 is actually 0xFFFFFFFE. In CGEventType.h that's defined as kCGEventTapDisabledByTimeout. There's a comment just about this that says:
/* Out of band event types. These are delivered to the event tap callback
to notify it of unusual conditions that disable the event tap. */
So it looks like your event tap is timing out. That's why your not getting anymore events after this.


EDIT 2: Just to confirm, you using an event tap because you need this functionality to trigger even if your application is not in the foreground right? Otherwise, I think you'd better off overriding sendEvent: in NSApplication.
 
Last edited:
Actually an event type CGEventType is defined in CGEventType.h as unsigned 32-bit integer. So your -2 is actually 0xFFFFFFFE. In CGEventType.h that's defined as kCGEventTapDisabledByTimeout. There's a comment just about this that says:

So it looks like your event tap is timing out. That's why your not getting anymore events after this.
Wonder why I get this event... I thought of a workaround, may this be a solution?
Code:
	if (type == -2) {
		[app reHandleEvent];
		return event;
	}
Code:
-(void)reHandleEvent {
	
	machPort =  CGEventTapCreate(kCGSessionEventTap,
				 kCGTailAppendEventTap,
				 kCGEventTapOptionDefault,
				 CGEventMaskBit(kCGEventKeyDown),
				 (CGEventTapCallBack) eventTapFunction,
				 self );
	
	NSMachPort *machPortWrapper = [[NSMachPort alloc] 
initWithMachPort:CFMachPortGetPort(machPort) 
options:NSMachPortDeallocateNone];
	
	[[NSRunLoop currentRunLoop] addPort:machPortWrapper forMode:NSDefaultRunLoopMode];	
	
	[machPortWrapper release];

}
Not sure if, when event tap get disabled, it gets also dismissed correctly or if it leaks somewhere...

EDIT 2: Just to confirm, you using an event tap because you need this functionality to trigger even if your application is not in the foreground right? Otherwise, I think you'd better off overriding sendEvent: in NSApplication.
Application runs in background :)
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.