From 6abcac459788d1a840e889af203468adf06974a9 Mon Sep 17 00:00:00 2001 From: stephena Date: Wed, 20 Jun 2007 16:33:23 +0000 Subject: [PATCH] Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/configure | 18 ++ stella/src/common/FrameBufferGL.cxx | 196 ++++---------- stella/src/common/FrameBufferGL.hxx | 41 +-- stella/src/common/FrameBufferSoft.cxx | 362 ++++++-------------------- stella/src/common/FrameBufferSoft.hxx | 39 +-- stella/src/common/VideoModeList.hxx | 114 ++++++++ stella/src/emucore/EventHandler.cxx | 15 +- stella/src/emucore/FrameBuffer.cxx | 307 ++++++++++++---------- stella/src/emucore/FrameBuffer.hxx | 211 +++++++-------- stella/src/emucore/OSystem.cxx | 87 +++++-- stella/src/emucore/OSystem.hxx | 45 +++- stella/src/emucore/Settings.cxx | 62 +++-- stella/src/emucore/Settings.hxx | 23 +- stella/src/gp2x/FrameBufferGP2X.cxx | 21 +- stella/src/gp2x/OSystemGP2X.cxx | 5 +- stella/src/gui/Launcher.cxx | 34 +-- stella/src/gui/Launcher.hxx | 8 +- stella/src/gui/OptionsDialog.cxx | 13 +- stella/src/gui/UIDialog.cxx | 83 ++++-- stella/src/gui/UIDialog.hxx | 15 +- stella/src/gui/VideoDialog.cxx | 266 ++++++++++--------- stella/src/gui/VideoDialog.hxx | 23 +- stella/src/gui/Widget.cxx | 24 +- stella/src/gui/Widget.hxx | 6 +- stella/src/psp/FSNodePSP.cxx | 285 -------------------- stella/src/psp/FrameBufferPSP.cxx | 119 --------- stella/src/psp/FrameBufferPSP.hxx | 73 ------ stella/src/psp/OSystemPSP.cxx | 172 ------------ stella/src/psp/OSystemPSP.hxx | 86 ------ stella/src/psp/SettingsPSP.cxx | 39 --- stella/src/psp/SettingsPSP.hxx | 46 ---- stella/src/psp/data/ICON0.PNG | Bin 6916 -> 0 bytes stella/src/psp/data/PIC1.PNG | Bin 157058 -> 0 bytes stella/src/psp/data/stellarc.psp | 41 --- stella/src/psp/module.mk | 12 - stella/src/psp/pspstdint.h | 175 ------------- stella/src/tools/check-sig.cxx | 72 +++++ stella/src/unix/OSystemUNIX.cxx | 28 +- stella/src/unix/OSystemUNIX.hxx | 12 +- 39 files changed, 1047 insertions(+), 2131 deletions(-) create mode 100644 stella/src/common/VideoModeList.hxx delete mode 100644 stella/src/psp/FSNodePSP.cxx delete mode 100644 stella/src/psp/FrameBufferPSP.cxx delete mode 100644 stella/src/psp/FrameBufferPSP.hxx delete mode 100644 stella/src/psp/OSystemPSP.cxx delete mode 100644 stella/src/psp/OSystemPSP.hxx delete mode 100644 stella/src/psp/SettingsPSP.cxx delete mode 100644 stella/src/psp/SettingsPSP.hxx delete mode 100644 stella/src/psp/data/ICON0.PNG delete mode 100644 stella/src/psp/data/PIC1.PNG delete mode 100644 stella/src/psp/data/stellarc.psp delete mode 100644 stella/src/psp/module.mk delete mode 100644 stella/src/psp/pspstdint.h create mode 100644 stella/src/tools/check-sig.cxx diff --git a/stella/configure b/stella/configure index fed35edd7..110694e9f 100755 --- a/stella/configure +++ b/stella/configure @@ -19,6 +19,7 @@ _zlib=auto # default option behaviour yes/no _build_gl=yes +_build_windowed=yes _build_sound=yes _build_debugger=yes _build_snapshot=yes @@ -255,6 +256,8 @@ Installation directories: Optional Features: --enable-gl enable/disable OpenGL rendering support [enabled] --disable-gl + --enable-windowed enable/disable windowed rendering modes [enabled] + --disable-windowed --enable-sound enable/disable sound support [enabled] --disable-sound --enable-debugger enable/disable all debugger options [enabled] @@ -298,6 +301,8 @@ for ac_option in $@; do case "$ac_option" in --enable-gl) _build_gl=yes ;; --disable-gl) _build_gl=no ;; + --enable-windowed) _build_windowed=yes ;; + --disable-windowed) _build_windowed=no ;; --enable-sound) _build_sound=yes ;; --disable-sound) _build_sound=no ;; --enable-debugger) _build_debugger=yes ;; @@ -538,6 +543,7 @@ if test -n "$_host"; then echo "Cross-compiling to $_host, forcing static build, and disabling OpenGL." _build_static=yes _build_gl=no + _build_windowed=no ;; *) echo "Cross-compiling to unknown target, please add your target to configure." @@ -689,6 +695,14 @@ else echo fi +if test "$_build_windowed" = "yes" ; then + echo_n " Windowed rendering modes enabled" + echo +else + echo_n " Windowed rendering modes disabled" + echo +fi + if test "$_build_sound" = "yes" ; then echo_n " Sound support enabled" echo @@ -852,6 +866,10 @@ if test "$_build_gl" = yes ; then DEFINES="$DEFINES -DDISPLAY_OPENGL" fi +if test "$_build_windowed" = yes ; then + DEFINES="$DEFINES -DWINDOWED_SUPPORT" +fi + if test "$_build_sound" = yes ; then DEFINES="$DEFINES -DSOUND_SUPPORT" fi diff --git a/stella/src/common/FrameBufferGL.cxx b/stella/src/common/FrameBufferGL.cxx index d8d52f505..553f6bf07 100644 --- a/stella/src/common/FrameBufferGL.cxx +++ b/stella/src/common/FrameBufferGL.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBufferGL.cxx,v 1.86 2007-01-15 00:07:51 stephena Exp $ +// $Id: FrameBufferGL.cxx,v 1.87 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifdef DISPLAY_OPENGL @@ -88,11 +88,8 @@ FrameBufferGL::FrameBufferGL(OSystem* osystem) : FrameBuffer(osystem), myTexture(NULL), myHaveTexRectEXT(false), - myScreenmode(0), - myScreenmodeCount(0), myFilterParamName("GL_NEAREST"), - myZoomLevel(1), - myFSScaleFactor(1.0), + myScaleFactor(1.0), myDirtyFlag(true) { } @@ -186,7 +183,7 @@ bool FrameBufferGL::loadFuncs(const string& library) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferGL::initSubsystem() +bool FrameBufferGL::initSubsystem(VideoMode mode) { mySDLFlags |= SDL_OPENGL; @@ -207,15 +204,8 @@ bool FrameBufferGL::initSubsystem() break; } - // Get the valid OpenGL screenmodes - myScreenmodeCount = 0; - myScreenmode = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_OPENGL); - if((myScreenmode != (SDL_Rect**) -1) && (myScreenmode != (SDL_Rect**) 0)) - for(uInt32 i = 0; myScreenmode[i]; ++i) - myScreenmodeCount++; - // Create the screen - if(!createScreen()) + if(!setVidMode(mode)) return false; // Now check to see what color components were actually created @@ -248,22 +238,55 @@ string FrameBufferGL::about() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::setAspectRatio() +bool FrameBufferGL::setVidMode(VideoMode mode) { - theAspectRatio = myOSystem->settings().getFloat("gl_aspect") / 2; - if(theAspectRatio <= 0.0) - theAspectRatio = 1.0; -} + myScreenDim.x = myScreenDim.y = 0; + myScreenDim.w = mode.screen_w; + myScreenDim.h = mode.screen_h; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::setScaler(Scaler scaler) -{ - myZoomLevel = scaler.zoom; -} + myImageDim.x = mode.image_x; + myImageDim.y = mode.image_y; + myImageDim.w = mode.image_w; + myImageDim.h = mode.image_h; + + // Activate stretching if its been requested and it makes sense to do so + myScaleFactor = 1.0; + if(fullScreen() && (mode.image_w < mode.screen_w) && + (mode.image_h < mode.screen_h)) + { + const string& gl_fsmax = myOSystem->settings().getString("gl_fsmax"); + bool inUIMode = + myOSystem->eventHandler().state() == EventHandler::S_LAUNCHER || + myOSystem->eventHandler().state() == EventHandler::S_DEBUGGER; + + // Only stretch in certain modes + if((gl_fsmax == "always") || + (inUIMode && gl_fsmax == "ui") || + (!inUIMode && gl_fsmax == "tia")) + { + float scaleX = float(myImageDim.w) / myScreenDim.w; + float scaleY = float(myImageDim.h) / myScreenDim.h; + + if(scaleX > scaleY) + myScaleFactor = float(myScreenDim.w) / myImageDim.w; + else + myScaleFactor = float(myScreenDim.h) / myImageDim.h; + + myImageDim.w = (Uint16) (myScaleFactor * myImageDim.w); + myImageDim.h = (Uint16) (myScaleFactor * myImageDim.h); + myImageDim.x = (myScreenDim.w - myImageDim.w) / 2; + myImageDim.y = (myScreenDim.h - myImageDim.h) / 2; + } + } + + // Combine the zoom level and scaler into one quantity + myScaleFactor *= (float) mode.zoom; + + GLdouble orthoWidth = (GLdouble) + (myImageDim.w / myScaleFactor); + GLdouble orthoHeight = (GLdouble) + (myImageDim.h / myScaleFactor); -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferGL::createScreen() -{ SDL_GL_SetAttribute( SDL_GL_RED_SIZE, myRGB[0] ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, myRGB[1] ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, myRGB[2] ); @@ -276,11 +299,6 @@ bool FrameBufferGL::createScreen() int vsync = myOSystem->settings().getBool("gl_vsync") ? 1 : 0; SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, vsync ); - // Set the screen coordinates - GLdouble orthoWidth = 0.0; - GLdouble orthoHeight = 0.0; - setDimensions(&orthoWidth, &orthoHeight); - // Create screen containing GL context myScreen = SDL_SetVideoMode(myScreenDim.w, myScreenDim.h, 0, mySDLFlags); if(myScreen == NULL) @@ -291,7 +309,7 @@ bool FrameBufferGL::createScreen() // Check for some extensions that can potentially speed up operation const char* extensions = (const char *) p_glGetString(GL_EXTENSIONS); - myHaveTexRectEXT = strstr(extensions, "ARB_texture_rectangle") != NULL; + myHaveTexRectEXT = strstr(extensions, "ARB_texture_rectangle") != NULL; // Initialize GL display p_glViewport(myImageDim.x, myImageDim.y, myImageDim.w, myImageDim.h); @@ -544,11 +562,11 @@ void FrameBufferGL::drawBitmap(uInt32* bitmap, Int32 tx, Int32 ty, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::translateCoords(Int32* x, Int32* y) +void FrameBufferGL::translateCoords(Int32& x, Int32& y) { // Wow, what a mess :) - *x = (Int32) (((*x - myImageDim.x) / (myZoomLevel * myFSScaleFactor * theAspectRatio))); - *y = (Int32) (((*y - myImageDim.y) / (myZoomLevel * myFSScaleFactor))); + x = (Int32) ((x - myImageDim.x) / myScaleFactor); + y = (Int32) ((y - myImageDim.y) / myScaleFactor); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -666,112 +684,6 @@ bool FrameBufferGL::createTextures() return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::setDimensions(GLdouble* orthoWidth, GLdouble* orthoHeight) -{ - // We always know the initial image width and height - // We have to determine final image dimensions as well as screen dimensions - myImageDim.x = 0; - myImageDim.y = 0; - myImageDim.w = (Uint16) (myBaseDim.w * myZoomLevel * theAspectRatio); - myImageDim.h = (Uint16) (myBaseDim.h * myZoomLevel); - myScreenDim = myImageDim; - - myFSScaleFactor = 1.0f; - - // Determine if we're in fullscreen or windowed mode - // In fullscreen mode, we clip the SDL screen to known resolutions - // In windowed mode, we use the actual image resolution for the SDL screen - if(mySDLFlags & SDL_FULLSCREEN) - { - float scaleX = 0.0f; - float scaleY = 0.0f; - - // When in a fullscreen, most text mode, attempt to do 'nice' scaling - // This means scaling will only use integral values (1, 2, 3, ...) - bool gl_fsmax = myOSystem->settings().getBool("gl_fsmax"); - bool nicescale = myOSystem->eventHandler().state() == EventHandler::S_LAUNCHER || - myOSystem->eventHandler().state() == EventHandler::S_DEBUGGER; - - if(gl_fsmax && myDesktopDim.w != 0 && myDesktopDim.h != 0) - { - // Use the largest available screen size - myScreenDim.w = myDesktopDim.w; - myScreenDim.h = myDesktopDim.h; - - scaleX = float(myImageDim.w) / myScreenDim.w; - scaleY = float(myImageDim.h) / myScreenDim.h; - - if(scaleX > scaleY) - myFSScaleFactor = float(myScreenDim.w) / myImageDim.w; - else - myFSScaleFactor = float(myScreenDim.h) / myImageDim.h; - - if(nicescale) - myFSScaleFactor = (int)myFSScaleFactor; - - myImageDim.w = (Uint16) (myFSScaleFactor * myImageDim.w); - myImageDim.h = (Uint16) (myFSScaleFactor * myImageDim.h); - } - else if(gl_fsmax && myScreenmode != (SDL_Rect**) -1) - { - // Use the largest available screen size - myScreenDim.w = myScreenmode[0]->w; - myScreenDim.h = myScreenmode[0]->h; - - scaleX = float(myImageDim.w) / myScreenDim.w; - scaleY = float(myImageDim.h) / myScreenDim.h; - - if(scaleX > scaleY) - myFSScaleFactor = float(myScreenDim.w) / myImageDim.w; - else - myFSScaleFactor = float(myScreenDim.h) / myImageDim.h; - - if(nicescale) - myFSScaleFactor = (int)myFSScaleFactor; - - myImageDim.w = (Uint16) (myFSScaleFactor * myImageDim.w); - myImageDim.h = (Uint16) (myFSScaleFactor * myImageDim.h); - } - else if(myScreenmode == (SDL_Rect**) -1) - { - // All modes are available, so use the exact image resolution - myScreenDim.w = myImageDim.w; - myScreenDim.h = myImageDim.h; - } - else // otherwise, search for a valid screenmode - { - for(uInt32 i = myScreenmodeCount-1; i >= 0; i--) - { - if(myImageDim.w <= myScreenmode[i]->w && myImageDim.h <= myScreenmode[i]->h) - { - myScreenDim.w = myScreenmode[i]->w; - myScreenDim.h = myScreenmode[i]->h; - break; - } - } - } - - // Now calculate the OpenGL coordinates - myImageDim.x = (myScreenDim.w - myImageDim.w) / 2; - myImageDim.y = (myScreenDim.h - myImageDim.h) / 2; - - *orthoWidth = (GLdouble) (myImageDim.w / (myZoomLevel * theAspectRatio * myFSScaleFactor)); - *orthoHeight = (GLdouble) (myImageDim.h / (myZoomLevel * myFSScaleFactor)); - } - else - { - *orthoWidth = (GLdouble) (myImageDim.w / (myZoomLevel * theAspectRatio)); - *orthoHeight = (GLdouble) (myImageDim.h / myZoomLevel); - } -/* - cerr << "myImageDim.x = " << myImageDim.x << ", myImageDim.y = " << myImageDim.y << endl; - cerr << "myImageDim.w = " << myImageDim.w << ", myImageDim.h = " << myImageDim.h << endl; - cerr << "myScreenDim.w = " << myScreenDim.w << ", myScreenDim.h = " << myScreenDim.h << endl; - cerr << endl; -*/ -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FrameBufferGL::myFuncsLoaded = false; diff --git a/stella/src/common/FrameBufferGL.hxx b/stella/src/common/FrameBufferGL.hxx index 7354dd0ab..1f5537e65 100644 --- a/stella/src/common/FrameBufferGL.hxx +++ b/stella/src/common/FrameBufferGL.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBufferGL.hxx,v 1.43 2007-01-12 16:03:10 stephena Exp $ +// $Id: FrameBufferGL.hxx,v 1.44 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_GL_HXX @@ -36,7 +36,7 @@ class GUI::Font; This class implements an SDL OpenGL framebuffer. @author Stephen Anthony - @version $Id: FrameBufferGL.hxx,v 1.43 2007-01-12 16:03:10 stephena Exp $ + @version $Id: FrameBufferGL.hxx,v 1.44 2007-06-20 16:33:22 stephena Exp $ */ class FrameBufferGL : public FrameBuffer { @@ -67,7 +67,7 @@ class FrameBufferGL : public FrameBuffer This method is called to initialize OpenGL video mode. Return false if any operation fails, otherwise return true. */ - virtual bool initSubsystem(); + virtual bool initSubsystem(VideoMode mode); /** This method is called to query the type of the FrameBuffer. @@ -80,22 +80,11 @@ class FrameBufferGL : public FrameBuffer virtual string about(); /** - This method is called to set the aspect ratio of the screen. - */ - virtual void setAspectRatio(); + This method is called to change to the given video mode. - /** - This method is called to change to the given scaler type. - - @param scaler The scaler to use for rendering the mediasource + @param mode The mode to use for rendering the mediasource */ - virtual void setScaler(Scaler scaler); - - /** - This method is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - virtual bool createScreen(); + virtual bool setVidMode(VideoMode mode); /** Switches between the two filtering options in OpenGL. @@ -200,7 +189,7 @@ class FrameBufferGL : public FrameBuffer @param x X coordinate to translate @param y Y coordinate to translate */ - inline virtual void translateCoords(Int32* x, Int32* y); + inline virtual void translateCoords(Int32& x, Int32& y); /** This method adds a dirty rectangle @@ -221,8 +210,6 @@ class FrameBufferGL : public FrameBuffer private: bool createTextures(); - void setDimensions(GLdouble* orthoWidth, GLdouble* orthoHeight); - inline uInt32 power_of_two(uInt32 input) { uInt32 value = 1; @@ -257,12 +244,6 @@ class FrameBufferGL : public FrameBuffer // Optional GL extensions that may increase performance bool myHaveTexRectEXT; - // The possible OpenGL screenmodes to use - SDL_Rect** myScreenmode; - - // The number of usable OpenGL screenmodes - uInt32 myScreenmodeCount; - // The depth of the texture buffer uInt32 myDepth; @@ -272,12 +253,8 @@ class FrameBufferGL : public FrameBuffer // The name of the texture filtering to use string myFilterParamName; - // Used for zooming/scaling - uInt32 myZoomLevel; - - // The scaling to use in fullscreen mode - // This is separate from both zoomlevel and aspect ratio - float myFSScaleFactor; + // The amount by which to scale the imagein fullscreen mode + float myScaleFactor; // TODO - will be removed when textured dirty rect support is added bool myDirtyFlag; diff --git a/stella/src/common/FrameBufferSoft.cxx b/stella/src/common/FrameBufferSoft.cxx index 8a38a5417..0f6971e55 100644 --- a/stella/src/common/FrameBufferSoft.cxx +++ b/stella/src/common/FrameBufferSoft.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBufferSoft.cxx,v 1.70 2007-01-30 17:13:07 stephena Exp $ +// $Id: FrameBufferSoft.cxx,v 1.71 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -32,10 +32,10 @@ FrameBufferSoft::FrameBufferSoft(OSystem* osystem) : FrameBuffer(osystem), myZoomLevel(1), - myRenderType(kDirtyRect), - myUseDirtyRects(true), - myRectList(NULL), - myOverlayRectList(NULL) + myRenderType(kSoftZoom_16), + myDirtyFlag(false), + myInUIMode(false), + myRectList(NULL) { } @@ -43,26 +43,23 @@ FrameBufferSoft::FrameBufferSoft(OSystem* osystem) FrameBufferSoft::~FrameBufferSoft() { delete myRectList; - delete myOverlayRectList; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSoft::initSubsystem() +bool FrameBufferSoft::initSubsystem(VideoMode mode) { // Set up the rectangle list to be used in the dirty update delete myRectList; myRectList = new RectList(); - delete myOverlayRectList; - myOverlayRectList = new RectList(); - if(!myRectList || !myOverlayRectList) + if(!myRectList) { cerr << "ERROR: Unable to get memory for SDL rects" << endl; return false; } // Create the screen - return createScreen(); + return setVidMode(mode); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -86,43 +83,25 @@ string FrameBufferSoft::about() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSoft::setAspectRatio() -{ - // Aspect ratio correction not yet available in software mode - theAspectRatio = 1.0; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSoft::setScaler(Scaler scaler) -{ - switch(scaler.type) - { - case kZOOM1X: - case kZOOM2X: - case kZOOM3X: - case kZOOM4X: - case kZOOM5X: - case kZOOM6X: - // Software framebuffer doesn't handle the fancy scaling modes - myZoomLevel = scaler.zoom; - break; - default: // should never get here - myZoomLevel = 1; - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSoft::createScreen() +bool FrameBufferSoft::setVidMode(VideoMode mode) { myScreenDim.x = myScreenDim.y = 0; + myScreenDim.w = mode.screen_w; + myScreenDim.h = mode.screen_h; - myScreenDim.w = myBaseDim.w * myZoomLevel; - myScreenDim.h = myBaseDim.h * myZoomLevel; + myImageDim.x = mode.image_x; + myImageDim.y = mode.image_y; + myImageDim.w = mode.image_w; + myImageDim.h = mode.image_h; - // In software mode, the image and screen dimensions are always the same - myImageDim = myScreenDim; + myZoomLevel = mode.zoom; + // Make sure to clear the screen + if(myScreen) + { + SDL_FillRect(myScreen, NULL, 0); + SDL_UpdateRect(myScreen, 0, 0, 0, 0); + } myScreen = SDL_SetVideoMode(myScreenDim.w, myScreenDim.h, 0, mySDLFlags); if(myScreen == NULL) { @@ -134,11 +113,11 @@ bool FrameBufferSoft::createScreen() // Make sure drawMediaSource() knows which renderer to use stateChanged(myOSystem->eventHandler().state()); + myBaseOffset = myImageDim.y * myPitch + myImageDim.x; // Erase old rects, since they've probably been scaled for // a different sized screen myRectList->start(); - myOverlayRectList->start(); return true; } @@ -150,164 +129,16 @@ void FrameBufferSoft::drawMediaSource() uInt8* currentFrame = mediasrc.currentFrameBuffer(); uInt8* previousFrame = mediasrc.previousFrameBuffer(); - uInt16 screenMultiple = (uInt16) myZoomLevel; uInt32 width = mediasrc.width(); uInt32 height = mediasrc.height(); - switch(myRenderType) // use switch/case, since we'll eventually have filters + switch(myRenderType) { - case kDirtyRect: - { - struct Rectangle - { - uInt8 color; - uInt16 x, y, width, height; - } rectangles[2][160]; - - // This array represents the rectangles that need displaying - // on the current scanline we're processing - Rectangle* currentRectangles = rectangles[0]; - - // This array represents the rectangles that are still active - // from the previous scanlines we have processed - Rectangle* activeRectangles = rectangles[1]; - - // Indicates the number of active rectangles - uInt16 activeCount = 0; - - // This update procedure requires theWidth to be a multiple of four. - // This is validated when the properties are loaded. - for(uInt16 y = 0; y < height; ++y) - { - // Indicates the number of current rectangles - uInt16 currentCount = 0; - - // Look at four pixels at a time to see if anything has changed - uInt32* current = (uInt32*)(currentFrame); - uInt32* previous = (uInt32*)(previousFrame); - - for(uInt16 x = 0; x < width; x += 4, ++current, ++previous) - { - // Has something changed in this set of four pixels? - if((*current != *previous) || theRedrawTIAIndicator) - { - uInt8* c = (uInt8*)current; - uInt8* p = (uInt8*)previous; - - // Look at each of the bytes that make up the uInt32 - for(uInt16 i = 0; i < 4; ++i, ++c, ++p) - { - // See if this pixel has changed - if((*c != *p) || theRedrawTIAIndicator) - { - // Can we extend a rectangle or do we have to create a new one? - if((currentCount != 0) && - (currentRectangles[currentCount - 1].color == *c) && - ((currentRectangles[currentCount - 1].x + - currentRectangles[currentCount - 1].width) == (x + i))) - { - currentRectangles[currentCount - 1].width += 1; - } - else - { - currentRectangles[currentCount].x = x + i; - currentRectangles[currentCount].y = y; - currentRectangles[currentCount].width = 1; - currentRectangles[currentCount].height = 1; - currentRectangles[currentCount].color = *c; - currentCount++; - } - } - } - } - } - - // Merge the active and current rectangles flushing any that are of no use - uInt16 activeIndex = 0; - - for(uInt16 t = 0; (t < currentCount) && (activeIndex < activeCount); ++t) - { - Rectangle& current = currentRectangles[t]; - Rectangle& active = activeRectangles[activeIndex]; - - // Can we merge the current rectangle with an active one? - if((current.x == active.x) && (current.width == active.width) && - (current.color == active.color)) - { - current.y = active.y; - current.height = active.height + 1; - - ++activeIndex; - } - // Is it impossible for this active rectangle to be merged? - else if(current.x >= active.x) - { - // Flush the active rectangle - SDL_Rect temp; - - temp.x = active.x * screenMultiple << 1; - temp.y = active.y * screenMultiple; - temp.w = active.width * screenMultiple << 1; - temp.h = active.height * screenMultiple; - - myRectList->add(&temp); - SDL_FillRect(myScreen, &temp, myDefPalette[active.color]); - - ++activeIndex; - } - } - - // Flush any remaining active rectangles - for(uInt16 s = activeIndex; s < activeCount; ++s) - { - Rectangle& active = activeRectangles[s]; - - SDL_Rect temp; - temp.x = active.x * screenMultiple << 1; - temp.y = active.y * screenMultiple; - temp.w = active.width * screenMultiple << 1; - temp.h = active.height * screenMultiple; - - myRectList->add(&temp); - SDL_FillRect(myScreen, &temp, myDefPalette[active.color]); - } - - // We can now make the current rectangles into the active rectangles - Rectangle* tmp = currentRectangles; - currentRectangles = activeRectangles; - activeRectangles = tmp; - activeCount = currentCount; - - currentFrame += width; - previousFrame += width; - } - - // Flush any rectangles that are still active - for(uInt16 t = 0; t < activeCount; ++t) - { - Rectangle& active = activeRectangles[t]; - - SDL_Rect temp; - temp.x = active.x * screenMultiple << 1; - temp.y = active.y * screenMultiple; - temp.w = active.width * screenMultiple << 1; - temp.h = active.height * screenMultiple; - - myRectList->add(&temp); - SDL_FillRect(myScreen, &temp, myDefPalette[active.color]); - } - break; // kDirtyRect - } - case kSoftZoom_16: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt16* buffer = (uInt16*)myScreen->pixels; + uInt16* buffer = (uInt16*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -331,6 +162,7 @@ void FrameBufferSoft::drawMediaSource() buffer[pos++] = (uInt16) myDefPalette[v]; buffer[pos++] = (uInt16) myDefPalette[v]; } + myDirtyFlag = true; } else pos += xstride + xstride; @@ -345,12 +177,8 @@ void FrameBufferSoft::drawMediaSource() case kSoftZoom_24: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt8* buffer = (uInt8*)myScreen->pixels; + uInt8* buffer = (uInt8*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -379,6 +207,7 @@ void FrameBufferSoft::drawMediaSource() buffer[pos++] = r; buffer[pos++] = g; buffer[pos++] = b; buffer[pos++] = r; buffer[pos++] = g; buffer[pos++] = b; } + myDirtyFlag = true; } else // try to eliminate multply whereever possible pos += xstride + xstride + xstride + xstride + xstride + xstride; @@ -393,12 +222,8 @@ void FrameBufferSoft::drawMediaSource() case kSoftZoom_32: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt32* buffer = (uInt32*)myScreen->pixels; + uInt32* buffer = (uInt32*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -422,6 +247,7 @@ void FrameBufferSoft::drawMediaSource() buffer[pos++] = (uInt32) myDefPalette[v]; buffer[pos++] = (uInt32) myDefPalette[v]; } + myDirtyFlag = true; } else pos += xstride + xstride; @@ -436,12 +262,8 @@ void FrameBufferSoft::drawMediaSource() case kPhosphor_16: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt16* buffer = (uInt16*)myScreen->pixels; + uInt16* buffer = (uInt16*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -469,17 +291,14 @@ void FrameBufferSoft::drawMediaSource() bufofsY += width; } SDL_UnlockSurface(myScreen); + myDirtyFlag = true; break; // kPhosphor_16 } case kPhosphor_24: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt8* buffer = (uInt8*)myScreen->pixels; + uInt8* buffer = (uInt8*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -511,17 +330,14 @@ void FrameBufferSoft::drawMediaSource() bufofsY += width; } SDL_UnlockSurface(myScreen); + myDirtyFlag = true; break; // kPhosphor_24 } case kPhosphor_32: { - SDL_Rect temp; - temp.x = temp.y = temp.w = temp.h = 0; - myRectList->add(&temp); - SDL_LockSurface(myScreen); - uInt32* buffer = (uInt32*)myScreen->pixels; + uInt32* buffer = (uInt32*)myScreen->pixels + myBaseOffset; uInt32 bufofsY = 0; uInt32 screenofsY = 0; for(uInt32 y = 0; y < height; ++y) @@ -549,6 +365,7 @@ void FrameBufferSoft::drawMediaSource() bufofsY += width; } SDL_UnlockSurface(myScreen); + myDirtyFlag = true; break; // kPhosphor_32 } } @@ -557,26 +374,28 @@ void FrameBufferSoft::drawMediaSource() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBufferSoft::preFrameUpdate() { - // Start a new rectlist on each display update - myRectList->start(); - - // Add all previous overlay rects, then erase - SDL_Rect* dirtyOverlayRects = myOverlayRectList->rects(); - for(unsigned int i = 0; i < myOverlayRectList->numRects(); ++i) - myRectList->add(&dirtyOverlayRects[i]); - myOverlayRectList->start(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBufferSoft::postFrameUpdate() { - if(myUseDirtyRects) +/* +cerr << "FrameBufferSoft::postFrameUpdate()" << endl + << " myInUIMode: " << myInUIMode << endl + << " myRectList->numRects(): " << myRectList->numRects() << endl + << " myDirtyFlag: " << myDirtyFlag << endl + << endl; +*/ + if(myInUIMode && myRectList->numRects() > 0) + { SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects()); - else if(myRectList->numRects() > 0) + } + else if(myDirtyFlag || myRectList->numRects() > 0) { SDL_Flip(myScreen); - myRectList->start(); + myDirtyFlag = false; } + myRectList->start(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -588,11 +407,13 @@ void FrameBufferSoft::scanline(uInt32 row, uInt8* data) uInt32 pixel = 0; uInt8 *p, r, g, b; - for(Int32 x = 0; x < myScreen->w; x++) + // Row will be offset by the amount the actual image is shifted down + row += myImageDim.y; + for(Int32 x = 0; x < myScreen->w; ++x) { - p = (Uint8*) ((uInt8*)myScreen->pixels + // Start at top of RAM - (row * myScreen->pitch) + // Go down 'row' lines - (x * myBytesPerPixel)); // Go in 'x' pixels + p = (Uint8*) ((uInt8*)myScreen->pixels + // Start at top of RAM + (row * myScreen->pitch) + // Go down 'row' lines + ((x + myImageDim.x) * myBytesPerPixel)); // Go in 'x' pixels switch(myBytesPerPixel) { @@ -638,8 +459,8 @@ void FrameBufferSoft::hLine(uInt32 x, uInt32 y, uInt32 x2, int color) SDL_Rect tmp; // Horizontal line - tmp.x = x * myZoomLevel; - tmp.y = y * myZoomLevel; + tmp.x = myImageDim.x + x * myZoomLevel; + tmp.y = myImageDim.y + y * myZoomLevel; tmp.w = (x2 - x + 1) * myZoomLevel; tmp.h = myZoomLevel; SDL_FillRect(myScreen, &tmp, myDefPalette[color]); @@ -651,8 +472,8 @@ void FrameBufferSoft::vLine(uInt32 x, uInt32 y, uInt32 y2, int color) SDL_Rect tmp; // Vertical line - tmp.x = x * myZoomLevel; - tmp.y = y * myZoomLevel; + tmp.x = myImageDim.x + x * myZoomLevel; + tmp.y = myImageDim.y + y * myZoomLevel; tmp.w = myZoomLevel; tmp.h = (y2 - y + 1) * myZoomLevel; SDL_FillRect(myScreen, &tmp, myDefPalette[color]); @@ -665,8 +486,8 @@ void FrameBufferSoft::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, SDL_Rect tmp; // Fill the rectangle - tmp.x = x * myZoomLevel; - tmp.y = y * myZoomLevel; + tmp.x = myImageDim.x + x * myZoomLevel; + tmp.y = myImageDim.y + y * myZoomLevel; tmp.w = w * myZoomLevel; tmp.h = h * myZoomLevel; SDL_FillRect(myScreen, &tmp, myDefPalette[color]); @@ -702,7 +523,7 @@ void FrameBufferSoft::drawChar(const GUI::Font* font, uInt8 chr, case 2: { // Get buffer position where upper-left pixel of the character will be drawn - uInt16* buffer = (uInt16*) myScreen->pixels + yorig * myPitch + xorig; + uInt16* buffer = (uInt16*) myScreen->pixels + myBaseOffset + yorig * myPitch + xorig; for(int y = h; y; --y) { const uInt16 fontbuf = *tmp++; @@ -728,7 +549,7 @@ void FrameBufferSoft::drawChar(const GUI::Font* font, uInt8 chr, case 3: { // Get buffer position where upper-left pixel of the character will be drawn - uInt8* buffer = (uInt8*) myScreen->pixels + yorig * myPitch + xorig; + uInt8* buffer = (uInt8*) myScreen->pixels + myBaseOffset + yorig * myPitch + xorig; uInt32 pixel = myDefPalette[color]; uInt8 r = (pixel & myFormat->Rmask) >> myFormat->Rshift; uInt8 g = (pixel & myFormat->Gmask) >> myFormat->Gshift; @@ -763,7 +584,7 @@ void FrameBufferSoft::drawChar(const GUI::Font* font, uInt8 chr, case 4: { // Get buffer position where upper-left pixel of the character will be drawn - uInt32* buffer = (uInt32*) myScreen->pixels + yorig * myPitch + xorig; + uInt32* buffer = (uInt32*) myScreen->pixels + myBaseOffset + yorig * myPitch + xorig; for(int y = h; y; --y) { const uInt16 fontbuf = *tmp++; @@ -805,8 +626,8 @@ void FrameBufferSoft::drawBitmap(uInt32* bitmap, Int32 xorig, Int32 yorig, { if(bitmap[y] & mask) { - rect.x = (x + xorig) * myZoomLevel; - rect.y = (y + yorig) * myZoomLevel; + rect.x = myImageDim.x + (x + xorig) * myZoomLevel; + rect.y = myImageDim.y + (y + yorig) * myZoomLevel; rect.w = rect.h = myZoomLevel; SDL_FillRect(myScreen, &rect, myDefPalette[color]); } @@ -815,27 +636,24 @@ void FrameBufferSoft::drawBitmap(uInt32* bitmap, Int32 xorig, Int32 yorig, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSoft::translateCoords(Int32* x, Int32* y) +void FrameBufferSoft::translateCoords(Int32& x, Int32& y) { - // We don't bother checking offsets or aspect ratios, since - // they're not yet supported in software mode. - *x /= myZoomLevel; - *y /= myZoomLevel; + x = (x - myImageDim.x) / myZoomLevel; + y = (y - myImageDim.y) / myZoomLevel; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBufferSoft::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { - // Add a dirty rect to the overlay rectangle list - // They will actually be added to the main rectlist in preFrameUpdate() + // Add a dirty rect to the UI rectangle list // TODO - intelligent merging of rectangles, to avoid overlap SDL_Rect temp; - temp.x = x * myZoomLevel; - temp.y = y * myZoomLevel; + temp.x = myImageDim.x + x * myZoomLevel; + temp.y = myImageDim.y + y * myZoomLevel; temp.w = w * myZoomLevel; temp.h = h * myZoomLevel; - myOverlayRectList->add(&temp); + myRectList->add(&temp); // cerr << "addDirtyRect(): " // << "x=" << temp.x << ", y=" << temp.y << ", w=" << temp.w << ", h=" << temp.h << endl; @@ -856,60 +674,38 @@ void FrameBufferSoft::stateChanged(EventHandler::State state) if(!myScreen) return; - // When in a UI mode, always use dirty rects - // Otherwise, check the 'dirtyrects' setting - // Phosphor mode implies a full update, so turn off dirty rects - bool emulation = (state == EventHandler::S_EMULATE || - state == EventHandler::S_PAUSE); - if(emulation) - { - if(myUsePhosphor) - myUseDirtyRects = false; - else - myUseDirtyRects = myOSystem->settings().getBool("dirtyrects"); - } - else - myUseDirtyRects = true; + myInUIMode = (state == EventHandler::S_LAUNCHER || + state == EventHandler::S_DEBUGGER); // Make sure drawMediaSource() knows which renderer to use - // Testing for dirty rects takes priority over phosphor mode, - // since phosphor mode only exists while emulating a ROM switch(myBytesPerPixel) { case 2: // 16-bit myPitch = myScreen->pitch/2; - if(myUsePhosphor && emulation) + if(myUsePhosphor) myRenderType = kPhosphor_16; - else if(myUseDirtyRects) - myRenderType = kDirtyRect; else myRenderType = kSoftZoom_16; break; case 3: // 24-bit myPitch = myScreen->pitch; - if(myUsePhosphor && emulation) + if(myUsePhosphor) myRenderType = kPhosphor_24; - else if(myUseDirtyRects) - myRenderType = kDirtyRect; else myRenderType = kSoftZoom_24; break; case 4: // 32-bit myPitch = myScreen->pitch/4; - if(myUsePhosphor && emulation) + if(myUsePhosphor) myRenderType = kPhosphor_32; - else if(myUseDirtyRects) - myRenderType = kDirtyRect; else myRenderType = kSoftZoom_32; break; default: - myRenderType = kDirtyRect; // What else should we do here? + myRenderType = kSoftZoom_16; // What else should we do here? break; } // Have the changes take effect myOSystem->eventHandler().refreshDisplay(); - -//cerr << "Render type = " << myRenderType << ", dirty rects = " << myUseDirtyRects << endl; } diff --git a/stella/src/common/FrameBufferSoft.hxx b/stella/src/common/FrameBufferSoft.hxx index bf4e6a956..7dd5bf676 100644 --- a/stella/src/common/FrameBufferSoft.hxx +++ b/stella/src/common/FrameBufferSoft.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBufferSoft.hxx,v 1.45 2007-01-15 00:07:51 stephena Exp $ +// $Id: FrameBufferSoft.hxx,v 1.46 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_SOFT_HXX @@ -33,7 +33,7 @@ class RectList; This class implements an SDL software framebuffer. @author Stephen Anthony - @version $Id: FrameBufferSoft.hxx,v 1.45 2007-01-15 00:07:51 stephena Exp $ + @version $Id: FrameBufferSoft.hxx,v 1.46 2007-06-20 16:33:22 stephena Exp $ */ class FrameBufferSoft : public FrameBuffer { @@ -55,7 +55,7 @@ class FrameBufferSoft : public FrameBuffer This method is called to initialize software video mode. Return false if any operation fails, otherwise return true. */ - virtual bool initSubsystem(); + virtual bool initSubsystem(VideoMode mode); /** This method is called to query the type of the FrameBuffer. @@ -68,22 +68,11 @@ class FrameBufferSoft : public FrameBuffer virtual string about(); /** - This method is called to set the aspect ratio of the screen. - */ - virtual void setAspectRatio(); + This method is called to change to the given videomode type. - /** - This method is called to change to the given scaler type. - - @param scaler The scaler to use for rendering the mediasource + @param mode The video mode to use for rendering the mediasource */ - virtual void setScaler(Scaler scaler); - - /** - This method is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - virtual bool createScreen(); + virtual bool setVidMode(VideoMode mode); /** Switches between the filtering options in software mode. @@ -188,7 +177,7 @@ class FrameBufferSoft : public FrameBuffer @param x X coordinate to translate @param y Y coordinate to translate */ - virtual void translateCoords(Int32* x, Int32* y); + virtual void translateCoords(Int32& x, Int32& y); /** This method adds a dirty rectangle @@ -215,10 +204,10 @@ class FrameBufferSoft : public FrameBuffer int myZoomLevel; int myBytesPerPixel; int myPitch; + int myBaseOffset; SDL_PixelFormat* myFormat; enum RenderType { - kDirtyRect, kSoftZoom_16, kSoftZoom_24, kSoftZoom_32, @@ -228,14 +217,14 @@ class FrameBufferSoft : public FrameBuffer }; RenderType myRenderType; - // Use dirty updates (SDL_UpdateRects instead of SDL_UpdateRect) - bool myUseDirtyRects; + // Indicates if the TIA image has been modified + bool myDirtyFlag; - // Used in the dirty update of the SDL surface + // Indicates if we're in a purely UI mode + bool myInUIMode; + + // Used in the dirty update of rectangles in non-TIA modes RectList* myRectList; - - // Used in the dirty update of the overlay surface - RectList* myOverlayRectList; }; #endif diff --git a/stella/src/common/VideoModeList.hxx b/stella/src/common/VideoModeList.hxx new file mode 100644 index 000000000..245a63393 --- /dev/null +++ b/stella/src/common/VideoModeList.hxx @@ -0,0 +1,114 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: VideoModeList.hxx,v 1.1 2007-06-20 16:33:22 stephena Exp $ +//============================================================================ + +#ifndef VIDMODE_LIST_HXX +#define VIDMODE_LIST_HXX + +#include "Array.hxx" +#include "bspf.hxx" + +struct VideoMode { + uInt32 image_x, image_y, image_w, image_h; + uInt32 screen_w, screen_h; + uInt32 zoom; + string name; +}; + +/** + This class implements an iterator around an array of VideoMode objects. + + @author Stephen Anthony + @version $Id: VideoModeList.hxx,v 1.1 2007-06-20 16:33:22 stephena Exp $ +*/ +class VideoModeList +{ + public: + VideoModeList() : myIdx(-1) { } + + void add(VideoMode mode) { myModeList.push_back(mode); } + + void clear() { myModeList.clear(); } + + bool isEmpty() const { return myModeList.isEmpty(); } + + uInt32 size() const { return myModeList.size(); } + + const VideoMode& previous() + { + --myIdx; + if(myIdx < 0) myIdx = myModeList.size() - 1; + return current(); + } + + const VideoMode& current() const + { + return myModeList[myIdx]; + } + + const VideoMode& next() + { + myIdx = (myIdx + 1) % myModeList.size(); + return current(); + } + + void setByResolution(uInt32 width, uInt32 height) + { + // Find the largest resolution within the given bounds + myIdx = 0; + for(unsigned int i = myModeList.size() - 1; i; --i) + { + if(myModeList[i].screen_w <= width && myModeList[i].screen_h <= height) + { + myIdx = i; + break; + } + } + } + + void setByZoom(uInt32 zoom) + { + // Find the largest zoom within the given bounds + myIdx = 0; + for(unsigned int i = myModeList.size() - 1; i; --i) + { + if(myModeList[i].zoom <= zoom) + { + myIdx = i; + break; + } + } + } + + static bool modesAreEqual(const VideoMode& m1, const VideoMode& m2) + { + return (m1.image_x == m2.image_x) && + (m1.image_y == m2.image_y) && + (m1.image_w == m2.image_w) && + (m1.image_h == m2.image_h) && + (m1.screen_w == m2.screen_w) && + (m1.screen_h == m2.screen_h) && + (m1.zoom == m2.zoom) && + (m1.name == m2.name); + } + + private: + Common::Array myModeList; + int myIdx; +}; + +#endif diff --git a/stella/src/emucore/EventHandler.cxx b/stella/src/emucore/EventHandler.cxx index 6385f207c..16153382f 100644 --- a/stella/src/emucore/EventHandler.cxx +++ b/stella/src/emucore/EventHandler.cxx @@ -14,7 +14,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: EventHandler.cxx,v 1.202 2007-02-22 02:15:46 stephena Exp $ +// $Id: EventHandler.cxx,v 1.203 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -388,11 +388,11 @@ void EventHandler::poll(uInt32 time) { #ifndef MAC_OSX case SDLK_EQUALS: - myOSystem->frameBuffer().scale(+1); + myOSystem->frameBuffer().changeVidMode(+1); break; case SDLK_MINUS: - myOSystem->frameBuffer().scale(-1); + myOSystem->frameBuffer().changeVidMode(-1); break; case SDLK_RETURN: @@ -510,11 +510,11 @@ void EventHandler::poll(uInt32 time) break; case SDLK_EQUALS: - myOSystem->frameBuffer().scale(+1); + myOSystem->frameBuffer().changeVidMode(+1); break; case SDLK_MINUS: - myOSystem->frameBuffer().scale(-1); + myOSystem->frameBuffer().changeVidMode(-1); break; case SDLK_RETURN: @@ -850,12 +850,13 @@ void EventHandler::handleMouseMotionEvent(SDL_Event& event) { // Take window zooming into account int x = event.motion.x, y = event.motion.y; - myOSystem->frameBuffer().translateCoords(&x, &y); + myOSystem->frameBuffer().translateCoords(x, y); // Determine which mode we're in, then send the event to the appropriate place if(myState == S_EMULATE) { int w = myOSystem->frameBuffer().baseWidth(); + if(x < 0 || x > w) return; // Grabmouse introduces some lag into the mouse movement, // so we need to fudge the numbers a bit @@ -882,7 +883,7 @@ void EventHandler::handleMouseButtonEvent(SDL_Event& event, int state) // Take window zooming into account Int32 x = event.button.x, y = event.button.y; //if (state) cerr << "B: x = " << x << ", y = " << y << endl; - myOSystem->frameBuffer().translateCoords(&x, &y); + myOSystem->frameBuffer().translateCoords(x, y); //if (state) cerr << "A: x = " << x << ", y = " << y << endl << endl; MouseButton button; diff --git a/stella/src/emucore/FrameBuffer.cxx b/stella/src/emucore/FrameBuffer.cxx index 5b417f0f7..26f132730 100644 --- a/stella/src/emucore/FrameBuffer.cxx +++ b/stella/src/emucore/FrameBuffer.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBuffer.cxx,v 1.117 2007-01-30 17:13:10 stephena Exp $ +// $Id: FrameBuffer.cxx,v 1.118 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -40,7 +40,6 @@ FrameBuffer::FrameBuffer(OSystem* osystem) : myOSystem(osystem), myScreen(0), - theAspectRatio(1.0), theRedrawTIAIndicator(true), myUsePhosphor(false), myPhosphorBlend(77), @@ -55,48 +54,33 @@ FrameBuffer::~FrameBuffer(void) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::initialize(const string& title, uInt32 width, uInt32 height, - bool useAspect) +void FrameBuffer::initialize(const string& title, uInt32 width, uInt32 height) { - bool isAlreadyInitialized = (SDL_WasInit(SDL_INIT_VIDEO) & SDL_INIT_VIDEO) > 0; + // Now (re)initialize the SDL video system + // These things only have to be done one per FrameBuffer creation + if(SDL_WasInit(SDL_INIT_VIDEO) == 0) + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) + return; + + myInitializedCount++; myBaseDim.x = myBaseDim.y = 0; myBaseDim.w = (uInt16) width; myBaseDim.h = (uInt16) height; - // Now (re)initialize the SDL video system - // These things only have to be done one per FrameBuffer creation - if(!isAlreadyInitialized) - { - Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER; - - if(SDL_Init(initflags) < 0) - return; - } - myInitializedCount++; - - // Query the desktop size - // This is really the job of SDL - int dwidth = 0, dheight = 0; - myOSystem->getScreenDimensions(dwidth, dheight); - myDesktopDim.w = dwidth; myDesktopDim.h = dheight; - - // Get the aspect ratio for the display if it's been enabled - theAspectRatio = 1.0; - if(useAspect) setAspectRatio(); - // Set fullscreen flag +#ifdef WINDOWED_SUPPORT mySDLFlags = myOSystem->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0; +#else + mySDLFlags = 0; +#endif - // Set the available scalers for this framebuffer, based on current eventhandler - // state and the maximum size of a window for the current desktop - setAvailableScalers(); + // Set the available video modes for this framebuffer + setAvailableVidModes(); // Initialize video subsystem - Scaler scaler; - getScaler(scaler, 0, currentScalerName()); - setScaler(scaler); - initSubsystem(); + VideoMode mode = getSavedVidMode(); + initSubsystem(mode); // Set window title and icon setWindowTitle(title); @@ -371,50 +355,73 @@ void FrameBuffer::toggleFullscreen() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBuffer::setFullscreen(bool enable) { +#ifdef WINDOWED_SUPPORT // Update the settings myOSystem->settings().setBool("fullscreen", enable); - if(enable) mySDLFlags |= SDL_FULLSCREEN; else mySDLFlags &= ~SDL_FULLSCREEN; - if(!createScreen()) - return; + // Do a dummy call to getSavedVidMode to set up the modelists + // and have it point to the correct 'current' mode + getSavedVidMode(); - myOSystem->eventHandler().refreshDisplay(); - setCursorState(); + // Do a mode change to the 'current' mode by not passing a '+1' or '-1' + // to changeVidMode() + changeVidMode(0); +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBuffer::scale(int direction, const string& type) +bool FrameBuffer::changeVidMode(int direction) { - Scaler newScaler; - const string& currentScaler = (direction == 0 ? type : currentScalerName()); - getScaler(newScaler, direction, currentScaler); + VideoMode oldmode = myCurrentModeList->current(); + if(direction == +1) + myCurrentModeList->next(); + else if(direction == -1) + myCurrentModeList->previous(); - // Only update the scaler if it's changed from the old one - if(currentScaler != string(newScaler.comparitor)) + VideoMode newmode = myCurrentModeList->current(); + if(!setVidMode(newmode)) + return false; + + myOSystem->eventHandler().refreshDisplay(); + setCursorState(); + showMessage(newmode.name); + + // Determine which mode we're in, and save to the appropriate setting + if(fullScreen()) + { + myOSystem->settings().setSize("fullres", newmode.screen_w, newmode.screen_h); + } + else { - setScaler(newScaler); - if(!createScreen()) - return false; - EventHandler::State state = myOSystem->eventHandler().state(); bool inTIAMode = (state == EventHandler::S_EMULATE || state == EventHandler::S_PAUSE || state == EventHandler::S_MENU || state == EventHandler::S_CMDMENU); - myOSystem->eventHandler().refreshDisplay(); - showMessage(newScaler.name); - if(inTIAMode) - myOSystem->settings().setString("scale_tia", newScaler.comparitor); + myOSystem->settings().setInt("zoom_tia", newmode.zoom); else - myOSystem->settings().setString("scale_ui", newScaler.comparitor); + myOSystem->settings().setInt("zoom_ui", newmode.zoom); } + return true; +/* +cerr << "New mode:" << endl + << " screen w = " << newmode.screen_w << endl + << " screen h = " << newmode.screen_h << endl + << " image x = " << newmode.image_x << endl + << " image y = " << newmode.image_y << endl + << " image w = " << newmode.image_w << endl + << " image h = " << newmode.image_h << endl + << " zoom = " << newmode.zoom << endl + << " name = " << newmode.name << endl + << endl; +*/ } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -459,7 +466,11 @@ void FrameBuffer::grabMouse(bool grab) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FrameBuffer::fullScreen() { - return myOSystem->settings().getBool("fullscreen"); +#ifdef WINDOWED_SUPPORT + return myOSystem->settings().getBool("fullscreen"); +#else + return true; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -666,108 +677,116 @@ uInt8 FrameBuffer::getPhosphor(uInt8 c1, uInt8 c2) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int FrameBuffer::maxWindowSizeForScreen() +uInt32 FrameBuffer::maxWindowSizeForScreen(uInt32 screenWidth, uInt32 screenHeight) { - uInt32 sWidth = myDesktopDim.w; - uInt32 sHeight = myDesktopDim.h; - uInt32 multiplier = 10; - - // If screenwidth or height could not be found, use default zoom value - if(sWidth == 0 || sHeight == 0) - return 4; - - bool found = false; - while(!found && (multiplier > 0)) + uInt32 multiplier = 1; + for(;;) { - // Figure out the desired size of the window - uInt32 width = (uInt32) (myBaseDim.w * multiplier * theAspectRatio); + // Figure out the zoomed size of the window + uInt32 width = myBaseDim.w * multiplier; uInt32 height = myBaseDim.h * multiplier; - if((width < sWidth) && (height < sHeight)) - found = true; - else - multiplier--; - } - - if(found) - return multiplier; - else - return 1; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::setAvailableScalers() -{ - /** Different emulation modes support different scalers, and the size - of the current desktop also determines how much a window can be - zoomed. */ - int maxsize = maxWindowSizeForScreen(); - myScalerList.clear(); - - for(int i = 0; i < kScalerListSize; ++i) - if(ourScalers[i].zoom <= maxsize) - myScalerList.push_back(&ourScalers[i]); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::getScaler(Scaler& scaler, int direction, const string& name) -{ - // First search for the scaler specified by name - int pos = -1; - for(unsigned int i = 0; i < myScalerList.size(); ++i) - { - if(BSPF_strcasecmp(myScalerList[i]->name, name.c_str()) == 0) - { - pos = i; + if((width > screenWidth) || (height > screenHeight)) break; - } + + ++multiplier; + } + return multiplier > 1 ? multiplier - 1 : 1; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBuffer::setAvailableVidModes() +{ + // First we look at windowed modes + // These can be sized exactly as required, since there's normally no + // restriction on window size (up the maximum size) + myWindowedModeList.clear(); + int max_zoom = maxWindowSizeForScreen(myOSystem->desktopWidth(), + myOSystem->desktopHeight()); + for(int i = 1; i <= max_zoom; ++i) + { + VideoMode m; + m.image_x = m.image_y = 0; + m.image_w = m.screen_w = myBaseDim.w * i; + m.image_h = m.screen_h = myBaseDim.h * i; + m.zoom = i; + ostringstream buf; + buf << "Zoom " << i << "x"; + m.name = buf.str(); + + myWindowedModeList.add(m); } - // If we found a scaler, look at direction - if(pos >= 0) + // Now consider the fullscreen modes + // There are often stricter requirements on these, and they're normally + // different depending on the OSystem in use + // As well, we usually can't get fullscreen modes in the exact size + // we want, so we need to calculate image offsets + myFullscreenModeList.clear(); + const ResolutionList& res = myOSystem->supportedResolutions(); + for(unsigned int i = 0; i < res.size(); ++i) { - switch(direction) + VideoMode m; + m.screen_w = res[i].width; + m.screen_h = res[i].height; + m.zoom = maxWindowSizeForScreen(m.screen_w, m.screen_h); + m.name = res[i].name; + + // Auto-calculate 'smart' centering; platform-specific framebuffers are + // free to ignore or augment it + m.image_w = myBaseDim.w * m.zoom; + m.image_h = myBaseDim.h * m.zoom; + m.image_x = (m.screen_w - m.image_w) / 2; + m.image_y = (m.screen_h - m.image_h) / 2; + +/* +cerr << "Fullscreen modes:" << endl + << " Mode " << i << endl + << " screen w = " << m.screen_w << endl + << " screen h = " << m.screen_h << endl + << " image x = " << m.image_x << endl + << " image y = " << m.image_y << endl + << " image w = " << m.image_w << endl + << " image h = " << m.image_h << endl + << " zoom = " << m.zoom << endl + << endl; +*/ + myFullscreenModeList.add(m); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +VideoMode FrameBuffer::getSavedVidMode() +{ + if(myOSystem->settings().getBool("fullscreen")) + { + // Point the modelist to fullscreen modes, and set the iterator to + // the mode closest to the given resolution + int w = -1, h = -1; + myOSystem->settings().getSize("fullres", w, h); + if(w < 0 || h < 0) { - case 0: // actual scaler specified in 'name' - // pos is already set from above - break; - case -1: // previous scaler from one specified in 'name' - pos--; - if(pos < 0) pos = myScalerList.size() - 1; - break; - case +1: // next scaler from one specified in 'name' - pos = (pos + 1) % myScalerList.size(); - break; + w = myOSystem->desktopWidth(); + h = myOSystem->desktopHeight(); } - scaler = *(myScalerList[pos]); + myCurrentModeList = &myFullscreenModeList; + myCurrentModeList->setByResolution(w, h); } else { - // Otherwise, get the largest scaler that's available - scaler = *(myScalerList[myScalerList.size()-1]); + // Point the modelist to windowed modes, and set the iterator to + // the mode closest to the given zoom level + EventHandler::State state = myOSystem->eventHandler().state(); + bool inTIAMode = (state == EventHandler::S_EMULATE || + state == EventHandler::S_PAUSE || + state == EventHandler::S_MENU || + state == EventHandler::S_CMDMENU); + int zoom = (inTIAMode ? myOSystem->settings().getInt("zoom_tia") : + myOSystem->settings().getInt("zoom_ui") ); + + myCurrentModeList = &myWindowedModeList; + myCurrentModeList->setByZoom(zoom); } + + return myCurrentModeList->current(); } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const string& FrameBuffer::currentScalerName() -{ - EventHandler::State state = myOSystem->eventHandler().state(); - bool inTIAMode = (state == EventHandler::S_EMULATE || - state == EventHandler::S_PAUSE || - state == EventHandler::S_MENU || - state == EventHandler::S_CMDMENU); - - return (inTIAMode ? - myOSystem->settings().getString("scale_tia") : - myOSystem->settings().getString("scale_ui") ); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Scaler FrameBuffer::ourScalers[kScalerListSize] = { - { kZOOM1X, "Zoom1x", "zoom1x", 1 }, - { kZOOM2X, "Zoom2x", "zoom2x", 2 }, - { kZOOM3X, "Zoom3x", "zoom3x", 3 }, - { kZOOM4X, "Zoom4x", "zoom4x", 4 }, - { kZOOM5X, "Zoom5x", "zoom5x", 5 }, - { kZOOM6X, "Zoom6x", "zoom6x", 6 } -}; diff --git a/stella/src/emucore/FrameBuffer.hxx b/stella/src/emucore/FrameBuffer.hxx index 947315687..ea6cd9999 100644 --- a/stella/src/emucore/FrameBuffer.hxx +++ b/stella/src/emucore/FrameBuffer.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBuffer.hxx,v 1.86 2007-01-30 17:13:10 stephena Exp $ +// $Id: FrameBuffer.hxx,v 1.87 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_HXX @@ -28,6 +28,7 @@ #include "MediaSrc.hxx" #include "Font.hxx" #include "GuiUtils.hxx" +#include "VideoModeList.hxx" class OSystem; class Console; @@ -76,14 +77,6 @@ enum { kNumColors }; -// Different types of scalers available -enum ScalerType { kZOOM1X, kZOOM2X, kZOOM3X, kZOOM4X, kZOOM5X, kZOOM6X }; -struct Scaler { - ScalerType type; - const char* name; - const char* comparitor; - int zoom; -}; /** This class encapsulates the MediaSource and is the basis for the video @@ -93,10 +86,12 @@ struct Scaler { All GUI elements (ala ScummVM) are drawn here as well. @author Stephen Anthony - @version $Id: FrameBuffer.hxx,v 1.86 2007-01-30 17:13:10 stephena Exp $ + @version $Id: FrameBuffer.hxx,v 1.87 2007-06-20 16:33:22 stephena Exp $ */ class FrameBuffer { + friend class TiaOutputWidget; + public: /** Creates a new Frame Buffer @@ -115,10 +110,8 @@ class FrameBuffer @param title The title of the window @param width The width of the framebuffer @param height The height of the framebuffer - @param aspect Whether to use the aspect ratio setting */ - void initialize(const string& title, uInt32 width, uInt32 height, - bool aspect = true); + void initialize(const string& title, uInt32 width, uInt32 height); /** Updates the display, which depending on the current mode could mean @@ -193,16 +186,14 @@ class FrameBuffer void setFullscreen(bool enable); /** - This method is called when the user wants to scale the window, in effect - changing to another scaler. - direction = -1 means window should go to the next lower scaler - direction = 0 means window should be created with the given scaler - direction = +1 means window should go to the next higher scaler + This method is called when the user wants to switch to the next available + video mode (functionality depends on fullscreen or windowed mode). + direction = -1 means go to the next lower video mode + direction = +1 means go to the next higher video mode @param direction Described above - @param type The scaler to use if direction is set to 0 */ - bool scale(int direction, const string& type = "zoom1x"); + bool changeVidMode(int direction); /** Sets the state of the cursor (hidden or grabbed) based on the @@ -287,73 +278,18 @@ class FrameBuffer */ void drawString(const GUI::Font* font, const string& str, int x, int y, int w, int color, TextAlignment align = kTextAlignLeft, - int deltax = 0, bool useEllipsis = true); + int deltax = 0, bool useEllipsis = true); /** Informs the Framebuffer of a change in EventHandler state. */ virtual void stateChanged(EventHandler::State state) { } + ////////////////////////////////////////////////////////////////////// + // The following methods are system-specific and must be implemented + // in derived classes. + ////////////////////////////////////////////////////////////////////// public: - ////////////////////////////////////////////////////////////////////// - // The following methods are system-specific and must be implemented - // in derived classes. - ////////////////////////////////////////////////////////////////////// - /** - This method is called to initialize the subsystem-specific video mode - with the given scaler. - */ - virtual bool initSubsystem() = 0; - - /** - This method is called to query the type of the FrameBuffer. - */ - virtual BufferType type() = 0; - - /** - This method is called to provide information about the FrameBuffer. - */ - virtual string about() = 0; - - /** - This method is called to set the aspect ratio of the screen. - */ - virtual void setAspectRatio() = 0; - - /** - This method is called to change to the given scaler type. - - @param scaler The scaler to use for rendering the mediasource - */ - virtual void setScaler(Scaler scaler) = 0; - - /** - This method is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - virtual bool createScreen() = 0; - - /** - Switches between the filtering options in the video subsystem. - */ - virtual void toggleFilter() = 0; - - /** - This method should be called anytime the MediaSource needs to be redrawn - to the screen. - */ - virtual void drawMediaSource() = 0; - - /** - This method is called before any drawing is done (per-frame). - */ - virtual void preFrameUpdate() = 0; - - /** - This method is called after any drawing is done (per-frame). - */ - virtual void postFrameUpdate() = 0; - /** This method is called to get the specified scanline data. @@ -362,15 +298,6 @@ class FrameBuffer */ virtual void scanline(uInt32 row, uInt8* data) = 0; - /** - This method is called to map a given r,g,b triple to the screen palette. - - @param r The red component of the color. - @param g The green component of the color. - @param b The blue component of the color. - */ - virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) = 0; - /** This method should be called to draw a horizontal line. @@ -434,7 +361,7 @@ class FrameBuffer @param x X coordinate to translate @param y Y coordinate to translate */ - virtual void translateCoords(Int32* x, Int32* y) = 0; + virtual void translateCoords(Int32& x, Int32& y) = 0; /** This method should be called to add a dirty rectangle @@ -452,6 +379,62 @@ class FrameBuffer */ virtual void enablePhosphor(bool enable, int blend) = 0; + /** + This method is called to query the type of the FrameBuffer. + */ + virtual BufferType type() = 0; + + protected: + /** + This method is called to initialize the video subsystem + with the given video mode. Normally, it will also call setVidMode(). + */ + virtual bool initSubsystem(VideoMode mode) = 0; + + /** + This method is called to change to the given video mode. + + @param mode The video mode to use for rendering the mediasource + */ + virtual bool setVidMode(VideoMode mode) = 0; + + /** + Switches between the filtering options in the video subsystem. + */ + virtual void toggleFilter() = 0; + + /** + This method should be called anytime the MediaSource needs to be redrawn + to the screen. + */ + virtual void drawMediaSource() = 0; + + /** + This method is called before any drawing is done (per-frame). + */ + virtual void preFrameUpdate() = 0; + + /** + This method is called after any drawing is done (per-frame). + */ + virtual void postFrameUpdate() = 0; + + /** + This method is called to map a given r,g,b triple to the screen palette. + + @param r The red component of the color. + @param g The green component of the color. + @param b The blue component of the color. + */ + virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) = 0; + + /** + This method is called to provide information about the FrameBuffer. + */ + virtual string about() = 0; + + + protected: // The parent system for the framebuffer OSystem* myOSystem; @@ -468,9 +451,6 @@ class FrameBuffer // Dimensions of the SDL window (not always the same as the image) SDL_Rect myScreenDim; - // Dimensions of the desktop area - SDL_Rect myDesktopDim; - // The SDL video buffer SDL_Surface* myScreen; @@ -481,9 +461,6 @@ class FrameBuffer Uint32 myDefPalette[256+kNumColors]; Uint32 myAvgPalette[256][256]; - // The aspect ratio of the window - float theAspectRatio; - // Indicates if the TIA area should be redrawn bool theRedrawTIAIndicator; @@ -515,34 +492,24 @@ class FrameBuffer uInt8 getPhosphor(uInt8 c1, uInt8 c2); /** - Calculate the maximum window size that the current screen can hold. - If not supported by platform, always return 4. + Calculate the maximum level by which the base window can be zoomed and + still fit in the given screen dimensions. */ - int maxWindowSizeForScreen(); + uInt32 maxWindowSizeForScreen(uInt32 screenWidth, uInt32 screenHeight); /** - Set the scalers available for this framebuffer based on current emulation - state and maximum window size. + Set all possible video modes (both windowed and fullscreen) available for + this framebuffer based on current emulation state and maximum window size. */ - void setAvailableScalers(); + void setAvailableVidModes(); /** - Returns a scaler based on the current eventhandler mode and the value of direction. - If there's any error, default to 'zoom1x'. - direction = -1 means previous scaler based on value in 'name' - direction = 0 means actual scaler based on value in 'name' - direction = +1 means next scaler based on value in 'name' + Returns an appropriate video mode based on the current eventhandler + state, taking into account the maximum size of the window. - @param scaler The reference to store the scaler we're looking for - @param direction Described above - @param name The name of the scaler + @return A valid VideoMode for this framebuffer */ - void getScaler(Scaler& scaler, int direction, const string& name); - - /** - Get the current scaler based on the eventhandler mode. - */ - const string& currentScalerName(); + VideoMode getSavedVidMode(); private: // Indicates the number of times the framebuffer was initialized @@ -560,14 +527,10 @@ class FrameBuffer }; Message myMessage; - // The various scalers available in TIA vs. non-TIA mode - // For the foreseeable future, the UI scalers will be restricted - // from using the more advanced scalers - enum { kScalerListSize = 6 }; - static Scaler ourScalers[kScalerListSize]; - - // The list of scalers available in the current mode - Common::Array myScalerList; + // The list of all available video modes for this framebuffer + VideoModeList myWindowedModeList; + VideoModeList myFullscreenModeList; + VideoModeList* myCurrentModeList; }; #endif diff --git a/stella/src/emucore/OSystem.cxx b/stella/src/emucore/OSystem.cxx index cd30c044e..26d5e82ea 100644 --- a/stella/src/emucore/OSystem.cxx +++ b/stella/src/emucore/OSystem.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OSystem.cxx,v 1.96 2007-02-06 23:34:33 stephena Exp $ +// $Id: OSystem.cxx,v 1.97 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -65,6 +65,22 @@ OSystem::OSystem() myFont(NULL), myConsoleFont(NULL) { +#ifdef DISPLAY_OPENGL + myFeatures += "OpenGL "; +#endif +#ifdef SOUND_SUPPORT + myFeatures += "Sound "; +#endif +#ifdef JOYSTICK_SUPPORT + myFeatures += "Joystick "; +#endif +#ifdef DEBUGGER_SUPPORT + myFeatures += "Debugger "; +#endif +#ifdef CHEATCODE_SUPPORT + myFeatures += "Cheats"; +#endif + #if 0 // Debugging info for the GUI widgets cerr << " kStaticTextWidget = " << kStaticTextWidget << endl; @@ -131,6 +147,11 @@ OSystem::~OSystem() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool OSystem::create() { + // Get relevant information about the video hardware + // This must be done before any graphics context is created, since + // it may be needed to initialize the size of graphical objects + queryVideoHardware(); + // Create fonts to draw text myFont = new GUI::Font(GUI::stellaDesc); myLauncherFont = new GUI::Font(GUI::stellaDesc); // FIXME @@ -161,23 +182,6 @@ bool OSystem::create() // that only have a single sound device (no hardware mixing) createSound(); - // Determine which features were conditionally compiled into Stella -#ifdef DISPLAY_OPENGL - myFeatures += "OpenGL "; -#endif -#ifdef SOUND_SUPPORT - myFeatures += "Sound "; -#endif -#ifdef JOYSTICK_SUPPORT - myFeatures += "Joystick "; -#endif -#ifdef DEBUGGER_SUPPORT - myFeatures += "Debugger "; -#endif -#ifdef CHEATCODE_SUPPORT - myFeatures += "Cheats"; -#endif - return true; } @@ -498,9 +502,11 @@ bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size) if(!in) return false; - *image = new uInt8[512 * 1024]; - in.read((char*)(*image), 512 * 1024); - *size = in.gcount(); + in.seekg(0, ios::end); + *size = (int)in.tellg(); + in.seekg(0, ios::beg); + *image = new uInt8[*size]; + in.read((char*)(*image), *size); in.close(); } @@ -702,6 +708,45 @@ void OSystem::mainLoop() } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void OSystem::queryVideoHardware() +{ + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) + return; + + // First get the maximum windowed desktop resolution + const SDL_VideoInfo* info = SDL_GetVideoInfo(); + myDesktopWidth = info->current_w; + myDesktopHeight = info->current_h; + + // Then get the valid fullscreen modes + // If there are any errors, just use the desktop resolution + ostringstream buf; + SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); + if((modes == (SDL_Rect**)0) || (modes == (SDL_Rect**)-1)) + { + Resolution r; + r.width = myDesktopWidth; + r.height = myDesktopHeight; + buf << r.width << "x" << r.height; + r.name = buf.str(); + myResolutions.push_back(r); + } + else + { + for(uInt32 i = 0; modes[i]; ++i) + { + Resolution r; + r.width = modes[i]->w; + r.height = modes[i]->h; + buf.str(""); + buf << r.width << "x" << r.height; + r.name = buf.str(); + myResolutions.insert_at(0, r); // insert in opposite (of descending) order + } + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* Palette is defined as follows: diff --git a/stella/src/emucore/OSystem.hxx b/stella/src/emucore/OSystem.hxx index 53c7e316a..8281085c6 100644 --- a/stella/src/emucore/OSystem.hxx +++ b/stella/src/emucore/OSystem.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OSystem.hxx,v 1.51 2007-01-01 18:04:49 stephena Exp $ +// $Id: OSystem.hxx,v 1.52 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifndef OSYSTEM_HXX @@ -28,16 +28,22 @@ class Debugger; class CheatManager; class VideoDialog; +#include "Array.hxx" #include "EventHandler.hxx" #include "FrameBuffer.hxx" #include "Sound.hxx" #include "Settings.hxx" #include "Console.hxx" -#include "StringList.hxx" #include "Font.hxx" #include "bspf.hxx" +struct Resolution { + uInt32 width; + uInt32 height; + string name; +}; +typedef Common::Array ResolutionList; /** This class provides an interface for accessing operating system specific @@ -45,7 +51,7 @@ class VideoDialog; other objects belong. @author Stephen Anthony - @version $Id: OSystem.hxx,v 1.51 2007-01-01 18:04:49 stephena Exp $ + @version $Id: OSystem.hxx,v 1.52 2007-06-20 16:33:22 stephena Exp $ */ class OSystem { @@ -193,6 +199,19 @@ class OSystem */ inline uInt32 frameRate() const { return myDisplayFrameRate; } + /** + Get the maximum dimensions of a window for the video hardware. + */ + const uInt32 desktopWidth() const { return myDesktopWidth; } + const uInt32 desktopHeight() const { return myDesktopHeight; } + + /** + Get the supported fullscreen resolutions for the video hardware. + + @return An array of supported resolutions + */ + const ResolutionList& supportedResolutions() const { return myResolutions; } + /** Return the default directory for storing data. */ @@ -303,11 +322,6 @@ class OSystem */ virtual uInt32 getTicks() = 0; - /** - This method queries the dimensions of the screen for the given device. - */ - virtual void getScreenDimensions(int& width, int& height) = 0; - ////////////////////////////////////////////////////////////////////// // The following methods are system-specific and can be overrided in // derived classes. Otherwise, the base methods will be used. @@ -355,6 +369,12 @@ class OSystem */ virtual void stateChanged(EventHandler::State state); + protected: + /** + Query the OSystem video hardware for resolution information. + */ + virtual void queryVideoHardware(); + protected: /** Set the base directory for all Stella files @@ -415,6 +435,12 @@ class OSystem // Pointer to the CheatManager object CheatManager* myCheatManager; + // Maximum dimensions of the desktop area + uInt32 myDesktopWidth, myDesktopHeight; + + // Supported fullscreen resolutions + ResolutionList myResolutions; + // Number of times per second to iterate through the main loop uInt32 myDisplayFrameRate; @@ -456,6 +482,9 @@ class OSystem }; TimingInfo myTimingInfo; + // Capabilities for this OSystem + uInt32 myCapabilities; + // Table of RGB values for GUI elements static uInt32 ourGUIColors[kNumUIPalettes][kNumColors-256]; diff --git a/stella/src/emucore/Settings.cxx b/stella/src/emucore/Settings.cxx index 0f50a2be9..1a5476606 100644 --- a/stella/src/emucore/Settings.cxx +++ b/stella/src/emucore/Settings.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Settings.cxx,v 1.116 2007-02-06 23:34:33 stephena Exp $ +// $Id: Settings.cxx,v 1.117 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -34,17 +34,16 @@ Settings::Settings(OSystem* osystem) // Add options that are common to all versions of Stella setInternal("video", "soft"); - setInternal("dirtyrects", "false"); // This only becomes useful at high resolutions setInternal("gl_filter", "nearest"); - setInternal("gl_aspect", "2.0"); - setInternal("gl_fsmax", "true"); + setInternal("gl_fsmax", "never"); setInternal("gl_lib", "libGL.so"); setInternal("gl_vsync", "false"); - setInternal("scale_ui", "zoom2x"); - setInternal("scale_tia", "zoom2x"); + setInternal("zoom_ui", "2"); + setInternal("zoom_tia", "2"); setInternal("fullscreen", "false"); + setInternal("fullres", ""); setInternal("center", "true"); setInternal("grabmouse", "false"); setInternal("palette", "standard"); @@ -80,7 +79,7 @@ Settings::Settings(OSystem* osystem) setInternal("lastrom", ""); setInternal("modtime", ""); setInternal("debugheight", "0"); - setInternal("launchersize", "2"); + setInternal("launcherres", "320x240"); setInternal("uipalette", "0"); setInternal("autoslot", "false"); } @@ -220,9 +219,9 @@ void Settings::validate() if(s != "linear" && s != "nearest") setInternal("gl_filter", "nearest"); - float f = getFloat("gl_aspect"); - if(f < 1.1 || f > 2.0) - setInternal("gl_aspect", "2.0"); + s = getString("gl_fsmax"); + if(s != "never" && s != "ui" && s != "tia" && s != "always") + setInternal("gl_fsmax", "never"); #endif #ifdef SOUND_SUPPORT @@ -237,15 +236,13 @@ void Settings::validate() setInternal("tiafreq", "31400"); #endif - s = getString("scale_ui"); - if(s != "zoom1x" && s != "zoom2x" && s != "zoom3x" && - s != "zoom4x" && s != "zoom5x" && s != "zoom6x") - setInternal("scale_ui", "zoom1x"); + i = getInt("zoom_ui"); + if(i < 1 || i > 10) + setInternal("zoom_ui", "2"); - s = getString("scale_tia"); - if(s != "zoom1x" && s != "zoom2x" && s != "zoom3x" && - s != "zoom4x" && s != "zoom5x" && s != "zoom6x") - setInternal("scale_tia", "zoom1x"); + i = getInt("zoom_tia"); + if(i < 1 || i > 10) + setInternal("zoom_tia", "2"); i = getInt("paddle"); if(i < 0 || i > 3) @@ -283,14 +280,15 @@ void Settings::usage() << " -gl_filter Type is one of the following:\n" << " nearest Normal scaling (GL_NEAREST)\n" << " linear Blurred scaling (GL_LINEAR)\n" - << " -gl_aspect Scale the width by the given amount\n" - << " -gl_fsmax <1|0> Use the largest available screenmode in fullscreen OpenGL\n" + << " -gl_fsmax Enable synchronize to vertical blank interrupt\n" << endl #endif - << " -scale_tia Use the specified scaler in emulation mode\n" - << " -scale_ui Use the specified scaler in non-emulation mode (ROM browser/debugger)\n" + << " -zoom_tia Use the specified zoom level in emulation mode\n" + << " -zoom_ui Use the specified zoom level in non-emulation mode (ROM browser/debugger)\n" << " -fullscreen <1|0> Play the game in fullscreen mode\n" + << " -fullres The resolution to use in fullscreen mode\n" << " -center <1|0> Centers game window (if possible)\n" << " -grabmouse <1|0> Keeps the mouse in the game window\n" << " -palette Display detailed information for the given ROM\n" - << " -launchersize <1|2|3> Set the size of the ROM launcher\n" + << " -launcherres The resolution to use in ROM launcher mode\n" << " -uipalette <1|2> Used the specified palette for UI elements\n" << " -help Show the text you're now reading\n" #ifdef DEBUGGER_SUPPORT @@ -460,6 +458,16 @@ void Settings::setString(const string& key, const string& value) setExternal(key, value); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Settings::getSize(const string& key, int& x, int& y) const +{ + string size = getString(key); + replace(size.begin(), size.end(), 'x', ' '); + istringstream buf(size); + buf >> x; + buf >> y; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int Settings::getInt(const string& key) const { @@ -528,6 +536,14 @@ const string& Settings::getString(const string& key) const return EmptyString; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Settings::setSize(const string& key, const int value1, const int value2) +{ + ostringstream buf; + buf << value1 << "x" << value2; + setString(key, buf.str()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int Settings::getInternalPos(const string& key) const { diff --git a/stella/src/emucore/Settings.hxx b/stella/src/emucore/Settings.hxx index 529d1ed9b..7fa5a5e67 100644 --- a/stella/src/emucore/Settings.hxx +++ b/stella/src/emucore/Settings.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Settings.hxx,v 1.31 2007-01-01 18:04:49 stephena Exp $ +// $Id: Settings.hxx,v 1.32 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #ifndef SETTINGS_HXX @@ -28,7 +28,7 @@ class OSystem; This class provides an interface for accessing frontend specific settings. @author Stephen Anthony - @version $Id: Settings.hxx,v 1.31 2007-01-01 18:04:49 stephena Exp $ + @version $Id: Settings.hxx,v 1.32 2007-06-20 16:33:22 stephena Exp $ */ class Settings { @@ -74,7 +74,7 @@ class Settings /** Get the value assigned to the specified key. If the key does - not exist then 0 is returned. + not exist then -1 is returned. @param key The key of the setting to lookup @return The integer value of the setting @@ -108,6 +108,15 @@ class Settings */ const string& getString(const string& key) const; + /** + Get the x*y size assigned to the specified key. If the key does + not exist (or is invalid) then results are -1 for each item. + + @param key The key of the setting to lookup + @return The x and y values encoded in the key + */ + void getSize(const string& key, int& x, int& y) const; + /** Set the value associated with key to the given value. @@ -140,6 +149,14 @@ class Settings */ void setString(const string& key, const string& value); + /** + Set the value associated with key to the given value. + + @param key The key of the setting + @param value The value to assign to the setting + */ + void setSize(const string& key, const int value1, const int value2); + private: // Copy constructor isn't supported by this class so make it private Settings(const Settings&); diff --git a/stella/src/gp2x/FrameBufferGP2X.cxx b/stella/src/gp2x/FrameBufferGP2X.cxx index cf07a5817..83feccfb4 100644 --- a/stella/src/gp2x/FrameBufferGP2X.cxx +++ b/stella/src/gp2x/FrameBufferGP2X.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: FrameBufferGP2X.cxx,v 1.21 2007-01-07 17:59:52 stephena Exp $ +// $Id: FrameBufferGP2X.cxx,v 1.22 2007-06-20 16:33:22 stephena Exp $ //============================================================================ #include @@ -40,10 +40,10 @@ FrameBufferGP2X::~FrameBufferGP2X() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferGP2X::initSubsystem() +bool FrameBufferGP2X::initSubsystem(VideoMode mode) { // Create the screen - return createScreen(); + return setVidMode(mode); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -54,20 +54,7 @@ string FrameBufferGP2X::about() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGP2X::setAspectRatio() -{ - // Aspect ratio correction not yet available in software mode - theAspectRatio = 1.0; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGP2X::setScaler(Scaler scaler) -{ - // Not supported, we always use 1x zoom -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferGP2X::createScreen() +bool FrameBufferGP2X::setVidMode(VideoMode mode) { // Make sure to clear the screen, since we're using different resolutions, // and there tends to be lingering artifacts in hardware mode diff --git a/stella/src/gp2x/OSystemGP2X.cxx b/stella/src/gp2x/OSystemGP2X.cxx index 6ba231588..5643bc61c 100644 --- a/stella/src/gp2x/OSystemGP2X.cxx +++ b/stella/src/gp2x/OSystemGP2X.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OSystemGP2X.cxx,v 1.26 2007-01-24 21:36:38 stephena Exp $ +// $Id: OSystemGP2X.cxx,v 1.27 2007-06-20 16:33:22 stephena Exp $ // Modified on 2006/01/06 by Alex Zaballa for use on GP2X //============================================================================ @@ -67,6 +67,9 @@ OSystemGP2X::OSystemGP2X(const string& path) : OSystem() // Set event arrays to a known state myPreviousEvents = new uInt8[8]; memset(myPreviousEvents, 0, 8); myCurrentEvents = new uInt8[8]; memset(myCurrentEvents, 0, 8); + + // GP2X doesn't have windowed mode; it's always in fullscreen + clearCapability(CAP_WINDOWED); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/stella/src/gui/Launcher.cxx b/stella/src/gui/Launcher.cxx index 263c0b5d9..2f23bacb6 100644 --- a/stella/src/gui/Launcher.cxx +++ b/stella/src/gui/Launcher.cxx @@ -13,9 +13,11 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Launcher.cxx,v 1.14 2007-01-19 21:53:27 stephena Exp $ +// $Id: Launcher.cxx,v 1.15 2007-06-20 16:33:23 stephena Exp $ //============================================================================ +#include + #include "LauncherDialog.hxx" #include "Version.hxx" #include "OSystem.hxx" @@ -30,28 +32,16 @@ Launcher::Launcher(OSystem* osystem) myWidth(320), myHeight(240) { - int size = myOSystem->settings().getInt("launchersize"); - switch(size) - { - case 1: - myWidth = 320; - myHeight = 240; - break; - case 2: - myWidth = 400; - myHeight = 300; - break; - case 3: - myWidth = 512; - myHeight = 384; - break; - } + int w, h; + myOSystem->settings().getSize("launcherres", w, h); + myWidth = w >= 0 ? w : 0; + myHeight = h >= 0 ? h : 0; // Error check the resolution - int w, h; - osystem->getScreenDimensions(w, h); - if(myWidth > w) myWidth = w; - if(myHeight > h) myHeight = h; + if(myWidth < 320) myWidth = 320; + if(myWidth > osystem->desktopWidth()) myWidth = osystem->desktopWidth(); + if(myHeight < 240) myHeight = 240; + if(myHeight > osystem->desktopHeight()) myHeight = osystem->desktopHeight(); myBaseDialog = new LauncherDialog(myOSystem, this, 0, 0, myWidth, myHeight); } @@ -65,5 +55,5 @@ Launcher::~Launcher() void Launcher::initializeVideo() { string title = string("Stella ") + STELLA_VERSION; - myOSystem->frameBuffer().initialize(title, myWidth, myHeight, false); + myOSystem->frameBuffer().initialize(title, myWidth, myHeight); } diff --git a/stella/src/gui/Launcher.hxx b/stella/src/gui/Launcher.hxx index b82864be1..a010061f3 100644 --- a/stella/src/gui/Launcher.hxx +++ b/stella/src/gui/Launcher.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Launcher.hxx,v 1.9 2007-01-01 18:04:53 stephena Exp $ +// $Id: Launcher.hxx,v 1.10 2007-06-20 16:33:23 stephena Exp $ //============================================================================ #ifndef LAUNCHER_HXX @@ -27,7 +27,7 @@ class OSystem; The base dialog for the ROM launcher in Stella. @author Stephen Anthony - @version $Id: Launcher.hxx,v 1.9 2007-01-01 18:04:53 stephena Exp $ + @version $Id: Launcher.hxx,v 1.10 2007-06-20 16:33:23 stephena Exp $ */ class Launcher : public DialogContainer { @@ -50,8 +50,8 @@ class Launcher : public DialogContainer private: // The width and height of this dialog // These can only be changed by exiting and restarting Stella - int myWidth; - int myHeight; + uInt32 myWidth; + uInt32 myHeight; }; #endif diff --git a/stella/src/gui/OptionsDialog.cxx b/stella/src/gui/OptionsDialog.cxx index f983945c2..7e34327f7 100644 --- a/stella/src/gui/OptionsDialog.cxx +++ b/stella/src/gui/OptionsDialog.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OptionsDialog.cxx,v 1.51 2007-01-23 09:37:38 knakos Exp $ +// $Id: OptionsDialog.cxx,v 1.52 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -107,19 +107,14 @@ OptionsDialog::OptionsDialog(OSystem* osystem, DialogContainer* parent, int x = 0, y = 0, w, h; // Now create all the dialogs attached to each menu button - w = 230; h = 135; + w = 230; h = 150; myVideoDialog = new VideoDialog(myOSystem, parent, font, x, y, w, h); w = 200; h = 140; myAudioDialog = new AudioDialog(myOSystem, parent, font, x, y, w, h); - // knakos: I think this is wrong: (instantiating twice) - //w = 230; h = 185; - //myInputDialog = new InputDialog(myOSystem, parent, font, x, y, w, h); - #ifdef _WIN32_WCE - int sx, sy; - myOSystem->getScreenDimensions(sx, sy); + int sx = myOSystem->desktopWidth(); // we scale the input dialog down a bit in low res devices. // looks only a little ugly, but the functionality is very welcome if(sx < 320) { w = 220; h = 176; } @@ -129,7 +124,7 @@ OptionsDialog::OptionsDialog(OSystem* osystem, DialogContainer* parent, #endif myInputDialog = new InputDialog(myOSystem, parent, font, x, y, w, h); - w = 200; h = 90; + w = 200; h = 105; myUIDialog = new UIDialog(myOSystem, parent, font, x, y, w, h); w = 280; h = 120; diff --git a/stella/src/gui/UIDialog.cxx b/stella/src/gui/UIDialog.cxx index 58caa84c4..3b9d7101e 100644 --- a/stella/src/gui/UIDialog.cxx +++ b/stella/src/gui/UIDialog.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: UIDialog.cxx,v 1.3 2007-01-23 09:37:39 knakos Exp $ +// $Id: UIDialog.cxx,v 1.4 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -45,13 +45,33 @@ UIDialog::UIDialog(OSystem* osystem, DialogContainer* parent, xpos = 10; ypos = 10; - // Launcher size - myLauncherPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight, - "Rom launcher size: ", lwidth); - myLauncherPopup->appendEntry("320x240", 1); - myLauncherPopup->appendEntry("400x300", 2); - myLauncherPopup->appendEntry("512x384", 3); - wid.push_back(myLauncherPopup); + // Launcher width and height + myLauncherWidthSlider = new SliderWidget(this, font, xpos, ypos, pwidth, + lineHeight, "Launcher Width: ", + lwidth, kLWidthChanged); + myLauncherWidthSlider->setMinValue(320); + myLauncherWidthSlider->setMaxValue(800); + myLauncherWidthSlider->setStepValue(10); + wid.push_back(myLauncherWidthSlider); + myLauncherWidthLabel = + new StaticTextWidget(this, font, + xpos + myLauncherWidthSlider->getWidth() + 4, + ypos + 1, 15, fontHeight, "", kTextAlignLeft); + myLauncherWidthLabel->setFlags(WIDGET_CLEARBG); + ypos += lineHeight + 4; + + myLauncherHeightSlider = new SliderWidget(this, font, xpos, ypos, pwidth, + lineHeight, "Launcher Height: ", + lwidth, kLHeightChanged); + myLauncherHeightSlider->setMinValue(240); + myLauncherHeightSlider->setMaxValue(600); + myLauncherHeightSlider->setStepValue(10); + wid.push_back(myLauncherHeightSlider); + myLauncherHeightLabel = + new StaticTextWidget(this, font, + xpos + myLauncherHeightSlider->getWidth() + 4, + ypos + 1, 15, fontHeight, "", kTextAlignLeft); + myLauncherHeightLabel->setFlags(WIDGET_CLEARBG); ypos += lineHeight + 4; // UI Palette @@ -103,16 +123,21 @@ UIDialog::~UIDialog() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void UIDialog::loadConfig() { - int i; - // Launcher size - i = instance()->settings().getInt("launchersize"); - if(i < 1 || i > 3) - i = 1; - myLauncherPopup->setSelectedTag(i); + int w, h; + instance()->settings().getSize("launcherres", w, h); + if(w < 320) w = 320; + if(w > 800) w = 800; + if(h < 240) h = 240; + if(h > 600) h = 600; + + myLauncherWidthSlider->setValue(w); + myLauncherWidthLabel->setValue(w); + myLauncherHeightSlider->setValue(h); + myLauncherHeightLabel->setValue(h); // UI palette - i = instance()->settings().getInt("uipalette"); + int i = instance()->settings().getInt("uipalette"); if(i < 1 || i > 2) i = 1; myPalettePopup->setSelectedTag(i); @@ -121,26 +146,28 @@ void UIDialog::loadConfig() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void UIDialog::saveConfig() { - Settings& settings = instance()->settings(); - int i; - // Launcher size - i = myLauncherPopup->getSelectedTag(); - settings.setInt("launchersize", i); + instance()->settings().setSize("launcherres", + myLauncherWidthSlider->getValue(), myLauncherHeightSlider->getValue()); // UI palette - i = myPalettePopup->getSelectedTag(); - settings.setInt("uipalette", i); + instance()->settings().setInt("uipalette", + myPalettePopup->getSelectedTag()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void UIDialog::setDefaults() { + int w = MIN(instance()->desktopWidth(), (const uInt32) 400); + int h = MIN(instance()->desktopHeight(), (const uInt32) 300); + myLauncherWidthSlider->setValue(w); + myLauncherWidthLabel->setValue(w); + myLauncherHeightSlider->setValue(h); + myLauncherHeightLabel->setValue(h); + #if !defined (GP2X) - myLauncherPopup->setSelectedTag(2); myPalettePopup->setSelectedTag(1); #else - myLauncherPopup->setSelectedTag(1); myPalettePopup->setSelectedTag(2); #endif @@ -152,6 +179,14 @@ void UIDialog::handleCommand(CommandSender* sender, int cmd, int data, int id) { switch(cmd) { + case kLWidthChanged: + myLauncherWidthLabel->setValue(myLauncherWidthSlider->getValue()); + break; + + case kLHeightChanged: + myLauncherHeightLabel->setValue(myLauncherHeightSlider->getValue()); + break; + case kOKCmd: saveConfig(); close(); diff --git a/stella/src/gui/UIDialog.hxx b/stella/src/gui/UIDialog.hxx index ecb6b7628..74258902a 100644 --- a/stella/src/gui/UIDialog.hxx +++ b/stella/src/gui/UIDialog.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: UIDialog.hxx,v 1.2 2007-01-01 18:04:54 stephena Exp $ +// $Id: UIDialog.hxx,v 1.3 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -26,6 +26,8 @@ class CommandSender; class Dialog; class DialogContainer; class PopUpWidget; +class SliderWidget; +class StaticTextWidget; #include "OSystem.hxx" #include "bspf.hxx" @@ -38,7 +40,11 @@ class UIDialog : public Dialog ~UIDialog(); protected: - PopUpWidget* myLauncherPopup; + SliderWidget* myLauncherWidthSlider; + StaticTextWidget* myLauncherWidthLabel; + SliderWidget* myLauncherHeightSlider; + StaticTextWidget* myLauncherHeightLabel; + PopUpWidget* myPalettePopup; private: @@ -47,6 +53,11 @@ class UIDialog : public Dialog void setDefaults(); virtual void handleCommand(CommandSender* sender, int cmd, int data, int id); + + enum { + kLWidthChanged = 'UIlw', + kLHeightChanged = 'UIlh', + }; }; #endif diff --git a/stella/src/gui/VideoDialog.cxx b/stella/src/gui/VideoDialog.cxx index fe89d4f73..e9978047c 100644 --- a/stella/src/gui/VideoDialog.cxx +++ b/stella/src/gui/VideoDialog.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: VideoDialog.cxx,v 1.42 2007-01-23 09:37:39 knakos Exp $ +// $Id: VideoDialog.cxx,v 1.43 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -66,16 +66,14 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, wid.push_back(myFilterPopup); ypos += lineHeight + 4; - // Aspect ratio - myAspectRatioSlider = new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight, - "GL Aspect: ", lwidth, kAspectRatioChanged); - myAspectRatioSlider->setMinValue(1); myAspectRatioSlider->setMaxValue(100); - wid.push_back(myAspectRatioSlider); - myAspectRatioLabel = new StaticTextWidget(this, font, - xpos + myAspectRatioSlider->getWidth() + 4, - ypos + 1, - 15, fontHeight, "", kTextAlignLeft); - myAspectRatioLabel->setFlags(WIDGET_CLEARBG); + // GL FS stretch + myFSStretchPopup = new PopUpWidget(this, font, xpos, ypos, + pwidth, lineHeight, "GL Stretch: ", lwidth); + myFSStretchPopup->appendEntry("Never", 1); + myFSStretchPopup->appendEntry("UI mode", 2); + myFSStretchPopup->appendEntry("TIA mode", 3); + myFSStretchPopup->appendEntry("Always", 4); + wid.push_back(myFSStretchPopup); ypos += lineHeight + 4; // Palette @@ -88,27 +86,28 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, wid.push_back(myPalettePopup); ypos += lineHeight + 4; - // Available TIA scalers - myTIAScalerPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, - lineHeight, "TIA Scaler: ", lwidth); - myTIAScalerPopup->appendEntry("Zoom1x", 1); - myTIAScalerPopup->appendEntry("Zoom2x", 2); - myTIAScalerPopup->appendEntry("Zoom3x", 3); - myTIAScalerPopup->appendEntry("Zoom4x", 4); - myTIAScalerPopup->appendEntry("Zoom5x", 5); - myTIAScalerPopup->appendEntry("Zoom6x", 6); - wid.push_back(myTIAScalerPopup); - + // Available UI zoom levels + myUIZoomSlider = new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight, + "UI Zoom: ", lwidth, kUIZoomChanged); + myUIZoomSlider->setMinValue(1); myUIZoomSlider->setMaxValue(10); + wid.push_back(myUIZoomSlider); + myUIZoomLabel = new StaticTextWidget(this, font, + xpos + myUIZoomSlider->getWidth() + 4, + ypos + 1, + 15, fontHeight, "", kTextAlignLeft); + myUIZoomLabel->setFlags(WIDGET_CLEARBG); ypos += lineHeight + 4; - myUIScalerPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, - lineHeight, "UI Scaler: ", lwidth); - myUIScalerPopup->appendEntry("Zoom1x", 1); - myUIScalerPopup->appendEntry("Zoom2x", 2); - myUIScalerPopup->appendEntry("Zoom3x", 3); - myUIScalerPopup->appendEntry("Zoom4x", 4); - myUIScalerPopup->appendEntry("Zoom5x", 5); - myUIScalerPopup->appendEntry("Zoom6x", 6); - wid.push_back(myUIScalerPopup); + + // Available TIA zoom levels + myTIAZoomSlider = new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight, + "TIA Zoom: ", lwidth, kTIAZoomChanged); + myTIAZoomSlider->setMinValue(1); myTIAZoomSlider->setMaxValue(10); + wid.push_back(myTIAZoomSlider); + myTIAZoomLabel = new StaticTextWidget(this, font, + xpos + myTIAZoomSlider->getWidth() + 4, + ypos + 1, + 15, fontHeight, "", kTextAlignLeft); + myTIAZoomLabel->setFlags(WIDGET_CLEARBG); // Move over to the next column xpos += 115; ypos = 10; @@ -127,7 +126,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, // Fullscreen myFullscreenCheckbox = new CheckboxWidget(this, font, xpos + 5, ypos, - "Fullscreen mode"); + "Fullscreen mode", kFullScrChanged); wid.push_back(myFullscreenCheckbox); ypos += lineHeight + 4; @@ -137,23 +136,23 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, wid.push_back(myColorLossCheckbox); ypos += lineHeight + 4; - // Use dirty rectangle merging - myDirtyRectCheckbox = new CheckboxWidget(this, font, xpos + 5, ypos, - "Dirty-rect merging"); - wid.push_back(myDirtyRectCheckbox); - ypos += lineHeight + 4; - - // Use desktop res in OpenGL - myUseDeskResCheckbox = new CheckboxWidget(this, font, xpos + 5, ypos, - "Desktop Res in FS"); - wid.push_back(myUseDeskResCheckbox); - ypos += lineHeight + 4; - // Use sync to vblank in OpenGL myUseVSyncCheckbox = new CheckboxWidget(this, font, xpos + 5, ypos, "GL VSync"); wid.push_back(myUseVSyncCheckbox); - ypos += lineHeight + 20; + ypos += lineHeight + 4; + + // Center window (in windowed mode) + myCenterCheckbox = new CheckboxWidget(this, font, xpos + 5, ypos, + "Center window (*)"); + wid.push_back(myCenterCheckbox); + ypos += lineHeight + 4; + + // Add message concerning usage + lwidth = font.getStringWidth("(*) Requires application restart"); + new StaticTextWidget(this, font, 10, _h - 38, lwidth, fontHeight, + "(*) Requires application restart", + kTextAlignLeft); // Add Defaults, OK and Cancel buttons ButtonWidget* b; @@ -177,11 +176,19 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent, addToFocusList(wid); -#ifdef _WIN32_WCE - myTIAScalerPopup->clearFlags(WIDGET_ENABLED); - myUIScalerPopup->clearFlags(WIDGET_ENABLED); + // Disable certain functions when we know they aren't present +#ifndef DISPLAY_GL + myFilterPopup->clearFlags(WIDGET_ENABLED); + myFSStretchPopup->clearFlags(WIDGET_ENABLED); + myUseVSyncCheckbox->clearFlags(WIDGET_ENABLED); +#endif +#ifndef WINDOWED_SUPPORT + myUIZoomSlider->clearFlags(WIDGET_ENABLED); + myUIZoomLabel->clearFlags(WIDGET_ENABLED); + myTIAZoomSlider->clearFlags(WIDGET_ENABLED); + myTIAZoomLabel->clearFlags(WIDGET_ENABLED); myFullscreenCheckbox->clearFlags(WIDGET_ENABLED); - myDirtyRectCheckbox->clearFlags(WIDGET_ENABLED); + myCenterCheckbox->clearFlags(WIDGET_ENABLED); #endif } @@ -196,7 +203,6 @@ void VideoDialog::loadConfig() string s; bool b; int i; - double f; // Renderer setting s = instance()->settings().getString("video"); @@ -208,14 +214,13 @@ void VideoDialog::loadConfig() if(s == "linear") myFilterPopup->setSelectedTag(1); else if(s == "nearest") myFilterPopup->setSelectedTag(2); - // Aspect ratio - another huge hack - s = instance()->settings().getString("gl_aspect"); - f = instance()->settings().getFloat("gl_aspect"); - if(f < 1.1) { f = 1.1; s = "1.1"; } - else if(f > 2.0) { f = 2.0; s = "2.0"; } - i = (int)((f * 10) - 10) * 10; - myAspectRatioSlider->setValue(i); - myAspectRatioLabel->setLabel(s); + // GL stretch setting + s = instance()->settings().getString("gl_fsmax"); + if(s == "never") myFSStretchPopup->setSelectedTag(1); + else if(s == "ui") myFSStretchPopup->setSelectedTag(2); + else if(s == "tia") myFSStretchPopup->setSelectedTag(3); + else if(s == "always") myFSStretchPopup->setSelectedTag(4); + else myFSStretchPopup->setSelectedTag(1); // Palette s = instance()->settings().getString("palette"); @@ -224,25 +229,17 @@ void VideoDialog::loadConfig() else if(s == "z26") myPalettePopup->setSelectedTag(3); else if(s == "user") myPalettePopup->setSelectedTag(4); - // TIA Scaler - s = instance()->settings().getString("scale_tia"); - if(s == "zoom1x") myTIAScalerPopup->setSelectedTag(1); - else if(s == "zoom2x") myTIAScalerPopup->setSelectedTag(2); - else if(s == "zoom3x") myTIAScalerPopup->setSelectedTag(3); - else if(s == "zoom4x") myTIAScalerPopup->setSelectedTag(4); - else if(s == "zoom5x") myTIAScalerPopup->setSelectedTag(5); - else if(s == "zoom6x") myTIAScalerPopup->setSelectedTag(6); - else myTIAScalerPopup->setSelectedTag(0); + // UI zoom level + s = instance()->settings().getString("zoom_ui"); + i = instance()->settings().getInt("zoom_ui"); + myUIZoomSlider->setValue(i); + myUIZoomLabel->setLabel(s); - // UI Scaler - s = instance()->settings().getString("scale_ui"); - if(s == "zoom1x") myUIScalerPopup->setSelectedTag(1); - else if(s == "zoom2x") myUIScalerPopup->setSelectedTag(2); - else if(s == "zoom3x") myUIScalerPopup->setSelectedTag(3); - else if(s == "zoom4x") myUIScalerPopup->setSelectedTag(4); - else if(s == "zoom5x") myUIScalerPopup->setSelectedTag(5); - else if(s == "zoom6x") myUIScalerPopup->setSelectedTag(6); - else myUIScalerPopup->setSelectedTag(0); + // TIA zoom level + s = instance()->settings().getString("zoom_tia"); + i = instance()->settings().getInt("zoom_tia"); + myTIAZoomSlider->setValue(i); + myTIAZoomLabel->setLabel(s); // FIXME - what to do with this?? myFrameRateSlider->setEnabled(false); @@ -250,23 +247,20 @@ void VideoDialog::loadConfig() // Fullscreen b = instance()->settings().getBool("fullscreen"); myFullscreenCheckbox->setState(b); + handleFullscreenChange(b); // PAL color-loss effect b = instance()->settings().getBool("colorloss"); myColorLossCheckbox->setState(b); - // Dirty-rect merging (software mode only) - b = instance()->settings().getBool("dirtyrects"); - myDirtyRectCheckbox->setState(b); - - // Use desktop resolution in fullscreen mode (GL mode only) - b = instance()->settings().getBool("gl_fsmax"); - myUseDeskResCheckbox->setState(b); - // Use sync to vertical blank (GL mode only) b = instance()->settings().getBool("gl_vsync"); myUseVSyncCheckbox->setState(b); + // Center window + b = instance()->settings().getBool("center"); + myCenterCheckbox->setState(b); + // Make sure that mutually-exclusive items are not enabled at the same time i = myRendererPopup->getSelectedTag(); handleRendererChange(i); @@ -291,9 +285,19 @@ void VideoDialog::saveConfig() else if(i == 2) s = "nearest"; instance()->settings().setString("gl_filter", s); + // GL stretch setting + i = myFSStretchPopup->getSelectedTag(); + if(i == 1) s = "never"; + else if(i == 2) s = "ui"; + else if(i == 3) s = "tia"; + else if(i == 4) s = "always"; + instance()->settings().setString("gl_fsmax", s); + +/* // Aspect ratio s = myAspectRatioLabel->getLabel(); instance()->settings().setString("gl_aspect", s); +*/ // Palette i = myPalettePopup->getSelectedTag(); @@ -303,25 +307,13 @@ void VideoDialog::saveConfig() else if(i == 4) s = "user"; instance()->settings().setString("palette", s); - // TIA Scaler - i = myTIAScalerPopup->getSelectedTag(); - if(i == 1) s = "zoom1x"; - else if(i == 2) s = "zoom2x"; - else if(i == 3) s = "zoom3x"; - else if(i == 4) s = "zoom4x"; - else if(i == 5) s = "zoom5x"; - else if(i == 6) s = "zoom6x"; - instance()->settings().setString("scale_tia", s); - // UI Scaler - i = myUIScalerPopup->getSelectedTag(); - if(i == 1) s = "zoom1x"; - else if(i == 2) s = "zoom2x"; - else if(i == 3) s = "zoom3x"; - else if(i == 4) s = "zoom4x"; - else if(i == 5) s = "zoom5x"; - else if(i == 6) s = "zoom6x"; - instance()->settings().setString("scale_ui", s); + s = myUIZoomLabel->getLabel(); + instance()->settings().setString("zoom_ui", s); + + // TIA Scaler + s = myTIAZoomLabel->getLabel(); + instance()->settings().setString("zoom_tia", s); // Framerate FIXME - I haven't figured out what to do with this yet /* @@ -338,18 +330,14 @@ void VideoDialog::saveConfig() b = myColorLossCheckbox->getState(); instance()->settings().setBool("colorloss", b); - // Dirty rectangle merging (software mode only) - b = myDirtyRectCheckbox->getState(); - instance()->settings().setBool("dirtyrects", b); - - // Use desktop resolution in fullscreen mode (GL mode only) - b = myUseDeskResCheckbox->getState(); - instance()->settings().setBool("gl_fsmax", b); - // Use sync to vertical blank (GL mode only) b = myUseVSyncCheckbox->getState(); instance()->settings().setBool("gl_vsync", b); + // Center window + b = myCenterCheckbox->getState(); + instance()->settings().setBool("center", b); + // Finally, issue a complete framebuffer re-initialization instance()->createFrameBuffer(false); } @@ -359,43 +347,52 @@ void VideoDialog::setDefaults() { myRendererPopup->setSelectedTag(1); myFilterPopup->setSelectedTag(1); + myFSStretchPopup->setSelectedTag(1); myPalettePopup->setSelectedTag(1); - myTIAScalerPopup->setSelectedTag(2); - myUIScalerPopup->setSelectedTag(2); + myUIZoomSlider->setValue(2); + myUIZoomLabel->setLabel("2"); + myTIAZoomSlider->setValue(2); + myTIAZoomLabel->setLabel("2"); // myFrameRateSlider->setValue(0); // myFrameRateLabel->setLabel("0"); - // For some unknown reason (ie, a bug), slider widgets can only - // take certain ranges of numbers. So we have to fudge things ... - myAspectRatioSlider->setValue(100); - myAspectRatioLabel->setLabel("2.0"); - myFullscreenCheckbox->setState(false); myColorLossCheckbox->setState(false); - myDirtyRectCheckbox->setState(false); - myUseDeskResCheckbox->setState(true); myUseVSyncCheckbox->setState(true); + myCenterCheckbox->setState(true); // Make sure that mutually-exclusive items are not enabled at the same time handleRendererChange(1); // 1 indicates software mode + handleFullscreenChange(false); // indicates fullscreen deactivated } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void VideoDialog::handleRendererChange(int item) { +#ifdef DISPLAY_OPENGL // When we're in software mode, certain OpenGL-related options are disabled bool gl = (item > 1) ? true : false; myFilterPopup->setEnabled(gl); - myAspectRatioSlider->setEnabled(gl); - myAspectRatioLabel->setEnabled(gl); - myUseDeskResCheckbox->setEnabled(gl); + myFSStretchPopup->setEnabled(gl); + myFSStretchPopup->setEnabled(gl); myUseVSyncCheckbox->setEnabled(gl); - // Also, in OpenGL mode, certain software related items are disabled - myDirtyRectCheckbox->setEnabled(!gl); + _dirty = true; +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void VideoDialog::handleFullscreenChange(bool enable) +{ +#ifdef WINDOWED_SUPPORT + myUIZoomSlider->setEnabled(!enable); + myUIZoomLabel->setEnabled(!enable); + myTIAZoomSlider->setEnabled(!enable); + myTIAZoomLabel->setEnabled(!enable); _dirty = true; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -417,23 +414,22 @@ void VideoDialog::handleCommand(CommandSender* sender, int cmd, handleRendererChange(data); break; - case kAspectRatioChanged: - { - // This is terribly dirty, but what can we do? - float ratio = (((myAspectRatioSlider->getValue() + 9) / 10) / 10.0) + 1.0; - ostringstream r; - if(ratio == 2.0) - r << ratio << ".0"; - else - r << ratio; - myAspectRatioLabel->setLabel(r.str()); + case kUIZoomChanged: + myUIZoomLabel->setValue(myUIZoomSlider->getValue()); + break; + + case kTIAZoomChanged: + myTIAZoomLabel->setValue(myTIAZoomSlider->getValue()); break; - } case kFrameRateChanged: myFrameRateLabel->setValue(myFrameRateSlider->getValue()); break; + case kFullScrChanged: + handleFullscreenChange(myFullscreenCheckbox->getState()); + break; + default: Dialog::handleCommand(sender, cmd, data, 0); break; diff --git a/stella/src/gui/VideoDialog.hxx b/stella/src/gui/VideoDialog.hxx index 998c3af63..1f4e40351 100644 --- a/stella/src/gui/VideoDialog.hxx +++ b/stella/src/gui/VideoDialog.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: VideoDialog.hxx,v 1.19 2007-01-01 18:04:55 stephena Exp $ +// $Id: VideoDialog.hxx,v 1.20 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -46,29 +46,32 @@ class VideoDialog : public Dialog void setDefaults(); void handleRendererChange(int item); + void handleFullscreenChange(bool enable); virtual void handleCommand(CommandSender* sender, int cmd, int data, int id); private: PopUpWidget* myRendererPopup; PopUpWidget* myFilterPopup; - SliderWidget* myAspectRatioSlider; - StaticTextWidget* myAspectRatioLabel; + PopUpWidget* myFSStretchPopup; PopUpWidget* myPalettePopup; - PopUpWidget* myTIAScalerPopup; - PopUpWidget* myUIScalerPopup; + SliderWidget* myUIZoomSlider; + StaticTextWidget* myUIZoomLabel; + SliderWidget* myTIAZoomSlider; + StaticTextWidget* myTIAZoomLabel; SliderWidget* myFrameRateSlider; StaticTextWidget* myFrameRateLabel; CheckboxWidget* myFullscreenCheckbox; CheckboxWidget* myColorLossCheckbox; - CheckboxWidget* myDirtyRectCheckbox; - CheckboxWidget* myUseDeskResCheckbox; CheckboxWidget* myUseVSyncCheckbox; + CheckboxWidget* myCenterCheckbox; enum { - kRendererChanged = 'VDrd', - kAspectRatioChanged = 'VDar', - kFrameRateChanged = 'VDfr' + kRendererChanged = 'VDrd', + kUIZoomChanged = 'VDui', + kTIAZoomChanged = 'VDti', + kFrameRateChanged = 'VDfr', + kFullScrChanged = 'VDfs' }; }; diff --git a/stella/src/gui/Widget.cxx b/stella/src/gui/Widget.cxx index 18187adff..843067ad4 100644 --- a/stella/src/gui/Widget.cxx +++ b/stella/src/gui/Widget.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Widget.cxx,v 1.48 2007-01-15 13:51:55 stephena Exp $ +// $Id: Widget.cxx,v 1.49 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -525,10 +525,8 @@ void SliderWidget::setValue(int value) << ", max = " << _valueMax << ", min = " << _valueMin << endl;*/ - if(value < _valueMin) - value = _valueMin; - else if(value > _valueMax) - value = _valueMax; + if(value < _valueMin) value = _valueMin; + else if(value > _valueMax) value = _valueMax; if(value != _value) { @@ -542,14 +540,18 @@ void SliderWidget::setValue(int value) void SliderWidget::setMinValue(int value) { _valueMin = value; -// _stepValue = (int) ((_valueMax - _valueMin) * 0.05); // Step at 5% intervals } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void SliderWidget::setMaxValue(int value) { _valueMax = value; -// _stepValue = (int) ((_valueMax - _valueMin) * 0.05); // Step at 5% intervals +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void SliderWidget::setStepValue(int value) +{ + _stepValue = value; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -637,11 +639,17 @@ void SliderWidget::drawWidget(bool hilite) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int SliderWidget::valueToPos(int value) { + if(value < _valueMin) value = _valueMin; + else if(value > _valueMax) value = _valueMax; + return ((_w - _labelWidth - 4) * (value - _valueMin) / (_valueMax - _valueMin)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int SliderWidget::posToValue(int pos) { - return (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - 4) + _valueMin; + int value = (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - 4) + _valueMin; + + // Scale the position to the correct interval (according to step value) + return value - (value % _stepValue); } diff --git a/stella/src/gui/Widget.hxx b/stella/src/gui/Widget.hxx index 261b018c3..b6b1ecea9 100644 --- a/stella/src/gui/Widget.hxx +++ b/stella/src/gui/Widget.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Widget.hxx,v 1.53 2007-01-01 18:04:55 stephena Exp $ +// $Id: Widget.hxx,v 1.54 2007-06-20 16:33:23 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -88,7 +88,7 @@ enum { This is the base class for all widgets. @author Stephen Anthony - @version $Id: Widget.hxx,v 1.53 2007-01-01 18:04:55 stephena Exp $ + @version $Id: Widget.hxx,v 1.54 2007-06-20 16:33:23 stephena Exp $ */ class Widget : public GuiObject { @@ -289,6 +289,8 @@ class SliderWidget : public ButtonWidget int getMinValue() const { return _valueMin; } void setMaxValue(int value); int getMaxValue() const { return _valueMax; } + void setStepValue(int value); + int getStepValue() const { return _stepValue; } virtual void handleMouseMoved(int x, int y, int button); virtual void handleMouseDown(int x, int y, int button, int clickCount); diff --git a/stella/src/psp/FSNodePSP.cxx b/stella/src/psp/FSNodePSP.cxx deleted file mode 100644 index 5e1fb45d6..000000000 --- a/stella/src/psp/FSNodePSP.cxx +++ /dev/null @@ -1,285 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: FSNodePSP.cxx,v 1.3 2007-01-01 18:04:55 stephena Exp $ -// -// Based on code from ScummVM - Scumm Interpreter -// Copyright (C) 2002-2004 The ScummVM project -//============================================================================ - -#include "FSNode.hxx" - -#include -#include -#include -#include -#include - -/* - * Implementation of the Stella file system API based on POSIX for PSP - */ - -class PSPFilesystemNode : public AbstractFilesystemNode -{ - public: - PSPFilesystemNode(); - PSPFilesystemNode(const string& path); - PSPFilesystemNode(const PSPFilesystemNode* node); - - virtual string displayName() const { return _displayName; } - virtual bool isValid() const { return _isValid; } - virtual bool isDirectory() const { return _isDirectory; } - virtual string path() const { return _path; } - - virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const; - virtual AbstractFilesystemNode* parent() const; - static void stripTailingSlashes(char * buf); - protected: - string _displayName; - bool _isDirectory; - bool _isValid; - string _path; -}; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static const char* lastPathComponent(const string& str) -{ - const char *start = str.c_str(); - const char *cur = start + str.size() - 2; - - while (cur > start && *cur != '/') - --cur; - - return cur+1; -} - -static void stripTailingSlashes(char * buf) -{ - char * ptr; - ptr = buf + strlen(buf)-1; - while(*(ptr)=='/') *(ptr--)='\0'; -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static string validatePath(const string& p) -{ - string path = p; - if(p.size() <= 0 || p[0] == '/') - path = "ms0:/"; - - return path; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AbstractFilesystemNode* FilesystemNode::getRoot() -{ - return new PSPFilesystemNode(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AbstractFilesystemNode* FilesystemNode::getNodeForPath(const string& path) -{ - return new PSPFilesystemNode(validatePath(path)); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PSPFilesystemNode::PSPFilesystemNode() -{ - const char buf[] = "ms0:/stella/"; - _path = buf; - _displayName = string("stella"); - _isValid = true; - _isDirectory = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PSPFilesystemNode::PSPFilesystemNode(const string& p) -{ - string path = validatePath(p); - - Int32 len = 0, offset = path.size(); - SceIoStat st; - - _path = path; - - // Extract last component from path - const char *str = path.c_str(); - while (offset > 0 && str[offset-1] == '/') - offset--; - while (offset > 0 && str[offset-1] != '/') - { - len++; - offset--; - } - _displayName = string(str + offset, len); - - // Check whether it is a directory, and whether the file actually exists - //_isValid = (0 == stat(_path.c_str(), &st)); - //_isDirectory = S_ISDIR(st.st_mode); - _isValid = (0 == sceIoGetstat(_path.c_str(), &st)); - _isDirectory = FIO_S_ISDIR(st.st_mode); - -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PSPFilesystemNode::PSPFilesystemNode(const PSPFilesystemNode* node) -{ - _displayName = node->_displayName; - _isValid = node->_isValid; - _isDirectory = node->_isDirectory; - _path = node->_path; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FSList PSPFilesystemNode::listDir(ListMode mode) const -{ -// assert(_isDirectory); - FSList myList; - SceUID dfd = sceIoDopen (_path.c_str()); - SceIoDirent *dp; - dp = (SceIoDirent*)malloc(sizeof(SceIoDirent)); -#ifdef PSP_DEBUG - fprintf(stdout,"PSPFilesystemNode::listDir: dir='%s'\n",_path.c_str()); -#endif - - if (!dfd){ -#ifdef PSP_DEBUG - fprintf(stdout,"PSPFilesystemNode::listDir: no dir handle\n"); -#endif - return myList; - } - - while (sceIoDread(dfd,dp) > 0){ - - if (dp->d_name[0]=='.') - continue; - - PSPFilesystemNode entry; - entry._displayName = dp->d_name; - entry._path = _path; - if (entry._path.length() > 0 && entry._path[entry._path.length()-1] != '/') - entry._path += "/"; - - entry._path += dp->d_name; - entry._isDirectory = dp->d_stat.st_attr & FIO_SO_IFDIR; - - // Honor the chosen mode - if ((mode == kListFilesOnly && entry._isDirectory) || - (mode == kListDirectoriesOnly && !entry._isDirectory)) - continue; - - if (entry._isDirectory) - entry._path += "/"; - - myList.push_back(wrap(new PSPFilesystemNode(&entry))); - } - sceIoDclose(dfd); - free(dp); - return myList; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AbstractFilesystemNode *PSPFilesystemNode::parent() const -{ - if (_path == "/") - return 0; - - PSPFilesystemNode* p = new PSPFilesystemNode(); - const char *start = _path.c_str(); - const char *end = lastPathComponent(_path); - - p->_path = string(start, end - start); - p->_displayName = lastPathComponent(p->_path); - - p->_isValid = true; - p->_isDirectory = true; - - return p; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool AbstractFilesystemNode::fileExists(const string& path) -{ - SceIoStat st; -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::fileExists '%s'\n",path.c_str()); -#endif - if(sceIoGetstat(path.c_str(), &st) != 0){ -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::fileExists error \n"); -#endif - return false; - } -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::fileExists return '%i'\n", !FIO_SO_ISREG(st.st_mode)); -#endif - return !FIO_SO_ISREG(st.st_mode); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool AbstractFilesystemNode::dirExists(const string& in) -{ - char tmp_buf[1024]; - strncpy(tmp_buf,in.c_str(),1023); - stripTailingSlashes(tmp_buf); - string path = (char*)tmp_buf; -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::dirExists '%s'\n", path.c_str()); -#endif - SceIoStat st; - if(sceIoGetstat(path.c_str(), &st) != 0) - return false; -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::dirExists return '%i'\n", !FIO_SO_ISDIR(st.st_mode)); -#endif - return !FIO_SO_ISDIR(st.st_mode); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool AbstractFilesystemNode::makeDir(const string& path) -{ - return sceIoMkdir(path.c_str(), 0777) == 0; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string AbstractFilesystemNode::modTime(const string& in) -{ - char tmp_buf[1024]; - strncpy(tmp_buf,in.c_str(),1023); - stripTailingSlashes(tmp_buf); - string path = (char*)tmp_buf; - SceIoStat st; -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::modTime '%s'\n",path.c_str()); -#endif - - if(sceIoGetstat(path.c_str(), &st) < 0){ -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::modTime returns error\n"); -#endif - return ""; - } - ostringstream buf; - buf << (unsigned short)st.st_mtime.year - << (unsigned short)st.st_mtime.month - << (unsigned short)st.st_mtime.day - << (unsigned short)st.st_mtime.hour - << (unsigned short)st.st_mtime.minute - << (unsigned short)st.st_mtime.second; - -#ifdef PSP_DEBUG - fprintf(stdout,"AbstractFilesystemNode::modTime returns '%s'\n",buf.str().c_str()); -#endif - return buf.str(); -} diff --git a/stella/src/psp/FrameBufferPSP.cxx b/stella/src/psp/FrameBufferPSP.cxx deleted file mode 100644 index db149fdc7..000000000 --- a/stella/src/psp/FrameBufferPSP.cxx +++ /dev/null @@ -1,119 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: FrameBufferPSP.cxx,v 1.3 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#include -#include -#include - -#include "Console.hxx" -#include "FrameBufferPSP.hxx" -#include "MediaSrc.hxx" -#include "Settings.hxx" -#include "OSystem.hxx" -#include "Font.hxx" -#include "GuiUtils.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameBufferPSP::FrameBufferPSP(OSystem* osystem) - : FrameBufferSoft(osystem) -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameBufferPSP::~FrameBufferPSP() -{ - delete myRectList; - delete myOverlayRectList; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferPSP::initSubsystem() -{ - // Set up the rectangle list to be used in the dirty update - delete myRectList; - myRectList = new RectList(); - delete myOverlayRectList; - myOverlayRectList = new RectList(); - -#ifdef PSP_DEBUG - fprintf(stdout, "FrameBufferPSP::initSubsystem\n"); -#endif - if(!myRectList || !myOverlayRectList) - { - cerr << "ERROR: Unable to get memory for SDL rects" << endl; - return false; - } - - // Create the screen - if(!createScreen()) - return false; - - // Show some info - if(myOSystem->settings().getBool("showinfo")) - cout << "Video rendering: Software mode" << endl << endl; - - // Precompute the GUI palette - // We abuse the concept of 'enum' by referring directly to the integer values - for(uInt8 i = 0; i < kNumColors-256; i++) - myPalette[i+256] = mapRGB(ourGUIColors[i][0], ourGUIColors[i][1], ourGUIColors[i][2]); - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferPSP::createScreen() -{ - myScreenDim.x = myScreenDim.y = 0; - - myScreenDim.w = myBaseDim.w; - myScreenDim.h = myBaseDim.h; - - // In software mode, the image and screen dimensions are always the same - myImageDim = myScreenDim; - if (mySDLFlags & SDL_HWSURFACE ) - { - /* double buff is broken */ - mySDLFlags = SDL_HWSURFACE; - myScreenDim.w = myDesktopDim.w; - myScreenDim.h = myDesktopDim.w; -#ifdef PSP_DEBUG - fprintf(stdout, "FrameBufferPSP::createScreen Hardware Mode " - "myScreenDim.w='%i' myScreenDim.h='%i'\n", - myScreenDim.w,myScreenDim.h); -#endif -} - else -{ -#ifdef PSP_DEBUG - fprintf(stdout, "FrameBufferPSP::createScreen Software Mode " - "myScreenDim.w='%i' myScreenDim.h='%i'\n", - myScreenDim.w,myScreenDim.h); -#endif -} - - myScreen = SDL_SetVideoMode(myScreenDim.w, myScreenDim.h, 0, mySDLFlags); - if(myScreen == NULL) - { - fprintf(stdout,"ERROR: Unable to open SDL window: %s\n",SDL_GetError()); - return false; - } - myOSystem->eventHandler().refreshDisplay(); - - return true; -} - diff --git a/stella/src/psp/FrameBufferPSP.hxx b/stella/src/psp/FrameBufferPSP.hxx deleted file mode 100644 index 080c73015..000000000 --- a/stella/src/psp/FrameBufferPSP.hxx +++ /dev/null @@ -1,73 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: FrameBufferPSP.hxx,v 1.4 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#ifndef FRAMEBUFFER_PSP_HXX -#define FRAMEBUFFER_PSP_HXX - - - -#include "Font.hxx" -#include "bspf.hxx" -#include "GuiUtils.hxx" -#include "FrameBufferSoft.hxx" - - -/** - This class implements an SDL software framebuffer. - - @author Stephen Anthony - @version $Id: FrameBufferPSP.hxx,v 1.4 2007-01-01 18:04:55 stephena Exp $ -*/ -class FrameBufferPSP : public FrameBufferSoft -{ - public: - /** - Creates a new software framebuffer - */ - FrameBufferPSP(OSystem* osystem); - - /** - Destructor - */ - virtual ~FrameBufferPSP(); - - ////////////////////////////////////////////////////////////////////// - // The following methods are derived from FrameBuffer.hxx - ////////////////////////////////////////////////////////////////////// - /** - This method is called to initialize software video mode. - Return false if any operation fails, otherwise return true. - */ - virtual bool initSubsystem(); - - /** - This method is called to query the type of the FrameBuffer. - */ - virtual BufferType type() { return kSoftBuffer; } - - /** - This method is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - virtual bool createScreen(); - - -}; - - -#endif diff --git a/stella/src/psp/OSystemPSP.cxx b/stella/src/psp/OSystemPSP.cxx deleted file mode 100644 index 39b64a8c8..000000000 --- a/stella/src/psp/OSystemPSP.cxx +++ /dev/null @@ -1,172 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: OSystemPSP.cxx,v 1.8 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "bspf.hxx" -#include "OSystem.hxx" -#include "OSystemPSP.hxx" - -#ifdef HAVE_GETTIMEOFDAY - #include - #include -#endif - - -/** - Each derived class is responsible for calling the following methods - in its constructor: - - setBaseDir() - setStateDir() - setPropertiesFiles() - setConfigFiles() - setCacheFile() - - And for initializing the following variables: - - myDriverList (a StringList) - - See OSystem.hxx for a further explanation -*/ - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -OSystemPSP::OSystemPSP(const string& path) : OSystem() -{ - // First set variables that the OSystem needs - string basedir = string("ms0:/stella"); - setBaseDir(basedir); - - string statedir = basedir + "/state"; - setStateDir(statedir); - - string userPropertiesFile = basedir + "/stella.pro"; - string systemPropertiesFile = "/etc/stella.pro"; - setConfigFiles(userPropertiesFile, systemPropertiesFile); - - string userConfigFile = basedir + "/stellarc"; - string systemConfigFile = "/etc/stellarc"; - setConfigFiles(userConfigFile, systemConfigFile); - - string cacheFile = basedir + "/stella.cache"; - setCacheFile(cacheFile); - -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -OSystemPSP::~OSystemPSP() -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void OSystemPSP::mainLoop() -{ - // These variables are common to both timing options - // and are needed to calculate the overall frames per second. - uInt32 frameTime = 0, numberOfFrames = 0; - - // Set up less accurate timing stuff - uInt32 startTime, virtualTime, currentTime; - - // Set the base for the timers - virtualTime = getTicks(); - frameTime = 0; - - // Overclock CPU to 333MHz - if (settings().getBool("pspoverclock")) - { - scePowerSetClockFrequency(333,333,166); - fprintf(stderr,"OSystemPSP::mainLoop overclock to 333\n"); - } - else - { - fprintf(stderr,"OSystemPSP::mainLoop NOT overclock\n"); - } - - // Main game loop - for(;;) - { - // Exit if the user wants to quit - if(myEventHandler->doQuit()) - break; - - startTime = getTicks(); - myEventHandler->poll(startTime); - myFrameBuffer->update(); - currentTime = getTicks(); - virtualTime += myTimePerFrame; - if(currentTime < virtualTime) - SDL_Delay((virtualTime - currentTime)/1000); - - currentTime = getTicks() - startTime; - frameTime += currentTime; - ++numberOfFrames; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 OSystemPSP::getTicks() -{ -#if defined(HAVE_GETTIMEOFDAY) - timeval now; - gettimeofday(&now, 0); - return (uInt32) (now.tv_sec * 1000000 + now.tv_usec); -#else - return (uInt32) SDL_GetTicks() * 1000; -#endif -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void OSystemPSP::getScreenDimensions(int& width, int& height) -{ - width = 480; - height = 272; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void OSystemPSP::setDefaultJoymap() -{ - myEventHandler->setDefaultJoyMapping(Event::TakeSnapshot, 0, 0); // Triangle - myEventHandler->setDefaultJoyMapping(Event::LoadState, 0, 1); // Circle - myEventHandler->setDefaultJoyMapping(Event::JoystickZeroFire, 0, 2); // Cross - myEventHandler->setDefaultJoyMapping(Event::SaveState, 0, 3); // Square - myEventHandler->setDefaultJoyMapping(Event::MenuMode, 0, 4); // Left trigger - myEventHandler->setDefaultJoyMapping(Event::CmdMenuMode, 0, 5); // Right trigger - myEventHandler->setDefaultJoyMapping(Event::JoystickZeroDown, 0, 6); // Down - myEventHandler->setDefaultJoyMapping(Event::JoystickZeroLeft, 0, 7); // Left - myEventHandler->setDefaultJoyMapping(Event::JoystickZeroUp, 0, 8); // Up - myEventHandler->setDefaultJoyMapping(Event::JoystickZeroRight, 0, 9); // Right - myEventHandler->setDefaultJoyMapping(Event::ConsoleSelect, 0, 10); // Select - myEventHandler->setDefaultJoyMapping(Event::ConsoleReset, 0, 11); // Start - myEventHandler->setDefaultJoyMapping(Event::NoType, 0, 12); // Home - myEventHandler->setDefaultJoyMapping(Event::NoType, 0, 13); // Hold -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void OSystemPSP::setDefaultJoyAxisMap() -{ -} diff --git a/stella/src/psp/OSystemPSP.hxx b/stella/src/psp/OSystemPSP.hxx deleted file mode 100644 index aa163c114..000000000 --- a/stella/src/psp/OSystemPSP.hxx +++ /dev/null @@ -1,86 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: OSystemPSP.hxx,v 1.7 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#ifndef OSYSTEM_PSP_HXX -#define OSYSTEM_PSP_HXX - -#include "bspf.hxx" - - -/** - This class defines PSP-specific settings. - - @author Stephen Anthony - @version $Id: OSystemPSP.hxx,v 1.7 2007-01-01 18:04:55 stephena Exp $ -*/ -class OSystemPSP : public OSystem -{ - public: - /** - Create a new PSP-specific operating system object - */ - OSystemPSP(const string& path); - - /** - Destructor - */ - virtual ~OSystemPSP(); - - public: - /** - This method runs the main loop. Since different platforms - may use different timing methods and/or algorithms, this method has - been abstracted to each platform. - */ - void mainLoop(); - - /** - This method returns number of ticks in microseconds. - - @return Current time in microseconds. - */ - uInt32 getTicks(); - - /** - This method determines the default mapping of joystick buttons to - Stella events for the PSP device. - */ - void setDefaultJoymap(); - - /** - This method determines the default mapping of joystick axis to - Stella events for for the PSP device. - */ - void setDefaultJoyAxisMap(); - - /** - This method queries the dimensions of the screen for this hardware. - */ - virtual void getScreenDimensions(int& width, int& height); -}; - -// FIXME - this doesn't even compile any more ... - -/* - kJDirUp = 8, kJDirUpLeft = -1, - kJDirLeft = 7, kJDirDownLeft = -2, - kJDirDown = 6, kJDirDownRight = -3, - kJDirRight = 9, kJDirUpRight = -4 -*/ - -#endif diff --git a/stella/src/psp/SettingsPSP.cxx b/stella/src/psp/SettingsPSP.cxx deleted file mode 100644 index 66d692307..000000000 --- a/stella/src/psp/SettingsPSP.cxx +++ /dev/null @@ -1,39 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: SettingsPSP.cxx,v 1.6 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#include "bspf.hxx" -#include "Settings.hxx" -#include "SettingsPSP.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SettingsPSP::SettingsPSP(OSystem* osystem) - : Settings(osystem) -{ - set("accurate", "false"); - set("zoom", "1"); - set("romdir", "ms0:/stella/roms/"); - set("ssdir", "ms0:/stella/snapshots/"); - set("sound", "true"); - set("pspoverclock", "false"); - set("joymouse", "true"); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SettingsPSP::~SettingsPSP() -{ -} diff --git a/stella/src/psp/SettingsPSP.hxx b/stella/src/psp/SettingsPSP.hxx deleted file mode 100644 index 95027db95..000000000 --- a/stella/src/psp/SettingsPSP.hxx +++ /dev/null @@ -1,46 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: SettingsPSP.hxx,v 1.3 2007-01-01 18:04:55 stephena Exp $ -//============================================================================ - -#ifndef SETTINGS_PSP_HXX -#define SETTINGS_PSP_HXX - -class OSystem; - -#include "bspf.hxx" - -/** - This class defines PSP-like OS's (Linux) system specific settings. - - @author Stephen Anthony - @version $Id: SettingsPSP.hxx,v 1.3 2007-01-01 18:04:55 stephena Exp $ -*/ -class SettingsPSP : public Settings -{ - public: - /** - Create a new PSP settings object - */ - SettingsPSP(OSystem* osystem); - - /** - Destructor - */ - virtual ~SettingsPSP(); -}; - -#endif diff --git a/stella/src/psp/data/ICON0.PNG b/stella/src/psp/data/ICON0.PNG deleted file mode 100644 index a8cdf9e5407ce184a7e1aa8dd348f65808f0bdb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6916 zcmaKQbyO5z*#FSoASErRbPFgA(%nddgw%r6(#=XEAky89l;qN}beD99G)PIk^F6=c zzwdj_ot-mx_RidUKk+=zd{I}G!^5V+1^@t0L0(1^Jja8FIwm^!dsTIh3!bnb@&=y) z0Egti0|`jWpa1}D6+3BZb#+^3H|Ni`&JcP9X=!?htFyJ8!$$z{TFTV&)K9#ACbo9{ zkw8~!;j}izAqZe(s=yp1G{__T3Ydoy5=-=j!LW5n$%r!Kb$WwmvE?&in>nwVZ6qUM zhEY03LdSF~#SMPmCr3wj^LK4~8GH9*&~bu5B4U<=-MmkLux8jhkp;RqHTmA5_CRDZ z`E^u!!d*nS4O$-noT5Oz{M~BA?~$sV0b~F&%lU$MVB;yIMV& z6)@8Y(qu=5cLO;U5{#Uv@G7K0gGOFCao-g7wl+n4uff9lLA#&EXw*cOe93J|J}pDdZ~?98D+ z29RppJMBw9086@n?b@v|-!fK?A@+p#Z4^}KUV=6&SAFmu&2wxVoFit7+;;A*D6uEL z4d?YJ|10qBqAeT?RdNuM#GF9{Mr0AYz&M6Uo*IkFXfDtHHL!qwA4aOd*f;1}fLto| zDZyBt>72urR5->+;$QsoAU$v7ePo*)Pf}2w2FeVwyTs2DI;zP2oX?YP`~=2<-}3Ay zsp@G(0~_;-CzXD?y8(WYv6g|8Jowhpi{?hr5wd1q^r=_|o>}hb3+ToPSkmaf@gSpy zXPKX1zCex&Bss)12_@`y2#$}J(^uA)E@3`oswZy2)WZr1<>`LOm{_7#&+JWn+v{Z^ zQX8-TzJleLftDeQ+2kcYiD{zz3;dz4+p5?M%<;SmI12Xi%kmrrS_NWLgmxLqEC=z9 zL#x}Eb%7Q1DG5#S1o3KL?}mD|n@C0aV4KQVc`q~zl86-rK8o+47H0EGrRJ1rjFx82 zV3xr|vJDj+b3D|!v?5AL_}$Z(N7J84BMLb3XD8C^Z0xRWx%S;!uzrQm%BD=DOxgd& z*x%a^`Rj>`7mgP)AaC)C(w@?XvY4WYk}FlY2yHqgbx@moo|ih+<~zZ6XB~akD^_2! zgkj=jk!0Ou|Kz#H$Mrd!G|7pRaA%)K3}1@T{NrvwVx*Wd5-~Tl! zcj@>y|C0RA$=t~#-5YLxOu=|c2Oc5I(~)?~-}c|_nkP>~H%f>Qwk-+0w)v9^`7fJU zjw5Wg1iZ+_tSg+KTU79`eq6DCXKrD~yIjy)(6`<3dNj#C&AMf>e%fthWRQKOVxeN_ zFjjar!#>NrW3g@dJ|?VCHlt55N-%2R+tN2*1ll*1Z`T#yb;Wd{x|h216$*1k2kZxq zb9xTy)OzW-=_2Wf#*)hP%3hCj$2HPT(zw@CuP?QnmUs^D53vu^4$+TIe?Je!u*R!Fv$^D#pAwXWF6ptWI@kk-pax9K4 zcFOifB=1liw@fumU*@N`rms{kI?ntW5$*FUzf<8%ryery5j~OodwQ+E=`ymFS)5+n zeG$LMW#VCCU~<<^tM(&_^y9y=eH5{NG30FT?1+w{4#qXn z4krIO|5SgB`>eaS{}2yxH=P^M^JizOXl6)VNIOXFK~zCMQ0!6YP*YJ0F$B;pFGkx0ZY>HtcDWWW$)5WQNrLxJOu&Y3GPy!^bh#2>qRlZk6{( z8Zlw(at^R&Iy=f#@>@UB*Q~r{oPRXxwcK3XuG!}*whiuV+GEe+N~3?iUH;VQ>S0sy z$wAY>@-V$Qu(_-`$SdMT_AG_ijpPN^(UncMz^UTeptD-B{gdSEMEEIQCG}a%4z;PH z;;LJ&_ovI)(fZNhQ4|4lfuj#2jTOu1xq|su-kAZ$2;GOu@}`bOmZiDQU!Fb}_#rf8 zZ&y!}4%-^oy!7nbW&JLlrjCPu#Zg&Mw{-6M|7w==RO`!%^C|SfxRO0N_~hp7uu|RG zS%>AQDHua5F9O*~4|HBiKT7j#fEcpkcT4Vcy<98u_1RMYojcaE(lff28qjo!c_0sE zn-b}Cui(gc%n{9PKlvS~=diajV#>I4e_C zTy!RN1$wP~pK5QcVcc#bJ)R{_?slx)WO_YxvAP#$eA)*2%Xn?E%-p0@DOx2e=MQ<< zkgxvWeDLuaaisUI%I&Yp!Q07`>41Nq;Ip6SZ{|6Tv!U%L{fE>|PAtEkH#4_f-gsOc zk$YkvE3X)P$9YxXcPu^p!T3k;C!qeZ_9NVJ{l*M#Hr``EaDtB&k@cALG##-qB{w2B z%k-WpCN7hXjv9}u{oHCSaih4dm}Gp|U-t=m@VR_%?P2pyXpJw} zj(IAZ56m`EZ6sAB0iZV4_f8`byr#F5*Hi%jZ)N}p3;}@KC-A%v0PfrX@W%`QM7{$6 zx${@kzV`q?$eHm~HGem-=08XO$3g&&QVF#<*48%U_1rJR=Y(*&FeJ4ZbpMYuXN^s)Im!{_)eobnGY^2E!EO81-!kzi65si(F=bO;;Z7e(HU^OWzDMC8B zIPfMD2M690lZw=LTp0;eMtWo-!XQkT1gZ=@ArcWR5cB_a{{JpyMz9m?3N>R&9qF2! zoYZfIFpy%S?T%$0oTc_`06pBz0Z(Gj@iFY|80jS#6C{eK8Tj}}J~dF~=H>>+wfkIi zRvG<7PAfV)YFc}=p@Q6=@0664A6D|=EYHq{ zadq5m&XP>T(!8y)n`Y_l?L9pa&Qx7#gOd39`FWkL2Mt{BxC$3&z6RU2t8jOBcYIn} z^vFCoH?D13|8%jMsW(a_5dol18U|i$g%F5ISXkuAd6&dn z{;VsCg56`kVr9kF(b17tPylD+AjmH$*lopYZe=9{k=)qWfJH?~%gUl@vyu^$kldR5 zjv`~zm8jIUoGsH)FQ3K4!Pye0rl5ego^;$z$@czIziv^HfkK6c5eU)V_^hnh<721J zfhv=BDh>_~a6olGCMhv-@Y0fDi8d=YZb)7pqkiRFAhzpIqZ-3Xb=Kr~#7@quPZ@#W z+Y-KhMUKqN(PkaU3^AtwrzT&hY0FOqwob;3v5{0h4B+&1qvxpz&utx!1U^sbIetNL z@z!L%Tq5VhzS}ud`dbwh6?qjE8XK6Whe!7YB&V_xk259eq>n15zZ~-k@ro21kT5k} z`%A8FVZrF?>I!aKqtma*fjByhLU_4eH8KFwad%_1u&Ahe-z~4S6wA@kv3lexuhsK} zk)0hE+@UsjF?Ol;a9r=Si3Ai11p>jv;o{??SHeN2BLznAbU-$w0QhM78*_dt(^8F% ztD|Lb-+F@4aAa^`Kh|ClW0~mq&Smh;;M0jcpFyS*_s0!4r%zTqoBQA#lvm<1ol4Q% zzNA`A!mna!Yirwm(zy0XgmMG%Sf& za&feo-}2!gZ6!2Fbu0847O34i=Rw(kkly^xkDFLt#z`xxwHeQXqiu^7!pyG@7Y9d1 zLX9{hq;f$7=$uyDu*zru@ZgDLs)7iIhKJ|X)+TU8@YOs_E-vmjjEQ98i)4;5z5kk; z`o*}_<4milXCrXY0bB}ohHCM=-G7t94nwAjK{u>?rl6o8uc#tQi@`m^S{E^brn{TH0qFQ_b zOCTjBJrztIweDGM_d%oF1OM{-;ySq7W8bIb4fk!}K>E%TsDk{oN6$6Wl8|d)6%h4=_Tpb$NHY{Pijh^q*K1Tn{ zlt3sYEzOXEKmtGr78e(9{!Xj8+~2qub^6n2mXd;7M$Tmd$`Lw2{S}92BumsM!^`7j zHKwAXBJf@7_RIneewo`kw z($EFm^XOI@q9rSag@>cxKi-{y3?>x)13R+?6%IWkefBJ`vC(5{kB*X3(!_+8ikg~; ziYn5%La2S3`PQ?>>6ggN%*-kB{YC^G zed3Ubevc+8TJiL2k)juWsXUKYuz)SbJua#-E5dY6roSR;*9188~lKigB!v3LA zIUXi74n5oK3xb*#78dF^I#7c44I&kBc`$G3=B9m$W>~2U286_yYEp%oxeX0uc6N5n z-j}Q`t*y6#Dn~H~rXApr1(JjBE%$T+12OS6}ja)c45F}mr=BCAS5w}5U8B0q`NC5w; zOtTT^3ub2Xr^g4-r`uqlBbl2s;G&~E8K;kigY4 zS5Z)a3V0D!8vH;$2&T!Yya|!jSXjXbQHAblJfGN z$A>#K>iJ{N#_4?-O-*8KZ0z7=>c=(uZ2ZzHEfvth{u~_K?$C!x1w}?ilCx%Ep}j~P z0z>QZhQB!U95pN~>=hT+p7H&~-Z&1vpexb<$l%U1wC-Nt+np5P@k?DwQW7eMDcdXL zuR~ThJ1M4+n^W`qvjMuB{o-tOb#-`DX-5aG{ai&?p;8Kc3Stt>LYS4lVR~Gor>A#u zD(QiT)M@IZR|KIW}1M3ft%UIP90xzz!lsa+)7R@%-W< zAvqZ>st-9#ilO->p*_1v8wC~?7RZkf!^-}J1quLDm1t;mG)%P^ zr({EDIi$!=qm?%pNsso;*C=L2Mn+q$Osl*bDHRpG^Yiog#Ke%pLkG~NU%Y(DBrLpm zFtj_K&806Pm>xr3ydYoZqnY+*%V%deIry3C^3swSH-!wBwd+UI7B>Zm5EBJtpVPVX zL69Q63l0x_HJOx@bjrh63zg0*s@WsI^}j!p+uqqR-{=k-1ZOm9yZuWheH0yZjp%6H z``f+jU>pyE9*jCx&^eqs?$gq!d~Y@)?$5tW0NIG0?r=Qn`&|YwT?2C{i{(Z~SZwTW zam>}#6}iZH^SqXFad}0BOhAD6U+;&4I&ri(iSae{s+5@MX!xfF^tOfpnqw)am1d%N z8qjG%i;7s{u7$en8w^L%1e7K^8o+EqvQ(Q}Sg^hhzrXhdo{FMTzrqD4W86Jai-INexmVW{V_vwRp`Zwy zw3YGnd}G8(zPYtktSe!JZCSIYw4RM7&dF4pn;XQX=778`^JdP9SCTb(v-b;0qTV4}N=^#}y*Ee>9;Ou}$N&xcr zw?-VAMlF^D$*%=aaANP>k5`1nMg{bztM62{Y3-TZL_Rlh^{IxfJo_#DdG|DEMIyL1 z=sQctl#?$uEM1r7Ew&5vrLeGYjXpalHd`kr+LYwqS@37_`Rs=EFuKvl$2(^*%3m;o z62mc^Nw+7+eJfvt?X;|-qm%sGo`i|s$$Uw#0^zJ!Zc;t)_N1-n`W+7l2hK_7li1So zvWA}CK$H!L^gZN|_W3qdVO14=a&q!_0sC+3brDV{%Y+2}^|KdNeKBA1nw z4R*wE&L1C62EXX0aG9b5lU;}*cruTmVEDg(-f`|jOmeZ9iV&QlhplNGlt&dZF!K=d zxvGf38RU+hw?Y2tgNYs1-NOd@Be81MyD#9N?BoqGKB}0BgG;daRt!*o-W%INY!soW z-mKWxgJlGmkI=t-i9sscWyXEF9Q_=BAauTKQDEab#W?n5yh@v%hPW>kB!7GL(+r8s5XpJS6B4sENW{gDJdPS ztX{As4&8KuMcx%y3|O|dwi=`$`XWDCS%tPkHjqE)>h8Lj1O&W({rWHrhkPL+fvD@_ z;*m$^&i3Bk4=@B;Vqjn_NEVg>0$|P9;4dq05l{*|(1dx__OEhId>? z(%+09d?xzER0Nh$i5{PsiLp9YHexUcXis-wm$2Q@^nsI{OLv-MreilKW12XN_bMEG zy;;+;3+N?*<4^a_MUi8+doml2J}x!Te}7+#h=>$zQ%YQ)tjXEf?BV>3B_EoYm`D=E zl}aK~`M)a4|F!b$VjD01K`ZY6{Ht^Xer|I!1#r`5i`+#$?u_*htRd-Lo@i#kDmK5R zC9R&ci)}?MskpFkW2RK=KYBk0#}Uwp_1a|ny*Gq$Gk_5t2-GT81rcALtNakZ(&8TV zgm)7kPw4x2yH`+%X(H^hiR$g+V_vHdIhf;k=l75F=?!oZRRqH${J05zSO64cRb{HA H%!2<18Pq}U diff --git a/stella/src/psp/data/PIC1.PNG b/stella/src/psp/data/PIC1.PNG deleted file mode 100644 index 7841d36e5a7a99111428f0272cfff6719401444d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157058 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 Q1Oh%+%&wVM8#~AS52^uTqW}N^ diff --git a/stella/src/psp/data/stellarc.psp b/stella/src/psp/data/stellarc.psp deleted file mode 100644 index e46207b11..000000000 --- a/stella/src/psp/data/stellarc.psp +++ /dev/null @@ -1,41 +0,0 @@ -; Stella configuration file -; -; Lines starting with ';' are comments and are ignored. -; Spaces and tabs are ignored. -; -; Format MUST be as follows: -; command = value -; -; Commmands are the same as those specified on the commandline, -; without the '-' character. -; -; Values are the same as those allowed on the commandline. -; Boolean values are specified as 1 (or true) and 0 (or false) -; -video = soft -video_driver = -gl_filter = nearest -gl_aspect = 2.0 -gl_fsmax = false -zoom = 1 -fullscreen = false -grabmouse = 1 -center = true -palette = standard -debugheight = 20 -sound = 1 -fragsize = 1024 -volume = 100 -keymap = -joymap = -paddle = 0 -showinfo = false -ssdir = ms0:/stella/snapshots/ -ssname = romname -sssingle = false -romdir = ms0:/stella/roms/ -lastrom = -modtime = -accurate = false -break = -pspoverclock = false \ No newline at end of file diff --git a/stella/src/psp/module.mk b/stella/src/psp/module.mk deleted file mode 100644 index 15261f84a..000000000 --- a/stella/src/psp/module.mk +++ /dev/null @@ -1,12 +0,0 @@ -MODULE := src/psp - -MODULE_OBJS := \ - src/psp/FSNodePSP.o \ - src/psp/OSystemPSP.o \ - src/psp/SettingsPSP.o \ - src/psp/FrameBufferPSP.o -MODULE_DIRS += \ - src/psp - -# Include common rules -include $(srcdir)/common.rules diff --git a/stella/src/psp/pspstdint.h b/stella/src/psp/pspstdint.h deleted file mode 100644 index 32620ddee..000000000 --- a/stella/src/psp/pspstdint.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SCE CONFIDENTIAL - PSP(TM) Programmer Tool Runtime Library Release 1.5.0 - * - * Copyright (C) 2005 Sony Computer Entertainment Inc. - * All Rights Reserved. - * - */ -/* - * - * PSP(TM) integer types - * - * pspstdint.h - * - * Version Date Design Log - * -------------------------------------------------------------------- - * 0.00 2005-01-19 kono the first version - */ - - -#ifndef _SCE_PSPSTDINT_H -#define _SCE_PSPSTDINT_H - -/* Exact-width integer types */ -#ifndef _SCE_PSPSTDINT_int8_t_DEFINED -#define _SCE_PSPSTDINT_int8_t_DEFINED -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef int int32_t; -typedef unsigned int uint32_t; -#if defined(__GNUC__) -__extension__ typedef long long int64_t __attribute__((mode(DI))); -__extension__ typedef unsigned long long uint64_t __attribute__((mode(DI))); -#else /* defined(__GNUC__) */ -typedef long long int64_t; -typedef unsigned long long uint64_t; -#endif /* defined(__GNUC__) */ -#endif /* _SCE_PSPSTDINT_int8_t_DEFINED */ - - -/* Minimum-width integer types */ -#ifndef _SCE_PSPSTDINT_int_least8_t_DEFINED -#define _SCE_PSPSTDINT_int_least8_t_DEFINED -typedef signed char int_least8_t; -typedef unsigned char uint_least8_t; -typedef short int_least16_t; -typedef unsigned short uint_least16_t; -typedef int int_least32_t; -typedef unsigned int uint_least32_t; -#if defined(__GNUC__) -__extension__ typedef long long int_least64_t __attribute__((mode(DI))); -__extension__ typedef unsigned long long uint_least64_t __attribute__((mode(DI))); -#else /* defined(__GNUC__) */ -typedef long long int_least64_t; -typedef unsigned long long uint_least64_t; -#endif /* defined(__GNUC__) */ -#endif /* _SCE_PSPSTDINT_int_least8_t_DEFINED */ - - -/* Fastest minimum-width integer types */ -#ifndef _SCE_PSPSTDINT_int_fast8_t_DEFINED -#define _SCE_PSPSTDINT_int_fast8_t_DEFINED -typedef char int_fast8_t; -typedef unsigned char uint_fast8_t; -typedef int int_fast16_t; -typedef unsigned int uint_fast16_t; -typedef int int_fast32_t; -typedef unsigned int uint_fast32_t; -#if defined(__GNUC__) -__extension__ typedef long long int_fast64_t __attribute__((mode(DI))); -__extension__ typedef unsigned long long uint_fast64_t __attribute__((mode(DI))); -#else /* defined(__GNUC__) */ -typedef long long int_fast64_t; -typedef unsigned long long uint_fast64_t; -#endif /* defined(__GNUC__) */ -#endif /* _SCE_PSPSTDINT_int_fast8_t_DEFINED */ - - -/* Integer types capable of holding object pointers */ -#ifndef _SCE_PSPSTDINT_intptr_t_DEFINED -#define _SCE_PSPSTDINT_intptr_t_DEFINED -typedef int intptr_t; -typedef unsigned int uintptr_t; -#endif /* _SCE_PSPSTDINT_intptr_t_DEFINED */ - - -/* Gereat-width integer types */ -#ifndef _SCE_PSPSTDINT_intmax_t_DEFINED -#define _SCE_PSPSTDINT_intmax_t_DEFINED -#if defined(__GNUC__) -typedef long long intmax_t __attribute__((mode(DI))); -typedef unsigned long long uintmax_t __attribute__((mode(DI))); -#else /* defined(__GNUC__) */ -typedef long long intmax_t; -typedef unsigned long long uintmax_t; -#endif /* defined(__GNUC__) */ -#endif /* _SCE_PSPSTDINT_intmax_t_DEFINED */ - - -/* Limits of specified-width intger types */ -#if (!(defined(_LANGUAGE_C_PLUS_PLUS)||defined(__cplusplus)||defined(c_plusplus)))||defined(__STDC_CONSTANT_MACROS) - -/* Limits of exact-width integer types */ -#define INT8_MIN (-128) -#define INT16_MIN (-32767-1) -#define INT32_MIN (-2147483647-1) -#define INT64_MIN (-9223372036854775807LL-1) -#define INT8_MAX (127) -#define INT16_MAX (32767) -#define INT32_MAX (2147483647) -#define INT64_MAX (9223372036854775807LL) -#define UINT8_MAX (255) -#define UINT16_MAX (65535) -#define UINT32_MAX (4294967295U) -#define UINT64_MAX (18446744073709551615ULL) - -/* Limits of minimum-width integer types */ -#define INT_LEAST8_MIN (-128) -#define INT_LEAST16_MIN (-32767-1) -#define INT_LEAST32_MIN (-2147483647-1) -#define INT_LEAST64_MIN (-9223372036854775807LL-1) -#define INT_LEAST8_MAX (127) -#define INT_LEAST16_MAX (32767) -#define INT_LEAST32_MAX (2147483647) -#define INT_LEAST64_MAX (9223372036854775807LL) -#define UINT_LEAST8_MAX (255) -#define UINT_LEAST16_MAX (65535) -#define UINT_LEAST32_MAX (4294967295U) -#define UINT_LEAST64_MAX (18446744073709551615ULL) - -/* Limits of fastest minimum-width integer types */ -#define INT_FAST8_MIN (-128) -#define INT_FAST16_MIN (-2147483647-1) -#define INT_FAST32_MIN (-2147483647-1) -#define INT_FAST64_MIN (-9223372036854775807LL-1) -#define INT_FAST8_MAX (127) -#define INT_FAST16_MAX (2147483647) -#define INT_FAST32_MAX (2147483647) -#define INT_FAST64_MAX (9223372036854775807LL) -#define UINT_FAST8_MAX (255) -#define UINT_FAST16_MAX (4294967295U) -#define UINT_FAST32_MAX (4294967295U) -#define UINT_FAST64_MAX (18446744073709551615ULL) - -/* Limits of integer types capable of holding object pointers */ -#define INTPTR_MIN (-2147483647-1) -#define INTPTR_MAX (2147483647) -#define UINTPTR_MAX (4294967295U) - -/* Limits of greates-width intger types */ -#define INTMAX_MIN (-9223372036854775807LL-1) -#define INTMAX_MAX (9223372036854775807LL) -#define UINTMAX_MAX (18446744073709551615ULL) - - -/* Macros for minimum-width integer constants */ -#define INT8_C(c) c -#define INT16_C(c) c -#define INT32_C(c) c -#define INT64_C(c) c ## LL -#define UINT8_C(c) c ## U -#define UINT16_C(c) c ## U -#define UINT32_C(c) c ## U -#define UINT64_C(c) c ## ULL - -/* Macros for greatest-width integer constants */ -#define INTMAX_C(c) c ## LL -#define UINTMAX_C(c) c ## ULL - - -#endif /* (!(defined(_LANGUAGE_C_PLUS_PLUS)||defined(__cplusplus)||defined(c_plusplus)))||defined(__STDC_CONSTANT_MACROS) */ - -#endif /* _SCE_PSPSTDINT_H */ - diff --git a/stella/src/tools/check-sig.cxx b/stella/src/tools/check-sig.cxx new file mode 100644 index 000000000..56688e97d --- /dev/null +++ b/stella/src/tools/check-sig.cxx @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +using namespace std; + +typedef unsigned char uInt8; +typedef unsigned int uInt32; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int searchForBytes(const uInt8* image, uInt32 imagesize, + const uInt8* signature, uInt32 sigsize) +{ + uInt32 count = 0; + for(uInt32 i = 0; i < imagesize - sigsize; ++i) + { + uInt32 matches = 0; + for(uInt32 j = 0; j < sigsize; ++j) + { + if(image[i+j] == signature[j]) + ++matches; + else + break; + } + if(matches == sigsize) + ++count; + } + + return count; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int main(int ac, char* av[]) +{ + if(ac != 3) + { + cout << "usage: " << av[0] << " \n"; + exit(0); + } + + ifstream in(av[1], ios_base::binary); + in.seekg(0, ios::end); + int i_size = (int) in.tellg(); + in.seekg(0, ios::beg); + + uInt8* image = new uInt8[i_size]; + in.read((char*)(image), i_size); + in.close(); + + int s_size = 0; + uInt8* sig = new uInt8[strlen(av[2])/2]; + istringstream buf(av[2]); + + uInt32 c; + while(buf >> hex >> c) + { + sig[s_size++] = (uInt8)c; +// cerr << "character = " << hex << (int)sig[s_size-1] << endl; + } +// cerr << "sig size = " << hex << s_size << endl; + + int result = searchForBytes(image, i_size, sig, s_size); + if(result > 0) + cout << setw(3) << result << " hits: \'" << av[2] << "\' - \"" << av[1] << "\"" << endl; + + delete[] image; + delete[] sig; + + return 0; +} diff --git a/stella/src/unix/OSystemUNIX.cxx b/stella/src/unix/OSystemUNIX.cxx index 0fbc770d5..782663c14 100644 --- a/stella/src/unix/OSystemUNIX.cxx +++ b/stella/src/unix/OSystemUNIX.cxx @@ -13,11 +13,10 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OSystemUNIX.cxx,v 1.25 2007-01-01 18:04:55 stephena Exp $ +// $Id: OSystemUNIX.cxx,v 1.26 2007-06-20 16:33:23 stephena Exp $ //============================================================================ #include -#include #include #include @@ -81,28 +80,3 @@ uInt32 OSystemUNIX::getTicks() return (uInt32) SDL_GetTicks() * 1000; #endif } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void OSystemUNIX::getScreenDimensions(int& width, int& height) -{ - // We might need to temporarily enable VIDEO support to check - // screen dimensions - bool isAlreadyInitialized = (SDL_WasInit(SDL_INIT_VIDEO) & SDL_INIT_VIDEO) > 0; - if(!isAlreadyInitialized) - SDL_Init(SDL_INIT_VIDEO); - - SDL_SysWMinfo myWMInfo; - SDL_VERSION(&myWMInfo.version); - if(SDL_GetWMInfo(&myWMInfo) > 0 && myWMInfo.subsystem == SDL_SYSWM_X11) - { - myWMInfo.info.x11.lock_func(); - width = DisplayWidth(myWMInfo.info.x11.display, - DefaultScreen(myWMInfo.info.x11.display)); - height = DisplayHeight(myWMInfo.info.x11.display, - DefaultScreen(myWMInfo.info.x11.display)); - myWMInfo.info.x11.unlock_func(); - } - - if(!isAlreadyInitialized) - SDL_QuitSubSystem(SDL_INIT_VIDEO); -} diff --git a/stella/src/unix/OSystemUNIX.hxx b/stella/src/unix/OSystemUNIX.hxx index ac70acc7b..b1f5a57cb 100644 --- a/stella/src/unix/OSystemUNIX.hxx +++ b/stella/src/unix/OSystemUNIX.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: OSystemUNIX.hxx,v 1.14 2007-01-01 18:04:55 stephena Exp $ +// $Id: OSystemUNIX.hxx,v 1.15 2007-06-20 16:33:23 stephena Exp $ //============================================================================ #ifndef OSYSTEM_UNIX_HXX @@ -21,12 +21,11 @@ #include "bspf.hxx" - /** This class defines UNIX-like OS's (Linux) system specific settings. @author Stephen Anthony - @version $Id: OSystemUNIX.hxx,v 1.14 2007-01-01 18:04:55 stephena Exp $ + @version $Id: OSystemUNIX.hxx,v 1.15 2007-06-20 16:33:23 stephena Exp $ */ class OSystemUNIX : public OSystem { @@ -41,19 +40,12 @@ class OSystemUNIX : public OSystem */ virtual ~OSystemUNIX(); - public: /** This method returns number of ticks in microseconds. @return Current time in microseconds. */ uInt32 getTicks(); - - /** - This method queries the dimensions of the screen for this hardware. - It is assumed that a UNIX SDL framebuffer is using X11. - */ - void getScreenDimensions(int& width, int& height); }; #endif