From fc96e5b0d8a40b429eab45b3fc86f49387f2875b Mon Sep 17 00:00:00 2001 From: stephena Date: Sun, 9 Nov 2003 23:53:20 +0000 Subject: [PATCH] Integrated the SDL software and OpenGL modes into stella.sdl. You can now select the video backend with '-video soft' or '-video gl'. Cleaned up the menu text lines that were longer than the current display width. These lines are now truncated and have a '...' appended to them. The menu modes are now more tolerant of display sizes. So (for example), if some port uses a 640 pixel wide framebuffer, the menu will now resize and fully take advantage of all available space. I've looked at the Windows code, but I still haven't mustered the strength to work on it. It will basically be a complete rewrite of sound, video, and input classes. And I'm not looking forward to it ... git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@201 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/src/build/makefile | 15 +- stella/src/emucore/FrameBuffer.cxx | 248 ++++++------- stella/src/emucore/FrameBuffer.hxx | 42 +-- stella/src/ui/sdl/FrameBufferGL.cxx | 275 ++++---------- stella/src/ui/sdl/FrameBufferGL.hxx | 132 ++----- stella/src/ui/sdl/FrameBufferSDL.cxx | 510 ++------------------------ stella/src/ui/sdl/FrameBufferSDL.hxx | 142 ++----- stella/src/ui/sdl/FrameBufferSoft.cxx | 475 ++++++++++++++++++++++++ stella/src/ui/sdl/FrameBufferSoft.hxx | 149 ++++++++ stella/src/ui/sdl/SettingsUNIX.cxx | 30 +- stella/src/ui/sdl/mainSDL.cxx | 51 ++- 11 files changed, 955 insertions(+), 1114 deletions(-) create mode 100644 stella/src/ui/sdl/FrameBufferSoft.cxx create mode 100644 stella/src/ui/sdl/FrameBufferSoft.hxx diff --git a/stella/src/build/makefile b/stella/src/build/makefile index 9e41e6e3a..7dddd2da9 100644 --- a/stella/src/build/makefile +++ b/stella/src/build/makefile @@ -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: makefile,v 1.44 2003-11-06 22:22:32 stephena Exp $ +## $Id: makefile,v 1.45 2003-11-09 23:53:19 stephena Exp $ ##============================================================================ ##============================================================================ @@ -34,7 +34,7 @@ SOUND_OSS = 1 #SOUND_SDL = 1 ### to include OpenGL video support (SDL) -DISPLAY_OPENGL = 0 +# OPENGL_SUPPORT = 1 ### to include joystick support (SDL) # JOYSTICK_SUPPORT = 1 @@ -140,16 +140,12 @@ ifeq ($(SOUND_ALSA), 1) LIBS.SDL += -lasound endif -ifeq ($(DISPLAY_OPENGL), 1) +ifeq ($(OPENGL_SUPPORT), 1) OPTS.SDL += -DDISPLAY_OPENGL=1 OBJS.SDL += FrameBufferGL.o LIBS.SDL += -lGL -lGLU endif -ifeq ($(DISPLAY_OPENGL), 0) - OBJS.SDL += FrameBufferSDL.o -endif - default: @echo "" @@ -185,7 +181,7 @@ linux-sdl: LDFLAGS+="$(CFLAGS.SDL)" \ LDLIBS="-lX11 -lXext" \ LDLIBS+="$(LIBS.SDL)" \ - OBJS="mainSDL.o SettingsUNIX.o" \ + OBJS="mainSDL.o SettingsUNIX.o FrameBufferSDL.o FrameBufferSoft.o" \ OBJS+="$(OBJS.SDL)" ############################################################################### @@ -403,6 +399,9 @@ RectList.o: $(UI)/sdl/RectList.cxx $(UI)/sdl/RectList.hxx FrameBufferSDL.o: $(UI)/sdl/FrameBufferSDL.cxx $(UI)/sdl/FrameBufferSDL.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/sdl/FrameBufferSDL.cxx +FrameBufferSoft.o: $(UI)/sdl/FrameBufferSoft.cxx $(UI)/sdl/FrameBufferSoft.hxx + $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/sdl/FrameBufferSoft.cxx + FrameBufferGL.o: $(UI)/sdl/FrameBufferGL.cxx $(UI)/sdl/FrameBufferGL.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/sdl/FrameBufferGL.cxx diff --git a/stella/src/emucore/FrameBuffer.cxx b/stella/src/emucore/FrameBuffer.cxx index d7a660546..56628a936 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.3 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBuffer.cxx,v 1.4 2003-11-09 23:53:19 stephena Exp $ //============================================================================ #include @@ -28,8 +28,6 @@ #include "FrameBuffer.hxx" // Eventually, these may become variables -#define FGCOLOR 10 // A white color in NTSC and PAL mode -#define BGCOLOR 0 // A black color in NTSC and PAL mode #define FONTWIDTH 8 #define FONTHEIGHT 8 @@ -53,13 +51,16 @@ FrameBuffer::FrameBuffer() myHeight(300), theRedrawEntireFrameIndicator(true), myPauseStatus(false), + myFGColor(10), + myBGColor(0), myFrameRate(0), myCurrentWidget(W_NONE), myRemapEventSelectedFlag(false), mySelectedEvent(Event::NoType), myMenuMode(false), theMenuChangedIndicator(false), - myMaxLines(0), + myMaxRows(0), + myMaxColumns(0), myMainMenuIndex(0), myMainMenuItems(sizeof(ourMainMenu)/sizeof(MainMenuItem)), myRemapMenuIndex(0), @@ -95,29 +96,46 @@ void FrameBuffer::initDisplay(Console* console, MediaSource* mediasrc) ourPropertiesInfo[7] = "MD5SUM:"; ourPropertiesInfo[8] = myConsole->properties().get("Cartridge.MD5"); - // Figure out the longest string - for(uInt8 i = 0; i < 9; i++) - if(ourPropertiesInfo[i].length() > myInfoMenuWidth) - myInfoMenuWidth = ourPropertiesInfo[i].length(); - // Get the arrays containing key and joystick mappings myConsole->eventHandler().getKeymapArray(&myKeyTable, &myKeyTableSize); myConsole->eventHandler().getJoymapArray(&myJoyTable, &myJoyTableSize); myFrameRate = myConsole->settings().getInt("framerate"); - loadRemapMenu(); - // Now initialize the derived class init(); - // The following has to be done after the initialization of the derived class - // Determine the maximum number of items that can be onscreen vertically - myMaxLines = myHeight / LINEOFFSET - 2; + // The following has to be done after the initialization of the derived class, + // since we need the exact width and height of the display + + // Determine the maximum number of characters that can be onscreen + myMaxColumns = myWidth / FONTWIDTH - 3; + myMaxRows = myHeight / LINEOFFSET - 2; + // Set up the correct bounds for the remap menu - myRemapMenuMaxLines = myRemapMenuItems > myMaxLines ? myMaxLines : myRemapMenuItems; + myRemapMenuMaxLines = myRemapMenuItems > myMaxRows ? myMaxRows : myRemapMenuItems; myRemapMenuLowIndex = 0; myRemapMenuHighIndex = myRemapMenuMaxLines; + + // Figure out the longest properties string, + // and cut any string that is wider than the display + for(uInt8 i = 0; i < 9; i++) + { + if(ourPropertiesInfo[i].length() > (uInt32) myInfoMenuWidth) + { + myInfoMenuWidth = ourPropertiesInfo[i].length(); + if(myInfoMenuWidth > myMaxColumns) + { + myInfoMenuWidth = myMaxColumns; + string s = ourPropertiesInfo[i]; + ourPropertiesInfo[i] = s.substr(0, myMaxColumns - 3) + "..."; + } + } + } + + // Finally, load the remap menu with strings, + // clipping any strings which are wider than the display + loadRemapMenu(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -151,8 +169,8 @@ void FrameBuffer::update() uInt32 y = myHeight - height - LINEOFFSET/2; // Draw the bounded box and text - drawBoundedBox(x, y, width, height, FGCOLOR, BGCOLOR); - drawText(x + XBOXOFFSET/2, LINEOFFSET/2 + y, myMessageText, FGCOLOR); + drawBoundedBox(x, y+1, width, height-2); + drawText(x + XBOXOFFSET/2, LINEOFFSET/2 + y, myMessageText); myMessageTime--; // Erase this message on next update @@ -167,7 +185,6 @@ void FrameBuffer::update() // or the menus have changed if(theMenuChangedIndicator || theRedrawEntireFrameIndicator) { -cerr << "redrawing screen and menus\n"; drawMediaSource(); // Then overlay any menu items @@ -232,35 +249,35 @@ inline void FrameBuffer::drawMainMenu() // Draw the bounded box and text, leaving a little room for arrows xpos = x + XBOXOFFSET; - drawBoundedBox(x-2, y-2, width+3, height+3, FGCOLOR, BGCOLOR); + drawBoundedBox(x-2, y-2, width+3, height+3); for(i = 0; i < myMainMenuItems; i++) - drawText(xpos, LINEOFFSET*i + y + YBOXOFFSET, ourMainMenu[i].action, FGCOLOR); + drawText(xpos, LINEOFFSET*i + y + YBOXOFFSET, ourMainMenu[i].action); // Now draw the selection arrow around the currently selected item ypos = LINEOFFSET*myMainMenuIndex + y + YBOXOFFSET; - drawChar(x, ypos, LEFTARROW, FGCOLOR); - drawChar(x + width - FONTWIDTH, ypos, RIGHTARROW, FGCOLOR); + drawChar(x, ypos, LEFTARROW); + drawChar(x + width - FONTWIDTH, ypos, RIGHTARROW); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline void FrameBuffer::drawRemapMenu() { - uInt32 x, y, width, height, i, xpos, ypos; + uInt32 x, y, width, height, xpos, ypos; width = (myWidth >> 3) * FONTWIDTH - (FONTWIDTH << 1); - height = myMaxLines*LINEOFFSET + (FONTHEIGHT << 1); + height = myMaxRows*LINEOFFSET + (FONTHEIGHT << 1); x = (myWidth >> 1) - (width >> 1); y = (myHeight >> 1) - (height >> 1); // Draw the bounded box and text, leaving a little room for arrows - drawBoundedBox(x-2, y-2, width+3, height+3, FGCOLOR, BGCOLOR); - for(i = myRemapMenuLowIndex; i < myRemapMenuHighIndex; i++) + drawBoundedBox(x-2, y-2, width+3, height+3); + for(Int32 i = myRemapMenuLowIndex; i < myRemapMenuHighIndex; i++) { ypos = LINEOFFSET*(i-myRemapMenuLowIndex) + y + YBOXOFFSET; - drawText(x + XBOXOFFSET, ypos, ourRemapMenu[i].action, FGCOLOR); + drawText(x + XBOXOFFSET, ypos, ourRemapMenu[i].action); xpos = width - ourRemapMenu[i].key.length() * FONTWIDTH; - drawText(xpos, ypos, ourRemapMenu[i].key, FGCOLOR); + drawText(xpos, ypos, ourRemapMenu[i].key); } // Normally draw an arrow indicating the current line, @@ -268,8 +285,8 @@ inline void FrameBuffer::drawRemapMenu() if(!myRemapEventSelectedFlag) { ypos = LINEOFFSET*(myRemapMenuIndex-myRemapMenuLowIndex) + y + YBOXOFFSET; - drawChar(x, ypos, LEFTARROW, FGCOLOR); - drawChar(x + width - FONTWIDTH, ypos, RIGHTARROW, FGCOLOR); + drawChar(x, ypos, LEFTARROW); + drawChar(x + width - FONTWIDTH, ypos, RIGHTARROW); } else { @@ -277,19 +294,19 @@ inline void FrameBuffer::drawRemapMenu() // Left marker is at the beginning of event name text xpos = width - ourRemapMenu[myRemapMenuIndex].key.length() * FONTWIDTH - FONTWIDTH; - drawChar(xpos, ypos, LEFTMARKER, FGCOLOR); + drawChar(xpos, ypos, LEFTMARKER); // Right marker is at the end of the line - drawChar(x + width - FONTWIDTH, ypos, RIGHTMARKER, FGCOLOR); + drawChar(x + width - FONTWIDTH, ypos, RIGHTMARKER); } // Finally, indicate that there are more items to the top or bottom xpos = (width >> 1) - (FONTWIDTH >> 1); - if(myRemapMenuHighIndex - myMaxLines > 0) - drawChar(xpos, y, UPARROW, FGCOLOR); + if(myRemapMenuHighIndex - myMaxRows > 0) + drawChar(xpos, y, UPARROW); - if(myRemapMenuLowIndex + myMaxLines < myRemapMenuItems) - drawChar(xpos, height - (FONTWIDTH >> 1), DOWNARROW, FGCOLOR); + if(myRemapMenuLowIndex + myMaxRows < myRemapMenuItems) + drawChar(xpos, height - (FONTWIDTH >> 1), DOWNARROW); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -304,9 +321,9 @@ inline void FrameBuffer::drawInfoMenu() // Draw the bounded box and text xpos = x + XBOXOFFSET; - drawBoundedBox(x, y, width, height, FGCOLOR, BGCOLOR); + drawBoundedBox(x, y, width, height); for(i = 0; i < 9; i++) - drawText(xpos, LINEOFFSET*i + y + YBOXOFFSET, ourPropertiesInfo[i], FGCOLOR); + drawText(xpos, LINEOFFSET*i + y + YBOXOFFSET, ourPropertiesInfo[i]); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -325,9 +342,9 @@ void FrameBuffer::sendKeyEvent(StellaEvent::KeyCode key, Int32 state) if(key == StellaEvent::KCODE_RETURN) myCurrentWidget = currentSelectedWidget(); else if(key == StellaEvent::KCODE_UP) - moveCursorUp(); + moveCursorUp(1); else if(key == StellaEvent::KCODE_DOWN) - moveCursorDown(); + moveCursorDown(1); break; // MAIN_MENU @@ -347,13 +364,13 @@ void FrameBuffer::sendKeyEvent(StellaEvent::KeyCode key, Int32 state) myRemapEventSelectedFlag = true; } else if(key == StellaEvent::KCODE_UP) - moveCursorUp(); + moveCursorUp(1); else if(key == StellaEvent::KCODE_DOWN) - moveCursorDown(); + moveCursorDown(1); else if(key == StellaEvent::KCODE_PAGEUP) - movePageUp(); + moveCursorUp(4); else if(key == StellaEvent::KCODE_PAGEDOWN) - movePageDown(); + moveCursorDown(4); else if(key == StellaEvent::KCODE_ESCAPE) { myCurrentWidget = MAIN_MENU; @@ -383,7 +400,6 @@ void FrameBuffer::sendJoyEvent(StellaEvent::JoyStick stick, if(myCurrentWidget == W_NONE || state != 1) return; -cerr << "stick = " << stick << ", button = " << code << endl; // Redraw the menus whenever a joy event is received theMenuChangedIndicator = true; @@ -394,9 +410,9 @@ cerr << "stick = " << stick << ", button = " << code << endl; // if(key == StellaEvent::KCODE_RETURN) // myCurrentWidget = currentSelectedWidget(); if(stick == StellaEvent::JSTICK_0 && code == StellaEvent::JAXIS_UP) - moveCursorUp(); + moveCursorUp(1); else if(stick == StellaEvent::JSTICK_0 && code == StellaEvent::JAXIS_DOWN) - moveCursorDown(); + moveCursorDown(1); break; // MAIN_MENU @@ -407,9 +423,9 @@ cerr << "stick = " << stick << ", button = " << code << endl; myRemapEventSelectedFlag = false; } else if(stick == StellaEvent::JSTICK_0 && code == StellaEvent::JAXIS_UP) - moveCursorUp(); + moveCursorUp(1); else if(stick == StellaEvent::JSTICK_0 && code == StellaEvent::JAXIS_DOWN) - moveCursorDown(); + moveCursorDown(1); // else if(key == StellaEvent::KCODE_PAGEUP) // movePageUp(); // else if(key == StellaEvent::KCODE_PAGEDOWN) @@ -452,7 +468,7 @@ Event::Type FrameBuffer::currentSelectedEvent() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::moveCursorUp() +void FrameBuffer::moveCursorUp(uInt32 amt) { switch(myCurrentWidget) { @@ -463,20 +479,24 @@ void FrameBuffer::moveCursorUp() break; case REMAP_MENU: - // Since this menu will have more options than can fit it one screen, - // we have to implement a sliding window - if(myRemapMenuIndex > myRemapMenuLowIndex) + // First move cursor down by the given amt + myRemapMenuIndex -= amt; + + // Move down the boundaries + if(myRemapMenuIndex < myRemapMenuLowIndex) { - myRemapMenuIndex--; + Int32 x = myRemapMenuLowIndex - myRemapMenuIndex; + myRemapMenuLowIndex -= x; + myRemapMenuHighIndex -= x; } - else if(myRemapMenuIndex == myRemapMenuLowIndex) + + // Then scale back up, if necessary + if(myRemapMenuLowIndex < 0) { - if(myRemapMenuLowIndex > 0) - { - myRemapMenuLowIndex--; - myRemapMenuHighIndex--; - myRemapMenuIndex--; - } + Int32 x = 0 - myRemapMenuLowIndex; + myRemapMenuIndex += x; + myRemapMenuLowIndex += x; + myRemapMenuHighIndex += x; } break; @@ -487,7 +507,7 @@ void FrameBuffer::moveCursorUp() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::moveCursorDown() +void FrameBuffer::moveCursorDown(uInt32 amt) { switch(myCurrentWidget) { @@ -498,85 +518,27 @@ void FrameBuffer::moveCursorDown() break; case REMAP_MENU: - // Since this menu will have more options than can fit it one screen, - // we have to implement a sliding window - if(myRemapMenuIndex < myRemapMenuHighIndex - 1) + // First move cursor up by the given amount + myRemapMenuIndex += amt; + + // Move up the boundaries + if(myRemapMenuIndex >= myRemapMenuHighIndex) { - myRemapMenuIndex++; - } - else if(myRemapMenuIndex == myRemapMenuHighIndex - 1) - { - if(myRemapMenuHighIndex < myRemapMenuItems) - { - myRemapMenuLowIndex++; - myRemapMenuHighIndex++; - myRemapMenuIndex++; - } + Int32 x = myRemapMenuIndex - myRemapMenuHighIndex + 1; + + myRemapMenuLowIndex += x; + myRemapMenuHighIndex += x; } - break; - - default: // This should never happen - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::movePageUp() -{ - switch(myCurrentWidget) - { - case MAIN_MENU: - break; - - case REMAP_MENU: - if(myRemapMenuLowIndex < myMaxLines) + // Then scale back down, if necessary + if(myRemapMenuHighIndex >= myRemapMenuItems) { - myRemapMenuLowIndex = 0; - myRemapMenuHighIndex = myMaxLines; + Int32 x = myRemapMenuHighIndex - myRemapMenuItems; + + myRemapMenuIndex -= x; + myRemapMenuLowIndex -= x; + myRemapMenuHighIndex -= x; } - else - { - myRemapMenuLowIndex -= myMaxLines; - myRemapMenuHighIndex -= myMaxLines; - } - - // Don't scroll the cursor if it falls within the screen - if(myRemapMenuIndex < myRemapMenuLowIndex || - myRemapMenuIndex > myRemapMenuHighIndex-1) - myRemapMenuIndex = myRemapMenuHighIndex - 1; - - break; - - default: // This should never happen - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::movePageDown() -{ - switch(myCurrentWidget) - { - case MAIN_MENU: - break; - - case REMAP_MENU: - if(myRemapMenuHighIndex + myMaxLines >= myRemapMenuItems) - { - myRemapMenuHighIndex = myRemapMenuItems; - myRemapMenuLowIndex = myRemapMenuHighIndex - myMaxLines; - } - else - { - myRemapMenuLowIndex += myMaxLines; - myRemapMenuHighIndex += myMaxLines; - } - - // Don't scroll the cursor if it falls within the screen - if(myRemapMenuIndex < myRemapMenuLowIndex || - myRemapMenuIndex > myRemapMenuHighIndex-1) - myRemapMenuIndex = myRemapMenuLowIndex; break; @@ -589,7 +551,7 @@ void FrameBuffer::movePageDown() void FrameBuffer::loadRemapMenu() { // Fill the remap menu with the current key and joystick mappings - for(uInt32 i = 0; i < myRemapMenuItems; ++i) + for(Int32 i = 0; i < myRemapMenuItems; ++i) { Event::Type event = ourRemapMenu[i].event; ourRemapMenu[i].key = "None"; @@ -643,7 +605,17 @@ void FrameBuffer::loadRemapMenu() } if(key != "") - ourRemapMenu[i].key = key; + { + // 19 is the max size of the event names, and 2 is for the space in between + // (this could probably be cleaner ...) + uInt32 len = myMaxColumns - 19 - 2; + if(key.length() > len) + { + ourRemapMenu[i].key = key.substr(0, len - 3) + "..."; + } + else + ourRemapMenu[i].key = key; + } } } diff --git a/stella/src/emucore/FrameBuffer.hxx b/stella/src/emucore/FrameBuffer.hxx index f00142241..97eab8425 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.3 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBuffer.hxx,v 1.4 2003-11-09 23:53:19 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_HXX @@ -35,7 +35,7 @@ class Console; can be changed. @author Stephen Anthony - @version $Id: FrameBuffer.hxx,v 1.3 2003-11-06 22:22:32 stephena Exp $ + @version $Id: FrameBuffer.hxx,v 1.4 2003-11-09 23:53:19 stephena Exp $ */ class FrameBuffer { @@ -147,10 +147,8 @@ class FrameBuffer @param y The y coordinate @param w The width of the box @param h The height of the box - @param fg The color of the bounding sides - @param bg The color of the background */ - virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt8 fg, uInt8 bg) = 0; + virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h) = 0; /** This routine should be called to draw text at the specified coordinates. @@ -158,9 +156,8 @@ class FrameBuffer @param x The x coordinate @param y The y coordinate @param message The message text - @param fg The color of the text */ - virtual void drawText(uInt32 x, uInt32 y, const string& message, uInt8 fg) = 0; + virtual void drawText(uInt32 x, uInt32 y, const string& message) = 0; /** This routine should be called to draw character 'c' at the specified coordinates. @@ -168,9 +165,8 @@ class FrameBuffer @param x The x coordinate @param y The y coordinate @param c The character to draw - @param fg The color of the character */ - virtual void drawChar(uInt32 x, uInt32 y, uInt32 c, uInt8 fg) = 0; + virtual void drawChar(uInt32 x, uInt32 y, uInt32 c) = 0; /** This routine is called before any drawing is done (per-frame). @@ -208,6 +204,9 @@ class FrameBuffer // Table of bitmapped fonts. static const uInt8 ourFontData[2048]; + // Holds the foreground and background color table indices + uInt8 myFGColor, myBGColor; + private: // Enumeration representing the different types of user interface widgets enum Widget { W_NONE, MAIN_MENU, REMAP_MENU, INFO_MENU }; @@ -234,17 +233,11 @@ class FrameBuffer // Draw the info menu void drawInfoMenu(); - // Move the cursor up 1 line, possibly scrolling the list of items - void moveCursorUp(); + // Move the cursor up 'amt' lines, possibly scrolling the list of items + void moveCursorUp(uInt32 amt); - // Move the cursor down 1 line, possibly scrolling the list of items - void moveCursorDown(); - - // Move the list up 1 page and put the cursor at the top - void movePageUp(); - - // Move the list down 1 page and put the cursor at the top - void movePageDown(); + // Move the cursor down 'amt' lines, possibly scrolling the list of items + void moveCursorDown(uInt32 amt); // scan the mapping arrays and update the remap menu void loadRemapMenu(); @@ -287,14 +280,17 @@ class FrameBuffer bool theMenuChangedIndicator; // The maximum number of vertical lines of text that can be onscreen - uInt32 myMaxLines; + Int32 myMaxRows; + + // The maximum number of characters of text in a row + Int32 myMaxColumns; // Keep track of current selected main menu item uInt32 myMainMenuIndex, myMainMenuItems; // Keep track of current selected remap menu item - uInt32 myRemapMenuIndex, myRemapMenuLowIndex, myRemapMenuHighIndex; - uInt32 myRemapMenuItems, myRemapMenuMaxLines; + Int32 myRemapMenuIndex, myRemapMenuLowIndex, myRemapMenuHighIndex; + Int32 myRemapMenuItems, myRemapMenuMaxLines; // Message timer Int32 myMessageTime; @@ -303,7 +299,7 @@ class FrameBuffer string myMessageText; // The width of the information menu, determined by the longest string - uInt32 myInfoMenuWidth; + Int32 myInfoMenuWidth; // Holds information about the current selected ROM image string ourPropertiesInfo[9]; diff --git a/stella/src/ui/sdl/FrameBufferGL.cxx b/stella/src/ui/sdl/FrameBufferGL.cxx index bd3574c6c..fcc533b0b 100644 --- a/stella/src/ui/sdl/FrameBufferGL.cxx +++ b/stella/src/ui/sdl/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.1 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBufferGL.cxx,v 1.2 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #include @@ -22,20 +22,14 @@ #include "Console.hxx" #include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" #include "FrameBufferGL.hxx" #include "MediaSrc.hxx" #include "Settings.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FrameBufferGL::FrameBufferGL() - : myScreen(0), - myTexture(0), - x11Available(false), - theZoomLevel(1), - theMaxZoomLevel(1), - theGrabMouseIndicator(false), - theHideCursorIndicator(false), - isFullscreen(false) + : myTexture(0) { } @@ -46,6 +40,61 @@ FrameBufferGL::~FrameBufferGL() SDL_FreeSurface(myTexture); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FrameBufferGL::createScreen() +{ + int w = myWidth * theZoomLevel; + int h = myHeight * theZoomLevel; + + myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags); + if(myScreen == NULL) + { + cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl; + return false; + } + + glPushAttrib(GL_ENABLE_BIT); + glViewport(0, 0, myScreen->w, myScreen->h); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glOrtho(0.0, (GLdouble) myScreen->w/theZoomLevel, + (GLdouble) myScreen->h/theZoomLevel, 0.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + theRedrawEntireFrameIndicator = true; + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferGL::setupPalette(float shade) +{ +// FIXME - OpenGL should be able to shade the texture itself + const uInt32* gamePalette = myMediaSource->palette(); + for(uInt32 i = 0; i < 256; ++i) + { + Uint8 r, g, b, a; + + r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade); + g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade); + b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade); + a = 0xff; + + #if SDL_BYTEORDER == SDL_LIL_ENDIAN + myPalette[i] = (a << 24) | (b << 16) | (g << 8) | r; + #else + myPalette[i] = (r << 24) | (g << 16) | (b << 8) | a; + #endif + } + + theRedrawEntireFrameIndicator = true; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FrameBufferGL::init() { @@ -194,6 +243,7 @@ void FrameBufferGL::drawMediaSource() // FIXME - maybe less copying can be done? glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myTexture->w, myTexture->h, GL_RGBA, GL_UNSIGNED_BYTE, myTexture->pixels); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glColor3f(0.0, 0.0, 0.0); glBegin(GL_QUADS); glTexCoord2f(myTexCoord[0], myTexCoord[1]); glVertex2i(0, 0); @@ -216,214 +266,14 @@ void FrameBufferGL::postFrameUpdate() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferGL::createScreen() -{ - int w = myWidth * theZoomLevel; - int h = myHeight * theZoomLevel; - - myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags); - if(myScreen == NULL) - { - cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl; - return false; - } - - glPushAttrib(GL_ENABLE_BIT); - glViewport(0, 0, myScreen->w, myScreen->h); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - glOrtho(0.0, (GLdouble) myScreen->w/theZoomLevel, - (GLdouble) myScreen->h/theZoomLevel, 0.0, 0.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - theRedrawEntireFrameIndicator = true; - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::setupPalette(float shade) -{ -// FIXME - OpenGL should be able to shade the texture itself - const uInt32* gamePalette = myMediaSource->palette(); - for(uInt32 i = 0; i < 256; ++i) - { - Uint8 r, g, b, a; - - r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade); - g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade); - b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade); - a = 0xff; - - #if SDL_BYTEORDER == SDL_LIL_ENDIAN - myPalette[i] = (a << 24) | (b << 16) | (g << 8) | r; - #else - myPalette[i] = (r << 24) | (g << 16) | (b << 8) | a; - #endif - } - - theRedrawEntireFrameIndicator = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::pause(bool status) -{ - myPauseStatus = status; - - // Shade the palette to 75% normal value in pause mode - // FIXME - this seems like cheating, we should be using OpenGL instead - if(myPauseStatus) - setupPalette(0.75); - else - setupPalette(1.0); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::toggleFullscreen() -{ - isFullscreen = !isFullscreen; - if(isFullscreen) - mySDLFlags |= SDL_FULLSCREEN; - else - mySDLFlags &= ~SDL_FULLSCREEN; - - if(!createScreen()) - return; - - if(isFullscreen) // now in fullscreen mode - { - grabMouse(true); - showCursor(false); - } - else // now in windowed mode - { - grabMouse(theGrabMouseIndicator); - showCursor(!theHideCursorIndicator); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::resize(int mode) -{ - // reset size to that given in properties - // this is a special case of allowing a resize while in fullscreen mode - if(mode == 0) - { - myWidth = myMediaSource->width() << 1; - myHeight = myMediaSource->height(); - } - else if(mode == 1) // increase size - { - if(isFullscreen) - return; - - if(theZoomLevel == theMaxZoomLevel) - theZoomLevel = 1; - else - theZoomLevel++; - } - else if(mode == -1) // decrease size - { - if(isFullscreen) - return; - - if(theZoomLevel == 1) - theZoomLevel = theMaxZoomLevel; - else - theZoomLevel--; - } - - if(!createScreen()) - return; - - // Now update the settings - ostringstream tmp; - tmp << theZoomLevel; - myConsole->settings().set("zoom", tmp.str()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::showCursor(bool show) -{ - if(isFullscreen) - return; - - if(show) - SDL_ShowCursor(SDL_ENABLE); - else - SDL_ShowCursor(SDL_DISABLE); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::grabMouse(bool grab) -{ - if(isFullscreen) - return; - - if(grab) - SDL_WM_GrabInput(SDL_GRAB_ON); - else - SDL_WM_GrabInput(SDL_GRAB_OFF); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 FrameBufferGL::maxWindowSizeForScreen() -{ - if(!x11Available) - return 1; - -#ifdef UNIX - // Otherwise, lock the screen and get the width and height - myWMInfo.info.x11.lock_func(); - Display* theX11Display = myWMInfo.info.x11.display; - myWMInfo.info.x11.unlock_func(); - - int screenWidth = DisplayWidth(theX11Display, DefaultScreen(theX11Display)); - int screenHeight = DisplayHeight(theX11Display, DefaultScreen(theX11Display)); - - uInt32 multiplier = screenWidth / myWidth; - bool found = false; - - while(!found && (multiplier > 0)) - { - // Figure out the desired size of the window - int width = myWidth * multiplier; - int height = myHeight * multiplier; - - if((width < screenWidth) && (height < screenHeight)) - found = true; - else - multiplier--; - } - - if(found) - return multiplier; - else - return 1; -#else - return 1; -#endif -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, - uInt8 fg, uInt8 bg) +void FrameBufferGL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { // First draw the box in the background, alpha-blended - // We don't care about the specified bg color, since - // we always want black with alpha glEnable(GL_BLEND); glColor4f(0.0, 0.0, 0.0, 0.7); glRecti(x, y, x+w, y+h); // Now draw the outer edges - // Again, we don't care about the provided fg color, since - // we always want a light grey with no alpha glDisable(GL_BLEND); glColor3f(0.8, 0.8, 0.8); glBegin(GL_LINE_LOOP); @@ -435,8 +285,7 @@ void FrameBufferGL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::drawText(uInt32 xorig, uInt32 yorig, - const string& message, uInt8 fg) +void FrameBufferGL::drawText(uInt32 xorig, uInt32 yorig, const string& message) { glBindTexture(GL_TEXTURE_2D, myFontTextureID); glEnable(GL_BLEND); @@ -459,7 +308,7 @@ void FrameBufferGL::drawText(uInt32 xorig, uInt32 yorig, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferGL::drawChar(uInt32 x, uInt32 y, uInt32 c, uInt8 fg) +void FrameBufferGL::drawChar(uInt32 x, uInt32 y, uInt32 c) { if(c >= 256 ) return; diff --git a/stella/src/ui/sdl/FrameBufferGL.hxx b/stella/src/ui/sdl/FrameBufferGL.hxx index ea7d141cd..5d17178a0 100644 --- a/stella/src/ui/sdl/FrameBufferGL.hxx +++ b/stella/src/ui/sdl/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.1 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBufferGL.hxx,v 1.2 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_GL_HXX @@ -24,12 +24,19 @@ #include #include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" #include "bspf.hxx" class Console; class MediaSource; -class FrameBufferGL : public FrameBuffer +/** + This class implements an SDL OpenGL framebuffer. + + @author Stephen Anthony + @version $Id: FrameBufferGL.hxx,v 1.2 2003-11-09 23:53:20 stephena Exp $ +*/ +class FrameBufferGL : public FrameBufferSDL { public: /** @@ -42,6 +49,24 @@ class FrameBufferGL : public FrameBuffer */ virtual ~FrameBufferGL(); + ////////////////////////////////////////////////////////////////////// + // The following methods are derived from FrameBufferSDL.hxx + ////////////////////////////////////////////////////////////////////// + /** + This routine is called whenever the screen needs to be recreated. + It updates the global screen variable. + */ + virtual bool createScreen(); + + /** + Set up the palette for a screen of any depth > 8. + Scales the palette by 'shade'. + */ + virtual void setupPalette(float shade); + + ////////////////////////////////////////////////////////////////////// + // The following methods are derived from FrameBuffer.hxx + ////////////////////////////////////////////////////////////////////// /** This routine should be called once the console is created to setup the video system for us to use. Return false if any operation fails, @@ -63,10 +88,8 @@ class FrameBufferGL : public FrameBuffer @param y The y coordinate @param w The width of the box @param h The height of the box - @param fg The color of the bounding sides - @param bg The color of the background */ - virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt8 fg, uInt8 bg); + virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h); /** This routine should be called to draw text at the specified coordinates. @@ -74,9 +97,8 @@ class FrameBufferGL : public FrameBuffer @param x The x coordinate @param y The y coordinate @param message The message text - @param fg The color of the text */ - virtual void drawText(uInt32 x, uInt32 y, const string& message, uInt8 fg); + virtual void drawText(uInt32 x, uInt32 y, const string& message); /** This routine should be called to draw character 'c' at the specified coordinates. @@ -84,9 +106,8 @@ class FrameBufferGL : public FrameBuffer @param x The x coordinate @param y The y coordinate @param c The character to draw - @param fg The color of the character */ - virtual void drawChar(uInt32 x, uInt32 y, uInt32 c, uInt8 fg); + virtual void drawChar(uInt32 x, uInt32 y, uInt32 c); /** This routine is called before any drawing is done (per-frame). @@ -98,67 +119,7 @@ class FrameBufferGL : public FrameBuffer */ virtual void postFrameUpdate(); - /** - This routine is called when the emulation has been paused. - - @param status Toggle pause based on status - */ - virtual void pause(bool status); - - /** - Toggles between fullscreen and window mode. Grabmouse and hidecursor - activated when in fullscreen mode. - */ - void toggleFullscreen(); - - /** - This routine is called when the user wants to resize the window. - A '1' argument indicates that the window should increase in size, while '-1' - indicates that the windows should decrease in size. A '0' indicates that - the window should be sized according to the current properties. - Can't resize in fullscreen mode. Will only resize up to the maximum size - of the screen. - */ - void resize(int mode); - - /** - Shows or hides the cursor based on the given boolean value. - */ - void showCursor(bool show); - - /** - Grabs or ungrabs the mouse based on the given boolean value. - */ - void grabMouse(bool grab); - - /** - Set up the palette for a screen of any depth > 8. - Scales the palette by 'shade'. - */ - void setupPalette(float shade); - - /** - Answers if the display is currently in fullscreen mode. - */ - bool fullScreen() { return isFullscreen; } - - /** - Answers the current zoom level of the SDL window - */ - uInt32 zoomLevel() { return theZoomLevel; } - private: - /** - This routine is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - bool createScreen(); - - /** - Calculate the maximum window size that the current screen can hold. - Only works in X11 for now. If not running under X11, always return 1. - */ - uInt32 maxWindowSizeForScreen(); bool createTextures(); @@ -171,9 +132,6 @@ class FrameBufferGL : public FrameBuffer } private: - // The SDL video buffer - SDL_Surface* myScreen; - // The main texture buffer SDL_Surface* myTexture; @@ -197,36 +155,6 @@ class FrameBufferGL : public FrameBuffer // OpenGL texture coordinates for the font surface Coordinates myFontCoord[256]; - - // SDL initialization flags - uInt32 mySDLFlags; - - // SDL palette - uInt32 myPalette[256]; - - // Used to get window-manager specifics - SDL_SysWMinfo myWMInfo; - - // Indicates if we are running under X11 - bool x11Available; - - // Indicates the current zoom level of the SDL screen - uInt32 theZoomLevel; - - // Indicates the maximum zoom of the SDL screen - uInt32 theMaxZoomLevel; - - // Indicates whether the window is currently centered - bool isCentered; - - // Indicates if the mouse should be grabbed - bool theGrabMouseIndicator; - - // Indicates if the mouse cursor should be hidden - bool theHideCursorIndicator; - - // Indicates whether the game is currently in fullscreen - bool isFullscreen; }; #endif diff --git a/stella/src/ui/sdl/FrameBufferSDL.cxx b/stella/src/ui/sdl/FrameBufferSDL.cxx index c4cf26ebe..b884e4e29 100644 --- a/stella/src/ui/sdl/FrameBufferSDL.cxx +++ b/stella/src/ui/sdl/FrameBufferSDL.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: FrameBufferSDL.cxx,v 1.2 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBufferSDL.cxx,v 1.3 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #include @@ -31,7 +31,6 @@ FrameBufferSDL::FrameBufferSDL() : x11Available(false), theZoomLevel(1), theMaxZoomLevel(1), - isCentered(false), theGrabMouseIndicator(false), theHideCursorIndicator(false), isFullscreen(false) @@ -41,299 +40,6 @@ FrameBufferSDL::FrameBufferSDL() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FrameBufferSDL::~FrameBufferSDL() { - if(myRectList) - delete myRectList; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL::init() -{ - // Get the desired width and height of the display - myWidth = myMediaSource->width() << 1; - myHeight = myMediaSource->height(); - - // Now create the software SDL screen - Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER; - if(SDL_Init(initflags) < 0) - return false; - - // Check which system we are running under - x11Available = false; - SDL_VERSION(&myWMInfo.version); - if(SDL_GetWMInfo(&myWMInfo) > 0) - if(myWMInfo.subsystem == SDL_SYSWM_X11) - x11Available = true; - - // Get the maximum size of a window for THIS screen - theMaxZoomLevel = maxWindowSizeForScreen(); - - // Check to see if window size will fit in the screen - if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel) - theZoomLevel = theMaxZoomLevel; - else - theZoomLevel = myConsole->settings().getInt("zoom"); - - mySDLFlags = SDL_SWSURFACE; - mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0; - - // Set up the rectangle list to be used in the dirty update - myRectList = new RectList(); - if(!myRectList) - { - cerr << "ERROR: Unable to get memory for SDL rects" << endl; - return false; - } - - // Set the window title and icon - ostringstream name; - name << "Stella: \"" << myConsole->properties().get("Cartridge.Name") << "\""; - SDL_WM_SetCaption(name.str().c_str(), "stella"); - - // Create the screen - if(!createScreen()) - return false; - setupPalette(1.0); - - // Make sure that theUseFullScreenFlag sets up fullscreen mode correctly - theGrabMouseIndicator = myConsole->settings().getBool("grabmouse"); - theHideCursorIndicator = myConsole->settings().getBool("hidecursor"); - if(myConsole->settings().getBool("fullscreen")) - { - grabMouse(true); - showCursor(false); - isFullscreen = true; - } - else - { - // Keep mouse in game window if grabmouse is selected - grabMouse(theGrabMouseIndicator); - - // Show or hide the cursor depending on the 'hidecursor' argument - showCursor(!theHideCursorIndicator); - } - - // Center the window if centering is selected and not fullscreen - if(myConsole->settings().getBool("center") && - !myConsole->settings().getBool("fullscreen")) - centerScreen(); - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::drawMediaSource() -{ - uInt8* currentFrame = myMediaSource->currentFrameBuffer(); - uInt8* previousFrame = myMediaSource->previousFrameBuffer(); - uInt16 screenMultiple = (uInt16) theZoomLevel; - - uInt32 width = myMediaSource->width(); - uInt32 height = myMediaSource->height(); - - 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) || theRedrawEntireFrameIndicator) - { - 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) || theRedrawEntireFrameIndicator) - { - // 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, palette[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, palette[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, palette[active.color]); - } - - // The frame doesn't need to be completely redrawn anymore - theRedrawEntireFrameIndicator = false; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::preFrameUpdate() -{ - // Start a new rectlist on each display update - myRectList->start(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::postFrameUpdate() -{ - // Now update all the rectangles at once - SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool FrameBufferSDL::createScreen() -{ - int w = myWidth * theZoomLevel; - int h = myHeight * theZoomLevel; - - myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags); - if(myScreen == NULL) - { - cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl; - return false; - } - - theRedrawEntireFrameIndicator = true; - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::setupPalette(float shade) -{ - const uInt32* gamePalette = myMediaSource->palette(); - for(uInt32 i = 0; i < 256; ++i) - { - Uint8 r, g, b; - - r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade); - g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade); - b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade); - - switch(myScreen->format->BitsPerPixel) - { - case 15: - palette[i] = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); - break; - - case 16: - palette[i] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); - break; - - case 24: - case 32: - palette[i] = (r << 16) | (g << 8) | b; - break; - } - } - - theRedrawEntireFrameIndicator = true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -352,6 +58,12 @@ void FrameBufferSDL::pause(bool status) void FrameBufferSDL::toggleFullscreen() { isFullscreen = !isFullscreen; + + // Update the settings + ostringstream tmp; + tmp << isFullscreen; + myConsole->settings().set("fullscreen", tmp.str()); + if(isFullscreen) mySDLFlags |= SDL_FULLSCREEN; else @@ -369,9 +81,6 @@ void FrameBufferSDL::toggleFullscreen() { grabMouse(theGrabMouseIndicator); showCursor(!theHideCursorIndicator); - - if(myConsole->settings().getBool("center")) - centerScreen(); } } @@ -409,13 +118,7 @@ void FrameBufferSDL::resize(int mode) if(!createScreen()) return; - // A resize may mean that the window is no longer centered - isCentered = false; - - if(myConsole->settings().getBool("center")) - centerScreen(); - - // Now update the settings + // Update the settings ostringstream tmp; tmp << theZoomLevel; myConsole->settings().set("zoom", tmp.str()); @@ -431,6 +134,11 @@ void FrameBufferSDL::showCursor(bool show) SDL_ShowCursor(SDL_ENABLE); else SDL_ShowCursor(SDL_DISABLE); + + // Update the settings + ostringstream tmp; + tmp << !show; + myConsole->settings().set("hidecursor", tmp.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -443,35 +151,11 @@ void FrameBufferSDL::grabMouse(bool grab) SDL_WM_GrabInput(SDL_GRAB_ON); else SDL_WM_GrabInput(SDL_GRAB_OFF); -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::centerScreen() -{ - if(!x11Available) - { - cerr << "Window centering only available under X11.\n"; - return; - } - - if(isFullscreen || isCentered) - return; - - uInt32 x, y, w, h; - myWMInfo.info.x11.lock_func(); - Display* theX11Display = myWMInfo.info.x11.display; - Window theX11Window = myWMInfo.info.x11.wmwindow; - - w = DisplayWidth(theX11Display, DefaultScreen(theX11Display)); - h = DisplayHeight(theX11Display, DefaultScreen(theX11Display)); - x = (w - myScreen->w)/2; - y = (h - myScreen->h)/2; - - XMoveWindow(theX11Display, theX11Window, x, y); - myWMInfo.info.x11.unlock_func(); - - isCentered = true; - theRedrawEntireFrameIndicator = true; + // Update the settings + ostringstream tmp; + tmp << grab; + myConsole->settings().set("grabmouse", tmp.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -480,6 +164,7 @@ uInt32 FrameBufferSDL::maxWindowSizeForScreen() if(!x11Available) return 1; +#ifdef UNIX // Otherwise, lock the screen and get the width and height myWMInfo.info.x11.lock_func(); Display* theX11Display = myWMInfo.info.x11.display; @@ -507,160 +192,7 @@ uInt32 FrameBufferSDL::maxWindowSizeForScreen() return multiplier; else return 1; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, - uInt8 fg, uInt8 bg) -{ - SDL_Rect tmp; - - // Scale all values to the current window size - x *= theZoomLevel; - y *= theZoomLevel; - w *= theZoomLevel; - h *= theZoomLevel; - - // First draw the underlying box - tmp.x = x; - tmp.y = y; - tmp.w = w; - tmp.h = h; - myRectList->add(&tmp); - SDL_FillRect(myScreen, &tmp, palette[bg]); - - // Now draw the bounding sides - tmp.x = x; - tmp.y = y; - tmp.w = w; - tmp.h = theZoomLevel; - SDL_FillRect(myScreen, &tmp, palette[fg]); // top - - tmp.x = x; - tmp.y = y + h - theZoomLevel; - tmp.w = w; - tmp.h = theZoomLevel; - SDL_FillRect(myScreen, &tmp, palette[fg]); // bottom - - tmp.x = x; - tmp.y = y; - tmp.w = theZoomLevel; - tmp.h = h; - SDL_FillRect(myScreen, &tmp, palette[fg]); // left - - tmp.x = x + w - theZoomLevel; - tmp.y = y; - tmp.w = theZoomLevel; - tmp.h = h; - SDL_FillRect(myScreen, &tmp, palette[fg]); // right -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::drawText(uInt32 xorig, uInt32 yorig, - const string& message, uInt8 fg) -{ - SDL_Rect tmp; - - uInt8 length = message.length(); - for(uInt32 x = 0; x < length; x++) - { - for(uInt32 y = 0; y < 8; y++) - { - for(uInt32 z = 0; z < 8; z++) - { - char letter = message[x]; - if((ourFontData[(letter << 3) + y] >> z) & 1) - { -// myFrameBuffer[(y + yorig)*myWidth + (x<<3) + z + xorig] = 0xF0F0F0; - tmp.x = ((x<<3) + z + xorig) * theZoomLevel; - tmp.y = (y + yorig) * theZoomLevel; - tmp.w = tmp.h = theZoomLevel; - SDL_FillRect(myScreen, &tmp, palette[fg]); -// FIXME - this can be a lot more efficient - } - } - } - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL::drawChar(uInt32 xorig, uInt32 yorig, uInt32 c, uInt8 fg) -{ - if(c >= 256 ) - return; - - SDL_Rect tmp; - - for(uInt32 y = 0; y < 8; y++) - { - for(uInt32 z = 0; z < 8; z++) - { - if((ourFontData[(c << 3) + y] >> z) & 1) - { -// myFrameBuffer[(y + yorig)*myWidth + z + xorig] = 0xF0F0F0; - tmp.x = (z + xorig) * theZoomLevel; - tmp.y = (y + yorig) * theZoomLevel; - tmp.w = tmp.h = theZoomLevel; - myRectList->add(&tmp); - SDL_FillRect(myScreen, &tmp, palette[fg]); -// FIXME - this can be a lot more efficient - } - } - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -RectList::RectList(Uint32 size) -{ - currentSize = size; - currentRect = 0; - - rectArray = new SDL_Rect[currentSize]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -RectList::~RectList() -{ - delete[] rectArray; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RectList::add(SDL_Rect* newRect) -{ - if(currentRect >= currentSize) - { - currentSize = currentSize * 2; - SDL_Rect *temp = new SDL_Rect[currentSize]; - - for(Uint32 i = 0; i < currentRect; ++i) - temp[i] = rectArray[i]; - - delete[] rectArray; - rectArray = temp; - } - - rectArray[currentRect].x = newRect->x; - rectArray[currentRect].y = newRect->y; - rectArray[currentRect].w = newRect->w; - rectArray[currentRect].h = newRect->h; - - ++currentRect; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SDL_Rect* RectList::rects() -{ - return rectArray; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Uint32 RectList::numRects() -{ - return currentRect; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RectList::start() -{ - currentRect = 0; +#else + return 1; +#endif } diff --git a/stella/src/ui/sdl/FrameBufferSDL.hxx b/stella/src/ui/sdl/FrameBufferSDL.hxx index 763e43eb4..4f95abfe3 100644 --- a/stella/src/ui/sdl/FrameBufferSDL.hxx +++ b/stella/src/ui/sdl/FrameBufferSDL.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: FrameBufferSDL.hxx,v 1.2 2003-11-06 22:22:32 stephena Exp $ +// $Id: FrameBufferSDL.hxx,v 1.3 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #ifndef FRAMEBUFFER_SDL_HXX @@ -25,15 +25,22 @@ #include "FrameBuffer.hxx" #include "bspf.hxx" -class Console; -class MediaSource; -class RectList; +/** + This class is a base class for the SDL framebuffer and is derived from + the core FrameBuffer class. + It defines all common code shared between the software + and OpenGL video modes, as well as required methods defined in + the core FrameBuffer. + + @author Stephen Anthony + @version $Id: FrameBufferSDL.hxx,v 1.3 2003-11-09 23:53:20 stephena Exp $ +*/ class FrameBufferSDL : public FrameBuffer { public: /** - Creates a new SDL software framebuffer + Creates a new SDL framebuffer */ FrameBufferSDL(); @@ -42,68 +49,6 @@ class FrameBufferSDL : public FrameBuffer */ virtual ~FrameBufferSDL(); - /** - This routine should be called once the console is created to setup - the video system for us to use. Return false if any operation fails, - otherwise return true. - */ - virtual bool init(); - - /** - This routine should be called anytime the MediaSource needs to be redrawn - to the screen. - */ - virtual void drawMediaSource(); - - /** - This routine should be called to draw a rectangular box with sides - at the specified coordinates. - - @param x The x coordinate - @param y The y coordinate - @param w The width of the box - @param h The height of the box - @param fg The color of the bounding sides - @param bg The color of the background - */ - virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt8 fg, uInt8 bg); - - /** - This routine should be called to draw text at the specified coordinates. - - @param x The x coordinate - @param y The y coordinate - @param message The message text - @param fg The color of the text - */ - virtual void drawText(uInt32 x, uInt32 y, const string& message, uInt8 fg); - - /** - This routine should be called to draw character 'c' at the specified coordinates. - - @param x The x coordinate - @param y The y coordinate - @param c The character to draw - @param fg The color of the character - */ - virtual void drawChar(uInt32 x, uInt32 y, uInt32 c, uInt8 fg); - - /** - This routine is called before any drawing is done (per-frame). - */ - virtual void preFrameUpdate(); - - /** - This routine is called after any drawing is done (per-frame). - */ - virtual void postFrameUpdate(); - - /** - This routine is called when the emulation has been paused. - - @param status Toggle pause based on status - */ - virtual void pause(bool status); /** Toggles between fullscreen and window mode. Grabmouse and hidecursor @@ -141,41 +86,46 @@ class FrameBufferSDL : public FrameBuffer */ uInt32 zoomLevel() { return theZoomLevel; } - /** - This routine is called whenever the screen needs to be recreated. - It updates the global screen variable. - */ - bool createScreen(); - - /** - Centers the game window onscreen. Only works in X11 for now. - */ - void centerScreen(); - /** Calculate the maximum window size that the current screen can hold. Only works in X11 for now. If not running under X11, always return 4. */ uInt32 maxWindowSizeForScreen(); + ////////////////////////////////////////////////////////////////////// + // The following methods are derived from FrameBuffer.hxx + ////////////////////////////////////////////////////////////////////// + /** + This routine is called when the emulation has been paused. + + @param status Toggle pause based on status + */ + void pause(bool status); + + ////////////////////////////////////////////////////////////////////// + // The following methods must be defined in child classes + ////////////////////////////////////////////////////////////////////// + /** + This routine is called whenever the screen needs to be recreated. + It updates the global screen variable. + */ + virtual bool createScreen() = 0; + /** Set up the palette for a screen of any depth > 8. Scales the palette by 'shade'. */ - void setupPalette(float shade); + virtual void setupPalette(float shade) = 0; - private: + protected: // The SDL video buffer SDL_Surface* myScreen; - // Used in the dirty update of the SDL surface - RectList* myRectList; - // SDL initialization flags uInt32 mySDLFlags; // SDL palette - Uint32 palette[256]; + Uint32 myPalette[256]; // Used to get window-manager specifics SDL_SysWMinfo myWMInfo; @@ -189,9 +139,6 @@ class FrameBufferSDL : public FrameBuffer // Indicates the maximum zoom of the SDL screen uInt32 theMaxZoomLevel; - // Indicates whether the window is currently centered - bool isCentered; - // Indicates if the mouse should be grabbed bool theGrabMouseIndicator; @@ -202,25 +149,4 @@ class FrameBufferSDL : public FrameBuffer bool isFullscreen; }; -/** - - */ -class RectList -{ - public: - RectList(Uint32 size = 512); - ~RectList(); - - void add(SDL_Rect* rect); - - SDL_Rect* rects(); - Uint32 numRects(); - void start(); - - private: - Uint32 currentSize, currentRect; - - SDL_Rect* rectArray; -}; - #endif diff --git a/stella/src/ui/sdl/FrameBufferSoft.cxx b/stella/src/ui/sdl/FrameBufferSoft.cxx new file mode 100644 index 000000000..d9a921682 --- /dev/null +++ b/stella/src/ui/sdl/FrameBufferSoft.cxx @@ -0,0 +1,475 @@ +//============================================================================ +// +// 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-1999 by Bradford W. Mott +// +// 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.1 2003-11-09 23:53:20 stephena Exp $ +//============================================================================ + +#include +#include +#include + +#include "Console.hxx" +#include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" +#include "FrameBufferSoft.hxx" +#include "MediaSrc.hxx" +#include "Settings.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FrameBufferSoft::FrameBufferSoft() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FrameBufferSoft::~FrameBufferSoft() +{ + if(myRectList) + delete myRectList; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FrameBufferSoft::createScreen() +{ + int w = myWidth * theZoomLevel; + int h = myHeight * theZoomLevel; + + myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags); + if(myScreen == NULL) + { + cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl; + return false; + } + + theRedrawEntireFrameIndicator = true; + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::setupPalette(float shade) +{ + const uInt32* gamePalette = myMediaSource->palette(); + for(uInt32 i = 0; i < 256; ++i) + { + Uint8 r, g, b; + + r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade); + g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade); + b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade); + + switch(myScreen->format->BitsPerPixel) + { + case 15: + myPalette[i] = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); + break; + + case 16: + myPalette[i] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + break; + + case 24: + case 32: + myPalette[i] = (r << 16) | (g << 8) | b; + break; + } + } + + theRedrawEntireFrameIndicator = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool FrameBufferSoft::init() +{ + // Get the desired width and height of the display + myWidth = myMediaSource->width() << 1; + myHeight = myMediaSource->height(); + + // Now create the software SDL screen + Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER; + if(SDL_Init(initflags) < 0) + return false; + + // Check which system we are running under + x11Available = false; + SDL_VERSION(&myWMInfo.version); + if(SDL_GetWMInfo(&myWMInfo) > 0) + if(myWMInfo.subsystem == SDL_SYSWM_X11) + x11Available = true; + + // Get the maximum size of a window for THIS screen + theMaxZoomLevel = maxWindowSizeForScreen(); + + // Check to see if window size will fit in the screen + if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel) + theZoomLevel = theMaxZoomLevel; + else + theZoomLevel = myConsole->settings().getInt("zoom"); + + mySDLFlags = SDL_SWSURFACE; + mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0; + + // Set up the rectangle list to be used in the dirty update + myRectList = new RectList(); + if(!myRectList) + { + cerr << "ERROR: Unable to get memory for SDL rects" << endl; + return false; + } + + // Set the window title and icon + ostringstream name; + name << "Stella: \"" << myConsole->properties().get("Cartridge.Name") << "\""; + SDL_WM_SetCaption(name.str().c_str(), "stella"); + + // Create the screen + if(!createScreen()) + return false; + setupPalette(1.0); + + // Make sure that theUseFullScreenFlag sets up fullscreen mode correctly + theGrabMouseIndicator = myConsole->settings().getBool("grabmouse"); + theHideCursorIndicator = myConsole->settings().getBool("hidecursor"); + if(myConsole->settings().getBool("fullscreen")) + { + grabMouse(true); + showCursor(false); + isFullscreen = true; + } + else + { + // Keep mouse in game window if grabmouse is selected + grabMouse(theGrabMouseIndicator); + + // Show or hide the cursor depending on the 'hidecursor' argument + showCursor(!theHideCursorIndicator); + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::drawMediaSource() +{ + uInt8* currentFrame = myMediaSource->currentFrameBuffer(); + uInt8* previousFrame = myMediaSource->previousFrameBuffer(); + uInt16 screenMultiple = (uInt16) theZoomLevel; + + uInt32 width = myMediaSource->width(); + uInt32 height = myMediaSource->height(); + + 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) || theRedrawEntireFrameIndicator) + { + 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) || theRedrawEntireFrameIndicator) + { + // 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, myPalette[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, myPalette[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, myPalette[active.color]); + } + + // The frame doesn't need to be completely redrawn anymore + theRedrawEntireFrameIndicator = false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::preFrameUpdate() +{ + // Start a new rectlist on each display update + myRectList->start(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::postFrameUpdate() +{ + // Now update all the rectangles at once + SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h) +{ + SDL_Rect tmp; + + // Scale all values to the current window size + x *= theZoomLevel; + y *= theZoomLevel; + w *= theZoomLevel; + h *= theZoomLevel; + + // First draw the underlying box + tmp.x = x; + tmp.y = y; + tmp.w = w; + tmp.h = h; + myRectList->add(&tmp); + SDL_FillRect(myScreen, &tmp, myPalette[myBGColor]); + + // Now draw the bounding sides + tmp.x = x; + tmp.y = y; + tmp.w = w; + tmp.h = theZoomLevel; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // top + + tmp.x = x; + tmp.y = y + h - theZoomLevel; + tmp.w = w; + tmp.h = theZoomLevel; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // bottom + + tmp.x = x; + tmp.y = y; + tmp.w = theZoomLevel; + tmp.h = h; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // left + + tmp.x = x + w - theZoomLevel; + tmp.y = y; + tmp.w = theZoomLevel; + tmp.h = h; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // right +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::drawText(uInt32 xorig, uInt32 yorig, const string& message) +{ + SDL_Rect tmp; + + uInt8 length = message.length(); + for(uInt32 x = 0; x < length; x++) + { + for(uInt32 y = 0; y < 8; y++) + { + for(uInt32 z = 0; z < 8; z++) + { + char letter = message[x]; + if((ourFontData[(letter << 3) + y] >> z) & 1) + { + tmp.x = ((x<<3) + z + xorig) * theZoomLevel; + tmp.y = (y + yorig) * theZoomLevel; + tmp.w = tmp.h = theZoomLevel; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); + } + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBufferSoft::drawChar(uInt32 xorig, uInt32 yorig, uInt32 c) +{ + if(c >= 256 ) + return; + + SDL_Rect tmp; + + for(uInt32 y = 0; y < 8; y++) + { + for(uInt32 z = 0; z < 8; z++) + { + if((ourFontData[(c << 3) + y] >> z) & 1) + { + tmp.x = (z + xorig) * theZoomLevel; + tmp.y = (y + yorig) * theZoomLevel; + tmp.w = tmp.h = theZoomLevel; + SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +RectList::RectList(Uint32 size) +{ + currentSize = size; + currentRect = 0; + + rectArray = new SDL_Rect[currentSize]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +RectList::~RectList() +{ + delete[] rectArray; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RectList::add(SDL_Rect* newRect) +{ + if(currentRect >= currentSize) + { + currentSize = currentSize * 2; + SDL_Rect *temp = new SDL_Rect[currentSize]; + + for(Uint32 i = 0; i < currentRect; ++i) + temp[i] = rectArray[i]; + + delete[] rectArray; + rectArray = temp; + } + + rectArray[currentRect].x = newRect->x; + rectArray[currentRect].y = newRect->y; + rectArray[currentRect].w = newRect->w; + rectArray[currentRect].h = newRect->h; + + ++currentRect; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SDL_Rect* RectList::rects() +{ + return rectArray; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Uint32 RectList::numRects() +{ + return currentRect; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RectList::start() +{ + currentRect = 0; +} diff --git a/stella/src/ui/sdl/FrameBufferSoft.hxx b/stella/src/ui/sdl/FrameBufferSoft.hxx new file mode 100644 index 000000000..9ffbede49 --- /dev/null +++ b/stella/src/ui/sdl/FrameBufferSoft.hxx @@ -0,0 +1,149 @@ +//============================================================================ +// +// 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-1999 by Bradford W. Mott +// +// 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.1 2003-11-09 23:53:20 stephena Exp $ +//============================================================================ + +#ifndef FRAMEBUFFER_SOFT_HXX +#define FRAMEBUFFER_SOFT_HXX + +#include +#include + +#include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" +#include "bspf.hxx" + +class Console; +class MediaSource; +class RectList; + + +/** + This class implements an SDL software framebuffer. + + @author Stephen Anthony + @version $Id: FrameBufferSoft.hxx,v 1.1 2003-11-09 23:53:20 stephena Exp $ +*/ +class FrameBufferSoft : public FrameBufferSDL +{ + public: + /** + Creates a new SDL software framebuffer + */ + FrameBufferSoft(); + + /** + Destructor + */ + virtual ~FrameBufferSoft(); + + ////////////////////////////////////////////////////////////////////// + // The following methods are derived from FrameBufferSDL.hxx + ////////////////////////////////////////////////////////////////////// + /** + This routine is called whenever the screen needs to be recreated. + It updates the global screen variable. + */ + virtual bool createScreen(); + + /** + Set up the palette for a screen of any depth > 8. + Scales the palette by 'shade'. + */ + virtual void setupPalette(float shade); + + ////////////////////////////////////////////////////////////////////// + // The following methods are derived from FrameBuffer.hxx + ////////////////////////////////////////////////////////////////////// + /** + This routine should be called once the console is created to setup + the video system for us to use. Return false if any operation fails, + otherwise return true. + */ + virtual bool init(); + + /** + This routine should be called anytime the MediaSource needs to be redrawn + to the screen. + */ + virtual void drawMediaSource(); + + /** + This routine should be called to draw a rectangular box with sides + at the specified coordinates. + + @param x The x coordinate + @param y The y coordinate + @param w The width of the box + @param h The height of the box + */ + virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h); + + /** + This routine should be called to draw text at the specified coordinates. + + @param x The x coordinate + @param y The y coordinate + @param message The message text + */ + virtual void drawText(uInt32 x, uInt32 y, const string& message); + + /** + This routine should be called to draw character 'c' at the specified coordinates. + + @param x The x coordinate + @param y The y coordinate + @param c The character to draw + */ + virtual void drawChar(uInt32 x, uInt32 y, uInt32 c); + + /** + This routine is called before any drawing is done (per-frame). + */ + virtual void preFrameUpdate(); + + /** + This routine is called after any drawing is done (per-frame). + */ + virtual void postFrameUpdate(); + + private: + // Used in the dirty update of the SDL surface + RectList* myRectList; +}; + +/** + + */ +class RectList +{ + public: + RectList(Uint32 size = 512); + ~RectList(); + + void add(SDL_Rect* rect); + + SDL_Rect* rects(); + Uint32 numRects(); + void start(); + + private: + Uint32 currentSize, currentRect; + + SDL_Rect* rectArray; +}; + +#endif diff --git a/stella/src/ui/sdl/SettingsUNIX.cxx b/stella/src/ui/sdl/SettingsUNIX.cxx index 40dfad451..cda1b5e00 100644 --- a/stella/src/ui/sdl/SettingsUNIX.cxx +++ b/stella/src/ui/sdl/SettingsUNIX.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: SettingsUNIX.cxx,v 1.2 2003-11-06 22:22:32 stephena Exp $ +// $Id: SettingsUNIX.cxx,v 1.3 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #include @@ -62,20 +62,21 @@ SettingsUNIX::SettingsUNIX() myStateFile = ""; // Now create UNIX specific settings + set("video", "soft"); #ifdef DISPLAY_OPENGL set("gl_filter", "nearest"); #endif + set("sound", "oss"); + set("fullscreen", "false"); + set("grabmouse", "false"); + set("hidecursor", "false"); + set("volume", "-1"); + set("accurate", "true"); #ifdef SNAPSHOT_SUPPORT set("ssname", "romname"); set("ssdir", "./"); set("ssingle", "false"); #endif - set("fullscreen", "false"); - set("grabmouse", "false"); - set("hidecursor", "false"); - set("accurate", "true"); - set("volume", "-1"); - set("sound", "oss"); set("joyleft", "0"); set("joyright", "1"); } @@ -93,22 +94,26 @@ void SettingsUNIX::usage(string& message) << endl << "Valid options are:" << endl << endl + << " -video Type is one of the following:\n" + << " soft SDL software mode\n" #ifdef DISPLAY_OPENGL + << " gl SDL OpenGL mode\n" + << endl << " -gl_filter Type is one of the following:\n" << " nearest Normal scaling (GL_NEAREST)\n" << " linear Blurred scaling (GL_LINEAR)\n" << endl #endif - << " -sound Type is one of the following:\n" - << " 0 Disables all sound generation\n" + << " -sound Type is one of the following:\n" + << " 0 Disables all sound generation\n" #ifdef SOUND_ALSA - << " alsa ALSA version 0.9 driver\n" + << " alsa ALSA version 0.9 driver\n" #endif #ifdef SOUND_OSS - << " oss Open Sound System driver\n" + << " oss Open Sound System driver\n" #endif #ifdef SOUND_SDL - << " SDL Native SDL driver\n" + << " sdl Native SDL driver\n" #endif << endl << " -framerate Display the given number of frames per second\n" @@ -116,7 +121,6 @@ void SettingsUNIX::usage(string& message) << " -fullscreen <0|1> Play the game in fullscreen mode\n" << " -grabmouse <0|1> Keeps the mouse in the game window\n" << " -hidecursor <0|1> Hides the mouse cursor in the game window\n" - << " -center <0|1> Centers the game window onscreen\n" << " -volume Set the volume (0 - 100)\n" #ifdef HAVE_JOYSTICK << " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n" diff --git a/stella/src/ui/sdl/mainSDL.cxx b/stella/src/ui/sdl/mainSDL.cxx index a2f7c0928..79276e17b 100644 --- a/stella/src/ui/sdl/mainSDL.cxx +++ b/stella/src/ui/sdl/mainSDL.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: mainSDL.cxx,v 1.57 2003-11-06 22:22:32 stephena Exp $ +// $Id: mainSDL.cxx,v 1.58 2003-11-09 23:53:20 stephena Exp $ //============================================================================ #include @@ -35,18 +35,14 @@ #include "StellaEvent.hxx" #include "EventHandler.hxx" #include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" +#include "FrameBufferSoft.hxx" #include "PropsSet.hxx" #include "Sound.hxx" #include "Settings.hxx" #ifdef DISPLAY_OPENGL #include "FrameBufferGL.hxx" - // Pointer to the OpenGL display object or the null pointer - static FrameBufferGL* theDisplay = (FrameBufferGL*) NULL; -#else - #include "FrameBufferSDL.hxx" - // Pointer to the software display object or the null pointer - static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL; #endif #ifdef SOUND_ALSA @@ -83,6 +79,9 @@ static bool setupProperties(PropertiesSet& set); // Pointer to the console object or the null pointer static Console* theConsole = (Console*) NULL; +// Pointer to the display object or the null pointer +static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL; + // Pointer to the sound object or the null pointer static Sound* theSound = (Sound*) NULL; @@ -704,15 +703,27 @@ int main(int argc, char* argv[]) } // Create an SDL window + string videodriver = theSettings->getString("video"); + if(videodriver == "soft") + { + theDisplay = new FrameBufferSoft(); + if(theShowInfoFlag) + cout << "Using software mode for video.\n"; + } #ifdef DISPLAY_OPENGL - theDisplay = new FrameBufferGL(); - if(theShowInfoFlag) - cout << "Using OpenGL SDL for video.\n"; -#else - theDisplay = new FrameBufferSDL(); - if(theShowInfoFlag) - cout << "Using software SDL for video.\n"; + else if(videodriver == "gl") + { + theDisplay = new FrameBufferGL(); + if(theShowInfoFlag) + cout << "Using OpenGL mode for video.\n"; + } #endif + else // a driver that doesn't exist was requested, so use software mode + { + theDisplay = new FrameBufferSoft(); + if(theShowInfoFlag) + cout << "Using software mode for video.\n"; + } if(!theDisplay) { @@ -722,8 +733,8 @@ int main(int argc, char* argv[]) } // Create a sound object for playing audio - string driver = theSettings->getString("sound"); - if(driver == "0") + string sounddriver = theSettings->getString("sound"); + if(sounddriver == "0") { // even if sound has been disabled, we still need a sound object theSound = new Sound(); @@ -731,7 +742,7 @@ int main(int argc, char* argv[]) cout << "Sound disabled.\n"; } #ifdef SOUND_ALSA - else if(driver == "alsa") + else if(sounddriver == "alsa") { theSound = new SoundALSA(); if(theShowInfoFlag) @@ -739,7 +750,7 @@ int main(int argc, char* argv[]) } #endif #ifdef SOUND_OSS - else if(driver == "oss") + else if(sounddriver == "oss") { theSound = new SoundOSS(); if(theShowInfoFlag) @@ -747,7 +758,7 @@ int main(int argc, char* argv[]) } #endif #ifdef SOUND_SDL - else if(driver == "sdl") + else if(sounddriver == "sdl") { theSound = new SoundSDL(); if(theShowInfoFlag) @@ -756,7 +767,7 @@ int main(int argc, char* argv[]) #endif else // a driver that doesn't exist was requested, so disable sound { - cerr << "ERROR: Sound support for " << driver << " not available.\n"; + cerr << "ERROR: Sound support for " << sounddriver << " not available.\n"; theSound = new Sound(); }