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