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