Just use a copy
[llpp.git] / main_osx.m
blob29e753017a9f3fde34416e2c75e447446b59eb61
1 #include <Cocoa/Cocoa.h>
2 #include <OpenGL/gl.h>
4 #define CAML_NAME_SPACE
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <pthread.h>
10 #include <caml/mlvalues.h>
11 #include <caml/memory.h>
12 #include <caml/callback.h>
13 #include <caml/alloc.h>
14 #include <caml/fail.h>
16 enum {
17   EVENT_EXPOSE = 1,
18   EVENT_RESHAPE = 3,
19   EVENT_MOUSE = 4,
20   EVENT_MOTION = 5,
21   EVENT_PMOTION = 6,
22   EVENT_KEYDOWN = 7,
23   EVENT_ENTER = 8,
24   EVENT_LEAVE = 9,
25   EVENT_WINSTATE = 10,
26   EVENT_QUIT = 11,
27   EVENT_SCROLL = 12,
28   EVENT_ZOOM = 13,
29   EVENT_OPEN = 20
32 enum {
33   BUTTON_LEFT = 1,
34   BUTTON_RIGHT = 3,
35   BUTTON_WHEEL_UP = 4,
36   BUTTON_WHEEL_DOWN = 5
39 static int terminating = 0;
40 static pthread_mutex_t terminate_mutex = PTHREAD_MUTEX_INITIALIZER;
41 static int server_fd = -1;
42 static CGFloat backing_scale_factor = -1.0;
44 void Abort (NSString *format, ...)
46   va_list argList;
47   va_start (argList, format);
48   NSString *str = [[NSString alloc] initWithFormat:format arguments:argList];
49   va_end (argList);
50   NSLog (@"%@", str);
51   NSAlert *alert = [[NSAlert alloc] init];
52   [alert addButtonWithTitle:@"Quit"];
53   [alert setMessageText:@"Internal Error"];
54   [alert setInformativeText:str];
55   [alert setAlertStyle:NSCriticalAlertStyle];
56   [alert runModal];
57   [NSApp terminate:nil];
60 void *caml_main_thread (void *argv)
62   @autoreleasepool {
63     caml_main (argv);
64     pthread_mutex_lock (&terminate_mutex);
65     if (terminating == 0) {
66       terminating = 1;
67       [NSApp performSelectorOnMainThread:@selector(terminate:)
68                               withObject:nil
69                            waitUntilDone:NO];
70     }
71     pthread_mutex_unlock (&terminate_mutex);
72   }
73   pthread_exit (NULL);
76 NSCursor *GetCursor (int idx)
78   static NSCursor *cursors[5];
79   static BOOL initialised = NO;
81   if (initialised == NO) {
82     cursors[0] = [NSCursor arrowCursor];
83     cursors[1] = [NSCursor pointingHandCursor];
84     cursors[2] = [NSCursor arrowCursor];
85     cursors[3] = [NSCursor closedHandCursor];
86     cursors[4] = [NSCursor IBeamCursor];
87     initialised = YES;
88   }
90   return cursors[idx];
93 @implementation NSWindow (CategoryNSWindow)
95 - (BOOL)isFullScreen
97   return ([self styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask;
100 @end
102 @implementation NSView (CategoryNSView)
104 - (NSPoint)locationFromEvent:(NSEvent *)event
106   NSPoint point =
107     [self convertPointToBacking:[self convertPoint:[event locationInWindow] fromView:nil]];
108   NSRect bounds = [self convertRectToBacking:[self bounds]];
109   point.y = bounds.size.height - point.y;
110   return point;
113 - (NSRect)convertFrameToBacking
115   return [self convertRectToBacking:[self frame]];
118 @end
120 @implementation NSEvent (CategoryNSEvent)
122 - (int)deviceIndependentModifierFlags
124   return [self modifierFlags] & NSDeviceIndependentModifierFlagsMask;
127 @end
129 @interface Connector : NSObject
131 - (instancetype)initWithFileDescriptor:(int)fd;
133 - (void)notifyReshapeWidth:(int)w height:(int)h;
134 - (void)notifyExpose;
135 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask;
136 - (void)notifyQuit;
137 - (void)mouseEntered:(NSPoint)loc;
138 - (void)mouseExited;
139 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
140 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
141 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
142 @end
144 @implementation Connector
146   NSMutableData *data;
147   NSFileHandle *fileHandle;
150 - (instancetype)initWithFileDescriptor:(int)fd
152   self = [super init];
153   data = [NSMutableData dataWithLength:32];
154   fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd];
155   return self;
158 - (void)setByte:(int8_t)b offset:(int)off
160   [data replaceBytesInRange:NSMakeRange (off, 1) withBytes:&b];
163 - (void)setShort:(int16_t)s offset:(int)off
165   [data replaceBytesInRange:NSMakeRange (off, 2) withBytes:&s];
168 - (void)setInt:(int32_t)n offset:(int)off
170   [data replaceBytesInRange:NSMakeRange (off, 4) withBytes:&n];
173 - (void)writeData
175   [fileHandle writeData:data];
178 - (void)notifyReshapeWidth:(int)w height:(int)h
180   [self setByte:EVENT_RESHAPE offset:0];
181   [self setShort:w offset:16];
182   [self setShort:h offset:18];
183   [self writeData];
186 - (void)notifyExpose
188   [self setByte:EVENT_EXPOSE offset:0];
189   [self writeData];
192 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask
194   [self setByte:EVENT_KEYDOWN offset:0];
195   [self setInt:key offset:16];
196   [self setInt:mask offset:20];
197   [self writeData];
200 - (void)notifyWinstate:(BOOL)fullScreen
202   [self setByte:EVENT_WINSTATE offset:0];
203   [self setInt:fullScreen offset:16];
204   [self writeData];
207 - (void)notifyQuit
209   [self setByte:EVENT_QUIT offset:0];
210   [self writeData];
213 - (void)mouseEntered:(NSPoint)loc
215   [self setByte:EVENT_ENTER offset:0];
216   [self setShort:loc.x offset:16];
217   [self setShort:loc.y offset:20];
218   [self writeData];
221 - (void)mouseExited
223   [self setByte:EVENT_LEAVE offset:0];
224   [self writeData];
227 - (void)mouseDragged:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
229   [self setByte:EVENT_MOTION offset:0];
230   [self setShort:aPoint.x offset:16];
231   [self setShort:aPoint.y offset:20];
232   [self setInt:flags offset:24];
233   [self writeData];
236 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
238   [self setByte:EVENT_PMOTION offset:0];
239   [self setShort:aPoint.x offset:16];
240   [self setShort:aPoint.y offset:20];
241   [self setInt:flags offset:24];
242   [self writeData];
245 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
247   [self setByte:EVENT_MOUSE offset:0];
248   [self setShort:1 offset:10];
249   [self setInt:buttons offset:12];
250   [self setShort:aPoint.x offset:16];
251   [self setShort:aPoint.y offset:20];
252   [self setInt:flags offset:24];
253   [self writeData];
256 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
258   [self setByte:EVENT_MOUSE offset:0];
259   [self setShort:0 offset:10];
260   [self setInt:buttons offset:12];
261   [self setShort:aPoint.x offset:16];
262   [self setShort:aPoint.y offset:20];
263   [self setInt:flags offset:24];
264   [self writeData];
267 - (void)scrollByDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY
269   [self setByte:EVENT_SCROLL offset:0];
270   [self setInt:(int32_t) deltaX offset:16];
271   [self setInt:(int32_t) deltaY offset:20];
272   [self writeData];
275 - (void)zoom:(CGFloat)z at:(NSPoint)p
277   [self setByte:EVENT_ZOOM offset:0];
278   [self setInt:(int32_t) (z * 1000) offset:16];
279   [self setShort:p.x offset:20];
280   [self setShort:p.y offset:22];
281   [self writeData];
284 - (void)openFile:(NSString *)filename
286   const char *utf8 = [filename UTF8String];
287   unsigned len = [filename lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
288   [self setByte:EVENT_OPEN offset:0];
289   unsigned off = 0;
290   unsigned data_len = [data length] - 4;
291   while (off < len) {
292     unsigned chunk_len = MIN (data_len - 4, len - off);
293     [self setShort:chunk_len offset:2];
294     [data replaceBytesInRange:NSMakeRange (4, chunk_len) withBytes:(utf8 + off)];
295     [self writeData];
296     off += chunk_len;
297   }
298   [self setShort:0 offset:2];
299   [self writeData];
302 @end
304 @interface MyDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
306 - (int)getw;
307 - (int)geth;
308 - (void)swapb;
309 - (void)setwinbgcol:(NSColor *)col;
310 - (void)applicationWillFinishLaunching:(NSNotification *)not;
311 - (void)applicationDidFinishLaunching:(NSNotification *)not;
312 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
313 - (void)makeCurrentContext;
315 @end
317 @interface MyWindow : NSWindow
319 @end
321 @interface MyView : NSView
323   Connector *connector;
324   NSCursor *cursor;
327 - (instancetype)initWithFrame:(NSRect)frame connector:(Connector *)aConnector;
328 - (void)setCursor:(NSCursor *)aCursor;
330 @end
332 @implementation MyView
334 - (instancetype)initWithFrame:(NSRect)frame connector:(Connector *)aConnector
336   self = [super initWithFrame:frame];
338   if (self != NULL) {
339     connector = aConnector;
340     cursor = [NSCursor arrowCursor];
341     self.acceptsTouchEvents = YES;
342   }
344   return self;
347 - (void)setCursor:(NSCursor *)aCursor
349   cursor = aCursor;
352 -(void)resetCursorRects
354   [self addCursorRect:[self bounds] cursor:cursor];
357 - (void)drawRect:(NSRect)bounds
359   // NSLog(@"drawRect: %@", [NSValue valueWithRect:bounds]);
360   [connector notifyExpose];
363 - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
364   NSTrackingArea* trackingArea = [[NSTrackingArea alloc]
365                                    initWithRect:[self bounds]
366                                  options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect)
367                                           owner:self
368                                        userInfo:nil];
369   [self addTrackingArea:trackingArea];
372 - (void)keyDown:(NSEvent *)event
374   // int key = [event keyCode];
375   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
376   NSString *chars = [event charactersIgnoringModifiers];
377   const uint32_t *c = (uint32_t *) [chars cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
378   while (*c) {
379     if (*c == 0x7f && !(mask & NSFunctionKeyMask)) {
380       [connector keyDown:0x8 modifierFlags:mask];
381     } else {
382       [connector keyDown:*c modifierFlags:mask];
383     }
384     c++;
385   }
388 - (void)flagsChanged:(NSEvent *)event
390   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
391   NSLog (@"flagsChanged: 0x%lx", mask);
392   if (mask != 0) {
393     [connector keyDown:0 modifierFlags:mask];
394   }
397 - (void)mouseDown:(NSEvent *)event
399   [connector mouseDown:BUTTON_LEFT
400                atPoint:[self locationFromEvent:event]
401          modifierFlags:[event deviceIndependentModifierFlags]];
404 - (void)mouseUp:(NSEvent *)event
406   [connector mouseUp:BUTTON_LEFT
407              atPoint:[self locationFromEvent:event]
408        modifierFlags:[event deviceIndependentModifierFlags]];
411 - (void)rightMouseDown:(NSEvent *)event
413   [connector mouseDown:BUTTON_RIGHT
414                atPoint:[self locationFromEvent:event]
415          modifierFlags:[event deviceIndependentModifierFlags]];
418 - (void)rightMouseUp:(NSEvent *)event
420   [connector mouseUp:BUTTON_RIGHT
421              atPoint:[self locationFromEvent:event]
422        modifierFlags:[event deviceIndependentModifierFlags]];
425 - (void)rightMouseDragged:(NSEvent *)event
427   [connector mouseDragged:[self locationFromEvent:event]
428             modifierFlags:[event deviceIndependentModifierFlags]];
431 - (void)mouseDragged:(NSEvent *)event
433   [connector mouseDragged:[self locationFromEvent:event]
434             modifierFlags:[event deviceIndependentModifierFlags]];
437 - (void)mouseMoved:(NSEvent *)event
439   [connector mouseMoved:[self locationFromEvent:event]
440           modifierFlags:[event deviceIndependentModifierFlags]];
443 - (void)mouseEntered:(NSEvent *)event
445   [connector mouseEntered:[self locationFromEvent:event]];
448 - (void)mouseExited:(NSEvent *)event
450   [connector mouseExited];
453 - (void)scrollWheel:(NSEvent *)event
455   CGFloat deltaX = [event scrollingDeltaX];
456   CGFloat deltaY = -[event scrollingDeltaY];
458   if ([event hasPreciseScrollingDeltas]) {
459     [connector scrollByDeltaX:(backing_scale_factor * deltaX)
460                        deltaY:(backing_scale_factor * deltaY)];
461   } else {
462     NSPoint loc = [self locationFromEvent:event];
463     NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
464     if (deltaY > 0.0) {
465       [connector mouseDown:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
466       [connector mouseUp:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
467     } else if (deltaY < 0.0) {
468       [connector mouseDown:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
469       [connector mouseUp:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
470     }
471   }
474 - (void)magnifyWithEvent:(NSEvent *)event
476   [connector zoom:[event magnification] at:[self locationFromEvent:event]];
479 @end
481 @implementation MyWindow
483 - (BOOL)canBecomeKeyWindow
485   return YES;
488 @end
490 @implementation MyDelegate
492   char **argv;
493   MyWindow *window;
494   NSOpenGLContext *glContext;
495   pthread_t thread;
496   Connector *connector;
499 - (instancetype)initWithArgv:(char **)theArgv fileDescriptor:(int)fd
501   self = [super init];
502   if (self != NULL) {
503     argv = theArgv;
504     connector = [[Connector alloc] initWithFileDescriptor:fd];
505   }
506   return self;
509 - (void)setTitle:(NSString *)title
511   [window setTitle:title];
514 - (void)mapwin
516   [window makeKeyAndOrderFront:self];
519 - (int)getw
521   return [[window contentView] convertFrameToBacking].size.width;
524 - (int)geth
526   return [[window contentView] convertFrameToBacking].size.height;
529 - (void)setwinbgcol:(NSColor *)col
531   [window setBackgroundColor:col];
534 - (void)applicationWillFinishLaunching:(NSNotification *)not
536   NSLog(@"applicationWillFinishLaunching");
537   id menubar = [NSMenu new];
538   id appMenuItem = [NSMenuItem new];
539   id fileMenuItem = [NSMenuItem new];
540   id windowMenuItem = [NSMenuItem new];
541   id helpMenuItem = [NSMenuItem new];
542   [menubar addItem:appMenuItem];
543   [menubar addItem:fileMenuItem];
544   [menubar addItem:windowMenuItem];
545   [menubar addItem:helpMenuItem];
546   [NSApp setMainMenu:menubar];
547   id appMenu = [NSMenu new];
548   id appName = [[NSProcessInfo processInfo] processName];
549   id aboutMenuItem = [[NSMenuItem alloc] initWithTitle:[@"About " stringByAppendingString:appName]
550                                                 action:@selector(orderFrontStandardAboutPanel:)
551                                          keyEquivalent:@""];
552   id hideMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName]
553                                                action:@selector(hide:)
554                                         keyEquivalent:@"h"];
555   id hideOthersMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
556                                                      action:@selector(hideOtherApplications:)
557                                               keyEquivalent:@"h"];
558   [hideOthersMenuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
559   id showAllMenuItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
560                                                   action:@selector(unhideAllApplications:)
561                                            keyEquivalent:@""];
562   id quitMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Quit " stringByAppendingString:appName]
563                                                action:@selector(terminate:)
564                                         keyEquivalent:@"q"];
565   [appMenu addItem:aboutMenuItem];
566   [appMenu addItem:[NSMenuItem separatorItem]];
567   [appMenu addItem:hideMenuItem];
568   [appMenu addItem:hideOthersMenuItem];
569   [appMenu addItem:showAllMenuItem];
570   [appMenu addItem:[NSMenuItem separatorItem]];
571   [appMenu addItem:quitMenuItem];
572   [appMenuItem setSubmenu:appMenu];
574   id fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
575   id openMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open..."
576                                                action:@selector(openDocument:)
577                                         keyEquivalent:@"o"];
578   id closeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Close"
579                                                 action:@selector(performClose:)
580                                          keyEquivalent:@"w"];
581   [fileMenu addItem:openMenuItem];
582   [fileMenu addItem:[NSMenuItem separatorItem]];
583   [fileMenu addItem:closeMenuItem];
584   [fileMenuItem setSubmenu:fileMenu];
586   id windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
587   id miniaturizeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
588                                                    action:@selector(performMiniaturize:)
589                                             keyEquivalent:@"m"];
590   id zoomMenuItem = [[NSMenuItem alloc] initWithTitle:@"Zoom"
591                                                action:@selector(performZoom:)
592                                         keyEquivalent:@""];
594   [windowMenu addItem:miniaturizeMenuItem];
595   [windowMenu addItem:zoomMenuItem];
596   [windowMenuItem setSubmenu:windowMenu];
598   id helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
599   id reportIssueMenuItem = [[NSMenuItem alloc] initWithTitle:@"Report an issue..."
600                                                       action:@selector(reportIssue:)
601                                                keyEquivalent:@""];
602   [helpMenu addItem:reportIssueMenuItem];
603   [helpMenuItem setSubmenu:helpMenu];
605   window = [[MyWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
606                                        styleMask:(NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask | NSResizableWindowMask)
607                                          backing:NSBackingStoreBuffered
608                                            defer:NO];
610   [window center];
611   [window setAcceptsMouseMovedEvents:YES];
612   [window setDelegate:self];
615   [[NSNotificationCenter defaultCenter] addObserver:self
616                                            selector:@selector(didEnterFullScreen)
617                                                name:NSWindowDidEnterFullScreenNotification
618                                              object:window];
619   [[NSNotificationCenter defaultCenter] addObserver:self
620                                            selector:@selector(didExitFullScreen)
621                                                name:NSWindowDidExitFullScreenNotification
622                                              object:window];
624   MyView *myView = [[MyView alloc] initWithFrame:[[window contentView] bounds]
625                                        connector:connector];
627   [window setContentView:myView];
628   [window makeFirstResponder:myView];
630   [myView setWantsBestResolutionOpenGLSurface:YES];
632   NSOpenGLPixelFormatAttribute attrs[] =
633     {
634       NSOpenGLPFAAccelerated,
635       NSOpenGLPFADoubleBuffer,
636       NSOpenGLPFAColorSize, 24,
637       NSOpenGLPFAAlphaSize, 8,
638       NSOpenGLPFADepthSize, 24,
639       0
640     };
641   NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
642   glContext = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext:nil];
643   GLint swapInt = 1;
644   [glContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
645   [glContext setView:myView];
647   backing_scale_factor = [window backingScaleFactor];
650 - (void)reshape:(NSValue *)val
652   // NSLog (@"reshape: %@ isFullScreen: %d", val, [window isFullScreen]);
653   if ([window isFullScreen]) {
654     [window toggleFullScreen:self];
655   }
656   [window setFrame:[window frameRectForContentRect:[val rectValue]]
657            display:YES];
660 - (void)makeCurrentContext
662   [glContext makeCurrentContext];
663   NSLog (@"OpenGL Version: %s", glGetString(GL_VERSION));
666 - (void)swapb
668   [glContext flushBuffer];
671 - (void)didEnterFullScreen
673   // NSLog (@"didEnterFullScreen: %d", [window isFullScreen]);
674   [connector notifyWinstate:YES];
677 - (void)didExitFullScreen
679   // NSLog (@"didExitFullScreen: %d", [window isFullScreen]);
680   [connector notifyWinstate:NO];
683 - (void)fullscreen
685   // NSLog (@"fullscreen: %d", [window isFullScreen]);
686   if ([window isFullScreen] == NO) {
687     [window toggleFullScreen:self];
688   }
691 - (void)setCursor:(NSCursor *)aCursor
693   [[window contentView] setCursor: aCursor];
694   [window invalidateCursorRectsForView:[window contentView]];
697 - (void)windowDidResize:(NSNotification *)notification
699   [glContext update];
700   NSRect frame = [[window contentView] convertFrameToBacking];
701   [connector notifyReshapeWidth:frame.size.width height:frame.size.height];
704 - (void)windowDidMove:(NSNotification *)notification
706   [glContext update];
709 - (void)applicationWillTerminate:(NSDictionary *)userInfo
711   pthread_mutex_lock (&terminate_mutex);
712   if (terminating == 0) {
713     terminating = 1;
714     [connector notifyQuit];
715   }
716   pthread_mutex_unlock (&terminate_mutex);
717   pthread_join (thread, NULL);
720 - (void)windowDidChangeOcclusionState:(NSNotification *)notification
724 - (void)applicationDidFinishLaunching:(NSNotification *)not
726   NSLog(@"applicationDidFinishLaunching");
727   int ret = pthread_create (&thread, NULL, caml_main_thread, argv);
728   if (ret != 0) {
729     Abort (@"pthread_create: %s.", strerror (ret));
730   }
733 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
735   return YES;
738 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
740   NSLog (@"openFile: %@", filename);
741   [connector openFile:filename];
742   return YES;
745 - (void)openDocument:(id)sender
747   NSOpenPanel *openPanel = [NSOpenPanel openPanel];
748   [openPanel beginSheetModalForWindow:window
749                     completionHandler:^(NSInteger result){
750       if (result == NSFileHandlingPanelOKButton) {
751          NSString *filename = [[[openPanel URLs] objectAtIndex:0] path];
752          if (filename != nil) {
753            [self application:NSApp openFile:filename];
754          }
755       }
756     }];
759 - (void)reportIssue:(id)sender
761   [[NSWorkspace sharedWorkspace]
762     openURL:[NSURL URLWithString:@"https://github.com/moosotc/llpp/issues"]];
765 @end
767 CAMLprim value ml_mapwin (value unit)
769   CAMLparam1 (unit);
770   [[NSApp delegate] performSelectorOnMainThread:@selector(mapwin)
771                                      withObject:nil
772                                   waitUntilDone:YES];
773   CAMLreturn (Val_unit);
776 CAMLprim value ml_swapb (value unit)
778   CAMLparam1 (unit);
779   [[NSApp delegate] swapb];
780   CAMLreturn (Val_unit);
783 CAMLprim value ml_getw (value unit)
785   return Val_int([[NSApp delegate] getw]);
788 CAMLprim value ml_geth (value unit)
790   return Val_int([[NSApp delegate] geth]);
793 CAMLprim value ml_makecurrentcontext (value unit)
795   CAMLparam1 (unit);
796   [[NSApp delegate] makeCurrentContext];
797   CAMLreturn (Val_unit);
800 CAMLprim value ml_setwinbgcol (value col)
802   CAMLparam1 (col);
803   int r = ((col >> 16) & 0xff) / 255;
804   int g = ((col >> 8) & 0xff) / 255;
805   int b = ((col >> 0) & 0xff) / 255;
806   NSColor *color = [NSColor colorWithRed:r green:g blue:b alpha:1.0];
807   [[NSApp delegate] performSelectorOnMainThread:@selector(setwinbgcol:)
808                                      withObject:color
809                                   waitUntilDone:YES];
810   CAMLreturn (Val_unit);
813 CAMLprim value ml_settitle (value title)
815   CAMLparam1 (title);
816   NSString *str = [NSString stringWithUTF8String:String_val(title)];
817   [[NSApp delegate] performSelectorOnMainThread:@selector(setTitle:)
818                                      withObject:str
819                                   waitUntilDone:YES];
820   CAMLreturn (Val_unit);
823 CAMLprim value ml_reshape (value w, value h)
825   CAMLparam2 (w, h);
826   NSRect r = NSMakeRect (0, 0, Int_val (w), Int_val (h));
827   [[NSApp delegate] performSelectorOnMainThread:@selector(reshape:)
828                                      withObject:[NSValue valueWithRect:r]
829                                   waitUntilDone:YES];
830   CAMLreturn (Val_unit);
833 CAMLprim value ml_fullscreen (value unit)
835   CAMLparam1 (unit);
836   [[NSApp delegate] performSelectorOnMainThread:@selector(fullscreen)
837                                      withObject:nil
838                                   waitUntilDone:YES];
839   CAMLreturn (Val_unit);
842 CAMLprim value ml_setcursor (value curs)
844   CAMLparam1 (curs);
845   // NSLog (@"ml_setcursor: %d", Int_val (curs));
846   NSCursor *cursor = GetCursor (Int_val (curs));
847   [[NSApp delegate] performSelectorOnMainThread:@selector(setCursor:)
848                                      withObject:cursor
849                                   waitUntilDone:YES];
850   CAMLreturn (Val_unit);
853 CAMLprim value ml_get_server_fd (value unit)
855   CAMLparam1 (unit);
856   CAMLreturn (Val_int (server_fd));
859 CAMLprim value ml_get_backing_scale_factor (value unit)
861   CAMLparam1 (unit);
862   CAMLreturn (Val_int ((int) backing_scale_factor));
865 CAMLprim value ml_nslog (value str)
867   CAMLparam1 (str);
868   NSLog (@"%s", String_val (str));
869   CAMLreturn (Val_unit);
872 // HACK to eliminate arg injected by OS X -psn_...
873 int adjust_argv (int argc, char **argv)
875   if (argc > 1 && strncmp (argv[1], "-psn", 4) == 0) {
876     for (unsigned i = 1; i < argc - 1; i ++) {
877       argv[i] = argv[i+1];
878     }
879     argv[-- argc] = 0;
880   }
881   for (int i = 0; i < argc; i ++) {
882     NSLog (@"arg %d: %s", i, argv[i]);
883   }
884   return argc;
887 int main(int argc, char **argv)
889   @autoreleasepool {
890     int sv[2];
891     int ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sv);
892     if (ret != 0) {
893       Abort (@"socketpair: %s", strerror (ret));
894     }
895     // NSLog (@"socketpair sv0 %d sv1 %d", sv[0], sv[1]);
896     server_fd = sv[0];
897     argc = adjust_argv (argc, argv);
898     [NSApplication sharedApplication];
899     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
900     id delegate = [[MyDelegate alloc] initWithArgv:argv fileDescriptor:sv[1]];
901     [NSApp setDelegate:delegate];
902     [NSApp activateIgnoringOtherApps:YES];
903     [NSApp run];
904   }
905   return EXIT_SUCCESS;