# HG changeset patch # User Sam Lantinga # Date 1011725188 0 # Node ID d1447a846d8017b066b13031875221ff25018309 # Parent 9631db4d9ee16e9c0b8bff521ad556165682c6a3 Date: Sat, 19 Jan 2002 17:24:32 -0500 (EST) From: Darrell Walisser Subject: SDL Quartz video update -better mouse motion events -fixed minification bugs (except OpenGL) -fixed QZ_SetGamma for correct semantics -fade/unfade display before/after rez switch -experimental obscured-check/blind-copy code The obscured code, while it speeds up window drawing substantially, isn't ready yet. The reason is that there doesn't (yet) seem to be a way to know when the window is dragged or when the window suddenly comes to the foreground. Since Carbon windows seem to allow detection of such things, I suspect it is possible through some window server API. Cocoa(NSWindow) has no functions for such things, AFAIK. diff -r 9631db4d9ee1 -r d1447a846d80 src/video/quartz/SDL_QuartzEvents.m --- a/src/video/quartz/SDL_QuartzEvents.m Tue Jan 22 18:28:35 2002 +0000 +++ b/src/video/quartz/SDL_QuartzEvents.m Tue Jan 22 18:46:28 2002 +0000 @@ -19,6 +19,7 @@ Sam Lantinga slouken@libsdl.org */ +#include #include "SDL_QuartzKeys.h" @@ -305,7 +306,13 @@ static void QZ_PumpEvents (_THIS) { - NSDate *distantPast; + + static NSPoint lastMouse; + NSPoint mouse, saveMouse; + Point qdMouse; + CGMouseDelta dx, dy; + + NSDate *distantPast; NSEvent *event; NSRect winRect; NSRect titleBarRect; @@ -314,10 +321,36 @@ pool = [ [ NSAutoreleasePool alloc ] init ]; distantPast = [ NSDate distantPast ]; - winRect = NSMakeRect (0, 0, SDL_VideoSurface->w + 1, SDL_VideoSurface->h + 1); + winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h); titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w, SDL_VideoSurface->h + 22 ); - + + if (currentGrabMode != SDL_GRAB_ON) { /* if grabbed, the cursor can't move! (see fallback below) */ + + /* 1/2 second after a warp, the mouse cannot move (don't ask me why) */ + /* So, approximate motion with CGGetLastMouseDelta, which still works, somehow */ + if (! warp_flag) { + + GetGlobalMouse (&qdMouse); /* use Carbon since [ NSEvent mouseLocation ] is broken */ + mouse = NSMakePoint (qdMouse.h, qdMouse.v); + saveMouse = mouse; + + if (mouse.x != lastMouse.x || mouse.y != lastMouse.y) { + + QZ_PrivateCGToSDL (this, &mouse); + if (inForeground && NSPointInRect (mouse, winRect)) { + //printf ("Mouse Loc: (%f, %f)\n", mouse.x, mouse.y); + SDL_PrivateMouseMotion (0, 0, mouse.x, mouse.y); + } + } + lastMouse = saveMouse; + } + } + + /* accumulate any mouse events into one SDL mouse event */ + dx = 0; + dy = 0; + do { /* Poll for an event. This will not block */ @@ -330,22 +363,22 @@ BOOL isForGameWin; #define DO_MOUSE_DOWN(button, sendToWindow) do { \ - if ( inForeground ) { \ + if ( inForeground ) { \ if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ NSPointInRect([event locationInWindow], winRect) ) \ SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ - } \ - else { \ - QZ_DoActivate (this); \ - } \ - [ NSApp sendEvent:event ]; \ + } \ + else { \ + QZ_DoActivate (this); \ + } \ + [ NSApp sendEvent:event ]; \ } while(0) #define DO_MOUSE_UP(button, sendToWindow) do { \ if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ - !NSPointInRect([event locationInWindow], titleBarRect) )\ - SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ - [ NSApp sendEvent:event ]; \ + !NSPointInRect([event locationInWindow], titleBarRect) ) \ + SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ + [ NSApp sendEvent:event ]; \ } while(0) type = [ event type ]; @@ -365,7 +398,7 @@ DO_MOUSE_DOWN (1, 1); } break; - case 25: DO_MOUSE_DOWN (2, 0); break; + case NSOtherMouseDown: DO_MOUSE_DOWN (2, 0); break; case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break; case NSLeftMouseUp: @@ -377,7 +410,7 @@ DO_MOUSE_UP (1, 1); } break; - case 26: DO_MOUSE_UP (2, 0); break; + case NSOtherMouseUp: DO_MOUSE_UP (2, 0); break; case NSRightMouseUp: DO_MOUSE_UP (3, 0); break; case NSSystemDefined: //if ([event subtype] == 7) { @@ -389,30 +422,37 @@ case NSRightMouseDragged: case 27: case NSMouseMoved: - if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) - || NSPointInRect([event locationInWindow], winRect) ) - { - static int moves = 0; - NSPoint p; - - if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) { - p = [ NSEvent mouseLocation ]; - p.y = [[NSScreen mainScreen] frame].size.height - p.y; - } else { - p = [ event locationInWindow ]; - p.y = SDL_VideoSurface->h - p.y; - } - - if ( (moves % 10) == 0 ) { - SDL_PrivateMouseMotion (0, 0, p.x, p.y); - } - else { - CGMouseDelta dx, dy; - CGGetLastMouseDelta (&dx, &dy); - SDL_PrivateMouseMotion (0, 1, dx, dy); - } - moves++; - } + + if (currentGrabMode == SDL_GRAB_ON) { + + /** + * If input is grabbed, we'll wing it and try to send some mouse + * moved events with CGGetLastMouseDelta(). Not optimal, but better + * than nothing. + **/ + CGMouseDelta dx1, dy1; + CGGetLastMouseDelta (&dx1, &dy1); + dx += dx1; + dy += dy1; + } + else if (warp_flag) { + + Uint32 ticks; + + ticks = SDL_GetTicks(); + if (ticks - warp_ticks < 150) { + + CGMouseDelta dx1, dy1; + CGGetLastMouseDelta (&dx1, &dy1); + dx += dx1; + dy += dy1; + } + else { + + warp_flag = 0; + } + } + break; case NSScrollWheel: { @@ -435,8 +475,6 @@ case NSFlagsChanged: QZ_DoModifiers( [ event modifierFlags ] ); break; - /* case NSMouseEntered: break; */ - /* case NSMouseExited: break; */ case NSAppKitDefined: switch ( [ event subtype ] ) { case NSApplicationActivatedEventType: @@ -451,12 +489,17 @@ /* case NSApplicationDefined: break; */ /* case NSPeriodic: break; */ /* case NSCursorUpdate: break; */ - default: + + default: [ NSApp sendEvent:event ]; } } } while (event != nil); + /* check for accumulated mouse events */ + if (dx != 0 || dy != 0) + SDL_PrivateMouseMotion (0, 1, dx, dy); + [ pool release ]; } diff -r 9631db4d9ee1 -r d1447a846d80 src/video/quartz/SDL_QuartzVideo.h --- a/src/video/quartz/SDL_QuartzVideo.h Tue Jan 22 18:28:35 2002 +0000 +++ b/src/video/quartz/SDL_QuartzVideo.h Tue Jan 22 18:46:28 2002 +0000 @@ -34,25 +34,28 @@ - Multiple monitor support (currently only main display) - Accelerated blitting support - Set the window icon (dock icon when API is available) - - Avoid erasing window on minimize, or disable minimize + - Fix white OpenGL window on minimize + - Find out what events should be sent/ignored if window is mimimized + - Find a better way to deal with resolution/depth switch while app is running + - Resizeable windows + - Check accuracy of QZ_SetGamma() Problems: - OGL not working in full screen with software renderer - SetColors sets palette correctly but clears framebuffer - Crash in CG after several mode switches - - Retained windows don't draw their title bar quite right (OS Bug) - - Should I do depth switching for windowed modes? - No, not usually. - - Launch times are slow, maybe prebinding will help - - Direct framebuffer access has some artifacts, maybe a driver issue - - Cursor in 8 bit modes is screwy + - Retained windows don't draw their title bar quite right (OS Bug) (not using retained windows) + - Cursor in 8 bit modes is screwy (might just be Radeon PCI bug) + - Warping cursor delays mouse events for a fraction of a second, + there is a hack around this that helps a bit */ -#include +#include #include -#include #include #include "SDL_video.h" #include "SDL_error.h" +#include "SDL_timer.h" #include "SDL_syswm.h" #include "SDL_sysvideo.h" #include "SDL_pixels_c.h" @@ -71,21 +74,34 @@ } @end +/* Structure for rez switch gamma fades */ +/* We can hide the monitor flicker by setting the gamma tables to 0 */ +#define QZ_GAMMA_TABLE_SIZE 256 + +typedef struct { + + CGGammaValue red[QZ_GAMMA_TABLE_SIZE]; + CGGammaValue green[QZ_GAMMA_TABLE_SIZE]; + CGGammaValue blue[QZ_GAMMA_TABLE_SIZE]; + +} SDL_QuartzGammaTable; + +/* Main driver structure to store required state information */ typedef struct SDL_PrivateVideoData { - CGDirectDisplayID display; /* 0 == main display */ - CFDictionaryRef mode; - CFDictionaryRef save_mode; - CFArrayRef mode_list; - CGDirectPaletteRef palette; - NSOpenGLContext *gl_context; - Uint32 width, height, bpp; - Uint32 flags; - SDL_bool video_is_set; /* tell if the video mode was set */ - - /* Window-only fields */ - NSWindow *window; - NSQuickDrawView *view; + CGDirectDisplayID display; /* 0 == main display (only support single display) */ + CFDictionaryRef mode; /* current mode of the display */ + CFDictionaryRef save_mode; /* original mode of the display */ + CFArrayRef mode_list; /* list of available fullscreen modes */ + CGDirectPaletteRef palette; /* palette of an 8-bit display */ + NSOpenGLContext *gl_context; /* object that represents an OpenGL rendering context */ + Uint32 width, height, bpp; /* frequently used data about the display */ + Uint32 flags; /* flags for mode, for teardown purposes */ + Uint32 video_set; /* boolean; indicates if video was set correctly */ + Uint32 warp_flag; /* boolean; notify to event loop that a warp just occured */ + Uint32 warp_ticks; /* timestamp when the warp occured */ + NSWindow *window; /* Cocoa window to implement the SDL window */ + NSQuickDrawView *view; /* the window's view; draw 2D into this view */ } SDL_PrivateVideoData ; @@ -95,21 +111,68 @@ #define save_mode (this->hidden->save_mode) #define mode_list (this->hidden->mode_list) #define palette (this->hidden->palette) -#define glcontext (this->hidden->glcontext) -#define objc_video (this->hidden->objc_video) #define gl_context (this->hidden->gl_context) #define device_width (this->hidden->width) #define device_height (this->hidden->height) #define device_bpp (this->hidden->bpp) #define mode_flags (this->hidden->flags) -#define video_set (this->hidden->video_is_set) #define qz_window (this->hidden->window) -#define windowView (this->hidden->view) +#define window_view (this->hidden->view) +#define video_set (this->hidden->video_set) +#define warp_ticks (this->hidden->warp_ticks) +#define warp_flag (this->hidden->warp_flag) + +/* Obscuring code: maximum number of windows above ours (inclusive) */ +#define kMaxWindows 256 + +/* Some of the Core Graphics Server API for obscuring code */ +#define kCGSWindowLevelTop 2147483632 +#define kCGSWindowLevelDockIconDrag 500 +#define kCGSWindowLevelDockMenu 101 +#define kCGSWindowLevelMenuIgnore 21 +#define kCGSWindowLevelMenu 20 +#define kCGSWindowLevelDockLabel 12 +#define kCGSWindowLevelDockIcon 11 +#define kCGSWindowLevelDock 10 +#define kCGSWindowLevelUtility 3 +#define kCGSWindowLevelNormal 0 + +/* For completeness; We never use these window levels, they are always below us +#define kCGSWindowLevelMBarShadow -20 +#define kCGSWindowLevelDesktopPicture -2147483647 +#define kCGSWindowLevelDesktop -2147483648 +*/ -/* Interface for hardware fill not (yet) in the public API */ -int CGSDisplayHWFill (CGDirectDisplayID id, unsigned int x, unsigned int y, +typedef CGError CGSError; +typedef long CGSWindowCount; +typedef void * CGSConnectionID; +typedef int CGSWindowID; +typedef CGSWindowID* CGSWindowIDList; +typedef CGWindowLevel CGSWindowLevel; +typedef NSRect CGSRect; + +extern CGSConnectionID _CGSDefaultConnection (); + +extern CGSError CGSGetOnScreenWindowList (CGSConnectionID cid, + CGSConnectionID owner, + CGSWindowCount listCapacity, + CGSWindowIDList list, + CGSWindowCount *listCount); + +extern CGSError CGSGetScreenRectForWindow (CGSConnectionID cid, + CGSWindowID wid, + CGSRect *rect); + +extern CGWindowLevel CGSGetWindowLevel (CGSConnectionID cid, + CGSWindowID wid, + CGSWindowLevel *level); + +extern CGSError CGSDisplayHWFill (CGDirectDisplayID id, unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned int color); -int CGSDisplayCanHWFill (CGDirectDisplayID id); + +extern CGSError CGSDisplayCanHWFill (CGDirectDisplayID id); + +extern CGSError CGSGetMouseEnabledFlags (CGSConnectionID cid, CGSWindowID wid, int *flags); /* Bootstrap functions */ static int QZ_Available (); @@ -156,7 +219,7 @@ static int QZ_GL_LoadLibrary (_THIS, const char *location); /* Private function to warp the cursor (used internally) */ -static void QZ_PrivateWarpCursor (_THIS, int fullscreen, int h, int x, int y); +static void QZ_PrivateWarpCursor (_THIS, int x, int y); /* Cursor and Mouse functions */ static void QZ_FreeWMCursor (_THIS, WMcursor *cursor); @@ -177,3 +240,4 @@ static int QZ_IconifyWindow (_THIS); static SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode); /*static int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info);*/ + diff -r 9631db4d9ee1 -r d1447a846d80 src/video/quartz/SDL_QuartzVideo.m --- a/src/video/quartz/SDL_QuartzVideo.m Tue Jan 22 18:28:35 2002 +0000 +++ b/src/video/quartz/SDL_QuartzVideo.m Tue Jan 22 18:46:28 2002 +0000 @@ -32,9 +32,10 @@ #include "SDL_QuartzEvents.m" #include "SDL_QuartzWindow.m" + /* Bootstrap binding, enables entry point into the driver */ VideoBootStrap QZ_bootstrap = { - "Quartz", "MacOS X CoreGraphics", QZ_Available, QZ_CreateDevice + "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice }; /* Bootstrap functions */ @@ -44,7 +45,7 @@ static SDL_VideoDevice* QZ_CreateDevice (int device_index) { - #pragma unused (device_index) +#pragma unused (device_index) SDL_VideoDevice *device; SDL_PrivateVideoData *hidden; @@ -57,7 +58,7 @@ memset (device, 0, sizeof (*device) ); memset (hidden, 0, sizeof (*hidden) ); - + device->hidden = hidden; device->VideoInit = QZ_VideoInit; @@ -67,7 +68,7 @@ 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->FreeHWSurface = QZ_FreeHWSurface; @@ -83,7 +84,7 @@ 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; @@ -98,9 +99,9 @@ device->IconifyWindow = QZ_IconifyWindow; /*device->GetWMInfo = QZ_GetWMInfo;*/ device->GrabInput = QZ_GrabInput; - + device->free = QZ_DeleteDevice; - + return device; } @@ -112,30 +113,30 @@ static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) { - /* 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 (); - - /* Gather some information that is useful to know about the display */ - CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel), - kCFNumberSInt32Type, &device_bpp); + /* 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 (); + + /* 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); + CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth), + kCFNumberSInt32Type, &device_width); - video_format->BitsPerPixel = device_bpp; - - return 0; + CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight), + kCFNumberSInt32Type, &device_height); + + video_format->BitsPerPixel = device_bpp; + + return 0; } static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) { - CFIndex num_modes = CFArrayGetCount (mode_list); + CFIndex num_modes; CFIndex i; static SDL_Rect **list = NULL; @@ -157,6 +158,8 @@ list = NULL; } + num_modes = CFArrayGetCount (mode_list); + /* Build list of modes with the requested bpp */ for (i = 0; i < num_modes; i++) { @@ -201,16 +204,18 @@ list_size++; - if ( list == NULL) - list = (SDL_Rect**) malloc (sizeof(*list) * list_size+1); + if (list == NULL) + list = (SDL_Rect**) malloc (sizeof(*list) * (list_size+1) ); else - list = (SDL_Rect**) realloc (list, sizeof(*list) * list_size+1); + list = (SDL_Rect**) realloc (list, sizeof(*list) * (list_size+1)); rect = (SDL_Rect*) malloc (sizeof(**list)); - if (list == NULL || rect == NULL) + if (list == NULL || rect == NULL) { SDL_OutOfMemory (); - + return NULL; + } + rect->w = width; rect->h = height; @@ -241,43 +246,146 @@ return list; } +/* Gamma functions to try to hide the flash from a rez switch */ +/* Fade the display from normal to black */ +/* Save gamma tables for fade back to normal */ +static UInt32 QZ_FadeGammaOut (_THIS, SDL_QuartzGammaTable *table) { + + CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE], + greenTable[QZ_GAMMA_TABLE_SIZE], + blueTable[QZ_GAMMA_TABLE_SIZE]; + + float percent; + int j; + int actual; + + if ( (CGDisplayNoErr != CGGetDisplayTransferByTable + (display_id, QZ_GAMMA_TABLE_SIZE, + table->red, table->green, table->blue, &actual)) || + actual != QZ_GAMMA_TABLE_SIZE) { + + return 1; + } + + memcpy (redTable, table->red, sizeof(redTable)); + memcpy (greenTable, table->green, sizeof(greenTable)); + memcpy (blueTable, table->blue, sizeof(greenTable)); + + for (percent = 1.0; percent >= 0.0; percent -= 0.01) { + + for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { + + redTable[j] = redTable[j] * percent; + greenTable[j] = greenTable[j] * percent; + blueTable[j] = blueTable[j] * percent; + } + + if (CGDisplayNoErr != CGSetDisplayTransferByTable + (display_id, QZ_GAMMA_TABLE_SIZE, + redTable, greenTable, blueTable)) { + + CGDisplayRestoreColorSyncSettings(); + return 1; + } + + SDL_Delay (10); + } + + return 0; +} + +/* Fade the display from black to normal */ +/* Restore previously saved gamma values */ +static UInt32 QZ_FadeGammaIn (_THIS, SDL_QuartzGammaTable *table) { + + CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE], + greenTable[QZ_GAMMA_TABLE_SIZE], + blueTable[QZ_GAMMA_TABLE_SIZE]; + + float percent; + int j; + + memset (redTable, 0, sizeof(redTable)); + memset (greenTable, 0, sizeof(greenTable)); + memset (blueTable, 0, sizeof(greenTable)); + + for (percent = 0.0; percent <= 1.0; percent += 0.01) { + + for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { + + redTable[j] = table->red[j] * percent; + greenTable[j] = table->green[j] * percent; + blueTable[j] = table->blue[j] * percent; + } + + if (CGDisplayNoErr != CGSetDisplayTransferByTable + (display_id, QZ_GAMMA_TABLE_SIZE, + redTable, greenTable, blueTable)) { + + CGDisplayRestoreColorSyncSettings(); + return 1; + } + + SDL_Delay (10); + } + + return 0; +} + static void QZ_UnsetVideoMode (_THIS) { /* Reset values that may change between switches */ this->info.blit_fill = 0; this->FillHWRect = NULL; this->UpdateRects = NULL; - - /* Restore gamma settings */ - CGDisplayRestoreColorSyncSettings (); - /* Restore original screen resolution */ + /* Release fullscreen resources */ if ( mode_flags & SDL_FULLSCREEN ) { + + SDL_QuartzGammaTable gamma_table; + int gamma_error; + + gamma_error = QZ_FadeGammaOut (this, &gamma_table); + + /* Release the OpenGL context */ + /* Do this first to avoid trash on the display before fade */ + if ( mode_flags & SDL_OPENGL ) + QZ_TearDownOpenGL (this); + if (mode_flags & SDL_OPENGL) CGLSetFullScreen(NULL); - + + /* Restore original screen resolution/bpp */ CGDisplaySwitchToMode (display_id, save_mode); CGDisplayRelease (display_id); + ShowMenuBar (); + + if (! gamma_error) + QZ_FadeGammaIn (this, &gamma_table); } - /* Release window mode data structures */ + /* Release window mode resources */ else { if ( (mode_flags & SDL_OPENGL) == 0 ) { - UnlockPortBits ( [ windowView qdPort ] ); - [ windowView release ]; + UnlockPortBits ( [ window_view qdPort ] ); + [ window_view release ]; } [ qz_window setContentView:nil ]; [ qz_window setDelegate:nil ]; [ qz_window close ]; + [ qz_window release ]; + + /* Release the OpenGL context */ + if ( mode_flags & SDL_OPENGL ) + QZ_TearDownOpenGL (this); } + + /* Restore gamma settings */ + CGDisplayRestoreColorSyncSettings (); /* Set pixels to null (so other code doesn't try to free it) */ if (this->screen != NULL) this->screen->pixels = NULL; - /* Release the OpenGL context */ - if ( mode_flags & SDL_OPENGL ) - QZ_TearDownOpenGL (this); - /* Ensure the cursor will be visible and working when we quit */ CGDisplayShowCursor (display_id); CGAssociateMouseAndMouseCursorPosition (1); @@ -289,7 +397,9 @@ static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags) { int exact_match; - + int gamma_error; + SDL_QuartzGammaTable gamma_table; + /* See if requested mode exists */ mode = CGDisplayBestModeForParameters (display_id, bpp, width, height, &exact_match); @@ -301,34 +411,24 @@ goto ERR_NO_MATCH; } + /* Fade display to zero gamma */ + gamma_error = QZ_FadeGammaOut (this, &gamma_table); + /* Put up the blanking window (a window above all other windows) */ if ( CGDisplayNoErr != CGDisplayCapture (display_id) ) { 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; } - - /* None of these methods seem to fix the fullscreen artifacts bug(s) */ -#if USE_GDHANDLE - SetGDevice(GetMainDevice()); - current->pitch = (**(** GetMainDevice() ).gdPMap).rowBytes & 0x3FFF; - current->pixels = (**(** GetMainDevice() ).gdPMap).baseAddr; -#elif USE_CREATEPORT - device_port = CreateNewPortForCGDisplayID((Uint32*)display_id); - SetPort (device_port); - LockPortBits ( device_port ); - current->pixels = GetPixBaseAddr ( GetPortPixMap ( device_port ) ); - current->pitch = GetPixRowBytes ( GetPortPixMap ( device_port ) ); - UnlockPortBits ( device_port ); -#else + current->pixels = (Uint32*) CGDisplayBaseAddress (display_id); current->pitch = CGDisplayBytesPerRow (display_id); -#endif current->flags = 0; current->w = width; @@ -354,7 +454,7 @@ CGLContextObj ctx; if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { - return NULL; + goto ERR_NO_GL; } ctx = [ gl_context cglContext ]; @@ -367,22 +467,30 @@ } [ 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 the display to original gamma */ + if (! gamma_error ) + QZ_FadeGammaIn (this, &gamma_table); /* Save the flags to ensure correct tear-down */ mode_flags = current->flags; return current; - /* Since the blanking window covers *all* windows (even force quit) correct recovery is crutial */ + /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ ERR_NO_GL: CGDisplaySwitchToMode (display_id, save_mode); ERR_NO_SWITCH: CGDisplayRelease (display_id); - ERR_NO_CAPTURE: + ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); } ERR_NO_MATCH: return NULL; } @@ -440,16 +548,17 @@ /* For 2D, we set the content view to a NSQuickDrawView */ else { - windowView = [ [ NSQuickDrawView alloc ] init ]; - [ qz_window setContentView:windowView ]; + window_view = [ [ SDL_QuartzWindowView alloc ] init ]; + [ qz_window setContentView:window_view ]; [ qz_window makeKeyAndOrderFront:nil ]; - LockPortBits ( [ windowView qdPort ] ); - current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ windowView qdPort ] ) ); - current->pitch = GetPixRowBytes ( GetPortPixMap ( [ windowView qdPort ] ) ); - + LockPortBits ( [ window_view qdPort ] ); + current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ window_view qdPort ] ) ); + current->pitch = GetPixRowBytes ( GetPortPixMap ( [ window_view qdPort ] ) ); + current->flags |= SDL_SWSURFACE; current->flags |= SDL_PREALLOC; + if ( flags & SDL_NOFRAME ) current->flags |= SDL_NOFRAME; if ( flags & SDL_RESIZABLE ) @@ -462,6 +571,10 @@ this->UpdateRects = QZ_UpdateRects; } + + /* Save flags to ensure correct teardown */ + mode_flags = current->flags; + return current; } @@ -482,7 +595,7 @@ /* Setup windowed video */ else { /* Force bpp to the device's bpp */ - bpp = current->format->BitsPerPixel; + bpp = device_bpp; current = QZ_SetVideoWindowed (this, current, width, height, bpp, flags); if (current == NULL) return NULL; @@ -520,10 +633,7 @@ } } - /* Warp mouse to origin in order to get passive mouse motion events started correctly */ - QZ_PrivateWarpCursor (this, current->flags & SDL_FULLSCREEN, height, 0, 0); - - /* Signal successful completion */ + /* Signal successful completion (used internally) */ video_set = SDL_TRUE; return current; @@ -561,12 +671,293 @@ #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. + **/ +static int QZ_IsWindowObscured (NSWindow *window) { + +//#define TEST_OBSCURED 1 + +#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 isMainWindow; + 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 ]; + //isMainWindow = [ window isMainWindow ]; + + /* 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; + 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 +} + 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 ] && + ! (SDL_VideoSurface->flags & SDL_OPENGL)) { + + /** + * Set port alpha opaque so deminiaturize looks right + * This isn't so nice, but there is no + * initial deminatureize notification (before demini starts) + **/ + QZ_SetPortAlphaOpaque ([ [ qz_window contentView ] qdPort], + [ qz_window styleMask ] & NSBorderlessWindowMask); + } + 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 (); @@ -582,7 +973,7 @@ } /* Flush the dirty region */ - QDFlushPortBuffer ( [ windowView qdPort ], dirty ); + QDFlushPortBuffer ( [ window_view qdPort ], dirty ); DisposeRgn (dirty); DisposeRgn (temp); } @@ -597,6 +988,7 @@ 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; } @@ -605,7 +997,8 @@ return 1; } -static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) { +static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) { + } static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) { @@ -621,24 +1014,43 @@ static 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) ) + if ( CGDisplayNoErr == CGSetDisplayTransferByFormula + (display_id, min, max, red, min, max, green, min, max, blue) ) { + + return 0; + } + else { + return -1; - - return 0; + } } static int QZ_GetGamma (_THIS, float *red, float *green, float *blue) { CGGammaValue dummy; - if ( CGDisplayNoErr != CGGetDisplayTransferByFormula + if ( CGDisplayNoErr == CGGetDisplayTransferByFormula (display_id, &dummy, &dummy, red, &dummy, &dummy, green, &dummy, &dummy, blue) ) + return 0; + else return -1; - - return 0; } static int QZ_SetGammaRamp (_THIS, Uint16 *ramp) { @@ -660,11 +1072,11 @@ for (i=512; i < 768; i++) blueTable[i % 256] = ramp[i] / 65535.0; - if ( CGDisplayNoErr != CGSetDisplayTransferByTable + if ( CGDisplayNoErr == CGSetDisplayTransferByTable (display_id, tableSize, redTable, greenTable, blueTable) ) + return 0; + else return -1; - - return 0; } static int QZ_GetGammaRamp (_THIS, Uint16 *ramp) { @@ -695,7 +1107,8 @@ return 0; } -/* OpenGL helper functions */ +/* OpenGL helper functions (used internally) */ + static int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags) { NSOpenGLPixelFormatAttribute attr[32]; @@ -761,6 +1174,7 @@ [ gl_context release ]; } + /* SDL OpenGL functions */ static int QZ_GL_LoadLibrary (_THIS, const char *location) { @@ -805,6 +1219,7 @@ CGLGetParameter (ctx, param, (long*)value); */ + *value = -1; return -1; } @@ -817,3 +1232,5 @@ static void QZ_GL_SwapBuffers (_THIS) { [ gl_context flushBuffer ]; } + + diff -r 9631db4d9ee1 -r d1447a846d80 src/video/quartz/SDL_QuartzWM.m --- a/src/video/quartz/SDL_QuartzWM.m Tue Jan 22 18:28:35 2002 +0000 +++ b/src/video/quartz/SDL_QuartzWM.m Tue Jan 22 18:46:28 2002 +0000 @@ -87,40 +87,112 @@ return 1; } -static void QZ_PrivateWarpCursor (_THIS, int fullscreen, int h, int x, int y) { +/** + * Coordinate conversion functions, for convenience + * Cocoa sets the origin at the lower left corner of the window/screen + * SDL, CoreGraphics/WindowServer, and QuickDraw use the origin at the upper left corner + * The routines were written so they could be called before SetVideoMode() has finished; + * this might have limited usefulness at the moment, but the extra cost is trivial. + **/ + +/* Convert Cocoa screen coordinate to Cocoa window coordinate */ +static void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) { - CGPoint p; + *p = [ qz_window convertScreenToBase:*p ]; +} + + +/* Convert Cocoa window coordinate to Cocoa screen coordinate */ +static void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) { + + *p = [ qz_window convertBaseToScreen:*p ]; +} + +/* Convert SDL coordinate to Cocoa coordinate */ +static void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) { + + int height; - /* We require absolute screen coordiates for our warp */ - p.x = x; - p.y = h - y; + if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */ + + height = CGDisplayPixelsHigh (display_id); + } + else { + + height = NSHeight ( [ qz_window frame ] ); + if ( [ qz_window styleMask ] & NSTitledWindowMask ) { - if ( fullscreen ) - /* Already absolute coordinates */ - CGDisplayMoveCursorToPoint(display_id, p); - else { - /* Convert to absolute screen coordinates */ - NSPoint base, screen; - base = NSMakePoint (p.x, p.y); - screen = [ qz_window convertBaseToScreen:base ]; - p.x = screen.x; - p.y = device_height - screen.y; - CGDisplayMoveCursorToPoint (display_id, p); + height -= 22; + } + } + + p->y = height - p->y; +} + +/* Convert Cocoa coordinate to SDL coordinate */ +static void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) { + + QZ_PrivateSDLToCocoa (this, p); +} + +/* Convert SDL coordinate to window server (CoreGraphics) coordinate */ +static CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) { + + CGPoint cgp; + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + QZ_PrivateSDLToCocoa (this, p); + QZ_PrivateLocalToGlobal (this, p); + + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + } + + cgp.x = p->x; + cgp.y = p->y; + + return cgp; +} + +/* Convert window server (CoreGraphics) coordinate to SDL coordinate */ +static void QZ_PrivateCGToSDL (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + /* Convert CG Global to Cocoa Global */ + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + + QZ_PrivateGlobalToLocal (this, p); + QZ_PrivateCocoaToSDL (this, p); } } -static void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { +static void QZ_PrivateWarpCursor (_THIS, int x, int y) { + + NSPoint p; + CGPoint cgp; + p = NSMakePoint (x, y); + cgp = QZ_PrivateSDLToCG (this, &p); + CGDisplayMoveCursorToPoint (display_id, cgp); + warp_ticks = SDL_GetTicks(); + warp_flag = 1; +} + +static void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { + /* Only allow warping when in foreground */ if ( ! inForeground ) return; /* Do the actual warp */ - QZ_PrivateWarpCursor (this, SDL_VideoSurface->flags & SDL_FULLSCREEN, - SDL_VideoSurface->h, x, y); - - /* Generate mouse moved event */ - SDL_PrivateMouseMotion (SDL_RELEASED, 0, x, y); + QZ_PrivateWarpCursor (this, x, y); } static void QZ_MoveWMCursor (_THIS, int x, int y) { } @@ -199,6 +271,17 @@ return 0; } } +static int QZ_IconifyWindow (_THIS) { + + if ( ! [ qz_window isMiniaturized ] ) { + [ qz_window miniaturize:nil ]; + return 1; + } + else { + SDL_SetError ("Quartz window already iconified"); + return 0; + } +} /* static int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) { @@ -221,6 +304,7 @@ currentGrabMode = SDL_GRAB_ON; break; case SDL_GRAB_FULLSCREEN: + break; } diff -r 9631db4d9ee1 -r d1447a846d80 src/video/quartz/SDL_QuartzWindow.m --- a/src/video/quartz/SDL_QuartzWindow.m Tue Jan 22 18:28:35 2002 +0000 +++ b/src/video/quartz/SDL_QuartzWindow.m Tue Jan 22 18:46:28 2002 +0000 @@ -7,17 +7,74 @@ - (void)display; @end +/** + * Function to set the opacity of window's pixels to 100% + * The opacity is only used by the window server code that does the minimize effect + **/ +static void QZ_SetPortAlphaOpaque (CGrafPtr port, Uint32 noTitleBar) { + + Uint32 *pixels; + Uint32 rowPixels; + Uint32 width, height; + Uint32 bpp; + PixMapHandle pixMap; + Rect bounds; + int i, j; + + pixMap = GetPortPixMap ( port ); + bpp = GetPixDepth ( pixMap ); + + if (bpp == 32) { + + GetPortBounds ( port, &bounds ); + width = bounds.right - bounds.left; + height = bounds.bottom - bounds.top; + + LockPortBits (port); + + pixels = (Uint32*) GetPixBaseAddr ( pixMap ); + rowPixels = GetPixRowBytes ( pixMap ) / 4; + + if (! noTitleBar) { + + /* offset for title bar */ + pixels += rowPixels * 22; + } + + for (i = 0; i < height; i++) + for (j = 0; j < width; j++) { + + pixels[ (i * rowPixels) + j ] |= 0xFF000000; + } + + UnlockPortBits (port); + } +} + @implementation SDL_QuartzWindow -/* These methods should be rewritten to fix the miniaturize bug */ +/* override these methods to fix the miniaturize animation/dock icon bug */ - (void)miniaturize:(id)sender { + + if (SDL_VideoSurface->flags & SDL_OPENGL) { + + /* Grab framebuffer and put into NSImage */ + /* [ qz_window setMiniwindowImage:image ]; */ + } + else { + + QZ_SetPortAlphaOpaque ([ [ self contentView ] qdPort ], + [ self styleMask ] & NSBorderlessWindowMask); + } + [ super miniaturize:sender ]; } +/* this routine fires *after* deminiaturizing, so it might be useless to us */ - (void)deminiaturize:(id)sender { - [ super deminiaturize:sender ]; + [ super deminiaturize:sender ]; } - (void)display @@ -38,4 +95,14 @@ SDL_PrivateQuit(); return NO; } + @end + +/* empty class; probably could be used to fix bugs in the future */ +@interface SDL_QuartzWindowView : NSQuickDrawView +{} +@end + +@implementation SDL_QuartzWindowView + +@end \ No newline at end of file