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