mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-12-16 12:42:30 +00:00
chore(build): use SDL3
This commit is contained in:
parent
9d04a35d87
commit
b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions
34
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaclipboard.h
vendored
Normal file
34
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaclipboard.h
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoaclipboard_h_
|
||||
#define SDL_cocoaclipboard_h_
|
||||
|
||||
// Forward declaration
|
||||
@class SDL_CocoaVideoData;
|
||||
|
||||
extern void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data);
|
||||
extern bool Cocoa_SetClipboardData(SDL_VideoDevice *_this);
|
||||
extern void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
|
||||
extern bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
|
||||
|
||||
#endif // SDL_cocoaclipboard_h_
|
||||
262
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaclipboard.m
vendored
Normal file
262
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaclipboard.m
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../events/SDL_clipboardevents_c.h"
|
||||
|
||||
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
@interface Cocoa_PasteboardDataProvider : NSObject<NSPasteboardItemDataProvider>
|
||||
{
|
||||
SDL_ClipboardDataCallback m_callback;
|
||||
void *m_userdata;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Cocoa_PasteboardDataProvider
|
||||
|
||||
- (nullable instancetype)initWith:(SDL_ClipboardDataCallback)callback
|
||||
userData:(void *)userdata
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
m_callback = callback;
|
||||
m_userdata = userdata;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)pasteboard:(NSPasteboard *)pasteboard
|
||||
item:(NSPasteboardItem *)item
|
||||
provideDataForType:(NSPasteboardType)type
|
||||
{
|
||||
@autoreleasepool {
|
||||
size_t size = 0;
|
||||
CFStringRef mimeType;
|
||||
const void *callbackData;
|
||||
NSData *data;
|
||||
mimeType = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)type, kUTTagClassMIMEType);
|
||||
callbackData = m_callback(m_userdata, [(__bridge NSString *)mimeType UTF8String], &size);
|
||||
CFRelease(mimeType);
|
||||
if (callbackData == NULL || size == 0) {
|
||||
return;
|
||||
}
|
||||
data = [NSData dataWithBytes: callbackData length: size];
|
||||
[item setData: data forType: type];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static char **GetMimeTypes(int *pnformats)
|
||||
{
|
||||
char **new_mime_types = NULL;
|
||||
|
||||
*pnformats = 0;
|
||||
|
||||
int nformats = 0;
|
||||
int formatsSz = 0;
|
||||
NSArray<NSPasteboardItem *> *items = [[NSPasteboard generalPasteboard] pasteboardItems];
|
||||
NSUInteger nitems = [items count];
|
||||
if (nitems > 0) {
|
||||
for (NSPasteboardItem *item in items) {
|
||||
NSArray<NSString *> *types = [item types];
|
||||
for (NSString *type in types) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
UTType *uttype = [UTType typeWithIdentifier:type];
|
||||
NSString *mime_type = [uttype preferredMIMEType];
|
||||
if (mime_type) {
|
||||
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
formatsSz += len;
|
||||
++nformats;
|
||||
}
|
||||
}
|
||||
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
formatsSz += len;
|
||||
++nformats;
|
||||
}
|
||||
}
|
||||
|
||||
new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz);
|
||||
if (new_mime_types) {
|
||||
int i = 0;
|
||||
char *strPtr = (char *)(new_mime_types + nformats + 1);
|
||||
for (NSPasteboardItem *item in items) {
|
||||
NSArray<NSString *> *types = [item types];
|
||||
for (NSString *type in types) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
UTType *uttype = [UTType typeWithIdentifier:type];
|
||||
NSString *mime_type = [uttype preferredMIMEType];
|
||||
if (mime_type) {
|
||||
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
SDL_memcpy(strPtr, [mime_type UTF8String], len);
|
||||
new_mime_types[i++] = strPtr;
|
||||
strPtr += len;
|
||||
}
|
||||
}
|
||||
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
SDL_memcpy(strPtr, [type UTF8String], len);
|
||||
new_mime_types[i++] = strPtr;
|
||||
strPtr += len;
|
||||
}
|
||||
}
|
||||
|
||||
new_mime_types[nformats] = NULL;
|
||||
*pnformats = nformats;
|
||||
}
|
||||
}
|
||||
return new_mime_types;
|
||||
}
|
||||
|
||||
|
||||
void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSPasteboard *pasteboard;
|
||||
NSInteger count;
|
||||
|
||||
pasteboard = [NSPasteboard generalPasteboard];
|
||||
count = [pasteboard changeCount];
|
||||
if (count != data.clipboard_count) {
|
||||
if (count) {
|
||||
int nformats = 0;
|
||||
char **new_mime_types = GetMimeTypes(&nformats);
|
||||
if (new_mime_types) {
|
||||
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
|
||||
}
|
||||
}
|
||||
data.clipboard_count = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSPasteboardItem *newItem = [NSPasteboardItem new];
|
||||
NSMutableArray *utiTypes = [NSMutableArray new];
|
||||
Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];
|
||||
BOOL itemResult = FALSE;
|
||||
BOOL writeResult = FALSE;
|
||||
|
||||
if (_this->clipboard_callback) {
|
||||
for (int i = 0; i < _this->num_clipboard_mime_types; i++) {
|
||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, _this->clipboard_mime_types[i], kCFStringEncodingUTF8);
|
||||
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
||||
CFRelease(mimeType);
|
||||
|
||||
[utiTypes addObject: (__bridge NSString *)utiType];
|
||||
CFRelease(utiType);
|
||||
}
|
||||
itemResult = [newItem setDataProvider: provider forTypes: utiTypes];
|
||||
if (itemResult == FALSE) {
|
||||
return SDL_SetError("Unable to set clipboard item data");
|
||||
}
|
||||
|
||||
[pasteboard clearContents];
|
||||
writeResult = [pasteboard writeObjects: @[newItem]];
|
||||
if (writeResult == FALSE) {
|
||||
return SDL_SetError("Unable to set clipboard data");
|
||||
}
|
||||
} else {
|
||||
[pasteboard clearContents];
|
||||
}
|
||||
data.clipboard_count = [pasteboard changeCount];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsMimeType(const char *tag)
|
||||
{
|
||||
if (SDL_strchr(tag, '/')) {
|
||||
// MIME types have slashes
|
||||
return true;
|
||||
} else if (SDL_strchr(tag, '.')) {
|
||||
// UTI identifiers have periods
|
||||
return false;
|
||||
} else {
|
||||
// Not sure, but it's not a UTI identifier
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static CFStringRef GetUTIType(const char *tag)
|
||||
{
|
||||
CFStringRef utiType;
|
||||
if (IsMimeType(tag)) {
|
||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
|
||||
utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
||||
CFRelease(mimeType);
|
||||
} else {
|
||||
utiType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
|
||||
}
|
||||
return utiType;
|
||||
}
|
||||
|
||||
void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
void *data = NULL;
|
||||
*size = 0;
|
||||
for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
|
||||
NSData *itemData;
|
||||
CFStringRef utiType = GetUTIType(mime_type);
|
||||
itemData = [item dataForType: (__bridge NSString *)utiType];
|
||||
CFRelease(utiType);
|
||||
if (itemData != nil) {
|
||||
NSUInteger length = [itemData length];
|
||||
*size = (size_t)length;
|
||||
data = SDL_malloc(*size + sizeof(Uint32));
|
||||
if (data) {
|
||||
[itemData getBytes: data length: length];
|
||||
SDL_memset((Uint8 *)data + length, 0, sizeof(Uint32));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
|
||||
{
|
||||
bool result = false;
|
||||
@autoreleasepool {
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
CFStringRef utiType = GetUTIType(mime_type);
|
||||
if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
|
||||
result = true;
|
||||
}
|
||||
CFRelease(utiType);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
33
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaevents.h
vendored
Normal file
33
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaevents.h
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoaevents_h_
|
||||
#define SDL_cocoaevents_h_
|
||||
|
||||
extern void Cocoa_RegisterApp(void);
|
||||
extern Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp);
|
||||
extern void Cocoa_PumpEvents(SDL_VideoDevice *_this);
|
||||
extern int Cocoa_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
|
||||
extern void Cocoa_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_SuspendScreenSaver(SDL_VideoDevice *_this);
|
||||
|
||||
#endif // SDL_cocoaevents_h_
|
||||
680
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaevents.m
vendored
Normal file
680
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaevents.m
vendored
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win)
|
||||
{
|
||||
SDL_Window *sdlwindow = NULL;
|
||||
SDL_VideoDevice *device = SDL_GetVideoDevice();
|
||||
if (device && device->windows) {
|
||||
for (sdlwindow = device->windows; sdlwindow; sdlwindow = sdlwindow->next) {
|
||||
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow;
|
||||
if (win == nswindow) {
|
||||
return sdlwindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sdlwindow;
|
||||
}
|
||||
|
||||
@interface SDL3Application : NSApplication
|
||||
|
||||
- (void)terminate:(id)sender;
|
||||
- (void)sendEvent:(NSEvent *)theEvent;
|
||||
|
||||
+ (void)registerUserDefaults;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDL3Application
|
||||
|
||||
// Override terminate to handle Quit and System Shutdown smoothly.
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
SDL_SendQuit();
|
||||
}
|
||||
|
||||
static bool s_bShouldHandleEventsInSDLApplication = false;
|
||||
|
||||
static void Cocoa_DispatchEvent(NSEvent *theEvent)
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
|
||||
switch ([theEvent type]) {
|
||||
case NSEventTypeLeftMouseDown:
|
||||
case NSEventTypeOtherMouseDown:
|
||||
case NSEventTypeRightMouseDown:
|
||||
case NSEventTypeLeftMouseUp:
|
||||
case NSEventTypeOtherMouseUp:
|
||||
case NSEventTypeRightMouseUp:
|
||||
case NSEventTypeLeftMouseDragged:
|
||||
case NSEventTypeRightMouseDragged:
|
||||
case NSEventTypeOtherMouseDragged: // usually middle mouse dragged
|
||||
case NSEventTypeMouseMoved:
|
||||
case NSEventTypeScrollWheel:
|
||||
case NSEventTypeMouseEntered:
|
||||
case NSEventTypeMouseExited:
|
||||
Cocoa_HandleMouseEvent(_this, theEvent);
|
||||
break;
|
||||
case NSEventTypeKeyDown:
|
||||
case NSEventTypeKeyUp:
|
||||
case NSEventTypeFlagsChanged:
|
||||
Cocoa_HandleKeyEvent(_this, theEvent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch events here so that we can handle events caught by
|
||||
// nextEventMatchingMask in SDL, as well as events caught by other
|
||||
// processes (such as CEF) that are passed down to NSApp.
|
||||
- (void)sendEvent:(NSEvent *)theEvent
|
||||
{
|
||||
if (s_bShouldHandleEventsInSDLApplication) {
|
||||
Cocoa_DispatchEvent(theEvent);
|
||||
}
|
||||
|
||||
[super sendEvent:theEvent];
|
||||
}
|
||||
|
||||
+ (void)registerUserDefaults
|
||||
{
|
||||
BOOL momentumScrollSupported = (BOOL)SDL_GetHintBoolean(SDL_HINT_MAC_SCROLL_MOMENTUM, false);
|
||||
|
||||
NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:momentumScrollSupported], @"AppleMomentumScrollSupported",
|
||||
[NSNumber numberWithBool:YES], @"ApplePressAndHoldEnabled",
|
||||
[NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState",
|
||||
nil];
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
|
||||
}
|
||||
|
||||
@end // SDL3Application
|
||||
|
||||
// setAppleMenu disappeared from the headers in 10.4
|
||||
@interface NSApplication (NSAppleMenu)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
@interface SDL3AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
@public
|
||||
BOOL seenFirstActivate;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
- (void)localeDidChange:(NSNotification *)notification;
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context;
|
||||
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app;
|
||||
- (IBAction)menu:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation SDL3AppDelegate : NSObject
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
bool registerActivationHandlers = SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true);
|
||||
|
||||
seenFirstActivate = NO;
|
||||
|
||||
if (registerActivationHandlers) {
|
||||
[center addObserver:self
|
||||
selector:@selector(windowWillClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:nil];
|
||||
|
||||
[center addObserver:self
|
||||
selector:@selector(focusSomeWindow:)
|
||||
name:NSApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
|
||||
[center addObserver:self
|
||||
selector:@selector(screenParametersChanged:)
|
||||
name:NSApplicationDidChangeScreenParametersNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
[center addObserver:self
|
||||
selector:@selector(localeDidChange:)
|
||||
name:NSCurrentLocaleDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
[NSApp addObserver:self
|
||||
forKeyPath:@"effectiveAppearance"
|
||||
options:NSKeyValueObservingOptionInitial
|
||||
context:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[center removeObserver:self name:NSWindowWillCloseNotification object:nil];
|
||||
[center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
|
||||
[center removeObserver:self name:NSApplicationDidChangeScreenParametersNotification object:nil];
|
||||
[center removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
|
||||
[NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
|
||||
|
||||
// Remove our URL event handler only if we set it
|
||||
if ([NSApp delegate] == self) {
|
||||
[[NSAppleEventManager sharedAppleEventManager]
|
||||
removeEventHandlerForEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
NSWindow *win = (NSWindow *)[notification object];
|
||||
|
||||
if (![win isKeyWindow]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't do anything if this was not an SDL window that was closed
|
||||
if (FindSDLWindowForNSWindow(win) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* HACK: Make the next window in the z-order key when the key window is
|
||||
* closed. The custom event loop and/or windowing code we have seems to
|
||||
* prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
|
||||
*/
|
||||
|
||||
/* +[NSApp orderedWindows] never includes the 'About' window, but we still
|
||||
* want to try its list first since the behavior in other apps is to only
|
||||
* make the 'About' window key if no other windows are on-screen.
|
||||
*/
|
||||
for (NSWindow *window in [NSApp orderedWindows]) {
|
||||
if (window != win && [window canBecomeKeyWindow]) {
|
||||
if (![window isOnActiveSpace]) {
|
||||
continue;
|
||||
}
|
||||
[window makeKeyAndOrderFront:self];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If a window wasn't found above, iterate through all visible windows in
|
||||
* the active Space in z-order (including the 'About' window, if it's shown)
|
||||
* and make the first one key.
|
||||
*/
|
||||
for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) {
|
||||
NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]];
|
||||
if (window && window != win && [window canBecomeKeyWindow]) {
|
||||
[window makeKeyAndOrderFront:self];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)focusSomeWindow:(NSNotification *)aNotification
|
||||
{
|
||||
SDL_VideoDevice *device;
|
||||
/* HACK: Ignore the first call. The application gets a
|
||||
* applicationDidBecomeActive: a little bit after the first window is
|
||||
* created, and if we don't ignore it, a window that has been created with
|
||||
* SDL_WINDOW_MINIMIZED will ~immediately be restored.
|
||||
*/
|
||||
if (!seenFirstActivate) {
|
||||
seenFirstActivate = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't do anything if the application already has a key window
|
||||
* that is not an SDL window.
|
||||
*/
|
||||
if ([NSApp keyWindow] && FindSDLWindowForNSWindow([NSApp keyWindow]) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
device = SDL_GetVideoDevice();
|
||||
if (device && device->windows) {
|
||||
SDL_Window *window = device->windows;
|
||||
int i;
|
||||
for (i = 0; i < device->num_displays; ++i) {
|
||||
SDL_Window *fullscreen_window = device->displays[i]->fullscreen_window;
|
||||
if (fullscreen_window) {
|
||||
if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) {
|
||||
SDL_RestoreWindow(fullscreen_window);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->flags & SDL_WINDOW_MINIMIZED) {
|
||||
SDL_RestoreWindow(window);
|
||||
} else {
|
||||
SDL_RaiseWindow(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)screenParametersChanged:(NSNotification *)aNotification
|
||||
{
|
||||
SDL_VideoDevice *device = SDL_GetVideoDevice();
|
||||
if (device) {
|
||||
Cocoa_UpdateDisplays(device);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)localeDidChange:(NSNotification *)notification
|
||||
{
|
||||
SDL_SendLocaleChangedEvent();
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context
|
||||
{
|
||||
SDL_SetSystemTheme(Cocoa_GetSystemTheme());
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
return (BOOL)SDL_SendDropFile(NULL, NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL);
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true))
|
||||
return;
|
||||
|
||||
/* The menu bar of SDL apps which don't have the typical .app bundle
|
||||
* structure fails to work the first time a window is created (until it's
|
||||
* de-focused and re-focused), if this call is in Cocoa_RegisterApp instead
|
||||
* of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051
|
||||
*/
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) {
|
||||
// Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state.
|
||||
for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
|
||||
[i activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
||||
break;
|
||||
}
|
||||
SDL_Delay(300); // !!! FIXME: this isn't right.
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
/* If we call this before NSApp activation, macOS might print a complaint
|
||||
* about ApplePersistenceIgnoreState. */
|
||||
[SDL3Application registerUserDefaults];
|
||||
}
|
||||
|
||||
- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
|
||||
{
|
||||
NSString *path = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||
SDL_SendDropFile(NULL, NULL, [path UTF8String]);
|
||||
SDL_SendDropComplete(NULL);
|
||||
}
|
||||
|
||||
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app
|
||||
{
|
||||
// This just tells Cocoa that we didn't do any custom save state magic for the app,
|
||||
// so the system is safe to use NSSecureCoding internally, instead of using unencrypted
|
||||
// save states for backwards compatibility. If we don't return YES here, we'll get a
|
||||
// warning on the console at startup:
|
||||
//
|
||||
// ```
|
||||
// WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES.
|
||||
// ```
|
||||
//
|
||||
// More-detailed explanation:
|
||||
// https://stackoverflow.com/questions/77283578/sonoma-and-nsapplicationdelegate-applicationsupportssecurerestorablestate/77320845#77320845
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (IBAction)menu:(id)sender
|
||||
{
|
||||
SDL_TrayEntry *entry = [[sender representedObject] pointerValue];
|
||||
|
||||
SDL_ClickTrayEntry(entry);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static SDL3AppDelegate *appDelegate = nil;
|
||||
|
||||
static NSString *GetApplicationName(void)
|
||||
{
|
||||
NSString *appName = nil;
|
||||
|
||||
const char *metaname = SDL_GetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_APP_METADATA_NAME_STRING, NULL);
|
||||
if (metaname && *metaname) {
|
||||
appName = [NSString stringWithUTF8String:metaname];
|
||||
}
|
||||
|
||||
// Determine the application name
|
||||
if (!appName) {
|
||||
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (!appName) {
|
||||
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
}
|
||||
}
|
||||
|
||||
if (![appName length]) {
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
}
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
static bool LoadMainMenuNibIfAvailable(void)
|
||||
{
|
||||
NSDictionary *infoDict;
|
||||
NSString *mainNibFileName;
|
||||
bool success = false;
|
||||
|
||||
infoDict = [[NSBundle mainBundle] infoDictionary];
|
||||
if (infoDict) {
|
||||
mainNibFileName = [infoDict valueForKey:@"NSMainNibFile"];
|
||||
|
||||
if (mainNibFileName) {
|
||||
success = [[NSBundle mainBundle] loadNibNamed:mainNibFileName owner:[NSApplication sharedApplication] topLevelObjects:nil];
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void CreateApplicationMenus(void)
|
||||
{
|
||||
NSString *appName;
|
||||
NSString *title;
|
||||
NSMenu *appleMenu;
|
||||
NSMenu *serviceMenu;
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSMenu *mainMenu;
|
||||
|
||||
if (NSApp == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainMenu = [[NSMenu alloc] init];
|
||||
|
||||
// Create the main menu bar
|
||||
[NSApp setMainMenu:mainMenu];
|
||||
|
||||
// Create the application menu
|
||||
appName = GetApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
// Add menu items
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
|
||||
// !!! FIXME: Menu items can't take parameters, just a basic selector, so this should instead call a selector
|
||||
// !!! FIXME: that itself calls -[NSApplication orderFrontStandardAboutPanelWithOptions:optionsDictionary],
|
||||
// !!! FIXME: filling in that NSDictionary with SDL_GetAppMetadataProperty()
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
serviceMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
menuItem = [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:serviceMenu];
|
||||
|
||||
[NSApp setServicesMenu:serviceMenu];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = [appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
// Put menu into the menubar
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
// Tell the application object that this is now the application menu
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
// Create the window menu
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
// Add menu items
|
||||
[windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
|
||||
|
||||
// Add the fullscreen toggle menu option.
|
||||
/* Cocoa should update the title to Enter or Exit Full Screen automatically.
|
||||
* But if not, then just fallback to Toggle Full Screen.
|
||||
*/
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
|
||||
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
[windowMenu addItem:menuItem];
|
||||
|
||||
// Put menu into the menubar
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
// Tell the application object that this is now the window menu
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
}
|
||||
|
||||
void Cocoa_RegisterApp(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
// This can get called more than once! Be careful what you initialize!
|
||||
|
||||
if (NSApp == nil) {
|
||||
[SDL3Application sharedApplication];
|
||||
SDL_assert(NSApp != nil);
|
||||
|
||||
s_bShouldHandleEventsInSDLApplication = true;
|
||||
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
}
|
||||
|
||||
/* If there aren't already menus in place, look to see if there's
|
||||
* a nib we should use. If not, then manually create the basic
|
||||
* menus we meed.
|
||||
*/
|
||||
if ([NSApp mainMenu] == nil) {
|
||||
bool nibLoaded;
|
||||
|
||||
nibLoaded = LoadMainMenuNibIfAvailable();
|
||||
if (!nibLoaded) {
|
||||
CreateApplicationMenus();
|
||||
}
|
||||
}
|
||||
[NSApp finishLaunching];
|
||||
if ([NSApp delegate]) {
|
||||
/* The SDL app delegate calls this in didFinishLaunching if it's
|
||||
* attached to the NSApp, otherwise we need to call it manually.
|
||||
*/
|
||||
[SDL3Application registerUserDefaults];
|
||||
}
|
||||
}
|
||||
if (NSApp && !appDelegate) {
|
||||
appDelegate = [[SDL3AppDelegate alloc] init];
|
||||
|
||||
/* If someone else has an app delegate, it means we can't turn a
|
||||
* termination into SDL_Quit, and we can't handle application:openFile:
|
||||
*/
|
||||
if (![NSApp delegate]) {
|
||||
/* Only register the URL event handler if we are being set as the
|
||||
* app delegate to avoid replacing any existing event handler.
|
||||
*/
|
||||
[[NSAppleEventManager sharedAppleEventManager]
|
||||
setEventHandler:appDelegate
|
||||
andSelector:@selector(handleURLEvent:withReplyEvent:)
|
||||
forEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
|
||||
[(NSApplication *)NSApp setDelegate:appDelegate];
|
||||
} else {
|
||||
appDelegate->seenFirstActivate = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp)
|
||||
{
|
||||
static Uint64 timestamp_offset;
|
||||
Uint64 timestamp = (Uint64)(nsTimestamp * SDL_NS_PER_SECOND);
|
||||
Uint64 now = SDL_GetTicksNS();
|
||||
|
||||
if (!timestamp_offset) {
|
||||
timestamp_offset = (now - timestamp);
|
||||
}
|
||||
timestamp += timestamp_offset;
|
||||
|
||||
if (timestamp > now) {
|
||||
timestamp_offset -= (timestamp - now);
|
||||
timestamp = now;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
int Cocoa_PumpEventsUntilDate(SDL_VideoDevice *_this, NSDate *expiration, bool accumulate)
|
||||
{
|
||||
// Run any existing modal sessions.
|
||||
for (SDL_Window *w = _this->windows; w; w = w->next) {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)w->internal;
|
||||
if (data.modal_session) {
|
||||
[NSApp runModalSession:data.modal_session];
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
if (event == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!s_bShouldHandleEventsInSDLApplication) {
|
||||
Cocoa_DispatchEvent(event);
|
||||
}
|
||||
|
||||
// Pass events down to SDL3Application to be handled in sendEvent:
|
||||
[NSApp sendEvent:event];
|
||||
if (!accumulate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Cocoa_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (timeoutNS > 0) {
|
||||
NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow:(double)timeoutNS / SDL_NS_PER_SECOND];
|
||||
return Cocoa_PumpEventsUntilDate(_this, limitDate, false);
|
||||
} else if (timeoutNS == 0) {
|
||||
return Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], false);
|
||||
} else {
|
||||
while (Cocoa_PumpEventsUntilDate(_this, [NSDate distantFuture], false) == 0) {
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_PumpEvents(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true);
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
||||
location:NSMakePoint(0, 0)
|
||||
modifierFlags:0
|
||||
timestamp:0.0
|
||||
windowNumber:((__bridge SDL_CocoaWindowData *)window->internal).window_number
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
|
||||
[NSApp postEvent:event atStart:YES];
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_SuspendScreenSaver(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
|
||||
if (data.screensaver_assertion) {
|
||||
IOPMAssertionRelease(data.screensaver_assertion);
|
||||
data.screensaver_assertion = kIOPMNullAssertionID;
|
||||
}
|
||||
|
||||
if (_this->suspend_screensaver) {
|
||||
/* FIXME: this should ideally describe the real reason why the game
|
||||
* called SDL_DisableScreenSaver. Note that the name is only meant to be
|
||||
* seen by macOS power users. there's an additional optional human-readable
|
||||
* (localized) reason parameter which we don't set.
|
||||
*/
|
||||
IOPMAssertionID assertion = kIOPMNullAssertionID;
|
||||
NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"];
|
||||
IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep,
|
||||
(__bridge CFStringRef)name,
|
||||
NULL, NULL, NULL, 0, NULL,
|
||||
&assertion);
|
||||
data.screensaver_assertion = assertion;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
36
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoakeyboard.h
vendored
Normal file
36
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoakeyboard.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoakeyboard_h_
|
||||
#define SDL_cocoakeyboard_h_
|
||||
|
||||
extern void Cocoa_InitKeyboard(SDL_VideoDevice *_this);
|
||||
extern void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event);
|
||||
extern void Cocoa_QuitKeyboard(SDL_VideoDevice *_this);
|
||||
|
||||
extern bool Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
|
||||
extern bool Cocoa_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
extern bool Cocoa_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
|
||||
|
||||
#endif // SDL_cocoakeyboard_h_
|
||||
604
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoakeyboard.m
vendored
Normal file
604
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoakeyboard.m
vendored
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
#include "../../events/scancodes_darwin.h"
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUG_IME NSLog
|
||||
#else
|
||||
#define DEBUG_IME(...)
|
||||
#endif
|
||||
|
||||
@interface SDL3TranslatorResponder : NSView <NSTextInputClient>
|
||||
{
|
||||
NSString *_markedText;
|
||||
NSRange _markedRange;
|
||||
NSRange _selectedRange;
|
||||
SDL_Rect _inputRect;
|
||||
int _pendingRawCode;
|
||||
SDL_Scancode _pendingScancode;
|
||||
Uint64 _pendingTimestamp;
|
||||
}
|
||||
- (void)doCommandBySelector:(SEL)myselector;
|
||||
- (void)setInputRect:(const SDL_Rect *)rect;
|
||||
- (void)setPendingKey:(int)rawcode scancode:(SDL_Scancode)scancode timestamp:(Uint64)timestamp;
|
||||
- (void)sendPendingKey;
|
||||
- (void)clearPendingKey;
|
||||
@end
|
||||
|
||||
@implementation SDL3TranslatorResponder
|
||||
|
||||
- (void)setInputRect:(const SDL_Rect *)rect
|
||||
{
|
||||
SDL_copyp(&_inputRect, rect);
|
||||
}
|
||||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
const char *str;
|
||||
|
||||
DEBUG_IME(@"insertText: %@ replacementRange: (%d, %d)", aString,
|
||||
(int)replacementRange.location, (int)replacementRange.length);
|
||||
|
||||
/* Could be NSString or NSAttributedString, so we have
|
||||
* to test and convert it before return as SDL event */
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
str = [[aString string] UTF8String];
|
||||
} else {
|
||||
str = [aString UTF8String];
|
||||
}
|
||||
|
||||
// We're likely sending the composed text, so we reset the IME status.
|
||||
if ([self hasMarkedText]) {
|
||||
[self unmarkText];
|
||||
}
|
||||
|
||||
// Deliver the raw key event that generated this text
|
||||
[self sendPendingKey];
|
||||
|
||||
if ((int)replacementRange.location != -1) {
|
||||
// We're replacing the last character
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, true);
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, false);
|
||||
}
|
||||
|
||||
SDL_SendKeyboardText(str);
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)myselector
|
||||
{
|
||||
/* No need to do anything since we are not using Cocoa
|
||||
selectors to handle special keys, instead we use SDL
|
||||
key events to do the same job.
|
||||
*/
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText
|
||||
{
|
||||
return _markedText != nil;
|
||||
}
|
||||
|
||||
- (NSRange)markedRange
|
||||
{
|
||||
return _markedRange;
|
||||
}
|
||||
|
||||
- (NSRange)selectedRange
|
||||
{
|
||||
return _selectedRange;
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
aString = [aString string];
|
||||
}
|
||||
|
||||
if ([aString length] == 0) {
|
||||
[self unmarkText];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_markedText != aString) {
|
||||
_markedText = aString;
|
||||
}
|
||||
|
||||
_selectedRange = selectedRange;
|
||||
_markedRange = NSMakeRange(0, [aString length]);
|
||||
|
||||
// This key event was consumed by the IME
|
||||
[self clearPendingKey];
|
||||
|
||||
NSUInteger utf32SelectedRangeLocation = [[aString substringToIndex:selectedRange.location] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
|
||||
NSUInteger utf32SelectionRangeEnd = [[aString substringToIndex:(selectedRange.location + selectedRange.length)] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
|
||||
NSUInteger utf32SelectionRangeLength = utf32SelectionRangeEnd - utf32SelectedRangeLocation;
|
||||
|
||||
SDL_SendEditingText([aString UTF8String],
|
||||
(int)utf32SelectedRangeLocation, (int)utf32SelectionRangeLength);
|
||||
|
||||
DEBUG_IME(@"setMarkedText: %@, (%d, %d) replacement range (%d, %d)", _markedText,
|
||||
(int)selectedRange.location, (int)selectedRange.length,
|
||||
(int)replacementRange.location, (int)replacementRange.length);
|
||||
}
|
||||
|
||||
- (void)unmarkText
|
||||
{
|
||||
_markedText = nil;
|
||||
|
||||
// This key event was consumed by the IME
|
||||
[self clearPendingKey];
|
||||
|
||||
SDL_SendEditingText("", 0, 0);
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
NSWindow *window = [self window];
|
||||
NSRect contentRect = [window contentRectForFrameRect:[window frame]];
|
||||
float windowHeight = contentRect.size.height;
|
||||
NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
|
||||
_inputRect.w, _inputRect.h);
|
||||
|
||||
if (actualRange) {
|
||||
*actualRange = aRange;
|
||||
}
|
||||
|
||||
DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
|
||||
(int)aRange.location, (int)aRange.length, windowHeight,
|
||||
NSStringFromRect(rect));
|
||||
|
||||
rect = [window convertRectToScreen:rect];
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", (int)aRange.location, (int)aRange.length);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSInteger)conversationIdentifier
|
||||
{
|
||||
return (NSInteger)self;
|
||||
}
|
||||
|
||||
/* This method returns the index for character that is
|
||||
* nearest to thePoint. thPoint is in screen coordinate system.
|
||||
*/
|
||||
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
|
||||
{
|
||||
DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This method is the key to attribute extension.
|
||||
* We could add new attributes through this method.
|
||||
* NSInputServer examines the return value of this
|
||||
* method & constructs appropriate attributed string.
|
||||
*/
|
||||
- (NSArray *)validAttributesForMarkedText
|
||||
{
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
- (void)setPendingKey:(int)rawcode scancode:(SDL_Scancode)scancode timestamp:(Uint64)timestamp
|
||||
{
|
||||
_pendingRawCode = rawcode;
|
||||
_pendingScancode = scancode;
|
||||
_pendingTimestamp = timestamp;
|
||||
}
|
||||
|
||||
- (void)sendPendingKey
|
||||
{
|
||||
if (_pendingRawCode < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SendKeyboardKey(_pendingTimestamp, SDL_DEFAULT_KEYBOARD_ID, _pendingRawCode, _pendingScancode, true);
|
||||
[self clearPendingKey];
|
||||
}
|
||||
|
||||
- (void)clearPendingKey
|
||||
{
|
||||
_pendingRawCode = -1;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static bool IsModifierKeyPressed(unsigned int flags,
|
||||
unsigned int target_mask,
|
||||
unsigned int other_mask,
|
||||
unsigned int either_mask)
|
||||
{
|
||||
bool target_pressed = (flags & target_mask) != 0;
|
||||
bool other_pressed = (flags & other_mask) != 0;
|
||||
bool either_pressed = (flags & either_mask) != 0;
|
||||
|
||||
if (either_pressed != (target_pressed || other_pressed))
|
||||
return either_pressed;
|
||||
|
||||
return target_pressed;
|
||||
}
|
||||
|
||||
static void HandleModifiers(SDL_VideoDevice *_this, SDL_Scancode code, unsigned int modifierFlags)
|
||||
{
|
||||
bool pressed = false;
|
||||
|
||||
if (code == SDL_SCANCODE_LSHIFT) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELSHIFTKEYMASK,
|
||||
NX_DEVICERSHIFTKEYMASK, NX_SHIFTMASK);
|
||||
} else if (code == SDL_SCANCODE_LCTRL) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCTLKEYMASK,
|
||||
NX_DEVICERCTLKEYMASK, NX_CONTROLMASK);
|
||||
} else if (code == SDL_SCANCODE_LALT) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELALTKEYMASK,
|
||||
NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK);
|
||||
} else if (code == SDL_SCANCODE_LGUI) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCMDKEYMASK,
|
||||
NX_DEVICERCMDKEYMASK, NX_COMMANDMASK);
|
||||
} else if (code == SDL_SCANCODE_RSHIFT) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERSHIFTKEYMASK,
|
||||
NX_DEVICELSHIFTKEYMASK, NX_SHIFTMASK);
|
||||
} else if (code == SDL_SCANCODE_RCTRL) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCTLKEYMASK,
|
||||
NX_DEVICELCTLKEYMASK, NX_CONTROLMASK);
|
||||
} else if (code == SDL_SCANCODE_RALT) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERALTKEYMASK,
|
||||
NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK);
|
||||
} else if (code == SDL_SCANCODE_RGUI) {
|
||||
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCMDKEYMASK,
|
||||
NX_DEVICELCMDKEYMASK, NX_COMMANDMASK);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressed) {
|
||||
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, true);
|
||||
} else {
|
||||
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateKeymap(SDL_CocoaVideoData *data, bool send_event)
|
||||
{
|
||||
TISInputSourceRef key_layout;
|
||||
UCKeyboardLayout *keyLayoutPtr = NULL;
|
||||
CFDataRef uchrDataRef;
|
||||
|
||||
// See if the keymap needs to be updated
|
||||
key_layout = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if (key_layout == data.key_layout) {
|
||||
return;
|
||||
}
|
||||
data.key_layout = key_layout;
|
||||
|
||||
// Try Unicode data first
|
||||
uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
|
||||
if (uchrDataRef) {
|
||||
keyLayoutPtr = (UCKeyboardLayout *)CFDataGetBytePtr(uchrDataRef);
|
||||
}
|
||||
|
||||
if (!keyLayoutPtr) {
|
||||
CFRelease(key_layout);
|
||||
return;
|
||||
}
|
||||
|
||||
static struct {
|
||||
int flags;
|
||||
SDL_Keymod modstate;
|
||||
} mods[] = {
|
||||
{ 0, SDL_KMOD_NONE },
|
||||
{ shiftKey, SDL_KMOD_SHIFT },
|
||||
{ alphaLock, SDL_KMOD_CAPS },
|
||||
{ (shiftKey | alphaLock), (SDL_KMOD_SHIFT | SDL_KMOD_CAPS) },
|
||||
{ optionKey, SDL_KMOD_ALT },
|
||||
{ (optionKey | shiftKey), (SDL_KMOD_ALT | SDL_KMOD_SHIFT) },
|
||||
{ (optionKey | alphaLock), (SDL_KMOD_ALT | SDL_KMOD_CAPS) },
|
||||
{ (optionKey | shiftKey | alphaLock), (SDL_KMOD_ALT | SDL_KMOD_SHIFT | SDL_KMOD_CAPS) }
|
||||
};
|
||||
|
||||
UInt32 keyboard_type = LMGetKbdType();
|
||||
|
||||
SDL_Keymap *keymap = SDL_CreateKeymap();
|
||||
for (int m = 0; m < SDL_arraysize(mods); ++m) {
|
||||
for (int i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
|
||||
OSStatus err;
|
||||
UniChar s[8];
|
||||
UniCharCount len;
|
||||
UInt32 dead_key_state;
|
||||
|
||||
// Make sure this scancode is a valid character scancode
|
||||
SDL_Scancode scancode = darwin_scancode_table[i];
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN ||
|
||||
scancode == SDL_SCANCODE_DELETE ||
|
||||
(SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & SDLK_SCANCODE_MASK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap the scancode for these two wrongly translated keys
|
||||
* UCKeyTranslate() function does not do its job properly for ISO layout keyboards, where the key '@',
|
||||
* which is located in the top left corner of the keyboard right under the Escape key, and the additional
|
||||
* key '<', which is on the right of the Shift key, are inverted
|
||||
*/
|
||||
if ((scancode == SDL_SCANCODE_NONUSBACKSLASH || scancode == SDL_SCANCODE_GRAVE) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
|
||||
// see comments in scancodes_darwin.h
|
||||
scancode = (SDL_Scancode)((SDL_SCANCODE_NONUSBACKSLASH + SDL_SCANCODE_GRAVE) - scancode);
|
||||
}
|
||||
|
||||
dead_key_state = 0;
|
||||
err = UCKeyTranslate(keyLayoutPtr, i, kUCKeyActionDown,
|
||||
((mods[m].flags >> 8) & 0xFF), keyboard_type,
|
||||
kUCKeyTranslateNoDeadKeysMask,
|
||||
&dead_key_state, 8, &len, s);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len > 0 && s[0] != 0x10) {
|
||||
SDL_SetKeymapEntry(keymap, scancode, mods[m].modstate, s[0]);
|
||||
} else {
|
||||
// The default keymap doesn't have any SDL_KMOD_ALT entries, so we don't need to override them
|
||||
if (!(mods[m].modstate & SDL_KMOD_ALT)) {
|
||||
SDL_SetKeymapEntry(keymap, scancode, mods[m].modstate, SDLK_UNKNOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_SetKeymap(keymap, send_event);
|
||||
}
|
||||
|
||||
static void SDLCALL SDL_MacOptionAsAltChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata;
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
|
||||
if (hint && *hint) {
|
||||
if (SDL_strcmp(hint, "none") == 0) {
|
||||
data.option_as_alt = OptionAsAltNone;
|
||||
} else if (SDL_strcmp(hint, "only_left") == 0) {
|
||||
data.option_as_alt = OptionAsAltOnlyLeft;
|
||||
} else if (SDL_strcmp(hint, "only_right") == 0) {
|
||||
data.option_as_alt = OptionAsAltOnlyRight;
|
||||
} else if (SDL_strcmp(hint, "both") == 0) {
|
||||
data.option_as_alt = OptionAsAltBoth;
|
||||
}
|
||||
} else {
|
||||
data.option_as_alt = OptionAsAltNone;
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_InitKeyboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
|
||||
UpdateKeymap(data, false);
|
||||
|
||||
// Set our own names for the platform-dependent but layout-independent keys
|
||||
// This key is NumLock on the MacBook keyboard. :)
|
||||
// SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");
|
||||
SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
|
||||
SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
|
||||
SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
|
||||
SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
|
||||
|
||||
data.modifierFlags = (unsigned int)[NSEvent modifierFlags];
|
||||
SDL_ToggleModState(SDL_KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) ? true : false);
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_MAC_OPTION_AS_ALT, SDL_MacOptionAsAltChanged, _this);
|
||||
}
|
||||
|
||||
bool Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSView *parentView;
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow;
|
||||
|
||||
parentView = [nswindow contentView];
|
||||
|
||||
/* We only keep one field editor per process, since only the front most
|
||||
* window can receive text input events, so it make no sense to keep more
|
||||
* than one copy. When we switched to another window and requesting for
|
||||
* text input, simply remove the field editor from its superview then add
|
||||
* it to the front most window's content view */
|
||||
if (!data.fieldEdit) {
|
||||
data.fieldEdit = [[SDL3TranslatorResponder alloc] initWithFrame:NSMakeRect(0.0, 0.0, 0.0, 0.0)];
|
||||
}
|
||||
|
||||
if (![[data.fieldEdit superview] isEqual:parentView]) {
|
||||
// DEBUG_IME(@"add fieldEdit to window contentView");
|
||||
[data.fieldEdit removeFromSuperview];
|
||||
[parentView addSubview:data.fieldEdit];
|
||||
[nswindow makeFirstResponder:data.fieldEdit];
|
||||
}
|
||||
}
|
||||
return Cocoa_UpdateTextInputArea(_this, window);
|
||||
}
|
||||
|
||||
bool Cocoa_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
|
||||
if (data && data.fieldEdit) {
|
||||
[data.fieldEdit removeFromSuperview];
|
||||
data.fieldEdit = nil;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cocoa_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
if (data.fieldEdit) {
|
||||
[data.fieldEdit setInputRect:&window->text_input_rect];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static NSEvent *ReplaceEvent(NSEvent *event, OptionAsAlt option_as_alt)
|
||||
{
|
||||
if (option_as_alt == OptionAsAltNone) {
|
||||
return event;
|
||||
}
|
||||
|
||||
const unsigned int modflags = (unsigned int)[event modifierFlags];
|
||||
|
||||
bool ignore_alt_characters = false;
|
||||
|
||||
bool lalt_pressed = IsModifierKeyPressed(modflags, NX_DEVICELALTKEYMASK,
|
||||
NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK);
|
||||
bool ralt_pressed = IsModifierKeyPressed(modflags, NX_DEVICERALTKEYMASK,
|
||||
NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK);
|
||||
|
||||
if (option_as_alt == OptionAsAltOnlyLeft && lalt_pressed) {
|
||||
ignore_alt_characters = true;
|
||||
} else if (option_as_alt == OptionAsAltOnlyRight && ralt_pressed) {
|
||||
ignore_alt_characters = true;
|
||||
} else if (option_as_alt == OptionAsAltBoth && (lalt_pressed || ralt_pressed)) {
|
||||
ignore_alt_characters = true;
|
||||
}
|
||||
|
||||
bool cmd_pressed = modflags & NX_COMMANDMASK;
|
||||
bool ctrl_pressed = modflags & NX_CONTROLMASK;
|
||||
|
||||
ignore_alt_characters = ignore_alt_characters && !cmd_pressed && !ctrl_pressed;
|
||||
|
||||
if (ignore_alt_characters) {
|
||||
NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
|
||||
return [NSEvent keyEventWithType:[event type]
|
||||
location:[event locationInWindow]
|
||||
modifierFlags:modflags
|
||||
timestamp:[event timestamp]
|
||||
windowNumber:[event windowNumber]
|
||||
context:nil
|
||||
characters:charactersIgnoringModifiers
|
||||
charactersIgnoringModifiers:charactersIgnoringModifiers
|
||||
isARepeat:[event isARepeat]
|
||||
keyCode:[event keyCode]];
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event)
|
||||
{
|
||||
unsigned short scancode;
|
||||
SDL_Scancode code;
|
||||
SDL_CocoaVideoData *data = _this ? ((__bridge SDL_CocoaVideoData *)_this->internal) : nil;
|
||||
if (!data) {
|
||||
return; // can happen when returning from fullscreen Space on shutdown
|
||||
}
|
||||
|
||||
if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp) {
|
||||
event = ReplaceEvent(event, data.option_as_alt);
|
||||
}
|
||||
|
||||
scancode = [event keyCode];
|
||||
|
||||
if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
|
||||
// see comments in scancodes_darwin.h
|
||||
scancode = 60 - scancode;
|
||||
}
|
||||
|
||||
if (scancode < SDL_arraysize(darwin_scancode_table)) {
|
||||
code = darwin_scancode_table[scancode];
|
||||
} else {
|
||||
// Hmm, does this ever happen? If so, need to extend the keymap...
|
||||
code = SDL_SCANCODE_UNKNOWN;
|
||||
}
|
||||
|
||||
switch ([event type]) {
|
||||
case NSEventTypeKeyDown:
|
||||
if (![event isARepeat]) {
|
||||
// See if we need to rebuild the keyboard layout
|
||||
UpdateKeymap(data, true);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SCANCODES
|
||||
if (code == SDL_SCANCODE_UNKNOWN) {
|
||||
SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.", scancode);
|
||||
}
|
||||
#endif
|
||||
if (SDL_TextInputActive(SDL_GetKeyboardFocus())) {
|
||||
[data.fieldEdit setPendingKey:scancode scancode:code timestamp:Cocoa_GetEventTimestamp([event timestamp])];
|
||||
[data.fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
[data.fieldEdit sendPendingKey];
|
||||
} else if (SDL_GetKeyboardFocus()) {
|
||||
SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, true);
|
||||
}
|
||||
break;
|
||||
case NSEventTypeKeyUp:
|
||||
SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, false);
|
||||
break;
|
||||
case NSEventTypeFlagsChanged: {
|
||||
// see if the new modifierFlags mean any existing keys should be pressed/released...
|
||||
const unsigned int modflags = (unsigned int)[event modifierFlags];
|
||||
HandleModifiers(_this, SDL_SCANCODE_LSHIFT, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_LCTRL, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_LALT, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_LGUI, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_RSHIFT, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_RCTRL, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_RALT, modflags);
|
||||
HandleModifiers(_this, SDL_SCANCODE_RGUI, modflags);
|
||||
break;
|
||||
}
|
||||
default: // just to avoid compiler warnings
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_QuitKeyboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
}
|
||||
|
||||
typedef int CGSConnection;
|
||||
typedef enum
|
||||
{
|
||||
CGSGlobalHotKeyEnable = 0,
|
||||
CGSGlobalHotKeyDisable = 1,
|
||||
} CGSGlobalHotKeyOperatingMode;
|
||||
|
||||
extern CGSConnection _CGSDefaultConnection(void);
|
||||
extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
|
||||
|
||||
bool Cocoa_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
|
||||
{
|
||||
#ifdef SDL_MAC_NO_SANDBOX
|
||||
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), grabbed ? CGSGlobalHotKeyDisable : CGSGlobalHotKeyEnable);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
27
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamessagebox.h
vendored
Normal file
27
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamessagebox.h
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
extern bool Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
145
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamessagebox.m
vendored
Normal file
145
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamessagebox.m
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
@interface SDL3MessageBoxPresenter : NSObject
|
||||
{
|
||||
@public
|
||||
NSInteger clicked;
|
||||
NSWindow *nswindow;
|
||||
}
|
||||
- (id)initWithParentWindow:(SDL_Window *)window;
|
||||
@end
|
||||
|
||||
@implementation SDL3MessageBoxPresenter
|
||||
- (id)initWithParentWindow:(SDL_Window *)window
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
clicked = -1;
|
||||
|
||||
// Retain the NSWindow because we'll show the alert later on the main thread
|
||||
if (window) {
|
||||
nswindow = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow;
|
||||
} else {
|
||||
nswindow = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showAlert:(NSAlert *)alert
|
||||
{
|
||||
if (nswindow) {
|
||||
[alert beginSheetModalForWindow:nswindow
|
||||
completionHandler:^(NSModalResponse returnCode) {
|
||||
[NSApp stopModalWithCode:returnCode];
|
||||
}];
|
||||
clicked = [NSApp runModalForWindow:nswindow];
|
||||
nswindow = nil;
|
||||
} else {
|
||||
clicked = [alert runModal];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID, bool *result)
|
||||
{
|
||||
NSAlert *alert;
|
||||
const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
|
||||
SDL3MessageBoxPresenter *presenter;
|
||||
NSInteger clicked;
|
||||
int i;
|
||||
Cocoa_RegisterApp();
|
||||
|
||||
alert = [[NSAlert alloc] init];
|
||||
|
||||
if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
|
||||
[alert setAlertStyle:NSAlertStyleCritical];
|
||||
} else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
|
||||
[alert setAlertStyle:NSAlertStyleWarning];
|
||||
} else {
|
||||
[alert setAlertStyle:NSAlertStyleInformational];
|
||||
}
|
||||
|
||||
[alert setMessageText:[NSString stringWithUTF8String:messageboxdata->title]];
|
||||
[alert setInformativeText:[NSString stringWithUTF8String:messageboxdata->message]];
|
||||
|
||||
for (i = 0; i < messageboxdata->numbuttons; ++i) {
|
||||
const SDL_MessageBoxButtonData *sdlButton;
|
||||
NSButton *button;
|
||||
|
||||
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
|
||||
sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
|
||||
} else {
|
||||
sdlButton = &messageboxdata->buttons[i];
|
||||
}
|
||||
|
||||
button = [alert addButtonWithTitle:[NSString stringWithUTF8String:sdlButton->text]];
|
||||
if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
|
||||
[button setKeyEquivalent:@"\r"];
|
||||
} else if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
|
||||
[button setKeyEquivalent:@"\033"];
|
||||
} else {
|
||||
[button setKeyEquivalent:@""];
|
||||
}
|
||||
}
|
||||
|
||||
presenter = [[SDL3MessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window];
|
||||
|
||||
[presenter showAlert:alert];
|
||||
|
||||
clicked = presenter->clicked;
|
||||
if (clicked >= NSAlertFirstButtonReturn) {
|
||||
clicked -= NSAlertFirstButtonReturn;
|
||||
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
|
||||
clicked = messageboxdata->numbuttons - 1 - clicked;
|
||||
}
|
||||
*buttonID = buttons[clicked].buttonID;
|
||||
*result = true;
|
||||
} else {
|
||||
*result = SDL_SetError("Did not get a valid `clicked button' id: %ld", (long)clicked);
|
||||
}
|
||||
}
|
||||
|
||||
// Display a Cocoa message box
|
||||
bool Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
|
||||
{
|
||||
@autoreleasepool {
|
||||
__block bool result = 0;
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
Cocoa_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
Cocoa_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
66
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoametalview.h
vendored
Normal file
66
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoametalview.h
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com.
|
||||
*
|
||||
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
|
||||
* backed view.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoametalview_h_
|
||||
#define SDL_cocoametalview_h_
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
|
||||
|
||||
#import "../SDL_sysvideo.h"
|
||||
|
||||
#import "SDL_cocoawindow.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
@interface SDL3_cocoametalview : NSView
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frame
|
||||
highDPI:(BOOL)highDPI
|
||||
windowID:(Uint32)windowID
|
||||
opaque:(BOOL)opaque;
|
||||
|
||||
- (void)updateDrawableSize;
|
||||
- (NSView *)hitTest:(NSPoint)point;
|
||||
|
||||
// Override superclass tag so this class can set it.
|
||||
@property(assign, readonly) NSInteger tag;
|
||||
|
||||
@property(nonatomic) BOOL highDPI;
|
||||
@property(nonatomic) Uint32 sdlWindowID;
|
||||
|
||||
@end
|
||||
|
||||
SDL_MetalView Cocoa_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
void Cocoa_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view);
|
||||
void *Cocoa_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view);
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL)
|
||||
|
||||
#endif // SDL_cocoametalview_h_
|
||||
182
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoametalview.m
vendored
Normal file
182
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoametalview.m
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com.
|
||||
*
|
||||
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
|
||||
* backed view.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "../../events/SDL_windowevents_c.h"
|
||||
|
||||
#import "SDL_cocoametalview.h"
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
|
||||
|
||||
static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
|
||||
{
|
||||
/* Update the drawable size when SDL receives a size changed event for
|
||||
* the window that contains the metal view. It would be nice to use
|
||||
* - (void)resizeWithOldSuperviewSize:(NSSize)oldSize and
|
||||
* - (void)viewDidChangeBackingProperties instead, but SDL's size change
|
||||
* events don't always happen in the same frame (for example when a
|
||||
* resizable window exits a fullscreen Space via the user pressing the OS
|
||||
* exit-space button). */
|
||||
if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
|
||||
@autoreleasepool {
|
||||
SDL3_cocoametalview *view = (__bridge SDL3_cocoametalview *)userdata;
|
||||
if (view.sdlWindowID == event->window.windowID) {
|
||||
[view updateDrawableSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@implementation SDL3_cocoametalview
|
||||
|
||||
// Return a Metal-compatible layer.
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return NSClassFromString(@"CAMetalLayer");
|
||||
}
|
||||
|
||||
// Indicate the view wants to draw using a backing layer instead of drawRect.
|
||||
- (BOOL)wantsUpdateLayer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* When the wantsLayer property is set to YES, this method will be invoked to
|
||||
* return a layer instance.
|
||||
*/
|
||||
- (CALayer *)makeBackingLayer
|
||||
{
|
||||
return [self.class.layerClass layer];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frame
|
||||
highDPI:(BOOL)highDPI
|
||||
windowID:(Uint32)windowID
|
||||
opaque:(BOOL)opaque
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
self.highDPI = highDPI;
|
||||
self.sdlWindowID = windowID;
|
||||
self.wantsLayer = YES;
|
||||
|
||||
// Allow resize.
|
||||
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||
|
||||
self.layer.opaque = opaque;
|
||||
|
||||
SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
|
||||
|
||||
[self updateDrawableSize];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
|
||||
}
|
||||
|
||||
- (NSInteger)tag
|
||||
{
|
||||
return SDL_METALVIEW_TAG;
|
||||
}
|
||||
|
||||
- (void)updateDrawableSize
|
||||
{
|
||||
CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
|
||||
NSSize size = self.bounds.size;
|
||||
NSSize backingSize = size;
|
||||
|
||||
if (self.highDPI) {
|
||||
/* Note: NSHighResolutionCapable must be set to true in the app's
|
||||
* Info.plist in order for the backing size to be high res.
|
||||
*/
|
||||
backingSize = [self convertSizeToBacking:size];
|
||||
}
|
||||
|
||||
metalLayer.contentsScale = backingSize.height / size.height;
|
||||
metalLayer.drawableSize = NSSizeToCGSize(backingSize);
|
||||
}
|
||||
|
||||
- (NSView *)hitTest:(NSPoint)point
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
SDL_MetalView Cocoa_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
NSView *view = data.nswindow.contentView;
|
||||
BOOL highDPI = (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) != 0;
|
||||
BOOL opaque = (window->flags & SDL_WINDOW_TRANSPARENT) == 0;
|
||||
Uint32 windowID = SDL_GetWindowID(window);
|
||||
SDL3_cocoametalview *newview;
|
||||
SDL_MetalView metalview;
|
||||
|
||||
newview = [[SDL3_cocoametalview alloc] initWithFrame:view.frame
|
||||
highDPI:highDPI
|
||||
windowID:windowID
|
||||
opaque:opaque];
|
||||
if (newview == nil) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
[view addSubview:newview];
|
||||
|
||||
// Make sure the drawable size is up to date after attaching the view.
|
||||
[newview updateDrawableSize];
|
||||
|
||||
metalview = (SDL_MetalView)CFBridgingRetain(newview);
|
||||
|
||||
return metalview;
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3_cocoametalview *metalview = CFBridgingRelease(view);
|
||||
[metalview removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
void *Cocoa_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3_cocoametalview *cocoaview = (__bridge SDL3_cocoametalview *)view;
|
||||
return (__bridge void *)cocoaview.layer;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL)
|
||||
45
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamodes.h
vendored
Normal file
45
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamodes.h
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoamodes_h_
|
||||
#define SDL_cocoamodes_h_
|
||||
|
||||
struct SDL_DisplayData
|
||||
{
|
||||
CGDirectDisplayID display;
|
||||
};
|
||||
|
||||
struct SDL_DisplayModeData
|
||||
{
|
||||
CFMutableArrayRef modes;
|
||||
};
|
||||
|
||||
extern void Cocoa_InitModes(SDL_VideoDevice *_this);
|
||||
extern void Cocoa_UpdateDisplays(SDL_VideoDevice *_this);
|
||||
extern bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
|
||||
extern bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
|
||||
extern bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
|
||||
extern bool Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
|
||||
extern void Cocoa_QuitModes(SDL_VideoDevice *_this);
|
||||
extern SDL_VideoDisplay *Cocoa_FindSDLDisplayByCGDirectDisplayID(SDL_VideoDevice *_this, CGDirectDisplayID displayid);
|
||||
|
||||
#endif // SDL_cocoamodes_h_
|
||||
718
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamodes.m
vendored
Normal file
718
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamodes.m
vendored
Normal file
|
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
// We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName
|
||||
#include <IOKit/graphics/IOGraphicsLib.h>
|
||||
|
||||
// We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod
|
||||
#include <CoreVideo/CVBase.h>
|
||||
#include <CoreVideo/CVDisplayLink.h>
|
||||
|
||||
#if (IOGRAPHICSTYPES_REV < 40)
|
||||
#define kDisplayModeNativeFlag 0x02000000
|
||||
#endif
|
||||
|
||||
static bool CG_SetError(const char *prefix, CGDisplayErr result)
|
||||
{
|
||||
const char *error;
|
||||
|
||||
switch (result) {
|
||||
case kCGErrorFailure:
|
||||
error = "kCGErrorFailure";
|
||||
break;
|
||||
case kCGErrorIllegalArgument:
|
||||
error = "kCGErrorIllegalArgument";
|
||||
break;
|
||||
case kCGErrorInvalidConnection:
|
||||
error = "kCGErrorInvalidConnection";
|
||||
break;
|
||||
case kCGErrorInvalidContext:
|
||||
error = "kCGErrorInvalidContext";
|
||||
break;
|
||||
case kCGErrorCannotComplete:
|
||||
error = "kCGErrorCannotComplete";
|
||||
break;
|
||||
case kCGErrorNotImplemented:
|
||||
error = "kCGErrorNotImplemented";
|
||||
break;
|
||||
case kCGErrorRangeCheck:
|
||||
error = "kCGErrorRangeCheck";
|
||||
break;
|
||||
case kCGErrorTypeCheck:
|
||||
error = "kCGErrorTypeCheck";
|
||||
break;
|
||||
case kCGErrorInvalidOperation:
|
||||
error = "kCGErrorInvalidOperation";
|
||||
break;
|
||||
case kCGErrorNoneAvailable:
|
||||
error = "kCGErrorNoneAvailable";
|
||||
break;
|
||||
default:
|
||||
error = "Unknown Error";
|
||||
break;
|
||||
}
|
||||
return SDL_SetError("%s: %s", prefix, error);
|
||||
}
|
||||
|
||||
static NSScreen *GetNSScreenForDisplayID(CGDirectDisplayID displayID)
|
||||
{
|
||||
NSArray *screens = [NSScreen screens];
|
||||
|
||||
// !!! FIXME: maybe track the NSScreen in SDL_DisplayData?
|
||||
for (NSScreen *screen in screens) {
|
||||
const CGDirectDisplayID thisDisplay = (CGDirectDisplayID)[[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
if (thisDisplay == displayID) {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
SDL_VideoDisplay *Cocoa_FindSDLDisplayByCGDirectDisplayID(SDL_VideoDevice *_this, CGDirectDisplayID displayid)
|
||||
{
|
||||
for (int i = 0; i < _this->num_displays; i++) {
|
||||
const SDL_DisplayData *displaydata = _this->displays[i]->internal;
|
||||
if (displaydata && (displaydata->display == displayid)) {
|
||||
return _this->displays[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static float GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link)
|
||||
{
|
||||
float refreshRate = (float)CGDisplayModeGetRefreshRate(vidmode);
|
||||
|
||||
// CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays).
|
||||
if (refreshRate == 0 && link != NULL) {
|
||||
CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
|
||||
if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
|
||||
refreshRate = (float)time.timeScale / time.timeValue;
|
||||
}
|
||||
}
|
||||
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
static bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode)
|
||||
{
|
||||
uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
|
||||
|
||||
// Filter out modes which have flags that we don't want.
|
||||
if (ioflags & (kDisplayModeNeverShowFlag | kDisplayModeNotGraphicsQualityFlag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter out modes which don't have flags that we want.
|
||||
if (!(ioflags & kDisplayModeValidFlag) || !(ioflags & kDisplayModeSafeFlag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Uint32 GetDisplayModePixelFormat(CGDisplayModeRef vidmode)
|
||||
{
|
||||
// This API is deprecated in 10.11 with no good replacement (as of 10.15).
|
||||
CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
|
||||
Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN;
|
||||
|
||||
if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
|
||||
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
||||
pixelformat = SDL_PIXELFORMAT_ARGB8888;
|
||||
} else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
|
||||
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
||||
pixelformat = SDL_PIXELFORMAT_ARGB1555;
|
||||
} else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
|
||||
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
||||
pixelformat = SDL_PIXELFORMAT_ARGB2101010;
|
||||
} else {
|
||||
// ignore 8-bit and such for now.
|
||||
}
|
||||
|
||||
CFRelease(fmt);
|
||||
|
||||
return pixelformat;
|
||||
}
|
||||
|
||||
static bool GetDisplayMode(CGDisplayModeRef vidmode, bool vidmodeCurrent, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode)
|
||||
{
|
||||
SDL_DisplayModeData *data;
|
||||
bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode);
|
||||
size_t width = CGDisplayModeGetWidth(vidmode);
|
||||
size_t height = CGDisplayModeGetHeight(vidmode);
|
||||
size_t pixelW = width;
|
||||
size_t pixelH = height;
|
||||
uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
|
||||
float refreshrate = GetDisplayModeRefreshRate(vidmode, link);
|
||||
Uint32 format = GetDisplayModePixelFormat(vidmode);
|
||||
bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0;
|
||||
CFMutableArrayRef modes;
|
||||
|
||||
if (format == SDL_PIXELFORMAT_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't fail the current mode based on flags because this could prevent Cocoa_InitModes from
|
||||
* succeeding if the current mode lacks certain flags (esp kDisplayModeSafeFlag). */
|
||||
if (!vidmodeCurrent && !HasValidDisplayModeFlags(vidmode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
CFArrayAppendValue(modes, vidmode);
|
||||
|
||||
/* If a list of possible display modes is passed in, use it to filter out
|
||||
* modes that have duplicate sizes. We don't just rely on SDL's higher level
|
||||
* duplicate filtering because this code can choose what properties are
|
||||
* preferred, and it can add CGDisplayModes to the DisplayModeData's list of
|
||||
* modes to try (see comment below for why that's necessary). */
|
||||
pixelW = CGDisplayModeGetPixelWidth(vidmode);
|
||||
pixelH = CGDisplayModeGetPixelHeight(vidmode);
|
||||
|
||||
if (modelist != NULL) {
|
||||
CFIndex modescount = CFArrayGetCount(modelist);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < modescount; i++) {
|
||||
size_t otherW, otherH, otherpixelW, otherpixelH;
|
||||
float otherrefresh;
|
||||
Uint32 otherformat;
|
||||
bool otherGUI;
|
||||
CGDisplayModeRef othermode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modelist, i);
|
||||
uint32_t otherioflags = CGDisplayModeGetIOFlags(othermode);
|
||||
|
||||
if (CFEqual(vidmode, othermode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!HasValidDisplayModeFlags(othermode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
otherW = CGDisplayModeGetWidth(othermode);
|
||||
otherH = CGDisplayModeGetHeight(othermode);
|
||||
otherpixelW = CGDisplayModeGetPixelWidth(othermode);
|
||||
otherpixelH = CGDisplayModeGetPixelHeight(othermode);
|
||||
otherrefresh = GetDisplayModeRefreshRate(othermode, link);
|
||||
otherformat = GetDisplayModePixelFormat(othermode);
|
||||
otherGUI = CGDisplayModeIsUsableForDesktopGUI(othermode);
|
||||
|
||||
/* Ignore this mode if it's interlaced and there's a non-interlaced
|
||||
* mode in the list with the same properties.
|
||||
*/
|
||||
if (interlaced && ((otherioflags & kDisplayModeInterlacedFlag) == 0) && width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && refreshrate == otherrefresh && format == otherformat && usableForGUI == otherGUI) {
|
||||
CFRelease(modes);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ignore this mode if it's not usable for desktop UI and its
|
||||
* properties are equal to another GUI-capable mode in the list.
|
||||
*/
|
||||
if (width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && !usableForGUI && otherGUI && refreshrate == otherrefresh && format == otherformat) {
|
||||
CFRelease(modes);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If multiple modes have the exact same properties, they'll all
|
||||
* go in the list of modes to try when SetDisplayMode is called.
|
||||
* This is needed because kCGDisplayShowDuplicateLowResolutionModes
|
||||
* (which is used to expose highdpi display modes) can make the
|
||||
* list of modes contain duplicates (according to their properties
|
||||
* obtained via public APIs) which don't work with SetDisplayMode.
|
||||
* Those duplicate non-functional modes *do* have different pixel
|
||||
* formats according to their internal data structure viewed with
|
||||
* NSLog, but currently no public API can detect that.
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=4822
|
||||
*
|
||||
* As of macOS 10.15.0, those duplicates have the exact same
|
||||
* properties via public APIs in every way (even their IO flags and
|
||||
* CGDisplayModeGetIODisplayModeID is the same), so we could test
|
||||
* those for equality here too, but I'm intentionally not doing that
|
||||
* in case there are duplicate modes with different IO flags or IO
|
||||
* display mode IDs in the future. In that case I think it's better
|
||||
* to try them all in SetDisplayMode than to risk one of them being
|
||||
* correct but it being filtered out by SDL_AddFullscreenDisplayMode
|
||||
* as being a duplicate.
|
||||
*/
|
||||
if (width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && usableForGUI == otherGUI && refreshrate == otherrefresh && format == otherformat) {
|
||||
CFArrayAppendValue(modes, othermode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_zerop(mode);
|
||||
data = (SDL_DisplayModeData *)SDL_malloc(sizeof(*data));
|
||||
if (!data) {
|
||||
CFRelease(modes);
|
||||
return false;
|
||||
}
|
||||
data->modes = modes;
|
||||
mode->format = format;
|
||||
mode->w = (int)width;
|
||||
mode->h = (int)height;
|
||||
mode->pixel_density = (float)pixelW / width;
|
||||
mode->refresh_rate = refreshrate;
|
||||
mode->internal = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *Cocoa_GetDisplayName(CGDirectDisplayID displayID)
|
||||
{
|
||||
if (@available(macOS 10.15, *)) {
|
||||
NSScreen *screen = GetNSScreenForDisplayID(displayID);
|
||||
if (screen) {
|
||||
const char *name = [screen.localizedName UTF8String];
|
||||
if (name) {
|
||||
return SDL_strdup(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This API is deprecated in 10.9 with no good replacement (as of 10.15).
|
||||
io_service_t servicePort = CGDisplayIOServicePort(displayID);
|
||||
CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName);
|
||||
NSDictionary *localizedNames = [(__bridge NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
|
||||
char *displayName = NULL;
|
||||
|
||||
if ([localizedNames count] > 0) {
|
||||
displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
|
||||
}
|
||||
CFRelease(deviceInfo);
|
||||
return displayName;
|
||||
}
|
||||
|
||||
static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDROutputProperties *HDR)
|
||||
{
|
||||
HDR->SDR_white_level = 1.0f;
|
||||
HDR->HDR_headroom = 1.0f;
|
||||
|
||||
if (@available(macOS 10.15, *)) {
|
||||
NSScreen *screen = GetNSScreenForDisplayID(displayID);
|
||||
if (screen) {
|
||||
if (screen.maximumExtendedDynamicRangeColorComponentValue > 1.0f) {
|
||||
HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue;
|
||||
} else {
|
||||
HDR->HDR_headroom = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Cocoa_AddDisplay(CGDirectDisplayID display, bool send_event)
|
||||
{
|
||||
CGDisplayModeRef moderef = CGDisplayCopyDisplayMode(display);
|
||||
if (!moderef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)SDL_malloc(sizeof(*displaydata));
|
||||
if (!displaydata) {
|
||||
CGDisplayModeRelease(moderef);
|
||||
return false;
|
||||
}
|
||||
displaydata->display = display;
|
||||
|
||||
CVDisplayLinkRef link = NULL;
|
||||
CVDisplayLinkCreateWithCGDisplay(display, &link);
|
||||
|
||||
SDL_VideoDisplay viddisplay;
|
||||
SDL_zero(viddisplay);
|
||||
viddisplay.name = Cocoa_GetDisplayName(display); // this returns a strdup'ed string
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
if (!GetDisplayMode(moderef, true, NULL, link, &mode)) {
|
||||
CVDisplayLinkRelease(link);
|
||||
CGDisplayModeRelease(moderef);
|
||||
SDL_free(viddisplay.name);
|
||||
SDL_free(displaydata);
|
||||
return false;
|
||||
}
|
||||
|
||||
CVDisplayLinkRelease(link);
|
||||
CGDisplayModeRelease(moderef);
|
||||
|
||||
Cocoa_GetHDRProperties(displaydata->display, &viddisplay.HDR);
|
||||
|
||||
viddisplay.desktop_mode = mode;
|
||||
viddisplay.internal = displaydata;
|
||||
const bool retval = SDL_AddVideoDisplay(&viddisplay, send_event);
|
||||
SDL_free(viddisplay.name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void Cocoa_DisplayReconfigurationCallback(CGDirectDisplayID displayid, CGDisplayChangeSummaryFlags flags, void *userInfo)
|
||||
{
|
||||
#if 0
|
||||
SDL_Log("COCOA DISPLAY RECONFIG CALLBACK! display=%u", (unsigned int) displayid);
|
||||
#define CHECK_DISPLAY_RECONFIG_FLAG(x) if (flags & x) { SDL_Log(" - " #x); }
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayBeginConfigurationFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayMovedFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplaySetMainFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplaySetModeFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayAddFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayRemoveFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayEnabledFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayDisabledFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayMirrorFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayUnMirrorFlag);
|
||||
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayDesktopShapeChangedFlag);
|
||||
#undef CHECK_DISPLAY_RECONFIG_FLAG
|
||||
#endif
|
||||
|
||||
SDL_VideoDevice *_this = (SDL_VideoDevice *) userInfo;
|
||||
SDL_VideoDisplay *display = Cocoa_FindSDLDisplayByCGDirectDisplayID(_this, displayid); // will be NULL for newly-added (or newly-unmirrored) displays!
|
||||
|
||||
if (flags & kCGDisplayDisabledFlag) {
|
||||
flags |= kCGDisplayRemoveFlag; // treat this like a display leaving, even though it's still plugged in.
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayEnabledFlag) {
|
||||
flags |= kCGDisplayAddFlag; // treat this like a display leaving, even though it's still plugged in.
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayMirrorFlag) {
|
||||
flags |= kCGDisplayRemoveFlag; // treat this like a display leaving, even though it's still actually here.
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayUnMirrorFlag) {
|
||||
flags |= kCGDisplayAddFlag; // treat this like a new display arriving, even though it was here all along.
|
||||
}
|
||||
|
||||
if ((flags & kCGDisplayAddFlag) && (flags & kCGDisplayRemoveFlag)) {
|
||||
// sometimes you get a removed device that gets Add and Remove flags at the same time but the display dimensions are 0x0 or 1x1, hence the `> 1` test.
|
||||
// Mirrored things are always removed, since they don't represent a discrete display in this state.
|
||||
if (((flags & kCGDisplayMirrorFlag) == 0) && (CGDisplayPixelsWide(displayid) > 1)) {
|
||||
// Final state is connected
|
||||
flags &= ~kCGDisplayRemoveFlag;
|
||||
} else {
|
||||
// Final state is disconnected
|
||||
flags &= ~kCGDisplayAddFlag;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayAddFlag) {
|
||||
if (!display) {
|
||||
if (!Cocoa_AddDisplay(displayid, true)) {
|
||||
return; // oh well.
|
||||
}
|
||||
display = Cocoa_FindSDLDisplayByCGDirectDisplayID(_this, displayid);
|
||||
SDL_assert(display != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayRemoveFlag) {
|
||||
if (display) {
|
||||
SDL_DelVideoDisplay(display->id, true);
|
||||
display = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplaySetModeFlag) {
|
||||
if (display) {
|
||||
CGDisplayModeRef moderef = CGDisplayCopyDisplayMode(displayid);
|
||||
if (moderef) {
|
||||
CVDisplayLinkRef link = NULL;
|
||||
CVDisplayLinkCreateWithCGDisplay(displayid, &link);
|
||||
if (link) {
|
||||
SDL_DisplayMode mode;
|
||||
if (GetDisplayMode(moderef, true, NULL, link, &mode)) {
|
||||
SDL_SetDesktopDisplayMode(display, &mode);
|
||||
}
|
||||
CVDisplayLinkRelease(link);
|
||||
}
|
||||
CGDisplayModeRelease(moderef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplaySetMainFlag) {
|
||||
if (display) {
|
||||
for (int i = 0; i < _this->num_displays; i++) {
|
||||
if (_this->displays[i] == display) {
|
||||
if (i > 0) {
|
||||
// move this display to the front of _this->displays so it's treated as primary.
|
||||
SDL_memmove(&_this->displays[1], &_this->displays[0], sizeof (*_this->displays) * i);
|
||||
_this->displays[0] = display;
|
||||
}
|
||||
flags |= kCGDisplayMovedFlag; // we don't have an SDL event atm for "this display became primary," so at least let everyone know it "moved".
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayMovedFlag) {
|
||||
if (display) {
|
||||
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_MOVED, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & kCGDisplayDesktopShapeChangedFlag) {
|
||||
SDL_UpdateDesktopBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_InitModes(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
CGDisplayErr result;
|
||||
CGDisplayCount numDisplays = 0;
|
||||
|
||||
result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
|
||||
if (result != kCGErrorSuccess) {
|
||||
CG_SetError("CGGetOnlineDisplayList()", result);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isstack;
|
||||
CGDirectDisplayID *displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack);
|
||||
|
||||
result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
|
||||
if (result != kCGErrorSuccess) {
|
||||
CG_SetError("CGGetOnlineDisplayList()", result);
|
||||
SDL_small_free(displays, isstack);
|
||||
return;
|
||||
}
|
||||
|
||||
// future updates to the display graph will come through this callback.
|
||||
CGDisplayRegisterReconfigurationCallback(Cocoa_DisplayReconfigurationCallback, _this);
|
||||
|
||||
// Pick up the primary display in the first pass, then get the rest
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
for (int i = 0; i < numDisplays; ++i) {
|
||||
if (pass == 0) {
|
||||
if (!CGDisplayIsMain(displays[i])) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (CGDisplayIsMain(displays[i])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Cocoa_AddDisplay(displays[i], false);
|
||||
}
|
||||
}
|
||||
SDL_small_free(displays, isstack);
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_UpdateDisplays(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_HDROutputProperties HDR;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _this->num_displays; ++i) {
|
||||
SDL_VideoDisplay *display = _this->displays[i];
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
|
||||
|
||||
Cocoa_GetHDRProperties(displaydata->display, &HDR);
|
||||
SDL_SetDisplayHDRProperties(display, &HDR);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
|
||||
{
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
|
||||
CGRect cgrect;
|
||||
|
||||
cgrect = CGDisplayBounds(displaydata->display);
|
||||
rect->x = (int)cgrect.origin.x;
|
||||
rect->y = (int)cgrect.origin.y;
|
||||
rect->w = (int)cgrect.size.width;
|
||||
rect->h = (int)cgrect.size.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
|
||||
NSScreen *screen = GetNSScreenForDisplayID(displaydata->display);
|
||||
|
||||
if (screen == nil) {
|
||||
return SDL_SetError("Couldn't get NSScreen for display");
|
||||
}
|
||||
|
||||
{
|
||||
const NSRect frame = [screen visibleFrame];
|
||||
rect->x = (int)frame.origin.x;
|
||||
rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height);
|
||||
rect->w = (int)frame.size.width;
|
||||
rect->h = (int)frame.size.height;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
||||
{
|
||||
SDL_DisplayData *data = (SDL_DisplayData *)display->internal;
|
||||
CVDisplayLinkRef link = NULL;
|
||||
CFArrayRef modes;
|
||||
CFDictionaryRef dict = NULL;
|
||||
const CFStringRef dictkeys[] = { kCGDisplayShowDuplicateLowResolutionModes };
|
||||
const CFBooleanRef dictvalues[] = { kCFBooleanTrue };
|
||||
|
||||
CVDisplayLinkCreateWithCGDisplay(data->display, &link);
|
||||
|
||||
/* By default, CGDisplayCopyAllDisplayModes will only get a subset of the
|
||||
* system's available modes. For example on a 15" 2016 MBP, users can
|
||||
* choose 1920x1080@2x in System Preferences but it won't show up here,
|
||||
* unless we specify the option below.
|
||||
* The display modes returned by CGDisplayCopyAllDisplayModes are also not
|
||||
* high dpi-capable unless this option is set.
|
||||
* macOS 10.15 also seems to have a bug where entering, exiting, and
|
||||
* re-entering exclusive fullscreen with a low dpi display mode can cause
|
||||
* the content of the screen to move up, which this setting avoids:
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=4822
|
||||
*/
|
||||
|
||||
dict = CFDictionaryCreate(NULL,
|
||||
(const void **)dictkeys,
|
||||
(const void **)dictvalues,
|
||||
1,
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
modes = CGDisplayCopyAllDisplayModes(data->display, dict);
|
||||
|
||||
if (dict) {
|
||||
CFRelease(dict);
|
||||
}
|
||||
|
||||
if (modes) {
|
||||
CFIndex i;
|
||||
const CFIndex count = CFArrayGetCount(modes);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
if (GetDisplayMode(moderef, false, modes, link, &mode)) {
|
||||
if (!SDL_AddFullscreenDisplayMode(display, &mode)) {
|
||||
CFRelease(mode.internal->modes);
|
||||
SDL_free(mode.internal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(modes);
|
||||
}
|
||||
|
||||
CVDisplayLinkRelease(link);
|
||||
return true;
|
||||
}
|
||||
|
||||
static CGError SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayModeData *data)
|
||||
{
|
||||
/* SDL_DisplayModeData can contain multiple CGDisplayModes to try (with
|
||||
* identical properties), some of which might not work. See GetDisplayMode.
|
||||
*/
|
||||
CGError result = kCGErrorFailure;
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(data->modes); i++) {
|
||||
CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i);
|
||||
result = CGDisplaySetDisplayMode(display, moderef, NULL);
|
||||
if (result == kCGErrorSuccess) {
|
||||
// If this mode works, try it first next time.
|
||||
if (i > 0) {
|
||||
CFArrayExchangeValuesAtIndices(data->modes, i, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
|
||||
{
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
|
||||
SDL_DisplayModeData *data = mode->internal;
|
||||
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
|
||||
CGError result = kCGErrorSuccess;
|
||||
|
||||
b_inModeTransition = true;
|
||||
|
||||
// Fade to black to hide resolution-switching flicker
|
||||
if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
|
||||
CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
|
||||
}
|
||||
|
||||
if (data == display->desktop_mode.internal) {
|
||||
// Restoring desktop mode
|
||||
SetDisplayModeForDisplay(displaydata->display, data);
|
||||
} else {
|
||||
// Do the physical switch
|
||||
result = SetDisplayModeForDisplay(displaydata->display, data);
|
||||
}
|
||||
|
||||
// Fade in again (asynchronously)
|
||||
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
|
||||
CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
|
||||
CGReleaseDisplayFadeReservation(fade_token);
|
||||
}
|
||||
|
||||
b_inModeTransition = false;
|
||||
|
||||
if (result != kCGErrorSuccess) {
|
||||
return CG_SetError("CGDisplaySwitchToMode()", result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cocoa_QuitModes(SDL_VideoDevice *_this)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
CGDisplayRemoveReconfigurationCallback(Cocoa_DisplayReconfigurationCallback, _this);
|
||||
|
||||
for (i = 0; i < _this->num_displays; ++i) {
|
||||
SDL_VideoDisplay *display = _this->displays[i];
|
||||
SDL_DisplayModeData *mode;
|
||||
|
||||
if (display->current_mode->internal != display->desktop_mode.internal) {
|
||||
Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
|
||||
}
|
||||
|
||||
mode = display->desktop_mode.internal;
|
||||
CFRelease(mode->modes);
|
||||
|
||||
for (j = 0; j < display->num_fullscreen_modes; j++) {
|
||||
mode = display->fullscreen_modes[j].internal;
|
||||
CFRelease(mode->modes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
51
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamouse.h
vendored
Normal file
51
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamouse.h
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoamouse_h_
|
||||
#define SDL_cocoamouse_h_
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
extern bool Cocoa_InitMouse(SDL_VideoDevice *_this);
|
||||
extern NSWindow *Cocoa_GetMouseFocus();
|
||||
extern void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event);
|
||||
extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event);
|
||||
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
|
||||
extern void Cocoa_QuitMouse(SDL_VideoDevice *_this);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Whether we've seen a cursor warp since the last move event.
|
||||
bool seenWarp;
|
||||
// What location our last cursor warp was to.
|
||||
CGFloat lastWarpX;
|
||||
CGFloat lastWarpY;
|
||||
// What location we last saw the cursor move to.
|
||||
CGFloat lastMoveX;
|
||||
CGFloat lastMoveY;
|
||||
} SDL_MouseData;
|
||||
|
||||
@interface NSCursor (InvisibleCursor)
|
||||
+ (NSCursor *)invisibleCursor;
|
||||
@end
|
||||
|
||||
#endif // SDL_cocoamouse_h_
|
||||
591
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamouse.m
vendored
Normal file
591
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoamouse.m
vendored
Normal file
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoamouse.h"
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
|
||||
#if 0
|
||||
#define DEBUG_COCOAMOUSE
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_COCOAMOUSE
|
||||
#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DLog(...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
@implementation NSCursor (InvisibleCursor)
|
||||
+ (NSCursor *)invisibleCursor
|
||||
{
|
||||
static NSCursor *invisibleCursor = NULL;
|
||||
if (!invisibleCursor) {
|
||||
// RAW 16x16 transparent GIF
|
||||
static unsigned char cursorBytes[] = {
|
||||
0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
|
||||
0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
|
||||
0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
|
||||
0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
|
||||
};
|
||||
|
||||
NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
|
||||
length:sizeof(cursorBytes)
|
||||
freeWhenDone:NO];
|
||||
NSImage *cursorImage = [[NSImage alloc] initWithData:cursorData];
|
||||
invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
|
||||
hotSpot:NSZeroPoint];
|
||||
}
|
||||
|
||||
return invisibleCursor;
|
||||
}
|
||||
@end
|
||||
|
||||
static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSImage *nsimage;
|
||||
NSCursor *nscursor = NULL;
|
||||
SDL_Cursor *cursor = NULL;
|
||||
|
||||
nsimage = Cocoa_CreateImage(surface);
|
||||
if (nsimage) {
|
||||
nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(hot_x, hot_y)];
|
||||
}
|
||||
|
||||
if (nscursor) {
|
||||
cursor = SDL_calloc(1, sizeof(*cursor));
|
||||
if (cursor) {
|
||||
cursor->internal = (void *)CFBridgingRetain(nscursor);
|
||||
}
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor.
|
||||
If we can load them ourselves, use them, otherwise fallback to something standard but not super-great.
|
||||
Since these are under /System, they should be available even to sandboxed apps. */
|
||||
static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback)
|
||||
{
|
||||
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];
|
||||
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
|
||||
// we can't do animation atm. :/
|
||||
const int frames = (int)[[info valueForKey:@"frames"] integerValue];
|
||||
NSCursor *cursor;
|
||||
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
|
||||
if ((image == nil) || (image.isValid == NO)) {
|
||||
return [NSCursor performSelector:fallback];
|
||||
}
|
||||
|
||||
if (frames > 1) {
|
||||
#ifdef MAC_OS_VERSION_12_0 // same value as deprecated symbol.
|
||||
const NSCompositingOperation operation = NSCompositingOperationCopy;
|
||||
#else
|
||||
const NSCompositingOperation operation = NSCompositeCopy;
|
||||
#endif
|
||||
const NSSize cropped_size = NSMakeSize(image.size.width, (int)(image.size.height / frames));
|
||||
NSImage *cropped = [[NSImage alloc] initWithSize:cropped_size];
|
||||
if (cropped == nil) {
|
||||
return [NSCursor performSelector:fallback];
|
||||
}
|
||||
|
||||
[cropped lockFocus];
|
||||
{
|
||||
const NSRect cropped_rect = NSMakeRect(0, 0, cropped_size.width, cropped_size.height);
|
||||
[image drawInRect:cropped_rect fromRect:cropped_rect operation:operation fraction:1];
|
||||
}
|
||||
[cropped unlockFocus];
|
||||
image = cropped;
|
||||
}
|
||||
|
||||
cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];
|
||||
return cursor;
|
||||
}
|
||||
|
||||
static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSCursor *nscursor = NULL;
|
||||
SDL_Cursor *cursor = NULL;
|
||||
|
||||
switch (id) {
|
||||
case SDL_SYSTEM_CURSOR_DEFAULT:
|
||||
nscursor = [NSCursor arrowCursor];
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_TEXT:
|
||||
nscursor = [NSCursor IBeamCursor];
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_CROSSHAIR:
|
||||
nscursor = [NSCursor crosshairCursor];
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_WAIT: // !!! FIXME: this is more like WAITARROW
|
||||
nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_PROGRESS: // !!! FIXME: this is meant to be animated
|
||||
nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NWSE_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NESW_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_EW_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NS_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_MOVE:
|
||||
nscursor = LoadHiddenSystemCursor(@"move", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NOT_ALLOWED:
|
||||
nscursor = [NSCursor operationNotAllowedCursor];
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_POINTER:
|
||||
nscursor = [NSCursor pointingHandCursor];
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NW_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_N_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_NE_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_E_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_SE_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_S_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_SW_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
|
||||
break;
|
||||
case SDL_SYSTEM_CURSOR_W_RESIZE:
|
||||
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
|
||||
break;
|
||||
default:
|
||||
SDL_assert(!"Unknown system cursor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nscursor) {
|
||||
cursor = SDL_calloc(1, sizeof(*cursor));
|
||||
if (cursor) {
|
||||
// We'll free it later, so retain it here
|
||||
cursor->internal = (void *)CFBridgingRetain(nscursor);
|
||||
}
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_Cursor *Cocoa_CreateDefaultCursor(void)
|
||||
{
|
||||
SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
|
||||
return Cocoa_CreateSystemCursor(id);
|
||||
}
|
||||
|
||||
static void Cocoa_FreeCursor(SDL_Cursor *cursor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
CFBridgingRelease((void *)cursor->internal);
|
||||
SDL_free(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
static bool Cocoa_ShowCursor(SDL_Cursor *cursor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_VideoDevice *device = SDL_GetVideoDevice();
|
||||
SDL_Window *window = (device ? device->windows : NULL);
|
||||
for (; window != NULL; window = window->next) {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
if (data) {
|
||||
[data.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
|
||||
withObject:[data.nswindow contentView]
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_Window *SDL_FindWindowAtPoint(const float x, const float y)
|
||||
{
|
||||
const SDL_FPoint pt = { x, y };
|
||||
SDL_Window *i;
|
||||
for (i = SDL_GetVideoDevice()->windows; i; i = i->next) {
|
||||
const SDL_FRect r = { (float)i->x, (float)i->y, (float)i->w, (float)i->h };
|
||||
if (SDL_PointInRectFloat(&pt, &r)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool Cocoa_WarpMouseGlobal(float x, float y)
|
||||
{
|
||||
CGPoint point;
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
if (mouse->focus) {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)mouse->focus->internal;
|
||||
if ([data.listener isMovingOrFocusClickPending]) {
|
||||
DLog("Postponing warp, window being moved or focused.");
|
||||
[data.listener setPendingMoveX:x Y:y];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
point = CGPointMake(x, y);
|
||||
|
||||
Cocoa_HandleMouseWarp(point.x, point.y);
|
||||
|
||||
CGWarpMouseCursorPosition(point);
|
||||
|
||||
/* CGWarpMouse causes a short delay by default, which is preventable by
|
||||
* Calling this directly after. CGSetLocalEventsSuppressionInterval can also
|
||||
* prevent it, but it's deprecated as macOS 10.6.
|
||||
*/
|
||||
if (!mouse->relative_mode) {
|
||||
CGAssociateMouseAndMouseCursorPosition(YES);
|
||||
}
|
||||
|
||||
/* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
|
||||
* other implementations' APIs. Send what's appropriate.
|
||||
*/
|
||||
if (!mouse->relative_mode) {
|
||||
SDL_Window *win = SDL_FindWindowAtPoint(x, y);
|
||||
SDL_SetMouseFocus(win);
|
||||
if (win) {
|
||||
SDL_assert(win == mouse->focus);
|
||||
SDL_SendMouseMotion(0, win, SDL_GLOBAL_MOUSE_ID, false, x - win->x, y - win->y);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Cocoa_WarpMouse(SDL_Window *window, float x, float y)
|
||||
{
|
||||
return Cocoa_WarpMouseGlobal(window->x + x, window->y + y);
|
||||
}
|
||||
|
||||
static bool Cocoa_SetRelativeMouseMode(bool enabled)
|
||||
{
|
||||
CGError result;
|
||||
|
||||
if (enabled) {
|
||||
SDL_Window *window = SDL_GetKeyboardFocus();
|
||||
if (window) {
|
||||
/* We will re-apply the relative mode when the window finishes being moved,
|
||||
* if it is being moved right now.
|
||||
*/
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
if ([data.listener isMovingOrFocusClickPending]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// make sure the mouse isn't at the corner of the window, as this can confuse things if macOS thinks a window resize is happening on the first click.
|
||||
const CGPoint point = CGPointMake((float)(window->x + (window->w / 2)), (float)(window->y + (window->h / 2)));
|
||||
Cocoa_HandleMouseWarp(point.x, point.y);
|
||||
CGWarpMouseCursorPosition(point);
|
||||
}
|
||||
DLog("Turning on.");
|
||||
result = CGAssociateMouseAndMouseCursorPosition(NO);
|
||||
} else {
|
||||
DLog("Turning off.");
|
||||
result = CGAssociateMouseAndMouseCursorPosition(YES);
|
||||
}
|
||||
if (result != kCGErrorSuccess) {
|
||||
return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
|
||||
}
|
||||
|
||||
/* The hide/unhide calls are redundant most of the time, but they fix
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=2550
|
||||
*/
|
||||
if (enabled) {
|
||||
[NSCursor hide];
|
||||
} else {
|
||||
[NSCursor unhide];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Cocoa_CaptureMouse(SDL_Window *window)
|
||||
{
|
||||
/* our Cocoa event code already tracks the mouse outside the window,
|
||||
so all we have to do here is say "okay" and do what we always do. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static SDL_MouseButtonFlags Cocoa_GetGlobalMouseState(float *x, float *y)
|
||||
{
|
||||
const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
|
||||
const NSPoint cocoaLocation = [NSEvent mouseLocation];
|
||||
SDL_MouseButtonFlags result = 0;
|
||||
|
||||
*x = cocoaLocation.x;
|
||||
*y = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - cocoaLocation.y);
|
||||
|
||||
result |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
|
||||
result |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
|
||||
result |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
|
||||
result |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
|
||||
result |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Cocoa_InitMouse(SDL_VideoDevice *_this)
|
||||
{
|
||||
NSPoint location;
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
SDL_MouseData *internal = (SDL_MouseData *)SDL_calloc(1, sizeof(SDL_MouseData));
|
||||
if (internal == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mouse->internal = internal;
|
||||
mouse->CreateCursor = Cocoa_CreateCursor;
|
||||
mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
|
||||
mouse->ShowCursor = Cocoa_ShowCursor;
|
||||
mouse->FreeCursor = Cocoa_FreeCursor;
|
||||
mouse->WarpMouse = Cocoa_WarpMouse;
|
||||
mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
|
||||
mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
|
||||
mouse->CaptureMouse = Cocoa_CaptureMouse;
|
||||
mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
|
||||
|
||||
SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
|
||||
|
||||
location = [NSEvent mouseLocation];
|
||||
internal->lastMoveX = location.x;
|
||||
internal->lastMoveY = location.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Cocoa_HandleTitleButtonEvent(SDL_VideoDevice *_this, NSEvent *event)
|
||||
{
|
||||
SDL_Window *window;
|
||||
NSWindow *nswindow = [event window];
|
||||
|
||||
/* You might land in this function before SDL_Init if showing a message box.
|
||||
Don't dereference a NULL pointer if that happens. */
|
||||
if (_this == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (window = _this->windows; window; window = window->next) {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
if (data && data.nswindow == nswindow) {
|
||||
switch ([event type]) {
|
||||
case NSEventTypeLeftMouseDown:
|
||||
case NSEventTypeRightMouseDown:
|
||||
case NSEventTypeOtherMouseDown:
|
||||
[data.listener setFocusClickPending:[event buttonNumber]];
|
||||
break;
|
||||
case NSEventTypeLeftMouseUp:
|
||||
case NSEventTypeRightMouseUp:
|
||||
case NSEventTypeOtherMouseUp:
|
||||
[data.listener clearFocusClickPending:[event buttonNumber]];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NSWindow *Cocoa_MouseFocus;
|
||||
|
||||
NSWindow *Cocoa_GetMouseFocus()
|
||||
{
|
||||
return Cocoa_MouseFocus;
|
||||
}
|
||||
|
||||
void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
|
||||
{
|
||||
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
|
||||
SDL_Mouse *mouse;
|
||||
SDL_MouseData *data;
|
||||
NSPoint location;
|
||||
CGFloat lastMoveX, lastMoveY;
|
||||
float deltaX, deltaY;
|
||||
bool seenWarp;
|
||||
|
||||
// All events except NSEventTypeMouseExited can only happen if the window
|
||||
// has mouse focus, so we'll always set the focus even if we happen to miss
|
||||
// NSEventTypeMouseEntered, which apparently happens if the window is
|
||||
// created under the mouse on macOS 12.7
|
||||
NSEventType event_type = [event type];
|
||||
if (event_type == NSEventTypeMouseExited) {
|
||||
Cocoa_MouseFocus = NULL;
|
||||
} else {
|
||||
Cocoa_MouseFocus = [event window];
|
||||
}
|
||||
|
||||
switch (event_type) {
|
||||
case NSEventTypeMouseEntered:
|
||||
case NSEventTypeMouseExited:
|
||||
// Focus is handled above
|
||||
return;
|
||||
|
||||
case NSEventTypeMouseMoved:
|
||||
case NSEventTypeLeftMouseDragged:
|
||||
case NSEventTypeRightMouseDragged:
|
||||
case NSEventTypeOtherMouseDragged:
|
||||
break;
|
||||
|
||||
case NSEventTypeLeftMouseDown:
|
||||
case NSEventTypeLeftMouseUp:
|
||||
case NSEventTypeRightMouseDown:
|
||||
case NSEventTypeRightMouseUp:
|
||||
case NSEventTypeOtherMouseDown:
|
||||
case NSEventTypeOtherMouseUp:
|
||||
if ([event window]) {
|
||||
NSRect windowRect = [[[event window] contentView] frame];
|
||||
if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
|
||||
Cocoa_HandleTitleButtonEvent(_this, event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
// Ignore any other events.
|
||||
return;
|
||||
}
|
||||
|
||||
mouse = SDL_GetMouse();
|
||||
data = (SDL_MouseData *)mouse->internal;
|
||||
if (!data) {
|
||||
return; // can happen when returning from fullscreen Space on shutdown
|
||||
}
|
||||
|
||||
seenWarp = data->seenWarp;
|
||||
data->seenWarp = NO;
|
||||
|
||||
location = [NSEvent mouseLocation];
|
||||
lastMoveX = data->lastMoveX;
|
||||
lastMoveY = data->lastMoveY;
|
||||
data->lastMoveX = location.x;
|
||||
data->lastMoveY = location.y;
|
||||
DLog("Last seen mouse: (%g, %g)", location.x, location.y);
|
||||
|
||||
// Non-relative movement is handled in -[SDL3Cocoa_WindowListener mouseMoved:]
|
||||
if (!mouse->relative_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore events that aren't inside the client area (i.e. title bar.)
|
||||
if ([event window]) {
|
||||
NSRect windowRect = [[[event window] contentView] frame];
|
||||
if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
deltaX = [event deltaX];
|
||||
deltaY = [event deltaY];
|
||||
|
||||
if (seenWarp) {
|
||||
deltaX += (lastMoveX - data->lastWarpX);
|
||||
deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - data->lastWarpY);
|
||||
|
||||
DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
|
||||
}
|
||||
|
||||
SDL_SendMouseMotion(Cocoa_GetEventTimestamp([event timestamp]), mouse->focus, mouseID, true, deltaX, deltaY);
|
||||
}
|
||||
|
||||
void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
|
||||
{
|
||||
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
|
||||
SDL_MouseWheelDirection direction;
|
||||
CGFloat x, y;
|
||||
|
||||
x = -[event deltaX];
|
||||
y = [event deltaY];
|
||||
direction = SDL_MOUSEWHEEL_NORMAL;
|
||||
|
||||
if ([event isDirectionInvertedFromDevice] == YES) {
|
||||
direction = SDL_MOUSEWHEEL_FLIPPED;
|
||||
}
|
||||
|
||||
/* For discrete scroll events from conventional mice, always send a full tick.
|
||||
For continuous scroll events from trackpads, send fractional deltas for smoother scrolling. */
|
||||
if (![event hasPreciseScrollingDeltas]) {
|
||||
if (x > 0) {
|
||||
x = SDL_ceil(x);
|
||||
} else if (x < 0) {
|
||||
x = SDL_floor(x);
|
||||
}
|
||||
if (y > 0) {
|
||||
y = SDL_ceil(y);
|
||||
} else if (y < 0) {
|
||||
y = SDL_floor(y);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SendMouseWheel(Cocoa_GetEventTimestamp([event timestamp]), window, mouseID, x, y, direction);
|
||||
}
|
||||
|
||||
void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
|
||||
{
|
||||
/* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
|
||||
* since it gets included in the next movement event.
|
||||
*/
|
||||
SDL_MouseData *data = (SDL_MouseData *)SDL_GetMouse()->internal;
|
||||
data->lastWarpX = x;
|
||||
data->lastWarpY = y;
|
||||
data->seenWarp = true;
|
||||
|
||||
DLog("(%g, %g)", x, y);
|
||||
}
|
||||
|
||||
void Cocoa_QuitMouse(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
if (mouse) {
|
||||
if (mouse->internal) {
|
||||
SDL_free(mouse->internal);
|
||||
mouse->internal = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
88
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengl.h
vendored
Normal file
88
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengl.h
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoaopengl_h_
|
||||
#define SDL_cocoaopengl_h_
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CVDisplayLink.h>
|
||||
|
||||
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
struct SDL_GLDriverData
|
||||
{
|
||||
int initialized;
|
||||
};
|
||||
|
||||
@interface SDL3OpenGLContext : NSOpenGLContext
|
||||
{
|
||||
SDL_AtomicInt dirty;
|
||||
SDL_Window *window;
|
||||
CVDisplayLinkRef displayLink;
|
||||
@public
|
||||
SDL_Mutex *swapIntervalMutex;
|
||||
@public
|
||||
SDL_Condition *swapIntervalCond;
|
||||
@public
|
||||
SDL_AtomicInt swapIntervalSetting;
|
||||
@public
|
||||
SDL_AtomicInt swapIntervalsPassed;
|
||||
}
|
||||
|
||||
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
|
||||
shareContext:(NSOpenGLContext *)share;
|
||||
- (void)scheduleUpdate;
|
||||
- (void)updateIfNeeded;
|
||||
- (void)movedToNewScreen;
|
||||
- (void)setWindow:(SDL_Window *)window;
|
||||
- (SDL_Window *)window;
|
||||
- (void)explicitUpdate;
|
||||
- (void)cleanup;
|
||||
|
||||
@property(retain, nonatomic) NSOpenGLPixelFormat *openglPixelFormat; // macOS 10.10 has -[NSOpenGLContext pixelFormat] but this handles older OS releases.
|
||||
|
||||
@end
|
||||
|
||||
// OpenGL functions
|
||||
extern bool Cocoa_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
extern SDL_FunctionPointer Cocoa_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
|
||||
extern void Cocoa_GL_UnloadLibrary(SDL_VideoDevice *_this);
|
||||
extern SDL_GLContext Cocoa_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
|
||||
extern bool Cocoa_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
|
||||
extern bool Cocoa_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
|
||||
extern bool Cocoa_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // SDL_VIDEO_OPENGL_CGL
|
||||
|
||||
#endif // SDL_cocoaopengl_h_
|
||||
559
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengl.m
vendored
Normal file
559
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengl.m
vendored
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// NSOpenGL implementation of SDL OpenGL support
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "SDL_cocoaopengl.h"
|
||||
#include "SDL_cocoaopengles.h"
|
||||
|
||||
#include <OpenGL/CGLTypes.h>
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <OpenGL/CGLRenderers.h>
|
||||
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include "../../SDL_hints_c.h"
|
||||
|
||||
#define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"
|
||||
|
||||
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
// _Nullable is available starting Xcode 7
|
||||
#ifdef __has_feature
|
||||
#if __has_feature(nullability)
|
||||
#define HAS_FEATURE_NULLABLE
|
||||
#endif
|
||||
#endif
|
||||
#ifndef HAS_FEATURE_NULLABLE
|
||||
#define _Nullable
|
||||
#endif
|
||||
|
||||
static bool SDL_opengl_async_dispatch = false;
|
||||
|
||||
static void SDLCALL SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, false);
|
||||
}
|
||||
|
||||
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
|
||||
{
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)displayLinkContext;
|
||||
|
||||
// printf("DISPLAY LINK! %u\n", (unsigned int) SDL_GetTicks());
|
||||
const int setting = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
|
||||
if (setting != 0) { // nothing to do if vsync is disabled, don't even lock
|
||||
SDL_LockMutex(nscontext->swapIntervalMutex);
|
||||
SDL_AddAtomicInt(&nscontext->swapIntervalsPassed, 1);
|
||||
SDL_SignalCondition(nscontext->swapIntervalCond);
|
||||
SDL_UnlockMutex(nscontext->swapIntervalMutex);
|
||||
}
|
||||
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
@implementation SDL3OpenGLContext : NSOpenGLContext
|
||||
|
||||
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
|
||||
shareContext:(NSOpenGLContext *)share
|
||||
{
|
||||
self = [super initWithFormat:format shareContext:share];
|
||||
if (self) {
|
||||
self.openglPixelFormat = format;
|
||||
SDL_SetAtomicInt(&self->dirty, 0);
|
||||
self->window = NULL;
|
||||
SDL_SetAtomicInt(&self->swapIntervalSetting, 0);
|
||||
SDL_SetAtomicInt(&self->swapIntervalsPassed, 0);
|
||||
self->swapIntervalCond = SDL_CreateCondition();
|
||||
self->swapIntervalMutex = SDL_CreateMutex();
|
||||
if (!self->swapIntervalCond || !self->swapIntervalMutex) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// !!! FIXME: check return values.
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&self->displayLink);
|
||||
CVDisplayLinkSetOutputCallback(self->displayLink, &DisplayLinkCallback, (__bridge void *_Nullable)self);
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [format CGLPixelFormatObj]);
|
||||
CVDisplayLinkStart(displayLink);
|
||||
}
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)movedToNewScreen
|
||||
{
|
||||
if (self->displayLink) {
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [[self openglPixelFormat] CGLPixelFormatObj]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scheduleUpdate
|
||||
{
|
||||
SDL_AddAtomicInt(&self->dirty, 1);
|
||||
}
|
||||
|
||||
// This should only be called on the thread on which a user is using the context.
|
||||
- (void)updateIfNeeded
|
||||
{
|
||||
const int value = SDL_SetAtomicInt(&self->dirty, 0);
|
||||
if (value > 0) {
|
||||
// We call the real underlying update here, since -[SDL3OpenGLContext update] just calls us.
|
||||
[self explicitUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
// This should only be called on the thread on which a user is using the context.
|
||||
- (void)update
|
||||
{
|
||||
// This ensures that regular 'update' calls clear the atomic dirty flag.
|
||||
[self scheduleUpdate];
|
||||
[self updateIfNeeded];
|
||||
}
|
||||
|
||||
// Updates the drawable for the contexts and manages related state.
|
||||
- (void)setWindow:(SDL_Window *)newWindow
|
||||
{
|
||||
if (self->window) {
|
||||
SDL_CocoaWindowData *oldwindowdata = (__bridge SDL_CocoaWindowData *)self->window->internal;
|
||||
|
||||
// Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too.
|
||||
NSMutableArray *contexts = oldwindowdata.nscontexts;
|
||||
@synchronized(contexts) {
|
||||
[contexts removeObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
self->window = newWindow;
|
||||
|
||||
if (newWindow) {
|
||||
SDL_CocoaWindowData *windowdata = (__bridge SDL_CocoaWindowData *)newWindow->internal;
|
||||
NSView *contentview = windowdata.sdlContentView;
|
||||
|
||||
// Now sign up for scheduled updates for the new window.
|
||||
NSMutableArray *contexts = windowdata.nscontexts;
|
||||
@synchronized(contexts) {
|
||||
[contexts addObject:self];
|
||||
}
|
||||
|
||||
if ([self view] != contentview) {
|
||||
if ([NSThread isMainThread]) {
|
||||
[self setView:contentview];
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[self setView:contentview];
|
||||
});
|
||||
}
|
||||
if (self == [NSOpenGLContext currentContext]) {
|
||||
[self explicitUpdate];
|
||||
} else {
|
||||
[self scheduleUpdate];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ([NSThread isMainThread]) {
|
||||
[self setView:nil];
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:nil]; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (SDL_Window *)window
|
||||
{
|
||||
return self->window;
|
||||
}
|
||||
|
||||
- (void)explicitUpdate
|
||||
{
|
||||
if ([NSThread isMainThread]) {
|
||||
[super update];
|
||||
} else {
|
||||
if (SDL_opengl_async_dispatch) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[super update];
|
||||
});
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[super update];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cleanup
|
||||
{
|
||||
[self setWindow:NULL];
|
||||
|
||||
SDL_RemoveHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
|
||||
if (self->displayLink) {
|
||||
CVDisplayLinkRelease(self->displayLink);
|
||||
self->displayLink = nil;
|
||||
}
|
||||
if (self->swapIntervalCond) {
|
||||
SDL_DestroyCondition(self->swapIntervalCond);
|
||||
self->swapIntervalCond = NULL;
|
||||
}
|
||||
if (self->swapIntervalMutex) {
|
||||
SDL_DestroyMutex(self->swapIntervalMutex);
|
||||
self->swapIntervalMutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool Cocoa_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
|
||||
{
|
||||
// Load the OpenGL library
|
||||
if (path == NULL) {
|
||||
path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY);
|
||||
}
|
||||
if (path == NULL) {
|
||||
path = DEFAULT_OPENGL;
|
||||
}
|
||||
_this->gl_config.dll_handle = SDL_LoadObject(path);
|
||||
if (!_this->gl_config.dll_handle) {
|
||||
return false;
|
||||
}
|
||||
SDL_strlcpy(_this->gl_config.driver_path, path,
|
||||
SDL_arraysize(_this->gl_config.driver_path));
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_FunctionPointer Cocoa_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
|
||||
{
|
||||
return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
|
||||
}
|
||||
|
||||
void Cocoa_GL_UnloadLibrary(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_UnloadObject(_this->gl_config.dll_handle);
|
||||
_this->gl_config.dll_handle = NULL;
|
||||
}
|
||||
|
||||
SDL_GLContext Cocoa_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
|
||||
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
|
||||
NSOpenGLPixelFormatAttribute attr[32];
|
||||
NSOpenGLPixelFormat *fmt;
|
||||
SDL3OpenGLContext *context;
|
||||
SDL_GLContext sdlcontext;
|
||||
NSOpenGLContext *share_context = nil;
|
||||
int i = 0;
|
||||
const char *glversion;
|
||||
int glversion_major;
|
||||
int glversion_minor;
|
||||
NSOpenGLPixelFormatAttribute profile;
|
||||
int interval;
|
||||
int opaque;
|
||||
|
||||
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
// Switch to EGL based functions
|
||||
Cocoa_GL_UnloadLibrary(_this);
|
||||
_this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
|
||||
_this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
|
||||
_this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
|
||||
_this->GL_CreateContext = Cocoa_GLES_CreateContext;
|
||||
_this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
|
||||
_this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
|
||||
_this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
|
||||
_this->GL_SwapWindow = Cocoa_GLES_SwapWindow;
|
||||
_this->GL_DestroyContext = Cocoa_GLES_DestroyContext;
|
||||
|
||||
if (!Cocoa_GLES_LoadLibrary(_this, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return Cocoa_GLES_CreateContext(_this, window);
|
||||
#else
|
||||
SDL_SetError("SDL not configured with EGL support");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
|
||||
|
||||
profile = NSOpenGLProfileVersionLegacy;
|
||||
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
|
||||
profile = NSOpenGLProfileVersion3_2Core;
|
||||
}
|
||||
attr[i++] = NSOpenGLPFAOpenGLProfile;
|
||||
attr[i++] = profile;
|
||||
|
||||
attr[i++] = NSOpenGLPFAColorSize;
|
||||
attr[i++] = SDL_BYTESPERPIXEL(display->current_mode->format) * 8;
|
||||
|
||||
attr[i++] = NSOpenGLPFADepthSize;
|
||||
attr[i++] = _this->gl_config.depth_size;
|
||||
|
||||
if (_this->gl_config.double_buffer) {
|
||||
attr[i++] = NSOpenGLPFADoubleBuffer;
|
||||
}
|
||||
|
||||
if (_this->gl_config.stereo) {
|
||||
attr[i++] = NSOpenGLPFAStereo;
|
||||
}
|
||||
|
||||
if (_this->gl_config.stencil_size) {
|
||||
attr[i++] = NSOpenGLPFAStencilSize;
|
||||
attr[i++] = _this->gl_config.stencil_size;
|
||||
}
|
||||
|
||||
if ((_this->gl_config.accum_red_size +
|
||||
_this->gl_config.accum_green_size +
|
||||
_this->gl_config.accum_blue_size +
|
||||
_this->gl_config.accum_alpha_size) > 0) {
|
||||
attr[i++] = NSOpenGLPFAAccumSize;
|
||||
attr[i++] = _this->gl_config.accum_red_size + _this->gl_config.accum_green_size + _this->gl_config.accum_blue_size + _this->gl_config.accum_alpha_size;
|
||||
}
|
||||
|
||||
if (_this->gl_config.multisamplebuffers) {
|
||||
attr[i++] = NSOpenGLPFASampleBuffers;
|
||||
attr[i++] = _this->gl_config.multisamplebuffers;
|
||||
}
|
||||
|
||||
if (_this->gl_config.multisamplesamples) {
|
||||
attr[i++] = NSOpenGLPFASamples;
|
||||
attr[i++] = _this->gl_config.multisamplesamples;
|
||||
attr[i++] = NSOpenGLPFANoRecovery;
|
||||
}
|
||||
if (_this->gl_config.floatbuffers) {
|
||||
attr[i++] = NSOpenGLPFAColorFloat;
|
||||
}
|
||||
|
||||
if (_this->gl_config.accelerated >= 0) {
|
||||
if (_this->gl_config.accelerated) {
|
||||
attr[i++] = NSOpenGLPFAAccelerated;
|
||||
} else {
|
||||
attr[i++] = NSOpenGLPFARendererID;
|
||||
attr[i++] = kCGLRendererGenericFloatID;
|
||||
}
|
||||
}
|
||||
|
||||
attr[i++] = NSOpenGLPFAScreenMask;
|
||||
attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
|
||||
attr[i] = 0;
|
||||
|
||||
fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
||||
if (fmt == nil) {
|
||||
SDL_SetError("Failed creating OpenGL pixel format");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (_this->gl_config.share_with_current_context) {
|
||||
share_context = (__bridge NSOpenGLContext *)SDL_GL_GetCurrentContext();
|
||||
}
|
||||
|
||||
context = [[SDL3OpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
|
||||
|
||||
if (context == nil) {
|
||||
SDL_SetError("Failed creating OpenGL context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sdlcontext = (SDL_GLContext)CFBridgingRetain(context);
|
||||
|
||||
// vsync is handled separately by synchronizing with a display link.
|
||||
interval = 0;
|
||||
[context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
|
||||
|
||||
opaque = (window->flags & SDL_WINDOW_TRANSPARENT) ? 0 : 1;
|
||||
[context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
|
||||
|
||||
if (!Cocoa_GL_MakeCurrent(_this, window, sdlcontext)) {
|
||||
SDL_GL_DestroyContext(sdlcontext);
|
||||
SDL_SetError("Failed making OpenGL context current");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (_this->gl_config.major_version < 3 &&
|
||||
_this->gl_config.profile_mask == 0 &&
|
||||
_this->gl_config.flags == 0) {
|
||||
// This is a legacy profile, so to match other backends, we're done.
|
||||
} else {
|
||||
const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
|
||||
|
||||
glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum))SDL_GL_GetProcAddress("glGetString");
|
||||
if (!glGetStringFunc) {
|
||||
SDL_GL_DestroyContext(sdlcontext);
|
||||
SDL_SetError("Failed getting OpenGL glGetString entry point");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glversion = (const char *)glGetStringFunc(GL_VERSION);
|
||||
if (glversion == NULL) {
|
||||
SDL_GL_DestroyContext(sdlcontext);
|
||||
SDL_SetError("Failed getting OpenGL context version");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
|
||||
SDL_GL_DestroyContext(sdlcontext);
|
||||
SDL_SetError("Failed parsing OpenGL context version");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((glversion_major < _this->gl_config.major_version) ||
|
||||
((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
|
||||
SDL_GL_DestroyContext(sdlcontext);
|
||||
SDL_SetError("Failed creating OpenGL context at version requested");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* In the future we'll want to do this, but to match other platforms
|
||||
we'll leave the OpenGL version the way it is for now
|
||||
*/
|
||||
// _this->gl_config.major_version = glversion_major;
|
||||
// _this->gl_config.minor_version = glversion_minor;
|
||||
}
|
||||
return sdlcontext;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (context) {
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)context;
|
||||
if ([nscontext window] != window) {
|
||||
[nscontext setWindow:window];
|
||||
[nscontext updateIfNeeded];
|
||||
}
|
||||
[nscontext makeCurrentContext];
|
||||
} else {
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
|
||||
bool result;
|
||||
|
||||
if (nscontext == nil) {
|
||||
result = SDL_SetError("No current OpenGL context");
|
||||
} else {
|
||||
SDL_LockMutex(nscontext->swapIntervalMutex);
|
||||
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
|
||||
SDL_SetAtomicInt(&nscontext->swapIntervalSetting, interval);
|
||||
SDL_UnlockMutex(nscontext->swapIntervalMutex);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
|
||||
if (nscontext) {
|
||||
*interval = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
|
||||
return true;
|
||||
} else {
|
||||
return SDL_SetError("no OpenGL context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
|
||||
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
const int setting = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
|
||||
|
||||
if (setting == 0) {
|
||||
// nothing to do if vsync is disabled, don't even lock
|
||||
} else if (setting < 0) { // late swap tearing
|
||||
SDL_LockMutex(nscontext->swapIntervalMutex);
|
||||
while (SDL_GetAtomicInt(&nscontext->swapIntervalsPassed) == 0) {
|
||||
SDL_WaitCondition(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
|
||||
}
|
||||
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
|
||||
SDL_UnlockMutex(nscontext->swapIntervalMutex);
|
||||
} else {
|
||||
SDL_LockMutex(nscontext->swapIntervalMutex);
|
||||
do { // always wait here so we know we just hit a swap interval.
|
||||
SDL_WaitCondition(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
|
||||
} while ((SDL_GetAtomicInt(&nscontext->swapIntervalsPassed) % setting) != 0);
|
||||
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
|
||||
SDL_UnlockMutex(nscontext->swapIntervalMutex);
|
||||
}
|
||||
|
||||
// { static Uint64 prev = 0; const Uint64 now = SDL_GetTicks(); const unsigned int diff = (unsigned int) (now - prev); prev = now; printf("GLSWAPBUFFERS TICKS %u\n", diff); }
|
||||
|
||||
/* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
|
||||
threads try to swap at the same time, so put a mutex around it. */
|
||||
SDL_LockMutex(videodata.swaplock);
|
||||
[nscontext flushBuffer];
|
||||
[nscontext updateIfNeeded];
|
||||
SDL_UnlockMutex(videodata.swaplock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void DispatchedDestroyContext(SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)context;
|
||||
[nscontext cleanup];
|
||||
CFRelease(context);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
|
||||
{
|
||||
if ([NSThread isMainThread]) {
|
||||
DispatchedDestroyContext(context);
|
||||
} else {
|
||||
if (SDL_opengl_async_dispatch) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
DispatchedDestroyContext(context);
|
||||
});
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
DispatchedDestroyContext(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // SDL_VIDEO_OPENGL_CGL
|
||||
48
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengles.h
vendored
Normal file
48
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengles.h
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoaopengles_h_
|
||||
#define SDL_cocoaopengles_h_
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../SDL_egl_c.h"
|
||||
|
||||
// OpenGLES functions
|
||||
#define Cocoa_GLES_GetAttribute SDL_EGL_GetAttribute
|
||||
#define Cocoa_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
|
||||
#define Cocoa_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
|
||||
#define Cocoa_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
|
||||
#define Cocoa_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
|
||||
|
||||
extern bool Cocoa_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
extern SDL_GLContext Cocoa_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
|
||||
extern bool Cocoa_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
|
||||
extern bool Cocoa_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern SDL_EGLSurface Cocoa_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
#endif // SDL_VIDEO_OPENGL_EGL
|
||||
|
||||
#endif // SDL_cocoaopengles_h_
|
||||
156
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengles.m
vendored
Normal file
156
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoaopengles.m
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_COCOA) && defined(SDL_VIDEO_OPENGL_EGL)
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "SDL_cocoaopengles.h"
|
||||
#include "SDL_cocoaopengl.h"
|
||||
|
||||
// EGL implementation of SDL OpenGL support
|
||||
|
||||
bool Cocoa_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
|
||||
{
|
||||
// If the profile requested is not GL ES, switch over to WIN_GL functions
|
||||
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
Cocoa_GLES_UnloadLibrary(_this);
|
||||
_this->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
|
||||
_this->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
|
||||
_this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
|
||||
_this->GL_CreateContext = Cocoa_GL_CreateContext;
|
||||
_this->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
|
||||
_this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
|
||||
_this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
|
||||
_this->GL_SwapWindow = Cocoa_GL_SwapWindow;
|
||||
_this->GL_DestroyContext = Cocoa_GL_DestroyContext;
|
||||
_this->GL_GetEGLSurface = NULL;
|
||||
return Cocoa_GL_LoadLibrary(_this, path);
|
||||
#else
|
||||
return SDL_SetError("SDL not configured with OpenGL/CGL support");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_this->egl_data == NULL) {
|
||||
return SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_GLContext Cocoa_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_GLContext context;
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
|
||||
// Switch to CGL based functions
|
||||
Cocoa_GLES_UnloadLibrary(_this);
|
||||
_this->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
|
||||
_this->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
|
||||
_this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
|
||||
_this->GL_CreateContext = Cocoa_GL_CreateContext;
|
||||
_this->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
|
||||
_this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
|
||||
_this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
|
||||
_this->GL_SwapWindow = Cocoa_GL_SwapWindow;
|
||||
_this->GL_DestroyContext = Cocoa_GL_DestroyContext;
|
||||
_this->GL_GetEGLSurface = NULL;
|
||||
|
||||
if (!Cocoa_GL_LoadLibrary(_this, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Cocoa_GL_CreateContext(_this, window);
|
||||
}
|
||||
#endif
|
||||
|
||||
context = SDL_EGL_CreateContext(_this, data.egl_surface);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_EGL_DestroyContext(_this, context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cocoa_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return SDL_EGL_SwapBuffers(_this, ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return SDL_EGL_MakeCurrent(_this, window ? ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface : EGL_NO_SURFACE, context);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cocoa_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSView *v;
|
||||
// The current context is lost in here; save it and reset it.
|
||||
SDL_CocoaWindowData *windowdata = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
SDL_Window *current_win = SDL_GL_GetCurrentWindow();
|
||||
SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
|
||||
|
||||
if (_this->egl_data == NULL) {
|
||||
// !!! FIXME: commenting out this assertion is (I think) incorrect; figure out why driver_loaded is wrong for ANGLE instead. --ryan.
|
||||
#if 0 // When hint SDL_HINT_OPENGL_ES_DRIVER is set to "1" (e.g. for ANGLE support), _this->gl_config.driver_loaded can be 1, while the below lines function.
|
||||
SDL_assert(!_this->gl_config.driver_loaded);
|
||||
#endif
|
||||
if (!SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform)) {
|
||||
SDL_EGL_UnloadLibrary(_this);
|
||||
return false;
|
||||
}
|
||||
_this->gl_config.driver_loaded = 1;
|
||||
}
|
||||
|
||||
// Create the GLES window surface
|
||||
v = windowdata.nswindow.contentView;
|
||||
windowdata.egl_surface = SDL_EGL_CreateSurface(_this, window, (__bridge NativeWindowType)[v layer]);
|
||||
|
||||
if (windowdata.egl_surface == EGL_NO_SURFACE) {
|
||||
return SDL_SetError("Could not create GLES window surface");
|
||||
}
|
||||
|
||||
return Cocoa_GLES_MakeCurrent(_this, current_win, current_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_EGLSurface Cocoa_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA && SDL_VIDEO_OPENGL_EGL
|
||||
32
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoapen.h
vendored
Normal file
32
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoapen.h
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoapen_h_
|
||||
#define SDL_cocoapenm_h_
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
extern bool Cocoa_InitPen(SDL_VideoDevice *_this);
|
||||
extern bool Cocoa_HandlePenEvent(SDL_CocoaWindowData *_data, NSEvent *event); // return false if we didn't handle this event.
|
||||
extern void Cocoa_QuitPen(SDL_VideoDevice *_this);
|
||||
|
||||
#endif // SDL_cocoapen_h_
|
||||
178
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoapen.m
vendored
Normal file
178
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoapen.m
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoapen.h"
|
||||
#include "SDL_cocoavideo.h"
|
||||
|
||||
#include "../../events/SDL_pen_c.h"
|
||||
|
||||
bool Cocoa_InitPen(SDL_VideoDevice *_this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct Cocoa_PenHandle
|
||||
{
|
||||
NSUInteger deviceid;
|
||||
NSUInteger toolid;
|
||||
SDL_PenID pen;
|
||||
bool is_eraser;
|
||||
} Cocoa_PenHandle;
|
||||
|
||||
typedef struct FindPenByDeviceAndToolIDData
|
||||
{
|
||||
NSUInteger deviceid;
|
||||
NSUInteger toolid;
|
||||
void *handle;
|
||||
} FindPenByDeviceAndToolIDData;
|
||||
|
||||
static bool FindPenByDeviceAndToolID(void *handle, void *userdata)
|
||||
{
|
||||
const Cocoa_PenHandle *cocoa_handle = (const Cocoa_PenHandle *) handle;
|
||||
FindPenByDeviceAndToolIDData *data = (FindPenByDeviceAndToolIDData *) userdata;
|
||||
|
||||
if (cocoa_handle->deviceid != data->deviceid) {
|
||||
return false;
|
||||
} else if (cocoa_handle->toolid != data->toolid) {
|
||||
return false;
|
||||
}
|
||||
data->handle = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Cocoa_PenHandle *Cocoa_FindPenByDeviceID(NSUInteger deviceid, NSUInteger toolid)
|
||||
{
|
||||
FindPenByDeviceAndToolIDData data;
|
||||
data.deviceid = deviceid;
|
||||
data.toolid = toolid;
|
||||
data.handle = NULL;
|
||||
SDL_FindPenByCallback(FindPenByDeviceAndToolID, &data);
|
||||
return (Cocoa_PenHandle *) data.handle;
|
||||
}
|
||||
|
||||
static void Cocoa_HandlePenProximityEvent(SDL_CocoaWindowData *_data, NSEvent *event)
|
||||
{
|
||||
const NSUInteger devid = [event deviceID];
|
||||
const NSUInteger toolid = [event pointingDeviceID];
|
||||
|
||||
if (event.enteringProximity) { // new pen coming!
|
||||
const NSPointingDeviceType devtype = [event pointingDeviceType];
|
||||
const bool is_eraser = (devtype == NSPointingDeviceTypeEraser);
|
||||
const bool is_pen = (devtype == NSPointingDeviceTypePen);
|
||||
if (!is_eraser && !is_pen) {
|
||||
return; // we ignore other things, which hopefully is right.
|
||||
}
|
||||
|
||||
Cocoa_PenHandle *handle = (Cocoa_PenHandle *) SDL_calloc(1, sizeof (*handle));
|
||||
if (!handle) {
|
||||
return; // oh well.
|
||||
}
|
||||
|
||||
// Cocoa offers almost none of this information as specifics, but can without warning offer any of these specific things.
|
||||
SDL_PenInfo peninfo;
|
||||
SDL_zero(peninfo);
|
||||
peninfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE | (is_eraser ? SDL_PEN_CAPABILITY_ERASER : 0);
|
||||
peninfo.max_tilt = 90.0f;
|
||||
peninfo.num_buttons = 2;
|
||||
peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
|
||||
|
||||
handle->deviceid = devid;
|
||||
handle->toolid = toolid;
|
||||
handle->is_eraser = is_eraser;
|
||||
handle->pen = SDL_AddPenDevice(Cocoa_GetEventTimestamp([event timestamp]), NULL, &peninfo, handle);
|
||||
if (!handle->pen) {
|
||||
SDL_free(handle); // oh well.
|
||||
}
|
||||
} else { // old pen leaving!
|
||||
Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID(devid, toolid);
|
||||
if (handle) {
|
||||
SDL_RemovePenDevice(Cocoa_GetEventTimestamp([event timestamp]), handle->pen);
|
||||
SDL_free(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Cocoa_HandlePenPointEvent(SDL_CocoaWindowData *_data, NSEvent *event)
|
||||
{
|
||||
const Uint64 timestamp = Cocoa_GetEventTimestamp([event timestamp]);
|
||||
Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID([event deviceID], [event pointingDeviceID]);
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SDL_PenID pen = handle->pen;
|
||||
const NSEventButtonMask buttons = [event buttonMask];
|
||||
const NSPoint tilt = [event tilt];
|
||||
const NSPoint point = [event locationInWindow];
|
||||
const bool is_touching = (buttons & NSEventButtonMaskPenTip) != 0;
|
||||
SDL_Window *window = _data.window;
|
||||
|
||||
SDL_SendPenTouch(timestamp, pen, window, handle->is_eraser, is_touching);
|
||||
SDL_SendPenMotion(timestamp, pen, window, (float) point.x, (float) (window->h - point.y));
|
||||
SDL_SendPenButton(timestamp, pen, window, 1, ((buttons & NSEventButtonMaskPenLowerSide) != 0));
|
||||
SDL_SendPenButton(timestamp, pen, window, 2, ((buttons & NSEventButtonMaskPenUpperSide) != 0));
|
||||
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_PRESSURE, [event pressure]);
|
||||
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_ROTATION, [event rotation]);
|
||||
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_XTILT, ((float) tilt.x) * 90.0f);
|
||||
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_YTILT, ((float) -tilt.y) * 90.0f);
|
||||
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_TANGENTIAL_PRESSURE, event.tangentialPressure);
|
||||
}
|
||||
|
||||
bool Cocoa_HandlePenEvent(SDL_CocoaWindowData *_data, NSEvent *event)
|
||||
{
|
||||
NSEventType type = [event type];
|
||||
|
||||
if ((type != NSEventTypeTabletPoint) && (type != NSEventTypeTabletProximity)) {
|
||||
const NSEventSubtype subtype = [event subtype];
|
||||
if (subtype == NSEventSubtypeTabletPoint) {
|
||||
type = NSEventTypeTabletPoint;
|
||||
} else if (subtype == NSEventSubtypeTabletProximity) {
|
||||
type = NSEventTypeTabletProximity;
|
||||
} else {
|
||||
return false; // not a tablet event.
|
||||
}
|
||||
}
|
||||
|
||||
if (type == NSEventTypeTabletPoint) {
|
||||
Cocoa_HandlePenPointEvent(_data, event);
|
||||
} else if (type == NSEventTypeTabletProximity) {
|
||||
Cocoa_HandlePenProximityEvent(_data, event);
|
||||
} else {
|
||||
return false; // not a tablet event.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Cocoa_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
|
||||
{
|
||||
SDL_free(handle);
|
||||
}
|
||||
|
||||
void Cocoa_QuitPen(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_RemoveAllPenDevices(Cocoa_FreePenHandle, NULL);
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
28
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoashape.h
vendored
Normal file
28
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoashape.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoashape_h_
|
||||
#define SDL_cocoashape_h_
|
||||
|
||||
extern bool Cocoa_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
|
||||
|
||||
#endif // SDL_cocoashape_h_
|
||||
54
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoashape.m
vendored
Normal file
54
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoashape.m
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "SDL_cocoashape.h"
|
||||
|
||||
|
||||
bool Cocoa_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape)
|
||||
{
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
BOOL ignoresMouseEvents = NO;
|
||||
|
||||
if (shape) {
|
||||
SDL_FPoint point;
|
||||
SDL_GetGlobalMouseState(&point.x, &point.y);
|
||||
point.x -= window->x;
|
||||
point.y -= window->y;
|
||||
if (point.x >= 0.0f && point.x < window->w &&
|
||||
point.y >= 0.0f && point.y < window->h) {
|
||||
int x = (int)SDL_roundf((point.x / (window->w - 1)) * (shape->w - 1));
|
||||
int y = (int)SDL_roundf((point.y / (window->h - 1)) * (shape->h - 1));
|
||||
Uint8 a;
|
||||
|
||||
if (!SDL_ReadSurfacePixel(shape, x, y, NULL, NULL, NULL, &a) || a == SDL_ALPHA_TRANSPARENT) {
|
||||
ignoresMouseEvents = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
data.nswindow.ignoresMouseEvents = ignoresMouseEvents;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
71
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavideo.h
vendored
Normal file
71
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavideo.h
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoavideo_h_
|
||||
#define SDL_cocoavideo_h_
|
||||
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#include "SDL_cocoaclipboard.h"
|
||||
#include "SDL_cocoaevents.h"
|
||||
#include "SDL_cocoakeyboard.h"
|
||||
#include "SDL_cocoamodes.h"
|
||||
#include "SDL_cocoamouse.h"
|
||||
#include "SDL_cocoaopengl.h"
|
||||
#include "SDL_cocoawindow.h"
|
||||
#include "SDL_cocoapen.h"
|
||||
|
||||
// Private display data
|
||||
|
||||
@class SDL3TranslatorResponder;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OptionAsAltNone,
|
||||
OptionAsAltOnlyLeft,
|
||||
OptionAsAltOnlyRight,
|
||||
OptionAsAltBoth,
|
||||
} OptionAsAlt;
|
||||
|
||||
@interface SDL_CocoaVideoData : NSObject
|
||||
@property(nonatomic) int allow_spaces;
|
||||
@property(nonatomic) int trackpad_is_touch_only;
|
||||
@property(nonatomic) unsigned int modifierFlags;
|
||||
@property(nonatomic) void *key_layout;
|
||||
@property(nonatomic) SDL3TranslatorResponder *fieldEdit;
|
||||
@property(nonatomic) NSInteger clipboard_count;
|
||||
@property(nonatomic) IOPMAssertionID screensaver_assertion;
|
||||
@property(nonatomic) SDL_Mutex *swaplock;
|
||||
@property(nonatomic) OptionAsAlt option_as_alt;
|
||||
@end
|
||||
|
||||
// Utility functions
|
||||
extern SDL_SystemTheme Cocoa_GetSystemTheme(void);
|
||||
extern NSImage *Cocoa_CreateImage(SDL_Surface *surface);
|
||||
|
||||
#endif // SDL_cocoavideo_h_
|
||||
337
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavideo.m
vendored
Normal file
337
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavideo.m
vendored
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error SDL must be built with Objective-C ARC (automatic reference counting) enabled
|
||||
#endif
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "SDL_cocoavulkan.h"
|
||||
#include "SDL_cocoametalview.h"
|
||||
#include "SDL_cocoaopengles.h"
|
||||
#include "SDL_cocoamessagebox.h"
|
||||
#include "SDL_cocoashape.h"
|
||||
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
|
||||
@implementation SDL_CocoaVideoData
|
||||
|
||||
@end
|
||||
|
||||
// Initialization/Query functions
|
||||
static bool Cocoa_VideoInit(SDL_VideoDevice *_this);
|
||||
static void Cocoa_VideoQuit(SDL_VideoDevice *_this);
|
||||
|
||||
// Cocoa driver bootstrap functions
|
||||
|
||||
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (device->wakeup_lock) {
|
||||
SDL_DestroyMutex(device->wakeup_lock);
|
||||
}
|
||||
CFBridgingRelease(device->internal);
|
||||
SDL_free(device);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_VideoDevice *Cocoa_CreateDevice(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_VideoDevice *device;
|
||||
SDL_CocoaVideoData *data;
|
||||
|
||||
if (![NSThread isMainThread]) {
|
||||
return NULL; // this doesn't SDL_SetError() because SDL_VideoInit is just going to overwrite it.
|
||||
}
|
||||
|
||||
Cocoa_RegisterApp();
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
|
||||
if (device) {
|
||||
data = [[SDL_CocoaVideoData alloc] init];
|
||||
} else {
|
||||
data = nil;
|
||||
}
|
||||
if (!data) {
|
||||
SDL_free(device);
|
||||
return NULL;
|
||||
}
|
||||
device->internal = (SDL_VideoData *)CFBridgingRetain(data);
|
||||
device->wakeup_lock = SDL_CreateMutex();
|
||||
device->system_theme = Cocoa_GetSystemTheme();
|
||||
|
||||
// Set the function pointers
|
||||
device->VideoInit = Cocoa_VideoInit;
|
||||
device->VideoQuit = Cocoa_VideoQuit;
|
||||
device->GetDisplayBounds = Cocoa_GetDisplayBounds;
|
||||
device->GetDisplayUsableBounds = Cocoa_GetDisplayUsableBounds;
|
||||
device->GetDisplayModes = Cocoa_GetDisplayModes;
|
||||
device->SetDisplayMode = Cocoa_SetDisplayMode;
|
||||
device->PumpEvents = Cocoa_PumpEvents;
|
||||
device->WaitEventTimeout = Cocoa_WaitEventTimeout;
|
||||
device->SendWakeupEvent = Cocoa_SendWakeupEvent;
|
||||
device->SuspendScreenSaver = Cocoa_SuspendScreenSaver;
|
||||
|
||||
device->CreateSDLWindow = Cocoa_CreateWindow;
|
||||
device->SetWindowTitle = Cocoa_SetWindowTitle;
|
||||
device->SetWindowIcon = Cocoa_SetWindowIcon;
|
||||
device->SetWindowPosition = Cocoa_SetWindowPosition;
|
||||
device->SetWindowSize = Cocoa_SetWindowSize;
|
||||
device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize;
|
||||
device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize;
|
||||
device->SetWindowAspectRatio = Cocoa_SetWindowAspectRatio;
|
||||
device->SetWindowOpacity = Cocoa_SetWindowOpacity;
|
||||
device->GetWindowSizeInPixels = Cocoa_GetWindowSizeInPixels;
|
||||
device->ShowWindow = Cocoa_ShowWindow;
|
||||
device->HideWindow = Cocoa_HideWindow;
|
||||
device->RaiseWindow = Cocoa_RaiseWindow;
|
||||
device->MaximizeWindow = Cocoa_MaximizeWindow;
|
||||
device->MinimizeWindow = Cocoa_MinimizeWindow;
|
||||
device->RestoreWindow = Cocoa_RestoreWindow;
|
||||
device->SetWindowBordered = Cocoa_SetWindowBordered;
|
||||
device->SetWindowResizable = Cocoa_SetWindowResizable;
|
||||
device->SetWindowAlwaysOnTop = Cocoa_SetWindowAlwaysOnTop;
|
||||
device->SetWindowFullscreen = Cocoa_SetWindowFullscreen;
|
||||
device->GetWindowICCProfile = Cocoa_GetWindowICCProfile;
|
||||
device->GetDisplayForWindow = Cocoa_GetDisplayForWindow;
|
||||
device->SetWindowMouseRect = Cocoa_SetWindowMouseRect;
|
||||
device->SetWindowMouseGrab = Cocoa_SetWindowMouseGrab;
|
||||
device->SetWindowKeyboardGrab = Cocoa_SetWindowKeyboardGrab;
|
||||
device->DestroyWindow = Cocoa_DestroyWindow;
|
||||
device->SetWindowHitTest = Cocoa_SetWindowHitTest;
|
||||
device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
|
||||
device->UpdateWindowShape = Cocoa_UpdateWindowShape;
|
||||
device->FlashWindow = Cocoa_FlashWindow;
|
||||
device->SetWindowFocusable = Cocoa_SetWindowFocusable;
|
||||
device->SetWindowParent = Cocoa_SetWindowParent;
|
||||
device->SetWindowModal = Cocoa_SetWindowModal;
|
||||
device->SyncWindow = Cocoa_SyncWindow;
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
device->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
|
||||
device->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
|
||||
device->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
|
||||
device->GL_CreateContext = Cocoa_GL_CreateContext;
|
||||
device->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
|
||||
device->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
|
||||
device->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
|
||||
device->GL_SwapWindow = Cocoa_GL_SwapWindow;
|
||||
device->GL_DestroyContext = Cocoa_GL_DestroyContext;
|
||||
device->GL_GetEGLSurface = NULL;
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
|
||||
#endif
|
||||
device->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
|
||||
device->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
|
||||
device->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
|
||||
device->GL_CreateContext = Cocoa_GLES_CreateContext;
|
||||
device->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
|
||||
device->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
|
||||
device->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
|
||||
device->GL_SwapWindow = Cocoa_GLES_SwapWindow;
|
||||
device->GL_DestroyContext = Cocoa_GLES_DestroyContext;
|
||||
device->GL_GetEGLSurface = Cocoa_GLES_GetEGLSurface;
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_VULKAN
|
||||
device->Vulkan_LoadLibrary = Cocoa_Vulkan_LoadLibrary;
|
||||
device->Vulkan_UnloadLibrary = Cocoa_Vulkan_UnloadLibrary;
|
||||
device->Vulkan_GetInstanceExtensions = Cocoa_Vulkan_GetInstanceExtensions;
|
||||
device->Vulkan_CreateSurface = Cocoa_Vulkan_CreateSurface;
|
||||
device->Vulkan_DestroySurface = Cocoa_Vulkan_DestroySurface;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_METAL
|
||||
device->Metal_CreateView = Cocoa_Metal_CreateView;
|
||||
device->Metal_DestroyView = Cocoa_Metal_DestroyView;
|
||||
device->Metal_GetLayer = Cocoa_Metal_GetLayer;
|
||||
#endif
|
||||
|
||||
device->StartTextInput = Cocoa_StartTextInput;
|
||||
device->StopTextInput = Cocoa_StopTextInput;
|
||||
device->UpdateTextInputArea = Cocoa_UpdateTextInputArea;
|
||||
|
||||
device->SetClipboardData = Cocoa_SetClipboardData;
|
||||
device->GetClipboardData = Cocoa_GetClipboardData;
|
||||
device->HasClipboardData = Cocoa_HasClipboardData;
|
||||
|
||||
device->free = Cocoa_DeleteDevice;
|
||||
|
||||
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
|
||||
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
VideoBootStrap COCOA_bootstrap = {
|
||||
"cocoa", "SDL Cocoa video driver",
|
||||
Cocoa_CreateDevice,
|
||||
Cocoa_ShowMessageBox,
|
||||
false
|
||||
};
|
||||
|
||||
static bool Cocoa_VideoInit(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
|
||||
Cocoa_InitModes(_this);
|
||||
Cocoa_InitKeyboard(_this);
|
||||
if (!Cocoa_InitMouse(_this)) {
|
||||
return false;
|
||||
}
|
||||
if (!Cocoa_InitPen(_this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assume we have a mouse and keyboard
|
||||
// We could use GCMouse and GCKeyboard if we needed to, as is done in SDL_uikitevents.m
|
||||
SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false);
|
||||
SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false);
|
||||
|
||||
data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, true);
|
||||
data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, false);
|
||||
SDL_AddHintCallback(SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY, Cocoa_MenuVisibilityCallback, NULL);
|
||||
|
||||
data.swaplock = SDL_CreateMutex();
|
||||
if (!data.swaplock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Cocoa_VideoQuit(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
|
||||
Cocoa_QuitModes(_this);
|
||||
Cocoa_QuitKeyboard(_this);
|
||||
Cocoa_QuitMouse(_this);
|
||||
Cocoa_QuitPen(_this);
|
||||
SDL_DestroyMutex(data.swaplock);
|
||||
data.swaplock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// This function assumes that it's called from within an autorelease pool
|
||||
SDL_SystemTheme Cocoa_GetSystemTheme(void)
|
||||
{
|
||||
if (@available(macOS 10.14, *)) {
|
||||
NSAppearance* appearance = [[NSApplication sharedApplication] effectiveAppearance];
|
||||
|
||||
if ([appearance.name containsString: @"Dark"]) {
|
||||
return SDL_SYSTEM_THEME_DARK;
|
||||
}
|
||||
}
|
||||
return SDL_SYSTEM_THEME_LIGHT;
|
||||
}
|
||||
|
||||
// This function assumes that it's called from within an autorelease pool
|
||||
NSImage *Cocoa_CreateImage(SDL_Surface *surface)
|
||||
{
|
||||
NSImage *img;
|
||||
|
||||
img = [[NSImage alloc] initWithSize:NSMakeSize(surface->w, surface->h)];
|
||||
if (img == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL);
|
||||
if (!images) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (int i = 0; images[i]; ++i) {
|
||||
SDL_Surface *converted = SDL_ConvertSurface(images[i], SDL_PIXELFORMAT_RGBA32);
|
||||
if (!converted) {
|
||||
SDL_free(images);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Premultiply the alpha channel
|
||||
SDL_PremultiplySurfaceAlpha(converted, false);
|
||||
|
||||
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:converted->w
|
||||
pixelsHigh:converted->h
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bytesPerRow:converted->pitch
|
||||
bitsPerPixel:SDL_BITSPERPIXEL(converted->format)];
|
||||
if (imgrep == nil) {
|
||||
SDL_free(images);
|
||||
SDL_DestroySurface(converted);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Copy the pixels
|
||||
Uint8 *pixels = [imgrep bitmapData];
|
||||
SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
|
||||
SDL_DestroySurface(converted);
|
||||
|
||||
// Add the image representation
|
||||
[img addRepresentation:imgrep];
|
||||
}
|
||||
SDL_free(images);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
/*
|
||||
* macOS log support.
|
||||
*
|
||||
* This doesn't really have anything to do with the interfaces of the SDL video
|
||||
* subsystem, but we need to stuff this into an Objective-C source code file.
|
||||
*
|
||||
* NOTE: This is copypasted in src/video/uikit/SDL_uikitvideo.m! Be sure both
|
||||
* versions remain identical!
|
||||
*/
|
||||
|
||||
void SDL_NSLog(const char *prefix, const char *text)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSString *nsText = [NSString stringWithUTF8String:text];
|
||||
if (prefix && *prefix) {
|
||||
NSString *nsPrefix = [NSString stringWithUTF8String:prefix];
|
||||
NSLog(@"%@%@", nsPrefix, nsText);
|
||||
} else {
|
||||
NSLog(@"%@", nsText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_COCOA
|
||||
52
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavulkan.h
vendored
Normal file
52
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavulkan.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
|
||||
* SDL_x11vulkan.h.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoavulkan_h_
|
||||
#define SDL_cocoavulkan_h_
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||
|
||||
extern bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
extern void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
|
||||
extern char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
|
||||
extern bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
const struct VkAllocationCallbacks *allocator,
|
||||
VkSurfaceKHR *surface);
|
||||
extern void Cocoa_Vulkan_DestroySurface(SDL_VideoDevice *_this,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR surface,
|
||||
const struct VkAllocationCallbacks *allocator);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SDL_cocoavulkan_h_
|
||||
304
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavulkan.m
vendored
Normal file
304
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoavulkan.m
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
|
||||
* SDL_x11vulkan.c.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "SDL_cocoawindow.h"
|
||||
|
||||
#include "SDL_cocoametalview.h"
|
||||
#include "SDL_cocoavulkan.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
const char *defaultPaths[] = {
|
||||
"vulkan.framework/vulkan",
|
||||
"libvulkan.1.dylib",
|
||||
"libvulkan.dylib",
|
||||
"MoltenVK.framework/MoltenVK",
|
||||
"libMoltenVK.dylib"
|
||||
};
|
||||
|
||||
// Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF.
|
||||
#define DEFAULT_HANDLE RTLD_DEFAULT
|
||||
|
||||
bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
|
||||
{
|
||||
VkExtensionProperties *extensions = NULL;
|
||||
Uint32 extensionCount = 0;
|
||||
bool hasSurfaceExtension = false;
|
||||
bool hasMetalSurfaceExtension = false;
|
||||
bool hasMacOSSurfaceExtension = false;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
|
||||
|
||||
if (_this->vulkan_config.loader_handle) {
|
||||
return SDL_SetError("Vulkan Portability library is already loaded.");
|
||||
}
|
||||
|
||||
// Load the Vulkan loader library
|
||||
if (!path) {
|
||||
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
// Handle the case where Vulkan Portability is linked statically.
|
||||
vkGetInstanceProcAddr =
|
||||
(PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE,
|
||||
"vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
if (vkGetInstanceProcAddr) {
|
||||
_this->vulkan_config.loader_handle = DEFAULT_HANDLE;
|
||||
} else {
|
||||
const char **paths;
|
||||
const char *foundPath = NULL;
|
||||
int numPaths;
|
||||
int i;
|
||||
|
||||
if (path) {
|
||||
paths = &path;
|
||||
numPaths = 1;
|
||||
} else {
|
||||
/* Look for framework or .dylib packaged with the application
|
||||
* instead. */
|
||||
paths = defaultPaths;
|
||||
numPaths = SDL_arraysize(defaultPaths);
|
||||
}
|
||||
|
||||
for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
|
||||
foundPath = paths[i];
|
||||
_this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
|
||||
}
|
||||
|
||||
if (_this->vulkan_config.loader_handle == NULL) {
|
||||
return SDL_SetError("Failed to load Vulkan Portability library");
|
||||
}
|
||||
|
||||
SDL_strlcpy(_this->vulkan_config.loader_path, foundPath,
|
||||
SDL_arraysize(_this->vulkan_config.loader_path));
|
||||
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
|
||||
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
if (!vkGetInstanceProcAddr) {
|
||||
SDL_SetError("Failed to find %s in either executable or %s: %s",
|
||||
"vkGetInstanceProcAddr",
|
||||
_this->vulkan_config.loader_path,
|
||||
(const char *)dlerror());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
|
||||
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
|
||||
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
|
||||
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
|
||||
goto fail;
|
||||
}
|
||||
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
|
||||
(PFN_vkEnumerateInstanceExtensionProperties)
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
|
||||
&extensionCount);
|
||||
if (!extensions) {
|
||||
goto fail;
|
||||
}
|
||||
for (Uint32 i = 0; i < extensionCount; i++) {
|
||||
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasSurfaceExtension = true;
|
||||
} else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasMetalSurfaceExtension = true;
|
||||
} else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasMacOSSurfaceExtension = true;
|
||||
}
|
||||
}
|
||||
SDL_free(extensions);
|
||||
if (!hasSurfaceExtension) {
|
||||
SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
|
||||
goto fail;
|
||||
} else if (!hasMetalSurfaceExtension && !hasMacOSSurfaceExtension) {
|
||||
SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME " extensions");
|
||||
goto fail;
|
||||
}
|
||||
return true;
|
||||
|
||||
fail:
|
||||
SDL_UnloadObject(_this->vulkan_config.loader_handle);
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
||||
{
|
||||
if (_this->vulkan_config.loader_handle) {
|
||||
if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) {
|
||||
SDL_UnloadObject(_this->vulkan_config.loader_handle);
|
||||
}
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
Uint32 *count)
|
||||
{
|
||||
static const char *const extensionsForCocoa[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
|
||||
};
|
||||
if(count) {
|
||||
*count = SDL_arraysize(extensionsForCocoa);
|
||||
}
|
||||
return extensionsForCocoa;
|
||||
}
|
||||
|
||||
static bool Cocoa_Vulkan_CreateSurfaceViaMetalView(SDL_VideoDevice *_this,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
const struct VkAllocationCallbacks *allocator,
|
||||
VkSurfaceKHR *surface,
|
||||
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT,
|
||||
PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK)
|
||||
{
|
||||
VkResult rc;
|
||||
SDL_MetalView metalview = Cocoa_Metal_CreateView(_this, window);
|
||||
if (metalview == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vkCreateMetalSurfaceEXT) {
|
||||
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pLayer = (__bridge const CAMetalLayer *)
|
||||
Cocoa_Metal_GetLayer(_this, metalview);
|
||||
rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
|
||||
if (rc != VK_SUCCESS) {
|
||||
Cocoa_Metal_DestroyView(_this, metalview);
|
||||
return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
|
||||
}
|
||||
} else {
|
||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pView = (const void *)metalview;
|
||||
rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
|
||||
NULL, surface);
|
||||
if (rc != VK_SUCCESS) {
|
||||
Cocoa_Metal_DestroyView(_this, metalview);
|
||||
return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
|
||||
}
|
||||
}
|
||||
|
||||
/* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call
|
||||
* Metal_DestroyView from. Right now the metal view's ref count is +2 (one
|
||||
* from returning a new view object in CreateView, and one because it's
|
||||
* a subview of the window.) If we release the view here to make it +1, it
|
||||
* will be destroyed when the window is destroyed.
|
||||
*
|
||||
* TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough
|
||||
* knowledge of Metal can proceed. */
|
||||
CFBridgingRelease(metalview);
|
||||
|
||||
return true; // success!
|
||||
}
|
||||
|
||||
bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
const struct VkAllocationCallbacks *allocator,
|
||||
VkSurfaceKHR *surface)
|
||||
{
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
|
||||
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
|
||||
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
|
||||
(PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
|
||||
instance,
|
||||
"vkCreateMetalSurfaceEXT");
|
||||
PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK =
|
||||
(PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr(
|
||||
instance,
|
||||
"vkCreateMacOSSurfaceMVK");
|
||||
VkResult rc;
|
||||
|
||||
if (!_this->vulkan_config.loader_handle) {
|
||||
return SDL_SetError("Vulkan is not loaded");
|
||||
}
|
||||
|
||||
if (!vkCreateMetalSurfaceEXT && !vkCreateMacOSSurfaceMVK) {
|
||||
return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME
|
||||
" extensions are not enabled in the Vulkan instance.");
|
||||
}
|
||||
|
||||
if (window->flags & SDL_WINDOW_EXTERNAL) {
|
||||
@autoreleasepool {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
|
||||
if (![data.sdlContentView.layer isKindOfClass:[CAMetalLayer class]]) {
|
||||
[data.sdlContentView setLayer:[CAMetalLayer layer]];
|
||||
}
|
||||
|
||||
if (vkCreateMetalSurfaceEXT) {
|
||||
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pLayer = (CAMetalLayer *)data.sdlContentView.layer;
|
||||
rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
|
||||
if (rc != VK_SUCCESS) {
|
||||
return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
|
||||
}
|
||||
} else {
|
||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pView = (__bridge const void *)data.sdlContentView;
|
||||
rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
|
||||
allocator, surface);
|
||||
if (rc != VK_SUCCESS) {
|
||||
return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Cocoa_Vulkan_CreateSurfaceViaMetalView(_this, window, instance, allocator, surface, vkCreateMetalSurfaceEXT, vkCreateMacOSSurfaceMVK);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cocoa_Vulkan_DestroySurface(SDL_VideoDevice *_this,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR surface,
|
||||
const struct VkAllocationCallbacks *allocator)
|
||||
{
|
||||
if (_this->vulkan_config.loader_handle) {
|
||||
SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
|
||||
// TODO: Add CFBridgingRelease(metalview) here perhaps?
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
199
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoawindow.h
vendored
Normal file
199
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoawindow.h
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_cocoawindow_h_
|
||||
#define SDL_cocoawindow_h_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
#include "../SDL_egl_c.h"
|
||||
#endif
|
||||
|
||||
#define SDL_METALVIEW_TAG 255
|
||||
|
||||
@class SDL_CocoaWindowData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PENDING_OPERATION_NONE = 0x00,
|
||||
PENDING_OPERATION_ENTER_FULLSCREEN = 0x01,
|
||||
PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02,
|
||||
PENDING_OPERATION_MINIMIZE = 0x04,
|
||||
PENDING_OPERATION_ZOOM = 0x08
|
||||
} PendingWindowOperation;
|
||||
|
||||
@interface SDL3Cocoa_WindowListener : NSResponder <NSWindowDelegate>
|
||||
{
|
||||
/* SDL_CocoaWindowData owns this Listener and has a strong reference to it.
|
||||
* To avoid reference cycles, we could have either a weak or an
|
||||
* unretained ref to the WindowData. */
|
||||
__weak SDL_CocoaWindowData *_data;
|
||||
BOOL observingVisible;
|
||||
BOOL wasCtrlLeft;
|
||||
BOOL wasVisible;
|
||||
BOOL isFullscreenSpace;
|
||||
BOOL inFullscreenTransition;
|
||||
PendingWindowOperation pendingWindowOperation;
|
||||
BOOL isMoving;
|
||||
BOOL isMiniaturizing;
|
||||
NSInteger focusClickPending;
|
||||
float pendingWindowWarpX, pendingWindowWarpY;
|
||||
BOOL isDragAreaRunning;
|
||||
NSTimer *liveResizeTimer;
|
||||
}
|
||||
|
||||
- (BOOL)isTouchFromTrackpad:(NSEvent *)theEvent;
|
||||
- (void)listen:(SDL_CocoaWindowData *)data;
|
||||
- (void)pauseVisibleObservation;
|
||||
- (void)resumeVisibleObservation;
|
||||
- (BOOL)setFullscreenSpace:(BOOL)state;
|
||||
- (BOOL)isInFullscreenSpace;
|
||||
- (BOOL)isInFullscreenSpaceTransition;
|
||||
- (void)addPendingWindowOperation:(PendingWindowOperation)operation;
|
||||
- (void)close;
|
||||
|
||||
- (BOOL)isMoving;
|
||||
- (BOOL)isMovingOrFocusClickPending;
|
||||
- (void)setFocusClickPending:(NSInteger)button;
|
||||
- (void)clearFocusClickPending:(NSInteger)button;
|
||||
- (void)updateIgnoreMouseState:(NSEvent *)theEvent;
|
||||
- (void)setPendingMoveX:(float)x Y:(float)y;
|
||||
- (void)windowDidFinishMoving;
|
||||
- (void)onMovingOrFocusClickPendingStateCleared;
|
||||
|
||||
// Window delegate functionality
|
||||
- (BOOL)windowShouldClose:(id)sender;
|
||||
- (void)windowDidExpose:(NSNotification *)aNotification;
|
||||
- (void)windowDidChangeOcclusionState:(NSNotification *)aNotification;
|
||||
- (void)windowWillStartLiveResize:(NSNotification *)aNotification;
|
||||
- (void)windowDidEndLiveResize:(NSNotification *)aNotification;
|
||||
- (void)windowDidMove:(NSNotification *)aNotification;
|
||||
- (void)windowDidResize:(NSNotification *)aNotification;
|
||||
- (void)windowDidMiniaturize:(NSNotification *)aNotification;
|
||||
- (void)windowDidDeminiaturize:(NSNotification *)aNotification;
|
||||
- (void)windowDidBecomeKey:(NSNotification *)aNotification;
|
||||
- (void)windowDidResignKey:(NSNotification *)aNotification;
|
||||
- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification;
|
||||
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification;
|
||||
- (void)windowDidChangeScreen:(NSNotification *)aNotification;
|
||||
- (void)windowWillEnterFullScreen:(NSNotification *)aNotification;
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification;
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)aNotification;
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)aNotification;
|
||||
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
|
||||
|
||||
// See if event is in a drag area, toggle on window dragging.
|
||||
- (void)updateHitTest;
|
||||
- (BOOL)processHitTest:(NSEvent *)theEvent;
|
||||
|
||||
// Window event handling
|
||||
- (void)mouseDown:(NSEvent *)theEvent;
|
||||
- (void)rightMouseDown:(NSEvent *)theEvent;
|
||||
- (void)otherMouseDown:(NSEvent *)theEvent;
|
||||
- (void)mouseUp:(NSEvent *)theEvent;
|
||||
- (void)rightMouseUp:(NSEvent *)theEvent;
|
||||
- (void)otherMouseUp:(NSEvent *)theEvent;
|
||||
- (void)mouseMoved:(NSEvent *)theEvent;
|
||||
- (void)mouseDragged:(NSEvent *)theEvent;
|
||||
- (void)rightMouseDragged:(NSEvent *)theEvent;
|
||||
- (void)otherMouseDragged:(NSEvent *)theEvent;
|
||||
- (void)scrollWheel:(NSEvent *)theEvent;
|
||||
- (void)touchesBeganWithEvent:(NSEvent *)theEvent;
|
||||
- (void)touchesMovedWithEvent:(NSEvent *)theEvent;
|
||||
- (void)touchesEndedWithEvent:(NSEvent *)theEvent;
|
||||
- (void)touchesCancelledWithEvent:(NSEvent *)theEvent;
|
||||
|
||||
// Touch event handling
|
||||
- (void)handleTouches:(NSTouchPhase)phase withEvent:(NSEvent *)theEvent;
|
||||
|
||||
// Tablet event handling (but these also come through on mouse events sometimes!)
|
||||
- (void)tabletProximity:(NSEvent *)theEvent;
|
||||
- (void)tabletPoint:(NSEvent *)theEvent;
|
||||
|
||||
@end
|
||||
/* *INDENT-ON* */
|
||||
|
||||
@class SDL3OpenGLContext;
|
||||
@class SDL_CocoaVideoData;
|
||||
|
||||
@interface SDL_CocoaWindowData : NSObject
|
||||
@property(nonatomic) SDL_Window *window;
|
||||
@property(nonatomic) NSWindow *nswindow;
|
||||
@property(nonatomic) NSView *sdlContentView;
|
||||
@property(nonatomic) NSMutableArray *nscontexts;
|
||||
@property(nonatomic) BOOL in_blocking_transition;
|
||||
@property(nonatomic) BOOL fullscreen_space_requested;
|
||||
@property(nonatomic) BOOL was_zoomed;
|
||||
@property(nonatomic) NSInteger window_number;
|
||||
@property(nonatomic) NSInteger flash_request;
|
||||
@property(nonatomic) SDL_Window *keyboard_focus;
|
||||
@property(nonatomic) SDL3Cocoa_WindowListener *listener;
|
||||
@property(nonatomic) NSModalSession modal_session;
|
||||
@property(nonatomic) SDL_CocoaVideoData *videodata;
|
||||
@property(nonatomic) bool pending_size;
|
||||
@property(nonatomic) bool pending_position;
|
||||
@property(nonatomic) bool border_toggled;
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
@property(nonatomic) EGLSurface egl_surface;
|
||||
#endif
|
||||
@end
|
||||
|
||||
extern bool b_inModeTransition;
|
||||
|
||||
extern bool Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
|
||||
extern void Cocoa_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
|
||||
extern bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
|
||||
extern bool Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
|
||||
extern void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Cocoa_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
|
||||
extern void Cocoa_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
|
||||
extern void Cocoa_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
|
||||
extern SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
|
||||
extern void *Cocoa_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
|
||||
extern SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
|
||||
extern void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool Cocoa_SetWindowHitTest(SDL_Window *window, bool enabled);
|
||||
extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, bool accept);
|
||||
extern bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
extern bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
|
||||
extern bool Cocoa_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
|
||||
extern bool Cocoa_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
|
||||
extern bool Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
extern void Cocoa_MenuVisibilityCallback(void *userdata, const char *name, const char *oldValue, const char *newValue);
|
||||
|
||||
#endif // SDL_cocoawindow_h_
|
||||
3284
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoawindow.m
vendored
Normal file
3284
vendor/sdl-3.2.10/src/video/cocoa/SDL_cocoawindow.m
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue