diff options
Diffstat (limited to 'vendor/gioui.org/app/os_macos.m')
-rw-r--r-- | vendor/gioui.org/app/os_macos.m | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/vendor/gioui.org/app/os_macos.m b/vendor/gioui.org/app/os_macos.m new file mode 100644 index 0000000..f65099b --- /dev/null +++ b/vendor/gioui.org/app/os_macos.m @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// +build darwin,!ios + +@import AppKit; + +#include "_cgo_export.h" + +__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void); + +@interface GioAppDelegate : NSObject<NSApplicationDelegate> +@end + +@interface GioWindowDelegate : NSObject<NSWindowDelegate> +@end + +@implementation GioWindowDelegate +- (void)windowWillMiniaturize:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onHide((__bridge CFTypeRef)window.contentView); +} +- (void)windowDidDeminiaturize:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onShow((__bridge CFTypeRef)window.contentView); +} +- (void)windowWillEnterFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onFullscreen((__bridge CFTypeRef)window.contentView); +} +- (void)windowWillExitFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onWindowed((__bridge CFTypeRef)window.contentView); +} +- (void)windowDidChangeScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; + CFTypeRef view = (__bridge CFTypeRef)window.contentView; + gio_onChangeScreen(view, dispID); +} +- (void)windowDidBecomeKey:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onFocus((__bridge CFTypeRef)window.contentView, 1); +} +- (void)windowDidResignKey:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onFocus((__bridge CFTypeRef)window.contentView, 0); +} +- (void)windowWillClose:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + window.delegate = nil; + gio_onClose((__bridge CFTypeRef)window.contentView); +} +@end + +static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { + NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; + if (!event.hasPreciseScrollingDeltas) { + // dx and dy are in rows and columns. + dx *= 10; + dy *= 10; + } + // Origin is in the lower left corner. Convert to upper left. + CGFloat height = view.bounds.size.height; + gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]); +} + +@interface GioView : NSView <CALayerDelegate> +@end + +@implementation GioView +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self setNeedsDisplay:YES]; +} +// drawRect is called when OpenGL is used, displayLayer otherwise. +// Don't know why. +- (void)drawRect:(NSRect)r { + gio_onDraw((__bridge CFTypeRef)self); +} +- (void)displayLayer:(CALayer *)layer { + layer.contentsScale = self.window.backingScaleFactor; + gio_onDraw((__bridge CFTypeRef)self); +} +- (CALayer *)makeBackingLayer { + CALayer *layer = gio_layerFactory(); + layer.delegate = self; + return layer; +} +- (void)mouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)mouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)middleMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)middletMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)rightMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)rightMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)mouseMoved:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)mouseDragged:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)scrollWheel:(NSEvent *)event { + CGFloat dx = -event.scrollingDeltaX; + CGFloat dy = -event.scrollingDeltaY; + handleMouse(self, event, MOUSE_SCROLL, dx, dy); +} +- (void)keyDown:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], true); + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; +} +- (void)keyUp:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], false); +} +- (void)insertText:(id)string { + const char *utf8 = [string UTF8String]; + gio_onText((__bridge CFTypeRef)self, (char *)utf8); +} +- (void)doCommandBySelector:(SEL)sel { + // Don't pass commands up the responder chain. + // They will end up in a beep. +} +@end + +// Delegates are weakly referenced from their peers. Nothing +// else holds a strong reference to our window delegate, so +// keep a single global reference instead. +static GioWindowDelegate *globalWindowDel; + +static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { + gio_onFrameCallback(dl); + return kCVReturnSuccess; +} + +CFTypeRef gio_createDisplayLink(void) { + CVDisplayLinkRef dl; + CVDisplayLinkCreateWithActiveCGDisplays(&dl); + CVDisplayLinkSetOutputCallback(dl, displayLinkCallback, nil); + return dl; +} + +int gio_startDisplayLink(CFTypeRef dl) { + return CVDisplayLinkStart((CVDisplayLinkRef)dl); +} + +int gio_stopDisplayLink(CFTypeRef dl) { + return CVDisplayLinkStop((CVDisplayLinkRef)dl); +} + +void gio_releaseDisplayLink(CFTypeRef dl) { + CVDisplayLinkRelease((CVDisplayLinkRef)dl); +} + +void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { + CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did); +} + +void gio_hideCursor() { + @autoreleasepool { + [NSCursor hide]; + } +} + +void gio_showCursor() { + @autoreleasepool { + [NSCursor unhide]; + } +} + +void gio_setCursor(NSUInteger curID) { + @autoreleasepool { + switch (curID) { + case 1: + [NSCursor.arrowCursor set]; + break; + case 2: + [NSCursor.IBeamCursor set]; + break; + case 3: + [NSCursor.pointingHandCursor set]; + break; + case 4: + [NSCursor.crosshairCursor set]; + break; + case 5: + [NSCursor.resizeLeftRightCursor set]; + break; + case 6: + [NSCursor.resizeUpDownCursor set]; + break; + case 7: + [NSCursor.openHandCursor set]; + break; + default: + [NSCursor.arrowCursor set]; + break; + } + } +} + +CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { + @autoreleasepool { + NSRect rect = NSMakeRect(0, 0, width, height); + NSUInteger styleMask = NSTitledWindowMask | + NSResizableWindowMask | + NSMiniaturizableWindowMask | + NSClosableWindowMask; + + NSWindow* window = [[NSWindow alloc] initWithContentRect:rect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + if (minWidth > 0 || minHeight > 0) { + window.contentMinSize = NSMakeSize(minWidth, minHeight); + } + if (maxWidth > 0 || maxHeight > 0) { + window.contentMaxSize = NSMakeSize(maxWidth, maxHeight); + } + [window setAcceptsMouseMovedEvents:YES]; + if (title != nil) { + window.title = [NSString stringWithUTF8String: title]; + } + NSView *view = (__bridge NSView *)viewRef; + [window setContentView:view]; + [window makeFirstResponder:view]; + window.releasedWhenClosed = NO; + window.delegate = globalWindowDel; + return (__bridge_retained CFTypeRef)window; + } +} + +CFTypeRef gio_createView(void) { + @autoreleasepool { + NSRect frame = NSMakeRect(0, 0, 0, 0); + GioView* view = [[GioView alloc] initWithFrame:frame]; + view.wantsLayer = YES; + view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; + return CFBridgingRetain(view); + } +} + +@implementation GioAppDelegate +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp activateIgnoringOtherApps:YES]; + gio_onFinishLaunching(); +} +- (void)applicationDidHide:(NSNotification *)aNotification { + gio_onAppHide(); +} +- (void)applicationWillUnhide:(NSNotification *)notification { + gio_onAppShow(); +} +@end + +void gio_main() { + @autoreleasepool { + [NSApplication sharedApplication]; + GioAppDelegate *del = [[GioAppDelegate alloc] init]; + [NSApp setDelegate:del]; + + NSMenuItem *mainMenu = [NSMenuItem new]; + + NSMenu *menu = [NSMenu new]; + NSMenuItem *hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide" + action:@selector(hide:) + keyEquivalent:@"h"]; + [menu addItem:hideMenuItem]; + NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit" + action:@selector(terminate:) + keyEquivalent:@"q"]; + [menu addItem:quitMenuItem]; + [mainMenu setSubmenu:menu]; + NSMenu *menuBar = [NSMenu new]; + [menuBar addItem:mainMenu]; + [NSApp setMainMenu:menuBar]; + + globalWindowDel = [[GioWindowDelegate alloc] init]; + + [NSApp run]; + } +} |