Mercurial > sdl-ios-xcode
diff src/video/macdsp/SDL_dspvideo.c @ 0:74212992fb08
Initial revision
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Thu, 26 Apr 2001 16:45:43 +0000 |
parents | |
children | e8157fcb3114 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/video/macdsp/SDL_dspvideo.c Thu Apr 26 16:45:43 2001 +0000 @@ -0,0 +1,1386 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 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@devolution.com +*/ + +/* + Written by Darrell Walisser <dwaliss1@purdue.edu> + + Implementation notes ---------------------------------------------------------------------- + + A bit on GWorlds in VRAM from technote 1182: + + There are two important things to note about GWorld's allocated in + VRAM. First, the base address retrieved through GetPixBaseAddr or + read directly from the PixMap structure can become invalid anytime + memory is allocated in VRAM. This can occur either by explicit + allocations, such as calls to NewGWorld, or by implicit ones, such as + those associated with the internal texture allocation of OpenGL. The + stored pixel images themselves will still be valid but may have been + moved in VRAM, thus rendering any stored base addresses invalid. + You should never store an image's base address for longer than is + necessary and especially never across calls to NewGWorld or + texture-creation routines. + + Secondly, an offscreen pixel image allocated in VRAM can be + purged at system task time by the display driver. This means any + time your application yields time such by calling WaitNextEvent or + SystemTask you can lose your VRAM GWorld contents. While this + happens infrequently, usually associated with display resolution or + pixel depth changes you must code for this eventuality. This purge + can occur whether or not the GWorld is locked or not. A return value + of false from LockPixels, a NULL return value from GetPixBaseAddr + or NULL in the baseAddr field of the PixMap mean that the pixel + image has been purged. To reallocate it you can either call + UpdateGWorld or Dispose your current GWorld through + DisposeGWorld and reallocate it via NewGWorld. Either way you must + then rebuild the pixel image. + +------------------------------------------------------------------------------------ + + Currently, I don't account for (1). In my testing, NewGWorld never invalidated + other existing GWorlds in VRAM. However, I do have protection for (2). + Namely, I am using GetOSEvent() instead of WaitNextEvent() so that there are no + context switches (the app hogs the CPU). Eventually a book-keeping system should + be coded to take care of (1) and (2). + +------------------------------------------------------------------------------------ + + System requirements (* denotes optional): + + 1. DrawSprocket 1.7.3 + 2. *MacOS 9 or later for hardware accelerated blit / fill + 3. *May also require certain graphics hardware for (2). I trust that all Apple OEM + hardware will work. Third party accelerators may work if they have QuickDraw + acceleration in the drivers and the drivers have been updated for OS 9. The current + Voodoo 3 drivers (1.0b12) do not work. + + Coding suggestions: + + 1. Use SDL_UpdateRects ! + + If no QuickDraw acceleration is present, double-buffered surfaces will use a back buffer + in System memory. I recommend you use SDL_UpdateRects with double-buffered surfaces + for best performance on these cards, since the overhead is nearly zero for VRAM back buffer. + + 2. Load most-resident surfaces first. + + If you fill up VRAM or AGP memory, there is no contingency for purging to make room for the next one. + Therefore, you should load the surfaces you plan to use the most frequently first. + Sooner or later, I will code LRU replacement to help this. + + TODO: + Some kind of posterized mode for resolutions < 640x480. + Window support / fullscreen toggle. + Figure out how much VRAM is available. Put in video->info->video_mem. + Track VRAM usage. + + BUGS: + I can't create a hardware surface the same size as the screen?! How to fix? + + + + COMPILE OPTIONS: + + DSP_TRY_CC_AND_AA - Define if you want to try HWA color-key and alpha blitters + HW color-key blitting gives substantial improvements, + but hw alpha is neck-and-neck with SDL's soft bitter. + + DSP_NO_SYNC_VBL - Define for HWA double-buffered surfaces: don't sync + pseudo-flip to monitor redraw. + + DSP_NO_SYNC_OPENGL - Define for OpenGL surfaces: don't sync buffer swap. Synching buffer + swap may result in reduced performance, but can eliminate some + tearing artifacts. + CHANGELOG: + 09/17/00 Lots of little tweaks. Build modelist in reverse order so largest contexts + list first. Compared various methods with ROM methods and fixed rez switch + crashing bug in GL Tron. (Woohoo!) +*/ + +#define DSP_TRY_CC_AND_AA + +/* #define DSP_NO_SYNC_VBL */ + +#define DSP_NO_SYNC_OPENGL + + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if TARGET_API_MAC_CARBON +#include <Carbon.h> +#else +#include <LowMem.h> +#include <Gestalt.h> +#include <Devices.h> +#include <DiskInit.h> +#include <QDOffscreen.h> +#endif + +#include "SDL_video.h" +#include "SDL_blit.h" +#include "SDL_error.h" +#include "SDL_syswm.h" +#include "SDL_sysvideo.h" +#include "SDL_dspvideo.h" +#include "SDL_macgl_c.h" +#include "SDL_macwm_c.h" +#include "SDL_macmouse_c.h" +#include "SDL_macevents_c.h" + +/* Initialization/Query functions */ +static int DSp_VideoInit(_THIS, SDL_PixelFormat *vformat); +static SDL_Rect **DSp_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); +static SDL_Surface *DSp_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); +static int DSp_SetColors(_THIS, int firstcolor, int ncolors, + SDL_Color *colors); +static int DSp_CreatePalette(_THIS); +static int DSp_DestroyPalette(_THIS); +static void DSp_VideoQuit(_THIS); + +static int DSp_GetMainDevice (_THIS, GDHandle *device); +static void DSp_IsHWAvailable (_THIS, SDL_PixelFormat *vformat); +static void DSp_DSpUpdate(_THIS, int numrects, SDL_Rect *sdl_rects); +static void DSp_DirectUpdate(_THIS, int numrects, SDL_Rect *sdl_rects); + +/* Hardware surface functions */ +static int DSp_SetHWAlpha(_THIS, SDL_Surface *surface, UInt8 alpha); +static int DSp_SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key); +static int DSp_NewHWSurface(_THIS, CGrafPtr *port, int depth, int width, int height); +static int DSp_AllocHWSurface(_THIS, SDL_Surface *surface); +static int DSp_LockHWSurface(_THIS, SDL_Surface *surface); +static void DSp_UnlockHWSurface(_THIS, SDL_Surface *surface); +static void DSp_FreeHWSurface(_THIS, SDL_Surface *surface); +static int DSp_FlipHWSurface(_THIS, SDL_Surface *surface); +static int DSp_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dest); +static int DSp_HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); +static int DSp_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color); + +#ifdef HAVE_OPENGL + static void DSp_GL_SwapBuffers (_THIS); +#endif + +#if ! TARGET_API_MAC_CARBON + + #define GetPortPixRowBytes(x) ( (*(x->portPixMap))->rowBytes ) + #define GetGDevPixMap(x) ((**(x)).gdPMap) + #define GetPortPixMap(x) ((*(x)).portPixMap) + + #define GetPixDepth(y) ((**(y)).pixelSize) + //#define GetPixRowBytes(y) ((**(y)).rowBytes) + //#define GetPixBaseAddr(y) ((**(y)).baseAddr) + #define GetPixCTab(y) ((**(y)).pmTable) + #define GetPortBitMapForCopyBits(x) (&(((GrafPtr)(x))->portBits)) + +#else + #define GetPortPixRowBytes(x) (GetPixRowBytes(GetPortPixMap(x)) ) + #define GetGDevPixMap(x) ((**(x)).gdPMap) + +#endif + +typedef struct private_hwdata { + + GWorldPtr offscreen; // offscreen gworld in VRAM or AGP + + #ifdef DSP_TRY_CC_AND_AA + GWorldPtr mask; // transparent mask + RGBColor alpha; // alpha color + RGBColor trans; // transparent color + #endif + +} private_hwdata; + +typedef private_hwdata private_swdata ; /* have same fields */ + +/* Macintosh toolbox driver bootstrap functions */ + +static int DSp_Available(void) +{ + /* Check for DrawSprocket */ + /* This check is only meaningful if you weak-link DrawSprocketLib */ + return ((Ptr)DSpStartup != (Ptr)kUnresolvedCFragSymbolAddress); +} + +static void DSp_DeleteDevice(SDL_VideoDevice *device) +{ + /* -dw- taking no chances with null pointers */ + if (device) { + + if (device->hidden) { + + if (device->hidden->dspinfo) + free(device->hidden->dspinfo); + + free(device->hidden); + } + free(device); + } +} + +static SDL_VideoDevice *DSp_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice)); + if ( device ) { + memset(device, 0, sizeof (*device)); + device->hidden = (struct SDL_PrivateVideoData *) + malloc((sizeof *device->hidden)); + if (device->hidden) + memset(device->hidden, 0, sizeof ( *(device->hidden) ) ); + } + if ( (device == NULL) || (device->hidden == NULL) ) { + SDL_OutOfMemory(); + + if ( device ) { + + if (device->hidden) + free (device->hidden); + + free(device); + } + + return(NULL); + } + + /* Allocate DrawSprocket information */ + device->hidden->dspinfo = (struct DSpInfo *)malloc( + (sizeof *device->hidden->dspinfo)); + if ( device->hidden->dspinfo == NULL ) { + SDL_OutOfMemory(); + free(device->hidden); + free(device); + return(0); + } + memset(device->hidden->dspinfo, 0, (sizeof *device->hidden->dspinfo)); + + /* Set the function pointers */ + device->VideoInit = DSp_VideoInit; + device->ListModes = DSp_ListModes; + device->SetVideoMode = DSp_SetVideoMode; + device->SetColors = DSp_SetColors; + device->UpdateRects = NULL; + device->VideoQuit = DSp_VideoQuit; + device->AllocHWSurface = DSp_AllocHWSurface; + device->CheckHWBlit = NULL; + device->FillHWRect = NULL; + device->SetHWColorKey = NULL; + device->SetHWAlpha = NULL; + device->LockHWSurface = DSp_LockHWSurface; + device->UnlockHWSurface = DSp_UnlockHWSurface; + device->FlipHWSurface = DSp_FlipHWSurface; + device->FreeHWSurface = DSp_FreeHWSurface; +#ifdef HAVE_OPENGL + device->GL_MakeCurrent = Mac_GL_MakeCurrent; + device->GL_SwapBuffers = DSp_GL_SwapBuffers; +#endif + device->SetCaption = NULL; + device->SetIcon = NULL; + device->IconifyWindow = NULL; + device->GrabInput = NULL; + device->GetWMInfo = NULL; + device->FreeWMCursor = Mac_FreeWMCursor; + device->CreateWMCursor = Mac_CreateWMCursor; + device->ShowWMCursor = Mac_ShowWMCursor; + device->WarpWMCursor = Mac_WarpWMCursor; + device->InitOSKeymap = Mac_InitOSKeymap; + device->PumpEvents = Mac_PumpEvents; + + device->GrabInput = NULL; + device->CheckMouseMode = NULL; + + device->free = DSp_DeleteDevice; + + return device; +} + +VideoBootStrap DSp_bootstrap = { + "DSp", "MacOS DrawSprocket", + DSp_Available, DSp_CreateDevice +}; + +/* Use DSp/Display Manager to build mode list for given screen */ +static SDL_Rect** DSp_BuildModeList (const GDHandle gDevice) +{ + DSpContextAttributes attributes; + DSpContextReference context; + DisplayIDType displayID; + SDL_Rect temp_list [16]; + SDL_Rect **mode_list; + int width, height, i, j; + + #if TARGET_API_MAC_OSX + + displayID = 0; + + #else + /* Ask Display Manager for integer id of screen device */ + if ( DMGetDisplayIDByGDevice (gDevice, &displayID, SDL_TRUE) != noErr ) { + return NULL; + } + #endif + /* Get the first possible DSp context on this device */ + if ( DSpGetFirstContext (displayID, &context) != noErr ) { + return NULL; + } + + if ( DSpContext_GetAttributes (context, &attributes) != noErr ) + return NULL; + + for ( i = 0; i < SDL_TABLESIZE(temp_list); i++ ) { + width = attributes.displayWidth; + height = attributes.displayHeight; + + temp_list [i].x = 0 | attributes.displayBestDepth; + temp_list [i].y = 0; + temp_list [i].w = width; + temp_list [i].h = height; + + /* DSp will report many different contexts with the same width and height. */ + /* They will differ in bit depth and refresh rate. */ + /* We will ignore them until we reach one with a different width/height */ + /* When there are no more contexts to look at, we will quit building the list*/ + while ( width == attributes.displayWidth && height == attributes.displayHeight ) { + + OSStatus err = DSpGetNextContext (context, &context); + if (err != noErr) + if (err == kDSpContextNotFoundErr) + goto done; + else + return NULL; + + if ( DSpContext_GetAttributes (context, &attributes) != noErr ) + return NULL; + + temp_list [i].x |= attributes.displayBestDepth; + } + } +done: + i++; /* i was not incremented before kicking out of the loop */ + + mode_list = (SDL_Rect**) malloc (sizeof (SDL_Rect*) * (i+1)); + if (mode_list) { + + /* -dw- new stuff: build in reverse order so largest sizes list first */ + for (j = i-1; j >= 0; j--) { + mode_list [j] = (SDL_Rect*) malloc (sizeof (SDL_Rect)); + if (mode_list [j]) + memcpy (mode_list [j], &(temp_list [j]), sizeof (SDL_Rect)); + else { + SDL_OutOfMemory (); + return NULL; + } + } + mode_list [i] = NULL; /* append null to the end */ + } + else { + SDL_OutOfMemory (); + return NULL; + } + + return mode_list; +} + +static void DSp_IsHWAvailable (_THIS, SDL_PixelFormat *vformat) +{ + /* + VRAM GWorlds are only available on OS 9 or later. + Even with OS 9, some display drivers won't support it, + so we create a test GWorld and check for errors. + */ + + long versionSystem; + + dsp_vram_available = SDL_FALSE; + dsp_agp_available = SDL_FALSE; + + Gestalt ('sysv', &versionSystem); + if (0x00000860 < (versionSystem & 0x0000FFFF)) { + + GWorldPtr offscreen; + OSStatus err; + Rect bounds; + + SetRect (&bounds, 0, 0, 320, 240); + + err = NewGWorld (&offscreen, vformat->BitsPerPixel, &bounds, NULL, SDL_Display, useDistantHdwrMem | noNewDevice); + if (err == noErr) { + dsp_vram_available = SDL_TRUE; + DisposeGWorld (offscreen); + } + + err = NewGWorld (&offscreen, vformat->BitsPerPixel, &bounds, NULL, SDL_Display, useLocalHdwrMem | noNewDevice); + if (err == noErr) { + DisposeGWorld (offscreen); + dsp_agp_available = SDL_TRUE; + } + } +} + +static int DSp_GetMainDevice (_THIS, GDHandle *device) +{ + +#if TARGET_API_MAC_OSX + /* DSpUserSelectContext not available on OS X */ + *device = GetMainDevice(); + return 0; +#else + + DSpContextAttributes attrib; + DSpContextReference context; + DisplayIDType display_id; + GDHandle main_device; + GDHandle device_list; + + device_list = GetDeviceList (); + main_device = GetMainDevice (); + + /* Quick check to avoid slower method when only one display exists */ + if ( (**device_list).gdNextGD == NULL ) { + *device = main_device; + return 0; + } + + memset (&attrib, 0, sizeof (DSpContextAttributes)); + + /* These attributes are hopefully supported on all devices...*/ + attrib.displayWidth = 640; + attrib.displayHeight = 480; + attrib.displayBestDepth = 8; + attrib.backBufferBestDepth = 8; + attrib.displayDepthMask = kDSpDepthMask_All; + attrib.backBufferDepthMask = kDSpDepthMask_All; + attrib.colorNeeds = kDSpColorNeeds_Require; + attrib.pageCount = 1; + + if (noErr != DMGetDisplayIDByGDevice (main_device, &display_id, SDL_FALSE)) { + SDL_SetError ("Display Manager couldn't associate GDevice with a Display ID"); + return (-1); + } + + /* Put up dialog on main display to select which display to use */ + if (noErr != DSpUserSelectContext (&attrib, display_id, NULL, &context)) { + SDL_SetError ("DrawSprocket couldn't create a context"); + return (-1); + } + + if (noErr != DSpContext_GetDisplayID (context, &display_id)) { + SDL_SetError ("DrawSprocket couldn't get display ID"); + return (-1); + } + + if (noErr != DMGetGDeviceByDisplayID (display_id, &main_device, SDL_FALSE)) { + SDL_SetError ("Display Manager couldn't associate Display ID with GDevice"); + return (-1); + } + + *device = main_device; + return (0); +#endif +} + +static int DSp_VideoInit(_THIS, SDL_PixelFormat *vformat) +{ + + NumVersion dsp_version = DSpGetVersion (); + + if ( (dsp_version.majorRev == 1 && dsp_version.minorAndBugRev < 0x73) || + (dsp_version.majorRev < 1) ) { + + /* StandardAlert (kAlertStopAlert, "\pError!", + "\pI need DrawSprocket 1.7.3 or later!\n" + "You can find a newer version at http://www.apple.com/swupdates.", + NULL, NULL); + */ + SDL_SetError ("DrawSprocket version is too old. Need 1.7.3 or later."); + return (-1); + } + + if ( DSpStartup () != noErr ) { + SDL_SetError ("DrawSprocket couldn't startup"); + return(-1); + } + + /* Start DSpintosh events */ + Mac_InitEvents(this); + + /* Get a handle to the main monitor, or choose one on multiple monitor setups */ + if ( DSp_GetMainDevice(this, &SDL_Display) < 0) + return (-1); + + /* Determine pixel format */ + vformat->BitsPerPixel = GetPixDepth ( (**SDL_Display).gdPMap ); + dsp_old_depth = vformat->BitsPerPixel; + + switch (vformat->BitsPerPixel) { + case 16: + vformat->Rmask = 0x00007c00; + vformat->Gmask = 0x000003e0; + vformat->Bmask = 0x0000001f; + break; + default: + break; + } + + if ( DSp_CreatePalette (this) < 0 ) { + + SDL_SetError ("Could not create palette"); + return (-1); + } + + /* Get a list of available fullscreen modes */ + SDL_modelist = DSp_BuildModeList (SDL_Display); + if (SDL_modelist == NULL) { + SDL_SetError ("DrawSprocket could not build a mode list"); + return (-1); + } + + /* Check for VRAM and AGP GWorlds for HW Blitting */ + DSp_IsHWAvailable (this, vformat); + + this->info.wm_available = 0; + + if (dsp_vram_available || dsp_agp_available) { + + this->info.hw_available = SDL_TRUE; + + this->CheckHWBlit = DSp_CheckHWBlit; + this->info.blit_hw = SDL_TRUE; + + this->FillHWRect = DSp_FillHWRect; + this->info.blit_fill = SDL_TRUE; + + #ifdef DSP_TRY_CC_AND_AA + this->SetHWColorKey = DSp_SetHWColorKey; + this->info.blit_hw_CC = SDL_TRUE; + + this->SetHWAlpha = DSp_SetHWAlpha; + this->info.blit_hw_A = SDL_TRUE; + #endif + + } + + return(0); +} + +static SDL_Rect **DSp_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) +{ + static SDL_Rect *dsp_modes[16]; + int i = 0, j = 0; + + if ( format->BitsPerPixel == 0 ) + return ( (SDL_Rect**) NULL ); + + while (SDL_modelist[i] != NULL) { + + if (SDL_modelist[i]->x & format->BitsPerPixel) { + dsp_modes[j] = SDL_modelist[i]; + j++; + } + i++; + } + + dsp_modes[j] = NULL; + + return dsp_modes; +} + +/* Various screen update functions available */ +static void DSp_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); + +static volatile unsigned int retrace_count = 0; /* -dw- need volatile because it updates asychronously */ + +#if ! TARGET_API_MAC_OSX +Boolean DSp_VBLProc ( DSpContextReference context, void *ref_con ) +{ + retrace_count++; + + return 1; /* Darrell, is this right? */ +} +#endif + +static void DSp_SetHWError (OSStatus err, int is_agp) +{ + char message[1024]; + const char *fmt, *mem; + + if ( is_agp ) { + mem = "AGP Memory"; + } else { + mem = "VRAM"; + } + switch(err) { + case memFullErr: + fmt = "Hardware surface possible but not enough %s available"; + break; + case cDepthErr: + fmt = "Hardware surface possible but invalid color depth"; + break; + default: + fmt = "Hardware surface could not be allocated in %s - unknown error"; + break; + } + sprintf(message, fmt, mem); + SDL_SetError(message); +} + +/* put up a dialog to verify display change */ +static int DSp_ConfirmSwitch () { + + /* resource id's for dialog */ + const int rDialog = 1002; + const int bCancel = 1; + const int bOK = 2; + + DialogPtr dialog; + OSStatus err; + SInt32 response; + DialogItemIndex item = 0; + GrafPtr savePort; + + GetPort (&savePort); + + dialog = GetNewDialog (rDialog, NULL, (WindowPtr) -1); + if (dialog == NULL) + return (0); + + SetPort (dialog); + + SetDialogDefaultItem (dialog, bCancel); + SetDialogCancelItem (dialog, bCancel); + + SetEventMask (everyEvent); + FlushEvents (everyEvent, 0); + + /* On MacOS 8.5 or later, we can make the dialog go away after 15 seconds */ + /* This is good since it's possible user can't even see the dialog! */ + /* Requires linking to DialogsLib */ + err = Gestalt(gestaltSystemVersion,&response); + if (err == noErr && response >= 0x00000850) { + SetDialogTimeout(dialog, bCancel, 15); + } + + do { + + ModalDialog ( NULL, &item ); + + } while ( item != bCancel && item != bOK && err != noErr); + + DisposeWindow (dialog); + SetPort (savePort); + + SetEventMask(everyEvent - autoKeyMask); + FlushEvents(everyEvent, 0); + + return (item - 1); +} + +static void DSp_UnsetVideoMode(_THIS, SDL_Surface *current) +{ + + + if ( current->flags & SDL_OPENGL ) { + Mac_GL_Quit (this); + } + + if (dsp_context != NULL) { + + GWorldPtr front; + DSpContext_GetFrontBuffer (dsp_context, &front); + + if (front != dsp_back_buffer) + DisposeGWorld (dsp_back_buffer); + + if (current->hwdata) + free (current->hwdata); + + DSpContext_SetState (dsp_context, kDSpContextState_Inactive ); + DSpContext_Release (dsp_context); + + dsp_context = NULL; + } + + if (SDL_Window != NULL) { + DisposeWindow (SDL_Window); + SDL_Window = NULL; + } + + current->pixels = NULL; + current->flags = 0; +} + +static SDL_Surface *DSp_SetVideoMode(_THIS, + SDL_Surface *current, int width, int height, int bpp, Uint32 flags) +{ + + DisplayIDType display_id; + DSpContextAttributes attrib; + Fixed freq; + OSStatus err; + UInt32 rmask = 0, gmask = 0, bmask = 0; + + int page_count; + int double_buf; + int hw_surface; + int use_dsp_back_buffer; + + DSp_UnsetVideoMode (this, current); + + if (bpp != dsp_old_depth) + DSp_DestroyPalette (this); + + double_buf = (flags & SDL_DOUBLEBUF) != 0; + hw_surface = (flags & SDL_HWSURFACE) != 0; + use_dsp_back_buffer = !dsp_vram_available || !hw_surface ; + + current->flags |= SDL_FULLSCREEN; + +rebuild: + + if ( double_buf && use_dsp_back_buffer ) { + page_count = 2; + } else { + page_count = 1; + } + + memset (&attrib, 0, sizeof (DSpContextAttributes)); + attrib.displayWidth = width; + attrib.displayHeight = height; + attrib.displayBestDepth = bpp; + attrib.backBufferBestDepth = bpp; + attrib.displayDepthMask = kDSpDepthMask_All; + attrib.backBufferDepthMask = kDSpDepthMask_All; + attrib.colorNeeds = kDSpColorNeeds_Require; + attrib.colorTable = 0; + attrib.pageCount = page_count; + #if TARGET_API_MAC_OSX + + if ( DSpFindBestContext (&attrib, &dsp_context) != noErr ) { + SDL_SetError ("DrawSprocket couldn't find a context"); + return NULL; + } + + #else + if ( noErr != DMGetDisplayIDByGDevice (SDL_Display, &display_id, SDL_FALSE) ) { + SDL_SetError ("Display Manager couldn't associate GDevice with display_id"); + return NULL; + } + if ( DSpFindBestContextOnDisplayID (&attrib, &dsp_context, display_id) != noErr ) { + SDL_SetError ("DrawSprocket couldn't find a suitable context on given display"); + return NULL; + } + + #endif + if ( DSpContext_Reserve (dsp_context, &attrib) != noErr ) { + SDL_SetError ("DrawSprocket couldn't get the needed resources to build the display"); + return NULL; + } + + if ( (err = DSpContext_SetState (dsp_context, kDSpContextState_Active)) != noErr ) { + + if (err == kDSpConfirmSwitchWarning) { + + if ( ! DSp_ConfirmSwitch () ) { + + DSpContext_Release (dsp_context); + dsp_context = NULL; + SDL_SetError ("User cancelled display switch"); + return NULL; + } + else + /* Have to reactivate context. Why? */ + DSpContext_SetState (dsp_context, kDSpContextState_Active); + + } + else { + SDL_SetError ("DrawSprocket couldn't activate the context"); + return NULL; + } + } + + + if (bpp != dsp_old_depth) { + + DSp_CreatePalette (this); + + /* update format if display depth changed */ + if (bpp == 16) { + + rmask = 0x00007c00; + gmask = 0x000003e0; + bmask = 0x0000001f; + } + if ( ! SDL_ReallocFormat (current, bpp, rmask, gmask, bmask, 0 ) ) { + + SDL_SetError ("Could not reallocate video format."); + return(NULL); + } + } + + if (!double_buf) { + + /* single-buffer context */ + DSpContext_GetFrontBuffer (dsp_context, &dsp_back_buffer); + + current->hwdata = (private_hwdata*) malloc (sizeof (private_hwdata)); + if (current ->hwdata == NULL) { + SDL_OutOfMemory (); + return NULL; + } + current->hwdata->offscreen = dsp_back_buffer; + current->flags |= SDL_HWSURFACE; + this->UpdateRects = DSp_DirectUpdate; + } + else if ( use_dsp_back_buffer ) { + + DSpContext_GetBackBuffer (dsp_context, kDSpBufferKind_Normal, &dsp_back_buffer); + + current->flags |= SDL_DOUBLEBUF | SDL_SWSURFACE; /* only front buffer is in VRAM */ + this->UpdateRects = DSp_DSpUpdate; + } + else if ( DSp_NewHWSurface(this, &dsp_back_buffer, bpp, width-1, height-1) == 0 ) { + + current->hwdata = (private_hwdata*) malloc (sizeof (private_hwdata)); + if (current ->hwdata == NULL) { + SDL_OutOfMemory (); + return NULL; + } + + memset (current->hwdata, 0, sizeof (private_hwdata)); + current->hwdata->offscreen = dsp_back_buffer; + current->flags |= SDL_DOUBLEBUF | SDL_HWSURFACE; + this->UpdateRects = DSp_DirectUpdate; /* hardware doesn't do update rects, must be page-flipped */ + } + else { + + DSpContext_Release (dsp_context); + use_dsp_back_buffer = SDL_TRUE; + goto rebuild; + } + + current->pitch = GetPortPixRowBytes(dsp_back_buffer) & 0x3FFF; + current->pixels = GetPixBaseAddr(GetPortPixMap(dsp_back_buffer)); + + current->w = width; + current->h = height; + + #if ! TARGET_API_MAC_OSX + + if (use_dsp_back_buffer) { + + DSpContext_GetMonitorFrequency (dsp_context, &freq); + DSpContext_SetMaxFrameRate (dsp_context, freq >> 16); + } + + + if ( (current->flags & SDL_HWSURFACE) || (current->flags & SDL_OPENGL) ) + DSpContext_SetVBLProc (dsp_context, DSp_VBLProc, NULL); + #endif + + if (bpp == 8) + current->flags |= SDL_HWPALETTE; + + if (flags & SDL_OPENGL) { + + Rect rect; + RGBColor rgb = { 0.0, 0.0, 0.0 }; + GrafPtr save_port; + + SetRect (&rect, 0, 0, width, height); + SDL_Window = NewCWindow(nil, &( (**SDL_Display).gdRect), "\p", SDL_TRUE, plainDBox, (WindowPtr)-1, SDL_FALSE, 0); + + if (SDL_Window == NULL) { + + SDL_SetError ("DSp_SetVideoMode : OpenGL window could not be created."); + return NULL; + } + + /* Set window color to black to avoid white flash*/ + GetPort (&save_port); + SetPort (SDL_Window); + RGBForeColor (&rgb); + PaintRect (&rect); + SetPort (save_port); + + SetPortWindowPort (SDL_Window); + SelectWindow (SDL_Window); + + if ( Mac_GL_Init (this) < 0 ) { + + SDL_SetError ("DSp_SetVideoMode : could not create OpenGL context."); + return NULL; + } + + current->flags |= SDL_OPENGL; + } + + return current; +} + +#ifdef DSP_TRY_CC_AND_AA + +static int DSp_MakeHWMask (_THIS, SDL_Surface *surface) +{ + GDHandle save_device; + CGrafPtr save_port; + GWorldPtr temp; + RGBColor black = { 0, 0, 0 }; + RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF }; + Rect rect; + + Uint32 depth = GetPixDepth ( GetGDevPixMap (SDL_Display) ); + + SetRect (&rect, 0, 0, surface->w, surface->h); + + if ( noErr != NewGWorld (&(surface->hwdata->mask), depth, &rect, 0, SDL_Display, 0 ) < 0 ) { + + SDL_OutOfMemory (); + return (-1); + } + + if ( noErr != NewGWorld (&temp, depth, &rect, 0 , SDL_Display, 0 ) ) { + + SDL_OutOfMemory (); + return (-1); + } + + + GetGWorld (&save_port, &save_device); + SetGWorld (surface->hwdata->mask, SDL_Display); + + RGBForeColor (&white); + PaintRect (&rect); + + RGBBackColor (&(surface->hwdata->trans)); + + CopyBits ( GetPortBitMapForCopyBits(surface->hwdata->offscreen), + GetPortBitMapForCopyBits(surface->hwdata->mask), + &rect, &rect, transparent, NULL ); + + SetGWorld (surface->hwdata->mask, SDL_Display); + SetGWorld (save_port, save_device); + return (0); +} + +static int DSp_SetHWAlpha(_THIS, SDL_Surface *surface, UInt8 alpha) +{ + surface->hwdata->alpha.red = (alpha / 255.0) * 65535; + surface->hwdata->alpha.blue = (alpha / 255.0) * 65535; + surface->hwdata->alpha.green = (alpha / 255.0) * 65535; + + surface->flags |= SDL_SRCALPHA; + + if (surface->flags & SDL_SRCCOLORKEY) { + return(DSp_MakeHWMask (this, surface)); + } + return(0); +} + +static int DSp_SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key) +{ + CGrafPtr save_port; + GDHandle save_device; + + GetGWorld (&save_port, &save_device); + SetGWorld (surface->hwdata->offscreen, NULL); + + Index2Color (key, &(surface->hwdata->trans)); + surface->flags |= SDL_SRCCOLORKEY; + + SetGWorld (save_port, save_device); + + if ( surface->flags & SDL_SRCALPHA ) { + return(DSp_MakeHWMask (this, surface)); + } + return(0); +} + +#endif /* DSP_TRY_CC_AND_AA */ + +static int DSp_NewHWSurface(_THIS, CGrafPtr *port, int depth, int width, int height) { + + OSStatus err; + Rect bounds; + + SetRect (&bounds, 0, 0, width, height); + + if (dsp_vram_available) { + /* try VRAM */ + err = NewGWorld (port, depth, &bounds, 0 , SDL_Display, useDistantHdwrMem | noNewDevice ); + if (err != noErr) + DSp_SetHWError (err, SDL_FALSE); + else + return (0); + } + + if (dsp_agp_available) { + /* try AGP */ + err = NewGWorld (port, depth, &bounds, 0 , SDL_Display, useLocalHdwrMem | noNewDevice ); + + if (err != noErr) + DSp_SetHWError (err, SDL_TRUE); + else + return (0); + } + + return (-1); +} + +static int DSp_AllocHWSurface(_THIS, SDL_Surface *surface) +{ + GWorldPtr temp; + + if ( DSp_NewHWSurface (this, &temp, surface->format->BitsPerPixel, surface->w, surface->h) < 0 ) + return (-1); + + surface->hwdata = (private_hwdata*) malloc (sizeof (private_hwdata)); + if (surface->hwdata == NULL) { + SDL_OutOfMemory (); + return -1; + } + + memset (surface->hwdata, 0, sizeof(private_hwdata)); + surface->hwdata->offscreen = temp; + surface->pitch = GetPixRowBytes (GetPortPixMap (temp)) & 0x3FFF; + surface->pixels = GetPixBaseAddr (GetPortPixMap (temp)); + surface->flags |= SDL_HWSURFACE; +#ifdef DSP_TRY_CC_AND_AA + surface->flags |= SDL_HWACCEL; +#endif + return 0; +} + +static void DSp_FreeHWSurface(_THIS, SDL_Surface *surface) +{ + if (surface->hwdata->offscreen != NULL) + DisposeGWorld (surface->hwdata->offscreen); + free (surface->hwdata); + + surface->pixels = NULL; +} + +static int DSp_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dest) +{ + int accelerated; + + /* Set initial acceleration on */ + src->flags |= SDL_HWACCEL; + + /* Set the surface attributes */ + if ( (src->flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { + if ( ! this->info.blit_hw_A ) { + src->flags &= ~SDL_HWACCEL; + } + } + if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { + if ( ! this->info.blit_hw_CC ) { + src->flags &= ~SDL_HWACCEL; + } + } + + /* Check to see if final surface blit is accelerated */ + accelerated = !!(src->flags & SDL_HWACCEL); + if ( accelerated ) { + src->map->hw_blit = DSp_HWAccelBlit; + } + return(accelerated); +} + +static int DSp_HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + CGrafPtr save_port; + GDHandle save_device; + Rect src_rect, dst_rect; + RGBColor black = { 0, 0, 0 }; + RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF }; + +#ifdef DSP_TRY_CC_AND_AA + UInt32 mode; +#endif + + SetRect (&src_rect, srcrect->x, srcrect->y, srcrect->x + srcrect->w, srcrect->y + srcrect->h); + SetRect (&dst_rect, dstrect->x, dstrect->y, dstrect->x + dstrect->w, dstrect->y + dstrect->h); + + GetGWorld (&save_port, &save_device); + SetGWorld (dst->hwdata->offscreen, NULL); + + RGBForeColor (&black); + RGBBackColor (&white); + +#ifdef DSP_TRY_CC_AND_AA + + if ( (src->flags & SDL_SRCCOLORKEY) && + (src->flags & SDL_SRCALPHA) ) { + + OpColor (&(src->hwdata->alpha)); + + CopyDeepMask ( GetPortBitMapForCopyBits(src->hwdata->offscreen), + GetPortBitMapForCopyBits(src->hwdata->mask), + GetPortBitMapForCopyBits(dst->hwdata->offscreen), + &src_rect, &src_rect, &dst_rect, + blend, + NULL ); + } + else { + + if ( src->flags & SDL_SRCCOLORKEY) { + RGBBackColor (&(src->hwdata->trans) ); + mode = transparent; + } + else if (src->flags & SDL_SRCALPHA) { + + OpColor (&(src->hwdata->alpha)); + mode = blend; + } + else { + + mode = srcCopy; + } + + CopyBits ( GetPortBitMapForCopyBits(src->hwdata->offscreen), + GetPortBitMapForCopyBits(dst->hwdata->offscreen), + &src_rect, &dst_rect, mode, NULL ); + } +#else + + CopyBits ( &(((GrafPtr)(src->hwdata->offscreen))->portBits), + &(((GrafPtr)(dst->hwdata->offscreen))->portBits), + &src_rect, &dst_rect, srcCopy, NULL ); + +#endif /* DSP_TRY_CC_AND_AA */ + + SetGWorld (save_port, save_device); + + return(0); +} + +static int DSp_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) +{ + CGrafPtr save_port; + GDHandle save_device; + Rect fill_rect; + RGBColor rgb; + + SetRect (&fill_rect, rect->x, rect->y, rect->x + rect->w, rect->y + rect->h); + + GetGWorld (&save_port, &save_device); + SetGWorld (dst->hwdata->offscreen, NULL); + + Index2Color (color, &rgb); + + RGBForeColor (&rgb); + PaintRect (&fill_rect); + + SetGWorld (save_port, save_device); + + return(0); +} + +static int DSp_FlipHWSurface(_THIS, SDL_Surface *surface) +{ + if ( (surface->flags & SDL_HWSURFACE) ) { + CGrafPtr dsp_front_buffer, save_port; + Rect rect; + + unsigned int old_count; + + /* pseudo page flipping for VRAM back buffer*/ + DSpContext_GetFrontBuffer (dsp_context, &dsp_front_buffer); + SetRect (&rect, 0, 0, surface->w-1, surface->h-1); + + GetPort ((GrafPtr *)&save_port); + SetPort ((GrafPtr)dsp_front_buffer); + + /* wait for retrace */ + /* I have tried doing the swap in interrupt routine (VBL Proc) to do */ + /* it asynchronously, but apparently CopyBits isn't interrupt safe */ + + #if ! TARGET_API_MAC_OSX + #ifndef DSP_NO_SYNC_VBL + old_count = retrace_count; + while (old_count == retrace_count) + ; + #endif + #endif + + CopyBits ( GetPortBitMapForCopyBits(dsp_back_buffer), + GetPortBitMapForCopyBits(dsp_front_buffer), + &rect, &rect, srcCopy, NULL ); + + SetPort ((GrafPtr)save_port); + + } else { + /* not really page flipping at all: DSp just blits the dirty rectangles from DSp_UpdateRects */ + Boolean busy_flag; + DSpContext_SwapBuffers (dsp_context, NULL, &busy_flag); /* this waits for VBL */ + DSpContext_GetBackBuffer (dsp_context, kDSpBufferKind_Normal, &dsp_back_buffer); + surface->pixels = GetPixBaseAddr( GetPortPixMap(dsp_back_buffer) ); + } + return(0); +} + +static int DSp_LockHWSurface(_THIS, SDL_Surface *surface) +{ + if ( LockPixels (GetGWorldPixMap (surface->hwdata->offscreen)) ) + return 0; + else + return -1; +} + +static void DSp_UnlockHWSurface(_THIS, SDL_Surface *surface) +{ + UnlockPixels (GetGWorldPixMap (surface->hwdata->offscreen)); +} + +static void DSp_DirectUpdate(_THIS, int numrects, SDL_Rect *sdl_rects) +{ + return; +} + +static void DSp_DSpUpdate(_THIS, int numrects, SDL_Rect *sdl_rects) +{ +#if ! TARGET_API_MAC_OSX /* Unsupported DSp in here */ + int i; + Rect rect; + + for (i = 0; i < numrects; i++) { + + rect.top = sdl_rects[i].y; + rect.left = sdl_rects[i].x; + rect.bottom = sdl_rects[i].h + sdl_rects[i].y; + rect.right = sdl_rects[i].w + sdl_rects[i].x; + + DSpContext_InvalBackBufferRect (dsp_context, &rect); + } +#endif +} + +static int DSp_CreatePalette(_THIS) { + + + /* Create our palette */ + SDL_CTab = (CTabHandle)NewHandle(sizeof(ColorSpec)*256 + 8); + if ( SDL_CTab == nil ) { + SDL_OutOfMemory(); + return(-1); + } + (**SDL_CTab).ctSeed = GetCTSeed(); + (**SDL_CTab).ctFlags = 0; + (**SDL_CTab).ctSize = 255; + CTabChanged(SDL_CTab); + SDL_CPal = NewPalette(256, SDL_CTab, pmExplicit+pmTolerant, 0); + + return 0; +} + +static int DSp_DestroyPalette(_THIS) { + + /* Free palette and restore original one */ + if ( SDL_CTab != nil ) { + DisposeHandle((Handle)SDL_CTab); + SDL_CTab = nil; + } + if ( SDL_CPal != nil ) { + DisposePalette(SDL_CPal); + SDL_CPal = nil; + } + RestoreDeviceClut(SDL_Display); + + return (0); +} + +static int DSp_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) +{ + CTabHandle cTab; + + int i; + + cTab = SDL_CTab; + + /* Verify the range of colors */ + if ( (firstcolor+ncolors) > ((**cTab).ctSize+1) ) { + return(0); + } + + /* Set the screen palette and update the display */ + for(i = 0; i < ncolors; i++) { + int j = firstcolor + i; + (**cTab).ctTable[j].value = j; + (**cTab).ctTable[j].rgb.red = colors[i].r << 8 | colors[i].r; + (**cTab).ctTable[j].rgb.green = colors[i].g << 8 | colors[i].g; + (**cTab).ctTable[j].rgb.blue = colors[i].b << 8 | colors[i].b; + } + + SetGDevice(SDL_Display); + SetEntries(0, (**cTab).ctSize, (ColorSpec *)&(**cTab).ctTable); + + return(1); +} + +void DSp_VideoQuit(_THIS) +{ + int i; + + /* Free current video mode */ + DSp_UnsetVideoMode(this, this->screen); + + /* Free Palette and restore original */ + DSp_DestroyPalette (this); + + /* Free list of video modes */ + if ( SDL_modelist != NULL ) { + for ( i=0; SDL_modelist[i]; i++ ) { + free(SDL_modelist[i]); + } + free(SDL_modelist); + SDL_modelist = NULL; + } + + /* Unload DrawSprocket */ + DSpShutdown (); +} + +#ifdef HAVE_OPENGL + +/* swap buffers with v-sync */ +static void DSp_GL_SwapBuffers (_THIS) { + + #ifndef DSP_NO_SYNC_OPENGL + + unsigned int old_count; + + old_count = retrace_count; + while (old_count == retrace_count) + ; + #endif + + aglSwapBuffers (glContext); +} + +#endif