1/*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5  This software is provided 'as-is', without any express or implied
6  warranty.  In no event will the authors be held liable for any damages
7  arising from the use of this software.
8
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12
13  1. The origin of this software must not be misrepresented; you must not
14     claim that you wrote the original software. If you use this software
15     in a product, an acknowledgment in the product documentation would be
16     appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18     misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22
23#if SDL_VIDEO_DRIVER_COCOA
24
25#include "SDL.h"
26#include "SDL_endian.h"
27#include "SDL_cocoavideo.h"
28#include "SDL_cocoashape.h"
29#include "SDL_cocoavulkan.h"
30#include "SDL_cocoametalview.h"
31#include "SDL_assert.h"
32
33/* Initialization/Query functions */
34static int Cocoa_VideoInit(_THIS);
35static void Cocoa_VideoQuit(_THIS);
36
37/* Cocoa driver bootstrap functions */
38
39static int
40Cocoa_Available(void)
41{
42    return (1);
43}
44
45static void
46Cocoa_DeleteDevice(SDL_VideoDevice * device)
47{
48    SDL_free(device->driverdata);
49    SDL_free(device);
50}
51
52static SDL_VideoDevice *
53Cocoa_CreateDevice(int devindex)
54{
55    SDL_VideoDevice *device;
56    SDL_VideoData *data;
57
58    Cocoa_RegisterApp();
59
60    /* Initialize all variables that we clean on shutdown */
61    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
62    if (device) {
63        data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
64    } else {
65        data = NULL;
66    }
67    if (!data) {
68        SDL_OutOfMemory();
69        SDL_free(device);
70        return NULL;
71    }
72    device->driverdata = data;
73
74    /* Set the function pointers */
75    device->VideoInit = Cocoa_VideoInit;
76    device->VideoQuit = Cocoa_VideoQuit;
77    device->GetDisplayBounds = Cocoa_GetDisplayBounds;
78    device->GetDisplayUsableBounds = Cocoa_GetDisplayUsableBounds;
79    device->GetDisplayDPI = Cocoa_GetDisplayDPI;
80    device->GetDisplayModes = Cocoa_GetDisplayModes;
81    device->SetDisplayMode = Cocoa_SetDisplayMode;
82    device->PumpEvents = Cocoa_PumpEvents;
83    device->SuspendScreenSaver = Cocoa_SuspendScreenSaver;
84
85    device->CreateSDLWindow = Cocoa_CreateWindow;
86    device->CreateSDLWindowFrom = Cocoa_CreateWindowFrom;
87    device->SetWindowTitle = Cocoa_SetWindowTitle;
88    device->SetWindowIcon = Cocoa_SetWindowIcon;
89    device->SetWindowPosition = Cocoa_SetWindowPosition;
90    device->SetWindowSize = Cocoa_SetWindowSize;
91    device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize;
92    device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize;
93    device->SetWindowOpacity = Cocoa_SetWindowOpacity;
94    device->ShowWindow = Cocoa_ShowWindow;
95    device->HideWindow = Cocoa_HideWindow;
96    device->RaiseWindow = Cocoa_RaiseWindow;
97    device->MaximizeWindow = Cocoa_MaximizeWindow;
98    device->MinimizeWindow = Cocoa_MinimizeWindow;
99    device->RestoreWindow = Cocoa_RestoreWindow;
100    device->SetWindowBordered = Cocoa_SetWindowBordered;
101    device->SetWindowResizable = Cocoa_SetWindowResizable;
102    device->SetWindowFullscreen = Cocoa_SetWindowFullscreen;
103    device->SetWindowGammaRamp = Cocoa_SetWindowGammaRamp;
104    device->GetWindowGammaRamp = Cocoa_GetWindowGammaRamp;
105    device->SetWindowGrab = Cocoa_SetWindowGrab;
106    device->DestroyWindow = Cocoa_DestroyWindow;
107    device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
108    device->SetWindowHitTest = Cocoa_SetWindowHitTest;
109    device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
110
111    device->shape_driver.CreateShaper = Cocoa_CreateShaper;
112    device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
113    device->shape_driver.ResizeWindowShape = Cocoa_ResizeWindowShape;
114
115#if SDL_VIDEO_OPENGL_CGL
116    device->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
117    device->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
118    device->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
119    device->GL_CreateContext = Cocoa_GL_CreateContext;
120    device->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
121    device->GL_GetDrawableSize = Cocoa_GL_GetDrawableSize;
122    device->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
123    device->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
124    device->GL_SwapWindow = Cocoa_GL_SwapWindow;
125    device->GL_DeleteContext = Cocoa_GL_DeleteContext;
126#elif SDL_VIDEO_OPENGL_EGL
127    device->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
128    device->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
129    device->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
130    device->GL_CreateContext = Cocoa_GLES_CreateContext;
131    device->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
132    device->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
133    device->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
134    device->GL_SwapWindow = Cocoa_GLES_SwapWindow;
135    device->GL_DeleteContext = Cocoa_GLES_DeleteContext;
136#endif
137
138#if SDL_VIDEO_VULKAN
139    device->Vulkan_LoadLibrary = Cocoa_Vulkan_LoadLibrary;
140    device->Vulkan_UnloadLibrary = Cocoa_Vulkan_UnloadLibrary;
141    device->Vulkan_GetInstanceExtensions = Cocoa_Vulkan_GetInstanceExtensions;
142    device->Vulkan_CreateSurface = Cocoa_Vulkan_CreateSurface;
143    device->Vulkan_GetDrawableSize = Cocoa_Vulkan_GetDrawableSize;
144#endif
145
146#if SDL_VIDEO_METAL
147    device->Metal_CreateView = Cocoa_Metal_CreateView;
148    device->Metal_DestroyView = Cocoa_Metal_DestroyView;
149    device->Metal_GetLayer = Cocoa_Metal_GetLayer;
150    device->Metal_GetDrawableSize = Cocoa_Metal_GetDrawableSize;
151#endif
152
153    device->StartTextInput = Cocoa_StartTextInput;
154    device->StopTextInput = Cocoa_StopTextInput;
155    device->SetTextInputRect = Cocoa_SetTextInputRect;
156
157    device->SetClipboardText = Cocoa_SetClipboardText;
158    device->GetClipboardText = Cocoa_GetClipboardText;
159    device->HasClipboardText = Cocoa_HasClipboardText;
160
161    device->free = Cocoa_DeleteDevice;
162
163    return device;
164}
165
166VideoBootStrap COCOA_bootstrap = {
167    "cocoa", "SDL Cocoa video driver",
168    Cocoa_Available, Cocoa_CreateDevice
169};
170
171
172int
173Cocoa_VideoInit(_THIS)
174{
175    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
176
177    Cocoa_InitModes(_this);
178    Cocoa_InitKeyboard(_this);
179    if (Cocoa_InitMouse(_this) < 0) {
180        return -1;
181    }
182
183    data->allow_spaces = ((floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) && SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE));
184
185    /* The IOPM assertion API can disable the screensaver as of 10.7. */
186    data->screensaver_use_iopm = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
187
188    data->swaplock = SDL_CreateMutex();
189    if (!data->swaplock) {
190        return -1;
191    }
192
193    return 0;
194}
195
196void
197Cocoa_VideoQuit(_THIS)
198{
199    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
200    Cocoa_QuitModes(_this);
201    Cocoa_QuitKeyboard(_this);
202    Cocoa_QuitMouse(_this);
203    SDL_DestroyMutex(data->swaplock);
204    data->swaplock = NULL;
205}
206
207/* This function assumes that it's called from within an autorelease pool */
208NSImage *
209Cocoa_CreateImage(SDL_Surface * surface)
210{
211    SDL_Surface *converted;
212    NSBitmapImageRep *imgrep;
213    Uint8 *pixels;
214    int i;
215    NSImage *img;
216
217    converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0);
218    if (!converted) {
219        return nil;
220    }
221
222    imgrep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
223                    pixelsWide: converted->w
224                    pixelsHigh: converted->h
225                    bitsPerSample: 8
226                    samplesPerPixel: 4
227                    hasAlpha: YES
228                    isPlanar: NO
229                    colorSpaceName: NSDeviceRGBColorSpace
230                    bytesPerRow: converted->pitch
231                    bitsPerPixel: converted->format->BitsPerPixel] autorelease];
232    if (imgrep == nil) {
233        SDL_FreeSurface(converted);
234        return nil;
235    }
236
237    /* Copy the pixels */
238    pixels = [imgrep bitmapData];
239    SDL_memcpy(pixels, converted->pixels, converted->h * converted->pitch);
240    SDL_FreeSurface(converted);
241
242    /* Premultiply the alpha channel */
243    for (i = (surface->h * surface->w); i--; ) {
244        Uint8 alpha = pixels[3];
245        pixels[0] = (Uint8)(((Uint16)pixels[0] * alpha) / 255);
246        pixels[1] = (Uint8)(((Uint16)pixels[1] * alpha) / 255);
247        pixels[2] = (Uint8)(((Uint16)pixels[2] * alpha) / 255);
248        pixels += 4;
249    }
250
251    img = [[[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)] autorelease];
252    if (img != nil) {
253        [img addRepresentation: imgrep];
254    }
255    return img;
256}
257
258/*
259 * Mac OS X log support.
260 *
261 * This doesn't really have aything to do with the interfaces of the SDL video
262 *  subsystem, but we need to stuff this into an Objective-C source code file.
263 *
264 * NOTE: This is copypasted in src/video/uikit/SDL_uikitvideo.m! Be sure both
265 *  versions remain identical!
266 */
267
268void SDL_NSLog(const char *text)
269{
270    NSLog(@"%s", text);
271}
272
273#endif /* SDL_VIDEO_DRIVER_COCOA */
274
275/* vim: set ts=4 sw=4 expandtab: */
276