Added new rendering modes to FrameBufferSoft, which don't use dirty

rectangle merging.  This makes the 'dirtyrects' option more relevant,
since now if it's turned off, dirty rectangles are not merged, and
the screen is updated with SDL_Flip() instead of SDL_UpdateRects().
This new functionality is very similar to how z26 works, but
experiments on my Linux system show it to be twice as fast on
average :)  Dirty rectangle merging now defaults to off.  I'm
leaving it in, since it benefits people in some cases.  Basically,
non-dirty-rect support is optimal when many things are changing
onscreen at once, at the cost of more constant and generally slightly
higher CPU usage.  Dirty-rect support is optimal at larger resolutions,
where it's usually at least twice as fast as without, but is suboptimal
at larger resolutions when lots of stuff is changing.  At some point in
the future, maybe Stella itself can automatically switch modes depending
on which is faster at any point in time.

Added "[..]" previous directory functionality to BrowserWidget, the same
as already in the LauncherDialog.  Thanks for Alex and Lou for the
reminder.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1197 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2006-12-10 17:04:34 +00:00
parent de4ee80062
commit 6608b99d8c
10 changed files with 288 additions and 137 deletions

View File

@ -273,6 +273,9 @@ paddle mode for those ROMs that don't use paddle zero by default.</li>
the ROM properties to the internal defaults, deleting that ROMs properties
from the external properties file.</li>
<li>Fixed bug whereby modified ROM properties weren't being reloaded when
restarting a ROM.</li>
<li>Made 'phosphor' and 'phosphor blend' a ROM property, meaning it can be
set per-ROM.</li>

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: FrameBufferSoft.cxx,v 1.61 2006-12-08 16:48:56 stephena Exp $
// $Id: FrameBufferSoft.cxx,v 1.62 2006-12-10 17:04:33 stephena Exp $
//============================================================================
#include <SDL.h>
@ -31,7 +31,8 @@
FrameBufferSoft::FrameBufferSoft(OSystem* osystem)
: FrameBuffer(osystem),
myZoomLevel(1),
myRenderType(kSoftZoom),
myRenderType(kDirtyRect),
myUseDirtyRects(true),
myRectList(NULL),
myOverlayRectList(NULL)
{
@ -114,18 +115,9 @@ bool FrameBufferSoft::createScreen()
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
switch(myScreen->format->BitsPerPixel)
{
case 16:
myPitch = myScreen->pitch/2;
break;
case 24:
myPitch = myScreen->pitch;
break;
case 32:
myPitch = myScreen->pitch/4;
break;
}
// Make sure drawMediaSource() knows which renderer to use
stateChanged(myOSystem->eventHandler().state());
// Erase old rects, since they've probably been scaled for
// a different sized screen
@ -147,9 +139,9 @@ void FrameBufferSoft::drawMediaSource()
uInt32 width = mediasrc.width();
uInt32 height = mediasrc.height();
switch((int)myRenderType) // use switch/case, since we'll eventually have filters
switch(myRenderType) // use switch/case, since we'll eventually have filters
{
case kSoftZoom:
case kDirtyRect:
{
struct Rectangle
{
@ -289,7 +281,94 @@ void FrameBufferSoft::drawMediaSource()
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myDefPalette[active.color]);
}
break; // case 0
break; // kDirtyRect
}
case kSoftZoom_16:
{
SDL_Rect temp;
temp.x = temp.y = temp.w = temp.h = 0;
myRectList->add(&temp);
uInt16* buffer = (uInt16*)myScreen->pixels;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
if(v != w || theRedrawTIAIndicator)
{
while(xstride--)
{
buffer[pos++] = (uInt16) myDefPalette[v];
buffer[pos++] = (uInt16) myDefPalette[v];
}
}
else
pos += xstride + xstride;
}
screenofsY += myPitch;
}
bufofsY += width;
}
break; // kSoftZoom_16
}
case kSoftZoom_24:
{
break; // kSoftZoom_24
}
case kSoftZoom_32:
{
SDL_Rect temp;
temp.x = temp.y = temp.w = temp.h = 0;
myRectList->add(&temp);
uInt32* buffer = (uInt32*)myScreen->pixels;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
uInt8 v = currentFrame[bufofs];
uInt8 w = previousFrame[bufofs];
if(v != w || theRedrawTIAIndicator)
{
while(xstride--)
{
buffer[pos++] = (uInt32) myDefPalette[v];
buffer[pos++] = (uInt32) myDefPalette[v];
}
}
else
pos += xstride + xstride;
}
screenofsY += myPitch;
}
bufofsY += width;
}
break; // kSoftZoom_32
}
case kPhosphor_16:
@ -304,13 +383,13 @@ void FrameBufferSoft::drawMediaSource()
uInt16* buffer = (uInt16*)myScreen->pixels;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y )
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x )
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
@ -343,13 +422,13 @@ void FrameBufferSoft::drawMediaSource()
uInt8* buffer = (uInt8*)myScreen->pixels;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y )
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x )
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
@ -397,13 +476,13 @@ void FrameBufferSoft::drawMediaSource()
uInt32* buffer = (uInt32*)myScreen->pixels;
uInt32 bufofsY = 0;
uInt32 screenofsY = 0;
for(uInt32 y = 0; y < height; ++y )
for(uInt32 y = 0; y < height; ++y)
{
uInt32 ystride = myZoomLevel;
while(ystride--)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x )
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt32 xstride = myZoomLevel;
@ -646,29 +725,7 @@ void FrameBufferSoft::enablePhosphor(bool enable, int blend)
myUsePhosphor = enable;
myPhosphorBlend = blend;
if(myUsePhosphor)
{
switch(myScreen->format->BitsPerPixel)
{
case 16:
myPitch = myScreen->pitch/2;
myRenderType = kPhosphor_16;
break;
case 24:
myPitch = myScreen->pitch;
myRenderType = kPhosphor_24;
break;
case 32:
myPitch = myScreen->pitch/4;
myRenderType = kPhosphor_32;
break;
default:
myRenderType = kSoftZoom; // What else should we do here?
break;
}
}
else
myRenderType = kSoftZoom;
stateChanged(myOSystem->eventHandler().state());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -680,3 +737,60 @@ void FrameBufferSoft::cls()
SDL_UpdateRect(myScreen, 0, 0, 0, 0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::stateChanged(EventHandler::State state)
{
if(!myScreen)
return;
// When in a UI mode, always use dirty rects
// Otherwise, check the 'dirtyrects' setting
bool emulation = state == EventHandler::S_EMULATE;
if(emulation)
myUseDirtyRects = myOSystem->settings().getBool("dirtyrects");
else
myUseDirtyRects = true;
// Make sure drawMediaSource() knows which renderer to use
// Testing for dirty rects takes priority over phosphor mode,
// since phosphor mode only exists while emulating a ROM
switch(myScreen->format->BitsPerPixel)
{
case 16:
myPitch = myScreen->pitch/2;
if(myUsePhosphor && emulation)
myRenderType = kPhosphor_16;
else if(myUseDirtyRects)
myRenderType = kDirtyRect;
else
myRenderType = kSoftZoom_16;
break;
case 24:
myPitch = myScreen->pitch;
if(myUsePhosphor && emulation)
myRenderType = kPhosphor_24;
else if(myUseDirtyRects)
myRenderType = kDirtyRect;
else
myRenderType = kSoftZoom_24;
break;
case 32:
myPitch = myScreen->pitch/4;
if(myUsePhosphor && emulation)
myRenderType = kPhosphor_32;
else if(myUseDirtyRects)
myRenderType = kDirtyRect;
else
myRenderType = kSoftZoom_32;
break;
default:
myRenderType = kDirtyRect; // What else should we do here?
break;
}
// Have the changes take effect
myOSystem->eventHandler().refreshDisplay();
//cerr << "Render type: " << myRenderType << endl;
}

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: FrameBufferSoft.hxx,v 1.39 2006-12-08 16:48:56 stephena Exp $
// $Id: FrameBufferSoft.hxx,v 1.40 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SOFT_HXX
@ -33,7 +33,7 @@ class RectList;
This class implements an SDL software framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx,v 1.39 2006-12-08 16:48:56 stephena Exp $
@version $Id: FrameBufferSoft.hxx,v 1.40 2006-12-10 17:04:34 stephena Exp $
*/
class FrameBufferSoft : public FrameBuffer
{
@ -206,18 +206,29 @@ class FrameBufferSoft : public FrameBuffer
*/
virtual void cls();
/**
Informs the Framebuffer of a change in EventHandler state.
*/
virtual void stateChanged(EventHandler::State state);
private:
int myZoomLevel;
int myPitch;
enum RenderType {
kSoftZoom,
kDirtyRect,
kSoftZoom_16,
kSoftZoom_24,
kSoftZoom_32,
kPhosphor_16,
kPhosphor_24,
kPhosphor_32
};
RenderType myRenderType;
// Use dirty updates (SDL_UpdateRects instead of SDL_UpdateRect)
bool myUseDirtyRects;
// Used in the dirty update of the SDL surface
RectList* myRectList;

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: EventHandler.cxx,v 1.182 2006-12-09 00:25:19 stephena Exp $
// $Id: EventHandler.cxx,v 1.183 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#include <sstream>
@ -195,6 +195,7 @@ void EventHandler::refreshDisplay(bool forceUpdate)
switch(myState)
{
case S_EMULATE:
myOSystem->frameBuffer().cls();
myOSystem->frameBuffer().refresh();
break;
@ -2330,8 +2331,10 @@ void EventHandler::setEventState(State state)
break;
}
// Inform the OSystem about the new state
// Inform various subsystems about the new state
myOSystem->stateChanged(myState);
if(&myOSystem->frameBuffer())
myOSystem->frameBuffer().stateChanged(myState);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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.105 2006-12-08 16:49:25 stephena Exp $
// $Id: FrameBuffer.cxx,v 1.106 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#include <sstream>
@ -116,8 +116,6 @@ void FrameBuffer::initialize(const string& title, uInt32 width, uInt32 height,
// Erase any messages from a previous run
myMessage.counter = 0;
myUseDirtyRects = myOSystem->settings().getBool("dirtyrects");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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.77 2006-12-08 16:49:26 stephena Exp $
// $Id: FrameBuffer.hxx,v 1.78 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_HXX
@ -24,6 +24,7 @@
#include "bspf.hxx"
#include "Array.hxx"
#include "Event.hxx"
#include "EventHandler.hxx"
#include "MediaSrc.hxx"
#include "Font.hxx"
#include "GuiUtils.hxx"
@ -96,7 +97,7 @@ struct Scaler {
All GUI elements (ala ScummVM) are drawn here as well.
@author Stephen Anthony
@version $Id: FrameBuffer.hxx,v 1.77 2006-12-08 16:49:26 stephena Exp $
@version $Id: FrameBuffer.hxx,v 1.78 2006-12-10 17:04:34 stephena Exp $
*/
class FrameBuffer
{
@ -292,6 +293,11 @@ class FrameBuffer
int color, TextAlignment align = kTextAlignLeft,
int deltax = 0, bool useEllipsis = true);
/**
Informs the Framebuffer of a change in EventHandler state.
*/
virtual void stateChanged(EventHandler::State state) { }
public:
//////////////////////////////////////////////////////////////////////
// The following methods are system-specific and must be implemented
@ -485,9 +491,6 @@ class FrameBuffer
// Indicates if the TIA area should be redrawn
bool theRedrawTIAIndicator;
// Use dirty updates (SDL_UpdateRects instead of SDL_UpdateRect)
bool myUseDirtyRects;
// Use phosphor effect (aka no flicker on 30Hz screens)
bool myUsePhosphor;

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: Settings.cxx,v 1.95 2006-12-09 00:25:20 stephena Exp $
// $Id: Settings.cxx,v 1.96 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#include <cassert>
@ -34,7 +34,7 @@ Settings::Settings(OSystem* osystem)
// Add options that are common to all versions of Stella
setInternal("video", "soft");
setInternal("dirtyrects", "true");
setInternal("dirtyrects", "false"); // This only becomes useful at high resolutions
setInternal("gl_filter", "nearest");
setInternal("gl_aspect", "2.0");

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: FrameBufferGP2X.cxx,v 1.10 2006-12-08 16:49:31 stephena Exp $
// $Id: FrameBufferGP2X.cxx,v 1.11 2006-12-10 17:04:34 stephena Exp $
//============================================================================
#include <SDL.h>
@ -98,10 +98,10 @@ void FrameBufferGP2X::drawMediaSource()
if(!myUsePhosphor)
{
for(uInt32 y = 0; y < height; ++y )
for(uInt32 y = 0; y < height; ++y)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x )
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt8 v = currentFrame[bufofs];
@ -127,10 +127,10 @@ void FrameBufferGP2X::drawMediaSource()
// so we don't care about theRedrawTIAIndicator
myDirtyFlag = true;
for(uInt32 y = 0; y < height; ++y )
for(uInt32 y = 0; y < height; ++y)
{
uInt32 pos = screenofsY;
for(uInt32 x = 0; x < width; ++x )
for(uInt32 x = 0; x < width; ++x)
{
const uInt32 bufofs = bufofsY + x;
uInt8 v = currentFrame[bufofs];

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: BrowserDialog.cxx,v 1.22 2006-12-09 04:02:40 stephena Exp $
// $Id: BrowserDialog.cxx,v 1.23 2006-12-10 17:04:34 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -26,6 +26,7 @@
#include "FSNode.hxx"
#include "GuiObject.hxx"
#include "GuiUtils.hxx"
#include "GameList.hxx"
#include "BrowserDialog.hxx"
#include "bspf.hxx"
@ -43,7 +44,8 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
: Dialog(boss->instance(), boss->parent(), x, y, w, h),
CommandSender(boss),
_fileList(NULL),
_currentPath(NULL)
_currentPath(NULL),
_nodeList(NULL)
{
const int lineHeight = font.getLineHeight(),
bwidth = font.getStringWidth("Cancel") + 20,
@ -60,7 +62,7 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
ypos += lineHeight + 4;
_currentPath = new StaticTextWidget(this, font, xpos, ypos,
_w - 2 * xpos, lineHeight,
"DUMMY", kTextAlignLeft);
"", kTextAlignLeft);
// Add file list
ypos += lineHeight;
@ -99,6 +101,15 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
#endif
addFocusWidget(_fileList);
// Create a list of directory nodes
_nodeList = new GameList();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BrowserDialog::~BrowserDialog()
{
delete _nodeList;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -106,17 +117,65 @@ void BrowserDialog::setStartPath(const string& startpath)
{
// If no node has been set, or the last used one is now invalid,
// go back to the root/default dir.
_choice = FilesystemNode(startpath);
_node = FilesystemNode(startpath);
if (_choice.isValid())
_node = _choice;
else if (!_node.isValid())
if(!_node.isValid())
_node = FilesystemNode();
// Alway refresh file list
updateListing();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::updateListing()
{
if(!_node.isDirectory())
return;
// Start with empty list
_nodeList->clear();
// Update the path display
_currentPath->setLabel(_node.path());
// Read in the data from the file system
FSList content = _node.listDir();
content.sort();
// Add '[..]' to indicate previous folder
if(_node.hasParent())
{
const string& parent = _node.getParent().path();
_nodeList->appendGame(" [..]", parent, "", true);
}
// Now add the directory entries
for(unsigned int idx = 0; idx < content.size(); idx++)
{
string name = content[idx].displayName();
bool isDir = content[idx].isDirectory();
if(isDir)
name = " [" + name + "]";
_nodeList->appendGame(name, content[idx].path(), "", isDir);
}
// Now fill the list widget with the contents of the GameList
StringList l;
for (int i = 0; i < (int) _nodeList->size(); ++i)
l.push_back(_nodeList->name(i));
_fileList->setList(l);
if(_fileList->getList().size() > 0)
_fileList->setSelected(0);
// Only hilite the 'up' button if there's a parent directory
_goUpButton->setEnabled(_node.hasParent());
// Finally, redraw
setDirty(); draw();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
int data, int id)
@ -124,23 +183,11 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
switch (cmd)
{
case kChooseCmd:
{
// If nothing is selected in the list widget, choose the current dir.
// Else, choose the dir that is selected.
int selection = _fileList->getSelected();
if (selection >= 0 && selection < (int)_nodeContent.size())
_choice = _nodeContent[selection];
else
_choice = _node;
// Send a signal to the calling class that a selection has been made
// Since we aren't derived from a widget, we don't have a 'data' or 'id'
if(_cmd)
sendCommand(_cmd, 0, 0);
if(_cmd) sendCommand(_cmd, 0, 0);
close();
break;
}
case kGoUpCmd:
_node = _node.getParent();
@ -149,47 +196,18 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
case kListItemActivatedCmd:
case kListItemDoubleClickedCmd:
_node = _nodeContent[data];
updateListing();
{
int item = _fileList->getSelected();
if(item >= 0)
{
_node = _nodeList->path(item);
updateListing();
}
break;
}
default:
Dialog::handleCommand(sender, cmd, data, 0);
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void BrowserDialog::updateListing()
{
if(!_node.isDirectory())
return;
// Update the path display
_currentPath->setLabel(_node.path());
// Read in the data from the file system
_nodeContent = _node.listDir();
_nodeContent.sort();
// Populate the ListWidget
StringList list;
int size = _nodeContent.size();
for (int i = 0; i < size; i++)
{
if(_nodeContent[i].isDirectory())
list.push_back(" [" + _nodeContent[i].displayName() + "]");
else
list.push_back(_nodeContent[i].displayName());
}
_fileList->setList(list);
if(size > 0)
_fileList->setSelected(0);
// Only hilite the 'up' button if there's a parent directory
_goUpButton->setEnabled(_node.hasParent());
// Finally, redraw
setDirty(); draw();
}

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: BrowserDialog.hxx,v 1.9 2006-12-08 16:49:32 stephena Exp $
// $Id: BrowserDialog.hxx,v 1.10 2006-12-10 17:04:34 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -26,6 +26,7 @@ class GuiObject;
class ButtonWidget;
class StaticTextWidget;
class StringListWidget;
class GameList;
#include "Dialog.hxx"
#include "Command.hxx"
@ -38,7 +39,9 @@ class BrowserDialog : public Dialog, public CommandSender
BrowserDialog(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h);
const FilesystemNode& getResult() { return _choice; }
virtual ~BrowserDialog();
const FilesystemNode& getResult() { return _node; }
void setTitle(const string& title) { _title->setLabel(title); }
void setEmitSignal(int cmd) { _cmd = cmd; }
@ -48,23 +51,21 @@ class BrowserDialog : public Dialog, public CommandSender
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
void updateListing();
protected:
private:
enum {
kChooseCmd = 'CHOS',
kGoUpCmd = 'GOUP'
};
int _cmd;
StringListWidget* _fileList;
StaticTextWidget* _currentPath;
StaticTextWidget* _title;
ButtonWidget* _goUpButton;
FilesystemNode _node;
FSList _nodeContent;
FilesystemNode _choice;
private:
int _cmd;
enum {
kChooseCmd = 'CHOS',
kGoUpCmd = 'GOUP'
};
FilesystemNode _node;
GameList* _nodeList;
};
#endif