-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwiAppleHelper.mm
More file actions
333 lines (295 loc) · 9.89 KB
/
Copy pathwiAppleHelper.mm
File metadata and controls
333 lines (295 loc) · 9.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#include "wiAppleHelper.h"
#include "wiHelper.h"
#include "wiInput.h"
#include <AppKit/AppKit.h>
#include <CoreGraphics/CoreGraphics.h>
#include <mach-o/dyld.h>
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
namespace wi::apple
{
void SetMetalLayerToWindow(void* _window, void* _layer)
{
NSWindow* window = (__bridge NSWindow*)_window;
if (!window)
return;
CAMetalLayer* layer = (__bridge CAMetalLayer*)_layer;
if (!layer)
return;
window.contentView.wantsLayer = true;
window.contentView.layer = layer;
}
void* GetViewFromWindow(void* handle)
{
NSWindow* window = (__bridge NSWindow*)handle;
if (!window)
return nullptr;
NSView* contentView = window.contentView;
return (__bridge void*)contentView;
}
XMUINT2 GetWindowSizeNoScaling(void* handle)
{
if (!handle)
return XMUINT2(0, 0);
NSWindow* window = (__bridge NSWindow*)handle;
NSView* contentView = window.contentView;
if (!contentView)
return XMUINT2(0, 0);
CGRect bounds = contentView.bounds;
uint32_t pixelWidth = (uint32_t)round(bounds.size.width);
uint32_t pixelHeight = (uint32_t)round(bounds.size.height);
return XMUINT2(pixelWidth, pixelHeight);
}
XMUINT2 GetWindowSize(void* handle)
{
if (!handle)
return XMUINT2(0, 0);
NSWindow* window = (__bridge NSWindow*)handle;
NSView* contentView = window.contentView;
if (!contentView)
return XMUINT2(0, 0);
CGRect bounds = contentView.bounds;
CGFloat scale = window.backingScaleFactor; // 1.0 on non-Retina, 2.0 on Retina
uint32_t pixelWidth = (uint32_t)round(bounds.size.width * scale);
uint32_t pixelHeight = (uint32_t)round(bounds.size.height * scale);
return XMUINT2(pixelWidth, pixelHeight);
}
float GetDPIForWindow(void* handle)
{
NSWindow* window = (__bridge NSWindow*)handle;
if (!window || !window.screen)
return 96.0f;
CGFloat scale = window.screen.backingScaleFactor;
return scale * 96.0f;
}
XMFLOAT2 GetMousePositionInWindow(void* handle)
{
NSWindow* window = (__bridge NSWindow*)handle;
NSView* view = (__bridge NSView*)GetViewFromWindow(handle);
if (!window || !view)
return XMFLOAT2(-1.f, -1.f);
NSPoint mouseLocScreen = [NSEvent mouseLocation];
NSPoint mouseInWindow = [window convertRectFromScreen:NSMakeRect(mouseLocScreen.x, mouseLocScreen.y, 0, 0)].origin;
NSPoint mouseInView = [view convertPoint:mouseInWindow fromView:nil];
CGFloat heightPoints = view.bounds.size.height;
mouseInView.y = heightPoints - mouseInView.y;
CGFloat scale = view.window.backingScaleFactor;
return XMFLOAT2((float)(mouseInView.x * scale), (float)(mouseInView.y * scale));
}
void SetMousePositionInWindow(void* handle, XMFLOAT2 value)
{
NSWindow* window = (__bridge NSWindow*)handle;
if (!window)
return;
NSView* view = window.contentView;
if (!view)
return;
CGFloat scale = view.window.backingScaleFactor;
CGFloat pixelX = value.x;
CGFloat pixelY = value.y;
CGFloat pointX = pixelX / scale;
CGFloat pointY = pixelY / scale;
CGFloat heightPoints = view.bounds.size.height;
CGFloat flippedY = heightPoints - pointY;
NSPoint localPointInView = NSMakePoint(pointX, flippedY);
NSPoint pointInWindow = [view convertPoint:localPointInView toView:nil];
NSPoint pointOnScreen = [window convertPointToScreen:pointInWindow];
NSScreen* screen = window.screen;
CGFloat screenHeight = screen.frame.size.height;
pointOnScreen.y = screenHeight - pointOnScreen.y;
CGWarpMouseCursorPosition(pointOnScreen);
}
int MessageBox(const char* title, const char* message, const char* buttons)
{
@autoreleasepool {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@(title)];
[alert setInformativeText:@(message)];
[alert setAlertStyle:NSAlertStyleInformational];
if (buttons == nullptr)
{
[alert addButtonWithTitle:@"OK"];
[alert runModal];
}
else if (strcmp(buttons, "YesNo") == 0)
{
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"No"];
NSInteger button = [alert runModal];
switch (button) {
case NSAlertFirstButtonReturn: return (int)wi::helper::MessageBoxResult::Yes;
case NSAlertSecondButtonReturn: return (int)wi::helper::MessageBoxResult::No;
default: return (int)wi::helper::MessageBoxResult::Cancel;
}
}
else if (strcmp(buttons, "YesNoCancel") == 0)
{
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"No"];
[alert addButtonWithTitle:@"Cancel"];
NSInteger button = [alert runModal];
switch (button) {
case NSAlertFirstButtonReturn: return (int)wi::helper::MessageBoxResult::Yes;
case NSAlertSecondButtonReturn: return (int)wi::helper::MessageBoxResult::No;
default: return (int)wi::helper::MessageBoxResult::Cancel;
}
}
else if (strcmp(buttons, "OKCancel") == 0)
{
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
NSInteger button = [alert runModal];
switch (button) {
case NSAlertFirstButtonReturn: return (int)wi::helper::MessageBoxResult::OK;
default: return (int)wi::helper::MessageBoxResult::Cancel;
}
}
else if (strcmp(buttons, "AbortRetryIgnore") == 0)
{
[alert addButtonWithTitle:@"Abort"];
[alert addButtonWithTitle:@"Retry"];
[alert addButtonWithTitle:@"Ignore"];
NSInteger button = [alert runModal];
switch (button) {
case NSAlertFirstButtonReturn: return (int)wi::helper::MessageBoxResult::Abort;
case NSAlertSecondButtonReturn: return (int)wi::helper::MessageBoxResult::Retry;
default: return (int)wi::helper::MessageBoxResult::Ignore;
}
}
}
return (int)wi::helper::MessageBoxResult::OK;
}
std::string GetExecutablePath()
{
char rawPath[PATH_MAX];
uint32_t rawLength = sizeof(rawPath);
if (_NSGetExecutablePath(rawPath, &rawLength) != 0)
return {};
char* resolved = realpath(rawPath, nullptr);
if (!resolved)
return {};
std::string path(resolved);
free(resolved);
return path;
}
void CursorInit(void** cursor_table)
{
cursor_table[wi::input::CURSOR_DEFAULT] = (__bridge void*)[NSCursor arrowCursor];
cursor_table[wi::input::CURSOR_TEXTINPUT] = (__bridge void*)[NSCursor IBeamCursor];
cursor_table[wi::input::CURSOR_RESIZEALL] = (__bridge void*)[NSCursor closedHandCursor];
cursor_table[wi::input::CURSOR_RESIZE_NS] = (__bridge void*)[NSCursor resizeUpDownCursor];
cursor_table[wi::input::CURSOR_RESIZE_EW] = (__bridge void*)[NSCursor resizeLeftRightCursor];
cursor_table[wi::input::CURSOR_RESIZE_NESW] = (__bridge void*)[NSCursor closedHandCursor];
cursor_table[wi::input::CURSOR_RESIZE_NWSE] = (__bridge void*)[NSCursor closedHandCursor];
cursor_table[wi::input::CURSOR_HAND] = (__bridge void*)[NSCursor pointingHandCursor];
cursor_table[wi::input::CURSOR_NOTALLOWED] = (__bridge void*)[NSCursor operationNotAllowedCursor];
}
void CursorSet(void* cursor)
{
NSCursor* _cursor = (__bridge NSCursor*)cursor;
[_cursor set];
}
void CursorHide(bool hide)
{
static bool hidden = false;
if (hide)
{
if (!hidden)
{
hidden = true;
[NSCursor hide];
}
}
else
{
hidden = false;
[NSCursor unhide];
}
}
void SetWindowFullScreen(void* handle, bool fullscreen)
{
NSWindow* window = (__bridge NSWindow*)handle;
if (window == nil)
return;
bool isCurrentlyFullscreen = (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
if (isCurrentlyFullscreen == fullscreen)
return;
if (fullscreen)
{
NSWindowCollectionBehavior currentBehavior = window.collectionBehavior;
if ((currentBehavior & NSWindowCollectionBehaviorFullScreenPrimary) == 0 &&
(currentBehavior & NSWindowCollectionBehaviorFullScreenAuxiliary) == 0) {
window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
}
}
[window toggleFullScreen:nil];
}
bool IsWindowFullScreen(void* handle)
{
NSWindow* window = (__bridge NSWindow*)handle;
if (window == nil)
return false;
return (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
}
void OpenUrl(const char* url)
{
@autoreleasepool
{
NSString* nsurl = [NSString stringWithUTF8String:url];
NSURL* nsURL = [NSURL URLWithString:nsurl];
if (nsURL)
{
[[NSWorkspace sharedWorkspace] openURL:nsURL];
}
}
}
std::string GetClipboardText()
{
std::string ret;
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]];
if (![available isEqualToString:NSPasteboardTypeString])
return ret;
NSString* string = [pasteboard stringForType:NSPasteboardTypeString];
if (string == nil)
return ret;
const char* string_c = (const char*)[string UTF8String];
size_t string_len = strlen(string_c);
ret.resize((int)string_len);
strcpy(ret.data(), string_c);
return ret;
}
void SetClipboardText(const char* str)
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
[pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString];
}
void* CreateCursorFromARGB8ImageData(const void* data, uint32_t width, uint32_t height, int hotspotX, int hotspotY)
{
const size_t bytesPerRow = width * sizeof(uint32_t);
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes: nullptr
pixelsWide: width
pixelsHigh: height
bitsPerSample: 8
samplesPerPixel: 4
hasAlpha: YES
isPlanar: NO
colorSpaceName: NSDeviceRGBColorSpace
bitmapFormat: NSBitmapFormatAlphaNonpremultiplied | NSBitmapFormatAlphaFirst
bytesPerRow: bytesPerRow
bitsPerPixel: 32];
uint8_t* dst = [bitmap bitmapData];
size_t bytesToCopy = size_t(height) * bytesPerRow;
std::memcpy(dst, data, bytesToCopy);
NSImage* image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
[image addRepresentation:bitmap];
NSCursor* cursor = [[NSCursor alloc]
initWithImage:image
hotSpot:NSMakePoint(hotspotX, hotspotY)];
static wi::vector<NSCursor*> cursors; // Lifetime extender, a proper solution would require engine to know about autorelease pools lifetimes and manage cursor lifetime with that
cursors.push_back(cursor);
return (__bridge void*)cursor;
}
}