Ubuntu CI: make apt update before apt install
[llpp.git] / wsi / cocoa / cocoa.m
blobf2257545f34d24d842068df20a40179302ec96b9
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>
9 #include <string.h>
11 #include <caml/mlvalues.h>
12 #include <caml/memory.h>
13 #include <caml/callback.h>
14 #include <caml/alloc.h>
15 #include <caml/fail.h>
17 enum {
18   EVENT_EXPOSE = 1,
19   EVENT_RESHAPE = 3,
20   EVENT_MOUSE = 4,
21   EVENT_MOTION = 5,
22   EVENT_PMOTION = 6,
23   EVENT_KEYDOWN = 7,
24   EVENT_ENTER = 8,
25   EVENT_LEAVE = 9,
26   EVENT_WINSTATE = 10,
27   EVENT_QUIT = 11,
28   EVENT_SCROLL = 12,
29   EVENT_ZOOM = 13,
30   EVENT_OPEN = 20
33 enum {
34   BUTTON_LEFT = 1,
35   BUTTON_RIGHT = 3,
36   BUTTON_WHEEL_UP = 4,
37   BUTTON_WHEEL_DOWN = 5
40 static int terminating = 0;
41 static pthread_mutex_t terminate_mutex = PTHREAD_MUTEX_INITIALIZER;
42 static int server_fd = -1;
43 static CGFloat backing_scale_factor = -1.0;
45 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 10120
47 #define NS_CRITICAL_ALERT_STYLE NSAlertStyleCritical
48 #define NS_FULL_SCREEN_WINDOW_MASK NSWindowStyleMaskFullScreen
49 #define NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK NSEventModifierFlagDeviceIndependentFlagsMask
50 #define NS_FUNCTION_KEY_MASK NSEventModifierFlagFunction
51 #define NS_ALTERNATE_KEY_MASK NSEventModifierFlagOption
52 #define NS_COMMAND_KEY_MASK NSEventModifierFlagCommand
53 #define NS_CLOSABLE_WINDOW_MASK NSWindowStyleMaskClosable
54 #define NS_MINIATURIZABLE_WINDOW_MASK NSWindowStyleMaskMiniaturizable
55 #define NS_TITLED_WINDOW_MASK NSWindowStyleMaskTitled
56 #define NS_RESIZABLE_WINDOW_MASK NSWindowStyleMaskResizable
58 #else
60 #define NS_CRITICAL_ALERT_STYLE NSCriticalAlertStyle
61 #define NS_FULL_SCREEN_WINDOW_MASK NSFullScreenWindowMask
62 #define NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK NSDeviceIndependentModifierFlagsMask
63 #define NS_FUNCTION_KEY_MASK NSFunctionKeyMask
64 #define NS_ALTERNATE_KEY_MASK NSAlternateKeyMask
65 #define NS_COMMAND_KEY_MASK NSCommandKeyMask
66 #define NS_CLOSABLE_WINDOW_MASK NSClosableWindowMask
67 #define NS_MINIATURIZABLE_WINDOW_MASK NSMiniaturizableWindowMask
68 #define NS_TITLED_WINDOW_MASK NSTitledWindowMask
69 #define NS_RESIZABLE_WINDOW_MASK NSResizableWindowMask
71 #endif
73 void Abort (NSString *format, ...)
75   va_list argList;
76   va_start (argList, format);
77   NSString *str = [[NSString alloc] initWithFormat:format arguments:argList];
78   va_end (argList);
79   NSLog (@"%@", str);
80   NSAlert *alert = [[NSAlert alloc] init];
81   [alert addButtonWithTitle:@"Quit"];
82   [alert setMessageText:@"Internal Error"];
83   [alert setInformativeText:str];
84   [alert setAlertStyle:NS_CRITICAL_ALERT_STYLE];
85   [alert runModal];
86   [NSApp terminate:nil];
89 void *caml_main_thread (void *argv)
91   @autoreleasepool {
92     caml_main (argv);
93     pthread_mutex_lock (&terminate_mutex);
94     if (terminating == 0) {
95       terminating = 1;
96       [NSApp performSelectorOnMainThread:@selector(terminate:)
97                               withObject:nil
98                            waitUntilDone:NO];
99     }
100     pthread_mutex_unlock (&terminate_mutex);
101   }
102   pthread_exit (NULL);
105 NSCursor *GetCursor (int idx)
107   static NSCursor *cursors[5];
108   static BOOL initialised = NO;
110   if (initialised == NO) {
111     cursors[0] = [NSCursor arrowCursor];
112     cursors[1] = [NSCursor pointingHandCursor];
113     cursors[2] = [NSCursor arrowCursor];
114     cursors[3] = [NSCursor closedHandCursor];
115     cursors[4] = [NSCursor IBeamCursor];
116     initialised = YES;
117   }
119   return cursors[idx];
122 @implementation NSWindow (CategoryNSWindow)
124 - (BOOL)isFullScreen
126   return ([self styleMask] & NS_FULL_SCREEN_WINDOW_MASK) == NS_FULL_SCREEN_WINDOW_MASK;
129 @end
131 @implementation NSView (CategoryNSView)
133 - (NSPoint)locationFromEvent:(NSEvent *)event
135   NSPoint point =
136     [self convertPointToBacking:[self convertPoint:[event locationInWindow] fromView:nil]];
137   NSRect bounds = [self convertRectToBacking:[self bounds]];
138   point.y = bounds.size.height - point.y;
139   return point;
142 - (NSRect)convertFrameToBacking
144   return [self convertRectToBacking:[self frame]];
147 @end
149 @implementation NSEvent (CategoryNSEvent)
151 - (int)deviceIndependentModifierFlags
153   return [self modifierFlags] & NS_DEVICE_INDEPENDENT_MODIFIER_FLAGS_MASK;
156 @end
158 @interface Connector : NSObject
160 - (instancetype)initWithFileDescriptor:(int)fd;
162 - (void)notifyReshapeWidth:(int)w height:(int)h;
163 - (void)notifyExpose;
164 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask;
165 - (void)notifyQuit;
166 - (void)mouseEntered:(NSPoint)loc;
167 - (void)mouseExited;
168 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
169 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
170 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags;
171 @end
173 @implementation Connector
175   NSMutableData *data;
176   NSFileHandle *fileHandle;
179 - (instancetype)initWithFileDescriptor:(int)fd
181   self = [super init];
182   data = [NSMutableData dataWithLength:32];
183   fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd];
184   return self;
187 - (void)setByte:(int8_t)b offset:(int)off
189   [data replaceBytesInRange:NSMakeRange (off, 1) withBytes:&b];
192 - (void)setShort:(int16_t)s offset:(int)off
194   [data replaceBytesInRange:NSMakeRange (off, 2) withBytes:&s];
197 - (void)setInt:(int32_t)n offset:(int)off
199   [data replaceBytesInRange:NSMakeRange (off, 4) withBytes:&n];
202 - (void)writeData
204   [fileHandle writeData:data];
207 - (void)notifyReshapeWidth:(int)w height:(int)h
209   [self setByte:EVENT_RESHAPE offset:0];
210   [self setShort:w offset:16];
211   [self setShort:h offset:18];
212   [self writeData];
215 - (void)notifyExpose
217   [self setByte:EVENT_EXPOSE offset:0];
218   [self writeData];
221 - (void)keyDown:(uint32_t)key modifierFlags:(NSEventModifierFlags)mask
223   [self setByte:EVENT_KEYDOWN offset:0];
224   [self setInt:key offset:16];
225   [self setInt:mask offset:20];
226   [self writeData];
229 - (void)notifyWinstate:(BOOL)fullScreen
231   [self setByte:EVENT_WINSTATE offset:0];
232   [self setInt:fullScreen offset:16];
233   [self writeData];
236 - (void)notifyQuit
238   [self setByte:EVENT_QUIT offset:0];
239   [self writeData];
242 - (void)mouseEntered:(NSPoint)loc
244   [self setByte:EVENT_ENTER offset:0];
245   [self setShort:loc.x offset:16];
246   [self setShort:loc.y offset:20];
247   [self writeData];
250 - (void)mouseExited
252   [self setByte:EVENT_LEAVE offset:0];
253   [self writeData];
256 - (void)mouseDragged:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
258   [self setByte:EVENT_MOTION offset:0];
259   [self setShort:aPoint.x offset:16];
260   [self setShort:aPoint.y offset:20];
261   [self setInt:flags offset:24];
262   [self writeData];
265 - (void)mouseMoved:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
267   [self setByte:EVENT_PMOTION offset:0];
268   [self setShort:aPoint.x offset:16];
269   [self setShort:aPoint.y offset:20];
270   [self setInt:flags offset:24];
271   [self writeData];
274 - (void)mouseDown:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
276   [self setByte:EVENT_MOUSE offset:0];
277   [self setShort:1 offset:10];
278   [self setInt:buttons offset:12];
279   [self setShort:aPoint.x offset:16];
280   [self setShort:aPoint.y offset:20];
281   [self setInt:flags offset:24];
282   [self writeData];
285 - (void)mouseUp:(NSUInteger)buttons atPoint:(NSPoint)aPoint modifierFlags:(NSEventModifierFlags)flags
287   [self setByte:EVENT_MOUSE offset:0];
288   [self setShort:0 offset:10];
289   [self setInt:buttons offset:12];
290   [self setShort:aPoint.x offset:16];
291   [self setShort:aPoint.y offset:20];
292   [self setInt:flags offset:24];
293   [self writeData];
296 - (void)scrollByDeltaX:(CGFloat)deltaX deltaY:(CGFloat)deltaY
298   [self setByte:EVENT_SCROLL offset:0];
299   [self setInt:(int32_t) deltaX offset:16];
300   [self setInt:(int32_t) deltaY offset:20];
301   [self writeData];
304 - (void)zoom:(CGFloat)z at:(NSPoint)p
306   [self setByte:EVENT_ZOOM offset:0];
307   [self setInt:(int32_t) (z * 1000) offset:16];
308   [self setShort:p.x offset:20];
309   [self setShort:p.y offset:22];
310   [self writeData];
313 - (void)openFile:(NSString *)filename
315   const char *utf8 = [filename UTF8String];
316   unsigned len = [filename lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
317   [self setByte:EVENT_OPEN offset:0];
318   unsigned off = 0;
319   unsigned data_len = [data length] - 4;
320   while (off < len) {
321     unsigned chunk_len = MIN (data_len - 4, len - off);
322     [self setShort:chunk_len offset:2];
323     [data replaceBytesInRange:NSMakeRange (4, chunk_len) withBytes:(utf8 + off)];
324     [self writeData];
325     off += chunk_len;
326   }
327   [self setShort:0 offset:2];
328   [self writeData];
331 @end
333 @interface MyDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
335 - (int)getw;
336 - (int)geth;
337 - (void)swapb;
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 : NSOpenGLView
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   NSOpenGLPixelFormatAttribute attrs[] =
365     {
366       NSOpenGLPFAAccelerated,
367       NSOpenGLPFADoubleBuffer,
368       NSOpenGLPFAColorSize, 24,
369       NSOpenGLPFAAlphaSize, 8,
370       NSOpenGLPFADepthSize, 24,
371       0
372     };
373   NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
374   self = [super initWithFrame:frame pixelFormat:pixFormat];
376   if (self != NULL) {
377     connector = aConnector;
378     cursor = [NSCursor arrowCursor];
379     self.allowedTouchTypes = NSTouchTypeMaskDirect | NSTouchTypeMaskIndirect;
380     [self setWantsBestResolutionOpenGLSurface:YES];
381   }
383   return self;
386 - (void)setCursor:(NSCursor *)aCursor
388   cursor = aCursor;
391 -(void)resetCursorRects
393   [self addCursorRect:[self bounds] cursor:cursor];
396 - (void)drawRect:(NSRect)bounds
398   // NSLog(@"drawRect: %@", [NSValue valueWithRect:bounds]);
399   [connector notifyExpose];
402 - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
403   NSTrackingArea* trackingArea = [[NSTrackingArea alloc]
404                                    initWithRect:[self bounds]
405                                  options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect)
406                                           owner:self
407                                        userInfo:nil];
408   [self addTrackingArea:trackingArea];
411 - (void)keyDown:(NSEvent *)event
413   // int key = [event keyCode];
414   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
415   NSString *chars = [event charactersIgnoringModifiers];
416   const uint32_t *c = (uint32_t *) [chars cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
417   while (*c) {
418     if (*c == 0x7f && !(mask & NS_FUNCTION_KEY_MASK)) {
419       [connector keyDown:0x8 modifierFlags:mask];
420     } else {
421       [connector keyDown:*c modifierFlags:mask];
422     }
423     c++;
424   }
427 - (void)flagsChanged:(NSEvent *)event
429   NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
430   //NSLog (@"flagsChanged: 0x%lx", mask);
431   if (mask != 0) {
432     [connector keyDown:0 modifierFlags:mask];
433   }
436 - (void)mouseDown:(NSEvent *)event
438   [connector mouseDown:BUTTON_LEFT
439                atPoint:[self locationFromEvent:event]
440          modifierFlags:[event deviceIndependentModifierFlags]];
443 - (void)mouseUp:(NSEvent *)event
445   [connector mouseUp:BUTTON_LEFT
446              atPoint:[self locationFromEvent:event]
447        modifierFlags:[event deviceIndependentModifierFlags]];
450 - (void)rightMouseDown:(NSEvent *)event
452   [connector mouseDown:BUTTON_RIGHT
453                atPoint:[self locationFromEvent:event]
454          modifierFlags:[event deviceIndependentModifierFlags]];
457 - (void)rightMouseUp:(NSEvent *)event
459   [connector mouseUp:BUTTON_RIGHT
460              atPoint:[self locationFromEvent:event]
461        modifierFlags:[event deviceIndependentModifierFlags]];
464 - (void)rightMouseDragged:(NSEvent *)event
466   [connector mouseDragged:[self locationFromEvent:event]
467             modifierFlags:[event deviceIndependentModifierFlags]];
470 - (void)mouseDragged:(NSEvent *)event
472   [connector mouseDragged:[self locationFromEvent:event]
473             modifierFlags:[event deviceIndependentModifierFlags]];
476 - (void)mouseMoved:(NSEvent *)event
478   [connector mouseMoved:[self locationFromEvent:event]
479           modifierFlags:[event deviceIndependentModifierFlags]];
482 - (void)mouseEntered:(NSEvent *)event
484   [connector mouseEntered:[self locationFromEvent:event]];
487 - (void)mouseExited:(NSEvent *)event
489   [connector mouseExited];
492 - (void)scrollWheel:(NSEvent *)event
494   CGFloat deltaX = [event scrollingDeltaX];
495   CGFloat deltaY = -[event scrollingDeltaY];
497   if ([event hasPreciseScrollingDeltas]) {
498     [connector scrollByDeltaX:(backing_scale_factor * deltaX)
499                        deltaY:(backing_scale_factor * deltaY)];
500   } else {
501     NSPoint loc = [self locationFromEvent:event];
502     NSEventModifierFlags mask = [event deviceIndependentModifierFlags];
503     if (deltaY > 0.0) {
504       [connector mouseDown:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
505       [connector mouseUp:BUTTON_WHEEL_DOWN atPoint:loc modifierFlags:mask];
506     } else if (deltaY < 0.0) {
507       [connector mouseDown:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
508       [connector mouseUp:BUTTON_WHEEL_UP atPoint:loc modifierFlags:mask];
509     }
510   }
513 - (void)magnifyWithEvent:(NSEvent *)event
515   [connector zoom:[event magnification] at:[self locationFromEvent:event]];
518 @end
520 @implementation MyWindow
522 - (BOOL)canBecomeKeyWindow
524   return YES;
527 @end
529 @implementation MyDelegate
531   char **argv;
532   MyWindow *window;
533   NSOpenGLContext *glContext;
534   pthread_t thread;
535   Connector *connector;
538 - (instancetype)initWithArgv:(char **)theArgv fileDescriptor:(int)fd
540   self = [super init];
541   if (self != NULL) {
542     argv = theArgv;
543     connector = [[Connector alloc] initWithFileDescriptor:fd];
544   }
545   return self;
548 - (void)setTitle:(NSString *)title
550   [window setTitle:title];
553 - (void)mapwin
555   [window makeKeyAndOrderFront:self];
558 - (int)getw
560   return [[window contentView] convertFrameToBacking].size.width;
563 - (int)geth
565   return [[window contentView] convertFrameToBacking].size.height;
568 - (void)applicationWillFinishLaunching:(NSNotification *)not
570   NSLog(@"applicationWillFinishLaunching");
571   id menubar = [NSMenu new];
572   id appMenuItem = [NSMenuItem new];
573   id fileMenuItem = [NSMenuItem new];
574   id windowMenuItem = [NSMenuItem new];
575   id helpMenuItem = [NSMenuItem new];
576   [menubar addItem:appMenuItem];
577   [menubar addItem:fileMenuItem];
578   [menubar addItem:windowMenuItem];
579   [menubar addItem:helpMenuItem];
580   [NSApp setMainMenu:menubar];
581   id appMenu = [NSMenu new];
582   id appName = [[NSProcessInfo processInfo] processName];
583   id aboutMenuItem = [[NSMenuItem alloc] initWithTitle:[@"About " stringByAppendingString:appName]
584                                                 action:@selector(orderFrontStandardAboutPanel:)
585                                          keyEquivalent:@""];
586   id hideMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName]
587                                                action:@selector(hide:)
588                                         keyEquivalent:@"h"];
589   id hideOthersMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
590                                                      action:@selector(hideOtherApplications:)
591                                               keyEquivalent:@"h"];
592   [hideOthersMenuItem setKeyEquivalentModifierMask:(NS_ALTERNATE_KEY_MASK | NS_COMMAND_KEY_MASK)];
593   id showAllMenuItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
594                                                   action:@selector(unhideAllApplications:)
595                                            keyEquivalent:@""];
596   id quitMenuItem = [[NSMenuItem alloc] initWithTitle:[@"Quit " stringByAppendingString:appName]
597                                                action:@selector(terminate:)
598                                         keyEquivalent:@"q"];
599   [appMenu addItem:aboutMenuItem];
600   [appMenu addItem:[NSMenuItem separatorItem]];
601   [appMenu addItem:hideMenuItem];
602   [appMenu addItem:hideOthersMenuItem];
603   [appMenu addItem:showAllMenuItem];
604   [appMenu addItem:[NSMenuItem separatorItem]];
605   [appMenu addItem:quitMenuItem];
606   [appMenuItem setSubmenu:appMenu];
608   id fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
609   id openMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open..."
610                                                action:@selector(openDocument:)
611                                         keyEquivalent:@"o"];
612   id closeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Close"
613                                                 action:@selector(performClose:)
614                                          keyEquivalent:@"w"];
615   [fileMenu addItem:openMenuItem];
616   [fileMenu addItem:[NSMenuItem separatorItem]];
617   [fileMenu addItem:closeMenuItem];
618   [fileMenuItem setSubmenu:fileMenu];
620   id windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
621   id miniaturizeMenuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize"
622                                                    action:@selector(performMiniaturize:)
623                                             keyEquivalent:@"m"];
624   id zoomMenuItem = [[NSMenuItem alloc] initWithTitle:@"Zoom"
625                                                action:@selector(performZoom:)
626                                         keyEquivalent:@""];
628   [windowMenu addItem:miniaturizeMenuItem];
629   [windowMenu addItem:zoomMenuItem];
630   [windowMenuItem setSubmenu:windowMenu];
632   id helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
633   id reportIssueMenuItem = [[NSMenuItem alloc] initWithTitle:@"Report an issue..."
634                                                       action:@selector(reportIssue:)
635                                                keyEquivalent:@""];
636   [helpMenu addItem:reportIssueMenuItem];
637   [helpMenuItem setSubmenu:helpMenu];
639   window = [[MyWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
640                                        styleMask:(NS_CLOSABLE_WINDOW_MASK | NS_MINIATURIZABLE_WINDOW_MASK | NS_TITLED_WINDOW_MASK | NS_RESIZABLE_WINDOW_MASK)
641                                          backing:NSBackingStoreBuffered
642                                            defer:NO];
644   [window center];
645   [window setAcceptsMouseMovedEvents:YES];
646   [window setDelegate:self];
649   [[NSNotificationCenter defaultCenter] addObserver:self
650                                            selector:@selector(didEnterFullScreen)
651                                                name:NSWindowDidEnterFullScreenNotification
652                                              object:window];
653   [[NSNotificationCenter defaultCenter] addObserver:self
654                                            selector:@selector(didExitFullScreen)
655                                                name:NSWindowDidExitFullScreenNotification
656                                              object:window];
658   MyView *myView = [[MyView alloc] initWithFrame:[[window contentView] bounds]
659                                        connector:connector];
661   [window setContentView:myView];
662   [window makeFirstResponder:myView];
664   glContext = [myView openGLContext];
665   GLint swapInt = 1;
666   [glContext setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval];
668   backing_scale_factor = [window backingScaleFactor];
671 - (void)reshape:(NSValue *)val
673   // NSLog (@"reshape: %@ isFullScreen: %d", val, [window isFullScreen]);
674   if ([window isFullScreen]) {
675     [window toggleFullScreen:self];
676   }
677   [window setFrame:[window frameRectForContentRect:[val rectValue]]
678            display:YES];
681 - (void)makeCurrentContext
683   [glContext makeCurrentContext];
684   NSLog (@"OpenGL Version: %s", glGetString(GL_VERSION));
687 - (void)swapb
689   [glContext flushBuffer];
692 - (void)didEnterFullScreen
694   // NSLog (@"didEnterFullScreen: %d", [window isFullScreen]);
695   [connector notifyWinstate:YES];
698 - (void)didExitFullScreen
700   // NSLog (@"didExitFullScreen: %d", [window isFullScreen]);
701   [connector notifyWinstate:NO];
704 - (void)fullscreen
706   // NSLog (@"fullscreen: %d", [window isFullScreen]);
707   if ([window isFullScreen] == NO) {
708     [window toggleFullScreen:self];
709   }
712 - (void)setCursor:(NSCursor *)aCursor
714   [[window contentView] setCursor: aCursor];
715   [window invalidateCursorRectsForView:[window contentView]];
718 - (void)windowDidResize:(NSNotification *)notification
720   NSRect frame = [[window contentView] convertFrameToBacking];
721   [connector notifyReshapeWidth:frame.size.width height:frame.size.height];
724 - (void)applicationWillTerminate:(NSDictionary *)userInfo
726   pthread_mutex_lock (&terminate_mutex);
727   if (terminating == 0) {
728     terminating = 1;
729     [connector notifyQuit];
730   }
731   pthread_mutex_unlock (&terminate_mutex);
732   pthread_join (thread, NULL);
735 - (void)windowDidChangeOcclusionState:(NSNotification *)notification
739 - (void)applicationDidFinishLaunching:(NSNotification *)not
741   NSLog(@"applicationDidFinishLaunching");
742   int ret = pthread_create (&thread, NULL, caml_main_thread, argv);
743   if (ret != 0) {
744     Abort (@"pthread_create: %s.", strerror (ret));
745   }
748 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
750   return YES;
753 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
755   NSLog (@"openFile: %@", filename);
756   [connector openFile:filename];
757   return YES;
760 - (void)openDocument:(id)sender
762   NSOpenPanel *openPanel = [NSOpenPanel openPanel];
763   [openPanel beginSheetModalForWindow:window
764                     completionHandler:^(NSInteger result){
765       if (result == NSModalResponseOK) {
766          NSString *filename = [[[openPanel URLs] objectAtIndex:0] path];
767          if (filename != nil) {
768            [self application:NSApp openFile:filename];
769          }
770       }
771     }];
774 - (void)reportIssue:(id)sender
776   [[NSWorkspace sharedWorkspace]
777     openURL:[NSURL URLWithString:@"https://github.com/moosotc/llpp/issues"]];
780 @end
782 CAMLprim value ml_mapwin (value unit)
784   CAMLparam1 (unit);
785   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(mapwin)
786                                                    withObject:nil
787                                                 waitUntilDone:YES];
788   CAMLreturn (Val_unit);
791 CAMLprim value ml_swapb (value unit)
793   CAMLparam1 (unit);
794   [(MyDelegate *)[NSApp delegate] swapb];
795   CAMLreturn (Val_unit);
798 CAMLprim value ml_getw (value unit)
800   return Val_int([(MyDelegate *)[NSApp delegate] getw]);
803 CAMLprim value ml_geth (value unit)
805   return Val_int([(MyDelegate *)[NSApp delegate] geth]);
808 CAMLprim value ml_makecurrentcontext (value unit)
810   CAMLparam1 (unit);
811   [(MyDelegate *)[NSApp delegate] makeCurrentContext];
812   CAMLreturn (Val_unit);
815 CAMLprim value ml_settitle (value title)
817   CAMLparam1 (title);
818   NSString *str = [NSString stringWithUTF8String:String_val(title)];
819   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(setTitle:)
820                                                    withObject:str
821                                                 waitUntilDone:YES];
822   CAMLreturn (Val_unit);
825 CAMLprim value ml_reshape (value w, value h)
827   CAMLparam2 (w, h);
828   NSRect r = NSMakeRect (0, 0, Int_val (w), Int_val (h));
829   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(reshape:)
830                                                    withObject:[NSValue valueWithRect:r]
831                                                 waitUntilDone:YES];
832   CAMLreturn (Val_unit);
835 CAMLprim value ml_fullscreen (value unit)
837   CAMLparam1 (unit);
838   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(fullscreen)
839                                                    withObject:nil
840                                                 waitUntilDone:YES];
841   CAMLreturn (Val_unit);
844 CAMLprim value ml_setcursor (value curs)
846   CAMLparam1 (curs);
847   // NSLog (@"ml_setcursor: %d", Int_val (curs));
848   NSCursor *cursor = GetCursor (Int_val (curs));
849   [(MyDelegate *)[NSApp delegate] performSelectorOnMainThread:@selector(setCursor:)
850                                                    withObject:cursor
851                                                 waitUntilDone:YES];
852   CAMLreturn (Val_unit);
855 CAMLprim value ml_get_server_fd (value unit)
857   CAMLparam1 (unit);
858   CAMLreturn (Val_int (server_fd));
861 CAMLprim value ml_get_backing_scale_factor (value unit)
863   CAMLparam1 (unit);
864   CAMLreturn (Val_int ((int) backing_scale_factor));
867 CAMLprim value ml_nslog (value str)
869   CAMLparam1 (str);
870   NSLog (@"%s", String_val (str));
871   CAMLreturn (Val_unit);
874 // HACK to eliminate arg injected by OS X -psn_...
875 int adjust_argv (int argc, char **argv)
877   if (argc > 1 && strncmp (argv[1], "-psn", 4) == 0) {
878     for (unsigned i = 1; i < argc - 1; i ++) {
879       argv[i] = argv[i+1];
880     }
881     argv[-- argc] = 0;
882   }
883   for (int i = 0; i < argc; i ++) {
884     NSLog (@"arg %d: %s", i, argv[i]);
885   }
886   return argc;
889 void (*wsigladdr (const char *name)) (void)
891   static CFBundleRef framework = NULL;
892   if (framework == NULL)
893     framework = CFBundleGetBundleWithIdentifier (CFSTR ("com.apple.opengl"));
895   char *bytes;
896   CFStringRef str;
897   size_t namelenp1 = strlen (name) + 1;
898   bytes = CFAllocatorAllocate (CFAllocatorGetDefault(), namelenp1, 0);
899   memcpy (bytes, name, namelenp1);
900   str = CFStringCreateWithCStringNoCopy (NULL, bytes,
901                                          kCFStringEncodingMacRoman, NULL);
902   void (*ret) (void) = CFBundleGetFunctionPointerForName (framework, str);
903   CFRelease (str);
904   return ret;
907 int main(int argc, char **argv)
909   @autoreleasepool {
910     int sv[2];
911     int ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sv);
912     if (ret != 0) {
913       Abort (@"socketpair: %s", strerror (errno));
914     }
915     // NSLog (@"socketpair sv0 %d sv1 %d", sv[0], sv[1]);
916     server_fd = sv[0];
917     argc = adjust_argv (argc, argv);
918     [NSApplication sharedApplication];
919     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
920     id delegate = [[MyDelegate alloc] initWithArgv:argv fileDescriptor:sv[1]];
921     [NSApp setDelegate:delegate];
922     [NSApp activateIgnoringOtherApps:YES];
923     [NSApp run];
924   }
925   return EXIT_SUCCESS;