os_macos.m raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  // +build darwin,!ios
   4  
   5  @import AppKit;
   6  
   7  #include "_cgo_export.h"
   8  
   9  @interface GioAppDelegate : NSObject<NSApplicationDelegate>
  10  @end
  11  
  12  @interface GioWindowDelegate : NSObject<NSWindowDelegate>
  13  @end
  14  
  15  @implementation GioWindowDelegate
  16  - (void)windowWillMiniaturize:(NSNotification *)notification {
  17  	NSWindow *window = (NSWindow *)[notification object];
  18  	gio_onHide((__bridge CFTypeRef)window.contentView);
  19  }
  20  - (void)windowDidDeminiaturize:(NSNotification *)notification {
  21  	NSWindow *window = (NSWindow *)[notification object];
  22  	gio_onShow((__bridge CFTypeRef)window.contentView);
  23  }
  24  - (void)windowDidChangeScreen:(NSNotification *)notification {
  25  	NSWindow *window = (NSWindow *)[notification object];
  26  	CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
  27  	CFTypeRef view = (__bridge CFTypeRef)window.contentView;
  28  	gio_onChangeScreen(view, dispID);
  29  }
  30  - (void)windowDidBecomeKey:(NSNotification *)notification {
  31  	NSWindow *window = (NSWindow *)[notification object];
  32  	gio_onFocus((__bridge CFTypeRef)window.contentView, 1);
  33  }
  34  - (void)windowDidResignKey:(NSNotification *)notification {
  35  	NSWindow *window = (NSWindow *)[notification object];
  36  	gio_onFocus((__bridge CFTypeRef)window.contentView, 0);
  37  }
  38  - (void)windowWillClose:(NSNotification *)notification {
  39  	NSWindow *window = (NSWindow *)[notification object];
  40  	window.delegate = nil;
  41  	gio_onClose((__bridge CFTypeRef)window.contentView);
  42  }
  43  @end
  44  
  45  // Delegates are weakly referenced from their peers. Nothing
  46  // else holds a strong reference to our window delegate, so
  47  // keep a single global reference instead.
  48  static GioWindowDelegate *globalWindowDel;
  49  
  50  void gio_writeClipboard(unichar *chars, NSUInteger length) {
  51  	@autoreleasepool {
  52  		NSString *s = [NSString string];
  53  		if (length > 0) {
  54  			s = [NSString stringWithCharacters:chars length:length];
  55  		}
  56  		NSPasteboard *p = NSPasteboard.generalPasteboard;
  57  		[p declareTypes:@[NSPasteboardTypeString] owner:nil];
  58  		[p setString:s forType:NSPasteboardTypeString];
  59  	}
  60  }
  61  
  62  CFTypeRef gio_readClipboard(void) {
  63  	@autoreleasepool {
  64  		NSPasteboard *p = NSPasteboard.generalPasteboard;
  65  		NSString *content = [p stringForType:NSPasteboardTypeString];
  66  		return (__bridge_retained CFTypeRef)content;
  67  	}
  68  }
  69  
  70  CGFloat gio_viewHeight(CFTypeRef viewRef) {
  71  	NSView *view = (__bridge NSView *)viewRef;
  72  	return [view bounds].size.height;
  73  }
  74  
  75  CGFloat gio_viewWidth(CFTypeRef viewRef) {
  76  	NSView *view = (__bridge NSView *)viewRef;
  77  	return [view bounds].size.width;
  78  }
  79  
  80  CGFloat gio_getScreenBackingScale(void) {
  81  	return [NSScreen.mainScreen backingScaleFactor];
  82  }
  83  
  84  CGFloat gio_getViewBackingScale(CFTypeRef viewRef) {
  85  	NSView *view = (__bridge NSView *)viewRef;
  86  	return [view.window backingScaleFactor];
  87  }
  88  
  89  void gio_hideCursor() {
  90  	@autoreleasepool {
  91  		[NSCursor hide];
  92  	}
  93  }
  94  
  95  void gio_showCursor() {
  96  	@autoreleasepool {
  97  		[NSCursor unhide];
  98  	}
  99  }
 100  
 101  void gio_setCursor(NSUInteger curID) {
 102  	@autoreleasepool {
 103  		switch (curID) {
 104  			case 1:
 105  				[NSCursor.arrowCursor set];
 106  				break;
 107  			case 2:
 108  				[NSCursor.IBeamCursor set];
 109  				break;
 110  			case 3:
 111  				[NSCursor.pointingHandCursor set];
 112  				break;
 113  			case 4:
 114  				[NSCursor.crosshairCursor set];
 115  				break;
 116  			case 5:
 117  				[NSCursor.resizeLeftRightCursor set];
 118  				break;
 119  			case 6:
 120  				[NSCursor.resizeUpDownCursor set];
 121  				break;
 122  			case 7:
 123                  [NSCursor.openHandCursor set];
 124                  break;
 125  			default:
 126  				[NSCursor.arrowCursor set];
 127  				break;
 128  		}
 129  	}
 130  }
 131  
 132  static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
 133  	gio_onFrameCallback(dl);
 134  	return kCVReturnSuccess;
 135  }
 136  
 137  CFTypeRef gio_createDisplayLink(void) {
 138  	CVDisplayLinkRef dl;
 139  	CVDisplayLinkCreateWithActiveCGDisplays(&dl);
 140  	CVDisplayLinkSetOutputCallback(dl, displayLinkCallback, nil);
 141  	return dl;
 142  }
 143  
 144  int gio_startDisplayLink(CFTypeRef dl) {
 145  	return CVDisplayLinkStart((CVDisplayLinkRef)dl);
 146  }
 147  
 148  int gio_stopDisplayLink(CFTypeRef dl) {
 149  	return CVDisplayLinkStop((CVDisplayLinkRef)dl);
 150  }
 151  
 152  void gio_releaseDisplayLink(CFTypeRef dl) {
 153  	CVDisplayLinkRelease((CVDisplayLinkRef)dl);
 154  }
 155  
 156  void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
 157  	CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did);
 158  }
 159  
 160  NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) {
 161  	NSWindow *window = (__bridge NSWindow *)windowRef;
 162  	return [window cascadeTopLeftFromPoint:topLeft];
 163  }
 164  
 165  void gio_makeKeyAndOrderFront(CFTypeRef windowRef) {
 166  	NSWindow *window = (__bridge NSWindow *)windowRef;
 167  	[window makeKeyAndOrderFront:nil];
 168  }
 169  
 170  void gio_toggleFullScreen(CFTypeRef windowRef) {
 171  	NSWindow *window = (__bridge NSWindow *)windowRef;
 172  	[window toggleFullScreen:nil];
 173  }
 174  
 175  CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) {
 176  	@autoreleasepool {
 177  		NSRect rect = NSMakeRect(0, 0, width, height);
 178  		NSUInteger styleMask = NSTitledWindowMask |
 179  			NSResizableWindowMask |
 180  			NSMiniaturizableWindowMask |
 181  			NSClosableWindowMask;
 182  
 183  		NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
 184  													   styleMask:styleMask
 185  														 backing:NSBackingStoreBuffered
 186  														   defer:NO];
 187  		if (minWidth > 0 || minHeight > 0) {
 188  			window.contentMinSize = NSMakeSize(minWidth, minHeight);
 189  		}
 190  		if (maxWidth > 0 || maxHeight > 0) {
 191  			window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
 192  		}
 193  		[window setAcceptsMouseMovedEvents:YES];
 194  		if (title != nil) {
 195  		    window.title = [NSString stringWithUTF8String: title];
 196  		}
 197  		NSView *view = (__bridge NSView *)viewRef;
 198  		[window setContentView:view];
 199  		[window makeFirstResponder:view];
 200  		window.releasedWhenClosed = NO;
 201  		window.delegate = globalWindowDel;
 202  		return (__bridge_retained CFTypeRef)window;
 203  	}
 204  }
 205  
 206  void gio_close(CFTypeRef windowRef) {
 207    NSWindow* window = (__bridge NSWindow *)windowRef;
 208    [window performClose:nil];
 209  }
 210  
 211  void gio_setSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
 212    NSWindow* window = (__bridge NSWindow *)windowRef;
 213    NSSize size = NSMakeSize(width, height);
 214    [window setContentSize:size];
 215  }
 216  
 217  void gio_setMinSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
 218    NSWindow* window = (__bridge NSWindow *)windowRef;
 219    window.contentMinSize = NSMakeSize(width, height);
 220  }
 221  
 222  void gio_setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
 223    NSWindow* window = (__bridge NSWindow *)windowRef;
 224    window.contentMaxSize = NSMakeSize(width, height);
 225  }
 226  
 227  void gio_setTitle(CFTypeRef windowRef, const char *title) {
 228    NSWindow* window = (__bridge NSWindow *)windowRef;
 229    window.title = [NSString stringWithUTF8String: title];
 230  }
 231  
 232  @implementation GioAppDelegate
 233  - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
 234  	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
 235  	gio_onFinishLaunching();
 236  }
 237  - (void)applicationDidHide:(NSNotification *)aNotification {
 238  	gio_onAppHide();
 239  }
 240  - (void)applicationWillUnhide:(NSNotification *)notification {
 241  	gio_onAppShow();
 242  }
 243  @end
 244  
 245  void gio_main() {
 246  	@autoreleasepool {
 247  		[NSApplication sharedApplication];
 248  		GioAppDelegate *del = [[GioAppDelegate alloc] init];
 249  		[NSApp setDelegate:del];
 250  		[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
 251  
 252  		NSMenuItem *mainMenu = [NSMenuItem new];
 253  
 254  		NSMenu *menu = [NSMenu new];
 255  		NSMenuItem *hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide"
 256  															  action:@selector(hide:)
 257  													   keyEquivalent:@"h"];
 258  		[menu addItem:hideMenuItem];
 259  		NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
 260  															  action:@selector(terminate:)
 261  													   keyEquivalent:@"q"];
 262  		[menu addItem:quitMenuItem];
 263  		[mainMenu setSubmenu:menu];
 264  		NSMenu *menuBar = [NSMenu new];
 265  		[menuBar addItem:mainMenu];
 266  		[NSApp setMainMenu:menuBar];
 267  
 268  		globalWindowDel = [[GioWindowDelegate alloc] init];
 269  
 270  		[NSApp run];
 271  	}
 272  }
 273