#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
static bool Cocoa_VideoInit(SDL_VideoDevice *_this);
static void Cocoa_VideoQuit(SDL_VideoDevice *_this);
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
{
@autoreleasepool {
CFBridgingRelease(device->internal);
SDL_free(device);
}
}
static SDL_VideoDevice *Cocoa_CreateDevice(void)
{
@autoreleasepool {
SDL_VideoDevice *device;
SDL_CocoaVideoData *data;
if (![NSThread isMainThread]) {
return NULL; }
Cocoa_RegisterApp();
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->system_theme = Cocoa_GetSystemTheme();
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;
}
SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL);
SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL);
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;
}
}
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;
}
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;
}
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;
}
Uint8 *pixels = [imgrep bitmapData];
SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
SDL_DestroySurface(converted);
[img addRepresentation:imgrep];
}
SDL_free(images);
return img;
}
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