From cf9f42b65201bd487033d12cc60fbe8bb0f11090 Mon Sep 17 00:00:00 2001 From: stephena Date: Tue, 7 Jun 2005 01:14:39 +0000 Subject: [PATCH] Added debugging console/prompt commandline interface. It's still not quite working correctly, but at least text appears when you type. Changed the debugging TAB interface to use buttons instead. It seems this is a deficiency in the ScummVM GUI code, and I don't really want to figure out how to fix it. Of course, now the buttons have to be embedded in each dialog box, somehow ... git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@469 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/src/build/makefile | 8 +- stella/src/gui/DebuggerDialog.cxx | 202 ++++------ stella/src/gui/DebuggerDialog.hxx | 35 +- stella/src/gui/GuiUtils.hxx | 12 +- stella/src/gui/LauncherDialog.cxx | 4 +- stella/src/gui/ListWidget.cxx | 16 +- stella/src/gui/ListWidget.hxx | 3 +- stella/src/gui/PromptDialog.cxx | 649 ++++++++++++++++++++++++++++++ stella/src/gui/PromptDialog.hxx | 133 ++++++ 9 files changed, 887 insertions(+), 175 deletions(-) create mode 100644 stella/src/gui/PromptDialog.cxx create mode 100644 stella/src/gui/PromptDialog.hxx diff --git a/stella/src/build/makefile b/stella/src/build/makefile index ca0f5ee6a..c04df469b 100644 --- a/stella/src/build/makefile +++ b/stella/src/build/makefile @@ -13,7 +13,7 @@ ## See the file "license" for information on usage and redistribution of ## this file, and for a DISCLAIMER OF ALL WARRANTIES. ## -## $Id: makefile,v 1.86 2005-06-03 17:52:04 stephena Exp $ +## $Id: makefile,v 1.87 2005-06-07 01:14:38 stephena Exp $ ##============================================================================ ##============================================================================ @@ -159,7 +159,8 @@ GUI_OBJS = StellaFont.o Menu.o Launcher.o Debugger.o \ Dialog.o DialogContainer.o OptionsDialog.o VideoDialog.o AudioDialog.o \ EventMappingDialog.o GameInfoDialog.o HelpDialog.o AboutDialog.o \ LauncherDialog.o LauncherOptionsDialog.o BrowserDialog.o GameList.o \ - ProgressDialog.o DebuggerDialog.o + ProgressDialog.o \ + DebuggerDialog.o PromptDialog.o CORE_OBJS = Booster.o Cart.o Cart2K.o Cart3F.o Cart4K.o CartAR.o CartDPC.o \ CartE0.o CartE7.o CartF4.o CartF4SC.o CartF6.o CartF6SC.o \ @@ -450,3 +451,6 @@ ProgressDialog.o: $(GUI)/ProgressDialog.cxx $(GUI)/ProgressDialog.hxx DebuggerDialog.o: $(GUI)/DebuggerDialog.cxx $(GUI)/DebuggerDialog.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(GUI)/DebuggerDialog.cxx + +PromptDialog.o: $(GUI)/PromptDialog.cxx $(GUI)/PromptDialog.hxx + $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(GUI)/PromptDialog.cxx diff --git a/stella/src/gui/DebuggerDialog.cxx b/stella/src/gui/DebuggerDialog.cxx index 8fb4c1191..8588eec21 100644 --- a/stella/src/gui/DebuggerDialog.cxx +++ b/stella/src/gui/DebuggerDialog.cxx @@ -13,142 +13,72 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: DebuggerDialog.cxx,v 1.1 2005-06-03 17:52:06 stephena Exp $ +// $Id: DebuggerDialog.cxx,v 1.2 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project //============================================================================ #include "DialogContainer.hxx" -#include "BrowserDialog.hxx" -#include "PopUpWidget.hxx" -#include "TabWidget.hxx" -#include "FSNode.hxx" -#include "bspf.hxx" +#include "PromptDialog.hxx" #include "DebuggerDialog.hxx" enum { - kChooseRomDirCmd = 'roms', // rom select - kChooseSnapDirCmd = 'snps', // snap select - kRomDirChosenCmd = 'romc', // rom chosen - kSnapDirChosenCmd = 'snpc' // snap chosen + kPromptCmd = 'PRMT', + kCpuCmd = 'CPU ', + kRamCmd = 'RAM ', + kRomCmd = 'ROM ', + kTiaCmd = 'TIA ', + kCodeCmd = 'CODE' }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -DebuggerDialog::DebuggerDialog( - OSystem* osystem, DialogContainer* parent, - int x, int y, int w, int h) - : Dialog(osystem, parent, x, y, w, h) +DebuggerDialog::DebuggerDialog(OSystem* osystem, DialogContainer* parent, + int x, int y, int w, int h) + : Dialog(osystem, parent, x, y, w, h), + myPromptDialog(NULL), + myCpuDialog(NULL), + myRamDialog(NULL), + myRomDialog(NULL), + myTiaDialog(NULL), + myCodeDialog(NULL) { - const int vBorder = 4; - int yoffset; + const int border = 5; + const int space = 5; + const int width = 40; + int xpos = border; - // The tab widget - TabWidget* tab = new TabWidget(this, 0, vBorder, _w, _h); + // Add a row of buttons for the various debugger operations + new ButtonWidget(this, xpos, border, width, 16, "Prompt", kPromptCmd, 0); + xpos += space + width; + new ButtonWidget(this, xpos, border, width, 16, "CPU", kCpuCmd, 0); + xpos += space + width; + new ButtonWidget(this, xpos, border, width, 16, "RAM", kRamCmd, 0); + xpos += space + width; + new ButtonWidget(this, xpos, border, width, 16, "ROM", kRomCmd, 0); + xpos += space + width; + new ButtonWidget(this, xpos, border, width, 16, "TIA", kTiaCmd, 0); + xpos += space + width; + new ButtonWidget(this, xpos, border, width, 16, "Code", kCodeCmd, 0); + xpos += space + width; - // 1) The command prompt - tab->addTab("Prompt"); - yoffset = vBorder; - - // FIXME - add scrollable console/edittext window - - // 2) The CPU contents - tab->addTab(" CPU "); - yoffset = vBorder; - - // FIXME - add CPU registers - - // 3) The RAM contents - tab->addTab(" RAM "); - yoffset = vBorder; - - // FIXME - add 16x8 list of RAM contents -/* - // Snapshot path - new ButtonWidget(tab, 15, yoffset, kButtonWidth + 14, 16, "Path", kChooseSnapDirCmd, 0); - mySnapPath = new StaticTextWidget(tab, 5 + kButtonWidth + 30, - yoffset + 3, _w - (5 + kButtonWidth + 20) - 10, - kLineHeight, "", kTextAlignLeft); - yoffset += 22; - - // Snapshot save name - mySnapTypePopup = new PopUpWidget(tab, 10, yoffset, 140, kLineHeight, - "Save snapshot as: ", 87, 0); - mySnapTypePopup->appendEntry("romname", 1); - mySnapTypePopup->appendEntry("md5sum", 2); - yoffset += 18; - - // Snapshot single or multiple saves - mySnapSingleCheckbox = new CheckboxWidget(tab, 30, yoffset, 80, kLineHeight, - "Multiple snapshots"); -*/ - - // 4) The ROM contents - tab->addTab(" ROM "); - yoffset = vBorder; - - // FIXME - add ROM contents, somehow taking into account which bank is being - // viewed (maybe a button to switch banks?? - - // 5) The TIA contents - tab->addTab(" TIA "); - yoffset = vBorder; - - // FIXME - TIA registers - - // 6) The code listing - tab->addTab(" Code"); - yoffset = vBorder; - - // FIXME - Add code listing in assembly language - - // Activate the first tab - tab->setActiveTab(0); - -/* - // Create file browser dialog - int baseW = instance()->frameBuffer().baseWidth(); - int baseH = instance()->frameBuffer().baseHeight(); - myBrowser = new BrowserDialog(this, 60, 20, baseW - 120, baseH - 40); -*/ + const int xoff = border; + const int yoff = border + 16 + 2; + // And create the debugger dialog boxes + myPromptDialog = new PromptDialog(osystem, parent, x + xoff, y + yoff, + w - xoff, h - yoff); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DebuggerDialog::~DebuggerDialog() { -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DebuggerDialog::loadConfig() -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DebuggerDialog::saveConfig() -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DebuggerDialog::openRomBrowser() -{ + delete myPromptDialog; /* - myBrowser->setTitle("Select ROM directory:"); - myBrowser->setEmitSignal(kRomDirChosenCmd); - myBrowser->setStartPath(myRomPath->getLabel()); - - parent()->addDialog(myBrowser); -*/ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DebuggerDialog::openSnapBrowser() -{ -/* - myBrowser->setTitle("Select snapshot directory:"); - myBrowser->setEmitSignal(kSnapDirChosenCmd); - myBrowser->setStartPath(mySnapPath->getLabel()); - - parent()->addDialog(myBrowser); + delete myCpuDialog; + delete myRamDialog; + delete myRomDialog; + delete myTiaDialog; + delete myCodeDialog; */ } @@ -157,32 +87,40 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd, int data) { switch (cmd) { - case kOKCmd: - saveConfig(); - close(); + case kPromptCmd: + parent()->reStack(); + parent()->addDialog(myPromptDialog); break; - case kChooseRomDirCmd: - openRomBrowser(); + case kCpuCmd: + parent()->reStack(); +// parent()->addDialog(myCpuDialog); + cerr << "kCpuCmd\n"; break; - case kChooseSnapDirCmd: - openSnapBrowser(); + case kRamCmd: + parent()->reStack(); +// parent()->addDialog(myRamDialog); + cerr << "kRamCmd\n"; break; - case kRomDirChosenCmd: - { - FilesystemNode dir(myBrowser->getResult()); - myRomPath->setLabel(dir.path()); + case kRomCmd: + parent()->reStack(); +// parent()->addDialog(myRomDialog); + cerr << "kRomCmd\n"; break; - } - case kSnapDirChosenCmd: - { - FilesystemNode dir(myBrowser->getResult()); - mySnapPath->setLabel(dir.path()); + case kTiaCmd: + parent()->reStack(); +// parent()->addDialog(myTiaDialog); + cerr << "kTiaCmd\n"; + break; + + case kCodeCmd: + parent()->reStack(); +// parent()->addDialog(myCodeDialog); + cerr << "kCodeCmd\n"; break; - } default: Dialog::handleCommand(sender, cmd, data); diff --git a/stella/src/gui/DebuggerDialog.hxx b/stella/src/gui/DebuggerDialog.hxx index 647df05f1..5be43852a 100644 --- a/stella/src/gui/DebuggerDialog.hxx +++ b/stella/src/gui/DebuggerDialog.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: DebuggerDialog.hxx,v 1.1 2005-06-03 17:52:06 stephena Exp $ +// $Id: DebuggerDialog.hxx,v 1.2 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -24,10 +24,12 @@ class OSystem; class DialogContainer; -class BrowserDialog; -class CheckboxWidget; -class PopUpWidget; -class StaticTextWidget; +class PromptDialog; +class CpuDialog; +class RamDialog; +class RomDialog; +class TiaDialog; +class CodeDialog; #include "Dialog.hxx" @@ -38,25 +40,16 @@ class DebuggerDialog : public Dialog int x, int y, int w, int h); ~DebuggerDialog(); - virtual void loadConfig(); - virtual void saveConfig(); - virtual void handleCommand(CommandSender* sender, int cmd, int data); protected: - BrowserDialog* myBrowser; - - // Rom path controls - StaticTextWidget* myRomPath; - - // Snapshot controls - StaticTextWidget* mySnapPath; - PopUpWidget* mySnapTypePopup; - CheckboxWidget* mySnapSingleCheckbox; - - private: - void openRomBrowser(); - void openSnapBrowser(); + // The debugger dialogs + PromptDialog* myPromptDialog; + CpuDialog* myCpuDialog; + RamDialog* myRamDialog; + RomDialog* myRomDialog; + TiaDialog* myTiaDialog; + CodeDialog* myCodeDialog; }; #endif diff --git a/stella/src/gui/GuiUtils.hxx b/stella/src/gui/GuiUtils.hxx index 3cea93d4b..375e89390 100644 --- a/stella/src/gui/GuiUtils.hxx +++ b/stella/src/gui/GuiUtils.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: GuiUtils.hxx,v 1.8 2005-05-16 00:02:32 stephena Exp $ +// $Id: GuiUtils.hxx,v 1.9 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -29,7 +29,7 @@ Probably not very neat, but at least it works ... @author Stephen Anthony - @version $Id: GuiUtils.hxx,v 1.8 2005-05-16 00:02:32 stephena Exp $ + @version $Id: GuiUtils.hxx,v 1.9 2005-06-07 01:14:39 stephena Exp $ */ #define kLineHeight 12 @@ -74,6 +74,14 @@ static const string EmptyString(""); template inline void SWAP(T &a, T &b) { T tmp = a; a = b; b = tmp; } template inline T ABS (T x) { return (x>=0) ? x : -x; } +#if !defined(MIN) +template inline T MIN (T a, T b) { return (a inline T MAX (T a, T b) { return (a>b) ? a : b; } +#endif + #define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0]))) #endif diff --git a/stella/src/gui/LauncherDialog.cxx b/stella/src/gui/LauncherDialog.cxx index c13fd29e9..d8efe4182 100644 --- a/stella/src/gui/LauncherDialog.cxx +++ b/stella/src/gui/LauncherDialog.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: LauncherDialog.cxx,v 1.18 2005-05-26 18:56:58 stephena Exp $ +// $Id: LauncherDialog.cxx,v 1.19 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -66,7 +66,7 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent, myRomCount = new StaticTextWidget(this, _w - 100, 8, 90, kLineHeight, "", kTextAlignRight); - // Add three buttons at the bottom + // Add four buttons at the bottom const int border = 10; const int space = 8; const int buttons = 4; diff --git a/stella/src/gui/ListWidget.cxx b/stella/src/gui/ListWidget.cxx index ba64386ba..1bc335b40 100644 --- a/stella/src/gui/ListWidget.cxx +++ b/stella/src/gui/ListWidget.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: ListWidget.cxx,v 1.8 2005-05-13 18:28:05 stephena Exp $ +// $Id: ListWidget.cxx,v 1.9 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -36,7 +36,7 @@ ListWidget::ListWidget(GuiObject* boss, int x, int y, int w, int h) : Widget(boss, x, y, w - kScrollBarWidth, h), CommandSender(boss) { - _flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE; + _flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS; _type = kListWidget; _numberingMode = kListNumberingOne; _entriesPerPage = (_h - 2) / kLineHeight; @@ -133,18 +133,6 @@ void ListWidget::scrollBarRecalc() _scrollBar->recalc(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ListWidget::handleTickle() -{ -/* - uint32 time = g_system->getMillis(); - if (_editMode && _caretTime < time) { - _caretTime = time + kCaretBlinkTime; - drawCaret(_caretVisible); - } -*/ -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) { diff --git a/stella/src/gui/ListWidget.hxx b/stella/src/gui/ListWidget.hxx index 4085bdfdb..c38b132ba 100644 --- a/stella/src/gui/ListWidget.hxx +++ b/stella/src/gui/ListWidget.hxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: ListWidget.hxx,v 1.3 2005-05-13 18:28:06 stephena Exp $ +// $Id: ListWidget.hxx,v 1.4 2005-06-07 01:14:39 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project @@ -60,7 +60,6 @@ class ListWidget : public Widget, public CommandSender void setNumberingMode(NumberingMode numberingMode) { _numberingMode = numberingMode; } void scrollTo(int item); - virtual void handleTickle(); virtual void handleMouseDown(int x, int y, int button, int clickCount); virtual void handleMouseUp(int x, int y, int button, int clickCount); virtual void handleMouseWheel(int x, int y, int direction); diff --git a/stella/src/gui/PromptDialog.cxx b/stella/src/gui/PromptDialog.cxx new file mode 100644 index 000000000..5158ba422 --- /dev/null +++ b/stella/src/gui/PromptDialog.cxx @@ -0,0 +1,649 @@ +//============================================================================ +// +// 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-2005 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: PromptDialog.cxx,v 1.1 2005-06-07 01:14:39 stephena Exp $ +// +// Based on code from ScummVM - Scumm Interpreter +// Copyright (C) 2002-2004 The ScummVM project +//============================================================================ + +#include "ScrollBarWidget.hxx" +#include "FrameBuffer.hxx" + +#include "PromptDialog.hxx" + +//#define kConsoleCharWidth (g_consolefont.getMaxCharWidth()) +//#define kConsoleLineHeight (g_consolefont.getFontHeight() + 2) +#define kConsoleCharWidth (8) +#define kConsoleLineHeight (10 + 2) + + +#define PROMPT "> " + +/* TODO: + * - it is very inefficient to redraw the full thingy when just one char is added/removed. + * Instead, we could just copy the GFX of the blank console (i.e. after the transparent + * background is drawn, before any text is drawn). Then using that, it becomes trivial + * to erase a single character, do scrolling etc. + * - a *lot* of others things, this code is in no way complete and heavily under progress + */ + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PromptDialog::PromptDialog(OSystem* osystem, DialogContainer* parent, + int x, int y, int w, int h) + : Dialog(osystem, parent, x, y, w, h) +{ + // Calculate depending values + _lineWidth = (_w - kScrollBarWidth - 2) / kConsoleCharWidth; + _linesPerPage = (_h - 2) / kConsoleLineHeight; + + memset(_buffer, ' ', kBufferSize); + _linesInBuffer = kBufferSize / _lineWidth; + + _currentPos = 0; + _scrollLine = _linesPerPage - 1; + _firstLineInBuffer = 0; + + _caretVisible = true;//false; + _caretTime = 0; + + _slideMode = kNoSlideMode; + _slideTime = 0; + + // Add scrollbar + _scrollBar = new ScrollBarWidget(this, _w - kScrollBarWidth - 1, 0, kScrollBarWidth, _h); + _scrollBar->setTarget(this); + + // Init callback + _callbackProc = 0; + _callbackRefCon = 0; + + // Init History + _historyIndex = 0; + _historyLine = 0; + _historySize = 0; + for (int i = 0; i < kHistorySize; i++) + _history[i][0] = '\0'; + + _promptStartPos = _promptEndPos = -1; + + // Display greetings & prompt + print("HELLO!!");//gScummVMFullVersion); + print("\nDebugger is ready\n"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PromptDialog::~PromptDialog() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::drawDialog() +{ + FrameBuffer& fb = instance()->frameBuffer(); + + // Blend over the background + fb.blendRect(_x, _y, _w, _h, kBGColor, 2); + + // Draw a border + fb.hLine(_x, _y + _h - 1, _x + _w - 1, kColor); + + // Draw text + int start = _scrollLine - _linesPerPage + 1; + int y = _y + 2; + + for (int line = 0; line < _linesPerPage; line++) + { + int x = _x + 1; + for (int column = 0; column < _lineWidth; column++) { +#if 0 + int l = (start + line) % _linesInBuffer; + char c = buffer(l * _lineWidth + column); +#else + char c = buffer((start + line) * _lineWidth + column); +#endif + fb.drawChar(c, x, y, kTextColor); + x += kConsoleCharWidth; + } + y += kConsoleLineHeight; + } + + // Draw the scrollbar + _scrollBar->draw(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::handleMouseWheel(int x, int y, int direction) +{ + _scrollBar->handleMouseWheel(x, y, direction); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::handleKeyDown(int ascii, int keycode, int modifiers) +{ +cerr << "PromptDialog::handleKeyDown\n"; + int i; + + if (_slideMode != kNoSlideMode) + return; + + switch (keycode) + { + case '\n': // enter/return + case '\r': + { + if (_caretVisible) + drawCaret(true); + + nextLine(); + + assert(_promptEndPos >= _promptStartPos); + int len = _promptEndPos - _promptStartPos; + bool keepRunning = true; + + + if (len > 0) + { + // We have to allocate the string buffer with new, since VC++ sadly does not + // comply to the C++ standard, so we can't use a dynamic sized stack array. + char *str = new char[len + 1]; + + // Copy the user input to str + for (i = 0; i < len; i++) + str[i] = buffer(_promptStartPos + i); + str[len] = '\0'; + + // Add the input to the history + addToHistory(str); + + // Pass it to the input callback, if any + if (_callbackProc) + keepRunning = (*_callbackProc)(this, str, _callbackRefCon); + + // Get rid of the string buffer + delete [] str; + } + + print(PROMPT); + _promptStartPos = _promptEndPos = _currentPos; + + draw(); + instance()->frameBuffer().refresh(); + break; + } + + case 8: // backspace + if (_caretVisible) + drawCaret(true); + + if (_currentPos > _promptStartPos) + { + _currentPos--; + killChar(); + } + scrollToCurrent(); + draw(); // FIXME - not nice to redraw the full console just for one char! + instance()->frameBuffer().refresh(); + break; + + case 9: // tab + { + if (_completionCallbackProc) + { + int len = _currentPos - _promptStartPos; + assert(len >= 0); + char *str = new char[len + 1]; + + // Copy the user input to str + for (i = 0; i < len; i++) + str[i] = buffer(_promptStartPos + i); + str[len] = '\0'; + + char *completion = 0; + if ((*_completionCallbackProc)(this, str, completion, _callbackRefCon)) + { + if (_caretVisible) + drawCaret(true); + insertIntoPrompt(completion); + scrollToCurrent(); + draw(); + instance()->frameBuffer().refresh(); + delete[] completion; + } + delete[] str; + } + break; + } + + case 127: + killChar(); + draw(); + instance()->frameBuffer().refresh(); + break; + + case 256 + 24: // pageup + if (1) // FIXME - shift modifiers == OSystem::KBD_SHIFT) + { + _scrollLine -= _linesPerPage - 1; + if (_scrollLine < _firstLineInBuffer + _linesPerPage - 1) + _scrollLine = _firstLineInBuffer + _linesPerPage - 1; + updateScrollBuffer(); + draw(); + instance()->frameBuffer().refresh(); + } + break; + + case 256 + 25: // pagedown + if (1) // FIXME - shift modifiers == OSystem::KBD_SHIFT) + { + _scrollLine += _linesPerPage - 1; + if (_scrollLine > _promptEndPos / _lineWidth) + _scrollLine = _promptEndPos / _lineWidth; + updateScrollBuffer(); + draw(); + instance()->frameBuffer().refresh(); + } + break; + + case 256 + 22: // home + if (1) // FIXME - shift modifiers == OSystem::KBD_SHIFT) + { + _scrollLine = _firstLineInBuffer + _linesPerPage - 1; + updateScrollBuffer(); + } + else + _currentPos = _promptStartPos; + + draw(); + instance()->frameBuffer().refresh(); + break; + + case 256 + 23: // end + if (1) // FIXME - shift modifiers == OSystem::KBD_SHIFT) + { + _scrollLine = _promptEndPos / _lineWidth; + if (_scrollLine < _linesPerPage - 1) + _scrollLine = _linesPerPage - 1; + updateScrollBuffer(); + } + else + _currentPos = _promptEndPos; + + draw(); + instance()->frameBuffer().refresh(); + break; + + case 273: // cursor up + historyScroll(+1); + break; + + case 274: // cursor down + historyScroll(-1); + break; + + case 275: // cursor right + if (_currentPos < _promptEndPos) + _currentPos++; + draw(); + instance()->frameBuffer().refresh(); + break; + + case 276: // cursor left + if (_currentPos > _promptStartPos) + _currentPos--; + draw(); + instance()->frameBuffer().refresh(); + break; + + default: +/* FIXME + } else if (modifiers == OSystem::KBD_CTRL) { + specialKeys(keycode); +*/ + if (isprint((char)ascii)) + { + for (i = _promptEndPos - 1; i >= _currentPos; i--) + buffer(i + 1) = buffer(i); + _promptEndPos++; + putchar((char)ascii); + scrollToCurrent(); + } + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::insertIntoPrompt(const char* str) +{ + unsigned int l = strlen(str); + + for (int i = _promptEndPos - 1; i >= _currentPos; i--) + buffer(i + l) = buffer(i); + + for (unsigned int j = 0; j < l; ++j) + { + _promptEndPos++; + putcharIntern(str[j]); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::handleCommand(CommandSender* sender, int cmd, int data) +{ + switch (cmd) + { + case kSetPositionCmd: + int newPos = (int)data + _linesPerPage - 1 + _firstLineInBuffer; + if (newPos != _scrollLine) + { + _scrollLine = newPos; + draw(); + instance()->frameBuffer().refresh(); + } + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::specialKeys(int keycode) +{ +/* FIXME - add UNIX style line editing + switch (keycode) { + case 'a': + _currentPos = _promptStartPos; + draw(); + break; + case 'd': + if (_currentPos < _promptEndPos) { + killChar(); + draw(); + } + break; + case 'e': + _currentPos = _promptEndPos; + draw(); + break; + case 'k': + killLine(); + draw(); + break; + case 'w': + killLastWord(); + draw(); + break; + } +*/ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::killChar() +{ + for (int i = _currentPos; i < _promptEndPos; i++) + buffer(i) = buffer(i + 1); + + buffer(_promptEndPos) = ' '; + _promptEndPos--; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::killLine() +{ + for (int i = _currentPos; i < _promptEndPos; i++) + buffer(i) = ' '; + + _promptEndPos = _currentPos; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::killLastWord() +{ + int cnt = 0; + bool space = true; + while (_currentPos > _promptStartPos) + { + if (buffer(_currentPos - 1) == ' ') + { + if (!space) + break; + } + else + space = false; + + _currentPos--; + cnt++; + } + + for (int i = _currentPos; i < _promptEndPos; i++) + buffer(i) = buffer(i + cnt); + + buffer(_promptEndPos) = ' '; + _promptEndPos -= cnt; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::addToHistory(const char *str) +{ + strcpy(_history[_historyIndex], str); + _historyIndex = (_historyIndex + 1) % kHistorySize; + _historyLine = 0; + + if (_historySize < kHistorySize) + _historySize++; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::historyScroll(int direction) +{ + if (_historySize == 0) + return; + + if (_historyLine == 0 && direction > 0) + { + int i; + for (i = 0; i < _promptEndPos - _promptStartPos; i++) + _history[_historyIndex][i] = buffer(_promptStartPos + i); + + _history[_historyIndex][i] = '\0'; + } + + // Advance to the next line in the history + int line = _historyLine + direction; + if ((direction < 0 && line < 0) || (direction > 0 && line > _historySize)) + return; + _historyLine = line; + + // Hide caret if visible + if (_caretVisible) + drawCaret(true); + + // Remove the current user text + _currentPos = _promptStartPos; + killLine(); + + // ... and ensure the prompt is visible + scrollToCurrent(); + + // Print the text from the history + int idx; + if (_historyLine > 0) + idx = (_historyIndex - _historyLine + _historySize) % _historySize; + else + idx = _historyIndex; + + for (int i = 0; i < kLineBufferSize && _history[idx][i] != '\0'; i++) + putcharIntern(_history[idx][i]); + + _promptEndPos = _currentPos; + + // Ensure once more the caret is visible (in case of very long history entries) + scrollToCurrent(); + + draw(); + instance()->frameBuffer().refresh(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::nextLine() +{ + int line = _currentPos / _lineWidth; + if (line == _scrollLine) + _scrollLine++; + + _currentPos = (line + 1) * _lineWidth; + + updateScrollBuffer(); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Call this (at least) when the current line changes or when a new line is added +void PromptDialog::updateScrollBuffer() +{ + int lastchar = MAX(_promptEndPos, _currentPos); + int line = lastchar / _lineWidth; + int numlines = (line < _linesInBuffer) ? line + 1 : _linesInBuffer; + int firstline = line - numlines + 1; + + if (firstline > _firstLineInBuffer) + { + // clear old line from buffer + for (int i = lastchar; i < (line+1) * _lineWidth; ++i) + buffer(i) = ' '; + + _firstLineInBuffer = firstline; + } + + _scrollBar->_numEntries = numlines; + _scrollBar->_currentPos = _scrollBar->_numEntries - (line - _scrollLine + _linesPerPage); + _scrollBar->_entriesPerPage = _linesPerPage; + _scrollBar->recalc(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int PromptDialog::printf(const char *format, ...) +{ + va_list argptr; + + va_start(argptr, format); + int count = this->vprintf(format, argptr); + va_end (argptr); + return count; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int PromptDialog::vprintf(const char *format, va_list argptr) +{ + char buf[2048]; + +#if defined(WIN32) + int count = _vsnprintf(buf, sizeof(buf), format, argptr); +#else + int count = vsnprintf(buf, sizeof(buf), format, argptr); +#endif + print(buf); + return count; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::putchar(int c) +{ + if (_caretVisible) + drawCaret(true); + + putcharIntern(c); + draw(); // FIXME - not nice to redraw the full console just for one char! + instance()->frameBuffer().refresh(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::putcharIntern(int c) +{ + if (c == '\n') + nextLine(); + else + { + buffer(_currentPos) = (char)c; + _currentPos++; + if ((_scrollLine + 1) * _lineWidth == _currentPos) + { + _scrollLine++; + updateScrollBuffer(); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::print(const char *str) +{ + if (_caretVisible) + drawCaret(true); + + while (*str) + putcharIntern(*str++); + + draw(); + instance()->frameBuffer().refresh(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::drawCaret(bool erase) +{ + FrameBuffer& fb = instance()->frameBuffer(); + + int line = _currentPos / _lineWidth; + int displayLine = line - _scrollLine + _linesPerPage - 1; + + // Only draw caret if visible + if (!isVisible() || displayLine < 0 || displayLine >= _linesPerPage) + { + _caretVisible = false; + return; + } + + int x = _x + 1 + (_currentPos % _lineWidth) * kConsoleCharWidth; + int y = _y + displayLine * kConsoleLineHeight; + + char c = buffer(_currentPos); + if (erase) + { + fb.fillRect(x, y, kConsoleCharWidth, kConsoleLineHeight, kBGColor); + fb.drawChar(c, x, y + 2, kTextColor); + } + else + { + fb.fillRect(x, y, kConsoleCharWidth, kConsoleLineHeight, kTextColor); + fb.drawChar(c, x, y + 2, kBGColor); + } + //FIXMEg_gui.addDirtyRect(x, y, kConsoleCharWidth, kConsoleLineHeight); + fb.refresh(); + + _caretVisible = !erase; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PromptDialog::scrollToCurrent() +{ + int line = _promptEndPos / _lineWidth; + + if (line + _linesPerPage <= _scrollLine) + { + // TODO - this should only occur for loong edit lines, though + } + else if (line > _scrollLine) + { + _scrollLine = line; + updateScrollBuffer(); + } +} diff --git a/stella/src/gui/PromptDialog.hxx b/stella/src/gui/PromptDialog.hxx new file mode 100644 index 000000000..a6eed561b --- /dev/null +++ b/stella/src/gui/PromptDialog.hxx @@ -0,0 +1,133 @@ +//============================================================================ +// +// 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-2005 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: PromptDialog.hxx,v 1.1 2005-06-07 01:14:39 stephena Exp $ +// +// Based on code from ScummVM - Scumm Interpreter +// Copyright (C) 2002-2004 The ScummVM project +//============================================================================ + +#ifndef PROMPT_DIALOG_HXX +#define PROMPT_DIALOG_HXX + +class CommandSender; +class DialogContainer; +class ScrollBarWidget; + +#include +#include "Dialog.hxx" + +enum { + kBufferSize = 32768, + kLineBufferSize = 256, + + kHistorySize = 20 +}; + +class PromptDialog : public Dialog +{ + public: + PromptDialog(OSystem* osystem, DialogContainer* parent, + int x, int y, int w, int h); + virtual ~PromptDialog(); + + void handleMouseWheel(int x, int y, int direction); + void handleKeyDown(int ascii, int keycode, int modifiers); + void handleCommand(CommandSender* sender, int cmd, int data); + + public: + int printf(const char *format, ...); + int vprintf(const char *format, va_list argptr); +#undef putchar + void putchar(int c); + + typedef bool (*InputCallbackProc)(PromptDialog *console, const char *input, void *refCon); + typedef bool (*CompletionCallbackProc)(PromptDialog* console, const char *input, char*& completion, void *refCon); + + void setInputCallback(InputCallbackProc proc, void *refCon) { + _callbackProc = proc; + _callbackRefCon = refCon; + } + void setCompletionCallback(CompletionCallbackProc proc, void *refCon) { + _completionCallbackProc = proc; + _completionCallbackRefCon = refCon; + } + + protected: + inline char &buffer(int idx) { return _buffer[idx % kBufferSize]; } + + void drawDialog(); + void drawCaret(bool erase); + void putcharIntern(int c); + void insertIntoPrompt(const char *str); + void print(const char *str); + void updateScrollBuffer(); + void scrollToCurrent(); + + // Line editing + void specialKeys(int keycode); + void nextLine(); + void killChar(); + void killLine(); + void killLastWord(); + + // History + void addToHistory(const char *str); + void historyScroll(int direction); + + protected: + char _buffer[kBufferSize]; + int _linesInBuffer; + + int _lineWidth; + int _linesPerPage; + + int _currentPos; + int _scrollLine; + int _firstLineInBuffer; + + int _promptStartPos; + int _promptEndPos; + + bool _caretVisible; + int _caretTime; + + enum SlideMode { + kNoSlideMode, + kUpSlideMode, + kDownSlideMode + }; + + SlideMode _slideMode; + int _slideTime; + + ScrollBarWidget* _scrollBar; + + // The _callbackProc is called whenver a data line is entered + // + InputCallbackProc _callbackProc; + void *_callbackRefCon; + + // _completionCallbackProc is called when tab is pressed + CompletionCallbackProc _completionCallbackProc; + void *_completionCallbackRefCon; + + char _history[kHistorySize][kLineBufferSize]; + int _historySize; + int _historyIndex; + int _historyLine; +}; + +#endif