Reject empty keys
[llpp.git] / main_osx.m
blob298d6ca968422efb38592f85cc9394346b2729ac
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 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 10120
46 #define NS_CRITICAL_ALERT_STYLE NSAlertStyleCritical
47 #define NS_FULL_SCREEN_WINDOW_MASK NSWindowStyleMaskFullScreen
48 #define NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK NSEventModifierFlagDeviceIndependentFlagsMask
49 #define NS_FUNCTION_KEY_MASK NSEventModifierFlagFunction
50 #define NS_ALTERNATE_KEY_MASK NSEventModifierFlagOption
51 #define NS_COMMAND_KEY_MASK NSEventModifierFlagCommand
52 #define NS_CLOSABLE_WINDOW_MASK NSWindowStyleMaskClosable
53 #define NS_MINIATURIZABLE_WINDOW_MASK NSWindowStyleMaskMiniaturizable
54 #define NS_TITLED_WINDOW_MASK NSWindowStyleMaskTitled
55 #define NS_RESIZABLE_WINDOW_MASK NSWindowStyleMaskResizable
57 #else
59 #define NS_CRITICAL_ALERT_STYLE NSCriticalAlertStyle
60 #define NS_FULL_SCREEN_WINDOW_MASK NSFullScreenWindowMask
61 #define NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK NSDeviceIndependentModifierFlagsMask
62 #define NS_FUNCTION_KEY_MASK NSFunctionKeyMask
63 #define NS_ALTERNATE_KEY_MASK NSAlternateKeyMask
64 #define NS_COMMAND_KEY_MASK NSCommandKeyMask
65 #define NS_CLOSABLE_WINDOW_MASK NSClosableWindowMask
66 #define NS_MINIATURIZABLE_WINDOW_MASK NSMiniaturizableWindowMask
67 #define NS_TITLED_WINDOW_MASK NSTitledWindowMask
68 #define NS_RESIZABLE_WINDOW_MASK NSResizableWindowMask
70 #endif
72 void Abort (NSString *format, ...)
74   va_list argList;
75   va_start (argList, format);
76   NSString *str = [[NSString alloc] initWithFormat:format arguments:argList];
77   va_end (argList);
78   NSLog (@"%@", str);
79   NSAlert *alert = [[NSAlert alloc] init];
80   [alert addButtonWithTitle:@"Quit"];
81   [alert setMessageText:@"Internal Error"];
82   [alert setInformativeText:str];
83   [alert setAlertStyle:NS_CRITICAL_ALERT_STYLE];
84   [alert runModal];
85   [NSApp terminate:nil];
88 void *caml_main_thread (void *argv)
90   @autoreleasepool {
91     caml_main (argv);
92     pthread_mutex_lock (&terminate_mutex);
93     if (terminating == 0) {
94       terminating = 1;
95       [NSApp performSelectorOnMainThread:@selector(terminate:)
96                               withObject:nil
97                            waitUntilDone:NO];
98     }
99     pthread_mutex_unlock (&terminate_mutex);
100   }
101   pthread_exit (NULL);
104 NSCursor *GetCursor (int idx)
106   static NSCursor *cursors[5];
107   static BOOL initialised = NO;
109   if (initialised == NO) {
110     cursors[0] = [NSCursor arrowCursor];
111     cursors[1] = [NSCursor pointingHandCursor];
112     cursors[2] = [NSCursor arrowCursor];
113     cursors[3] = [NSCursor closedHandCursor];
114     cursors[4] = [NSCursor IBeamCursor];
115     initialised = YES;
116   }
118   return cursors[idx];
121 @implementation NSWindow (CategoryNSWindow)
123 - (BOOL)isFullScreen
125   return ([self styleMask] & NS_FULL_SCREEN_WINDOW_MASK) == NS_FULL_SCREEN_WINDOW_MASK;
128 @end
130 @implementation NSView (CategoryNSView)
132 - (NSPoint)locationFromEvent:(NSEvent *)event
134   NSPoint point =
135     [self convertPointToBacking:[self convertPoint:[event locationInWindow] fromView:nil]];
136   NSRect bounds = [self convertRectToBacking:[self bounds]];
137   point.y = bounds.size.height - point.y;
138   return point;
141 - (NSRect)convertFrameToBacking
143   return [self convertRectToBacking:[self frame]];
146 @end
148 @implementation NSEvent (CategoryNSEvent)
150 - (int)deviceIndependentModifierFlags
152   return [self modifierFlags] & NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK;
155 @end
157 @interface Connector : NSObject
159 - (instancetype)initWithFileDescriptor:(int)fd;
161 - (void)notifyReshapeWidth:(int)w height:(int)h;
162 - (void)notifyExpose;
163 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask;
164 - (void)notifyQuit;
165 - (void)mouseEntered:(NSPoint)loc;
166 - (void)mouseExited;
167 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
168 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
169 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
170 @end
172 @implementation Connector
174   NSMutableData *data;
175   NSFileHandle *fileHandle;
178 - (instancetype)initWithFileDescriptor:(int)fd
180   self = [super init];
181   data = [NSMutableData dataWithLength:32];
182   fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd];
183   return self;
186 - (void)setByte:(int8_t)b offset:(int)off
188   [data replaceBytesInRange:NSMakeRange (off, 1) withBytes:&b];
191 - (void)setShort:(int16_t)s offset:(int)off
193   [data replaceBytesInRange:NSMakeRange (off, 2) withBytes:&s];
196 - (void)setInt:(int32_t)n offset:(int)off
198   [data replaceBytesInRange:NSMakeRange (off, 4) withBytes:&n];
201 - (void)writeData
203   [fileHandle writeData:data];
206 - (void)notifyReshapeWidth:(int)w height:(int)h
208   [self setByte:EVENT_RESHAPE offset:0];
209   [self setShort:w offset:16];
210   [self setShort:h offset:18];
211   [self writeData];
214 - (void)notifyExpose
216   [self setByte:EVENT_EXPOSE offset:0];
217   [self writeData];
220 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask
222   [self setByte:EVENT_KEYDOWN offset:0];
223   [self setInt:key offset:16];
224   [self setInt:mask offset:20];
225   [self writeData];
228 - (void)notifyWinstate:(BOOL)fullScreen
230   [self setByte:EVENT_WINSTATE offset:0];
231   [self setInt:fullScreen offset:16];
232   [self writeData];
235 - (void)notifyQuit
237   [self setByte:EVENT_QUIT offset:0];
238   [self writeData];
241 - (void)mouseEntered:(NSPoint)loc
243   [self setByte:EVENT_ENTER offset:0];
244   [self setShort:loc.x offset:16];
245   [self setShort:loc.y offset:20];
246   [self writeData];
249 - (void)mouseExited
251   [self setByte:EVENT_LEAVE offset:0];
252   [self writeData];
255 - (void)mouseDragged:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
257   [self setByte:EVENT_MOTION offset:0];
258   [self setShort:aPoint.x offset:16];
259   [self setShort:aPoint.y offset:20];
260   [self setInt:flags offset:24];
261   [self writeData];
264 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
266   [self setByte:EVENT_PMOTION offset:0];
267   [self setShort:aPoint.x offset:16];
268   [self setShort:aPoint.y offset:20];
269   [self setInt:flags offset:24];
270   [self writeData];
273 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
275   [self setByte:EVENT_MOUSE offset:0];
276   [self setShort:1 offset:10];
277   [self setInt:buttons offset:12];
278   [self setShort:aPoint.x offset:16];
279   [self setShort:aPoint.y offset:20];
280   [self setInt:flags offset:24];
281   [self writeData];
284 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
286   [self setByte:EVENT_MOUSE offset:0];
287   [self setShort:0 offset:10];
288   [self setInt:buttons offset:12];
289   [self setShort:aPoint.x offset:16];
290   [self setShort:aPoint.y offset:20];
291   [self setInt:flags offset:24];
292   [self writeData];
295 - (void)scrollByDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY
297   [self setByte:EVENT_SCROLL offset:0];
298   [self setInt:(int32_t) deltaX offset:16];
299   [self setInt:(int32_t) deltaY offset:20];
300   [self writeData];
303 - (void)zoom:(CGFloat)z at:(NSPoint)p
305   [self setByte:EVENT_ZOOM offset:0];
306   [self setInt:(int32_t) (z * 1000) offset:16];
307   [self setShort:p.x offset:20];
308   [self setShort:p.y offset:22];
309   [self writeData];
312 - (void)openFile:(NSString *)filename
314   const char *utf8 = [filename UTF8String];
315   unsigned len = [filename lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
316   [self setByte:EVENT_OPEN offset:0];
317   unsigned off = 0;
318   unsigned data_len = [data length] - 4;
319   while (off < len) {
320     unsigned chunk_len = MIN (data_len - 4, len - off);
321     [self setShort:chunk_len offset:2];
322     [data replaceBytesInRange:NSMakeRange (4, chunk_len) withBytes:(utf8 + off)];
323     [self writeData];
324     off += chunk_len;
325   }
326   [self setShort:0 offset:2];
327   [self writeData];
330 @end
332 @interface MyDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
334 - (int)getw;
335 - (int)geth;
336 - (void)swapb;
337 - (void)setwinbgcol:(NSColor *)col;
338 - (void)applicationWillFinishLaunching:(NSNotification *)not;
339 - (void)applicationDidFinishLaunching:(NSNotification *)not;
340 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
341 - (void)makeCurrentContext;
343 @end
345 @interface MyWindow : NSWindow
347 @end
349 @interface MyView : NSView
351   Connector *connector;
352   NSCursor *cursor;
355 - (instancetype)initWithFrame:(NSRect)frame connector:(Connector *)aConnector;
356 - (void)setCursor:(NSCursor *)aCursor;
358 @end
360 @implementation MyView
362 - (instancetype)initWithFrame:(NSRect)frame connector:(Connector *)aConnector
364   self = [super initWithFrame:frame];
366   if (self != NULL) {
367     connector = aConnector;
368     cursor = [NSCursor arrowCursor];
369     self.acceptsTouchEvents = YES;
370   }
372   return self;
375 - (void)setCursor:(NSCursor *)aCursor
377   cursor = aCursor;
380 -(void)resetCursorRects
382   [self addCursorRect:[self bounds] cursor:cursor];
385 - (void)drawRect:(NSRect)bounds
387   // NSLog(@"drawRect: %@", [NSValue valueWithRect:bounds]);
388   [connector notifyExpose];
391 - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
392   NSTrackingArea* trackingArea = [[NSTrackingArea alloc]
393                                    initWithRect:[self bounds]
394                                  options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect)
395                                           owner:self
396                                        userInfo:nil];
397   [self addTrackingArea:trackingArea];
400 - (void)keyDown:(NSEvent *)event
402   // int key = [event keyCode];
403   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
404   NSString *chars = [event charactersIgnoringModifiers];
405   const uint32_t *c = (uint32_t *) [chars cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
406   while (*c) {
407     if (*c == 0x7f && !(mask & NS_FUNCTION_KEY_MASK)) {
408       [connector keyDown:0x8 modifierFlags:mask];
409     } else {
410       [connector keyDown:*c modifierFlags:mask];
411     }
412     c++;
413   }
416 - (void)flagsChanged:(NSEvent *)event
418   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
419   NSLog (@"flagsChanged: 0x%lx", mask);
420   if (mask != 0) {
421     [connector keyDown:0 modifierFlags:mask];
422   }
425 - (void)mouseDown:(NSEvent *)event
427   [connector mouseDown:BUTTON_LEFT
428                atPoint:[self locationFromEvent:event]
429          modifierFlags:[event deviceIndependentModifierFlags]];
432 - (void)mouseUp:(NSEvent *)event
434   [connector mouseUp:BUTTON_LEFT
435              atPoint:[self locationFromEvent:event]
436        modifierFlags:[event deviceIndependentModifierFlags]];
439 - (void)rightMouseDown:(NSEvent *)event
441   [connector mouseDown:BUTTON_RIGHT
442                atPoint:[self locationFromEvent:event]
443          modifierFlags:[event deviceIndependentModifierFlags]];
446 - (void)rightMouseUp:(NSEvent *)event
448   [connector mouseUp:BUTTON_RIGHT
449              atPoint:[self locationFromEvent:event]
450        modifierFlags:[event deviceIndependentModifierFlags]];
453 - (void)rightMouseDragged:(NSEvent *)event
455   [connector mouseDragged:[self locationFromEvent:event]
456             modifierFlags:[event deviceIndependentModifierFlags]];
459 - (void)mouseDragged:(NSEvent *)event
461   [connector mouseDragged:[self locationFromEvent:event]
462             modifierFlags:[event deviceIndependentModifierFlags]];
465 - (void)mouseMoved:(NSEvent *)event
467   [connector mouseMoved:[self locationFromEvent:event]
468           modifierFlags:[event deviceIndependentModifierFlags]];
471 - (void)mouseEntered:(NSEvent *)event
473   [connector mouseEntered:[self locationFromEvent:event]];
476 - (void)mouseExited:(NSEvent *)event
478   [connector mouseExited];
481 - (void)scrollWheel:(NSEvent *)event
483   CGFloat deltaX = [event scrollingDeltaX];
484   CGFloat deltaY = -[event scrollingDeltaY];
486   if ([event hasPreciseScrollingDeltas]) {
487     [connector scrollByDeltaX:(backing_scale_factor * deltaX)
488                        deltaY:(backing_scale_factor * deltaY)];
489   } else {
490     NSPoint loc = [self locationFromEvent:event];
491     NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
492     if (deltaY > 0.0) {
493       [connector mouseDown:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
494       [connector mouseUp:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
495     } else if (deltaY < 0.0) {
496       [connector mouseDown:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
497       [connector mouseUp:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
498     }
499   }
502 - (void)magnifyWithEvent:(NSEvent *)event
504   [connector zoom:[event magnification] at:[self locationFromEvent:event]];
507 @end
509 @implementation MyWindow
511 - (BOOL)canBecomeKeyWindow
513   return YES;
516 @end
518 @implementation MyDelegate
520   char **argv;
521   MyWindow *window;
522   NSOpenGLContext *glContext;
523   pthread_t thread;
524   Connector *connector;
527 - (instancetype)initWithArgv:(char **)theArgv fileDescriptor:(int)fd
529   self = [super init];
530   if (self != NULL) {
531     argv = theArgv;
532     connector = [[Connector alloc] initWithFileDescriptor:fd];
533   }
534   return self;
537 - (void)setTitle:(NSString *)title
539   [window setTitle:title];
542 - (void)mapwin
544   [window makeKeyAndOrderFront:self];
547 - (int)getw
549   return [[window contentView] convertFrameToBacking].size.width;
552 - (int)geth
554   return [[window contentView] convertFrameToBacking].size.height;
557 - (void)setwinbgcol:(NSColor *)col
559   [window setBackgroundColor:col];
562 - (void)applicationWillFinishLaunching:(NSNotification *)not
564   NSLog(@"applicationWillFinishLaunching");
565   id menubar = [NSMenu new];
566   id appMenuItem = [NSMenuItem new];
567   id fileMenuItem = [NSMenuItem new];
568   id windowMenuItem = [NSMenuItem new];
569   id helpMenuItem = [NSMenuItem new];
570   [menubar addItem:appMenuItem];
571   [menubar addItem:fileMenuItem];
572   [menubar addItem:windowMenuItem];
573   [menubar addItem:helpMenuItem];
574   [NSApp setMainMenu:menubar];
575   id appMenu = [NSMenu new];
576   id appName = [[NSProcessInfo processInfo] processName];
577   id aboutMenuItem = [[NSMenuItem alloc] initWithTitle:[@"About " stringByAppendingString:appName]
578                                                 action:@selector(orderFrontStandardAboutPanel:)
579                                          keyEquivalent:@""];
580   id hideMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName]
581                                                action:@selector(hide:)
582                                         keyEquivalent:@"h"];
583   id hideOthersMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
584                                                      action:@selector(hideOtherApplications:)
585                                               keyEquivalent:@"h"];
586   [hideOthersMenuItem setKeyEquivalentModifierMask:(NS_ALTERNATE_KEY_MASK | NS_COMMAND_KEY_MASK)];
587   id showAllMenuItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
588                                                   action:@selector(unhideAllApplications:)
589                                            keyEquivalent:@""];
590   id quitMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Quit " stringByAppendingString:appName]
591                                                action:@selector(terminate:)
592                                         keyEquivalent:@"q"];
593   [appMenu addItem:aboutMenuItem];
594   [appMenu addItem:[NSMenuItem separatorItem]];
595   [appMenu addItem:hideMenuItem];
596   [appMenu addItem:hideOthersMenuItem];
597   [appMenu addItem:showAllMenuItem];
598   [appMenu addItem:[NSMenuItem separatorItem]];
599   [appMenu addItem:quitMenuItem];
600   [appMenuItem setSubmenu:appMenu];
602   id fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
603   id openMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open..."
604                                                action:@selector(openDocument:)
605                                         keyEquivalent:@"o"];
606   id closeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Close"
607                                                 action:@selector(performClose:)
608                                          keyEquivalent:@"w"];
609   [fileMenu addItem:openMenuItem];
610   [fileMenu addItem:[NSMenuItem separatorItem]];
611   [fileMenu addItem:closeMenuItem];
612   [fileMenuItem setSubmenu:fileMenu];
614   id windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
615   id miniaturizeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
616                                                    action:@selector(performMiniaturize:)
617                                             keyEquivalent:@"m"];
618   id zoomMenuItem = [[NSMenuItem alloc] initWithTitle:@"Zoom"
619                                                action:@selector(performZoom:)
620                                         keyEquivalent:@""];
622   [windowMenu addItem:miniaturizeMenuItem];
623   [windowMenu addItem:zoomMenuItem];
624   [windowMenuItem setSubmenu:windowMenu];
626   id helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
627   id reportIssueMenuItem = [[NSMenuItem alloc] initWithTitle:@"Report an issue..."
628                                                       action:@selector(reportIssue:)
629                                                keyEquivalent:@""];
630   [helpMenu addItem:reportIssueMenuItem];
631   [helpMenuItem setSubmenu:helpMenu];
633   window = [[MyWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
634                                        styleMask:(NS_CLOSABLE_WINDOW_MASK | NS_MINIATURIZABLE_WINDOW_MASK | NS_TITLED_WINDOW_MASK | NS_RESIZABLE_WINDOW_MASK)
635                                          backing:NSBackingStoreBuffered
636                                            defer:NO];
638   [window center];
639   [window setAcceptsMouseMovedEvents:YES];
640   [window setDelegate:self];
643   [[NSNotificationCenter defaultCenter] addObserver:self
644                                            selector:@selector(didEnterFullScreen)
645                                                name:NSWindowDidEnterFullScreenNotification
646                                              object:window];
647   [[NSNotificationCenter defaultCenter] addObserver:self
648                                            selector:@selector(didExitFullScreen)
649                                                name:NSWindowDidExitFullScreenNotification
650                                              object:window];
652   MyView *myView = [[MyView alloc] initWithFrame:[[window contentView] bounds]
653                                        connector:connector];
655   [window setContentView:myView];
656   [window makeFirstResponder:myView];
658   [myView setWantsBestResolutionOpenGLSurface:YES];
660   NSOpenGLPixelFormatAttribute attrs[] =
661     {
662       NSOpenGLPFAAccelerated,
663       NSOpenGLPFADoubleBuffer,
664       NSOpenGLPFAColorSize, 24,
665       NSOpenGLPFAAlphaSize, 8,
666       NSOpenGLPFADepthSize, 24,
667       0
668     };
669   NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
670   glContext = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext:nil];
671   GLint swapInt = 1;
672   [glContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
673   [glContext setView:myView];
675   backing_scale_factor = [window backingScaleFactor];
678 - (void)reshape:(NSValue *)val
680   // NSLog (@"reshape: %@ isFullScreen: %d", val, [window isFullScreen]);
681   if ([window isFullScreen]) {
682     [window toggleFullScreen:self];
683   }
684   [window setFrame:[window frameRectForContentRect:[val rectValue]]
685            display:YES];
688 - (void)makeCurrentContext
690   [glContext makeCurrentContext];
691   NSLog (@"OpenGL Version: %s", glGetString(GL_VERSION));
694 - (void)swapb
696   [glContext flushBuffer];
699 - (void)didEnterFullScreen
701   // NSLog (@"didEnterFullScreen: %d", [window isFullScreen]);
702   [connector notifyWinstate:YES];
705 - (void)didExitFullScreen
707   // NSLog (@"didExitFullScreen: %d", [window isFullScreen]);
708   [connector notifyWinstate:NO];
711 - (void)fullscreen
713   // NSLog (@"fullscreen: %d", [window isFullScreen]);
714   if ([window isFullScreen] == NO) {
715     [window toggleFullScreen:self];
716   }
719 - (void)setCursor:(NSCursor *)aCursor
721   [[window contentView] setCursor: aCursor];
722   [window invalidateCursorRectsForView:[window contentView]];
725 - (void)windowDidResize:(NSNotification *)notification
727   [glContext update];
728   NSRect frame = [[window contentView] convertFrameToBacking];
729   [connector notifyReshapeWidth:frame.size.width height:frame.size.height];
732 - (void)windowDidMove:(NSNotification *)notification
734   [glContext update];
737 - (void)applicationWillTerminate:(NSDictionary *)userInfo
739   pthread_mutex_lock (&terminate_mutex);
740   if (terminating == 0) {
741     terminating = 1;
742     [connector notifyQuit];
743   }
744   pthread_mutex_unlock (&terminate_mutex);
745   pthread_join (thread, NULL);
748 - (void)windowDidChangeOcclusionState:(NSNotification *)notification
752 - (void)applicationDidFinishLaunching:(NSNotification *)not
754   NSLog(@"applicationDidFinishLaunching");
755   int ret = pthread_create (&thread, NULL, caml_main_thread, argv);
756   if (ret != 0) {
757     Abort (@"pthread_create: %s.", strerror (ret));
758   }
761 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
763   return YES;
766 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
768   NSLog (@"openFile: %@", filename);
769   [connector openFile:filename];
770   return YES;
773 - (void)openDocument:(id)sender
775   NSOpenPanel *openPanel = [NSOpenPanel openPanel];
776   [openPanel beginSheetModalForWindow:window
777                     completionHandler:^(NSInteger result){
778       if (result == NSFileHandlingPanelOKButton) {
779          NSString *filename = [[[openPanel URLs] objectAtIndex:0] path];
780          if (filename != nil) {
781            [self application:NSApp openFile:filename];
782          }
783       }
784     }];
787 - (void)reportIssue:(id)sender
789   [[NSWorkspace sharedWorkspace]
790     openURL:[NSURL URLWithString:@"https://github.com/moosotc/llpp/issues"]];
793 @end
795 CAMLprim value ml_mapwin (value unit)
797   CAMLparam1 (unit);
798   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(mapwin)
799                                                    withObject:nil
800                                                 waitUntilDone:YES];
801   CAMLreturn (Val_unit);
804 CAMLprim value ml_swapb (value unit)
806   CAMLparam1 (unit);
807   [(MyDelegate *)[NSApp delegate] swapb];
808   CAMLreturn (Val_unit);
811 CAMLprim value ml_getw (value unit)
813   return Val_int([(MyDelegate *)[NSApp delegate] getw]);
816 CAMLprim value ml_geth (value unit)
818   return Val_int([(MyDelegate *)[NSApp delegate] geth]);
821 CAMLprim value ml_makecurrentcontext (value unit)
823   CAMLparam1 (unit);
824   [(MyDelegate *)[NSApp delegate] makeCurrentContext];
825   CAMLreturn (Val_unit);
828 CAMLprim value ml_setwinbgcol (value col)
830   CAMLparam1 (col);
831   int r = ((col >> 16) & 0xff) / 255;
832   int g = ((col >> 8) & 0xff) / 255;
833   int b = ((col >> 0) & 0xff) / 255;
834   NSColor *color = [NSColor colorWithRed:r green:g blue:b alpha:1.0];
835   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(setwinbgcol:)
836                                                    withObject:color
837                                                 waitUntilDone:YES];
838   CAMLreturn (Val_unit);
841 CAMLprim value ml_settitle (value title)
843   CAMLparam1 (title);
844   NSString *str = [NSString stringWithUTF8String:String_val(title)];
845   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(setTitle:)
846                                                    withObject:str
847                                                 waitUntilDone:YES];
848   CAMLreturn (Val_unit);
851 CAMLprim value ml_reshape (value w, value h)
853   CAMLparam2 (w, h);
854   NSRect r = NSMakeRect (0, 0, Int_val (w), Int_val (h));
855   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(reshape:)
856                                                    withObject:[NSValue valueWithRect:r]
857                                                 waitUntilDone:YES];
858   CAMLreturn (Val_unit);
861 CAMLprim value ml_fullscreen (value unit)
863   CAMLparam1 (unit);
864   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(fullscreen)
865                                                    withObject:nil
866                                                 waitUntilDone:YES];
867   CAMLreturn (Val_unit);
870 CAMLprim value ml_setcursor (value curs)
872   CAMLparam1 (curs);
873   // NSLog (@"ml_setcursor: %d", Int_val (curs));
874   NSCursor *cursor = GetCursor (Int_val (curs));
875   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(setCursor:)
876                                                    withObject:cursor
877                                                 waitUntilDone:YES];
878   CAMLreturn (Val_unit);
881 CAMLprim value ml_get_server_fd (value unit)
883   CAMLparam1 (unit);
884   CAMLreturn (Val_int (server_fd));
887 CAMLprim value ml_get_backing_scale_factor (value unit)
889   CAMLparam1 (unit);
890   CAMLreturn (Val_int ((int) backing_scale_factor));
893 CAMLprim value ml_nslog (value str)
895   CAMLparam1 (str);
896   NSLog (@"%s", String_val (str));
897   CAMLreturn (Val_unit);
900 // HACK to eliminate arg injected by OS X -psn_...
901 int adjust_argv (int argc, char **argv)
903   if (argc > 1 && strncmp (argv[1], "-psn", 4) == 0) {
904     for (unsigned i = 1; i < argc - 1; i ++) {
905       argv[i] = argv[i+1];
906     }
907     argv[-- argc] = 0;
908   }
909   for (int i = 0; i < argc; i ++) {
910     NSLog (@"arg %d: %s", i, argv[i]);
911   }
912   return argc;
915 int main(int argc, char **argv)
917   @autoreleasepool {
918     int sv[2];
919     int ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sv);
920     if (ret != 0) {
921       Abort (@"socketpair: %s", strerror (ret));
922     }
923     // NSLog (@"socketpair sv0 %d sv1 %d", sv[0], sv[1]);
924     server_fd = sv[0];
925     argc = adjust_argv (argc, argv);
926     [NSApplication sharedApplication];
927     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
928     id delegate = [[MyDelegate alloc] initWithArgv:argv fileDescriptor:sv[1]];
929     [NSApp setDelegate:delegate];
930     [NSApp activateIgnoringOtherApps:YES];
931     [NSApp run];
932   }
933   return EXIT_SUCCESS;