SDL-1.2.14
[sdl_omap.git] / src / video / quartz / SDL_QuartzVideo.m
CommitLineData
e14743d1 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
45static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
46{
47 [nsscreen setFrame:frame];
48}
49#else
50static 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. */
66CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
67
68/* Bootstrap functions */
69static int QZ_Available ();
70static SDL_VideoDevice* QZ_CreateDevice (int device_index);
71static void QZ_DeleteDevice (SDL_VideoDevice *device);
72
73/* Initialization, Query, Setup, and Redrawing functions */
74static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format);
75
76static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format,
77 Uint32 flags);
78static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop);
79
80static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current,
81 int width, int height, int bpp,
82 Uint32 flags);
83static int QZ_ToggleFullScreen (_THIS, int on);
84static int QZ_SetColors (_THIS, int first_color,
85 int num_colors, SDL_Color *colors);
86
87static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface);
88static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
89static int QZ_ThreadFlip (_THIS);
90static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface);
91static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
92
93static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects);
94static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects);
95static void QZ_VideoQuit (_THIS);
96
97/* Hardware surface functions (for fullscreen mode only) */
98#if 0 /* Not used (apparently, it's really slow) */
99static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
100#endif
101static int QZ_LockHWSurface(_THIS, SDL_Surface *surface);
102static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
103static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
104static 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 */
108VideoBootStrap QZ_bootstrap = {
109 "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
110};
111
112
113/* Bootstrap functions */
114static int QZ_Available ()
115{
116 return 1;
117}
118
119static 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
192static void QZ_DeleteDevice (SDL_VideoDevice *device)
193{
194 SDL_free (device->hidden);
195 SDL_free (device);
196}
197
198static 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
271static 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
386static 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
397static 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
473static 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 */
661ERR_NO_GL:
662ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode);
663ERR_NO_SWITCH: CGReleaseAllDisplays ();
664ERR_NO_CAPTURE:
665ERR_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
672static 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
840static 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
914static int QZ_ToggleFullScreen (_THIS, int on)
915{
916 return 0;
917}
918
919static 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
943static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface)
944{
945 return 1;
946}
947
948static 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> */
953static 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
966static 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
1072static 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
1090static 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
1096static 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 */
1103static 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
1142static 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
1173static 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
1199static 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) */
1240static 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
1248static int QZ_LockHWSurface(_THIS, SDL_Surface *surface)
1249{
1250 return 1;
1251}
1252
1253static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface)
1254{
1255}
1256
1257static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface)
1258{
1259 return(-1); /* unallowed (no HWSURFACE support here). */
1260}
1261
1262static 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 */
1273int 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
1303int 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
1315int 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
1341int 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