123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- // SPDX-License-Identifier: Unlicense OR MIT
- // +build darwin,!ios
- #import <AppKit/AppKit.h>
- #include "_cgo_export.h"
- __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWithTrans);
- @interface GioAppDelegate : NSObject<NSApplicationDelegate>
- @end
- @interface GioWindowDelegate : NSObject<NSWindowDelegate>
- @end
- @interface GioView : NSView <CALayerDelegate,NSTextInputClient>
- @property uintptr_t handle;
- @property BOOL presentWithTrans;
- @end
- @implementation GioWindowDelegate
- - (void)windowWillMiniaturize:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- gio_onDraw(view.handle);
- }
- - (void)windowDidDeminiaturize:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- gio_onDraw(view.handle);
- }
- - (void)windowWillEnterFullScreen:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- gio_onDraw(view.handle);
- }
- - (void)windowWillExitFullScreen:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- gio_onDraw(view.handle);
- }
- - (void)windowDidChangeScreen:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
- GioView *view = (GioView *)window.contentView;
- gio_onChangeScreen(view.handle, dispID);
- }
- - (void)windowDidBecomeKey:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- if ([window firstResponder] == view) {
- gio_onFocus(view.handle, 1);
- }
- }
- - (void)windowDidResignKey:(NSNotification *)notification {
- NSWindow *window = (NSWindow *)[notification object];
- GioView *view = (GioView *)window.contentView;
- if ([window firstResponder] == view) {
- gio_onFocus(view.handle, 0);
- }
- }
- @end
- static void handleMouse(GioView *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(view.handle, (__bridge CFTypeRef)event, typ, event.buttonNumber, p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]);
- }
- @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(self.handle);
- }
- - (void)displayLayer:(CALayer *)layer {
- layer.contentsScale = self.window.backingScaleFactor;
- gio_onDraw(self.handle);
- }
- - (CALayer *)makeBackingLayer {
- CALayer *layer = gio_layerFactory(self.presentWithTrans);
- layer.delegate = self;
- return layer;
- }
- - (void)viewDidMoveToWindow {
- gio_onAttached(self.handle, self.window != nil ? 1 : 0);
- }
- - (void)mouseDown:(NSEvent *)event {
- handleMouse(self, event, MOUSE_DOWN, 0, 0);
- }
- - (void)mouseUp:(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)otherMouseDown:(NSEvent *)event {
- handleMouse(self, event, MOUSE_DOWN, 0, 0);
- }
- - (void)otherMouseUp:(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)rightMouseDragged:(NSEvent *)event {
- handleMouse(self, event, MOUSE_MOVE, 0, 0);
- }
- - (void)otherMouseDragged:(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(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
- }
- - (void)flagsChanged:(NSEvent *)event {
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
- gio_onFlagsChanged(self.handle, [event modifierFlags]);
- }
- - (void)keyUp:(NSEvent *)event {
- NSString *keys = [event charactersIgnoringModifiers];
- gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
- }
- - (void)insertText:(id)string {
- gio_onText(self.handle, (__bridge CFTypeRef)string);
- }
- - (void)doCommandBySelector:(SEL)action {
- if (!gio_onCommandBySelector(self.handle)) {
- [super doCommandBySelector:action];
- }
- }
- - (BOOL)hasMarkedText {
- int res = gio_hasMarkedText(self.handle);
- return res ? YES : NO;
- }
- - (NSRange)markedRange {
- return gio_markedRange(self.handle);
- }
- - (NSRange)selectedRange {
- return gio_selectedRange(self.handle);
- }
- - (void)unmarkText {
- gio_unmarkText(self.handle);
- }
- - (void)setMarkedText:(id)string
- selectedRange:(NSRange)selRange
- replacementRange:(NSRange)replaceRange {
- NSString *str;
- // string is either an NSAttributedString or an NSString.
- if ([string isKindOfClass:[NSAttributedString class]]) {
- str = [string string];
- } else {
- str = string;
- }
- gio_setMarkedText(self.handle, (__bridge CFTypeRef)str, selRange, replaceRange);
- }
- - (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText {
- return nil;
- }
- - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
- actualRange:(NSRangePointer)actualRange {
- NSString *str = CFBridgingRelease(gio_substringForProposedRange(self.handle, range, actualRange));
- return [[NSAttributedString alloc] initWithString:str attributes:nil];
- }
- - (void)insertText:(id)string
- replacementRange:(NSRange)replaceRange {
- NSString *str;
- // string is either an NSAttributedString or an NSString.
- if ([string isKindOfClass:[NSAttributedString class]]) {
- str = [string string];
- } else {
- str = string;
- }
- gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange);
- }
- - (NSUInteger)characterIndexForPoint:(NSPoint)p {
- return gio_characterIndexForPoint(self.handle, p);
- }
- - (NSRect)firstRectForCharacterRange:(NSRange)rng
- actualRange:(NSRangePointer)actual {
- NSRect r = gio_firstRectForCharacterRange(self.handle, rng, actual);
- r = [self convertRect:r toView:nil];
- return [[self window] convertRectToScreen:r];
- }
- - (void)applicationWillUnhide:(NSNotification *)notification {
- gio_onDraw(self.handle);
- }
- - (void)applicationDidHide:(NSNotification *)notification {
- gio_onDraw(self.handle);
- }
- - (void)dealloc {
- gio_onDestroy(self.handle);
- }
- - (BOOL) becomeFirstResponder {
- gio_onFocus(self.handle, 1);
- return [super becomeFirstResponder];
- }
- - (BOOL) resignFirstResponder {
- gio_onFocus(self.handle, 0);
- return [super resignFirstResponder];
- }
- @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 *handle) {
- 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];
- }
- }
- // some cursors are not public, this tries to use a private cursor
- // and uses fallback when the use of private cursor fails.
- static void trySetPrivateCursor(SEL cursorName, NSCursor* fallback) {
- if ([NSCursor respondsToSelector:cursorName]) {
- id object = [NSCursor performSelector:cursorName];
- if ([object isKindOfClass:[NSCursor class]]) {
- [(NSCursor*)object set];
- return;
- }
- }
- [fallback set];
- }
- void gio_setCursor(NSUInteger curID) {
- @autoreleasepool {
- switch (curID) {
- case 0: // pointer.CursorDefault
- [NSCursor.arrowCursor set];
- break;
- // case 1: // pointer.CursorNone
- case 2: // pointer.CursorText
- [NSCursor.IBeamCursor set];
- break;
- case 3: // pointer.CursorVerticalText
- [NSCursor.IBeamCursorForVerticalLayout set];
- break;
- case 4: // pointer.CursorPointer
- [NSCursor.pointingHandCursor set];
- break;
- case 5: // pointer.CursorCrosshair
- [NSCursor.crosshairCursor set];
- break;
- case 6: // pointer.CursorAllScroll
- // For some reason, using _moveCursor fails on Monterey.
- // trySetPrivateCursor(@selector(_moveCursor), NSCursor.arrowCursor);
- [NSCursor.arrowCursor set];
- break;
- case 7: // pointer.CursorColResize
- [NSCursor.resizeLeftRightCursor set];
- break;
- case 8: // pointer.CursorRowResize
- [NSCursor.resizeUpDownCursor set];
- break;
- case 9: // pointer.CursorGrab
- [NSCursor.openHandCursor set];
- break;
- case 10: // pointer.CursorGrabbing
- [NSCursor.closedHandCursor set];
- break;
- case 11: // pointer.CursorNotAllowed
- [NSCursor.operationNotAllowedCursor set];
- break;
- case 12: // pointer.CursorWait
- trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor);
- break;
- case 13: // pointer.CursorProgress
- trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor);
- break;
- case 14: // pointer.CursorNorthWestResize
- trySetPrivateCursor(@selector(_windowResizeNorthWestCursor), NSCursor.resizeUpDownCursor);
- break;
- case 15: // pointer.CursorNorthEastResize
- trySetPrivateCursor(@selector(_windowResizeNorthEastCursor), NSCursor.resizeUpDownCursor);
- break;
- case 16: // pointer.CursorSouthWestResize
- trySetPrivateCursor(@selector(_windowResizeSouthWestCursor), NSCursor.resizeUpDownCursor);
- break;
- case 17: // pointer.CursorSouthEastResize
- trySetPrivateCursor(@selector(_windowResizeSouthEastCursor), NSCursor.resizeUpDownCursor);
- break;
- case 18: // pointer.CursorNorthSouthResize
- [NSCursor.resizeUpDownCursor set];
- break;
- case 19: // pointer.CursorEastWestResize
- [NSCursor.resizeLeftRightCursor set];
- break;
- case 20: // pointer.CursorWestResize
- [NSCursor.resizeLeftCursor set];
- break;
- case 21: // pointer.CursorEastResize
- [NSCursor.resizeRightCursor set];
- break;
- case 22: // pointer.CursorNorthResize
- [NSCursor.resizeUpCursor set];
- break;
- case 23: // pointer.CursorSouthResize
- [NSCursor.resizeDownCursor set];
- break;
- case 24: // pointer.CursorNorthEastSouthWestResize
- trySetPrivateCursor(@selector(_windowResizeNorthEastSouthWestCursor), NSCursor.resizeUpDownCursor);
- break;
- case 25: // pointer.CursorNorthWestSouthEastResize
- trySetPrivateCursor(@selector(_windowResizeNorthWestSouthEastCursor), NSCursor.resizeUpDownCursor);
- break;
- default:
- [NSCursor.arrowCursor set];
- break;
- }
- }
- }
- CFTypeRef gio_createWindow(CFTypeRef viewRef, 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];
- NSView *view = (__bridge NSView *)viewRef;
- [window setContentView:view];
- window.delegate = globalWindowDel;
- return (__bridge_retained CFTypeRef)window;
- }
- }
- CFTypeRef gio_createView(int presentWithTrans) {
- @autoreleasepool {
- NSRect frame = NSMakeRect(0, 0, 0, 0);
- GioView* view = [[GioView alloc] initWithFrame:frame];
- view.presentWithTrans = presentWithTrans ? YES : NO;
- view.wantsLayer = YES;
- view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
- [[NSNotificationCenter defaultCenter] addObserver:view
- selector:@selector(applicationWillUnhide:)
- name:NSApplicationWillUnhideNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:view
- selector:@selector(applicationDidHide:)
- name:NSApplicationDidHideNotification
- object:nil];
- return CFBridgingRetain(view);
- }
- }
- void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
- @autoreleasepool {
- GioView *v = (__bridge GioView *)viewRef;
- v.handle = handle;
- }
- }
- @implementation GioAppDelegate
- - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
- [NSApp activateIgnoringOtherApps:YES];
- gio_onFinishLaunching();
- }
- @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];
- }
- }
|