/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2004 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 */ #ifdef SAVE_RCSID static char rcsid = "@(#) $Id$"; #endif #include #include #if TARGET_API_MAC_CARBON #include /* The fullscreen code requires the QuickTime framework, and the window is still at the back on MacOS X, which is where this code is needed. */ #if USE_QUICKTIME #include #endif #else #include #include #include #include #include #endif #include "SDL_video.h" #include "SDL_error.h" #include "SDL_syswm.h" #include "SDL_sysvideo.h" #include "SDL_romvideo.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 ROM_VideoInit(_THIS, SDL_PixelFormat *vformat); static SDL_Rect **ROM_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); static SDL_Surface *ROM_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); static int ROM_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); static void ROM_VideoQuit(_THIS); /* Hardware surface functions */ static int ROM_AllocHWSurface(_THIS, SDL_Surface *surface); static int ROM_LockHWSurface(_THIS, SDL_Surface *surface); static void ROM_UnlockHWSurface(_THIS, SDL_Surface *surface); static void ROM_FreeHWSurface(_THIS, SDL_Surface *surface); #if !TARGET_API_MAC_CARBON /* This seems not to be available? -sts Aug 2000 */ /* Saved state for the menu bar */ static RgnHandle gSaveGrayRgn = nil; static short gSaveMenuBar = 0; static Boolean gSaveCSVis = true; #if powerc /* Mixed mode glue to activate the 68K emulator and twiddle a register */ #define ONEWORDSTUB(p1) \ { 0x41FA, 0x0010, 0x209F, (p1), 0x41FA, \ 0x0008, 0x2F10, 0x4E75, 0x0000, 0x0000, 0x0000 } #define TWOWORDSTUB(p1,p2) \ { 0x41FA, 0x0012, 0x209F, (p1), (p2), 0x41FA, \ 0x0008, 0x2F10, 0x4E75, 0x0000, 0x0000, 0x0000 } #define THREEWORDSTUB(p1,p2,p3) \ { 0x41FA, 0x0014, 0x209F, (p1), (p2), (p3), 0x41FA, \ 0x0008, 0x2F10, 0x4E75, 0x0000, 0x0000, 0x0000 } /* ControlStrip inline glue for PowerPC */ static pascal Boolean SBIsControlStripVisible(void) { static short procData[] = TWOWORDSTUB(0x7000, 0xAAF2); ProcInfoType procInfo = kD0DispatchedPascalStackBased | RESULT_SIZE(SIZE_CODE(sizeof(Boolean))) | DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kFourByteCode); return((Boolean) CallUniversalProc((UniversalProcPtr) procData, procInfo, 0x00)); } static pascal void SBShowHideControlStrip(Boolean showIt) { static short procData[] = THREEWORDSTUB(0x303C, 0x0101, 0xAAF2); ProcInfoType procInfo = kD0DispatchedPascalStackBased | DISPATCHED_STACK_ROUTINE_SELECTOR_SIZE(kFourByteCode) | DISPATCHED_STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(Boolean))); CallUniversalProc((UniversalProcPtr) procData, procInfo, 0x01, showIt); } #endif /* powerc */ #endif /* !TARGET_API_MAC_CARBON */ /* Macintosh toolbox driver bootstrap functions */ static int ROM_Available(void) { return(1); } static void ROM_DeleteDevice(SDL_VideoDevice *device) { free(device->hidden); free(device); } static SDL_VideoDevice *ROM_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 == NULL) || (device->hidden == NULL) ) { SDL_OutOfMemory(); if ( device ) { free(device); } return(0); } memset(device->hidden, 0, (sizeof *device->hidden)); /* Set the function pointers */ device->VideoInit = ROM_VideoInit; device->ListModes = ROM_ListModes; device->SetVideoMode = ROM_SetVideoMode; device->SetColors = ROM_SetColors; device->UpdateRects = NULL; device->VideoQuit = ROM_VideoQuit; device->AllocHWSurface = ROM_AllocHWSurface; device->CheckHWBlit = NULL; device->FillHWRect = NULL; device->SetHWColorKey = NULL; device->SetHWAlpha = NULL; device->LockHWSurface = ROM_LockHWSurface; device->UnlockHWSurface = ROM_UnlockHWSurface; device->FlipHWSurface = NULL; device->FreeHWSurface = ROM_FreeHWSurface; #ifdef HAVE_OPENGL device->GL_MakeCurrent = Mac_GL_MakeCurrent; device->GL_SwapBuffers = Mac_GL_SwapBuffers; #ifdef __MWERKS__ device->GL_LoadLibrary = Mac_GL_LoadLibrary; device->GL_GetProcAddress = Mac_GL_GetProcAddress; #else device->GL_LoadLibrary = NULL; device->GL_GetProcAddress = NULL; #endif // CodeWarrior #endif // Have OpenGL device->SetCaption = Mac_SetCaption; 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->free = ROM_DeleteDevice; return device; } VideoBootStrap TOOLBOX_bootstrap = { "toolbox", "MacOS ROM Toolbox", ROM_Available, ROM_CreateDevice }; static int ROM_VideoInit(_THIS, SDL_PixelFormat *vformat) { long info; /* Check out some things about the system */ Gestalt(gestaltQuickdrawVersion, &info); if ( info == gestaltOriginalQD ) { SDL_SetError("Color Quickdraw not available"); return(-1); } /* Start ROMintosh events */ Mac_InitEvents(this); /* Get a handle to the main monitor */ SDL_Display = GetMainDevice(); /* Determine pixel format */ vformat->BitsPerPixel = (**(**SDL_Display).gdPMap).pixelSize; switch (vformat->BitsPerPixel) { case 16: /* 5-5-5 RGB */ vformat->Rmask = 0x00007c00; vformat->Gmask = 0x000003e0; vformat->Bmask = 0x0000001f; break; default: break; } /* 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); /* Get a list of available fullscreen modes */ SDL_modelist = (SDL_Rect **)malloc((1+1)*sizeof(SDL_Rect *)); if ( SDL_modelist ) { SDL_modelist[0] = (SDL_Rect *)malloc(sizeof(SDL_Rect)); if ( SDL_modelist[0] ) { SDL_modelist[0]->x = 0; SDL_modelist[0]->y = 0; SDL_modelist[0]->w = (**SDL_Display).gdRect.right; SDL_modelist[0]->h = (**SDL_Display).gdRect.bottom; } SDL_modelist[1] = NULL; } /* Fill in some window manager capabilities */ this->info.wm_available = 1; /* We're done! */ return(0); } static SDL_Rect **ROM_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) { if ( this->screen->format->BitsPerPixel == format->BitsPerPixel ) { if ( (flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) { return(SDL_modelist); } else { return((SDL_Rect **)-1); } } else { return((SDL_Rect **)0); } } static void ROM_HideMenuBar(_THIS) { #if !TARGET_API_MAC_CARBON /* This seems not to be available? -sts Aug 2000 */ RgnHandle drawRgn = nil; RgnHandle tempRgn = nil; RgnHandle grayRgn = nil; WindowPtr window = nil; GDHandle gd = nil; GrafPtr savePort; long response; short height; EventRecord theEvent; height = GetMBarHeight(); if ( height > 0 ) { tempRgn = NewRgn(); drawRgn = NewRgn(); gSaveGrayRgn = NewRgn(); if ( ! tempRgn || ! drawRgn || ! gSaveGrayRgn ) { goto CLEANUP; } grayRgn = GetGrayRgn(); /* No need to check for this */ GetPort(&savePort); /* Hide the control strip if it's present, and record its previous position into the dirty region for redrawing. This isn't necessary, but may help catch stray bits. */ CopyRgn(grayRgn, tempRgn); if (!Gestalt(gestaltControlStripAttr, &response) && (response & (1L << gestaltControlStripExists))) { gSaveCSVis = SBIsControlStripVisible(); if (gSaveCSVis) SBShowHideControlStrip(false); } DiffRgn(grayRgn, tempRgn, drawRgn); /* Save the gray region once the control strip is hidden*/ CopyRgn(grayRgn, gSaveGrayRgn); /* Change the menu height in lowmem */ gSaveMenuBar = height; LMSetMBarHeight(0); /* Walk the monitor rectangles, and combine any pieces that aren't in GrayRgn: menubar, round corners, fake floaters. */ for(gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) { if (!TestDeviceAttribute(gd, screenDevice)) continue; if (!TestDeviceAttribute(gd, screenActive)) continue; RectRgn(tempRgn, &(*gd)->gdRect); /* Get the whole screen */ DiffRgn(tempRgn, grayRgn, tempRgn); /* Subtract out GrayRgn */ UnionRgn(tempRgn, drawRgn, drawRgn);/* Combine all the bits */ } /* Add the bits into the GrayRgn */ UnionRgn(drawRgn, grayRgn, grayRgn); /* Modify the vis regions of exposed windows */ window = (FrontWindow()) ? FrontWindow() : (WindowPtr) -1L; PaintBehind(window, drawRgn); CalcVisBehind(window, drawRgn); SetPort(savePort); /* Yield time so that floaters can catch up */ EventAvail(0, &theEvent); EventAvail(0, &theEvent); EventAvail(0, &theEvent); EventAvail(0, &theEvent); } CLEANUP: if (tempRgn) DisposeRgn(tempRgn); if (drawRgn) DisposeRgn(drawRgn); #endif /* !TARGET_API_MAC_CARBON */ } static void ROM_ShowMenuBar(_THIS) { #if !TARGET_API_MAC_CARBON /* This seems not to be available? -sts Aug 2000 */ RgnHandle drawRgn = nil; RgnHandle menuRgn = nil; RgnHandle tempRgn = nil; RgnHandle grayRgn = nil; WindowPtr window = nil; GrafPtr wMgrPort; GrafPtr savePort; Rect menuRect; long response; short height; EventRecord theEvent; RGBColor saveRGB; RGBColor blackRGB = { 0, 0, 0 }; height = GetMBarHeight(); if ((height <= 0) && (gSaveMenuBar > 0)) { drawRgn = NewRgn(); menuRgn = NewRgn(); tempRgn = NewRgn(); if ( ! tempRgn || ! drawRgn || ! gSaveGrayRgn ) { goto CLEANUP; } grayRgn = GetGrayRgn(); /* No need to check for this */ GetPort(&savePort); GetWMgrPort(&wMgrPort); /* Set the height properly */ LMSetMBarHeight(gSaveMenuBar); /* Restore the old GrayRgn: rounded corners, etc, but not the menubar -- subtract that out first! */ if (gSaveGrayRgn) { menuRect = (*GetMainDevice())->gdRect; menuRect.bottom = menuRect.top + gSaveMenuBar; RectRgn(menuRgn, &menuRect); DiffRgn(grayRgn, gSaveGrayRgn, drawRgn); /* What do we inval? */ DiffRgn(drawRgn, menuRgn, drawRgn); /* Clip out the menu */ /* Now redraw the corners and other bits black */ SetPort(wMgrPort); GetClip(tempRgn); SetClip(drawRgn); GetForeColor(&saveRGB); RGBForeColor(&blackRGB); PaintRgn(drawRgn); RGBForeColor(&saveRGB); SetClip(tempRgn); SetPort(savePort); UnionRgn(drawRgn, menuRgn, drawRgn); /* Put back the menu */ /* Now actually restore the GrayRgn */ CopyRgn(gSaveGrayRgn, grayRgn); DisposeRgn(gSaveGrayRgn); gSaveGrayRgn = nil; } /* Modify the vis regions of exposed windows and draw menubar */ window = (FrontWindow()) ? FrontWindow() : (WindowPtr) -1L; PaintBehind(window, drawRgn); CalcVisBehind(window, drawRgn); DrawMenuBar(); SetPort(savePort); gSaveMenuBar = 0; /* Now show the control strip if it's present */ if (!Gestalt(gestaltControlStripAttr, &response) && (response & (1L << gestaltControlStripExists))) { if (gSaveCSVis && !SBIsControlStripVisible()) SBShowHideControlStrip(true); gSaveCSVis = true; } /* Yield time so that floaters can catch up */ EventAvail(0, &theEvent); EventAvail(0, &theEvent); EventAvail(0, &theEvent); EventAvail(0, &theEvent); } CLEANUP: if (drawRgn) DisposeRgn(drawRgn); if (menuRgn) DisposeRgn(menuRgn); if (tempRgn) DisposeRgn(tempRgn); #endif /* !TARGET_API_MAC_CARBON */ } /* Various screen update functions available */ static void ROM_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); static void ROM_WindowUpdate(_THIS, int numrects, SDL_Rect *rects); static void ROM_UnsetVideoMode(_THIS, SDL_Surface *current) { /* Free the current window, if any */ if ( SDL_Window != nil ) { GWorldPtr memworld; /* Handle OpenGL support */ Mac_GL_Quit(this); memworld = (GWorldPtr)GetWRefCon(SDL_Window); if ( memworld != nil ) { UnlockPixels(GetGWorldPixMap(memworld)); DisposeGWorld(memworld); } if ( (current->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) { #if USE_QUICKTIME EndFullScreen(fullscreen_ctx, nil); SDL_Window = nil; #else ROM_ShowMenuBar(this); #endif } } current->pixels = NULL; current->flags &= ~(SDL_HWSURFACE|SDL_FULLSCREEN); } static SDL_Surface *ROM_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags) { Rect wrect, orect; #if TARGET_API_MAC_CARBON Rect tmprect; #endif /* Free any previous video mode */ ROM_UnsetVideoMode(this, current); /* Create the ROM window and SDL video surface */ current->flags = 0; /* Clear flags */ current->w = width; current->h = height; SetRect(&wrect, 0, 0, width, height); if ( SDL_Window ) { /* If we recreate the window, don't move it around */ #if TARGET_API_MAC_CARBON orect = *GetWindowPortBounds(SDL_Window, &tmprect); #else orect = SDL_Window->portRect; #endif OffsetRect(&wrect, orect.left, orect.top); } else { /* Center the window the first time we show it */ OffsetRect(&wrect, (SDL_modelist[0]->w-width)/2, (SDL_modelist[0]->h-height)/2); } #if MACOSX && !USE_QUICKTIME /* Hum.. fullscreen mode is broken */ flags &= ~SDL_FULLSCREEN; #endif if ( (flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) { /* Create the fullscreen window and use screen bits */ current->flags |= SDL_HWSURFACE|SDL_FULLSCREEN; if ( SDL_Window ) { DisposeWindow(SDL_Window); } #if USE_QUICKTIME BeginFullScreen(&fullscreen_ctx, nil, 0,0, &SDL_Window, nil, 0); #else SDL_Window = NewCWindow(nil, &wrect, "\p", true, plainDBox, (WindowPtr)-1, false, 0); ROM_HideMenuBar(this); #endif current->pitch = (**(**SDL_Display).gdPMap).rowBytes & 0x3FFF; current->pixels = (**(**SDL_Display).gdPMap).baseAddr; this->UpdateRects = ROM_DirectUpdate; } else { GWorldPtr memworld; PixMapHandle pixmap; int style; style = noGrowDocProc; if ( flags & SDL_NOFRAME ) { style = plainDBox; current->flags |= SDL_NOFRAME; } else if ( flags & SDL_RESIZABLE ) { style = zoomDocProc; current->flags |= SDL_RESIZABLE; } if ( SDL_Window && (style == current_style) ) { /* Resize existing window, if necessary */ if ( ((orect.right-orect.left) != width) || ((orect.bottom-orect.top) != height) ) { SizeWindow(SDL_Window, width, height, false); } } else { /* Recreate the window in the new style */ if ( SDL_Window ) { DisposeWindow(SDL_Window); } SDL_Window = NewCWindow(nil, &wrect, "\p", true, style, (WindowPtr)-1, true, 0); /* Set the window title, if any */ { char *title; SDL_WM_GetCaption(&title, NULL); if ( title ) { Mac_SetCaption(this, title, NULL); } } } current_style = style; SetPalette(SDL_Window, SDL_CPal, false); ActivatePalette(SDL_Window); if ( NewGWorld(&memworld, 0, #if TARGET_API_MAC_CARBON GetWindowPortBounds(SDL_Window, &tmprect), #else &SDL_Window->portRect, #endif SDL_CTab, nil, 0) != noErr ) { SDL_SetError("NewGWorld() failed"); return(NULL); } SetWRefCon(SDL_Window, (long)memworld); pixmap = GetGWorldPixMap(memworld); LockPixels(pixmap); current->pitch = (**pixmap).rowBytes & 0x3FFF; current->pixels = GetPixBaseAddr(pixmap); this->UpdateRects = ROM_WindowUpdate; } SetPortWindowPort(SDL_Window); SelectWindow(SDL_Window); /* Handle OpenGL support */ if ( flags & SDL_OPENGL ) { if ( Mac_GL_Init(this) == 0 ) { current->flags |= SDL_OPENGL; } else { current = NULL; } } if ( (flags & SDL_HWPALETTE) && (flags & SDL_FULLSCREEN) ) current->flags |= SDL_HWPALETTE; /* We're live! */ return(current); } /* We don't actually allow hardware surfaces other than the main one */ static int ROM_AllocHWSurface(_THIS, SDL_Surface *surface) { return(-1); } static void ROM_FreeHWSurface(_THIS, SDL_Surface *surface) { return; } static int ROM_LockHWSurface(_THIS, SDL_Surface *surface) { return(0); } static void ROM_UnlockHWSurface(_THIS, SDL_Surface *surface) { return; } static void ROM_DirectUpdate(_THIS, int numrects, SDL_Rect *rects) { /* The application is already updating the visible video memory */ return; } static void ROM_WindowUpdate(_THIS, int numrects, SDL_Rect *rects) { GWorldPtr memworld; GrafPtr saveport; CGrafPtr thePort; const BitMap *memBits; const BitMap *winBits; int i; Rect update; /* Copy from the offscreen GWorld to the window port */ GetPort(&saveport); SetPortWindowPort(SDL_Window); thePort = GetWindowPort(SDL_Window); memworld = (GWorldPtr)GetWRefCon(SDL_Window); #if TARGET_API_MAC_CARBON memBits = GetPortBitMapForCopyBits((CGrafPtr) memworld); #else memBits = &((GrafPtr)memworld)->portBits; #endif #if TARGET_API_MAC_CARBON winBits = GetPortBitMapForCopyBits(thePort); #else winBits = &SDL_Window->portBits; #endif for ( i=0; iscreen->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) { cTab = (**(**SDL_Display).gdPMap).pmTable; } else { 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; } // if ( (this->screen->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) { GDevice **odisplay; odisplay = GetGDevice(); SetGDevice(SDL_Display); SetEntries(0, (**cTab).ctSize, (ColorSpec *)&(**cTab).ctTable); SetGDevice(odisplay); } return(1); } void ROM_VideoQuit(_THIS) { int i; /* Free current video mode */ ROM_UnsetVideoMode(this, this->screen); if ( SDL_Window ) { DisposeWindow(SDL_Window); SDL_Window = nil; } /* 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(GetMainDevice()); /* 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; } }