src/video/quartz/SDL_QuartzVideo.m
author Ryan C. Gordon <icculus@icculus.org>
Wed, 10 Aug 2011 01:25:00 -0400
branchSDL-1.2
changeset 5606 cf638ceb1f2c
parent 5574 7b7ad16f9704
child 5619 8e0dd46ad0e0
permissions -rw-r--r--
Fixed SDL 1.2 fullscreen OpenGL on Mac OS X 10.7.

I'm not sure if there was a way to make CGLSetFullScreen() work, but the
Cocoa path works fine on all Intel Macs.

Please note that the "official" way to do fullscreen in 10.7 is just to make
a window the size of the display mode, and OS X is now smart enough to
recognize this as "fullscreen", but this helps earlier versions of the OS in
any case.
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
slouken@4389
    27
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060   /* Fixed in Snow Leopard */
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 void         QZ_UpdateRects      (_THIS, int num_rects, SDL_Rect *rects);
slouken@761
    88
static void         QZ_VideoQuit        (_THIS);
slouken@761
    89
slouken@761
    90
static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface);
slouken@761
    91
static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
icculus@1120
    92
static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
slouken@761
    93
static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
slouken@47
    94
slouken@47
    95
/* Bootstrap binding, enables entry point into the driver */
slouken@47
    96
VideoBootStrap QZ_bootstrap = {
slouken@272
    97
    "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
slouken@47
    98
};
slouken@47
    99
slouken@588
   100
slouken@47
   101
/* Bootstrap functions */
slouken@4317
   102
static int QZ_Available ()
slouken@4317
   103
{
slouken@47
   104
    return 1;
slouken@47
   105
}
slouken@47
   106
slouken@4317
   107
static SDL_VideoDevice* QZ_CreateDevice (int device_index)
slouken@4317
   108
{
slouken@272
   109
#pragma unused (device_index)
slouken@47
   110
slouken@47
   111
    SDL_VideoDevice *device;
slouken@47
   112
    SDL_PrivateVideoData *hidden;
slouken@47
   113
slouken@1756
   114
    device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
slouken@1756
   115
    hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
slouken@47
   116
slouken@47
   117
    if (device == NULL || hidden == NULL)
slouken@47
   118
        SDL_OutOfMemory ();
slouken@47
   119
slouken@1756
   120
    SDL_memset (device, 0, sizeof (*device) );
slouken@1756
   121
    SDL_memset (hidden, 0, sizeof (*hidden) );
slouken@272
   122
slouken@47
   123
    device->hidden = hidden;
slouken@47
   124
slouken@47
   125
    device->VideoInit        = QZ_VideoInit;
slouken@47
   126
    device->ListModes        = QZ_ListModes;
slouken@47
   127
    device->SetVideoMode     = QZ_SetVideoMode;
slouken@47
   128
    device->ToggleFullScreen = QZ_ToggleFullScreen;
icculus@1212
   129
    device->UpdateMouse      = QZ_UpdateMouse;
slouken@47
   130
    device->SetColors        = QZ_SetColors;
icculus@5569
   131
    device->UpdateRects      = QZ_UpdateRects;
slouken@47
   132
    device->VideoQuit        = QZ_VideoQuit;
slouken@272
   133
slouken@47
   134
    device->LockHWSurface   = QZ_LockHWSurface;
slouken@47
   135
    device->UnlockHWSurface = QZ_UnlockHWSurface;
icculus@1120
   136
    device->AllocHWSurface   = QZ_AllocHWSurface;
slouken@47
   137
    device->FreeHWSurface   = QZ_FreeHWSurface;
slouken@47
   138
slouken@47
   139
    device->SetGamma     = QZ_SetGamma;
slouken@47
   140
    device->GetGamma     = QZ_GetGamma;
slouken@47
   141
    device->SetGammaRamp = QZ_SetGammaRamp;
slouken@47
   142
    device->GetGammaRamp = QZ_GetGammaRamp;
slouken@47
   143
slouken@47
   144
    device->GL_GetProcAddress = QZ_GL_GetProcAddress;
slouken@47
   145
    device->GL_GetAttribute   = QZ_GL_GetAttribute;
slouken@47
   146
    device->GL_MakeCurrent    = QZ_GL_MakeCurrent;
slouken@47
   147
    device->GL_SwapBuffers    = QZ_GL_SwapBuffers;
slouken@47
   148
    device->GL_LoadLibrary    = QZ_GL_LoadLibrary;
slouken@272
   149
slouken@47
   150
    device->FreeWMCursor   = QZ_FreeWMCursor;
slouken@47
   151
    device->CreateWMCursor = QZ_CreateWMCursor;
slouken@47
   152
    device->ShowWMCursor   = QZ_ShowWMCursor;
slouken@47
   153
    device->WarpWMCursor   = QZ_WarpWMCursor;
slouken@47
   154
    device->MoveWMCursor   = QZ_MoveWMCursor;
slouken@47
   155
    device->CheckMouseMode = QZ_CheckMouseMode;
slouken@47
   156
    device->InitOSKeymap   = QZ_InitOSKeymap;
slouken@47
   157
    device->PumpEvents     = QZ_PumpEvents;
slouken@47
   158
slouken@47
   159
    device->SetCaption    = QZ_SetCaption;
slouken@47
   160
    device->SetIcon       = QZ_SetIcon;
slouken@47
   161
    device->IconifyWindow = QZ_IconifyWindow;
slouken@47
   162
    /*device->GetWMInfo     = QZ_GetWMInfo;*/
slouken@47
   163
    device->GrabInput     = QZ_GrabInput;
slouken@272
   164
icculus@4204
   165
    /*
icculus@4204
   166
     * This is a big hassle, needing QuickDraw and QuickTime on older
icculus@4204
   167
     *  systems, and god knows what on 10.6, so we immediately fail here,
icculus@4204
   168
     *  which causes SDL to make an RGB surface and manage the YUV overlay
icculus@4204
   169
     *  in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel
icculus@4204
   170
     *  shader.  :)
icculus@4204
   171
     */
icculus@4204
   172
    /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/
slouken@390
   173
slouken@390
   174
    device->free             = QZ_DeleteDevice;
slouken@272
   175
slouken@47
   176
    return device;
slouken@47
   177
}
slouken@47
   178
slouken@4317
   179
static void QZ_DeleteDevice (SDL_VideoDevice *device)
slouken@4317
   180
{
slouken@1756
   181
    SDL_free (device->hidden);
slouken@1756
   182
    SDL_free (device);
slouken@47
   183
}
slouken@47
   184
slouken@4317
   185
static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format)
slouken@4317
   186
{
slouken@4049
   187
    NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
icculus@3936
   188
    const char *env = NULL;
slouken@4317
   189
	
slouken@272
   190
    /* Initialize the video settings; this data persists between mode switches */
slouken@272
   191
    display_id = kCGDirectMainDisplay;
slouken@4317
   192
slouken@4319
   193
#if 0 /* The mouse event code needs to take this into account... */
slouken@4319
   194
    env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
slouken@4319
   195
    if ( env ) {
slouken@4319
   196
        int monitor = SDL_atoi(env);
slouken@4319
   197
    	CGDirectDisplayID activeDspys [3];
slouken@4319
   198
    	CGDisplayCount dspyCnt;
slouken@4319
   199
    	CGGetActiveDisplayList (3, activeDspys, &dspyCnt);
slouken@4319
   200
        if ( monitor >= 0 && monitor < dspyCnt ) {
slouken@4319
   201
    	    display_id = activeDspys[monitor];
slouken@4319
   202
        }
slouken@4319
   203
    }
slouken@4319
   204
#endif
slouken@4319
   205
slouken@272
   206
    save_mode  = CGDisplayCurrentMode    (display_id);
slouken@272
   207
    mode_list  = CGDisplayAvailableModes (display_id);
slouken@272
   208
    palette    = CGPaletteCreateDefaultColorPalette ();
slouken@47
   209
slouken@4139
   210
    /* Allow environment override of screensaver disable. */
icculus@3936
   211
    env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
slouken@4139
   212
    if ( env ) {
slouken@4139
   213
        allow_screensaver = SDL_atoi(env);
slouken@4139
   214
    } else {
slouken@4139
   215
#ifdef SDL_VIDEO_DISABLE_SCREENSAVER
slouken@4139
   216
        allow_screensaver = 0;
slouken@4139
   217
#else
slouken@4139
   218
        allow_screensaver = 1;
slouken@4139
   219
#endif
slouken@4139
   220
    }
icculus@3936
   221
slouken@272
   222
    /* Gather some information that is useful to know about the display */
slouken@272
   223
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
slouken@272
   224
                      kCFNumberSInt32Type, &device_bpp);
slouken@47
   225
slouken@272
   226
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
slouken@272
   227
                      kCFNumberSInt32Type, &device_width);
slouken@272
   228
slouken@272
   229
    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
slouken@272
   230
                      kCFNumberSInt32Type, &device_height);
slouken@272
   231
slouken@1545
   232
    /* Determine the current screen size */
slouken@1545
   233
    this->info.current_w = device_width;
slouken@1545
   234
    this->info.current_h = device_height;
slouken@1545
   235
slouken@1545
   236
    /* Determine the default screen depth */
slouken@272
   237
    video_format->BitsPerPixel = device_bpp;
slouken@272
   238
slouken@501
   239
    /* Set misc globals */
slouken@501
   240
    current_grab_mode = SDL_GRAB_OFF;
slouken@761
   241
    cursor_should_be_visible    = YES;
slouken@779
   242
    cursor_visible              = YES;
slouken@823
   243
    current_mods = 0;
slouken@4123
   244
    field_edit =  [[SDLTranslatorResponder alloc] initWithFrame:r];
slouken@501
   245
    
icculus@876
   246
    if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
icculus@876
   247
        system_version = 0;
slouken@934
   248
    
slouken@555
   249
    /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
slouken@555
   250
    QZ_RegisterForSleepNotifications (this);
slouken@555
   251
    
slouken@1271
   252
    /* Fill in some window manager capabilities */
slouken@1271
   253
    this->info.wm_available = 1;
slouken@1271
   254
slouken@272
   255
    return 0;
slouken@47
   256
}
slouken@47
   257
slouken@4317
   258
static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
slouken@4317
   259
{
slouken@272
   260
    CFIndex num_modes;
slouken@47
   261
    CFIndex i;
slouken@47
   262
slouken@47
   263
    int list_size = 0;
slouken@390
   264
slouken@47
   265
    /* Any windowed mode is acceptable */
slouken@47
   266
    if ( (flags & SDL_FULLSCREEN) == 0 )
slouken@47
   267
        return (SDL_Rect**)-1;
slouken@390
   268
slouken@47
   269
    /* Free memory from previous call, if any */
slouken@501
   270
    if ( client_mode_list != NULL ) {
slouken@47
   271
slouken@390
   272
        int i;
slouken@47
   273
slouken@501
   274
        for (i = 0; client_mode_list[i] != NULL; i++)
slouken@1756
   275
            SDL_free (client_mode_list[i]);
slouken@47
   276
slouken@1756
   277
        SDL_free (client_mode_list);
slouken@501
   278
        client_mode_list = NULL;
slouken@47
   279
    }
slouken@390
   280
slouken@272
   281
    num_modes = CFArrayGetCount (mode_list);
slouken@272
   282
slouken@47
   283
    /* Build list of modes with the requested bpp */
slouken@155
   284
    for (i = 0; i < num_modes; i++) {
slouken@390
   285
slouken@155
   286
        CFDictionaryRef onemode;
slouken@155
   287
        CFNumberRef     number;
slouken@390
   288
        int bpp;
slouken@47
   289
slouken@390
   290
        onemode = CFArrayGetValueAtIndex (mode_list, i);
slouken@390
   291
        number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
slouken@390
   292
        CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
slouken@390
   293
slouken@390
   294
        if (bpp == format->BitsPerPixel) {
slouken@390
   295
slouken@390
   296
            int intvalue;
slouken@390
   297
            int hasMode;
slouken@390
   298
            int width, height;
slouken@390
   299
slouken@390
   300
            number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
slouken@390
   301
            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
slouken@390
   302
            width = (Uint16) intvalue;
slouken@390
   303
slouken@390
   304
            number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
slouken@390
   305
            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
slouken@390
   306
            height = (Uint16) intvalue;
slouken@390
   307
slouken@390
   308
            /* Check if mode is already in the list */
slouken@390
   309
            {
slouken@390
   310
                int i;
slouken@390
   311
                hasMode = SDL_FALSE;
slouken@390
   312
                for (i = 0; i < list_size; i++) {
slouken@501
   313
                    if (client_mode_list[i]->w == width && 
slouken@501
   314
                        client_mode_list[i]->h == height) {
slouken@390
   315
                        hasMode = SDL_TRUE;
slouken@390
   316
                        break;
slouken@390
   317
                    }
slouken@155
   318
                }
slouken@155
   319
            }
slouken@47
   320
slouken@390
   321
            /* Grow the list and add mode to the list */
slouken@390
   322
            if ( ! hasMode ) {
slouken@390
   323
slouken@390
   324
                SDL_Rect *rect;
slouken@390
   325
slouken@390
   326
                list_size++;
slouken@390
   327
slouken@501
   328
                if (client_mode_list == NULL)
slouken@501
   329
                    client_mode_list = (SDL_Rect**) 
slouken@1756
   330
                        SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
slouken@390
   331
                else
slouken@501
   332
                    client_mode_list = (SDL_Rect**) 
slouken@1756
   333
                        SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
slouken@390
   334
slouken@1756
   335
                rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
slouken@390
   336
slouken@501
   337
                if (client_mode_list == NULL || rect == NULL) {
slouken@390
   338
                    SDL_OutOfMemory ();
slouken@390
   339
                    return NULL;
slouken@390
   340
                }
slouken@390
   341
icculus@1218
   342
                rect->x = rect->y = 0;
slouken@390
   343
                rect->w = width;
slouken@390
   344
                rect->h = height;
slouken@390
   345
slouken@501
   346
                client_mode_list[list_size-1] = rect;
slouken@501
   347
                client_mode_list[list_size]   = NULL;
slouken@272
   348
            }
slouken@47
   349
        }
slouken@47
   350
    }
slouken@390
   351
slouken@155
   352
    /* Sort list largest to smallest (by area) */
slouken@155
   353
    {
slouken@155
   354
        int i, j;
slouken@155
   355
        for (i = 0; i < list_size; i++) {
slouken@155
   356
            for (j = 0; j < list_size-1; j++) {
slouken@390
   357
slouken@155
   358
                int area1, area2;
slouken@501
   359
                area1 = client_mode_list[j]->w * client_mode_list[j]->h;
slouken@501
   360
                area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
slouken@390
   361
slouken@155
   362
                if (area1 < area2) {
slouken@501
   363
                    SDL_Rect *tmp = client_mode_list[j];
slouken@501
   364
                    client_mode_list[j] = client_mode_list[j+1];
slouken@501
   365
                    client_mode_list[j+1] = tmp;
slouken@155
   366
                }
slouken@155
   367
            }
slouken@155
   368
        }
slouken@155
   369
    }
slouken@501
   370
    return client_mode_list;
slouken@47
   371
}
slouken@47
   372
slouken@657
   373
static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
slouken@657
   374
{
slouken@657
   375
    const char *window = getenv("SDL_VIDEO_WINDOW_POS");
slouken@657
   376
    if ( window ) {
slouken@657
   377
        if ( sscanf(window, "%d,%d", x, y) == 2 ) {
slouken@657
   378
            return SDL_TRUE;
slouken@657
   379
        }
slouken@657
   380
    }
slouken@657
   381
    return SDL_FALSE;
slouken@657
   382
}
slouken@657
   383
slouken@4317
   384
static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop)
slouken@4317
   385
{
slouken@47
   386
    /* Reset values that may change between switches */
slouken@501
   387
    this->info.blit_fill  = 0;
icculus@5569
   388
icculus@4204
   389
    if (cg_context) {
icculus@4204
   390
        CGContextFlush (cg_context);
icculus@4204
   391
        CGContextRelease (cg_context);
icculus@4204
   392
        cg_context = nil;
icculus@4204
   393
    }
icculus@4204
   394
    
slouken@272
   395
    /* Release fullscreen resources */
slouken@47
   396
    if ( mode_flags & SDL_FULLSCREEN ) {
slouken@272
   397
slouken@435
   398
        NSRect screen_rect;
icculus@5569
   399
slouken@4065
   400
        /* If we still have a valid window, close it. */
slouken@4065
   401
        if ( qz_window ) {
slouken@4090
   402
            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
   403
            [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
slouken@4065
   404
            qz_window = nil;
slouken@4065
   405
            window_view = nil;
slouken@4065
   406
        }
slouken@501
   407
        /* 
slouken@501
   408
            Release the OpenGL context
slouken@501
   409
            Do this first to avoid trash on the display before fade
slouken@501
   410
        */
slouken@501
   411
        if ( mode_flags & SDL_OPENGL ) {
slouken@501
   412
        
slouken@272
   413
            QZ_TearDownOpenGL (this);
icculus@5606
   414
            #ifdef __powerpc__  /* we only use this for pre-10.3 compatibility. */
slouken@501
   415
            CGLSetFullScreen (NULL);
icculus@5606
   416
            #endif
slouken@501
   417
        }
icculus@1340
   418
        if (to_desktop) {
slouken@3877
   419
            ShowMenuBar ();
icculus@1340
   420
            /* Restore original screen resolution/bpp */
icculus@1340
   421
            CGDisplaySwitchToMode (display_id, save_mode);
icculus@1340
   422
            CGReleaseAllDisplays ();
icculus@1340
   423
            /* 
icculus@1340
   424
                Reset the main screen's rectangle
icculus@1340
   425
                See comment in QZ_SetVideoFullscreen for why we do this
icculus@1340
   426
            */
icculus@1340
   427
            screen_rect = NSMakeRect(0,0,device_width,device_height);
icculus@4204
   428
            QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
icculus@1340
   429
        }
slouken@47
   430
    }
slouken@272
   431
    /* Release window mode resources */
slouken@390
   432
    else {
slouken@4090
   433
        id delegate = [ qz_window delegate ];
slouken@4090
   434
        [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
slouken@4090
   435
        if (delegate != nil) [ delegate release ];
slouken@390
   436
        qz_window = nil;
slouken@501
   437
        window_view = nil;
icculus@1160
   438
slouken@272
   439
        /* Release the OpenGL context */
slouken@272
   440
        if ( mode_flags & SDL_OPENGL )
slouken@272
   441
            QZ_TearDownOpenGL (this);
slouken@47
   442
    }
slouken@272
   443
slouken@155
   444
    /* Signal successful teardown */
slouken@155
   445
    video_set = SDL_FALSE;
slouken@47
   446
}
slouken@47
   447
slouken@47
   448
static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
slouken@4317
   449
                                           int height, int bpp, Uint32 flags)
slouken@4317
   450
{
icculus@1220
   451
    boolean_t exact_match = 0;
slouken@435
   452
    NSRect screen_rect;
slouken@588
   453
    CGError error;
slouken@4065
   454
    NSRect contentRect;
icculus@1340
   455
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
icculus@4204
   456
icculus@5569
   457
    current->flags = SDL_FULLSCREEN;
icculus@5569
   458
    current->w = width;
icculus@5569
   459
    current->h = height;
icculus@5569
   460
icculus@4442
   461
    contentRect = NSMakeRect (0, 0, width, height);
icculus@4442
   462
icculus@1340
   463
    /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
   464
       that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
   465
    if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
icculus@1340
   466
        CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
   467
    }
slouken@435
   468
    
slouken@501
   469
    /* Destroy any previous mode */
slouken@501
   470
    if (video_set == SDL_TRUE)
icculus@1340
   471
        QZ_UnsetVideoMode (this, FALSE);
slouken@501
   472
icculus@4204
   473
    /* Sorry, QuickDraw was ripped out. */
icculus@4204
   474
    if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
icculus@4204
   475
        SDL_SetError ("Embedded QuickDraw windows are no longer supported");
icculus@4204
   476
        goto ERR_NO_MATCH;
icculus@4204
   477
    }
icculus@4204
   478
slouken@47
   479
    /* See if requested mode exists */
slouken@390
   480
    mode = CGDisplayBestModeForParameters (display_id, bpp, width,
slouken@390
   481
                                           height, &exact_match);
slouken@390
   482
slouken@47
   483
    /* Require an exact match to the requested mode */
slouken@47
   484
    if ( ! exact_match ) {
slouken@501
   485
        SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
slouken@47
   486
        goto ERR_NO_MATCH;
slouken@47
   487
    }
slouken@155
   488
slouken@47
   489
    /* Put up the blanking window (a window above all other windows) */
slouken@588
   490
    if (getenv ("SDL_SINGLEDISPLAY"))
slouken@588
   491
        error = CGDisplayCapture (display_id);
slouken@588
   492
    else
slouken@588
   493
        error = CGCaptureAllDisplays ();
slouken@588
   494
        
slouken@588
   495
    if ( CGDisplayNoErr != error ) {
slouken@47
   496
        SDL_SetError ("Failed capturing display");
slouken@47
   497
        goto ERR_NO_CAPTURE;
slouken@47
   498
    }
slouken@272
   499
slouken@47
   500
    /* Do the physical switch */
slouken@47
   501
    if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
slouken@47
   502
        SDL_SetError ("Failed switching display resolution");
slouken@47
   503
        goto ERR_NO_SWITCH;
slouken@47
   504
    }
slouken@272
   505
slouken@4065
   506
    /* Check if we should recreate the window */
slouken@4065
   507
    if (qz_window == nil) {
slouken@4065
   508
        /* Manually create a window, avoids having a nib file resource */
slouken@4065
   509
        qz_window = [ [ SDL_QuartzWindow alloc ] 
slouken@4065
   510
            initWithContentRect:contentRect
icculus@5569
   511
                styleMask:NSBorderlessWindowMask
slouken@4065
   512
                    backing:NSBackingStoreBuffered
slouken@4065
   513
                        defer:NO ];
slouken@4065
   514
slouken@4065
   515
        if (qz_window != nil) {
slouken@4065
   516
            [ qz_window setAcceptsMouseMovedEvents:YES ];
slouken@4065
   517
            [ qz_window setViewsNeedDisplay:NO ];
icculus@5569
   518
            [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
slouken@4065
   519
        }
slouken@4065
   520
    }
slouken@4065
   521
    /* We already have a window, just change its size */
slouken@4065
   522
    else {
icculus@4267
   523
        [ qz_window setContentSize:contentRect.size ];
icculus@4267
   524
        current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
icculus@4267
   525
        [ window_view setFrameSize:contentRect.size ];
slouken@4065
   526
    }
slouken@4065
   527
slouken@47
   528
    /* Setup OpenGL for a fullscreen context */
slouken@47
   529
    if (flags & SDL_OPENGL) {
slouken@47
   530
slouken@47
   531
        CGLError err;
slouken@47
   532
        CGLContextObj ctx;
slouken@390
   533
slouken@47
   534
        if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
slouken@272
   535
            goto ERR_NO_GL;
slouken@47
   536
        }
slouken@390
   537
slouken@4065
   538
        /* Initialize the NSView and add it to our window.  The presence of a valid window and
slouken@4065
   539
           view allow the cursor to be changed whilst in fullscreen.*/
slouken@4065
   540
        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@4065
   541
        [ [ qz_window contentView ] addSubview:window_view ];	
slouken@4065
   542
        [ window_view release ];
slouken@4065
   543
icculus@5606
   544
#ifdef __powerpc__  /* there's a Cocoa API for this in 10.3 */
icculus@5606
   545
        /* CGLSetFullScreen() will handle this for us. */
icculus@5606
   546
        [ qz_window setLevel:NSNormalWindowLevel ];
icculus@5606
   547
icculus@4204
   548
        ctx = QZ_GetCGLContextObj (gl_context);
slouken@47
   549
        err = CGLSetFullScreen (ctx);
slouken@47
   550
        if (err) {
slouken@501
   551
            SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
slouken@47
   552
            goto ERR_NO_GL;
slouken@47
   553
        }
icculus@5606
   554
#else
icculus@5606
   555
        [ qz_window setLevel:CGShieldingWindowLevel() ];
icculus@5606
   556
        [ gl_context setView:window_view ];
icculus@5606
   557
        [ gl_context setFullScreen ];
icculus@5606
   558
        [ gl_context update ];
icculus@5606
   559
#endif
slouken@390
   560
slouken@47
   561
        [ gl_context makeCurrentContext];
slouken@272
   562
slouken@272
   563
        glClear (GL_COLOR_BUFFER_BIT);
slouken@272
   564
slouken@272
   565
        [ gl_context flushBuffer ];
slouken@390
   566
slouken@47
   567
        current->flags |= SDL_OPENGL;
slouken@47
   568
    }
icculus@5569
   569
    /* For 2D, we build a CGBitmapContext */
icculus@5569
   570
    else {
icculus@5569
   571
        CGColorSpaceRef cgColorspace;
icculus@5569
   572
icculus@5569
   573
        /* Only recreate the view if it doesn't already exist */
icculus@5569
   574
        if (window_view == nil) {
icculus@5569
   575
            window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
icculus@5569
   576
            [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
icculus@5569
   577
            [ [ qz_window contentView ] addSubview:window_view ];
icculus@5569
   578
            [ window_view release ];
icculus@5569
   579
        }
icculus@5569
   580
icculus@5569
   581
        cgColorspace = CGColorSpaceCreateDeviceRGB();
icculus@5569
   582
        current->pitch = 4 * current->w;
icculus@5569
   583
        current->pixels = SDL_malloc (current->h * current->pitch);
icculus@5569
   584
        
icculus@5569
   585
        cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
icculus@5569
   586
                        8, current->pitch, cgColorspace,
icculus@5569
   587
                        kCGImageAlphaNoneSkipFirst);
icculus@5569
   588
        CGColorSpaceRelease (cgColorspace);
icculus@5569
   589
        
icculus@5569
   590
        current->flags |= SDL_SWSURFACE;
icculus@5569
   591
        current->flags |= SDL_ASYNCBLIT;
icculus@5569
   592
        current->hwdata = (void *) cg_context;
icculus@5569
   593
icculus@5569
   594
        /* Force this window to draw above _everything_. */
icculus@5569
   595
        [ qz_window setLevel:CGShieldingWindowLevel() ];
icculus@5569
   596
    }
icculus@5569
   597
icculus@5569
   598
    [ qz_window setHasShadow:NO];
icculus@5569
   599
    [ qz_window setOpaque:YES];
icculus@5569
   600
    [ qz_window makeKeyAndOrderFront:nil ];
slouken@47
   601
slouken@47
   602
    /* If we don't hide menu bar, it will get events and interrupt the program */
slouken@47
   603
    HideMenuBar ();
slouken@272
   604
icculus@1340
   605
    /* Fade in again (asynchronously) */
icculus@1340
   606
    if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
icculus@1340
   607
        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   608
        CGReleaseDisplayFadeReservation(fade_token);
icculus@1340
   609
    }
slouken@390
   610
slouken@435
   611
    /* 
slouken@501
   612
        There is a bug in Cocoa where NSScreen doesn't synchronize
slouken@501
   613
        with CGDirectDisplay, so the main screen's frame is wrong.
slouken@501
   614
        As a result, coordinate translation produces incorrect results.
slouken@501
   615
        We can hack around this bug by setting the screen rect
slouken@501
   616
        ourselves. This hack should be removed if/when the bug is fixed.
slouken@435
   617
    */
slouken@435
   618
    screen_rect = NSMakeRect(0,0,width,height);
icculus@4204
   619
    QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
slouken@435
   620
slouken@47
   621
    /* Save the flags to ensure correct tear-down */
slouken@47
   622
    mode_flags = current->flags;
slouken@390
   623
slouken@1629
   624
    /* Set app state, hide cursor if necessary, ... */
slouken@1629
   625
    QZ_DoActivate(this);
icculus@1119
   626
slouken@47
   627
    return current;
slouken@47
   628
slouken@390
   629
    /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
slouken@588
   630
ERR_NO_GL:      
slouken@588
   631
ERR_DOUBLEBUF:  CGDisplaySwitchToMode (display_id, save_mode);
slouken@588
   632
ERR_NO_SWITCH:  CGReleaseAllDisplays ();
icculus@1340
   633
ERR_NO_CAPTURE:
icculus@1340
   634
ERR_NO_MATCH:   if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
icculus@1340
   635
                    CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   636
                    CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   637
                }
icculus@1340
   638
                return NULL;
slouken@47
   639
}
slouken@47
   640
slouken@47
   641
static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
slouken@4317
   642
                                         int height, int *bpp, Uint32 flags)
slouken@4317
   643
{
slouken@58
   644
    unsigned int style;
slouken@501
   645
    NSRect contentRect;
slouken@657
   646
    int center_window = 1;
slouken@657
   647
    int origin_x, origin_y;
icculus@1340
   648
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
slouken@390
   649
slouken@58
   650
    current->flags = 0;
slouken@58
   651
    current->w = width;
slouken@47
   652
    current->h = height;
slouken@501
   653
    
slouken@501
   654
    contentRect = NSMakeRect (0, 0, width, height);
icculus@4265
   655
slouken@501
   656
    /*
slouken@501
   657
        Check if we should completely destroy the previous mode 
slouken@501
   658
        - If it is fullscreen
slouken@501
   659
        - If it has different noframe or resizable attribute
slouken@501
   660
        - If it is OpenGL (since gl attributes could be different)
slouken@501
   661
        - If new mode is OpenGL, but previous mode wasn't
slouken@501
   662
    */
icculus@1340
   663
    if (video_set == SDL_TRUE) {
icculus@1340
   664
        if (mode_flags & SDL_FULLSCREEN) {
icculus@1340
   665
            /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
   666
               that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
   667
            if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
icculus@1340
   668
                CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
   669
            }
icculus@1340
   670
            QZ_UnsetVideoMode (this, TRUE);
icculus@1340
   671
        }
icculus@1340
   672
        else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
icculus@1340
   673
                  (mode_flags & SDL_OPENGL) || 
icculus@1340
   674
                  (flags & SDL_OPENGL) ) {
icculus@1340
   675
            QZ_UnsetVideoMode (this, TRUE);
icculus@1340
   676
        }
icculus@1340
   677
    }
slouken@683
   678
    
icculus@4204
   679
    /* Sorry, QuickDraw was ripped out. */
icculus@4204
   680
    if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
icculus@4204
   681
        SDL_SetError ("Embedded QuickDraw windows are no longer supported");
icculus@4204
   682
        if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@4204
   683
            CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@4204
   684
            CGReleaseDisplayFadeReservation (fade_token);
slouken@683
   685
        }
icculus@4204
   686
        return NULL;
slouken@683
   687
    }
icculus@4204
   688
slouken@501
   689
    /* Check if we should recreate the window */
slouken@501
   690
    if (qz_window == nil) {
slouken@501
   691
    
slouken@501
   692
        /* Set the window style based on input flags */
slouken@501
   693
        if ( flags & SDL_NOFRAME ) {
slouken@501
   694
            style = NSBorderlessWindowMask;
slouken@501
   695
            current->flags |= SDL_NOFRAME;
slouken@501
   696
        } else {
slouken@501
   697
            style = NSTitledWindowMask;
slouken@501
   698
            style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
slouken@501
   699
            if ( flags & SDL_RESIZABLE ) {
slouken@501
   700
                style |= NSResizableWindowMask;
slouken@501
   701
                current->flags |= SDL_RESIZABLE;
slouken@501
   702
            }
slouken@501
   703
        }
icculus@4265
   704
slouken@501
   705
        /* Manually create a window, avoids having a nib file resource */
slouken@501
   706
        qz_window = [ [ SDL_QuartzWindow alloc ] 
slouken@501
   707
            initWithContentRect:contentRect
slouken@501
   708
                styleMask:style 
slouken@501
   709
                    backing:NSBackingStoreBuffered
slouken@501
   710
                        defer:NO ];
slouken@501
   711
                          
slouken@501
   712
        if (qz_window == nil) {
slouken@501
   713
            SDL_SetError ("Could not create the Cocoa window");
icculus@1340
   714
            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   715
                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   716
                CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   717
            }
slouken@501
   718
            return NULL;
slouken@501
   719
        }
icculus@4265
   720
slouken@4090
   721
        /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */
slouken@501
   722
        QZ_SetCaption(this, this->wm_title, this->wm_icon);
slouken@501
   723
        [ qz_window setAcceptsMouseMovedEvents:YES ];
slouken@501
   724
        [ qz_window setViewsNeedDisplay:NO ];
icculus@4265
   725
icculus@4265
   726
        if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
icculus@4265
   727
            /* have to flip the Y value (NSPoint is lower left corner origin) */
icculus@4265
   728
            [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))];
icculus@4265
   729
            center_window = 0;
icculus@4267
   730
        } else if ( center_window ) {
slouken@657
   731
            [ qz_window center ];
slouken@657
   732
        }
icculus@4265
   733
slouken@501
   734
        [ qz_window setDelegate:
slouken@4090
   735
            [ [ SDL_QuartzWindowDelegate alloc ] init ] ];
slouken@4070
   736
        [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
slouken@501
   737
    }
slouken@501
   738
    /* We already have a window, just change its size */
slouken@501
   739
    else {
icculus@4267
   740
        [ qz_window setContentSize:contentRect.size ];
icculus@4267
   741
        current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
icculus@4267
   742
        [ window_view setFrameSize:contentRect.size ];
slouken@501
   743
    }
slouken@390
   744
slouken@501
   745
    /* For OpenGL, we bind the context to a subview */
slouken@47
   746
    if ( flags & SDL_OPENGL ) {
slouken@390
   747
icculus@1183
   748
        if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
icculus@1340
   749
            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   750
                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   751
                CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   752
            }
slouken@47
   753
            return NULL;
slouken@47
   754
        }
slouken@390
   755
slouken@501
   756
        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@832
   757
        [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
slouken@501
   758
        [ [ qz_window contentView ] addSubview:window_view ];
slouken@501
   759
        [ gl_context setView: window_view ];
slouken@501
   760
        [ window_view release ];
slouken@47
   761
        [ gl_context makeCurrentContext];
slouken@158
   762
        [ qz_window makeKeyAndOrderFront:nil ];
slouken@47
   763
        current->flags |= SDL_OPENGL;
slouken@47
   764
    }
icculus@4204
   765
    /* For 2D, we build a CGBitmapContext */
slouken@47
   766
    else {
icculus@4204
   767
        CGColorSpaceRef cgColorspace;
slouken@390
   768
slouken@501
   769
        /* Only recreate the view if it doesn't already exist */
slouken@501
   770
        if (window_view == nil) {
slouken@501
   771
        
icculus@4204
   772
            window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
slouken@832
   773
            [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
slouken@501
   774
            [ [ qz_window contentView ] addSubview:window_view ];
slouken@501
   775
            [ window_view release ];
slouken@501
   776
            [ qz_window makeKeyAndOrderFront:nil ];
slouken@501
   777
        }
slouken@501
   778
        
icculus@4204
   779
        cgColorspace = CGColorSpaceCreateDeviceRGB();
icculus@4204
   780
        current->pitch = 4 * current->w;
icculus@4204
   781
        current->pixels = SDL_malloc (current->h * current->pitch);
icculus@4204
   782
        
icculus@4204
   783
        cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
icculus@4204
   784
                        8, current->pitch, cgColorspace,
icculus@4204
   785
                        kCGImageAlphaNoneSkipFirst);
icculus@4204
   786
        CGColorSpaceRelease (cgColorspace);
icculus@4204
   787
        
slouken@47
   788
        current->flags |= SDL_SWSURFACE;
slouken@498
   789
        current->flags |= SDL_ASYNCBLIT;
icculus@4204
   790
        current->hwdata = (void *) cg_context;
slouken@47
   791
    }
slouken@390
   792
slouken@272
   793
    /* Save flags to ensure correct teardown */
slouken@272
   794
    mode_flags = current->flags;
slouken@390
   795
icculus@1340
   796
    /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
icculus@1340
   797
    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
   798
        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
   799
        CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
   800
    }
icculus@1340
   801
slouken@47
   802
    return current;
slouken@47
   803
}
slouken@47
   804
slouken@390
   805
static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
slouken@4317
   806
                                     int height, int bpp, Uint32 flags)
slouken@4317
   807
{
slouken@47
   808
    current->flags = 0;
icculus@852
   809
    current->pixels = NULL;
slouken@390
   810
icculus@5569
   811
    /* Force bpp to 32 */
icculus@5569
   812
    bpp = 32;
icculus@5569
   813
slouken@47
   814
    /* Setup full screen video */
slouken@47
   815
    if ( flags & SDL_FULLSCREEN ) {
slouken@47
   816
        current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
slouken@47
   817
        if (current == NULL)
slouken@47
   818
            return NULL;
slouken@47
   819
    }
slouken@47
   820
    /* Setup windowed video */
slouken@47
   821
    else {
icculus@1183
   822
        current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags);
slouken@47
   823
        if (current == NULL)
slouken@47
   824
            return NULL;
slouken@47
   825
    }
slouken@390
   826
slouken@47
   827
    /* Setup the new pixel format */
slouken@47
   828
    {
slouken@390
   829
        int amask = 0,
slouken@390
   830
        rmask = 0,
slouken@390
   831
        gmask = 0,
slouken@390
   832
        bmask = 0;
slouken@390
   833
slouken@47
   834
        switch (bpp) {
slouken@47
   835
            case 16:   /* (1)-5-5-5 RGB */
slouken@390
   836
                amask = 0;
slouken@58
   837
                rmask = 0x7C00;
slouken@58
   838
                gmask = 0x03E0;
slouken@58
   839
                bmask = 0x001F;
slouken@47
   840
                break;
slouken@47
   841
            case 24:
slouken@47
   842
                SDL_SetError ("24bpp is not available");
slouken@47
   843
                return NULL;
slouken@47
   844
            case 32:   /* (8)-8-8-8 ARGB */
slouken@155
   845
                amask = 0x00000000;
slouken@4341
   846
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
icculus@5569
   847
                rmask = 0x0000FF00;
icculus@5569
   848
                gmask = 0x00FF0000;
icculus@5569
   849
                bmask = 0xFF000000;
icculus@4204
   850
#else
icculus@5569
   851
                rmask = 0x00FF0000;
icculus@5569
   852
                gmask = 0x0000FF00;
icculus@5569
   853
                bmask = 0x000000FF;
icculus@4204
   854
#endif
slouken@47
   855
                break;
slouken@47
   856
        }
slouken@390
   857
slouken@47
   858
        if ( ! SDL_ReallocFormat (current, bpp,
slouken@47
   859
                                  rmask, gmask, bmask, amask ) ) {
slouken@390
   860
            SDL_SetError ("Couldn't reallocate pixel format");
slouken@390
   861
            return NULL;
slouken@4236
   862
        }
slouken@47
   863
    }
slouken@390
   864
slouken@272
   865
    /* Signal successful completion (used internally) */
slouken@155
   866
    video_set = SDL_TRUE;
slouken@390
   867
slouken@47
   868
    return current;
slouken@47
   869
}
slouken@47
   870
slouken@4317
   871
static int QZ_ToggleFullScreen (_THIS, int on)
slouken@4317
   872
{
slouken@576
   873
    return 0;
slouken@47
   874
}
slouken@47
   875
slouken@390
   876
static int QZ_SetColors (_THIS, int first_color, int num_colors,
slouken@4317
   877
                         SDL_Color *colors)
slouken@4317
   878
{
slouken@47
   879
    CGTableCount  index;
slouken@47
   880
    CGDeviceColor color;
slouken@390
   881
slouken@47
   882
    for (index = first_color; index < first_color+num_colors; index++) {
slouken@390
   883
slouken@47
   884
        /* Clamp colors between 0.0 and 1.0 */
slouken@47
   885
        color.red   = colors->r / 255.0;
slouken@47
   886
        color.blue  = colors->b / 255.0;
slouken@47
   887
        color.green = colors->g / 255.0;
slouken@390
   888
slouken@47
   889
        colors++;
slouken@390
   890
slouken@47
   891
        CGPaletteSetColorAtIndex (palette, color, index);
slouken@47
   892
    }
slouken@390
   893
slouken@47
   894
    if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
slouken@47
   895
        return 0;
slouken@390
   896
slouken@47
   897
    return 1;
slouken@47
   898
}
slouken@47
   899
slouken@498
   900
slouken@761
   901
/* Resize icon, BMP format */
slouken@761
   902
static const unsigned char QZ_ResizeIcon[] = {
slouken@761
   903
    0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
slouken@761
   904
    0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
slouken@761
   905
    0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
slouken@761
   906
    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   907
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   908
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   909
    0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
slouken@761
   910
    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
slouken@761
   911
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
slouken@761
   912
    0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
slouken@761
   913
    0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   914
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
slouken@761
   915
    0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
slouken@761
   916
    0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   917
    0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
slouken@761
   918
    0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   919
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
slouken@761
   920
    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
slouken@761
   921
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   922
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
slouken@761
   923
    0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   924
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   925
    0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
slouken@761
   926
    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   927
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
slouken@761
   928
    0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   929
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   930
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
slouken@761
   931
    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   932
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   933
    0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
slouken@761
   934
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   935
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
slouken@761
   936
    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   937
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
slouken@761
   938
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
slouken@761
   939
};
slouken@761
   940
slouken@4317
   941
static void QZ_DrawResizeIcon (_THIS)
slouken@4317
   942
{
slouken@761
   943
    /* Check if we should draw the resize icon */
slouken@761
   944
    if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
slouken@761
   945
    
icculus@4204
   946
        SDL_Rect icon_rect;
slouken@761
   947
        
icculus@4204
   948
        /* Create the icon image */
icculus@4204
   949
        if (resize_icon == NULL) {
icculus@4204
   950
        
icculus@4204
   951
            SDL_RWops *rw;
icculus@4204
   952
            SDL_Surface *tmp;
slouken@761
   953
            
icculus@4204
   954
            rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
icculus@4204
   955
            tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
icculus@4204
   956
                                                            
icculus@4204
   957
            resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);
icculus@4204
   958
            SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
slouken@761
   959
            
icculus@4204
   960
            SDL_FreeSurface (tmp);
icculus@4204
   961
        }
slouken@761
   962
            
icculus@4204
   963
        icon_rect.x = SDL_VideoSurface->w - 13;
icculus@4204
   964
        icon_rect.y = SDL_VideoSurface->h - 13;
icculus@4204
   965
        icon_rect.w = 13;
icculus@4204
   966
        icon_rect.h = 13;
slouken@761
   967
            
icculus@4204
   968
        SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
slouken@761
   969
    }
slouken@761
   970
}
slouken@761
   971
slouken@4317
   972
static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects)
slouken@4317
   973
{
slouken@47
   974
    if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
slouken@47
   975
        QZ_GL_SwapBuffers (this);
slouken@47
   976
    }
slouken@501
   977
    else if ( [ qz_window isMiniaturized ] ) {
slouken@501
   978
    
slouken@501
   979
        /* Do nothing if miniaturized */
slouken@272
   980
    }
slouken@501
   981
    
slouken@47
   982
    else {
icculus@4204
   983
        CGContextRef cgc = (CGContextRef)
icculus@4204
   984
            [[NSGraphicsContext graphicsContextWithWindow: qz_window]
icculus@4204
   985
                graphicsPort];
icculus@4204
   986
        QZ_DrawResizeIcon (this);
icculus@4204
   987
        CGContextFlush (cg_context);
icculus@4204
   988
        CGImageRef image = CGBitmapContextCreateImage (cg_context);
icculus@4204
   989
        CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height);
slouken@501
   990
        
icculus@4204
   991
        CGContextDrawImage (cgc, rectangle, image);
icculus@4204
   992
        CGImageRelease(image);
icculus@4204
   993
        CGContextFlush (cgc);
slouken@47
   994
    }
slouken@47
   995
}
slouken@47
   996
slouken@4317
   997
static void QZ_VideoQuit (_THIS)
slouken@4317
   998
{
icculus@1340
   999
    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
icculus@1340
  1000
icculus@560
  1001
    /* Restore gamma settings */
icculus@560
  1002
    CGDisplayRestoreColorSyncSettings ();
icculus@560
  1003
icculus@560
  1004
    /* Ensure the cursor will be visible and working when we quit */
icculus@560
  1005
    CGDisplayShowCursor (display_id);
icculus@560
  1006
    CGAssociateMouseAndMouseCursorPosition (1);
icculus@560
  1007
    
icculus@1340
  1008
    if (mode_flags & SDL_FULLSCREEN) {
icculus@1340
  1009
        /* Fade to black to hide resolution-switching flicker (and garbage
icculus@1340
  1010
           that is displayed by a destroyed OpenGL context, if applicable) */
icculus@1340
  1011
        if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
icculus@1340
  1012
            CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
icculus@1340
  1013
        }
icculus@1340
  1014
        QZ_UnsetVideoMode (this, TRUE);
icculus@1340
  1015
        if (fade_token != kCGDisplayFadeReservationInvalidToken) {
icculus@1340
  1016
            CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
icculus@1340
  1017
            CGReleaseDisplayFadeReservation (fade_token);
icculus@1340
  1018
        }
icculus@1340
  1019
    }
icculus@1340
  1020
    else
icculus@1340
  1021
        QZ_UnsetVideoMode (this, TRUE);
icculus@1340
  1022
    
slouken@47
  1023
    CGPaletteRelease (palette);
icculus@1181
  1024
icculus@1189
  1025
    if (opengl_library) {
icculus@1189
  1026
        SDL_UnloadObject(opengl_library);
icculus@1189
  1027
        opengl_library = NULL;
icculus@1181
  1028
    }
icculus@1181
  1029
    this->gl_config.driver_loaded = 0;
slouken@4049
  1030
slouken@4049
  1031
    if (field_edit) {
slouken@4049
  1032
        [field_edit release];
slouken@4049
  1033
        field_edit = NULL;
slouken@4049
  1034
    }
slouken@47
  1035
}
slouken@47
  1036
slouken@4317
  1037
static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1038
{
slouken@47
  1039
    return 1;
slouken@47
  1040
}
slouken@47
  1041
slouken@4317
  1042
static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1043
{
slouken@47
  1044
}
slouken@47
  1045
slouken@4317
  1046
static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface)
slouken@4317
  1047
{
icculus@1120
  1048
    return(-1); /* unallowed (no HWSURFACE support here). */
icculus@1120
  1049
}
icculus@1120
  1050
slouken@4317
  1051
static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface)
slouken@4317
  1052
{
slouken@47
  1053
}
slouken@47
  1054
slouken@47
  1055
/* Gamma functions */
slouken@4317
  1056
int QZ_SetGamma (_THIS, float red, float green, float blue)
slouken@4317
  1057
{
slouken@47
  1058
    const CGGammaValue min = 0.0, max = 1.0;
slouken@272
  1059
slouken@272
  1060
    if (red == 0.0)
slouken@272
  1061
        red = FLT_MAX;
slouken@272
  1062
    else
slouken@272
  1063
        red = 1.0 / red;
slouken@272
  1064
slouken@272
  1065
    if (green == 0.0)
slouken@272
  1066
        green = FLT_MAX;
slouken@272
  1067
    else
slouken@272
  1068
        green = 1.0 / green;
slouken@272
  1069
slouken@272
  1070
    if (blue == 0.0)
slouken@272
  1071
        blue = FLT_MAX;
slouken@272
  1072
    else
slouken@272
  1073
        blue  = 1.0 / blue;
slouken@390
  1074
slouken@390
  1075
    if ( CGDisplayNoErr == CGSetDisplayTransferByFormula
slouken@390
  1076
         (display_id, min, max, red, min, max, green, min, max, blue) ) {
slouken@390
  1077
slouken@272
  1078
        return 0;
slouken@272
  1079
    }
slouken@272
  1080
    else {
slouken@390
  1081
slouken@47
  1082
        return -1;
slouken@272
  1083
    }
slouken@47
  1084
}
slouken@47
  1085
slouken@4317
  1086
int QZ_GetGamma (_THIS, float *red, float *green, float *blue)
slouken@4317
  1087
{
slouken@47
  1088
    CGGammaValue dummy;
slouken@272
  1089
    if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
slouken@390
  1090
         (display_id, &dummy, &dummy, red,
slouken@390
  1091
          &dummy, &dummy, green, &dummy, &dummy, blue) )
slouken@390
  1092
slouken@272
  1093
        return 0;
slouken@272
  1094
    else
slouken@47
  1095
        return -1;
slouken@47
  1096
}
slouken@47
  1097
slouken@4317
  1098
int QZ_SetGammaRamp (_THIS, Uint16 *ramp)
slouken@4317
  1099
{
slouken@390
  1100
    const CGTableCount tableSize = 255;
slouken@390
  1101
    CGGammaValue redTable[tableSize];
slouken@390
  1102
    CGGammaValue greenTable[tableSize];
slouken@390
  1103
    CGGammaValue blueTable[tableSize];
slouken@390
  1104
slouken@390
  1105
    int i;
slouken@390
  1106
slouken@390
  1107
    /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
slouken@390
  1108
    for (i = 0; i < 256; i++)
slouken@390
  1109
        redTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1110
slouken@390
  1111
    for (i=256; i < 512; i++)
slouken@390
  1112
        greenTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1113
slouken@390
  1114
    for (i=512; i < 768; i++)
slouken@390
  1115
        blueTable[i % 256] = ramp[i] / 65535.0;
slouken@390
  1116
slouken@390
  1117
    if ( CGDisplayNoErr == CGSetDisplayTransferByTable
slouken@390
  1118
         (display_id, tableSize, redTable, greenTable, blueTable) )
slouken@272
  1119
        return 0;
slouken@272
  1120
    else
slouken@47
  1121
        return -1;
slouken@47
  1122
}
slouken@47
  1123
slouken@4317
  1124
int QZ_GetGammaRamp (_THIS, Uint16 *ramp)
slouken@4317
  1125
{
slouken@47
  1126
    const CGTableCount tableSize = 255;
slouken@47
  1127
    CGGammaValue redTable[tableSize];
slouken@47
  1128
    CGGammaValue greenTable[tableSize];
slouken@47
  1129
    CGGammaValue blueTable[tableSize];
slouken@47
  1130
    CGTableCount actual;
slouken@47
  1131
    int i;
slouken@390
  1132
slouken@390
  1133
    if ( CGDisplayNoErr != CGGetDisplayTransferByTable
slouken@390
  1134
         (display_id, tableSize, redTable, greenTable, blueTable, &actual) ||
slouken@390
  1135
         actual != tableSize)
slouken@390
  1136
slouken@47
  1137
        return -1;
slouken@390
  1138
slouken@47
  1139
    /* Pack tables into one array, with values from 0 to 65535 */
slouken@47
  1140
    for (i = 0; i < 256; i++)
slouken@47
  1141
        ramp[i] = redTable[i % 256] * 65535.0;
slouken@390
  1142
slouken@47
  1143
    for (i=256; i < 512; i++)
slouken@47
  1144
        ramp[i] = greenTable[i % 256] * 65535.0;
slouken@390
  1145
slouken@47
  1146
    for (i=512; i < 768; i++)
slouken@47
  1147
        ramp[i] = blueTable[i % 256] * 65535.0;
slouken@390
  1148
slouken@390
  1149
    return 0;
slouken@47
  1150
}
slouken@47
  1151