// JLH = James Hammons <jlhamm@acm.org>
//
// Who When What
-// --- ---------- -------------------------------------------------------------
+// --- ---------- ------------------------------------------------------------
// JLH 12/23/2009 Created this file
// JLH 12/20/2010 Added settings, menus & toolbars
// JLH 07/05/2011 Added CD BIOS functionality to GUI
//
// - Add dbl click/enter to select in cart list, ESC to dimiss [DONE]
// - Autoscan/autoload all available BIOS from 'software' folder [DONE]
-// - Add 1 key jumping in cartridge list (press 'R', jumps to carts starting with 'R', etc) [DONE]
+// - Add 1 key jumping in cartridge list (press 'R', jumps to carts starting
+// with 'R', etc) [DONE]
// - Controller configuration [DONE]
//
// STILL TO BE DONE:
//
// - Fix bug in switching between PAL & NTSC in fullscreen mode.
// - Remove SDL dependencies (sound, mainly) from Jaguar core lib
-// - Fix inconsistency with trailing slashes in paths (eeproms needs one, software doesn't)
+// - Fix inconsistency with trailing slashes in paths (eeproms needs one,
+// software doesn't)
//
// SFDX CODE: S1E9T8H5M23YS
MainWin::MainWin(bool autoRun): running(true), powerButtonOn(false),
showUntunedTankCircuit(true), cartridgeLoaded(false), CDActive(false),
- pauseForFileSelector(false), loadAndGo(autoRun), plzDontKillMyComputer(false)
+ pauseForFileSelector(false), loadAndGo(autoRun), scannedSoftwareFolder(false), plzDontKillMyComputer(false)
{
debugbar = NULL;
powerRed.addFile(":/res/power-off.png", QSize(), QIcon::Normal, QIcon::Off);
powerRed.addFile(":/res/power-on-red.png", QSize(), QIcon::Normal, QIcon::On);
-// powerAct = new QAction(QIcon(":/res/power.png"), tr("&Power"), this);
powerAct = new QAction(powerGreen, tr("&Power"), this);
powerAct->setStatusTip(tr("Powers Jaguar on/off"));
powerAct->setCheckable(true);
powerAct->setChecked(false);
-// powerAct->setDisabled(true);
connect(powerAct, SIGNAL(triggered()), this, SLOT(TogglePowerState()));
QIcon pauseIcon;
pauseIcon.addFile(":/res/pause-off", QSize(), QIcon::Normal, QIcon::Off);
pauseIcon.addFile(":/res/pause-on", QSize(), QIcon::Normal, QIcon::On);
-// pauseAct = new QAction(QIcon(":/res/pause.png"), tr("Pause"), this);
pauseAct = new QAction(pauseIcon, tr("Pause"), this);
pauseAct->setStatusTip(tr("Toggles the running state"));
pauseAct->setCheckable(true);
palAct->setCheckable(true);
connect(palAct, SIGNAL(triggered()), this, SLOT(SetPAL()));
- blurAct = new QAction(QIcon(":/res/blur.png"), tr("Blur"), this);
+ blur.addFile(":/res/blur-off.png", QSize(), QIcon::Normal, QIcon::Off);
+ blur.addFile(":/res/blur-on.png", QSize(), QIcon::Normal, QIcon::On);
+
+ blurAct = new QAction(blur, tr("Blur"), this);
blurAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
blurAct->setCheckable(true);
connect(blurAct, SIGNAL(triggered()), this, SLOT(ToggleBlur()));
// Create status bar
statusBar()->showMessage(tr("Ready"));
-
ReadSettings();
-
- // Do this in case original size isn't correct (mostly for the first-run case)
+ // Do this in case original size isn't correct (mostly for the first-run
+ // case)
ResizeMainWindow();
- // Create our test pattern bitmap
+ // Create our test pattern bitmaps
QImage tempImg(":/res/test-pattern.jpg");
QImage tempImgScaled = tempImg.scaled(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT_PAL, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
}
+ QImage tempImg2(":/res/test-pattern-pal");
+ QImage tempImgScaled2 = tempImg2.scaled(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT_PAL, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ for(uint32_t y=0; y<VIRTUAL_SCREEN_HEIGHT_PAL; y++)
+ {
+ const QRgb * scanline = (QRgb *)tempImgScaled2.constScanLine(y);
+
+ for(uint32_t x=0; x<VIRTUAL_SCREEN_WIDTH; x++)
+ {
+ uint32_t pixel = (qRed(scanline[x]) << 24) | (qGreen(scanline[x]) << 16) | (qBlue(scanline[x]) << 8) | 0xFF;
+ testPattern2[(y * VIRTUAL_SCREEN_WIDTH) + x] = pixel;
+ }
+ }
+
// Set up timer based loop for animation...
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
// Run the scanner if nothing passed in and *not* Alpine mode...
// NB: Really need to look into caching the info scanned in here...
filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
+ scannedSoftwareFolder = true;
}
void MainWin::keyPressEvent(QKeyEvent * e)
{
+#ifndef VJ_REMOVE_DEV_CODE
// From jaguar.cpp
extern bool startM68KTracing;
// From joystick.cpp
extern int blit_start_log;
// From blitter.cpp
extern bool startConciseBlitLogging;
-
+#endif
// We ignore the Alt key for now, since it causes problems with the GUI
if (e->key() == Qt::Key_Alt)
e->accept();
return;
}
+// Bar this shite from release versions kthxbai
+#ifndef VJ_REMOVE_DEV_CODE
else if (e->key() == Qt::Key_F11)
{
startM68KTracing = true;
e->accept();
return;
}
+#endif
else if (e->key() == Qt::Key_F8)
{
// ggn: For extra NYAN pleasure...
{
enum { P1LEFT = 0, P1RIGHT, P1UP, P1DOWN, P2LEFT, P2RIGHT, P2UP, P2DOWN };
// We kill bad key combos here, before they can get to the emulator...
- // This also kills the illegal instruction problem that cropped up in Rayman!
- // May want to do this by killing the old one instead of ignoring the new one...
- // Seems to work better that way...
+ // This also kills the illegal instruction problem that cropped up in
+ // Rayman!
-// The problem with this approach is that it causes bad results because it doesn't do
-// any checking of previous states. Need to come up with something better because this
-// causes problems where the keyboard acts as if it were unresponsive. :-P
-#if 0
- if ((e->key() == vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
- || (e->key() == vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
- || (e->key() == vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
- || (e->key() == vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U]))
- return;
-#else
-#if 0
- if (e->key() == (int)vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
- joypad_0_buttons[BUTTON_R] = 0;
- if (e->key() == (int)vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
- joypad_0_buttons[BUTTON_L] = 0;
- if (e->key() == (int)vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
- joypad_0_buttons[BUTTON_D] = 0;
- if (e->key() == (int)vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U])
- joypad_0_buttons[BUTTON_U] = 0;
-
- if (e->key() == (int)vjs.p2KeyBindings[BUTTON_L] && joypad_1_buttons[BUTTON_R])
- joypad_1_buttons[BUTTON_R] = 0;
- if (e->key() == (int)vjs.p2KeyBindings[BUTTON_R] && joypad_1_buttons[BUTTON_L])
- joypad_1_buttons[BUTTON_L] = 0;
- if (e->key() == (int)vjs.p2KeyBindings[BUTTON_U] && joypad_1_buttons[BUTTON_D])
- joypad_1_buttons[BUTTON_D] = 0;
- if (e->key() == (int)vjs.p2KeyBindings[BUTTON_D] && joypad_1_buttons[BUTTON_U])
- joypad_1_buttons[BUTTON_U] = 0;
-#else
-//hrm, this still has sticky state problems... Ugh!
// First, settle key states...
if (e->key() == (int)vjs.p1KeyBindings[BUTTON_L])
keyHeld[P1LEFT] = state;
else if (e->key() == (int)vjs.p2KeyBindings[BUTTON_D])
keyHeld[P2DOWN] = state;
- // Next, check for conflicts and bail out if there are any...
- if ((keyHeld[P1LEFT] && keyHeld[P1RIGHT])
- || (keyHeld[P1UP] && keyHeld[P1DOWN])
- || (keyHeld[P2LEFT] && keyHeld[P2RIGHT])
- || (keyHeld[P2UP] && keyHeld[P2DOWN]))
- return;
-#endif
-#endif
+ // Next, check for conflicts and kill 'em if there are any...
+ if (keyHeld[P1LEFT] && keyHeld[P1RIGHT])
+ keyHeld[P1LEFT] = keyHeld[P1RIGHT] = false;
- // No bad combos exist, let's stuff the emulator key buffers...!
+ if (keyHeld[P1UP] && keyHeld[P1DOWN])
+ keyHeld[P1UP] = keyHeld[P1DOWN] = false;
+
+ if (keyHeld[P2LEFT] && keyHeld[P2RIGHT])
+ keyHeld[P2LEFT] = keyHeld[P2RIGHT] = false;
+
+ if (keyHeld[P2UP] && keyHeld[P2DOWN])
+ keyHeld[P2UP] = keyHeld[P2DOWN] = false;
+
+ // No bad combos exist now, let's stuff the emulator key buffers...!
for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
{
if (e->key() == (int)vjs.p1KeyBindings[i])
-// joypad_0_buttons[i] = (uint8)state;
joypad0Buttons[i] = (state ? 0x01 : 0x00);
-// Pad #2 is screwing up pad #1. Prolly a problem in joystick.cpp...
-// So let's try to fix it there. :-P [DONE]
if (e->key() == (int)vjs.p2KeyBindings[i])
-// joypad_1_buttons[i] = (uint8)state;
joypad1Buttons[i] = (state ? 0x01 : 0x00);
}
}
+//
+// N.B.: The profile system AutoConnect functionality sets the gamepad IDs here.
+//
void MainWin::HandleGamepads(void)
{
Gamepad::Update();
for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
{
if (vjs.p1KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS))
- joypad0Buttons[i] = (Gamepad::GetState(0, vjs.p1KeyBindings[i]) ? 0x01 : 0x00);
-/*{
-if (vjs.p1KeyBindings[i] & JOY_AXIS)
- printf("Axis state (HandleGamepads): %i\n", joypad0Buttons[i]);
-}*/
+ joypad0Buttons[i] = (Gamepad::GetState(gamepadIDSlot1, vjs.p1KeyBindings[i]) ? 0x01 : 0x00);
+
if (vjs.p2KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS))
- joypad1Buttons[i] = (Gamepad::GetState(1, vjs.p2KeyBindings[i]) ? 0x01 : 0x00);
+ joypad1Buttons[i] = (Gamepad::GetState(gamepadIDSlot2, vjs.p2KeyBindings[i]) ? 0x01 : 0x00);
}
}
dlg.generalTab->useUnknownSoftware->setChecked(allowUnknownSoftware);
dlg.controllerTab1->profileNum = lastEditedProfile;
dlg.controllerTab1->SetupLastUsedProfile();
+// maybe instead of this, we tell the controller tab to work on a copy that gets
+// written if the user hits 'OK'.
+ SaveProfiles(); // Just in case user cancels
if (dlg.exec() == false)
+ {
+ RestoreProfiles();
return;
+ }
QString before = vjs.ROMPath;
QString alpineBefore = vjs.alpineROMPath;
//ick.
allowUnknownSoftware = dlg.generalTab->useUnknownSoftware->isChecked();
lastEditedProfile = dlg.controllerTab1->profileNum;
+ AutoConnectProfiles();
// We rescan the "software" folder if the user either changed the path or
// checked/unchecked the "Allow unknown files" option in the config dialog.
//
void MainWin::Timer(void)
{
+#if 0
+static uint32_t ntscTickCount;
+ if (vjs.hardwareTypeNTSC)
+ {
+ ntscTickCount++;
+ ntscTickCount %= 3;
+ timer->start(16 + (ntscTickCount == 0 ? 1 : 0));
+ }
+#endif
+
if (!running)
return;
HandleGamepads();
JaguarExecuteNew();
videoWidget->HandleMouseHiding();
+
+static uint32_t refresh = 0;
+ // Do autorefresh on debug windows
+ // Have to be careful, too much causes the emulator to slow way down!
+ if (vjs.hardwareTypeAlpine)
+ {
+ if (refresh == 60)
+ {
+ memBrowseWin->RefreshContents();
+ cpuBrowseWin->RefreshContents();
+ refresh = 0;
+ }
+ else
+ refresh++;
+ }
}
videoWidget->updateGL();
// the same as the picture buffer's pitch.
for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
{
- memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
+ if (vjs.hardwareTypeNTSC)
+ memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
+ else
+ memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern2 + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
}
}
}
void MainWin::InsertCart(void)
{
+ // Check to see if we did autorun, 'cause we didn't load anything in that
+ // case
+ if (!scannedSoftwareFolder)
+ {
+ filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
+ scannedSoftwareFolder = true;
+ }
+
// If the emulator is running, we pause it here and unpause it later
// if we dismiss the file selector without choosing anything
if (running && powerButtonOn)
running = false; // Prevent bad things(TM) from happening...
pauseForFileSelector = false; // Reset the file selector pause flag
- char * biosPointer = jaguarBootROM;
+ uint8_t * biosPointer = jaguarBootROM;
if (vjs.hardwareTypeAlpine)
biosPointer = jaguarDevBootROM2;
powerButtonOn = false;
TogglePowerState();
// We have to load our software *after* the Jaguar RESET
- cartridgeLoaded = JaguarLoadFile(file.toAscii().data());
+ cartridgeLoaded = JaguarLoadFile(file.toUtf8().data());
SET32(jaguarMainRAM, 0, 0x00200000); // Set top of stack...
// This is icky because we've already done it
if (debugbar)
debugbar->hide();
- showFullScreen();
// This is needed because the fullscreen may happen on a different
// screen than screen 0:
int screenNum = QApplication::desktop()->screenNumber(videoWidget);
-// QRect r = QApplication::desktop()->availableGeometry(screenNum);
QRect r = QApplication::desktop()->screenGeometry(screenNum);
double targetWidth = (double)VIRTUAL_SCREEN_WIDTH,
targetHeight = (double)(vjs.hardwareTypeNTSC ? VIRTUAL_SCREEN_HEIGHT_NTSC : VIRTUAL_SCREEN_HEIGHT_PAL);
}
else
{
+ // Seems Qt is fussy about this: showNormal() has to go first, or it
+ // will keep the window stuck in a psuedo-fullscreen mode with no way
+ // to get out of it (except closing the app).
+ showNormal();
+
// Reset the video widget to windowed mode
videoWidget->offset = 0;
videoWidget->fullscreen = false;
if (debugbar)
debugbar->show();
- showNormal();
ResizeMainWindow();
move(mainWinPosition);
}
{
for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
{
- memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
+ if (vjs.hardwareTypeNTSC)
+ memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
+ else
+ memcpy(videoWidget->buffer + (y * videoWidget->textureWidth), testPattern2 + (y * VIRTUAL_SCREEN_WIDTH), VIRTUAL_SCREEN_WIDTH * sizeof(uint32_t));
}
}
- show();
-
- for(int i=0; i<2; i++)
- {
- resize(0, 0);
- usleep(2000);
- QApplication::processEvents();
- }
+ adjustSize();
}
vjs.allowWritesToROM = settings.value("writeROM", false).toBool();
vjs.biosType = settings.value("biosType", BT_M_SERIES).toInt();
vjs.useFastBlitter = settings.value("useFastBlitter", false).toBool();
- strcpy(vjs.EEPROMPath, settings.value("EEPROMs", "./eeproms/").toString().toAscii().data());
- strcpy(vjs.ROMPath, settings.value("ROMs", "./software/").toString().toAscii().data());
- strcpy(vjs.alpineROMPath, settings.value("DefaultROM", "").toString().toAscii().data());
- strcpy(vjs.absROMPath, settings.value("DefaultABS", "").toString().toAscii().data());
+ strcpy(vjs.EEPROMPath, settings.value("EEPROMs", QStandardPaths::writableLocation(QStandardPaths::DataLocation).append("/eeproms/")).toString().toUtf8().data());
+ strcpy(vjs.ROMPath, settings.value("ROMs", QStandardPaths::writableLocation(QStandardPaths::DataLocation).append("/software/")).toString().toUtf8().data());
+ strcpy(vjs.alpineROMPath, settings.value("DefaultROM", "").toString().toUtf8().data());
+ strcpy(vjs.absROMPath, settings.value("DefaultABS", "").toString().toUtf8().data());
WriteLog("MainWin: Paths\n");
WriteLog(" EEPROMPath = \"%s\"\n", vjs.EEPROMPath);
WriteLog(" absROMPath = \"%s\"\n", vjs.absROMPath);
WriteLog("Pipelined DSP = %s\n", (vjs.usePipelinedDSP ? "ON" : "off"));
+#if 0
// Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
vjs.p1KeyBindings[BUTTON_U] = settings.value("p1k_up", Qt::Key_S).toInt();
vjs.p1KeyBindings[BUTTON_D] = settings.value("p1k_down", Qt::Key_X).toInt();
vjs.p2KeyBindings[BUTTON_9] = settings.value("p2k_9", Qt::Key_9).toInt();
vjs.p2KeyBindings[BUTTON_d] = settings.value("p2k_pound", Qt::Key_Slash).toInt();
vjs.p2KeyBindings[BUTTON_s] = settings.value("p2k_star", Qt::Key_Asterisk).toInt();
+#endif
ReadProfiles(&settings);
}
-
+
void MainWin::WriteSettings(void)
{
QSettings settings("Underground Software", "Virtual Jaguar");
settings.setValue("DefaultROM", vjs.alpineROMPath);
settings.setValue("DefaultABS", vjs.absROMPath);
+#if 0
settings.setValue("p1k_up", vjs.p1KeyBindings[BUTTON_U]);
settings.setValue("p1k_down", vjs.p1KeyBindings[BUTTON_D]);
settings.setValue("p1k_left", vjs.p1KeyBindings[BUTTON_L]);
settings.setValue("p2k_9", vjs.p2KeyBindings[BUTTON_9]);
settings.setValue("p2k_pound", vjs.p2KeyBindings[BUTTON_d]);
settings.setValue("p2k_star", vjs.p2KeyBindings[BUTTON_s]);
+#endif
WriteProfiles(&settings);
}