Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is it that Cocoa GUI Class Runs without invoking NSApplication or NSRunLoop

Why does the following code work ? It's a small Cocoa program that uses NSOpenPanel to select a file and open it in Emacs.app. It can be run from the command line with the starting directory as an argument.

How does NSOpenPanel run without invoking NSApplication or NSRunLoop ? What are the limitations on a Cocoa program that doesn't explicitly start NSApplication or NSRunLoop ? I would have thought one of them was: you can't use any kind of GUI. Perhaps by invoking NSOpenPanel, some fallback code being called that invokes NSRunLoop ? I put breakpoints on +[NSApplication alloc] and +[NSRunLoop alloc] and they were not triggered.

main.m:

#import <Cocoa/Cocoa.h>

NSString *selectFileWithStartPath(NSString *path) {
  NSString *answer = nil;
  NSOpenPanel* panel = [NSOpenPanel openPanel];
  panel.allowsMultipleSelection = NO;
  panel.canChooseFiles = YES;
  panel.canChooseDirectories = NO;
  panel.resolvesAliases = YES;
  if([panel runModalForDirectory:path file:nil] == NSOKButton)
    answer = [[[panel URLs] objectAtIndex:0] path];
  return answer;
}

int main(int argc, const char * argv[]) {
  NSString *startPath = argc > 1 ? [NSString stringWithUTF8String:argv[1]] : @"/Users/Me/Docs";
  printf("%s\n", argv[1]);
  BOOL isDir;
  if([[NSFileManager defaultManager] fileExistsAtPath:startPath isDirectory:&isDir] && isDir) {
    system([[NSString stringWithFormat:@"find %@ -name \\*~ -exec rm {} \\;", startPath] UTF8String]);
    NSString *file = selectFileWithStartPath(startPath);
    if(file) [[NSWorkspace sharedWorkspace] openFile:file withApplication:@"Emacs.app"];
  }
}
like image 590
Colin Avatar asked Apr 08 '14 16:04

Colin


1 Answers

runModalForDirectory:file:types: creates and runs its own event loop. From the documentation:

Displays the panel and begins a modal event loop that is terminated when the user clicks either OK or Cancel.

You can see that also if you pause the program while the "Open" dialog is active and print the stack backtrace in the debugger console:

frame #0: 0x00007fff8b855a1a libsystem_kernel.dylib`mach_msg_trap + 10
frame #1: 0x00007fff8b854d18 libsystem_kernel.dylib`mach_msg + 64
frame #2: 0x00007fff8549f155 CoreFoundation`__CFRunLoopServiceMachPort + 181
frame #3: 0x00007fff8549e779 CoreFoundation`__CFRunLoopRun + 1161
frame #4: 0x00007fff8549e0b5 CoreFoundation`CFRunLoopRunSpecific + 309
frame #5: 0x00007fff88381a0d HIToolbox`RunCurrentEventLoopInMode + 226
frame #6: 0x00007fff883817b7 HIToolbox`ReceiveNextEventCommon + 479
frame #7: 0x00007fff883815bc HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 65
frame #8: 0x00007fff838ca3de AppKit`_DPSNextEvent + 1434
frame #9: 0x00007fff838c9a2b AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
frame #10: 0x00007fff83c28e2e AppKit`-[NSApplication _realDoModalLoop:peek:] + 642
frame #11: 0x00007fff83c2754e AppKit`-[NSApplication runModalForWindow:] + 117
frame #12: 0x00007fff83ef5d0b AppKit`-[NSSavePanel runModal] + 276
frame #13: 0x0000000100000c61 xxx`selectFileWithStartPath(path=0x00000001000010b8) + 225 at main.m:18
frame #14: 0x0000000100000e0d xxx`main(argc=1, argv=0x00007fff5fbff9c0) + 189 at main.m:26

As you can see in frame #11, -[NSApplication runModalForWindow:] is used to run a modal event loop for the "Open" dialog.

like image 139
Martin R Avatar answered Oct 29 '22 17:10

Martin R