src/video/quartz/SDL_QuartzVideo.m
author Sam Lantinga
Sat, 10 Oct 2009 15:10:06 +0000
branchSDL-1.2
changeset 4319 edefeb52a627
parent 4317 719faf118c38
child 4340 0deabd35275b
permissions -rw-r--r--
Added support for SDL_VIDEO_FULLSCREEN_DISPLAY, but mouse events need to be fixed up.
slouken@47
     1
/*
slouken@47
     2
    SDL - Simple DirectMedia Layer
slouken@4159
     3
    Copyright (C) 1997-2009  Sam Lantinga
slouken@47
     4
slouken@47
     5
    This library is free software; you can redistribute it and/or
slouken@47
     6
    modify it under the terms of the GNU Library General Public
slouken@47
     7
    License as published by the Free Software Foundation; either
slouken@47
     8
    version 2 of the License, or (at your option) any later version.
slouken@47
     9
slouken@47
    10
    This library is distributed in the hope that it will be useful,
slouken@47
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@47
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@47
    13
    Library General Public License for more details.
slouken@47
    14
slouken@47
    15
    You should have received a copy of the GNU Library General Public
slouken@47
    16
    License along with this library; if not, write to the Free
slouken@47
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@47
    18
slouken@47
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@47
    21
*/
slouken@1403
    22
#include "SDL_config.h"
slouken@47
    23
slouken@47
    24
#include "SDL_QuartzVideo.h"
slouken@761
    25
#include "SDL_QuartzWindow.h"
slouken@47
    26
icculus@4204
    27
#ifdef __powerpc__  /* I'm gambling they fixed this by 10.4. --ryan. */
icculus@4204
    28
/*
slouken@761
    29
    Add methods to get at private members of NSScreen. 
slouken@761
    30
    Since there is a bug in Apple's screen switching code
slouken@761
    31
    that does not update this variable when switching
slouken@761
    32
    to fullscreen, we'll set it manually (but only for the
slouken@761
    33
    main screen).
slouken@761
    34
*/
slouken@761
    35
@interface NSScreen (NSScreenAccess)
slouken@761
    36
- (void) setFrame:(NSRect)frame;
slouken@761
    37
@end
slouken@761
    38
slouken@761
    39
@implementation NSScreen (NSScreenAccess)
slouken@761
    40
- (void) setFrame:(NSRect)frame;
slouken@761
    41
{
slouken@761
    42
    _frame = frame;
slouken@761
    43
}
slouken@761
    44
@end
icculus@4204
    45
static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
icculus@4204
    46
{
icculus@4204
    47
    [nsscreen setFrame:frame];
icculus@4204
    48
}
icculus@4204
    49
#else
icculus@4204
    50
static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
icculus@4204
    51
{
icculus@4204
    52
}
icculus@4204
    53
#endif
slouken@761
    54
slouken@4123
    55
@interface SDLTranslatorResponder : NSTextView
slouken@4123
    56
{
slouken@4123
    57
}
slouken@4123
    58
- (void) doCommandBySelector:(SEL)myselector;
slouken@4123
    59
@end
slouken@4123
    60
slouken@4123
    61
@implementation SDLTranslatorResponder
slouken@4123
    62
- (void) doCommandBySelector:(SEL) myselector {}
slouken@4123
    63
@end
slouken@4123
    64
icculus@4204
    65
/* absent in 10.3.9.  */
icculus@4204
    66
CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
slouken@761
    67
slouken@761
    68
/* Bootstrap functions */
slouken@761
    69
static int              QZ_Available ();
slouken@761
    70
static SDL_VideoDevice* QZ_CreateDevice (int device_index);
slouken@761
    71
static void             QZ_DeleteDevice (SDL_VideoDevice *device);
slouken@761
    72
slouken@761
    73
/* Initialization, Query, Setup, and Redrawing functions */
slouken@761
    74
static int          QZ_VideoInit        (_THIS, SDL_PixelFormat *video_format);
slouken@761
    75
slouken@761
    76
static SDL_Rect**   QZ_ListModes        (_THIS, SDL_PixelFormat *format,
slouken@761
    77
                                         Uint32 flags);
icculus@1340
    78
static void         QZ_UnsetVideoMode   (_THIS, BOOL to_desktop);
slouken@761
    79
slouken@761
    80
static SDL_Surface* QZ_SetVideoMode     (_THIS, SDL_Surface *current,
slouken@761
    81
                                         int width, int height, int bpp,
slouken@761
    82
                                         Uint32 flags);
slouken@761
    83
static int          QZ_ToggleFullScreen (_THIS, int on);
slouken@761
    84
static int          QZ_SetColors        (_THIS, int first_color,
slouken@761
    85
                                         int num_colors, SDL_Color *colors);
slouken@761
    86
slouken@761
    87
static int          QZ_LockDoubleBuffer   (_THIS, SDL_Surface *surface);
slouken@761
    88
static void         QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
slouken@761
    89
static int          QZ_ThreadFlip         (_THIS);
slouken@761
    90
static int          QZ_FlipDoubleBuffer   (_THIS, SDL_Surface *surface);
slouken@761
    91
static void         QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
slouken@761
    92
slouken@761
    93
static void         QZ_DirectUpdate     (_THIS, int num_rects, SDL_Rect *rects);
slouken@761
    94
static void         QZ_UpdateRects      (_THIS, int num_rects, SDL_Rect *rects);
slouken@761
    95
static void         QZ_VideoQuit        (_THIS);
slouken@761
    96
slouken@761
    97
/* Hardware surface functions (for fullscreen mode only) */
slouken@761
    98
#if 0 /* Not used (apparently, it's really slow) */
slouken@761
    99
static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
slouken@761
   100
#endif
slouken@761
   101
static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface);
slouken@761
   102
static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
icculus@1120
   103
static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
slouken@761
   104
static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
slouken@761
   105
/* static int  QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */
slouken@47
   106
slouken@47
   107
/* Bootstrap binding, enables entry point into the driver */
slouken@47
   108
VideoBootStrap QZ_bootstrap = {
slouken@272
   109
    "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
slouken@47
   110
};
slouken@47
   111
slouken@588
   112
slouken@47
   113
/* Bootstrap functions */
slouken@4317
   114
static int QZ_Available ()
slouken@4317
   115
{
slouken@47
   116
    return 1;
slouken@47
   117
}
slouken@47
   118
slouken@4317
   119
static SDL_VideoDevice* QZ_CreateDevice (int device_index)
slouken@4317
   120
{
slouken@272
   121
#pragma unused (device_index)
slouken@47
   122
slouken@47
   123
    SDL_VideoDevice *device;
slouken@47
   124
    SDL_PrivateVideoData *hidden;
slouken@47
   125
slouken@1756
   126
    device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
slouken@1756
   127
    hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
slouken@47
   128
slouken@47
   129
    if (device == NULL || hidden == NULL)
slouken@47
   130
        SDL_OutOfMemory ();
slouken@47
   131
slouken@1756
   132
    SDL_memset (device, 0, sizeof (*device) );
slouken@1756
   133
    SDL_memset (hidden, 0, sizeof (*hidden) );
slouken@272
   134
slouken@47
   135
    device->hidden = hidden;
slouken@47
   136
slouken@47
   137
    device->VideoInit        = QZ_VideoInit;
slouken@47
   138
    device->ListModes        = QZ_ListModes;
slouken@47
   139
    device->SetVideoMode     = QZ_SetVideoMode;
slouken@47
   140
    device->ToggleFullScreen = QZ_ToggleFullScreen;
icculus@1212
   141
    device->UpdateMouse      = QZ_UpdateMouse;
slouken@47
   142
    device->SetColors        = QZ_SetColors;
slouken@47
   143
    /* device->UpdateRects      = QZ_UpdateRects; this is determined by SetVideoMode() */
slouken@47
   144
    device->VideoQuit        = QZ_VideoQuit;
slouken@272
   145
slouken@47
   146
    device->LockHWSurface   = QZ_LockHWSurface;
slouken@47
   147
    device->UnlockHWSurface = QZ_UnlockHWSurface;
icculus@1120
   148
    device->AllocHWSurface   = QZ_AllocHWSurface;
slouken@47
   149
    device->FreeHWSurface   = QZ_FreeHWSurface;
slouken@47
   150
    /* device->FlipHWSurface   = QZ_FlipHWSurface */;
slouken@47
   151
slouken@47
   152
    device->SetGamma     = QZ_SetGamma;
slouken@47
   153
    device->GetGamma     = QZ_GetGamma;
slouken@47
   154
    device->SetGammaRamp = QZ_SetGammaRamp;
slouken@47
   155
    device->GetGammaRamp = QZ_GetGammaRamp;
slouken@47
   156
slouken@47
   157
    device->GL_GetProcAddress = QZ_GL_GetProcAddress;
slouken@47
   158
    device->GL_GetAttribute   = QZ_GL_GetAttribute;
slouken@47
   159
    device->GL_MakeCurrent    = QZ_GL_MakeCurrent;
slouken@47
   160
    device->GL_SwapBuffers    = QZ_GL_SwapBuffers;
slouken@47
   161
    device->GL_LoadLibrary    = QZ_GL_LoadLibrary;
slouken@272
   162
slouken@47
   163
    device->FreeWMCursor   = QZ_FreeWMCursor;
slouken@47
   164
    device->CreateWMCursor = QZ_CreateWMCursor;
slouken@47
   165
    device->ShowWMCursor   = QZ_ShowWMCursor;
slouken@47
   166
    device->WarpWMCursor   = QZ_WarpWMCursor;
slouken@47
   167
    device->MoveWMCursor   = QZ_MoveWMCursor;
slouken@47
   168
    device->CheckMouseMode = QZ_CheckMouseMode;
slouken@47
   169
    device->InitOSKeymap   = QZ_InitOSKeymap;
slouken@47
   170
    device->PumpEvents     = QZ_PumpEvents;
slouken@47
   171
slouken@47
   172
    device->SetCaption    = QZ_SetCaption;
slouken@47
   173
    device->SetIcon       = QZ_SetIcon;
slouken@47
   174
    device->IconifyWindow = QZ_IconifyWindow;
slouken@47
   175
    /*device->GetWMInfo     = QZ_GetWMInfo;*/
slouken@47
   176
    device->GrabInput     = QZ_GrabInput;
slouken@272
   177
icculus@4204
   178
    /*
icculus@4204
   179
     * This is a big hassle, needing QuickDraw and QuickTime on older
icculus@4204
   180
     *  systems, and god knows what on 10.6, so we immediately fail here,
icculus@4204
   181
     *  which causes SDL to make an RGB surface and manage the YUV overlay
icculus@4204
   182
     *  in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel
icculus@4204
   183
     *  shader.  :)
icculus@4204
   184
     */
icculus@4204
   185
    /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/
slouken@390
   186
slouken@390
   187
    device->free             = QZ_DeleteDevice;
slouken@272
   188
slouken@47
   189
    return device;
slouken@47
   190
}
slouken@47
   191
slouken@4317
   192
static void QZ_DeleteDevice (SDL_VideoDevice *device)
slouken@4317
   193
{
slouken@1756
   194
    SDL_free (device->hidden);
slouken@1756
   195
    SDL_free (device);
slouken@47
   196
}
slouken@47
   197
slouken@4317
   198
static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format)
slouken@4317
   199
{
slouken@4049
   200
    NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
icculus@3936
   201
    const char *env = NULL;
slouken@4317
   202
	
slouken@272
   203
    /* Initialize the video settings; this data persists between mode switches */
slouken@272
   204
    display_id = kCGDirectMainDisplay;
slouken@4317
   205
slouken@4319
   206
#if 0 /* The mouse event code needs to take this into account... */
slouken@4319
   207
    env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
slouken@4319
   208
    if ( env ) {
slouken@4319
   209
        int monitor = SDL_atoi(env);
slouken@4319
   210
    	CGDirectDisplayID activeDspys [3];
slouken@4319
   211
    	CGDisplayCount dspyCnt;
slouken@4319
   212
    	CGGetActiveDisplayList (3, activeDspys, &dspyCnt);
slouken@4319
   213
        if ( monitor >= 0 && monitor < dspyCnt ) {
slouken@4319
   214
    	    display_id = activeDspys[monitor];
slouken@4319
   215
        }
slouken@4319
   216
    }
slouken@4319
   217
#endif
slouken@4319
   218
slouken@272
   219
    save_mode  = CGDisplayCurrentMode    (display_id);
slouken@272
   220
    mode_list  = CGDisplayAvailableModes (display_id);
slouken@272
   221
    palette    = CGPaletteCreateDefaultColorPalette ();
slouken@272
   222
slouken@4139
   223
    /* Allow environment override of screensaver disable. */
icculus@3936
   224
    env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
slouken@4139
   225
    if ( env ) {
slouken@4139
   226
        allow_screensaver = SDL_atoi(env);
slouken@4139
   227
    } else {
slouken@4139
   228
#ifdef SDL_VIDEO_DISABLE_SCREENSAVER
slouken@4139
   229
        allow_screensaver = 0;
slouken@4139
   230
#else
slouken@4139
   231
        allow_screensaver = 1;
slouken@4139
   232
#endif
slouken@4139
   233
    }
icculus@3936
   234
slouken@272
   235
    /* Gather some information that is useful to know about the display */
slouken@272
   236
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
slouken@272
   237
                      kCFNumberSInt32Type, &device_bpp);
slouken@47
   238
slouken@272
   239
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
slouken@272
   240
                      kCFNumberSInt32Type, &device_width);
slouken@47
   241
slouken@272
   242
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
slouken@272
   243
                      kCFNumberSInt32Type, &device_height);
slouken@272
   244
slouken@1545
   245
    /* Determine the current screen size */
slouken@1545
   246
    this->info.current_w = device_width;
slouken@1545
   247
    this->info.current_h = device_height;
slouken@1545
   248
slouken@1545
   249
    /* Determine the default screen depth */
slouken@272
   250
    video_format->BitsPerPixel = device_bpp;
slouken@272
   251
slouken@501
   252
    /* Set misc globals */
slouken@501
   253
    current_grab_mode = SDL_GRAB_OFF;
slouken@761
   254
    cursor_should_be_visible    = YES;
slouken@779
   255
    cursor_visible              = YES;
slouken@823
   256
    current_mods = 0;
slouken@4123
   257
    field_edit =  [[SDLTranslatorResponder alloc] initWithFrame:r];
slouken@501
   258
    
icculus@876
   259
    if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
icculus@876
   260
        system_version = 0;
slouken@934
   261
    
slouken@555
   262
    /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
slouken@555
   263
    QZ_RegisterForSleepNotifications (this);
slouken@555
   264
    
slouken@1271
   265
    /* Fill in some window manager capabilities */
slouken@1271
   266
    this->info.wm_available = 1;
slouken@1271
   267
slouken@272
   268
    return 0;
slouken@47
   269
}
slouken@47
   270
slouken@4317
   271
static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
slouken@4317
   272
{
slouken@272
   273
    CFIndex num_modes;
slouken@47
   274
    CFIndex i;
slouken@47
   275
slouken@47
   276
    int list_size = 0;
slouken@390
   277
slouken@47
   278
    /* Any windowed mode is acceptable */
slouken@47
   279
    if ( (flags & SDL_FULLSCREEN) == 0 )
slouken@47
   280
        return (SDL_Rect**)-1;
slouken@390
   281
slouken@47
   282
    /* Free memory from previous call, if any */
slouken@501
   283
    if ( client_mode_list != NULL ) {
slouken@47
   284
slouken@390
   285
        int i;
slouken@47
   286
slouken@501
   287
        for (i = 0; client_mode_list[i] != NULL; i++)
slouken@1756
   288
            SDL_free (client_mode_list[i]);
slouken@47
   289
slouken@1756
   290
        SDL_free (client_mode_list);
slouken@501
   291
        client_mode_list = NULL;
slouken@47
   292
    }
slouken@390
   293
slouken@272
   294
    num_modes = CFArrayGetCount (mode_list);
slouken@272
   295
slouken@47
   296
    /* Build list of modes with the requested bpp */
slouken@155
   297
    for (i = 0; i < num_modes; i++) {
slouken@390
   298
slouken@155
   299
        CFDictionaryRef onemode;
slouken@155
   300
        CFNumberRef     number;
slouken@390
   301
        int bpp;
slouken@390
   302
slouken@390
   303
        onemode = CFArrayGetValueAtIndex (mode_list, i);
slouken@390
   304
        number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
slouken@390
   305
        CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
slouken@390
   306
slouken@390
   307
        if (bpp == format->BitsPerPixel) {
slouken@390
   308
slouken@390
   309
            int intvalue;
slouken@390
   310
            int hasMode;
slouken@390
   311
            int width, height;
slouken@47
   312
slouken@390
   313
            number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
slouken@390
   314
            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
slouken@390
   315
            width = (Uint16) intvalue;
slouken@390
   316
slouken@390
   317
            number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
slouken@390
   318
            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
slouken@390
   319
            height = (Uint16) intvalue;
slouken@390
   320
slouken@390
   321
            /* Check if mode is already in the list */
slouken@390
   322
            {
slouken@390
   323
                int i;
slouken@390
   324
                hasMode = SDL_FALSE;
slouken@390
   325
                for (i = 0; i < list_size; i++) {
slouken@501
   326
                    if (client_mode_list[i]->w == width && 
slouken@501
   327
                        client_mode_list[i]->h == height) {
slouken@390
   328
                        hasMode = SDL_TRUE;
slouken@390
   329
                        break;
slouken@390
   330
                    }
slouken@155
   331
                }
slouken@155
   332
            }
slouken@390
   333
slouken@390
   334
            /* Grow the list and add mode to the list */
slouken@390
   335
            if ( ! hasMode ) {
slouken@390
   336
slouken@390
   337
                SDL_Rect *rect;
slouken@390
   338
slouken@390
   339
                list_size++;
slouken@390
   340
slouken@501
   341
                if (client_mode_list == NULL)
slouken@501
   342
                    client_mode_list = (SDL_Rect**) 
slouken@1756
   343
                        SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
slouken@390
   344
                else
slouken@501
   345
                    client_mode_list = (SDL_Rect**) 
slouken@1756
   346
                        SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
slouken@390
   347
slouken@1756
   348
                rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
slouken@47
   349
slouken@501
   350
                if (client_mode_list == NULL || rect == NULL) {
slouken@390
   351
                    SDL_OutOfMemory ();
slouken@390
   352
                    return NULL;
slouken@390
   353
                }
slouken@390
   354
icculus@1218
   355
                rect->x = rect->y = 0;
slouken@390
   356
                rect->w = width;
slouken@390
   357
                rect->h = height;
slouken@390
   358
slouken@501
   359
                client_mode_list[list_size-1] = rect;
slouken@501
   360
                client_mode_list[list_size]   = NULL;
slouken@272
   361
            }
slouken@47
   362
        }
slouken@47
   363
    }
slouken@390
   364
slouken@155
   365
    /* Sort list largest to smallest (by area) */
slouken@155
   366
    {
slouken@155
   367
        int i, j;
slouken@155
   368
        for (i = 0; i < list_size; i++) {
slouken@155
   369
            for (j = 0; j < list_size-1; j++) {
slouken@390
   370
slouken@155
   371
                int area1, area2;
slouken@501
   372
                area1 = client_mode_list[j]->w * client_mode_list[j]->h;
slouken@501
   373
                area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
slouken@390
   374
slouken@155
   375
                if (area1 < area2) {
slouken@501
   376
                    SDL_Rect *tmp = client_mode_list[j];
slouken@501
   377
                    client_mode_list[j] = client_mode_list[j+1];
slouken@501
   378
                    client_mode_list[j+1] = tmp;
slouken@155
   379
                }
slouken@155
   380
            }
slouken@155
   381
        }
slouken@155
   382
    }
slouken@501
   383
    return client_mode_list;
slouken@47
   384
}
slouken@47
   385
slouken@657
   386
static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
slouken@657
   387
{
slouken@657
   388
    const char *window = getenv("SDL_VIDEO_WINDOW_POS");
slouken@657
   389
    if ( window ) {
slouken@657
   390
        if ( sscanf(window, "%d,%d", x, y) == 2 ) {
slouken@657
   391
            return SDL_TRUE;
slouken@657
   392
        }
slouken@657
   393
    }
slouken@657
   394
    return SDL_FALSE;
slouken@657
   395
}
slouken@657
   396
slouken@4317
   397
static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop)
slouken@4317
   398
{
slouken@47
   399
    /* Reset values that may change between switches */
slouken@501
   400
    this->info.blit_fill  = 0;
slouken@501
   401
    this->FillHWRect      = NULL;
slouken@501
   402
    this->UpdateRects     = NULL;
slouken@501
   403
    this->LockHWSurface   = NULL;
slouken@501
   404
    this->UnlockHWSurface = NULL;
slouken@501
   405
    
icculus@4204
   406
    if (cg_context) {
icculus@4204
   407
        CGContextFlush (cg_context);
icculus@4204
   408
        CGContextRelease (cg_context);
icculus@4204
   409
        cg_context = nil;
icculus@4204
   410
    }
icculus@4204
   411
    
slouken@272
   412
    /* Release fullscreen resources */
slouken@47
   413
    if ( mode_flags & SDL_FULLSCREEN ) {
slouken@272
   414
slouken@435
   415
        NSRect screen_rect;
slouken@435
   416
        
slouken@588
   417
        /*  Release double buffer stuff */
icculus@1144
   418
        if ( mode_flags & SDL_DOUBLEBUF) {
slouken@588
   419
            quit_thread = YES;
slouken@588
   420
            SDL_SemPost (sem1);
slouken@588
   421
            SDL_WaitThread (thread, NULL);
slouken@588
   422
            SDL_DestroySemaphore (sem1);
slouken@588
   423
            SDL_DestroySemaphore (sem2);
slouken@1756
   424
            SDL_free (sw_buffers[0]);
slouken@588
   425
        }
slouken@588
   426
        
slouken@4065
   427
        /* If we still have a valid window, close it. */
slouken@4065
   428
        if ( qz_window ) {
slouken@4090
   429
            NSCAssert([ qz_window delegate ] == nil, @"full screen window shouldn't have a delegate"); /* if that should ever change, we'd have to release it here */
slouken@4090
   430
            [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
slouken@4065
   431
            qz_window = nil;
slouken@4065
   432
            window_view = nil;
slouken@4065
   433
        }
slouken@501
   434
        /* 
slouken@501
   435
            Release the OpenGL context
slouken@501
   436
            Do this first to avoid trash on the display before fade
slouken@501
   437
        */
slouken@501
   438
        if ( mode_flags & SDL_OPENGL ) {
slouken@501
   439
        
slouken@272
   440
            QZ_TearDownOpenGL (this);
slouken@501
   441
            CGLSetFullScreen (NULL);
slouken@501
   442
        }
icculus@1340
   443
        if (to_desktop) {
slouken@3877
   444
            ShowMenuBar ();
icculus@1340
   445
            /* Restore original screen resolution/bpp */
icculus@1340
   446
            CGDisplaySwitchToMode (display_id, save_mode);
icculus@1340
   447
            CGReleaseAllDisplays ();
icculus@1340
   448
            /* 
icculus@1340
   449
                Reset the main screen's rectangle
icculus@1340
   450
                See comment in QZ_SetVideoFullscreen for why we do this
icculus@1340
   451
            */
icculus@1340
   452
            screen_rect = NSMakeRect(0,0,device_width,device_height);
icculus@4204
   453
            QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
icculus@1340
   454
        }
slouken@47
   455
    }
slouken@272
   456
    /* Release window mode resources */
slouken@390
   457
    else {
slouken@4090
   458
        id delegate = [ qz_window delegate ];
slouken@4090
   459
        [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
slouken@4090
   460
        if (delegate != nil) [ delegate release ];
slouken@390
   461
        qz_window = nil;
slouken@501
   462
        window_view = nil;
icculus@1160
   463
slouken@272
   464
        /* Release the OpenGL context */
slouken@272
   465
        if ( mode_flags & SDL_OPENGL )
slouken@272
   466
            QZ_TearDownOpenGL (this);
slouken@47
   467
    }
slouken@272
   468
slouken@155
   469
    /* Signal successful teardown */
slouken@155
   470
    video_set = SDL_FALSE;
slouken@47
   471
}
slouken@47
   472
slouken@47
   473
static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
slouken@4317
   474
                                           int height, int bpp, Uint32 flags)
slouken@4317
   475
{
icculus@1220
   476
    boolean_t exact_match = 0;
slouken@435
   477
    NSRect screen_rect;
slouken@588
   478
    CGError error;
slouken@4065
   479
    NSRect contentRect;
icculus@1340
   480
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
icculus@4204
   481
icculus@1340
   482
    /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
   483
       that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
   484
    if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
icculus@1340
   485
        CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
   486
    }
slouken@435
   487
    
slouken@501
   488
    /* Destroy any previous mode */
slouken@501
   489
    if (video_set == SDL_TRUE)
icculus@1340
   490
        QZ_UnsetVideoMode (this, FALSE);
slouken@501
   491
icculus@4204
   492
    /* Sorry, QuickDraw was ripped out. */
icculus@4204
   493
    if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
icculus@4204
   494
        SDL_SetError ("Embedded QuickDraw windows are no longer supported");
icculus@4204
   495
        goto ERR_NO_MATCH;
icculus@4204
   496
    }
icculus@4204
   497
slouken@47
   498
    /* See if requested mode exists */
slouken@390
   499
    mode = CGDisplayBestModeForParameters (display_id, bpp, width,
slouken@390
   500
                                           height, &exact_match);
slouken@390
   501
slouken@47
   502
    /* Require an exact match to the requested mode */
slouken@47
   503
    if ( ! exact_match ) {
slouken@501
   504
        SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
slouken@47
   505
        goto ERR_NO_MATCH;
slouken@47
   506
    }
slouken@155
   507
slouken@47
   508
    /* Put up the blanking window (a window above all other windows) */
slouken@588
   509
    if (getenv ("SDL_SINGLEDISPLAY"))
slouken@588
   510
        error = CGDisplayCapture (display_id);
slouken@588
   511
    else
slouken@588
   512
        error = CGCaptureAllDisplays ();
slouken@588
   513
        
slouken@588
   514
    if ( CGDisplayNoErr != error ) {
slouken@47
   515
        SDL_SetError ("Failed capturing display");
slouken@47
   516
        goto ERR_NO_CAPTURE;
slouken@47
   517
    }
slouken@272
   518
slouken@47
   519
    /* Do the physical switch */
slouken@47
   520
    if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
slouken@47
   521
        SDL_SetError ("Failed switching display resolution");
slouken@47
   522
        goto ERR_NO_SWITCH;
slouken@47
   523
    }
slouken@272
   524
slouken@47
   525
    current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
slouken@47
   526
    current->pitch  = CGDisplayBytesPerRow (display_id);
slouken@47
   527
slouken@58
   528
    current->flags = 0;
slouken@47
   529
    current->w = width;
slouken@47
   530
    current->h = height;
slouken@390
   531
    current->flags |= SDL_FULLSCREEN;
slouken@47
   532
    current->flags |= SDL_HWSURFACE;
slouken@501
   533
    current->flags |= SDL_PREALLOC;
icculus@4204
   534
    /* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */
icculus@4204
   535
slouken@501
   536
    this->UpdateRects     = QZ_DirectUpdate;
slouken@501
   537
    this->LockHWSurface   = QZ_LockHWSurface;
slouken@501
   538
    this->UnlockHWSurface = QZ_UnlockHWSurface;
slouken@588
   539
slouken@588
   540
    /* Setup double-buffer emulation */
slouken@588
   541
    if ( flags & SDL_DOUBLEBUF ) {
slouken@588
   542
        
slouken@588
   543
        /*
slouken@588
   544
            Setup a software backing store for reasonable results when
slouken@588
   545
            double buffering is requested (since a single-buffered hardware
slouken@588
   546
            surface looks hideous).
slouken@588
   547
            
slouken@588
   548
            The actual screen blit occurs in a separate thread to allow 
slouken@588
   549
            other blitting while waiting on the VBL (and hence results in higher framerates).
slouken@588
   550
        */
slouken@588
   551
        this->LockHWSurface = NULL;
slouken@588
   552
        this->UnlockHWSurface = NULL;
slouken@588
   553
        this->UpdateRects = NULL;
slouken@588
   554
        
slouken@588
   555
        current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
slouken@588
   556
        this->UpdateRects = QZ_DoubleBufferUpdate;
slouken@588
   557
        this->LockHWSurface = QZ_LockDoubleBuffer;
slouken@588
   558
        this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
slouken@588
   559
        this->FlipHWSurface = QZ_FlipDoubleBuffer;
slouken@588
   560
slouken@1756
   561
        current->pixels = SDL_malloc (current->pitch * current->h * 2);
slouken@588
   562
        if (current->pixels == NULL) {
slouken@588
   563
            SDL_OutOfMemory ();
slouken@588
   564
            goto ERR_DOUBLEBUF;
slouken@588
   565
        }
slouken@588
   566
        
slouken@588
   567
        sw_buffers[0] = current->pixels;
slouken@588
   568
        sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
slouken@588
   569
        
slouken@588
   570
        quit_thread = NO;
slouken@588
   571
        sem1 = SDL_CreateSemaphore (0);
slouken@588
   572
        sem2 = SDL_CreateSemaphore (1);
slouken@588
   573
        thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
slouken@47
   574
    }
slouken@390
   575
slouken@47
   576
    if ( CGDisplayCanSetPalette (display_id) )
slouken@47
   577
        current->flags |= SDL_HWPALETTE;
slouken@390
   578
slouken@4065
   579
    /* Check if we should recreate the window */
slouken@4065
   580
    if (qz_window == nil) {
slouken@4065
   581
        /* Manually create a window, avoids having a nib file resource */
slouken@4065
   582
        qz_window = [ [ SDL_QuartzWindow alloc ] 
slouken@4065
   583
            initWithContentRect:contentRect
icculus@4204
   584
                styleMask:0
slouken@4065
   585
                    backing:NSBackingStoreBuffered
slouken@4065
   586
                        defer:NO ];
slouken@4065
   587
slouken@4065
   588
        if (qz_window != nil) {
slouken@4065
   589
            [ qz_window setAcceptsMouseMovedEvents:YES ];
slouken@4065
   590
            [ qz_window setViewsNeedDisplay:NO ];
slouken@4065
   591
        }
slouken@4065
   592
    }
slouken@4065
   593
    /* We already have a window, just change its size */
slouken@4065
   594
    else {
icculus@4267
   595
        [ qz_window setContentSize:contentRect.size ];
icculus@4267
   596
        current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
icculus@4267
   597
        [ window_view setFrameSize:contentRect.size ];
slouken@4065
   598
    }
slouken@4065
   599
slouken@47
   600
    /* Setup OpenGL for a fullscreen context */
slouken@47
   601
    if (flags & SDL_OPENGL) {
slouken@47
   602
slouken@47
   603
        CGLError err;
slouken@47
   604
        CGLContextObj ctx;
slouken@390
   605
slouken@47
   606
        if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
slouken@272
   607
            goto ERR_NO_GL;
slouken@47
   608
        }
slouken@390
   609
slouken@4065
   610
        /* Initialize the NSView and add it to our window.  The presence of a valid window and
slouken@4065
   611
           view allow the cursor to be changed whilst in fullscreen.*/
slouken@4065
   612
        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@4065
   613
        [ [ qz_window contentView ] addSubview:window_view ];	
slouken@4065
   614
        [ window_view release ];
slouken@4065
   615
icculus@4204
   616
        ctx = QZ_GetCGLContextObj (gl_context);
slouken@47
   617
        err = CGLSetFullScreen (ctx);
slouken@390
   618
slouken@47
   619
        if (err) {
slouken@501
   620
            SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
slouken@47
   621
            goto ERR_NO_GL;
slouken@47
   622
        }
slouken@390
   623
slouken@47
   624
        [ gl_context makeCurrentContext];
slouken@272
   625
slouken@272
   626
        glClear (GL_COLOR_BUFFER_BIT);
slouken@272
   627
slouken@272
   628
        [ gl_context flushBuffer ];
slouken@390
   629
slouken@47
   630
        current->flags |= SDL_OPENGL;
slouken@47
   631
    }
slouken@47
   632
slouken@47
   633
    /* If we don't hide menu bar, it will get events and interrupt the program */
slouken@47
   634
    HideMenuBar ();
slouken@272
   635
icculus@1340
   636
    /* Fade in again (asynchronously) */
icculus@1340
   637
    if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
icculus@1340
   638
        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   639
        CGReleaseDisplayFadeReservation(fade_token);
icculus@1340
   640
    }
slouken@390
   641
slouken@435
   642
    /* 
slouken@501
   643
        There is a bug in Cocoa where NSScreen doesn't synchronize
slouken@501
   644
        with CGDirectDisplay, so the main screen's frame is wrong.
slouken@501
   645
        As a result, coordinate translation produces incorrect results.
slouken@501
   646
        We can hack around this bug by setting the screen rect
slouken@501
   647
        ourselves. This hack should be removed if/when the bug is fixed.
slouken@435
   648
    */
slouken@435
   649
    screen_rect = NSMakeRect(0,0,width,height);
icculus@4204
   650
    QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
slouken@435
   651
slouken@47
   652
    /* Save the flags to ensure correct tear-down */
slouken@47
   653
    mode_flags = current->flags;
slouken@390
   654
slouken@1629
   655
    /* Set app state, hide cursor if necessary, ... */
slouken@1629
   656
    QZ_DoActivate(this);
icculus@1119
   657
slouken@47
   658
    return current;
slouken@47
   659
slouken@390
   660
    /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
slouken@588
   661
ERR_NO_GL:      
slouken@588
   662
ERR_DOUBLEBUF:  CGDisplaySwitchToMode (display_id, save_mode);
slouken@588
   663
ERR_NO_SWITCH:  CGReleaseAllDisplays ();
icculus@1340
   664
ERR_NO_CAPTURE:
icculus@1340
   665
ERR_NO_MATCH:   if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
icculus@1340
   666
                    CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   667
                    CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   668
                }
icculus@1340
   669
                return NULL;
slouken@47
   670
}
slouken@47
   671
slouken@47
   672
static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
slouken@4317
   673
                                         int height, int *bpp, Uint32 flags)
slouken@4317
   674
{
slouken@58
   675
    unsigned int style;
slouken@501
   676
    NSRect contentRect;
slouken@657
   677
    int center_window = 1;
slouken@657
   678
    int origin_x, origin_y;
icculus@1340
   679
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
slouken@390
   680
slouken@58
   681
    current->flags = 0;
slouken@58
   682
    current->w = width;
slouken@47
   683
    current->h = height;
slouken@501
   684
    
slouken@501
   685
    contentRect = NSMakeRect (0, 0, width, height);
icculus@4265
   686
slouken@501
   687
    /*
slouken@501
   688
        Check if we should completely destroy the previous mode 
slouken@501
   689
        - If it is fullscreen
slouken@501
   690
        - If it has different noframe or resizable attribute
slouken@501
   691
        - If it is OpenGL (since gl attributes could be different)
slouken@501
   692
        - If new mode is OpenGL, but previous mode wasn't
slouken@501
   693
    */
icculus@1340
   694
    if (video_set == SDL_TRUE) {
icculus@1340
   695
        if (mode_flags & SDL_FULLSCREEN) {
icculus@1340
   696
            /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
   697
               that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
   698
            if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
icculus@1340
   699
                CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
   700
            }
icculus@1340
   701
            QZ_UnsetVideoMode (this, TRUE);
icculus@1340
   702
        }
icculus@1340
   703
        else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
icculus@1340
   704
                  (mode_flags & SDL_OPENGL) || 
icculus@1340
   705
                  (flags & SDL_OPENGL) ) {
icculus@1340
   706
            QZ_UnsetVideoMode (this, TRUE);
icculus@1340
   707
        }
icculus@1340
   708
    }
slouken@683
   709
    
icculus@4204
   710
    /* Sorry, QuickDraw was ripped out. */
icculus@4204
   711
    if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
icculus@4204
   712
        SDL_SetError ("Embedded QuickDraw windows are no longer supported");
icculus@4204
   713
        if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@4204
   714
            CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@4204
   715
            CGReleaseDisplayFadeReservation (fade_token);
slouken@683
   716
        }
icculus@4204
   717
        return NULL;
slouken@683
   718
    }
icculus@4204
   719
slouken@501
   720
    /* Check if we should recreate the window */
slouken@501
   721
    if (qz_window == nil) {
slouken@501
   722
    
slouken@501
   723
        /* Set the window style based on input flags */
slouken@501
   724
        if ( flags & SDL_NOFRAME ) {
slouken@501
   725
            style = NSBorderlessWindowMask;
slouken@501
   726
            current->flags |= SDL_NOFRAME;
slouken@501
   727
        } else {
slouken@501
   728
            style = NSTitledWindowMask;
slouken@501
   729
            style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
slouken@501
   730
            if ( flags & SDL_RESIZABLE ) {
slouken@501
   731
                style |= NSResizableWindowMask;
slouken@501
   732
                current->flags |= SDL_RESIZABLE;
slouken@501
   733
            }
slouken@501
   734
        }
icculus@4265
   735
slouken@501
   736
        /* Manually create a window, avoids having a nib file resource */
slouken@501
   737
        qz_window = [ [ SDL_QuartzWindow alloc ] 
slouken@501
   738
            initWithContentRect:contentRect
slouken@501
   739
                styleMask:style 
slouken@501
   740
                    backing:NSBackingStoreBuffered
slouken@501
   741
                        defer:NO ];
slouken@501
   742
                          
slouken@501
   743
        if (qz_window == nil) {
slouken@501
   744
            SDL_SetError ("Could not create the Cocoa window");
icculus@1340
   745
            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   746
                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   747
                CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   748
            }
slouken@501
   749
            return NULL;
slouken@501
   750
        }
icculus@4265
   751
slouken@4090
   752
        /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */
slouken@501
   753
        QZ_SetCaption(this, this->wm_title, this->wm_icon);
slouken@501
   754
        [ qz_window setAcceptsMouseMovedEvents:YES ];
slouken@501
   755
        [ qz_window setViewsNeedDisplay:NO ];
icculus@4265
   756
icculus@4265
   757
        if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
icculus@4265
   758
            /* have to flip the Y value (NSPoint is lower left corner origin) */
icculus@4265
   759
            [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))];
icculus@4265
   760
            center_window = 0;
icculus@4267
   761
        } else if ( center_window ) {
slouken@657
   762
            [ qz_window center ];
slouken@657
   763
        }
icculus@4265
   764
slouken@501
   765
        [ qz_window setDelegate:
slouken@4090
   766
            [ [ SDL_QuartzWindowDelegate alloc ] init ] ];
slouken@4070
   767
        [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
slouken@501
   768
    }
slouken@501
   769
    /* We already have a window, just change its size */
slouken@501
   770
    else {
icculus@4267
   771
        [ qz_window setContentSize:contentRect.size ];
icculus@4267
   772
        current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
icculus@4267
   773
        [ window_view setFrameSize:contentRect.size ];
slouken@501
   774
    }
slouken@390
   775
slouken@501
   776
    /* For OpenGL, we bind the context to a subview */
slouken@47
   777
    if ( flags & SDL_OPENGL ) {
slouken@390
   778
icculus@1183
   779
        if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
icculus@1340
   780
            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   781
                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   782
                CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   783
            }
slouken@47
   784
            return NULL;
slouken@47
   785
        }
slouken@390
   786
slouken@501
   787
        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@832
   788
        [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
slouken@501
   789
        [ [ qz_window contentView ] addSubview:window_view ];
slouken@501
   790
        [ gl_context setView: window_view ];
slouken@501
   791
        [ window_view release ];
slouken@47
   792
        [ gl_context makeCurrentContext];
slouken@158
   793
        [ qz_window makeKeyAndOrderFront:nil ];
slouken@47
   794
        current->flags |= SDL_OPENGL;
slouken@47
   795
    }
icculus@4204
   796
    /* For 2D, we build a CGBitmapContext */
slouken@47
   797
    else {
icculus@4204
   798
        CGColorSpaceRef cgColorspace;
slouken@390
   799
slouken@501
   800
        /* Only recreate the view if it doesn't already exist */
slouken@501
   801
        if (window_view == nil) {
slouken@501
   802
        
icculus@4204
   803
            window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@832
   804
            [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
slouken@501
   805
            [ [ qz_window contentView ] addSubview:window_view ];
slouken@501
   806
            [ window_view release ];
slouken@501
   807
            [ qz_window makeKeyAndOrderFront:nil ];
slouken@501
   808
        }
slouken@501
   809
        
icculus@4204
   810
        cgColorspace = CGColorSpaceCreateDeviceRGB();
icculus@4204
   811
        current->pitch = 4 * current->w;
icculus@4204
   812
        current->pixels = SDL_malloc (current->h * current->pitch);
slouken@498
   813
        
icculus@4204
   814
        cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
icculus@4204
   815
                        8, current->pitch, cgColorspace,
icculus@4204
   816
                        kCGImageAlphaNoneSkipFirst);
icculus@4204
   817
        CGColorSpaceRelease (cgColorspace);
icculus@4204
   818
        
icculus@4204
   819
        current->flags |= SDL_SWSURFACE;
icculus@4204
   820
        current->flags |= SDL_ASYNCBLIT;
icculus@4204
   821
        current->hwdata = (void *) cg_context;
icculus@4204
   822
        
slouken@498
   823
        this->UpdateRects     = QZ_UpdateRects;
icculus@4204
   824
        this->LockHWSurface   = QZ_LockHWSurface;
icculus@4204
   825
        this->UnlockHWSurface = QZ_UnlockHWSurface;
slouken@47
   826
    }
slouken@390
   827
slouken@272
   828
    /* Save flags to ensure correct teardown */
slouken@272
   829
    mode_flags = current->flags;
slouken@390
   830
icculus@1340
   831
    /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
icculus@1340
   832
    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   833
        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   834
        CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   835
    }
icculus@1340
   836
slouken@47
   837
    return current;
slouken@47
   838
}
slouken@47
   839
slouken@390
   840
static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
slouken@4317
   841
                                     int height, int bpp, Uint32 flags)
slouken@4317
   842
{
slouken@47
   843
    current->flags = 0;
icculus@852
   844
    current->pixels = NULL;
slouken@390
   845
slouken@47
   846
    /* Setup full screen video */
slouken@47
   847
    if ( flags & SDL_FULLSCREEN ) {
slouken@47
   848
        current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
slouken@47
   849
        if (current == NULL)
slouken@47
   850
            return NULL;
slouken@47
   851
    }
slouken@47
   852
    /* Setup windowed video */
slouken@47
   853
    else {
icculus@4204
   854
        /* Force bpp to 32 */
icculus@4204
   855
        bpp = 32;
icculus@1183
   856
        current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags);
slouken@47
   857
        if (current == NULL)
slouken@47
   858
            return NULL;
slouken@47
   859
    }
slouken@390
   860
slouken@47
   861
    /* Setup the new pixel format */
slouken@47
   862
    {
slouken@390
   863
        int amask = 0,
slouken@390
   864
        rmask = 0,
slouken@390
   865
        gmask = 0,
slouken@390
   866
        bmask = 0;
slouken@390
   867
slouken@47
   868
        switch (bpp) {
slouken@47
   869
            case 16:   /* (1)-5-5-5 RGB */
slouken@390
   870
                amask = 0;
slouken@58
   871
                rmask = 0x7C00;
slouken@58
   872
                gmask = 0x03E0;
slouken@58
   873
                bmask = 0x001F;
slouken@47
   874
                break;
slouken@47
   875
            case 24:
slouken@47
   876
                SDL_SetError ("24bpp is not available");
slouken@47
   877
                return NULL;
slouken@47
   878
            case 32:   /* (8)-8-8-8 ARGB */
slouken@155
   879
                amask = 0x00000000;
slouken@4236
   880
		if ( flags & SDL_FULLSCREEN )
slouken@4236
   881
		{
slouken@4236
   882
			rmask = 0x00FF0000;
slouken@4236
   883
			gmask = 0x0000FF00;
slouken@4236
   884
			bmask = 0x000000FF;
slouken@4236
   885
		}
slouken@4236
   886
		else
slouken@4236
   887
		{
icculus@4204
   888
#ifdef __LITTLE_ENDIAN__
slouken@4236
   889
			rmask = 0x0000FF00;
slouken@4236
   890
			gmask = 0x00FF0000;
slouken@4236
   891
			bmask = 0xFF000000;
icculus@4204
   892
#else
slouken@4236
   893
			rmask = 0x00FF0000;
slouken@4236
   894
			gmask = 0x0000FF00;
slouken@4236
   895
			bmask = 0x000000FF;
icculus@4204
   896
#endif
slouken@4236
   897
		}
slouken@47
   898
                break;
slouken@47
   899
        }
slouken@390
   900
slouken@47
   901
        if ( ! SDL_ReallocFormat (current, bpp,
slouken@47
   902
                                  rmask, gmask, bmask, amask ) ) {
slouken@390
   903
            SDL_SetError ("Couldn't reallocate pixel format");
slouken@390
   904
            return NULL;
slouken@4236
   905
        }
slouken@47
   906
    }
slouken@390
   907
slouken@272
   908
    /* Signal successful completion (used internally) */
slouken@155
   909
    video_set = SDL_TRUE;
slouken@390
   910
slouken@47
   911
    return current;
slouken@47
   912
}
slouken@47
   913
slouken@4317
   914
static int QZ_ToggleFullScreen (_THIS, int on)
slouken@4317
   915
{
slouken@576
   916
    return 0;
slouken@47
   917
}
slouken@47
   918
slouken@390
   919
static int QZ_SetColors (_THIS, int first_color, int num_colors,
slouken@4317
   920
                         SDL_Color *colors)
slouken@4317
   921
{
slouken@47
   922
    CGTableCount  index;
slouken@47
   923
    CGDeviceColor color;
slouken@390
   924
slouken@47
   925
    for (index = first_color; index < first_color+num_colors; index++) {
slouken@390
   926
slouken@47
   927
        /* Clamp colors between 0.0 and 1.0 */
slouken@47
   928
        color.red   = colors->r / 255.0;
slouken@47
   929
        color.blue  = colors->b / 255.0;
slouken@47
   930
        color.green = colors->g / 255.0;
slouken@390
   931
slouken@47
   932
        colors++;
slouken@390
   933
slouken@47
   934
        CGPaletteSetColorAtIndex (palette, color, index);
slouken@47
   935
    }
slouken@390
   936
slouken@47
   937
    if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
slouken@47
   938
        return 0;
slouken@390
   939
slouken@47
   940
    return 1;
slouken@47
   941
}
slouken@47
   942
slouken@4317
   943
static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface)
slouken@4317
   944
{
slouken@588
   945
    return 1;
slouken@588
   946
}
slouken@588
   947
slouken@4317
   948
static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface)
slouken@4317
   949
{
slouken@588
   950
}
slouken@588
   951
slouken@4317
   952
/* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
slouken@4317
   953
static AbsoluteTime QZ_SecondsToAbsolute ( double seconds )
slouken@4317
   954
{
slouken@588
   955
    union
slouken@588
   956
    {
slouken@761
   957
        UInt64 i;
slouken@588
   958
        Nanoseconds ns;
slouken@588
   959
    } temp;
slouken@588
   960
        
slouken@588
   961
    temp.i = seconds * 1000000000.0;
slouken@588
   962
    
slouken@588
   963
    return NanosecondsToAbsolute ( temp.ns );
slouken@588
   964
}
slouken@588
   965
slouken@4317
   966
static int QZ_ThreadFlip (_THIS)
slouken@4317
   967
{
slouken@588
   968
    Uint8 *src, *dst;
slouken@588
   969
    int skip, len, h;
slouken@588
   970
    
slouken@588
   971
    /*
slouken@588
   972
        Give this thread the highest scheduling priority possible,
slouken@588
   973
        in the hopes that it will immediately run after the VBL delay
slouken@588
   974
    */
slouken@588
   975
    {
slouken@588
   976
        pthread_t current_thread;
slouken@588
   977
        int policy;
slouken@588
   978
        struct sched_param param;
slouken@588
   979
        
slouken@588
   980
        current_thread = pthread_self ();
slouken@588
   981
        pthread_getschedparam (current_thread, &policy, &param);
slouken@588
   982
        policy = SCHED_RR;
slouken@588
   983
        param.sched_priority = sched_get_priority_max (policy);
slouken@588
   984
        pthread_setschedparam (current_thread, policy, &param);
slouken@588
   985
    }
slouken@588
   986
    
slouken@588
   987
    while (1) {
slouken@588
   988
    
slouken@588
   989
        SDL_SemWait (sem1);
slouken@588
   990
        if (quit_thread)
slouken@588
   991
            return 0;
slouken@588
   992
                
icculus@1219
   993
        /*
icculus@1219
   994
         * We have to add SDL_VideoSurface->offset here, since we might be a
icculus@1219
   995
         *  smaller surface in the center of the framebuffer (you asked for
icculus@1219
   996
         *  a fullscreen resolution smaller than the hardware could supply
icculus@1219
   997
         *  so SDL is centering it in a bigger resolution)...
icculus@1219
   998
         */
slouken@1487
   999
        dst = (Uint8 *)CGDisplayBaseAddress (display_id) + SDL_VideoSurface->offset;
icculus@1219
  1000
        src = current_buffer + SDL_VideoSurface->offset;
slouken@588
  1001
        len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
slouken@588
  1002
        h = SDL_VideoSurface->h;
slouken@588
  1003
        skip = SDL_VideoSurface->pitch;
slouken@588
  1004
    
slouken@588
  1005
        /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
slouken@588
  1006
        {
slouken@588
  1007
            
slouken@588
  1008
            /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
slouken@588
  1009
            double refreshRate;
slouken@588
  1010
            double linesPerSecond;
slouken@588
  1011
            double target;
slouken@588
  1012
            double position;
slouken@588
  1013
            double adjustment;
slouken@588
  1014
            AbsoluteTime nextTime;        
slouken@588
  1015
            CFNumberRef refreshRateCFNumber;
slouken@588
  1016
            
slouken@588
  1017
            refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
slouken@588
  1018
            if ( NULL == refreshRateCFNumber ) {
slouken@588
  1019
                SDL_SetError ("Mode has no refresh rate");
slouken@588
  1020
                goto ERROR;
slouken@588
  1021
            }
slouken@588
  1022
            
slouken@588
  1023
            if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
slouken@588
  1024
                SDL_SetError ("Error getting refresh rate");
slouken@588
  1025
                goto ERROR;
slouken@588
  1026
            }
slouken@588
  1027
            
slouken@588
  1028
            if ( 0 == refreshRate ) {
slouken@588
  1029
               
slouken@588
  1030
               SDL_SetError ("Display has no refresh rate, using 60hz");
slouken@588
  1031
                
slouken@588
  1032
                /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
slouken@588
  1033
                refreshRate = 60.0;
slouken@588
  1034
            }
slouken@588
  1035
            
slouken@588
  1036
            linesPerSecond = refreshRate * h;
slouken@588
  1037
            target = h;
slouken@588
  1038
        
slouken@588
  1039
            /* Figure out the first delay so we start off about right */
slouken@588
  1040
            position = CGDisplayBeamPosition (display_id);
slouken@588
  1041
            if (position > target)
slouken@588
  1042
                position = 0;
slouken@588
  1043
            
slouken@588
  1044
            adjustment = (target - position) / linesPerSecond; 
slouken@588
  1045
            
slouken@588
  1046
            nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
slouken@588
  1047
        
slouken@588
  1048
            MPDelayUntil (&nextTime);
slouken@588
  1049
        }
slouken@588
  1050
        
slouken@588
  1051
        
slouken@588
  1052
        /* On error, skip VBL delay */
slouken@588
  1053
        ERROR:
slouken@588
  1054
        
icculus@4204
  1055
        /* TODO: use CGContextDrawImage here too!  Create two CGContextRefs the same way we
icculus@4204
  1056
           create two buffers, replace current_buffer with current_context and set it
icculus@4204
  1057
           appropriately in QZ_FlipDoubleBuffer.  */
slouken@588
  1058
        while ( h-- ) {
slouken@588
  1059
        
slouken@1756
  1060
            SDL_memcpy (dst, src, len);
slouken@588
  1061
            src += skip;
slouken@588
  1062
            dst += skip;
slouken@588
  1063
        }
slouken@588
  1064
        
slouken@588
  1065
        /* signal flip completion */
slouken@588
  1066
        SDL_SemPost (sem2);
slouken@588
  1067
    }
slouken@588
  1068
    
slouken@588
  1069
    return 0;
slouken@588
  1070
}
slouken@588
  1071
        
slouken@4317
  1072
static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface)
slouken@4317
  1073
{
slouken@588
  1074
    /* wait for previous flip to complete */
slouken@588
  1075
    SDL_SemWait (sem2);
slouken@588
  1076
    
slouken@588
  1077
    current_buffer = surface->pixels;
slouken@588
  1078
        
slouken@588
  1079
    if (surface->pixels == sw_buffers[0])
slouken@588
  1080
        surface->pixels = sw_buffers[1];
slouken@588
  1081
    else
slouken@588
  1082
        surface->pixels = sw_buffers[0];
slouken@588
  1083
    
slouken@588
  1084
    /* signal worker thread to do the flip */
slouken@588
  1085
    SDL_SemPost (sem1);
slouken@588
  1086
    
slouken@588
  1087
    return 0;
slouken@588
  1088
}
slouken@588
  1089
slouken@4317
  1090
static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects)
slouken@4317
  1091
{
slouken@588
  1092
    /* perform a flip if someone calls updaterects on a doublebuferred surface */
slouken@588
  1093
    this->FlipHWSurface (this, SDL_VideoSurface);
slouken@588
  1094
}
slouken@588
  1095
slouken@4317
  1096
static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects)
slouken@4317
  1097
{
slouken@390
  1098
#pragma unused(this,num_rects,rects)
slouken@47
  1099
}
slouken@47
  1100
slouken@498
  1101
slouken@761
  1102
/* Resize icon, BMP format */
slouken@761
  1103
static const unsigned char QZ_ResizeIcon[] = {
slouken@761
  1104
    0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
slouken@761
  1105
    0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
slouken@761
  1106
    0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
slouken@761
  1107
    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1108
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1109
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1110
    0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
slouken@761
  1111
    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
slouken@761
  1112
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
slouken@761
  1113
    0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
slouken@761
  1114
    0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1115
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
slouken@761
  1116
    0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
slouken@761
  1117
    0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1118
    0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
slouken@761
  1119
    0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1120
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
slouken@761
  1121
    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
slouken@761
  1122
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1123
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
slouken@761
  1124
    0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1125
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1126
    0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
slouken@761
  1127
    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1128
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
slouken@761
  1129
    0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1130
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1131
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
slouken@761
  1132
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1133
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1134
    0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
  1135
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1136
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
slouken@761
  1137
    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1138
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
  1139
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
slouken@761
  1140
};
slouken@761
  1141
slouken@4317
  1142
static void QZ_DrawResizeIcon (_THIS)
slouken@4317
  1143
{
slouken@761
  1144
    /* Check if we should draw the resize icon */
slouken@761
  1145
    if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
slouken@761
  1146
    
icculus@4204
  1147
        SDL_Rect icon_rect;
slouken@761
  1148
        
icculus@4204
  1149
        /* Create the icon image */
icculus@4204
  1150
        if (resize_icon == NULL) {
icculus@4204
  1151
        
icculus@4204
  1152
            SDL_RWops *rw;
icculus@4204
  1153
            SDL_Surface *tmp;
slouken@761
  1154
            
icculus@4204
  1155
            rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
icculus@4204
  1156
            tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
icculus@4204
  1157
                                                            
icculus@4204
  1158
            resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);
icculus@4204
  1159
            SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
slouken@761
  1160
            
icculus@4204
  1161
            SDL_FreeSurface (tmp);
icculus@4204
  1162
        }
slouken@761
  1163
            
icculus@4204
  1164
        icon_rect.x = SDL_VideoSurface->w - 13;
icculus@4204
  1165
        icon_rect.y = SDL_VideoSurface->h - 13;
icculus@4204
  1166
        icon_rect.w = 13;
icculus@4204
  1167
        icon_rect.h = 13;
icculus@4204
  1168
            
icculus@4204
  1169
        SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
slouken@761
  1170
    }
slouken@761
  1171
}
slouken@761
  1172
slouken@4317
  1173
static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects)
slouken@4317
  1174
{
slouken@47
  1175
    if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
slouken@47
  1176
        QZ_GL_SwapBuffers (this);
slouken@47
  1177
    }
slouken@501
  1178
    else if ( [ qz_window isMiniaturized ] ) {
slouken@501
  1179
    
slouken@501
  1180
        /* Do nothing if miniaturized */
slouken@272
  1181
    }
slouken@501
  1182
    
slouken@47
  1183
    else {
icculus@4204
  1184
        CGContextRef cgc = (CGContextRef)
icculus@4204
  1185
            [[NSGraphicsContext graphicsContextWithWindow: qz_window]
icculus@4204
  1186
                graphicsPort];
icculus@4204
  1187
        QZ_DrawResizeIcon (this);
icculus@4204
  1188
        CGContextFlush (cg_context);
icculus@4204
  1189
        CGImageRef image = CGBitmapContextCreateImage (cg_context);
icculus@4204
  1190
        CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height);
slouken@501
  1191
        
icculus@4204
  1192
        CGContextDrawImage (cgc, rectangle, image);
icculus@4204
  1193
        CGImageRelease(image);
icculus@4204
  1194
        CGContextFlush (cgc);
icculus@4204
  1195
        CGContextRelease (cgc);
slouken@47
  1196
    }
slouken@47
  1197
}
slouken@47
  1198
slouken@4317
  1199
static void QZ_VideoQuit (_THIS)
slouken@4317
  1200
{
icculus@1340
  1201
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
icculus@1340
  1202
icculus@560
  1203
    /* Restore gamma settings */
icculus@560
  1204
    CGDisplayRestoreColorSyncSettings ();
icculus@560
  1205
icculus@560
  1206
    /* Ensure the cursor will be visible and working when we quit */
icculus@560
  1207
    CGDisplayShowCursor (display_id);
icculus@560
  1208
    CGAssociateMouseAndMouseCursorPosition (1);
icculus@560
  1209
    
icculus@1340
  1210
    if (mode_flags & SDL_FULLSCREEN) {
icculus@1340
  1211
        /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
  1212
           that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
  1213
        if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
icculus@1340
  1214
            CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
  1215
        }
icculus@1340
  1216
        QZ_UnsetVideoMode (this, TRUE);
icculus@1340
  1217
        if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
  1218
            CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
  1219
            CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
  1220
        }
icculus@1340
  1221
    }
icculus@1340
  1222
    else
icculus@1340
  1223
        QZ_UnsetVideoMode (this, TRUE);
icculus@1340
  1224
    
slouken@47
  1225
    CGPaletteRelease (palette);
icculus@1181
  1226
icculus@1189
  1227
    if (opengl_library) {
icculus@1189
  1228
        SDL_UnloadObject(opengl_library);
icculus@1189
  1229
        opengl_library = NULL;
icculus@1181
  1230
    }
icculus@1181
  1231
    this->gl_config.driver_loaded = 0;
slouken@4049
  1232
slouken@4049
  1233
    if (field_edit) {
slouken@4049
  1234
        [field_edit release];
slouken@4049
  1235
        field_edit = NULL;
slouken@4049
  1236
    }
slouken@47
  1237
}
slouken@47
  1238
slouken@674
  1239
#if 0 /* Not used (apparently, it's really slow) */
slouken@4317
  1240
static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color)
slouken@4317
  1241
{
slouken@47
  1242
    CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color);
slouken@272
  1243
slouken@47
  1244
    return 0;
slouken@47
  1245
}
slouken@674
  1246
#endif
slouken@47
  1247
slouken@4317
  1248
static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1249
{
slouken@47
  1250
    return 1;
slouken@47
  1251
}
slouken@47
  1252
slouken@4317
  1253
static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1254
{
slouken@47
  1255
}
slouken@47
  1256
slouken@4317
  1257
static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1258
{
icculus@1120
  1259
    return(-1); /* unallowed (no HWSURFACE support here). */
icculus@1120
  1260
}
icculus@1120
  1261
slouken@4317
  1262
static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface)
slouken@4317
  1263
{
slouken@47
  1264
}
slouken@47
  1265
slouken@47
  1266
/*
slouken@390
  1267
 int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) {
slouken@390
  1268
     return 0;
slouken@390
  1269
 }
slouken@390
  1270
 */
slouken@47
  1271
slouken@47
  1272
/* Gamma functions */
slouken@4317
  1273
int QZ_SetGamma (_THIS, float red, float green, float blue)
slouken@4317
  1274
{
slouken@47
  1275
    const CGGammaValue min = 0.0, max = 1.0;
slouken@272
  1276
slouken@272
  1277
    if (red == 0.0)
slouken@272
  1278
        red = FLT_MAX;
slouken@272
  1279
    else
slouken@272
  1280
        red = 1.0 / red;
slouken@272
  1281
slouken@272
  1282
    if (green == 0.0)
slouken@272
  1283
        green = FLT_MAX;
slouken@272
  1284
    else
slouken@272
  1285
        green = 1.0 / green;
slouken@272
  1286
slouken@272
  1287
    if (blue == 0.0)
slouken@272
  1288
        blue = FLT_MAX;
slouken@272
  1289
    else
slouken@272
  1290
        blue  = 1.0 / blue;
slouken@390
  1291
slouken@390
  1292
    if ( CGDisplayNoErr == CGSetDisplayTransferByFormula
slouken@390
  1293
         (display_id, min, max, red, min, max, green, min, max, blue) ) {
slouken@390
  1294
slouken@272
  1295
        return 0;
slouken@272
  1296
    }
slouken@272
  1297
    else {
slouken@390
  1298
slouken@47
  1299
        return -1;
slouken@272
  1300
    }
slouken@47
  1301
}
slouken@47
  1302
slouken@4317
  1303
int QZ_GetGamma (_THIS, float *red, float *green, float *blue)
slouken@4317
  1304
{
slouken@47
  1305
    CGGammaValue dummy;
slouken@272
  1306
    if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
slouken@390
  1307
         (display_id, &dummy, &dummy, red,
slouken@390
  1308
          &dummy, &dummy, green, &dummy, &dummy, blue) )
slouken@390
  1309
slouken@272
  1310
        return 0;
slouken@272
  1311
    else
slouken@47
  1312
        return -1;
slouken@47
  1313
}
slouken@47
  1314
slouken@4317
  1315
int QZ_SetGammaRamp (_THIS, Uint16 *ramp)
slouken@4317
  1316
{
slouken@390
  1317
    const CGTableCount tableSize = 255;
slouken@390
  1318
    CGGammaValue redTable[tableSize];
slouken@390
  1319
    CGGammaValue greenTable[tableSize];
slouken@390
  1320
    CGGammaValue blueTable[tableSize];
slouken@390
  1321
slouken@390
  1322
    int i;
slouken@390
  1323
slouken@390
  1324
    /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
slouken@390
  1325
    for (i = 0; i < 256; i++)
slouken@390
  1326
        redTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1327
slouken@390
  1328
    for (i=256; i < 512; i++)
slouken@390
  1329
        greenTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1330
slouken@390
  1331
    for (i=512; i < 768; i++)
slouken@390
  1332
        blueTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1333
slouken@390
  1334
    if ( CGDisplayNoErr == CGSetDisplayTransferByTable
slouken@390
  1335
         (display_id, tableSize, redTable, greenTable, blueTable) )
slouken@272
  1336
        return 0;
slouken@272
  1337
    else
slouken@47
  1338
        return -1;
slouken@47
  1339
}
slouken@47
  1340
slouken@4317
  1341
int QZ_GetGammaRamp (_THIS, Uint16 *ramp)
slouken@4317
  1342
{
slouken@47
  1343
    const CGTableCount tableSize = 255;
slouken@47
  1344
    CGGammaValue redTable[tableSize];
slouken@47
  1345
    CGGammaValue greenTable[tableSize];
slouken@47
  1346
    CGGammaValue blueTable[tableSize];
slouken@47
  1347
    CGTableCount actual;
slouken@47
  1348
    int i;
slouken@390
  1349
slouken@390
  1350
    if ( CGDisplayNoErr != CGGetDisplayTransferByTable
slouken@390
  1351
         (display_id, tableSize, redTable, greenTable, blueTable, &actual) ||
slouken@390
  1352
         actual != tableSize)
slouken@390
  1353
slouken@47
  1354
        return -1;
slouken@390
  1355
slouken@47
  1356
    /* Pack tables into one array, with values from 0 to 65535 */
slouken@47
  1357
    for (i = 0; i < 256; i++)
slouken@47
  1358
        ramp[i] = redTable[i % 256] * 65535.0;
slouken@390
  1359
slouken@47
  1360
    for (i=256; i < 512; i++)
slouken@47
  1361
        ramp[i] = greenTable[i % 256] * 65535.0;
slouken@390
  1362
slouken@47
  1363
    for (i=512; i < 768; i++)
slouken@47
  1364
        ramp[i] = blueTable[i % 256] * 65535.0;
slouken@390
  1365
slouken@390
  1366
    return 0;
slouken@47
  1367
}
slouken@47
  1368