]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/mainwin.cpp
c73d6708a6cbffb87b90685325223847f1e80391
[virtualjaguar] / src / gui / mainwin.cpp
1 //
2 // mainwin.cpp - Qt-based GUI for Virtual Jaguar: Main Application Window
3 // by James L. Hammons
4 // (C) 2009 Underground Software
5 //
6 // JLH = James L. Hammons <jlhamm@acm.org>
7 //
8 // Who  When        What
9 // ---  ----------  -------------------------------------------------------------
10 // JLH  12/23/2009  Created this file
11 // JLH  12/20/2010  Added settings, menus & toolbars
12 // JLH  07/05/2011  Added CD BIOS functionality to GUI
13 //
14
15 // FIXED:
16 //
17 //
18 // STILL TO BE DONE:
19 //
20 // - Autoscan/autoload all available BIOS from 'software' folder
21 // - Controller configuration
22 // - Remove SDL dependencies (sound, mainly) from Jaguar core lib
23 // - Add 1 key jumping in cartridge list (press 'R', jumps to carts starting with 'R', etc)
24 // - Add dbl click/enter to select in cart list, ESC to dimiss
25 //
26
27 /*
28 For BIOS autoscan, infrastructure is already there in filethread.cpp; just need to figure out
29 if we want to scan every time, or stuff filenames found into the config file, or what.
30 Should filethread emit signal that's intercepted here? Maybe...
31 */
32
33 // Uncomment this for debugging...
34 //#define DEBUG
35 //#define DEBUGFOO                      // Various tool debugging...
36 //#define DEBUGTP                               // Toolpalette debugging...
37
38 #include "mainwin.h"
39
40 #include "SDL.h"
41 #include "glwidget.h"
42 #include "about.h"
43 #include "settings.h"
44 #include "filepicker.h"
45 #include "configdialog.h"
46 #include "generaltab.h"
47 #include "version.h"
48
49 #include "jaguar.h"
50 #include "tom.h"
51 #include "log.h"
52 #include "file.h"
53 #include "joystick.h"
54
55 #ifdef __GCCWIN32__
56 // Apparently on win32, usleep() is not pulled in by the usual suspects.
57 #include <unistd.h>
58 #endif
59
60 // Uncomment this to use built-in BIOS/CD-ROM BIOS
61 // You'll need a copy of jagboot.h & jagcd.h for this to work...!
62 // Creating those is left as an exercise for the reader. ;-)
63 //#define USE_BUILT_IN_BIOS
64
65 #ifdef USE_BUILT_IN_BIOS
66 #include "jagboot.h"
67 #include "jagcd.h"
68 #endif
69
70 // The way BSNES controls things is by setting a timer with a zero
71 // timeout, sleeping if not emulating anything. Seems there has to be a
72 // better way.
73
74 // It has a novel approach to plugging-in/using different video/audio/input
75 // methods, can we do something similar or should we just use the built-in
76 // QOpenGL?
77
78 // We're going to try to use the built-in OpenGL support and see how it goes.
79 // We'll make the VJ core modular so that it doesn't matter what GUI is in
80 // use, we can drop it in anywhere and use it as-is.
81
82 MainWin::MainWin(): running(false), powerButtonOn(false), showUntunedTankCircuit(true),
83         cartridgeLoaded(false), CDActive(false)
84 {
85         videoWidget = new GLWidget(this);
86         setCentralWidget(videoWidget);
87         setWindowIcon(QIcon(":/res/vj-icon.png"));
88 //      setWindowTitle("Virtual Jaguar v2.0.0");
89         setWindowTitle("Virtual Jaguar " VJ_RELEASE_VERSION );
90
91         aboutWin = new AboutWindow(this);
92         filePickWin = new FilePickerWindow(this);
93
94     videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
95     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
96
97         setUnifiedTitleAndToolBarOnMac(true);
98
99         // Create actions
100
101         quitAppAct = new QAction(tr("E&xit"), this);
102         quitAppAct->setShortcuts(QKeySequence::Quit);
103         quitAppAct->setStatusTip(tr("Quit Virtual Jaguar"));
104         connect(quitAppAct, SIGNAL(triggered()), this, SLOT(close()));
105
106         powerAct = new QAction(QIcon(":/res/power.png"), tr("&Power"), this);
107         powerAct->setStatusTip(tr("Powers Jaguar on/off"));
108         powerAct->setCheckable(true);
109         powerAct->setChecked(false);
110         powerAct->setDisabled(true);
111         connect(powerAct, SIGNAL(triggered()), this, SLOT(TogglePowerState()));
112
113         pauseAct = new QAction(QIcon(":/res/pause.png"), tr("Pause"), this);
114         pauseAct->setStatusTip(tr("Toggles the running state"));
115         pauseAct->setCheckable(true);
116         pauseAct->setDisabled(true);
117         pauseAct->setShortcut(QKeySequence(tr("Esc")));
118         connect(pauseAct, SIGNAL(triggered()), this, SLOT(ToggleRunState()));
119
120         zoomActs = new QActionGroup(this);
121
122         x1Act = new QAction(QIcon(":/res/zoom100.png"), tr("Zoom 100%"), zoomActs);
123         x1Act->setStatusTip(tr("Set window zoom to 100%"));
124         x1Act->setCheckable(true);
125         connect(x1Act, SIGNAL(triggered()), this, SLOT(SetZoom100()));
126
127         x2Act = new QAction(QIcon(":/res/zoom200.png"), tr("Zoom 200%"), zoomActs);
128         x2Act->setStatusTip(tr("Set window zoom to 200%"));
129         x2Act->setCheckable(true);
130         connect(x2Act, SIGNAL(triggered()), this, SLOT(SetZoom200()));
131
132         x3Act = new QAction(QIcon(":/res/zoom300.png"), tr("Zoom 300%"), zoomActs);
133         x3Act->setStatusTip(tr("Set window zoom to 300%"));
134         x3Act->setCheckable(true);
135         connect(x3Act, SIGNAL(triggered()), this, SLOT(SetZoom300()));
136
137         tvTypeActs = new QActionGroup(this);
138
139         ntscAct = new QAction(QIcon(":/res/ntsc.png"), tr("NTSC"), tvTypeActs);
140         ntscAct->setStatusTip(tr("Sets Jaguar to NTSC mode"));
141         ntscAct->setCheckable(true);
142         connect(ntscAct, SIGNAL(triggered()), this, SLOT(SetNTSC()));
143
144         palAct = new QAction(QIcon(":/res/pal.png"), tr("PAL"), tvTypeActs);
145         palAct->setStatusTip(tr("Sets Jaguar to PAL mode"));
146         palAct->setCheckable(true);
147         connect(palAct, SIGNAL(triggered()), this, SLOT(SetPAL()));
148
149         blurAct = new QAction(QIcon(":/res/generic.png"), tr("Blur"), this);
150         blurAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
151         blurAct->setCheckable(true);
152         connect(blurAct, SIGNAL(triggered()), this, SLOT(ToggleBlur()));
153
154         aboutAct = new QAction(QIcon(":/res/vj-icon.png"), tr("&About..."), this);
155         aboutAct->setStatusTip(tr("Blatant self-promotion"));
156         connect(aboutAct, SIGNAL(triggered()), this, SLOT(ShowAboutWin()));
157
158         filePickAct = new QAction(QIcon(":/res/software.png"), tr("&Insert Cartridge..."), this);
159         filePickAct->setStatusTip(tr("Insert a cartridge into Virtual Jaguar"));
160         filePickAct->setShortcut(QKeySequence(tr("Ctrl+i")));
161         connect(filePickAct, SIGNAL(triggered()), this, SLOT(InsertCart()));
162
163         configAct = new QAction(QIcon(":/res/generic.png"), tr("&Configure"), this);
164         configAct->setStatusTip(tr("Configure options for Virtual Jaguar"));
165         configAct->setShortcut(QKeySequence(tr("Ctrl+c")));
166         connect(configAct, SIGNAL(triggered()), this, SLOT(Configure()));
167
168         useCDAct = new QAction(QIcon(":/res/generic.png"), tr("&Use CD Unit"), this);
169         useCDAct->setStatusTip(tr("Use Jaguar Virtual CD unit"));
170 //      useCDAct->setShortcut(QKeySequence(tr("Ctrl+c")));
171         useCDAct->setCheckable(true);
172         connect(useCDAct, SIGNAL(triggered()), this, SLOT(ToggleCDUsage()));
173
174         // Misc. connections...
175         connect(filePickWin, SIGNAL(RequestLoad(QString)), this, SLOT(LoadSoftware(QString)));
176
177         // Create menus & toolbars
178
179         fileMenu = menuBar()->addMenu(tr("&File"));
180         fileMenu->addAction(filePickAct);
181         fileMenu->addAction(useCDAct);
182         fileMenu->addAction(powerAct);
183         fileMenu->addAction(pauseAct);
184         fileMenu->addAction(configAct);
185         fileMenu->addAction(quitAppAct);
186
187         helpMenu = menuBar()->addMenu(tr("&Help"));
188         helpMenu->addAction(aboutAct);
189
190         toolbar = addToolBar(tr("Stuff"));
191         toolbar->addAction(powerAct);
192         toolbar->addAction(pauseAct);
193         toolbar->addAction(filePickAct);
194         toolbar->addAction(useCDAct);
195         toolbar->addSeparator();
196         toolbar->addAction(x1Act);
197         toolbar->addAction(x2Act);
198         toolbar->addAction(x3Act);
199         toolbar->addSeparator();
200         toolbar->addAction(ntscAct);
201         toolbar->addAction(palAct);
202         toolbar->addSeparator();
203         toolbar->addAction(blurAct);
204
205         //      Create status bar
206         statusBar()->showMessage(tr("Ready"));
207
208         ReadSettings();
209
210         // Set toolbar buttons/menus based on settings read in (sync the UI)...
211         blurAct->setChecked(vjs.glFilter);
212         x1Act->setChecked(zoomLevel == 1);
213         x2Act->setChecked(zoomLevel == 2);
214         x3Act->setChecked(zoomLevel == 3);
215         running = powerAct->isChecked();
216         ntscAct->setChecked(vjs.hardwareTypeNTSC);
217         palAct->setChecked(!vjs.hardwareTypeNTSC);
218
219         // Do this in case original size isn't correct (mostly for the first-run case)
220         ResizeMainWindow();
221
222         // Set up timer based loop for animation...
223         timer = new QTimer(this);
224         connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
225         timer->start(20);
226
227         WriteLog("Virtual Jaguar %s (Last full build was on %s %s)\n", VJ_RELEASE_VERSION, __DATE__, __TIME__);
228         WriteLog("VJ: Initializing jaguar subsystem...\n");
229         JaguarInit();
230
231         // Get the BIOS ROM
232 #ifdef USE_BUILT_IN_BIOS
233         WriteLog("VJ: Using built in BIOS/CD BIOS...\n");
234         memcpy(jaguarBootROM, jagBootROM, 0x20000);
235         memcpy(jaguarCDBootROM, jagCDROM, 0x40000);
236         BIOSLoaded = CDBIOSLoaded = true;
237 #else
238 // What would be nice here would be a way to check if the BIOS was loaded so that we
239 // could disable the pushbutton on the Misc Options menu... !!! FIX !!! [DONE here, but needs to be fixed in GUI as well!]
240         WriteLog("VJ: About to attempt to load BIOSes...\n");
241 //This is short-circuiting the file finding thread... ??? WHY ???
242 //Not anymore. Was related to a QImage object creation/corruption bug elsewhere.
243         BIOSLoaded = (JaguarLoadROM(jaguarBootROM, vjs.jagBootPath) == 0x20000 ? true : false);
244         WriteLog("VJ: BIOS is %savailable...\n", (BIOSLoaded ? "" : "not "));
245         CDBIOSLoaded = (JaguarLoadROM(jaguarCDBootROM, vjs.CDBootPath) == 0x40000 ? true : false);
246         WriteLog("VJ: CD BIOS is %savailable...\n", (CDBIOSLoaded ? "" : "not "));
247 #endif
248
249         filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
250 }
251
252 void MainWin::closeEvent(QCloseEvent * event)
253 {
254         WriteSettings();
255         event->accept(); // ignore() if can't close for some reason
256 }
257
258 void MainWin::keyPressEvent(QKeyEvent * e)
259 {
260         HandleKeys(e, true);
261 }
262
263 void MainWin::keyReleaseEvent(QKeyEvent * e)
264 {
265         HandleKeys(e, false);
266 }
267
268 void MainWin::HandleKeys(QKeyEvent * e, bool state)
269 {
270         // We kill bad key combos here, before they can get to the emulator...
271         // This also kills the illegal instruction problem that cropped up in Rayman!
272         // May want to do this by killing the old one instead of ignoring the new one...
273         // Seems to work better that way...
274 #if 0
275         if ((e->key() == vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
276                 || (e->key() == vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
277                 || (e->key() == vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
278                 || (e->key() == vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U]))
279                 return;
280 #else
281         if (e->key() == vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
282                 joypad_0_buttons[BUTTON_R] = 0;
283         if (e->key() == vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
284                 joypad_0_buttons[BUTTON_L] = 0;
285         if (e->key() == vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
286                 joypad_0_buttons[BUTTON_D] = 0;
287         if (e->key() == vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U])
288                 joypad_0_buttons[BUTTON_U] = 0;
289 #endif
290
291         // No bad combos exist, let's stuff the emulator key buffers...!
292         for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
293         {
294                 if (e->key() == vjs.p1KeyBindings[i])
295                         joypad_0_buttons[i] = (uint8)state;
296         }
297 }
298
299 void MainWin::Open(void)
300 {
301 }
302
303 void MainWin::Configure(void)
304 {
305         // Call the configuration dialog and update settings
306         ConfigDialog dlg(this);
307         //ick.
308         dlg.generalTab->useUnknownSoftware->setChecked(allowUnknownSoftware);
309
310         if (dlg.exec() == false)
311                 return;
312
313         QString before = vjs.ROMPath;
314         dlg.UpdateVJSettings();
315         QString after = vjs.ROMPath;
316
317         bool allowOld = allowUnknownSoftware;
318         //ick.
319         allowUnknownSoftware = dlg.generalTab->useUnknownSoftware->isChecked();
320
321         // We rescan the "software" folder if the user either changed the path or
322         // checked/unchecked the "Allow unknown files" option in the config dialog.
323         if ((before != after) || (allowOld != allowUnknownSoftware))
324                 filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
325
326         // Just in case we crash before a clean exit...
327         WriteSettings();
328 }
329
330 //
331 // Here's the main emulator loop
332 //
333 void MainWin::Timer(void)
334 {
335         if (!running)
336                 return;
337
338         if (showUntunedTankCircuit)
339         {
340                 // Random hash & trash
341                 // We try to simulate an untuned tank circuit here... :-)
342                 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
343                 {
344                         for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
345                         {
346                                 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24;// | (rand() & 0xFF);//0x000000FF;
347         //                      buffer[(y * textureWidth) + x] = x*y;
348                         }
349                 }
350         }
351         else
352         {
353                 // Otherwise, run the Jaguar simulation
354                 JaguarExecuteNew();
355 //              memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->rasterWidth);
356                 memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->textureWidth * sizeof(uint32_t));
357 //              memcpy(surface->pixels, backbuffer, TOMGetVideoModeWidth() * TOMGetVideoModeHeight() * 4);
358         }
359
360         videoWidget->updateGL();
361 }
362
363 #if 0
364 Window * RunEmu(void)
365 {
366 //      extern uint32 * backbuffer;
367         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
368         memset(overlayPixels, 0x00, 640 * 480 * 4);                     // Clear out overlay...
369
370 //This is crappy... !!! FIX !!!
371 //      extern bool finished, showGUI;
372
373         sdlemuDisableOverlay();
374
375 //      uint32 nFrame = 0, nFrameskip = 0;
376         uint32 totalFrames = 0;
377         finished = false;
378         bool showMessage = true;
379         uint32 showMsgFrames = 120;
380         uint8 transparency = 0xFF;
381         // Pass a message to the "joystick" code to debounce the ESC key...
382         debounceRunKey = true;
383
384         uint32 cartType = 4;
385         if (jaguarRomSize == 0x200000)
386                 cartType = 0;
387         else if (jaguarRomSize == 0x400000)
388                 cartType = 1;
389         else if (jaguarMainRomCRC32 == 0x687068D5)
390                 cartType = 2;
391         else if (jaguarMainRomCRC32 == 0x55A0669C)
392                 cartType = 3;
393
394         const char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
395         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
396
397         while (!finished)
398         {
399                 // Set up new backbuffer with new pixels and data
400                 JaguarExecuteNew();
401                 totalFrames++;
402 //WriteLog("Frame #%u...\n", totalFrames);
403 //extern bool doDSPDis;
404 //if (totalFrames == 373)
405 //      doDSPDis = true;
406
407 //Problem: Need to do this *only* when the state changes from visible to not...
408 //Also, need to clear out the GUI when not on (when showMessage is active...)
409 if (showGUI || showMessage)
410         sdlemuEnableOverlay();
411 else
412         sdlemuDisableOverlay();
413
414 //Add in a new function for clearing patches of screen (ClearOverlayRect)
415
416 // Also: Take frame rate into account when calculating fade time...
417
418                 // Some QnD GUI stuff here...
419                 if (showGUI)
420                 {
421                         FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
422                         extern uint32 gpu_pc, dsp_pc;
423                         DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
424                         DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
425                         DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
426                 }
427
428                 if (showMessage)
429                 {
430                         DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
431                         DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
432                         DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguarMainRomCRC32);
433
434                         if (showMsgFrames == 0)
435                         {
436                                 transparency--;
437
438                                 if (transparency == 0)
439 {
440                                         showMessage = false;
441 /*extern bool doGPUDis;
442 doGPUDis = true;//*/
443 }
444
445                         }
446                         else
447                                 showMsgFrames--;
448                 }
449
450                 frameCount++;
451
452                 if (SDL_GetTicks() - elapsedTicks > 250)
453                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
454         }
455
456         // Save the background for the GUI...
457         // In this case, we squash the color to monochrome, then force it to blue + green...
458         for(uint32 i=0; i<TOMGetVideoModeWidth() * 256; i++)
459         {
460                 uint32 pixel = backbuffer[i];
461                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
462                 pixel = ((r + g + b) / 3) & 0x00FF;
463                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
464         }
465
466         sdlemuEnableOverlay();
467
468         return NULL;
469 }
470 #endif
471
472 void MainWin::TogglePowerState(void)
473 {
474         powerButtonOn = !powerButtonOn;
475
476         if (!powerButtonOn)
477         {
478                 pauseAct->setChecked(false);
479                 pauseAct->setDisabled(true);
480                 showUntunedTankCircuit = true;
481                 running = true;
482         }
483         else
484         {
485                 if (!CDActive)
486                 {
487                         showUntunedTankCircuit = (cartridgeLoaded ? false : true);
488                         pauseAct->setChecked(false);
489                         pauseAct->setDisabled(!cartridgeLoaded);
490                 }
491                 else
492                 {
493 // Should check for cartridgeLoaded here as well...!
494 // We can clear it when toggling CDActive on, so that when we power cycle it does the
495 // expected thing. Otherwise, if we use the file picker to insert a cart, we expect
496 // to run the cart! Maybe have a RemoveCart function that only works if the CD unit
497 // is active?
498                         showUntunedTankCircuit = false;
499                         pauseAct->setChecked(false);
500                         pauseAct->setDisabled(false);
501                         memcpy(jagMemSpace + 0x800000, jaguarCDBootROM, 0x40000);
502                 }
503
504 //(Err, what's so crappy about this? It seems to do what it's supposed to...)
505 //This is crappy!!! !!! FIX !!!
506 //Is this even needed any more? Hmm. Maybe. Dunno.
507 //Seems like it is... But then again, maybe not. Have to test it to see.
508                 WriteLog("GUI: Resetting Jaguar...\n");
509                 JaguarReset();
510                 running = true;
511         }
512 }
513
514 void MainWin::ToggleRunState(void)
515 {
516         running = !running;
517
518         if (!running)
519         {
520 #if 0
521                 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
522                         for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
523                                 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = 0x00000000;
524 #else
525 //              for(uint32_t i=0; i<TOMGetVideoModeWidth() * 256; i++)
526                 for(uint32_t i=0; i<videoWidget->textureWidth * 256; i++)
527                 {
528                         uint32_t pixel = backbuffer[i];
529 //                      uint8_t b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
530                         uint8_t r = (pixel >> 24) & 0xFF, g = (pixel >> 16) & 0xFF, b = (pixel >> 8) & 0xFF;
531                         pixel = ((r + g + b) / 3) & 0x00FF;
532 //                      backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
533                         backbuffer[i] = 0x000000FF | (pixel << 16) | (pixel << 8);
534                 }
535
536 //              memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->rasterWidth);
537                 memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->textureWidth * sizeof(uint32_t));
538 #endif
539
540                 videoWidget->updateGL();
541         }
542 }
543
544 void MainWin::SetZoom100(void)
545 {
546         zoomLevel = 1;
547         ResizeMainWindow();
548 }
549
550 void MainWin::SetZoom200(void)
551 {
552         zoomLevel = 2;
553         ResizeMainWindow();
554 }
555
556 void MainWin::SetZoom300(void)
557 {
558         zoomLevel = 3;
559         ResizeMainWindow();
560 }
561
562 void MainWin::SetNTSC(void)
563 {
564         vjs.hardwareTypeNTSC = true;
565         ResizeMainWindow();
566 }
567
568 void MainWin::SetPAL(void)
569 {
570         vjs.hardwareTypeNTSC = false;
571         ResizeMainWindow();
572 }
573
574 void MainWin::ToggleBlur(void)
575 {
576         vjs.glFilter = !vjs.glFilter;
577 }
578
579 void MainWin::ShowAboutWin(void)
580 {
581         aboutWin->show();
582 }
583
584 void MainWin::InsertCart(void)
585 {
586         filePickWin->show();
587 }
588
589 void MainWin::LoadSoftware(QString file)
590 {
591         running = false;                                                        //  Prevent bad things(TM) from happening...
592         SET32(jaguarMainRAM, 0, 0x00200000);            // Set top of stack...
593         cartridgeLoaded = (JaguarLoadFile(file.toAscii().data()) ? true : false);
594
595         powerAct->setDisabled(false);
596         powerAct->setChecked(true);
597         powerButtonOn = false;
598         TogglePowerState();
599
600 //      QString newTitle = QString("Virtual Jaguar v2.0.0 - Now playing: %1")
601         QString newTitle = QString("Virtual Jaguar " VJ_RELEASE_VERSION
602                 " - Now playing: %1")
603                 .arg(filePickWin->GetSelectedPrettyName());
604         setWindowTitle(newTitle);
605 }
606
607 void MainWin::ToggleCDUsage(void)
608 {
609         CDActive = !CDActive;
610
611         if (CDActive)
612         {
613                 powerAct->setDisabled(false);
614         }
615         else
616         {
617                 powerAct->setDisabled(true);
618         }
619 }
620
621 void MainWin::ResizeMainWindow(void)
622 {
623         videoWidget->setFixedSize(zoomLevel * 320, zoomLevel * (vjs.hardwareTypeNTSC ? 240 : 256));
624         show();
625
626         for(int i=0; i<2; i++)
627         {
628                 resize(0, 0);
629                 usleep(2000);
630                 QApplication::processEvents();
631         }
632 }
633
634 void MainWin::ReadSettings(void)
635 {
636         QSettings settings("Underground Software", "Virtual Jaguar");
637         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
638         QSize size = settings.value("size", QSize(400, 400)).toSize();
639         resize(size);
640         move(pos);
641         pos = settings.value("cartLoadPos", QPoint(200, 200)).toPoint();
642         filePickWin->move(pos);
643
644         zoomLevel = settings.value("zoom", 1).toInt();
645         allowUnknownSoftware = settings.value("showUnknownSoftware", false).toBool();
646
647         vjs.useJoystick      = settings.value("useJoystick", false).toBool();
648         vjs.joyport          = settings.value("joyport", 0).toInt();
649         vjs.hardwareTypeNTSC = settings.value("hardwareTypeNTSC", true).toBool();
650         vjs.frameSkip        = settings.value("frameSkip", 0).toInt();
651         vjs.useJaguarBIOS    = settings.value("useJaguarBIOS", false).toBool();
652         vjs.DSPEnabled       = settings.value("DSPEnabled", false).toBool();
653         vjs.usePipelinedDSP  = settings.value("usePipelinedDSP", false).toBool();
654         vjs.fullscreen       = settings.value("fullscreen", false).toBool();
655         vjs.useOpenGL        = settings.value("useOpenGL", true).toBool();
656         vjs.glFilter         = settings.value("glFilterType", 0).toInt();
657         vjs.renderType       = settings.value("renderType", 0).toInt();
658         strcpy(vjs.jagBootPath, settings.value("JagBootROM", "./bios/[BIOS] Atari Jaguar (USA, Europe).zip").toString().toAscii().data());
659         strcpy(vjs.CDBootPath, settings.value("CDBootROM", "./bios/jagcd.rom").toString().toAscii().data());
660         strcpy(vjs.EEPROMPath, settings.value("EEPROMs", "./eeproms").toString().toAscii().data());
661         strcpy(vjs.ROMPath, settings.value("ROMs", "./software").toString().toAscii().data());
662 WriteLog("MainWin: Paths\n");
663 WriteLog("    jagBootPath = \"%s\"\n", vjs.jagBootPath);
664 WriteLog("    CDBootPath  = \"%s\"\n", vjs.CDBootPath);
665 WriteLog("    EEPROMPath  = \"%s\"\n", vjs.EEPROMPath);
666 WriteLog("    ROMPath     = \"%s\"\n", vjs.ROMPath);
667
668         // Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
669         vjs.p1KeyBindings[BUTTON_U] = settings.value("p1k_up", Qt::Key_Up).toInt();
670         vjs.p1KeyBindings[BUTTON_D] = settings.value("p1k_down", Qt::Key_Down).toInt();
671         vjs.p1KeyBindings[BUTTON_L] = settings.value("p1k_left", Qt::Key_Left).toInt();
672         vjs.p1KeyBindings[BUTTON_R] = settings.value("p1k_right", Qt::Key_Right).toInt();
673         vjs.p1KeyBindings[BUTTON_C] = settings.value("p1k_c", Qt::Key_Z).toInt();
674         vjs.p1KeyBindings[BUTTON_B] = settings.value("p1k_b", Qt::Key_X).toInt();
675         vjs.p1KeyBindings[BUTTON_A] = settings.value("p1k_a", Qt::Key_C).toInt();
676         vjs.p1KeyBindings[BUTTON_OPTION] = settings.value("p1k_option", Qt::Key_Apostrophe).toInt();
677         vjs.p1KeyBindings[BUTTON_PAUSE] = settings.value("p1k_pause", Qt::Key_Return).toInt();
678         vjs.p1KeyBindings[BUTTON_0] = settings.value("p1k_0", Qt::Key_0).toInt();
679         vjs.p1KeyBindings[BUTTON_1] = settings.value("p1k_1", Qt::Key_1).toInt();
680         vjs.p1KeyBindings[BUTTON_2] = settings.value("p1k_2", Qt::Key_2).toInt();
681         vjs.p1KeyBindings[BUTTON_3] = settings.value("p1k_3", Qt::Key_3).toInt();
682         vjs.p1KeyBindings[BUTTON_4] = settings.value("p1k_4", Qt::Key_4).toInt();
683         vjs.p1KeyBindings[BUTTON_5] = settings.value("p1k_5", Qt::Key_5).toInt();
684         vjs.p1KeyBindings[BUTTON_6] = settings.value("p1k_6", Qt::Key_6).toInt();
685         vjs.p1KeyBindings[BUTTON_7] = settings.value("p1k_7", Qt::Key_7).toInt();
686         vjs.p1KeyBindings[BUTTON_8] = settings.value("p1k_8", Qt::Key_8).toInt();
687         vjs.p1KeyBindings[BUTTON_9] = settings.value("p1k_9", Qt::Key_9).toInt();
688         vjs.p1KeyBindings[BUTTON_d] = settings.value("p1k_pound", Qt::Key_Slash).toInt();
689         vjs.p1KeyBindings[BUTTON_s] = settings.value("p1k_star", Qt::Key_Asterisk).toInt();
690
691         vjs.p2KeyBindings[BUTTON_U] = settings.value("p2k_up", Qt::Key_Up).toInt();
692         vjs.p2KeyBindings[BUTTON_D] = settings.value("p2k_down", Qt::Key_Down).toInt();
693         vjs.p2KeyBindings[BUTTON_L] = settings.value("p2k_left", Qt::Key_Left).toInt();
694         vjs.p2KeyBindings[BUTTON_R] = settings.value("p2k_right", Qt::Key_Right).toInt();
695         vjs.p2KeyBindings[BUTTON_C] = settings.value("p2k_c", Qt::Key_Z).toInt();
696         vjs.p2KeyBindings[BUTTON_B] = settings.value("p2k_b", Qt::Key_X).toInt();
697         vjs.p2KeyBindings[BUTTON_A] = settings.value("p2k_a", Qt::Key_C).toInt();
698         vjs.p2KeyBindings[BUTTON_OPTION] = settings.value("p2k_option", Qt::Key_Apostrophe).toInt();
699         vjs.p2KeyBindings[BUTTON_PAUSE] = settings.value("p2k_pause", Qt::Key_Return).toInt();
700         vjs.p2KeyBindings[BUTTON_0] = settings.value("p2k_0", Qt::Key_0).toInt();
701         vjs.p2KeyBindings[BUTTON_1] = settings.value("p2k_1", Qt::Key_1).toInt();
702         vjs.p2KeyBindings[BUTTON_2] = settings.value("p2k_2", Qt::Key_2).toInt();
703         vjs.p2KeyBindings[BUTTON_3] = settings.value("p2k_3", Qt::Key_3).toInt();
704         vjs.p2KeyBindings[BUTTON_4] = settings.value("p2k_4", Qt::Key_4).toInt();
705         vjs.p2KeyBindings[BUTTON_5] = settings.value("p2k_5", Qt::Key_5).toInt();
706         vjs.p2KeyBindings[BUTTON_6] = settings.value("p2k_6", Qt::Key_6).toInt();
707         vjs.p2KeyBindings[BUTTON_7] = settings.value("p2k_7", Qt::Key_7).toInt();
708         vjs.p2KeyBindings[BUTTON_8] = settings.value("p2k_8", Qt::Key_8).toInt();
709         vjs.p2KeyBindings[BUTTON_9] = settings.value("p2k_9", Qt::Key_9).toInt();
710         vjs.p2KeyBindings[BUTTON_d] = settings.value("p2k_pound", Qt::Key_Slash).toInt();
711         vjs.p2KeyBindings[BUTTON_s] = settings.value("p2k_star", Qt::Key_Asterisk).toInt();
712 }
713
714 void MainWin::WriteSettings(void)
715 {
716         QSettings settings("Underground Software", "Virtual Jaguar");
717         settings.setValue("pos", pos());
718         settings.setValue("size", size());
719         settings.setValue("cartLoadPos", filePickWin->pos());
720
721         settings.setValue("zoom", zoomLevel);
722         settings.setValue("showUnknownSoftware", allowUnknownSoftware);
723
724         settings.setValue("useJoystick", vjs.useJoystick);
725         settings.setValue("joyport", vjs.joyport);
726         settings.setValue("hardwareTypeNTSC", vjs.hardwareTypeNTSC);
727         settings.setValue("frameSkip", vjs.frameSkip);
728         settings.setValue("useJaguarBIOS", vjs.useJaguarBIOS);
729         settings.setValue("DSPEnabled", vjs.DSPEnabled);
730         settings.setValue("usePipelinedDSP", vjs.usePipelinedDSP);
731         settings.setValue("fullscreen", vjs.fullscreen);
732         settings.setValue("useOpenGL", vjs.useOpenGL);
733         settings.setValue("glFilterType", vjs.glFilter);
734         settings.setValue("renderType", vjs.renderType);
735         settings.setValue("JagBootROM", vjs.jagBootPath);
736         settings.setValue("CDBootROM", vjs.CDBootPath);
737         settings.setValue("EEPROMs", vjs.EEPROMPath);
738         settings.setValue("ROMs", vjs.ROMPath);
739
740         settings.setValue("p1k_up", vjs.p1KeyBindings[BUTTON_U]);
741         settings.setValue("p1k_down", vjs.p1KeyBindings[BUTTON_D]);
742         settings.setValue("p1k_left", vjs.p1KeyBindings[BUTTON_L]);
743         settings.setValue("p1k_right", vjs.p1KeyBindings[BUTTON_R]);
744         settings.setValue("p1k_c", vjs.p1KeyBindings[BUTTON_C]);
745         settings.setValue("p1k_b", vjs.p1KeyBindings[BUTTON_B]);
746         settings.setValue("p1k_a", vjs.p1KeyBindings[BUTTON_A]);
747         settings.setValue("p1k_option", vjs.p1KeyBindings[BUTTON_OPTION]);
748         settings.setValue("p1k_pause", vjs.p1KeyBindings[BUTTON_PAUSE]);
749         settings.setValue("p1k_0", vjs.p1KeyBindings[BUTTON_0]);
750         settings.setValue("p1k_1", vjs.p1KeyBindings[BUTTON_1]);
751         settings.setValue("p1k_2", vjs.p1KeyBindings[BUTTON_2]);
752         settings.setValue("p1k_3", vjs.p1KeyBindings[BUTTON_3]);
753         settings.setValue("p1k_4", vjs.p1KeyBindings[BUTTON_4]);
754         settings.setValue("p1k_5", vjs.p1KeyBindings[BUTTON_5]);
755         settings.setValue("p1k_6", vjs.p1KeyBindings[BUTTON_6]);
756         settings.setValue("p1k_7", vjs.p1KeyBindings[BUTTON_7]);
757         settings.setValue("p1k_8", vjs.p1KeyBindings[BUTTON_8]);
758         settings.setValue("p1k_9", vjs.p1KeyBindings[BUTTON_9]);
759         settings.setValue("p1k_pound", vjs.p1KeyBindings[BUTTON_d]);
760         settings.setValue("p1k_star", vjs.p1KeyBindings[BUTTON_s]);
761
762         settings.setValue("p2k_up", vjs.p2KeyBindings[BUTTON_U]);
763         settings.setValue("p2k_down", vjs.p2KeyBindings[BUTTON_D]);
764         settings.setValue("p2k_left", vjs.p2KeyBindings[BUTTON_L]);
765         settings.setValue("p2k_right", vjs.p2KeyBindings[BUTTON_R]);
766         settings.setValue("p2k_c", vjs.p2KeyBindings[BUTTON_C]);
767         settings.setValue("p2k_b", vjs.p2KeyBindings[BUTTON_B]);
768         settings.setValue("p2k_a", vjs.p2KeyBindings[BUTTON_A]);
769         settings.setValue("p2k_option", vjs.p2KeyBindings[BUTTON_OPTION]);
770         settings.setValue("p2k_pause", vjs.p2KeyBindings[BUTTON_PAUSE]);
771         settings.setValue("p2k_0", vjs.p2KeyBindings[BUTTON_0]);
772         settings.setValue("p2k_1", vjs.p2KeyBindings[BUTTON_1]);
773         settings.setValue("p2k_2", vjs.p2KeyBindings[BUTTON_2]);
774         settings.setValue("p2k_3", vjs.p2KeyBindings[BUTTON_3]);
775         settings.setValue("p2k_4", vjs.p2KeyBindings[BUTTON_4]);
776         settings.setValue("p2k_5", vjs.p2KeyBindings[BUTTON_5]);
777         settings.setValue("p2k_6", vjs.p2KeyBindings[BUTTON_6]);
778         settings.setValue("p2k_7", vjs.p2KeyBindings[BUTTON_7]);
779         settings.setValue("p2k_8", vjs.p2KeyBindings[BUTTON_8]);
780         settings.setValue("p2k_9", vjs.p2KeyBindings[BUTTON_9]);
781         settings.setValue("p2k_pound", vjs.p2KeyBindings[BUTTON_d]);
782         settings.setValue("p2k_star", vjs.p2KeyBindings[BUTTON_s]);
783 }
784
785 // Here's how Byuu does it...
786 // I think I have it working now... :-)
787 #if 0
788 void Utility::resizeMainWindow()
789 {
790   unsigned region = config().video.context->region;
791   unsigned multiplier = config().video.context->multiplier;
792   unsigned width = 256 * multiplier;
793   unsigned height = (region == 0 ? 224 : 239) * multiplier;
794
795   if(config().video.context->correctAspectRatio)
796   {
797     if(region == 0)
798         {
799       width = (double)width * config().video.ntscAspectRatio + 0.5;  //NTSC adjust
800     }
801         else
802         {
803       width = (double)width * config().video.palAspectRatio  + 0.5;  //PAL adjust
804     }
805   }
806
807   if(config().video.isFullscreen == false)
808   {
809     //get effective desktop work area region (ignore Windows taskbar, OS X dock, etc.)
810     QRect deskRect = QApplication::desktop()->availableGeometry(mainWindow);
811
812     //ensure window size will not be larger than viewable desktop area
813     constrainSize(height, width, deskRect.height()); //- frameHeight);
814     constrainSize(width, height, deskRect.width());  //- frameWidth );
815
816     mainWindow->canvas->setFixedSize(width, height);
817     mainWindow->show();
818   }
819   else
820   {
821     for(unsigned i = 0; i < 2; i++)
822         {
823       unsigned iWidth = width, iHeight = height;
824
825       constrainSize(iHeight, iWidth, mainWindow->canvasContainer->size().height());
826       constrainSize(iWidth, iHeight, mainWindow->canvasContainer->size().width());
827
828       //center canvas onscreen; ensure it is not larger than viewable area
829       mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
830       mainWindow->canvas->setFixedSize(iWidth, iHeight);
831       mainWindow->canvas->setMinimumSize(0, 0);
832
833       usleep(2000);
834       QApplication::processEvents();
835     }
836   }
837
838   //workaround for Qt/Xlib bug:
839   //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor;
840   //so force it to show Qt::ArrowCursor, as expected
841   mainWindow->setCursor(Qt::ArrowCursor);
842   mainWindow->canvasContainer->setCursor(Qt::ArrowCursor);
843   mainWindow->canvas->setCursor(Qt::ArrowCursor);
844
845   //workaround for DirectSound(?) bug:
846   //window resizing sometimes breaks audio sync, this call re-initializes it
847   updateAvSync();
848 }
849
850 void Utility::setScale(unsigned scale)
851 {
852   config().video.context->multiplier = scale;
853   resizeMainWindow();
854   mainWindow->shrink();
855   mainWindow->syncUi();
856 }
857
858 void QbWindow::shrink()
859 {
860   if(config().video.isFullscreen == false)
861   {
862     for(unsigned i = 0; i < 2; i++)
863         {
864       resize(0, 0);
865       usleep(2000);
866       QApplication::processEvents();
867     }
868   }
869 }
870 #endif