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