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
This commit is contained in:
stephena 2003-11-09 23:53:20 +00:00
parent 8239748980
commit fc96e5b0d8
11 changed files with 955 additions and 1114 deletions

View File

@ -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

View File

@ -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 <sstream>
@ -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)
// Then scale back down, if necessary
if(myRemapMenuHighIndex >= myRemapMenuItems)
{
case MAIN_MENU:
break;
Int32 x = myRemapMenuHighIndex - myRemapMenuItems;
case REMAP_MENU:
if(myRemapMenuLowIndex < myMaxLines)
{
myRemapMenuLowIndex = 0;
myRemapMenuHighIndex = myMaxLines;
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,8 +605,18 @@ void FrameBuffer::loadRemapMenu()
}
if(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;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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];

View File

@ -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 <SDL.h>
@ -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;

View File

@ -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 <SDL_syswm.h>
#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

View File

@ -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 <SDL.h>
@ -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
}

View File

@ -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

View File

@ -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 <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#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;
}

View File

@ -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 <SDL.h>
#include <SDL_syswm.h>
#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

View File

@ -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 <cstdlib>
@ -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,7 +94,11 @@ void SettingsUNIX::usage(string& message)
<< endl
<< "Valid options are:" << endl
<< endl
<< " -video <type> Type is one of the following:\n"
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
<< " linear Blurred scaling (GL_LINEAR)\n"
@ -108,7 +113,7 @@ void SettingsUNIX::usage(string& message)
<< " oss Open Sound System driver\n"
#endif
#ifdef SOUND_SDL
<< " SDL Native SDL driver\n"
<< " sdl Native SDL driver\n"
#endif
<< endl
<< " -framerate <number> 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 <number> Set the volume (0 - 100)\n"
#ifdef HAVE_JOYSTICK
<< " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n"

View File

@ -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 <fstream>
@ -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
else if(videodriver == "gl")
{
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";
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();
}