/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" #if defined(__APPLE__) && defined(__MACH__) #include #if USE_QUICKTIME #include #endif #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) #include /* The fullscreen code requires the QuickTime framework, and the window is still at the back on Mac OS X, which is where this code is needed. */ #if USE_QUICKTIME #include #endif #else #include #include #include #include #include #include #endif #include "SDL_video.h" #include "SDL_syswm.h" #include "../SDL_sysvideo.h" #include "SDL_romvideo.h" #include "../maccommon/SDL_macgl_c.h" #include "../maccommon/SDL_macwm_c.h" #include "../maccommon/SDL_macmouse_c.h" #include "../maccommon/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) { SDL_free (device->hidden); SDL_free (device); } static SDL_VideoDevice * ROM_CreateDevice (int devindex) { SDL_VideoDevice *device; /* Initialize all variables that we clean on shutdown */ device = (SDL_VideoDevice *) SDL_malloc (sizeof (SDL_VideoDevice)); if (device) { SDL_memset (device, 0, (sizeof *device)); device->hidden = (struct SDL_PrivateVideoData *) SDL_malloc ((sizeof *device->hidden)); } if ((device == NULL) || (device->hidden == NULL)) { SDL_OutOfMemory (); if (device) { SDL_free (device); } return (0); } SDL_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; #if SDL_VIDEO_OPENGL device->GL_MakeCurrent = Mac_GL_MakeCurrent; device->GL_SwapBuffers = Mac_GL_SwapBuffers; device->GL_LoadLibrary = Mac_GL_LoadLibrary; device->GL_GetProcAddress = Mac_GL_GetProcAddress; #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 the current screen size */ this->info.current_w = (**SDL_Display).gdRect.right; this->info.current_h = (**SDL_Display).gdRect.bottom; /* 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 **) SDL_malloc ((1 + 1) * sizeof (SDL_Rect *)); if (SDL_modelist) { SDL_modelist[0] = (SDL_Rect *) SDL_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 defined(__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_INTERNALOPENGL) { if (Mac_GL_Init (this) == 0) { current->flags |= SDL_INTERNALOPENGL; } 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 && ACCESSOR_CALLS_ARE_FUNCTIONS memBits = GetPortBitMapForCopyBits ((CGrafPtr) memworld); #else memBits = &((GrafPtr) memworld)->portBits; #endif #if TARGET_API_MAC_CARBON && ACCESSOR_CALLS_ARE_FUNCTIONS winBits = GetPortBitMapForCopyBits (thePort); #else winBits = &SDL_Window->portBits; #endif for (i = 0; i < numrects; ++i) { update.left = rects[i].x; update.right = rects[i].x + rects[i].w; update.top = rects[i].y; update.bottom = rects[i].y + rects[i].h; CopyBits (memBits, winBits, &update, &update, srcCopy, nil); } #if TARGET_API_MAC_CARBON if (QDIsPortBuffered (thePort)) { QDFlushPortBuffer (thePort, NULL); } #endif SetPort (saveport); } static int ROM_SetColors (_THIS, int firstcolor, int ncolors, SDL_Color * colors) { CTabHandle cTab; int i; /* Get the colortable from the either the display or window */ if ((this->screen->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) { SDL_free (SDL_modelist[i]); } SDL_free (SDL_modelist); SDL_modelist = NULL; } } /* vi: set ts=4 sw=4 expandtab: */