2022-06-25 12:19:59 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
|
|
|
|
#include <CoreFoundation/CFBundle.h>
|
|
|
|
#include <ApplicationServices/ApplicationServices.h>
|
2022-06-30 15:28:51 +02:00
|
|
|
#include <Foundation/NSUserDefaults.h>
|
2023-02-08 13:51:56 +01:00
|
|
|
#include <AppKit/NSScreen.h>
|
2024-02-24 22:46:52 +01:00
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#include <CoreText/CoreText.h>
|
2022-06-25 12:19:59 +02:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
2023-12-30 23:52:25 +01:00
|
|
|
|
|
|
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <GLFW/glfw3native.h>
|
|
|
|
|
2023-05-14 21:50:58 +02:00
|
|
|
#import <Cocoa/Cocoa.h>
|
2023-05-14 20:20:22 +02:00
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
|
2025-01-04 15:35:06 +01:00
|
|
|
#include <hex/helpers/keys.hpp>
|
|
|
|
|
2023-05-27 17:45:41 +02:00
|
|
|
void errorMessageMacos(const char *cMessage) {
|
|
|
|
CFStringRef strMessage = CFStringCreateWithCString(NULL, cMessage, kCFStringEncodingUTF8);
|
|
|
|
CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, strMessage, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2023-05-14 22:03:04 +02:00
|
|
|
void openFile(const char *path);
|
2024-02-24 22:46:52 +01:00
|
|
|
void registerFont(const char *fontName, const char *fontPath);
|
2023-05-14 20:20:22 +02:00
|
|
|
|
2023-05-14 22:03:04 +02:00
|
|
|
void openWebpageMacos(const char *url) {
|
2022-06-25 12:19:59 +02:00
|
|
|
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
|
|
|
|
LSOpenCFURLRef(urlRef, NULL);
|
|
|
|
CFRelease(urlRef);
|
|
|
|
}
|
|
|
|
|
2023-05-14 22:03:04 +02:00
|
|
|
bool isMacosSystemDarkModeEnabled(void) {
|
2022-06-30 15:09:57 +02:00
|
|
|
NSString * appleInterfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
|
|
|
|
|
|
|
|
if (appleInterfaceStyle && [appleInterfaceStyle length] > 0) {
|
|
|
|
return [[appleInterfaceStyle lowercaseString] containsString:@"dark"];
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-14 22:03:04 +02:00
|
|
|
float getBackingScaleFactor(void) {
|
2023-02-08 13:51:56 +01:00
|
|
|
return [[NSScreen mainScreen] backingScaleFactor];
|
|
|
|
}
|
|
|
|
|
2024-02-11 14:12:14 +01:00
|
|
|
void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) {
|
2023-12-30 23:52:25 +01:00
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
2024-02-03 22:39:31 +01:00
|
|
|
|
2023-12-30 23:52:25 +01:00
|
|
|
cocoaWindow.titleVisibility = NSWindowTitleHidden;
|
2024-01-30 11:21:34 +01:00
|
|
|
|
2024-02-11 14:12:14 +01:00
|
|
|
if (borderlessWindowMode) {
|
|
|
|
cocoaWindow.titlebarAppearsTransparent = YES;
|
|
|
|
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
|
|
|
|
|
|
|
|
[cocoaWindow setOpaque:NO];
|
|
|
|
[cocoaWindow setHasShadow:YES];
|
|
|
|
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
|
|
|
|
}
|
2024-02-03 22:39:31 +01:00
|
|
|
}
|
2024-01-30 11:21:34 +01:00
|
|
|
|
2024-02-03 22:39:31 +01:00
|
|
|
bool isMacosFullScreenModeEnabled(GLFWwindow *window) {
|
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
|
|
|
return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
|
2023-12-30 23:52:25 +01:00
|
|
|
}
|
|
|
|
|
2024-02-24 22:46:52 +01:00
|
|
|
void enumerateFontsMacos(void) {
|
|
|
|
CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames();
|
|
|
|
CFIndex count = CFArrayGetCount(fontDescriptors);
|
|
|
|
|
|
|
|
for (CFIndex i = 0; i < count; i++) {
|
|
|
|
CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i);
|
|
|
|
|
|
|
|
// Get font path
|
|
|
|
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontNameAttribute : (__bridge NSString *)fontName };
|
|
|
|
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes);
|
|
|
|
CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
|
|
|
|
CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);
|
|
|
|
|
|
|
|
registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]);
|
|
|
|
|
|
|
|
CFRelease(descriptor);
|
|
|
|
CFRelease(fontURL);
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease(fontDescriptors);
|
|
|
|
}
|
|
|
|
|
2024-05-20 11:27:57 +02:00
|
|
|
void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window) {
|
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
|
|
|
|
|
|
|
// Consult user preferences: "System Settings -> Desktop & Dock -> Double-click a window's title bar to"
|
|
|
|
NSString* action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"];
|
|
|
|
|
|
|
|
if (action == nil || [action isEqualToString:@"None"]) {
|
|
|
|
// Nothing to do
|
|
|
|
} else if ([action isEqualToString:@"Minimize"]) {
|
|
|
|
if ([cocoaWindow isMiniaturizable]) {
|
|
|
|
[cocoaWindow miniaturize:nil];
|
|
|
|
}
|
|
|
|
} else if ([action isEqualToString:@"Maximize"]) {
|
|
|
|
// `[NSWindow zoom:_ sender]` takes over pumping the main runloop for the duration of the resize,
|
|
|
|
// and would interfere with our renderer's frame logic. Schedule it for the next frame
|
|
|
|
|
|
|
|
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
|
|
|
if ([cocoaWindow isZoomable]) {
|
|
|
|
[cocoaWindow zoom:nil];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-01 16:45:11 +01:00
|
|
|
void macosSetWindowMovable(GLFWwindow *window, bool movable) {
|
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
|
|
|
|
|
|
|
[cocoaWindow setMovable:movable];
|
|
|
|
}
|
|
|
|
|
2024-05-20 11:27:57 +02:00
|
|
|
bool macosIsWindowBeingResizedByUser(GLFWwindow *window) {
|
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
|
|
|
|
|
|
|
return cocoaWindow.inLiveResize;
|
|
|
|
}
|
|
|
|
|
2024-06-12 19:51:12 +02:00
|
|
|
void macosMarkContentEdited(GLFWwindow *window, bool edited) {
|
|
|
|
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
|
|
|
|
|
|
|
[cocoaWindow setDocumentEdited:edited];
|
|
|
|
}
|
|
|
|
|
2023-05-14 18:35:35 +02:00
|
|
|
@interface HexDocument : NSDocument
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation HexDocument
|
|
|
|
|
2023-05-14 20:20:22 +02:00
|
|
|
- (BOOL) readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError {
|
2023-05-14 22:03:04 +02:00
|
|
|
NSString* urlString = [url absoluteString];
|
|
|
|
const char* utf8String = [urlString UTF8String];
|
|
|
|
|
2024-02-23 20:17:33 +01:00
|
|
|
const char *prefix = "file://";
|
|
|
|
if (strncmp(utf8String, prefix, strlen(prefix)) == 0)
|
|
|
|
utf8String += strlen(prefix);
|
|
|
|
|
2023-05-14 22:03:04 +02:00
|
|
|
openFile(utf8String);
|
2023-05-14 20:20:22 +02:00
|
|
|
|
2023-05-14 18:35:35 +02:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2025-01-04 15:35:06 +01:00
|
|
|
void macosGetKey(enum Keys key, int *output) {
|
|
|
|
*output = 0x00;
|
|
|
|
switch (key) {
|
|
|
|
case Space: *output = ' '; break;
|
|
|
|
case Apostrophe: *output = '\''; break;
|
|
|
|
case Comma: *output = ','; break;
|
|
|
|
case Minus: *output = '-'; break;
|
|
|
|
case Period: *output = '.'; break;
|
|
|
|
case Slash: *output = '/'; break;
|
|
|
|
case Num0: *output = '0'; break;
|
|
|
|
case Num1: *output = '1'; break;
|
|
|
|
case Num2: *output = '2'; break;
|
|
|
|
case Num3: *output = '3'; break;
|
|
|
|
case Num4: *output = '4'; break;
|
|
|
|
case Num5: *output = '5'; break;
|
|
|
|
case Num6: *output = '6'; break;
|
|
|
|
case Num7: *output = '7'; break;
|
|
|
|
case Num8: *output = '8'; break;
|
|
|
|
case Num9: *output = '9'; break;
|
|
|
|
case Semicolon: *output = ';'; break;
|
|
|
|
case Equals: *output = '='; break;
|
|
|
|
case A: *output = 'a'; break;
|
|
|
|
case B: *output = 'b'; break;
|
|
|
|
case C: *output = 'c'; break;
|
|
|
|
case D: *output = 'd'; break;
|
|
|
|
case E: *output = 'e'; break;
|
|
|
|
case F: *output = 'f'; break;
|
|
|
|
case G: *output = 'g'; break;
|
|
|
|
case H: *output = 'h'; break;
|
|
|
|
case I: *output = 'i'; break;
|
|
|
|
case J: *output = 'j'; break;
|
|
|
|
case K: *output = 'k'; break;
|
|
|
|
case L: *output = 'l'; break;
|
|
|
|
case M: *output = 'm'; break;
|
|
|
|
case N: *output = 'n'; break;
|
|
|
|
case O: *output = 'o'; break;
|
|
|
|
case P: *output = 'p'; break;
|
|
|
|
case Q: *output = 'q'; break;
|
|
|
|
case R: *output = 'r'; break;
|
|
|
|
case S: *output = 's'; break;
|
|
|
|
case T: *output = 't'; break;
|
|
|
|
case U: *output = 'u'; break;
|
|
|
|
case V: *output = 'v'; break;
|
|
|
|
case W: *output = 'w'; break;
|
|
|
|
case X: *output = 'x'; break;
|
|
|
|
case Y: *output = 'y'; break;
|
|
|
|
case Z: *output = 'z'; break;
|
|
|
|
case LeftBracket: *output = '/'; break;
|
|
|
|
case Backslash: *output = '\\'; break;
|
|
|
|
case RightBracket: *output = ']'; break;
|
|
|
|
case GraveAccent: *output = '`'; break;
|
|
|
|
case World1: break;
|
|
|
|
case World2: break;
|
|
|
|
case Escape: break;
|
|
|
|
case Enter: *output = NSEnterCharacter; break;
|
|
|
|
case Tab: *output = NSTabCharacter; break;
|
|
|
|
case Backspace: *output = NSBackspaceCharacter; break;
|
|
|
|
case Insert: *output = NSInsertFunctionKey; break;
|
|
|
|
case Delete: *output = NSDeleteCharacter; break;
|
|
|
|
case Right: *output = NSRightArrowFunctionKey; break;
|
|
|
|
case Left: *output = NSLeftArrowFunctionKey; break;
|
|
|
|
case Down: *output = NSDownArrowFunctionKey; break;
|
|
|
|
case Up: *output = NSUpArrowFunctionKey; break;
|
|
|
|
case PageUp: *output = NSPageUpFunctionKey; break;
|
|
|
|
case PageDown: *output = NSPageDownFunctionKey; break;
|
|
|
|
case Home: *output = NSHomeFunctionKey; break;
|
|
|
|
case End: *output = NSEndFunctionKey; break;
|
|
|
|
case CapsLock: break;
|
|
|
|
case ScrollLock: *output = NSScrollLockFunctionKey; break;
|
|
|
|
case NumLock: break;
|
|
|
|
case PrintScreen: *output = NSPrintScreenFunctionKey; break;
|
|
|
|
case Pause: *output = NSPauseFunctionKey; break;
|
|
|
|
case F1: *output = NSF1FunctionKey; break;
|
|
|
|
case F2: *output = NSF2FunctionKey; break;
|
|
|
|
case F3: *output = NSF3FunctionKey; break;
|
|
|
|
case F4: *output = NSF4FunctionKey; break;
|
|
|
|
case F5: *output = NSF5FunctionKey; break;
|
|
|
|
case F6: *output = NSF6FunctionKey; break;
|
|
|
|
case F7: *output = NSF7FunctionKey; break;
|
|
|
|
case F8: *output = NSF8FunctionKey; break;
|
|
|
|
case F9: *output = NSF9FunctionKey; break;
|
|
|
|
case F10: *output = NSF10FunctionKey; break;
|
|
|
|
case F11: *output = NSF11FunctionKey; break;
|
|
|
|
case F12: *output = NSF12FunctionKey; break;
|
|
|
|
case F13: *output = NSF13FunctionKey; break;
|
|
|
|
case F14: *output = NSF14FunctionKey; break;
|
|
|
|
case F15: *output = NSF15FunctionKey; break;
|
|
|
|
case F16: *output = NSF16FunctionKey; break;
|
|
|
|
case F17: *output = NSF17FunctionKey; break;
|
|
|
|
case F18: *output = NSF18FunctionKey; break;
|
|
|
|
case F19: *output = NSF19FunctionKey; break;
|
|
|
|
case F20: *output = NSF20FunctionKey; break;
|
|
|
|
case F21: *output = NSF21FunctionKey; break;
|
|
|
|
case F22: *output = NSF22FunctionKey; break;
|
|
|
|
case F23: *output = NSF23FunctionKey; break;
|
|
|
|
case F24: *output = NSF24FunctionKey; break;
|
|
|
|
case F25: *output = NSF25FunctionKey; break;
|
|
|
|
case KeyPad0: *output = '0'; break;
|
|
|
|
case KeyPad1: *output = '1'; break;
|
|
|
|
case KeyPad2: *output = '2'; break;
|
|
|
|
case KeyPad3: *output = '3'; break;
|
|
|
|
case KeyPad4: *output = '4'; break;
|
|
|
|
case KeyPad5: *output = '5'; break;
|
|
|
|
case KeyPad6: *output = '6'; break;
|
|
|
|
case KeyPad7: *output = '7'; break;
|
|
|
|
case KeyPad8: *output = '8'; break;
|
|
|
|
case KeyPad9: *output = '9'; break;
|
|
|
|
case KeyPadDecimal: *output = '.'; break;
|
|
|
|
case KeyPadDivide: *output = '/'; break;
|
|
|
|
case KeyPadMultiply: *output = '*'; break;
|
|
|
|
case KeyPadSubtract: *output = '-'; break;
|
|
|
|
case KeyPadAdd: *output = '+'; break;
|
|
|
|
case KeyPadEnter: *output = NSEnterCharacter; break;
|
|
|
|
case KeyPadEqual: *output = '='; break;
|
|
|
|
case Menu: *output = NSMenuFunctionKey; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-04 14:55:58 +01:00
|
|
|
#endif
|