mirror of https://github.com/stella-emu/stella.git
376 lines
11 KiB
C++
376 lines
11 KiB
C++
//============================================================================
|
|
//
|
|
// 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-2018 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//============================================================================
|
|
|
|
#include "Dialog.hxx"
|
|
#include "Font.hxx"
|
|
#include "EventHandler.hxx"
|
|
#include "FrameBuffer.hxx"
|
|
#include "FBSurface.hxx"
|
|
#include "OSystem.hxx"
|
|
#include "Widget.hxx"
|
|
#include "StateManager.hxx"
|
|
#include "RewindManager.hxx"
|
|
|
|
|
|
#include "Console.hxx"
|
|
#include "TIA.hxx"
|
|
#include "System.hxx"
|
|
|
|
|
|
#include "TimeMachineDialog.hxx"
|
|
#include "Base.hxx"
|
|
using Common::Base;
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|
int max_w, int max_h)
|
|
: Dialog(osystem, parent)
|
|
{
|
|
const int BUTTON_W = 16, BUTTON_H = 14;
|
|
|
|
/*static uInt32 PAUSE[BUTTON_H] =
|
|
{
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000,
|
|
0b0001111001111000
|
|
};*/
|
|
static uInt32 PLAY[BUTTON_H] =
|
|
{
|
|
0b0110000000000000,
|
|
0b0111100000000000,
|
|
0b0111111000000000,
|
|
0b0111111110000000,
|
|
0b0111111111100000,
|
|
0b0111111111111000,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111111111111000,
|
|
0b0111111111100000,
|
|
0b0111111110000000,
|
|
0b0111111000000000,
|
|
0b0111100000000000,
|
|
0b0110000000000000
|
|
};
|
|
static uInt32 REWIND_ALL[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0110000110000110,
|
|
0b0110001110001110,
|
|
0b0110011110011110,
|
|
0b0110111110111110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0110111110111110,
|
|
0b0110011110011110,
|
|
0b0110001110001110,
|
|
0b0110000110000110,
|
|
0
|
|
};
|
|
static uInt32 REWIND_10[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0000010000100110,
|
|
0b0000110001100110,
|
|
0b0001110011100110,
|
|
0b0011110111100110,
|
|
0b0111111111100110,
|
|
0b1111111111100110,
|
|
0b1111111111100110,
|
|
0b0111111111100110,
|
|
0b0011110111100110,
|
|
0b0001110011100110,
|
|
0b0000110001100110,
|
|
0b0000010000100110,
|
|
0
|
|
};
|
|
static uInt32 REWIND_1[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0000001100011100,
|
|
0b0000011100011100,
|
|
0b0000111100011100,
|
|
0b0001111100011100,
|
|
0b0011111100011100,
|
|
0b0111111100011100,
|
|
0b0111111100011100,
|
|
0b0011111100011100,
|
|
0b0001111100011100,
|
|
0b0000111100011100,
|
|
0b0000011100011100,
|
|
0b0000001100011100,
|
|
0
|
|
};
|
|
static uInt32 UNWIND_1[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0011100011000000,
|
|
0b0011100011100000,
|
|
0b0011100011110000,
|
|
0b0011100011111000,
|
|
0b0011100011111100,
|
|
0b0011100011111110,
|
|
0b0011100011111110,
|
|
0b0011100011111100,
|
|
0b0011100011111000,
|
|
0b0011100011110000,
|
|
0b0011100011100000,
|
|
0b0011100011000000,
|
|
0
|
|
};
|
|
static uInt32 UNWIND_10[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0110010000100000,
|
|
0b0110011000110000,
|
|
0b0110011100111000,
|
|
0b0110011110111100,
|
|
0b0110011111111110,
|
|
0b0110011111111111,
|
|
0b0110011111111111,
|
|
0b0110011111111110,
|
|
0b0110011110111100,
|
|
0b0110011100111000,
|
|
0b0110011000110000,
|
|
0b0110010000100000,
|
|
0
|
|
};
|
|
static uInt32 UNWIND_ALL[BUTTON_H] =
|
|
{
|
|
0,
|
|
0b0110000110000110,
|
|
0b0111000111000110,
|
|
0b0111100111100110,
|
|
0b0111110111110110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111111111111110,
|
|
0b0111110111110110,
|
|
0b0111100111100110,
|
|
0b0111000111000110,
|
|
0b0110000110000110,
|
|
0
|
|
};
|
|
|
|
const GUI::Font& font = instance().frameBuffer().font();
|
|
const int H_BORDER = 6, BUTTON_GAP = 4, V_BORDER = 4; // FIXME, V_GAP = 4;
|
|
const int buttonWidth = BUTTON_W + 8,
|
|
buttonHeight = BUTTON_H + 10,
|
|
rowHeight = font.getLineHeight();
|
|
|
|
WidgetArray wid;
|
|
int xpos, ypos;
|
|
|
|
// Set real dimensions
|
|
_w = 20 * (buttonWidth + BUTTON_GAP) + 20;
|
|
_h = V_BORDER * 2 + rowHeight + buttonHeight + 2;
|
|
|
|
this->clearFlags(WIDGET_CLEARBG); // does only work combined with blending (0..100)!
|
|
this->clearFlags(WIDGET_BORDER);
|
|
|
|
xpos = H_BORDER;
|
|
ypos = V_BORDER;
|
|
|
|
// Add index info
|
|
myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, " ", TextAlign::Left, kBGColor);
|
|
myCurrentIdxWidget->setTextColor(kWidColor);
|
|
myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("8888"), ypos,
|
|
" ", TextAlign::Right, kBGColor);
|
|
myLastIdxWidget->setTextColor(kWidColor);
|
|
ypos += rowHeight;
|
|
|
|
// Add time info
|
|
myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos + 3, "04:32 59", TextAlign::Left, kBGColor);
|
|
myCurrentTimeWidget->setTextColor(kWidColor);
|
|
myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("XX:XX XX"), ypos + 3,
|
|
"12:25 59", TextAlign::Right, kBGColor);
|
|
myLastTimeWidget->setTextColor(kWidColor);
|
|
xpos = myCurrentTimeWidget->getRight() + BUTTON_GAP * 4;
|
|
|
|
// Add buttons
|
|
myRewindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_ALL,
|
|
BUTTON_W, BUTTON_H, kRewindAll);
|
|
wid.push_back(myRewindAllWidget);
|
|
xpos += buttonWidth + BUTTON_GAP;
|
|
|
|
myRewind10Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_10,
|
|
BUTTON_W, BUTTON_H, kRewind10);
|
|
wid.push_back(myRewind10Widget);
|
|
xpos += buttonWidth + BUTTON_GAP;
|
|
|
|
myRewind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_1,
|
|
BUTTON_W, BUTTON_H, kRewind1);
|
|
wid.push_back(myRewind1Widget);
|
|
xpos += buttonWidth + BUTTON_GAP*2;
|
|
|
|
/*myPauseWidget = new ButtonWidget(this, font, xpos, ypos - 2, buttonWidth + 4, buttonHeight + 4, PAUSE,
|
|
BUTTON_W, BUTTON_H, kPause);
|
|
wid.push_back(myPauseWidget);
|
|
myPauseWidget->clearFlags(WIDGET_ENABLED);*/
|
|
myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, PLAY,
|
|
BUTTON_W, BUTTON_H, kPlay);
|
|
wid.push_back(myPlayWidget);
|
|
xpos += buttonWidth + BUTTON_GAP*2;
|
|
|
|
myUnwind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_1,
|
|
BUTTON_W, BUTTON_H, kUnwind1);
|
|
wid.push_back(myUnwind1Widget);
|
|
xpos += buttonWidth + BUTTON_GAP;
|
|
|
|
myUnwind10Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_10,
|
|
BUTTON_W, BUTTON_H, kUnwind10);
|
|
wid.push_back(myUnwind10Widget);
|
|
xpos += buttonWidth + BUTTON_GAP;
|
|
|
|
myUnwindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_ALL,
|
|
BUTTON_W, BUTTON_H, kUnwindAll);
|
|
wid.push_back(myUnwindAllWidget);
|
|
xpos = myUnwindAllWidget->getRight() + BUTTON_GAP * 3;
|
|
|
|
// Add message
|
|
myMessageWidget = new StaticTextWidget(this, font, xpos, ypos + 3, " ",
|
|
TextAlign::Left, kBGColor);
|
|
myMessageWidget->setTextColor(kWidColor);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void TimeMachineDialog::center()
|
|
{
|
|
// Place on the bottom of the screen, centered horizontally
|
|
const GUI::Size& screen = instance().frameBuffer().screenSize();
|
|
const GUI::Rect& dst = surface().dstRect();
|
|
surface().setDstPos((screen.w - dst.width()) >> 1, screen.h - dst.height() - 10);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void TimeMachineDialog::loadConfig()
|
|
{
|
|
surface().attributes().blending = true;
|
|
surface().attributes().blendalpha = 80;
|
|
surface().applyAttributes();
|
|
|
|
handleWinds();
|
|
myMessageWidget->setLabel("");
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
|
|
int data, int id)
|
|
{
|
|
//cerr << cmd << endl;
|
|
switch(cmd)
|
|
{
|
|
case kPlay:
|
|
instance().eventHandler().leaveMenuMode();
|
|
break;
|
|
|
|
case kRewind1:
|
|
handleWinds(-1);
|
|
break;
|
|
|
|
case kRewind10:
|
|
handleWinds(-10);
|
|
break;
|
|
|
|
case kRewindAll:
|
|
handleWinds(-1000);
|
|
break;
|
|
|
|
case kUnwind1:
|
|
handleWinds(1);
|
|
break;
|
|
|
|
case kUnwind10:
|
|
handleWinds(10);
|
|
break;
|
|
|
|
case kUnwindAll:
|
|
handleWinds(1000);
|
|
break;
|
|
|
|
default:
|
|
Dialog::handleCommand(sender, cmd, data, 0);
|
|
}
|
|
}
|
|
|
|
string TimeMachineDialog::getTimeString(uInt64 cycles)
|
|
{
|
|
const Int32 scanlines = std::max(instance().console().tia().scanlinesLastFrame(), 240u);
|
|
const bool isNTSC = scanlines <= 287;
|
|
const Int32 NTSC_FREQ = 1193182; // ~76*262*60
|
|
const Int32 PAL_FREQ = 1182298; // ~76*312*50
|
|
const Int32 freq = isNTSC ? NTSC_FREQ : PAL_FREQ; // = cycles/second
|
|
|
|
uInt32 minutes = cycles / (freq * 60);
|
|
cycles -= minutes * (freq * 60);
|
|
uInt32 seconds = cycles / freq;
|
|
cycles -= seconds * freq;
|
|
uInt32 frames = cycles / (scanlines * 76);
|
|
|
|
stringstream time;
|
|
time << Common::Base::toString(minutes, Common::Base::F_10_02) << ":";
|
|
time << Common::Base::toString(seconds, Common::Base::F_10_02) << ".";
|
|
time << Common::Base::toString(frames, Common::Base::F_10_02);
|
|
|
|
return time.str();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void TimeMachineDialog::handleWinds(Int32 numWinds)
|
|
{
|
|
RewindManager& r = instance().state().rewindManager();
|
|
|
|
if(numWinds)
|
|
{
|
|
uInt64 startCycles = instance().console().tia().cycles();
|
|
if(numWinds < 0)
|
|
r.rewindState(-numWinds);
|
|
else
|
|
r.unwindState(numWinds);
|
|
string message = r.getUnitString(instance().console().tia().cycles() - startCycles);
|
|
|
|
myMessageWidget->setLabel((numWinds < 0 ? "(-" : "(+") + message + ")");
|
|
}
|
|
// Update time
|
|
myCurrentTimeWidget->setLabel(getTimeString(r.getCurrentCycles() - r.getFirstCycles()));
|
|
myLastTimeWidget->setLabel(getTimeString(r.getLastCycles() - r.getFirstCycles()));
|
|
// Update index
|
|
myCurrentIdxWidget->setValue(r.getCurrentIdx());
|
|
myLastIdxWidget->setValue(r.getLastIdx());
|
|
// enable/disable buttons
|
|
myRewindAllWidget->setEnabled(!r.atFirst());
|
|
myRewind10Widget->setEnabled(!r.atFirst());
|
|
myRewind1Widget->setEnabled(!r.atFirst());
|
|
myUnwindAllWidget->setEnabled(!r.atLast());
|
|
myUnwind10Widget->setEnabled(!r.atLast());
|
|
myUnwind1Widget->setEnabled(!r.atLast());
|
|
}
|