| /* |
| SDL - Simple DirectMedia Layer |
| Copyright (C) 1997-2003 Sam Lantinga |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public |
| License along with this library; if not, write to the Free |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| Sam Lantinga |
| slouken@libsdl.org |
| */ |
| #include "SDL_config.h" |
| |
| #include "SDL_QuartzVideo.h" |
| #include "SDL_QuartzWindow.h" |
| #include "SDL_QuartzWM.h" |
| |
| /* |
| Add methods to get at private members of NSScreen. |
| Since there is a bug in Apple's screen switching code |
| that does not update this variable when switching |
| to fullscreen, we'll set it manually (but only for the |
| main screen). |
| */ |
| @interface NSScreen (NSScreenAccess) |
| - (void) setFrame:(NSRect)frame; |
| @end |
| |
| @implementation NSScreen (NSScreenAccess) |
| - (void) setFrame:(NSRect)frame; |
| { |
| _frame = frame; |
| } |
| @end |
| |
| |
| /* Bootstrap functions */ |
| static int QZ_Available (); |
| static SDL_VideoDevice* QZ_CreateDevice (int device_index); |
| static void QZ_DeleteDevice (SDL_VideoDevice *device); |
| |
| /* Initialization, Query, Setup, and Redrawing functions */ |
| static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format); |
| |
| static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, |
| Uint32 flags); |
| static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop); |
| |
| static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, |
| int width, int height, int bpp, |
| Uint32 flags); |
| static int QZ_ToggleFullScreen (_THIS, int on); |
| static int QZ_SetColors (_THIS, int first_color, |
| int num_colors, SDL_Color *colors); |
| |
| static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface); |
| static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface); |
| static int QZ_ThreadFlip (_THIS); |
| static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface); |
| static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects); |
| |
| static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects); |
| static int QZ_LockWindow (_THIS, SDL_Surface *surface); |
| static void QZ_UnlockWindow (_THIS, SDL_Surface *surface); |
| static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects); |
| static void QZ_VideoQuit (_THIS); |
| |
| /* Hardware surface functions (for fullscreen mode only) */ |
| #if 0 /* Not used (apparently, it's really slow) */ |
| static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color); |
| #endif |
| static int QZ_LockHWSurface(_THIS, SDL_Surface *surface); |
| static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface); |
| static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface); |
| static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface); |
| /* static int QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */ |
| |
| /* Bootstrap binding, enables entry point into the driver */ |
| VideoBootStrap QZ_bootstrap = { |
| "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice |
| }; |
| |
| |
| /* Bootstrap functions */ |
| static int QZ_Available () { |
| return 1; |
| } |
| |
| static SDL_VideoDevice* QZ_CreateDevice (int device_index) { |
| |
| #pragma unused (device_index) |
| |
| SDL_VideoDevice *device; |
| SDL_PrivateVideoData *hidden; |
| |
| device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) ); |
| hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) ); |
| |
| if (device == NULL || hidden == NULL) |
| SDL_OutOfMemory (); |
| |
| SDL_memset (device, 0, sizeof (*device) ); |
| SDL_memset (hidden, 0, sizeof (*hidden) ); |
| |
| device->hidden = hidden; |
| |
| device->VideoInit = QZ_VideoInit; |
| device->ListModes = QZ_ListModes; |
| device->SetVideoMode = QZ_SetVideoMode; |
| device->ToggleFullScreen = QZ_ToggleFullScreen; |
| device->UpdateMouse = QZ_UpdateMouse; |
| device->SetColors = QZ_SetColors; |
| /* device->UpdateRects = QZ_UpdateRects; this is determined by SetVideoMode() */ |
| device->VideoQuit = QZ_VideoQuit; |
| |
| device->LockHWSurface = QZ_LockHWSurface; |
| device->UnlockHWSurface = QZ_UnlockHWSurface; |
| device->AllocHWSurface = QZ_AllocHWSurface; |
| device->FreeHWSurface = QZ_FreeHWSurface; |
| /* device->FlipHWSurface = QZ_FlipHWSurface */; |
| |
| device->SetGamma = QZ_SetGamma; |
| device->GetGamma = QZ_GetGamma; |
| device->SetGammaRamp = QZ_SetGammaRamp; |
| device->GetGammaRamp = QZ_GetGammaRamp; |
| |
| device->GL_GetProcAddress = QZ_GL_GetProcAddress; |
| device->GL_GetAttribute = QZ_GL_GetAttribute; |
| device->GL_MakeCurrent = QZ_GL_MakeCurrent; |
| device->GL_SwapBuffers = QZ_GL_SwapBuffers; |
| device->GL_LoadLibrary = QZ_GL_LoadLibrary; |
| |
| device->FreeWMCursor = QZ_FreeWMCursor; |
| device->CreateWMCursor = QZ_CreateWMCursor; |
| device->ShowWMCursor = QZ_ShowWMCursor; |
| device->WarpWMCursor = QZ_WarpWMCursor; |
| device->MoveWMCursor = QZ_MoveWMCursor; |
| device->CheckMouseMode = QZ_CheckMouseMode; |
| device->InitOSKeymap = QZ_InitOSKeymap; |
| device->PumpEvents = QZ_PumpEvents; |
| |
| device->SetCaption = QZ_SetCaption; |
| device->SetIcon = QZ_SetIcon; |
| device->IconifyWindow = QZ_IconifyWindow; |
| device->SetWindowPos = QZ_SetWindowPos; |
| device->GetWindowPos = QZ_GetWindowPos; |
| device->IsWindowVisible = QZ_IsWindowVisible; |
| device->GetMonitorDPI = QZ_GetMonitorDPI; |
| device->GetMonitorRect = QZ_GetMonitorRect; |
| device->GetWMInfo = QZ_GetWMInfo; |
| device->GrabInput = QZ_GrabInput; |
| |
| device->CreateYUVOverlay = QZ_CreateYUVOverlay; |
| |
| device->free = QZ_DeleteDevice; |
| |
| return device; |
| } |
| |
| static void QZ_DeleteDevice (SDL_VideoDevice *device) { |
| |
| SDL_free (device->hidden); |
| SDL_free (device); |
| } |
| |
| static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) { |
| |
| NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0); |
| const char *env = NULL; |
| |
| /* Initialize the video settings; this data persists between mode switches */ |
| display_id = kCGDirectMainDisplay; |
| save_mode = CGDisplayCurrentMode (display_id); |
| mode_list = CGDisplayAvailableModes (display_id); |
| palette = CGPaletteCreateDefaultColorPalette (); |
| |
| env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); |
| allow_screensaver = ( env && SDL_atoi(env) ) ? YES : NO; |
| |
| /* Gather some information that is useful to know about the display */ |
| CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel), |
| kCFNumberSInt32Type, &device_bpp); |
| |
| CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth), |
| kCFNumberSInt32Type, &device_width); |
| |
| CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight), |
| kCFNumberSInt32Type, &device_height); |
| |
| /* Determine the current screen size */ |
| this->info.current_w = device_width; |
| this->info.current_h = device_height; |
| |
| /* Determine the default screen depth */ |
| video_format->BitsPerPixel = device_bpp; |
| |
| /* Set misc globals */ |
| current_grab_mode = SDL_GRAB_OFF; |
| cursor_should_be_visible = YES; |
| cursor_visible = YES; |
| current_mods = 0; |
| field_edit = [[NSTextView alloc] initWithFrame:r]; |
| |
| if ( Gestalt(gestaltSystemVersion, &system_version) != noErr ) |
| system_version = 0; |
| |
| /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ |
| QZ_RegisterForSleepNotifications (this); |
| |
| /* Fill in some window manager capabilities */ |
| this->info.wm_available = 1; |
| |
| return 0; |
| } |
| |
| static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) { |
| |
| CFIndex num_modes; |
| CFIndex i; |
| |
| int list_size = 0; |
| |
| /* Any windowed mode is acceptable */ |
| if ( (flags & SDL_FULLSCREEN) == 0 ) |
| return (SDL_Rect**)-1; |
| |
| /* Free memory from previous call, if any */ |
| if ( client_mode_list != NULL ) { |
| |
| int i; |
| |
| for (i = 0; client_mode_list[i] != NULL; i++) |
| SDL_free (client_mode_list[i]); |
| |
| SDL_free (client_mode_list); |
| client_mode_list = NULL; |
| } |
| |
| num_modes = CFArrayGetCount (mode_list); |
| |
| /* Build list of modes with the requested bpp */ |
| for (i = 0; i < num_modes; i++) { |
| |
| CFDictionaryRef onemode; |
| CFNumberRef number; |
| int bpp; |
| |
| onemode = CFArrayGetValueAtIndex (mode_list, i); |
| number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel); |
| CFNumberGetValue (number, kCFNumberSInt32Type, &bpp); |
| |
| if (bpp == format->BitsPerPixel) { |
| |
| int intvalue; |
| int hasMode; |
| int width, height; |
| |
| number = CFDictionaryGetValue (onemode, kCGDisplayWidth); |
| CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); |
| width = (Uint16) intvalue; |
| |
| number = CFDictionaryGetValue (onemode, kCGDisplayHeight); |
| CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); |
| height = (Uint16) intvalue; |
| |
| /* Check if mode is already in the list */ |
| { |
| int i; |
| hasMode = SDL_FALSE; |
| for (i = 0; i < list_size; i++) { |
| if (client_mode_list[i]->w == width && |
| client_mode_list[i]->h == height) { |
| hasMode = SDL_TRUE; |
| break; |
| } |
| } |
| } |
| |
| /* Grow the list and add mode to the list */ |
| if ( ! hasMode ) { |
| |
| SDL_Rect *rect; |
| |
| list_size++; |
| |
| if (client_mode_list == NULL) |
| client_mode_list = (SDL_Rect**) |
| SDL_malloc (sizeof(*client_mode_list) * (list_size+1) ); |
| else |
| client_mode_list = (SDL_Rect**) |
| SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1)); |
| |
| rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list)); |
| |
| if (client_mode_list == NULL || rect == NULL) { |
| SDL_OutOfMemory (); |
| return NULL; |
| } |
| |
| rect->x = rect->y = 0; |
| rect->w = width; |
| rect->h = height; |
| |
| client_mode_list[list_size-1] = rect; |
| client_mode_list[list_size] = NULL; |
| } |
| } |
| } |
| |
| /* Sort list largest to smallest (by area) */ |
| { |
| int i, j; |
| for (i = 0; i < list_size; i++) { |
| for (j = 0; j < list_size-1; j++) { |
| |
| int area1, area2; |
| area1 = client_mode_list[j]->w * client_mode_list[j]->h; |
| area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h; |
| |
| if (area1 < area2) { |
| SDL_Rect *tmp = client_mode_list[j]; |
| client_mode_list[j] = client_mode_list[j+1]; |
| client_mode_list[j+1] = tmp; |
| } |
| } |
| } |
| } |
| return client_mode_list; |
| } |
| |
| static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y) |
| { |
| const char *window = getenv("SDL_VIDEO_WINDOW_POS"); |
| if ( window ) { |
| if ( sscanf(window, "%d,%d", x, y) == 2 ) { |
| return SDL_TRUE; |
| } |
| } |
| return SDL_FALSE; |
| } |
| |
| static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop) { |
| |
| /* Reset values that may change between switches */ |
| this->info.blit_fill = 0; |
| this->FillHWRect = NULL; |
| this->UpdateRects = NULL; |
| this->LockHWSurface = NULL; |
| this->UnlockHWSurface = NULL; |
| |
| /* Release fullscreen resources */ |
| if ( mode_flags & SDL_FULLSCREEN ) { |
| |
| NSRect screen_rect; |
| |
| /* Release double buffer stuff */ |
| if ( mode_flags & SDL_DOUBLEBUF) { |
| quit_thread = YES; |
| SDL_SemPost (sem1); |
| SDL_WaitThread (thread, NULL); |
| SDL_DestroySemaphore (sem1); |
| SDL_DestroySemaphore (sem2); |
| SDL_free (sw_buffers[0]); |
| } |
| |
| /* If we still have a valid window, close it. */ |
| if ( qz_window ) { |
| [ qz_window close ]; |
| [ qz_window release ]; |
| qz_window = nil; |
| window_view = nil; |
| } |
| /* |
| Release the OpenGL context |
| Do this first to avoid trash on the display before fade |
| */ |
| if ( mode_flags & SDL_OPENGL ) { |
| |
| QZ_TearDownOpenGL (this); |
| CGLSetFullScreen (NULL); |
| } |
| if (to_desktop) { |
| ShowMenuBar (); |
| /* Restore original screen resolution/bpp */ |
| CGDisplaySwitchToMode (display_id, save_mode); |
| CGReleaseAllDisplays (); |
| /* |
| Reset the main screen's rectangle |
| See comment in QZ_SetVideoFullscreen for why we do this |
| */ |
| screen_rect = NSMakeRect(0,0,device_width,device_height); |
| [ [ NSScreen mainScreen ] setFrame:screen_rect ]; |
| } |
| } |
| /* Release window mode resources */ |
| else { |
| |
| [ qz_window close ]; |
| [ qz_window release ]; |
| qz_window = nil; |
| window_view = nil; |
| |
| /* Release the OpenGL context */ |
| if ( mode_flags & SDL_OPENGL ) |
| QZ_TearDownOpenGL (this); |
| } |
| |
| /* Signal successful teardown */ |
| video_set = SDL_FALSE; |
| } |
| |
| static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width, |
| int height, int bpp, Uint32 flags) { |
| boolean_t exact_match = 0; |
| NSRect screen_rect; |
| CGError error; |
| NSRect contentRect; |
| BOOL isCustom = NO; |
| CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
| |
| /* Fade to black to hide resolution-switching flicker (and garbage |
| that is displayed by a destroyed OpenGL context, if applicable) */ |
| if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) { |
| CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
| } |
| |
| /* Destroy any previous mode */ |
| if (video_set == SDL_TRUE) |
| QZ_UnsetVideoMode (this, FALSE); |
| |
| /* See if requested mode exists */ |
| mode = CGDisplayBestModeForParameters (display_id, bpp, width, |
| height, &exact_match); |
| |
| /* Require an exact match to the requested mode */ |
| if ( ! exact_match ) { |
| SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp); |
| goto ERR_NO_MATCH; |
| } |
| |
| /* Put up the blanking window (a window above all other windows) */ |
| if (getenv ("SDL_SINGLEDISPLAY")) |
| error = CGDisplayCapture (display_id); |
| else |
| error = CGCaptureAllDisplays (); |
| |
| if ( CGDisplayNoErr != error ) { |
| SDL_SetError ("Failed capturing display"); |
| goto ERR_NO_CAPTURE; |
| } |
| |
| /* Do the physical switch */ |
| if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) { |
| SDL_SetError ("Failed switching display resolution"); |
| goto ERR_NO_SWITCH; |
| } |
| |
| current->pixels = (Uint32*) CGDisplayBaseAddress (display_id); |
| current->pitch = CGDisplayBytesPerRow (display_id); |
| |
| current->flags = 0; |
| current->w = width; |
| current->h = height; |
| current->flags |= SDL_FULLSCREEN; |
| current->flags |= SDL_HWSURFACE; |
| current->flags |= SDL_PREALLOC; |
| |
| this->UpdateRects = QZ_DirectUpdate; |
| this->LockHWSurface = QZ_LockHWSurface; |
| this->UnlockHWSurface = QZ_UnlockHWSurface; |
| |
| /* Setup double-buffer emulation */ |
| if ( flags & SDL_DOUBLEBUF ) { |
| |
| /* |
| Setup a software backing store for reasonable results when |
| double buffering is requested (since a single-buffered hardware |
| surface looks hideous). |
| |
| The actual screen blit occurs in a separate thread to allow |
| other blitting while waiting on the VBL (and hence results in higher framerates). |
| */ |
| this->LockHWSurface = NULL; |
| this->UnlockHWSurface = NULL; |
| this->UpdateRects = NULL; |
| |
| current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF); |
| this->UpdateRects = QZ_DoubleBufferUpdate; |
| this->LockHWSurface = QZ_LockDoubleBuffer; |
| this->UnlockHWSurface = QZ_UnlockDoubleBuffer; |
| this->FlipHWSurface = QZ_FlipDoubleBuffer; |
| |
| current->pixels = SDL_malloc (current->pitch * current->h * 2); |
| if (current->pixels == NULL) { |
| SDL_OutOfMemory (); |
| goto ERR_DOUBLEBUF; |
| } |
| |
| sw_buffers[0] = current->pixels; |
| sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h; |
| |
| quit_thread = NO; |
| sem1 = SDL_CreateSemaphore (0); |
| sem2 = SDL_CreateSemaphore (1); |
| thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this); |
| } |
| |
| if ( CGDisplayCanSetPalette (display_id) ) |
| current->flags |= SDL_HWPALETTE; |
| |
| /* The code below checks for any valid custom windows and views. If none are |
| available, then we create new ones. Window/View code was added in FULLSCREEN |
| so that special events like the changing of the cursor image would be handled |
| ( only the front-most and active application can change the cursor appearance |
| and with no valid window/view in FULLSCREEN, SDL wouldn't update its cursor. ) |
| */ |
| /* Check for user-specified window and view */ |
| { |
| char *windowPtrString = getenv ("SDL_NSWindowPointer"); |
| char *viewPtrString = getenv ("SDL_NSQuickDrawViewPointer"); |
| |
| contentRect = NSMakeRect (0, 0, width, height); |
| |
| if (windowPtrString && viewPtrString) { |
| /* Release any previous window */ |
| if ( qz_window ) { |
| [ qz_window release ]; |
| qz_window = nil; |
| } |
| |
| qz_window = (NSWindow*)atoi(windowPtrString); |
| window_view = (NSQuickDrawView*)atoi(viewPtrString); |
| isCustom = YES; |
| /* |
| Retain reference to window because we |
| might release it in QZ_UnsetVideoMode |
| */ |
| [ qz_window retain ]; |
| } |
| } |
| /* Check if we should recreate the window */ |
| if (qz_window == nil) { |
| /* Manually create a window, avoids having a nib file resource */ |
| qz_window = [ [ SDL_QuartzWindow alloc ] |
| initWithContentRect:contentRect |
| styleMask:nil |
| backing:NSBackingStoreBuffered |
| defer:NO ]; |
| |
| if (qz_window != nil) { |
| [ qz_window setAcceptsMouseMovedEvents:YES ]; |
| [ qz_window setViewsNeedDisplay:NO ]; |
| } |
| } |
| /* We already have a window, just change its size */ |
| else { |
| if (!isCustom) { |
| [ qz_window setContentSize:contentRect.size ]; |
| current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; |
| [ window_view setFrameSize:contentRect.size ]; |
| } |
| } |
| |
| /* Setup OpenGL for a fullscreen context */ |
| if (flags & SDL_OPENGL) { |
| |
| CGLError err; |
| CGLContextObj ctx; |
| |
| if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { |
| goto ERR_NO_GL; |
| } |
| |
| /* Initialize the NSView and add it to our window. The presence of a valid window and |
| view allow the cursor to be changed whilst in fullscreen.*/ |
| window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; |
| [ [ qz_window contentView ] addSubview:window_view ]; |
| [ window_view release ]; |
| |
| ctx = [ gl_context cglContext ]; |
| err = CGLSetFullScreen (ctx); |
| |
| if (err) { |
| SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err)); |
| goto ERR_NO_GL; |
| } |
| |
| [ gl_context makeCurrentContext]; |
| |
| glClear (GL_COLOR_BUFFER_BIT); |
| |
| [ gl_context flushBuffer ]; |
| |
| current->flags |= SDL_OPENGL; |
| } |
| |
| /* If we don't hide menu bar, it will get events and interrupt the program */ |
| HideMenuBar (); |
| |
| /* Fade in again (asynchronously) */ |
| if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation(fade_token); |
| } |
| |
| /* |
| There is a bug in Cocoa where NSScreen doesn't synchronize |
| with CGDirectDisplay, so the main screen's frame is wrong. |
| As a result, coordinate translation produces incorrect results. |
| We can hack around this bug by setting the screen rect |
| ourselves. This hack should be removed if/when the bug is fixed. |
| */ |
| screen_rect = NSMakeRect(0,0,width,height); |
| [ [ NSScreen mainScreen ] setFrame:screen_rect ]; |
| |
| /* Save the flags to ensure correct tear-down */ |
| mode_flags = current->flags; |
| |
| /* Set app state, hide cursor if necessary, ... */ |
| QZ_DoActivate(this); |
| |
| return current; |
| |
| /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ |
| ERR_NO_GL: |
| ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode); |
| ERR_NO_SWITCH: CGReleaseAllDisplays (); |
| ERR_NO_CAPTURE: |
| ERR_NO_MATCH: if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation (fade_token); |
| } |
| return NULL; |
| } |
| |
| static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width, |
| int height, int *bpp, Uint32 flags) { |
| unsigned int style; |
| NSRect contentRect; |
| BOOL isCustom = NO; |
| int center_window = 1; |
| int origin_x, origin_y; |
| CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
| |
| current->flags = 0; |
| current->w = width; |
| current->h = height; |
| |
| contentRect = NSMakeRect (0, 0, width, height); |
| |
| /* |
| Check if we should completely destroy the previous mode |
| - If it is fullscreen |
| - If it has different noframe or resizable attribute |
| - If it is OpenGL (since gl attributes could be different) |
| - If new mode is OpenGL, but previous mode wasn't |
| */ |
| if (video_set == SDL_TRUE) { |
| if (mode_flags & SDL_FULLSCREEN) { |
| /* Fade to black to hide resolution-switching flicker (and garbage |
| that is displayed by a destroyed OpenGL context, if applicable) */ |
| if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { |
| CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
| } |
| QZ_UnsetVideoMode (this, TRUE); |
| } |
| else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) || |
| (mode_flags & SDL_OPENGL) || |
| (flags & SDL_OPENGL) ) { |
| QZ_UnsetVideoMode (this, TRUE); |
| } |
| } |
| |
| /* Check for user-specified window and view */ |
| { |
| char *windowPtrString = getenv ("SDL_NSWindowPointer"); |
| char *viewPtrString = getenv ("SDL_NSQuickDrawViewPointer"); |
| |
| if (windowPtrString && viewPtrString) { |
| |
| /* Release any previous window */ |
| if ( qz_window ) { |
| [ qz_window release ]; |
| qz_window = nil; |
| } |
| |
| qz_window = (NSWindow*)atoi(windowPtrString); |
| window_view = (NSQuickDrawView*)atoi(viewPtrString); |
| isCustom = YES; |
| |
| /* |
| Retain reference to window because we |
| might release it in QZ_UnsetVideoMode |
| */ |
| [ qz_window retain ]; |
| |
| style = [ qz_window styleMask ]; |
| /* Check resizability */ |
| if ( style & NSResizableWindowMask ) |
| current->flags |= SDL_RESIZABLE; |
| |
| /* Check frame */ |
| if ( style & NSBorderlessWindowMask ) |
| current->flags |= SDL_NOFRAME; |
| } |
| } |
| |
| /* Check if we should recreate the window */ |
| if (qz_window == nil) { |
| |
| /* Set the window style based on input flags */ |
| if ( flags & SDL_NOFRAME ) { |
| style = NSBorderlessWindowMask; |
| current->flags |= SDL_NOFRAME; |
| } else { |
| style = NSTitledWindowMask; |
| style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); |
| if ( flags & SDL_RESIZABLE ) { |
| style |= NSResizableWindowMask; |
| current->flags |= SDL_RESIZABLE; |
| } |
| } |
| /* Manually create a window, avoids having a nib file resource */ |
| qz_window = [ [ SDL_QuartzWindow alloc ] |
| initWithContentRect:contentRect |
| styleMask:style |
| backing:NSBackingStoreBuffered |
| defer:NO ]; |
| |
| if (qz_window == nil) { |
| SDL_SetError ("Could not create the Cocoa window"); |
| if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation (fade_token); |
| } |
| return NULL; |
| } |
| |
| /*[ qz_window setReleasedWhenClosed:YES ];*/ |
| QZ_SetCaption(this, this->wm_title, this->wm_icon); |
| [ qz_window setAcceptsMouseMovedEvents:YES ]; |
| [ qz_window setViewsNeedDisplay:NO ]; |
| |
| if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) { |
| [ qz_window setFrameTopLeftPoint:NSMakePoint(origin_x,this->info.current_h - origin_y) ]; |
| } else { |
| [ qz_window center ]; |
| } |
| [ qz_window setDelegate: |
| [ [ [ SDL_QuartzWindowDelegate alloc ] init ] autorelease ] ]; |
| [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ]; |
| } |
| /* We already have a window, just change its size */ |
| else { |
| |
| if (!isCustom) { |
| [ qz_window setContentSize:contentRect.size ]; |
| current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; |
| [ window_view setFrameSize:contentRect.size ]; |
| } |
| } |
| |
| /* For OpenGL, we bind the context to a subview */ |
| if ( flags & SDL_OPENGL ) { |
| |
| if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) { |
| if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation (fade_token); |
| } |
| return NULL; |
| } |
| |
| window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; |
| [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; |
| [ [ qz_window contentView ] addSubview:window_view ]; |
| [ gl_context setView: window_view ]; |
| [ window_view release ]; |
| [ gl_context makeCurrentContext]; |
| [ qz_window makeKeyAndOrderFront:nil ]; |
| current->flags |= SDL_OPENGL; |
| } |
| /* For 2D, we set the subview to an NSQuickDrawView */ |
| else { |
| short qdbpp = 0; |
| CGrafPtr qdport; |
| |
| /* Only recreate the view if it doesn't already exist */ |
| if (window_view == nil) { |
| |
| window_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ]; |
| [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; |
| [ [ qz_window contentView ] addSubview:window_view ]; |
| [ window_view release ]; |
| [ qz_window makeKeyAndOrderFront:nil ]; |
| } |
| |
| qdport = [ window_view qdPort ]; |
| |
| LockPortBits ( qdport ); |
| current->pixels = GetPixBaseAddr ( GetPortPixMap ( qdport ) ); |
| current->pitch = GetPixRowBytes ( GetPortPixMap ( qdport ) ); |
| qdbpp = GetPixDepth ( GetPortPixMap ( qdport ) ); |
| UnlockPortBits ( qdport ); |
| |
| /* QuickDraw may give a 16-bit shadow surface on 8-bit displays! */ |
| *bpp = qdbpp; |
| |
| current->flags |= SDL_SWSURFACE; |
| current->flags |= SDL_PREALLOC; |
| current->flags |= SDL_ASYNCBLIT; |
| |
| /* |
| current->pixels now points to the window's pixels |
| We want it to point to the *view's* pixels |
| */ |
| { |
| NSRect winFrame = [ qz_window frame ]; |
| NSRect viewFrame = [ window_view frame ]; |
| |
| int vOffset = winFrame.size.height - viewFrame.size.height - viewFrame.origin.y; |
| int hOffset = viewFrame.origin.x; |
| |
| current->pixels = (Uint8 *)current->pixels + (vOffset * current->pitch) + hOffset * (qdbpp/8); |
| } |
| this->UpdateRects = QZ_UpdateRects; |
| this->LockHWSurface = QZ_LockWindow; |
| this->UnlockHWSurface = QZ_UnlockWindow; |
| } |
| |
| /* Save flags to ensure correct teardown */ |
| mode_flags = current->flags; |
| |
| /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */ |
| if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation (fade_token); |
| } |
| |
| return current; |
| } |
| |
| static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width, |
| int height, int bpp, Uint32 flags) { |
| |
| current->flags = 0; |
| current->pixels = NULL; |
| |
| /* Setup full screen video */ |
| if ( flags & SDL_FULLSCREEN ) { |
| current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags ); |
| if (current == NULL) |
| return NULL; |
| } |
| /* Setup windowed video */ |
| else { |
| /* Force bpp to the device's bpp */ |
| bpp = device_bpp; |
| current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags); |
| if (current == NULL) |
| return NULL; |
| } |
| |
| /* Setup the new pixel format */ |
| { |
| int amask = 0, |
| rmask = 0, |
| gmask = 0, |
| bmask = 0; |
| |
| switch (bpp) { |
| case 16: /* (1)-5-5-5 RGB */ |
| amask = 0; |
| rmask = 0x7C00; |
| gmask = 0x03E0; |
| bmask = 0x001F; |
| break; |
| case 24: |
| SDL_SetError ("24bpp is not available"); |
| return NULL; |
| case 32: /* (8)-8-8-8 ARGB */ |
| amask = 0x00000000; |
| rmask = 0x00FF0000; |
| gmask = 0x0000FF00; |
| bmask = 0x000000FF; |
| break; |
| } |
| |
| if ( ! SDL_ReallocFormat (current, bpp, |
| rmask, gmask, bmask, amask ) ) { |
| SDL_SetError ("Couldn't reallocate pixel format"); |
| return NULL; |
| } |
| } |
| |
| /* Signal successful completion (used internally) */ |
| video_set = SDL_TRUE; |
| |
| return current; |
| } |
| |
| static int QZ_ToggleFullScreen (_THIS, int on) { |
| return 0; |
| } |
| |
| static int QZ_SetColors (_THIS, int first_color, int num_colors, |
| SDL_Color *colors) { |
| |
| CGTableCount index; |
| CGDeviceColor color; |
| |
| for (index = first_color; index < first_color+num_colors; index++) { |
| |
| /* Clamp colors between 0.0 and 1.0 */ |
| color.red = colors->r / 255.0; |
| color.blue = colors->b / 255.0; |
| color.green = colors->g / 255.0; |
| |
| colors++; |
| |
| CGPaletteSetColorAtIndex (palette, color, index); |
| } |
| |
| if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) ) |
| return 0; |
| |
| return 1; |
| } |
| |
| static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) { |
| |
| return 1; |
| } |
| |
| static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) { |
| |
| } |
| |
| /* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */ |
| static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) { |
| |
| union |
| { |
| UInt64 i; |
| Nanoseconds ns; |
| } temp; |
| |
| temp.i = seconds * 1000000000.0; |
| |
| return NanosecondsToAbsolute ( temp.ns ); |
| } |
| |
| static int QZ_ThreadFlip (_THIS) { |
| |
| Uint8 *src, *dst; |
| int skip, len, h; |
| |
| /* |
| Give this thread the highest scheduling priority possible, |
| in the hopes that it will immediately run after the VBL delay |
| */ |
| { |
| pthread_t current_thread; |
| int policy; |
| struct sched_param param; |
| |
| current_thread = pthread_self (); |
| pthread_getschedparam (current_thread, &policy, ¶m); |
| policy = SCHED_RR; |
| param.sched_priority = sched_get_priority_max (policy); |
| pthread_setschedparam (current_thread, policy, ¶m); |
| } |
| |
| while (1) { |
| |
| SDL_SemWait (sem1); |
| if (quit_thread) |
| return 0; |
| |
| /* |
| * We have to add SDL_VideoSurface->offset here, since we might be a |
| * smaller surface in the center of the framebuffer (you asked for |
| * a fullscreen resolution smaller than the hardware could supply |
| * so SDL is centering it in a bigger resolution)... |
| */ |
| dst = (Uint8 *)CGDisplayBaseAddress (display_id) + SDL_VideoSurface->offset; |
| src = current_buffer + SDL_VideoSurface->offset; |
| len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel; |
| h = SDL_VideoSurface->h; |
| skip = SDL_VideoSurface->pitch; |
| |
| /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */ |
| { |
| |
| /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */ |
| double refreshRate; |
| double linesPerSecond; |
| double target; |
| double position; |
| double adjustment; |
| AbsoluteTime nextTime; |
| CFNumberRef refreshRateCFNumber; |
| |
| refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate); |
| if ( NULL == refreshRateCFNumber ) { |
| SDL_SetError ("Mode has no refresh rate"); |
| goto ERROR; |
| } |
| |
| if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) { |
| SDL_SetError ("Error getting refresh rate"); |
| goto ERROR; |
| } |
| |
| if ( 0 == refreshRate ) { |
| |
| SDL_SetError ("Display has no refresh rate, using 60hz"); |
| |
| /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */ |
| refreshRate = 60.0; |
| } |
| |
| linesPerSecond = refreshRate * h; |
| target = h; |
| |
| /* Figure out the first delay so we start off about right */ |
| position = CGDisplayBeamPosition (display_id); |
| if (position > target) |
| position = 0; |
| |
| adjustment = (target - position) / linesPerSecond; |
| |
| nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment)); |
| |
| MPDelayUntil (&nextTime); |
| } |
| |
| |
| /* On error, skip VBL delay */ |
| ERROR: |
| |
| while ( h-- ) { |
| |
| SDL_memcpy (dst, src, len); |
| src += skip; |
| dst += skip; |
| } |
| |
| /* signal flip completion */ |
| SDL_SemPost (sem2); |
| } |
| |
| return 0; |
| } |
| |
| static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) { |
| |
| /* wait for previous flip to complete */ |
| SDL_SemWait (sem2); |
| |
| current_buffer = surface->pixels; |
| |
| if (surface->pixels == sw_buffers[0]) |
| surface->pixels = sw_buffers[1]; |
| else |
| surface->pixels = sw_buffers[0]; |
| |
| /* signal worker thread to do the flip */ |
| SDL_SemPost (sem1); |
| |
| return 0; |
| } |
| |
| |
| static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) { |
| |
| /* perform a flip if someone calls updaterects on a doublebuferred surface */ |
| this->FlipHWSurface (this, SDL_VideoSurface); |
| } |
| |
| static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) { |
| #pragma unused(this,num_rects,rects) |
| } |
| |
| /* |
| The obscured code is based on work by Matt Slot fprefect@ambrosiasw.com, |
| who supplied sample code for Carbon. |
| */ |
| |
| /*#define TEST_OBSCURED 1*/ |
| |
| #if TEST_OBSCURED |
| #include "CGS.h" |
| #endif |
| |
| static int QZ_IsWindowObscured (NSWindow *window) { |
| |
| |
| #if TEST_OBSCURED |
| |
| /* |
| In order to determine if a direct copy to the screen is possible, |
| we must figure out if there are any windows covering ours (including shadows). |
| This can be done by querying the window server about the on screen |
| windows for their screen rectangle and window level. |
| The procedure used below is puts accuracy before speed; however, it aims to call |
| the window server the fewest number of times possible to keep things reasonable. |
| In my testing on a 300mhz G3, this routine typically takes < 2 ms. -DW |
| |
| Notes: |
| -Calls into the Window Server involve IPC which is slow. |
| -Getting a rectangle seems slower than getting the window level |
| -The window list we get back is in sorted order, top to bottom |
| -On average, I suspect, most windows above ours are dock icon windows (hence optimization) |
| -Some windows above ours are always there, and cannot move or obscure us (menu bar) |
| |
| Bugs: |
| -no way (yet) to deactivate direct drawing when a window is dragged, |
| or suddenly obscured, so drawing continues and can produce garbage |
| We need some kind of locking mechanism on window movement to prevent this |
| |
| -deactivated normal windows use activated normal |
| window shadows (slight inaccuraccy) |
| */ |
| |
| /* Cache the connection to the window server */ |
| static CGSConnectionID cgsConnection = (CGSConnectionID) -1; |
| |
| /* Cache the dock icon windows */ |
| static CGSWindowID dockIcons[kMaxWindows]; |
| static int numCachedDockIcons = 0; |
| |
| CGSWindowID windows[kMaxWindows]; |
| CGSWindowCount i, count; |
| CGSWindowLevel winLevel; |
| CGSRect winRect; |
| |
| CGSRect contentRect; |
| int windowNumber; |
| int firstDockIcon; |
| int dockIconCacheMiss; |
| int windowContentOffset; |
| |
| int obscured = SDL_TRUE; |
| |
| if ( [ window isVisible ] ) { |
| |
| /* |
| walk the window list looking for windows over top of |
| (or casting a shadow on) ours |
| */ |
| |
| /* |
| Get a connection to the window server |
| Should probably be moved out into SetVideoMode() or InitVideo() |
| */ |
| if (cgsConnection == (CGSConnectionID) -1) { |
| cgsConnection = (CGSConnectionID) 0; |
| cgsConnection = _CGSDefaultConnection (); |
| } |
| |
| if (cgsConnection) { |
| |
| if ( ! [ window styleMask ] & NSBorderlessWindowMask ) |
| windowContentOffset = 22; |
| else |
| windowContentOffset = 0; |
| |
| windowNumber = [ window windowNumber ]; |
| |
| /* The window list is sorted according to order on the screen */ |
| count = 0; |
| CGSGetOnScreenWindowList (cgsConnection, 0, kMaxWindows, windows, &count); |
| CGSGetScreenRectForWindow (cgsConnection, windowNumber, &contentRect); |
| |
| /* adjust rect for window title bar (if present) */ |
| contentRect.origin.y += windowContentOffset; |
| contentRect.size.height -= windowContentOffset; |
| |
| firstDockIcon = -1; |
| dockIconCacheMiss = SDL_FALSE; |
| |
| /* |
| The first window is always an empty window with level kCGSWindowLevelTop |
| so start at index 1 |
| */ |
| for (i = 1; i < count; i++) { |
| |
| /* If we reach our window in the list, it cannot be obscured */ |
| if (windows[i] == windowNumber) { |
| |
| obscured = SDL_FALSE; |
| break; |
| } |
| else { |
| |
| float shadowSide; |
| float shadowTop; |
| float shadowBottom; |
| |
| CGSGetWindowLevel (cgsConnection, windows[i], &winLevel); |
| |
| if (winLevel == kCGSWindowLevelDockIcon) { |
| |
| int j; |
| |
| if (firstDockIcon < 0) { |
| |
| firstDockIcon = i; |
| |
| if (numCachedDockIcons > 0) { |
| |
| for (j = 0; j < numCachedDockIcons; j++) { |
| |
| if (windows[i] == dockIcons[j]) |
| i++; |
| else |
| break; |
| } |
| |
| if (j != 0) { |
| |
| i--; |
| |
| if (j < numCachedDockIcons) { |
| |
| dockIconCacheMiss = SDL_TRUE; |
| } |
| } |
| |
| } |
| } |
| |
| continue; |
| } |
| else if (winLevel == kCGSWindowLevelMenuIgnore |
| /* winLevel == kCGSWindowLevelTop */) { |
| |
| continue; /* cannot obscure window */ |
| } |
| else if (winLevel == kCGSWindowLevelDockMenu || |
| winLevel == kCGSWindowLevelMenu) { |
| |
| shadowSide = 18; |
| shadowTop = 4; |
| shadowBottom = 22; |
| } |
| else if (winLevel == kCGSWindowLevelUtility) { |
| |
| shadowSide = 8; |
| shadowTop = 4; |
| shadowBottom = 12; |
| } |
| else if (winLevel == kCGSWindowLevelNormal) { |
| |
| /* |
| These numbers are for foreground windows, |
| they are too big (but will work) for background windows |
| */ |
| shadowSide = 20; |
| shadowTop = 10; |
| shadowBottom = 24; |
| } |
| else if (winLevel == kCGSWindowLevelDock) { |
| |
| /* Create dock icon cache */ |
| if (numCachedDockIcons != (i-firstDockIcon) || |
| dockIconCacheMiss) { |
| |
| numCachedDockIcons = i - firstDockIcon; |
| SDL_memcpy (dockIcons, &(windows[firstDockIcon]), |
| numCachedDockIcons * sizeof(*windows)); |
| } |
| |
| /* no shadow */ |
| shadowSide = 0; |
| shadowTop = 0; |
| shadowBottom = 0; |
| } |
| else { |
| |
| /* |
| kCGSWindowLevelDockLabel, |
| kCGSWindowLevelDock, |
| kOther??? |
| */ |
| |
| /* no shadow */ |
| shadowSide = 0; |
| shadowTop = 0; |
| shadowBottom = 0; |
| } |
| |
| CGSGetScreenRectForWindow (cgsConnection, windows[i], &winRect); |
| |
| winRect.origin.x -= shadowSide; |
| winRect.origin.y -= shadowTop; |
| winRect.size.width += shadowSide; |
| winRect.size.height += shadowBottom; |
| |
| if (NSIntersectsRect (contentRect, winRect)) { |
| |
| obscured = SDL_TRUE; |
| break; |
| } |
| |
| } /* window was not our window */ |
| |
| } /* iterate over windows */ |
| |
| } /* get cgsConnection */ |
| |
| } /* window is visible */ |
| |
| return obscured; |
| #else |
| return SDL_TRUE; |
| #endif |
| } |
| |
| |
| /* Locking functions for the software window buffer */ |
| static int QZ_LockWindow (_THIS, SDL_Surface *surface) { |
| #if 1 |
| return LockPortBits( [ window_view qdPort ] ); |
| #else |
| CGrafPtr qdport = [ window_view qdPort ]; |
| int result = LockPortBits ( qdport ); |
| |
| if ( !result ) { |
| Uint8* pixels = GetPixBaseAddr ( GetPortPixMap ( qdport ) ); |
| NSRect viewFrame = [ window_view frame ]; |
| NSRect winFrame = [ qz_window frame ]; |
| int vOffset = winFrame.size.height - viewFrame.size.height - viewFrame.origin.y; |
| int hOffset = viewFrame.origin.x; |
| |
| pixels = pixels + (vOffset * surface->pitch) + hOffset *4; |
| |
| if ( surface->pixels != pixels ) { |
| fprintf(stderr,"XXX: surface->pixels is %p, should be %p\n", surface->pixels, pixels); |
| } |
| } else { |
| fprintf(stderr, "XXX: could not lock port %p for surface %p\n", qdport, surface); |
| } |
| return result; |
| #endif |
| } |
| |
| static void QZ_UnlockWindow (_THIS, SDL_Surface *surface) { |
| |
| UnlockPortBits ( [ window_view qdPort ] ); |
| } |
| |
| /* Resize icon, BMP format */ |
| static const unsigned char QZ_ResizeIcon[] = { |
| 0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, |
| 0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00, |
| 0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda, |
| 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, |
| 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87, |
| 0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8, |
| 0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8, |
| 0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda, |
| 0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda, |
| 0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7, |
| 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, |
| 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8, |
| 0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc, |
| 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb, |
| 0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8, |
| 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc, |
| 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b |
| }; |
| |
| static void QZ_DrawResizeIcon (_THIS, RgnHandle dirtyRegion) { |
| |
| /* Check if we should draw the resize icon */ |
| if (SDL_VideoSurface->flags & SDL_RESIZABLE) { |
| |
| Rect icon; |
| SetRect (&icon, SDL_VideoSurface->w - 13, SDL_VideoSurface->h - 13, |
| SDL_VideoSurface->w, SDL_VideoSurface->h); |
| |
| if (RectInRgn (&icon, dirtyRegion)) { |
| |
| SDL_Rect icon_rect; |
| |
| /* Create the icon image */ |
| if (resize_icon == NULL) { |
| |
| SDL_RWops *rw; |
| SDL_Surface *tmp; |
| |
| rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon)); |
| tmp = SDL_LoadBMP_RW (rw, SDL_TRUE); |
| |
| resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY); |
| SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF); |
| |
| SDL_FreeSurface (tmp); |
| } |
| |
| icon_rect.x = SDL_VideoSurface->w - 13; |
| icon_rect.y = SDL_VideoSurface->h - 13; |
| icon_rect.w = 13; |
| icon_rect.h = 13; |
| |
| SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect); |
| } |
| } |
| } |
| |
| static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) { |
| |
| if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) { |
| QZ_GL_SwapBuffers (this); |
| } |
| else if ( [ qz_window isMiniaturized ] ) { |
| |
| /* Do nothing if miniaturized */ |
| } |
| |
| else if ( ! QZ_IsWindowObscured (qz_window) ) { |
| |
| /* Use direct copy to flush contents to the display */ |
| CGrafPtr savePort; |
| CGrafPtr dstPort, srcPort; |
| const BitMap *dstBits, *srcBits; |
| Rect dstRect, srcRect; |
| Point offset; |
| int i; |
| |
| GetPort (&savePort); |
| |
| dstPort = CreateNewPortForCGDisplayID ((UInt32)display_id); |
| srcPort = [ window_view qdPort ]; |
| |
| offset.h = 0; |
| offset.v = 0; |
| SetPort (srcPort); |
| LocalToGlobal (&offset); |
| |
| SetPort (dstPort); |
| |
| LockPortBits (dstPort); |
| LockPortBits (srcPort); |
| |
| dstBits = GetPortBitMapForCopyBits (dstPort); |
| srcBits = GetPortBitMapForCopyBits (srcPort); |
| |
| for (i = 0; i < numRects; i++) { |
| |
| SetRect (&srcRect, rects[i].x, rects[i].y, |
| rects[i].x + rects[i].w, |
| rects[i].y + rects[i].h); |
| |
| SetRect (&dstRect, |
| rects[i].x + offset.h, |
| rects[i].y + offset.v, |
| rects[i].x + rects[i].w + offset.h, |
| rects[i].y + rects[i].h + offset.v); |
| |
| CopyBits (srcBits, dstBits, |
| &srcRect, &dstRect, srcCopy, NULL); |
| |
| } |
| |
| SetPort (savePort); |
| } |
| else { |
| /* Use QDFlushPortBuffer() to flush content to display */ |
| int i; |
| RgnHandle dirty = NewRgn (); |
| RgnHandle temp = NewRgn (); |
| |
| SetEmptyRgn (dirty); |
| |
| /* Build the region of dirty rectangles */ |
| for (i = 0; i < numRects; i++) { |
| |
| MacSetRectRgn (temp, rects[i].x, rects[i].y, |
| rects[i].x + rects[i].w, rects[i].y + rects[i].h); |
| MacUnionRgn (dirty, temp, dirty); |
| } |
| |
| QZ_DrawResizeIcon (this, dirty); |
| |
| /* Flush the dirty region */ |
| QDFlushPortBuffer ( [ window_view qdPort ], dirty ); |
| DisposeRgn (dirty); |
| DisposeRgn (temp); |
| } |
| } |
| |
| static void QZ_VideoQuit (_THIS) { |
| |
| CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; |
| |
| /* Restore gamma settings */ |
| CGDisplayRestoreColorSyncSettings (); |
| |
| /* Ensure the cursor will be visible and working when we quit */ |
| CGDisplayShowCursor (display_id); |
| CGAssociateMouseAndMouseCursorPosition (1); |
| |
| if (mode_flags & SDL_FULLSCREEN) { |
| /* Fade to black to hide resolution-switching flicker (and garbage |
| that is displayed by a destroyed OpenGL context, if applicable) */ |
| if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { |
| CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); |
| } |
| QZ_UnsetVideoMode (this, TRUE); |
| if (fade_token != kCGDisplayFadeReservationInvalidToken) { |
| CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); |
| CGReleaseDisplayFadeReservation (fade_token); |
| } |
| } |
| else |
| QZ_UnsetVideoMode (this, TRUE); |
| |
| CGPaletteRelease (palette); |
| |
| if (opengl_library) { |
| SDL_UnloadObject(opengl_library); |
| opengl_library = NULL; |
| } |
| this->gl_config.driver_loaded = 0; |
| |
| if (field_edit) { |
| [field_edit release]; |
| field_edit = NULL; |
| } |
| } |
| |
| #if 0 /* Not used (apparently, it's really slow) */ |
| static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) { |
| |
| CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color); |
| |
| return 0; |
| } |
| #endif |
| |
| static int QZ_LockHWSurface(_THIS, SDL_Surface *surface) { |
| |
| return 1; |
| } |
| |
| static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) { |
| |
| } |
| |
| static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface) { |
| return(-1); /* unallowed (no HWSURFACE support here). */ |
| } |
| |
| static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) { |
| } |
| |
| /* |
| int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) { |
| return 0; |
| } |
| */ |
| |
| /* Gamma functions */ |
| int QZ_SetGamma (_THIS, float red, float green, float blue) { |
| |
| const CGGammaValue min = 0.0, max = 1.0; |
| |
| if (red == 0.0) |
| red = FLT_MAX; |
| else |
| red = 1.0 / red; |
| |
| if (green == 0.0) |
| green = FLT_MAX; |
| else |
| green = 1.0 / green; |
| |
| if (blue == 0.0) |
| blue = FLT_MAX; |
| else |
| blue = 1.0 / blue; |
| |
| if ( CGDisplayNoErr == CGSetDisplayTransferByFormula |
| (display_id, min, max, red, min, max, green, min, max, blue) ) { |
| |
| return 0; |
| } |
| else { |
| |
| return -1; |
| } |
| } |
| |
| int QZ_GetGamma (_THIS, float *red, float *green, float *blue) { |
| |
| CGGammaValue dummy; |
| if ( CGDisplayNoErr == CGGetDisplayTransferByFormula |
| (display_id, &dummy, &dummy, red, |
| &dummy, &dummy, green, &dummy, &dummy, blue) ) |
| |
| return 0; |
| else |
| return -1; |
| } |
| |
| int QZ_SetGammaRamp (_THIS, Uint16 *ramp) { |
| |
| const CGTableCount tableSize = 255; |
| CGGammaValue redTable[tableSize]; |
| CGGammaValue greenTable[tableSize]; |
| CGGammaValue blueTable[tableSize]; |
| |
| int i; |
| |
| /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */ |
| for (i = 0; i < 256; i++) |
| redTable[i % 256] = ramp[i] / 65535.0; |
| |
| for (i=256; i < 512; i++) |
| greenTable[i % 256] = ramp[i] / 65535.0; |
| |
| for (i=512; i < 768; i++) |
| blueTable[i % 256] = ramp[i] / 65535.0; |
| |
| if ( CGDisplayNoErr == CGSetDisplayTransferByTable |
| (display_id, tableSize, redTable, greenTable, blueTable) ) |
| return 0; |
| else |
| return -1; |
| } |
| |
| int QZ_GetGammaRamp (_THIS, Uint16 *ramp) { |
| |
| const CGTableCount tableSize = 255; |
| CGGammaValue redTable[tableSize]; |
| CGGammaValue greenTable[tableSize]; |
| CGGammaValue blueTable[tableSize]; |
| CGTableCount actual; |
| int i; |
| |
| if ( CGDisplayNoErr != CGGetDisplayTransferByTable |
| (display_id, tableSize, redTable, greenTable, blueTable, &actual) || |
| actual != tableSize) |
| |
| return -1; |
| |
| /* Pack tables into one array, with values from 0 to 65535 */ |
| for (i = 0; i < 256; i++) |
| ramp[i] = redTable[i % 256] * 65535.0; |
| |
| for (i=256; i < 512; i++) |
| ramp[i] = greenTable[i % 256] * 65535.0; |
| |
| for (i=512; i < 768; i++) |
| ramp[i] = blueTable[i % 256] * 65535.0; |
| |
| return 0; |
| } |
| |