Assertion failure in NSEvent when runModalForWindow

Discussion in 'Mac Programming' started by Saphrosit, Apr 30, 2011.

  1. macrumors newbie

    Joined:
    Oct 10, 2010
    #1
    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
     
  2. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #2
    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
    
     
  3. thread starter macrumors newbie

    Joined:
    Oct 10, 2010
    #3
    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...
     
  4. jiminaus, May 1, 2011
    Last edited: May 1, 2011

    macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #4
    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:
    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.
     
  5. Saphrosit, May 1, 2011
    Last edited: May 2, 2011

    thread starter macrumors newbie

    Joined:
    Oct 10, 2010
    #5
    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...

    Application runs in background :)
     

Share This Page