]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/mainwin.cpp
Added beginnings of ROM directory scanning functionality
[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 //
13
14 // FIXED:
15 //
16 //
17 // STILL TO BE DONE:
18 //
19 //
20
21 // Uncomment this for debugging...
22 //#define DEBUG
23 //#define DEBUGFOO                      // Various tool debugging...
24 //#define DEBUGTP                               // Toolpalette debugging...
25
26 #include "mainwin.h"
27
28 //#include <QtGui>
29 //#include <QtOpenGL>
30 #include "glwidget.h"
31 #include "about.h"
32 #include "settings.h"
33 #include "filepicker.h"
34
35 // The way BSNES controls things is by setting a timer with a zero
36 // timeout, sleeping if not emulating anything. Seems there has to be a
37 // better way.
38
39 // It has a novel approach to plugging-in/using different video/audio/input
40 // methods, can we do something similar or should we just use the built-in
41 // QOpenGL?
42
43 // We're going to try to use the built-in OpenGL support and see how it goes.
44 // We'll make the VJ core modular so that it doesn't matter what GUI is in
45 // use, we can drop it in anywhere and use it as-is.
46
47 MainWin::MainWin()
48 {
49         videoWidget = new GLWidget(this);
50         setCentralWidget(videoWidget);
51         setWindowIcon(QIcon(":/res/vj.xpm"));
52         setWindowTitle("Virtual Jaguar v2.0.0");
53
54         ReadSettings();
55         setUnifiedTitleAndToolBarOnMac(true);
56
57         aboutWin = new AboutWindow(this);
58         filePickWin = new FilePickerWindow(this);
59
60     videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
61     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
62
63         // Create actions
64
65         quitAppAct = new QAction(tr("E&xit"), this);
66         quitAppAct->setShortcuts(QKeySequence::Quit);
67         quitAppAct->setStatusTip(tr("Quit Virtual Jaguar"));
68         connect(quitAppAct, SIGNAL(triggered()), this, SLOT(close()));
69
70         powerAct = new QAction(QIcon(":/res/power.png"), tr("&Power"), this);
71         powerAct->setStatusTip(tr("Toggle running state"));
72         powerAct->setCheckable(true);
73         connect(powerAct, SIGNAL(triggered()), this, SLOT(ToggleRunState()));
74
75         zoomActs = new QActionGroup(this);
76
77         x1Act = new QAction(QIcon(":/res/zoom100.png"), tr("Zoom 100%"), zoomActs);
78         x1Act->setStatusTip(tr("Set window zoom to 100%"));
79         x1Act->setCheckable(true);
80         connect(x1Act, SIGNAL(triggered()), this, SLOT(SetZoom100()));
81
82         x2Act = new QAction(QIcon(":/res/zoom200.png"), tr("Zoom 200%"), zoomActs);
83         x2Act->setStatusTip(tr("Set window zoom to 200%"));
84         x2Act->setCheckable(true);
85         connect(x2Act, SIGNAL(triggered()), this, SLOT(SetZoom200()));
86
87         x3Act = new QAction(QIcon(":/res/zoom300.png"), tr("Zoom 300%"), zoomActs);
88         x3Act->setStatusTip(tr("Set window zoom to 300%"));
89         x3Act->setCheckable(true);
90         connect(x3Act, SIGNAL(triggered()), this, SLOT(SetZoom300()));
91
92         tvTypeActs = new QActionGroup(this);
93
94         ntscAct = new QAction(QIcon(":/res/generic.png"), tr("NTSC"), tvTypeActs);
95         ntscAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
96         ntscAct->setCheckable(true);
97         connect(ntscAct, SIGNAL(triggered()), this, SLOT(SetNTSC()));
98
99         palAct = new QAction(QIcon(":/res/generic.png"), tr("PAL"), tvTypeActs);
100         palAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
101         palAct->setCheckable(true);
102         connect(palAct, SIGNAL(triggered()), this, SLOT(SetPAL()));
103
104         blurAct = new QAction(QIcon(":/res/generic.png"), tr("Blur"), this);
105         blurAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
106         blurAct->setCheckable(true);
107         connect(blurAct, SIGNAL(triggered()), this, SLOT(ToggleBlur()));
108
109         aboutAct = new QAction(QIcon(":/res/generic.png"), tr("&About..."), this);
110         aboutAct->setStatusTip(tr("Blatant self-promotion"));
111         connect(aboutAct, SIGNAL(triggered()), this, SLOT(ShowAboutWin()));
112
113         filePickAct = new QAction(QIcon(":/res/generic.png"), tr("Insert Cartridge..."), this);
114         filePickAct->setStatusTip(tr("Insert a cartridge into Virtual Jaguar"));
115         connect(filePickAct, SIGNAL(triggered()), this, SLOT(InsertCart()));
116
117         // Create menus & toolbars
118
119         fileMenu = menuBar()->addMenu(tr("&File"));
120         fileMenu->addAction(filePickAct);
121         fileMenu->addAction(powerAct);
122         fileMenu->addAction(quitAppAct);
123
124         fileMenu = menuBar()->addMenu(tr("&Help"));
125         fileMenu->addAction(aboutAct);
126
127         QToolBar * toolbar = addToolBar(tr("Stuff"));
128         toolbar->addAction(powerAct);
129         toolbar->addAction(x1Act);
130         toolbar->addAction(x2Act);
131         toolbar->addAction(x3Act);
132         toolbar->addAction(ntscAct);
133         toolbar->addAction(palAct);
134         toolbar->addAction(blurAct);
135
136         //      Create status bar
137         statusBar()->showMessage(tr("Ready"));
138
139         // Set toolbar button based on setting read in (sync the UI)...
140         blurAct->setChecked(vjs.glFilter);
141         x1Act->setChecked(zoomLevel == 1);
142         x2Act->setChecked(zoomLevel == 2);
143         x3Act->setChecked(zoomLevel == 3);
144         running = powerAct->isChecked();
145         ntscAct->setChecked(vjs.hardwareTypeNTSC);
146         palAct->setChecked(!vjs.hardwareTypeNTSC);
147
148         // Do this in case original size isn't correct (mostly for the first-run case)
149         ResizeMainWindow();
150
151         // Set up timer based loop for animation...
152         timer = new QTimer(this);
153         connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
154         timer->start(20);
155 }
156
157 void MainWin::closeEvent(QCloseEvent * event)
158 {
159         WriteSettings();
160         event->accept(); // ignore() if can't close for some reason
161 }
162
163 void MainWin::Open(void)
164 {
165 }
166
167 void MainWin::Timer(void)
168 {
169         if (!running)
170                 return;
171
172         // Random hash & trash
173         // We try to simulate an untuned tank circuit here... :-)
174         for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
175         {
176                 for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
177                 {
178                         videoWidget->buffer[(y * videoWidget->textureWidth) + x] = (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24;// | (rand() & 0xFF);//0x000000FF;
179 //                      buffer[(y * textureWidth) + x] = x*y;
180                 }
181         }
182
183         videoWidget->updateGL();
184 }
185
186 void MainWin::ToggleRunState(void)
187 {
188         running = !running;
189
190         if (!running)
191         {
192                 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
193                         for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
194                                 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = 0x00000000;
195
196                 videoWidget->updateGL();
197         }
198 }
199
200 void MainWin::SetZoom100(void)
201 {
202         zoomLevel = 1;
203         ResizeMainWindow();
204 }
205
206 void MainWin::SetZoom200(void)
207 {
208         zoomLevel = 2;
209         ResizeMainWindow();
210 }
211
212 void MainWin::SetZoom300(void)
213 {
214         zoomLevel = 3;
215         ResizeMainWindow();
216 }
217
218 void MainWin::SetNTSC(void)
219 {
220         vjs.hardwareTypeNTSC = true;
221         ResizeMainWindow();
222 }
223
224 void MainWin::SetPAL(void)
225 {
226         vjs.hardwareTypeNTSC = false;
227         ResizeMainWindow();
228 }
229
230 void MainWin::ToggleBlur(void)
231 {
232         vjs.glFilter = !vjs.glFilter;
233 }
234
235 void MainWin::ShowAboutWin(void)
236 {
237         aboutWin->show();
238 }
239
240 void MainWin::InsertCart(void)
241 {
242         filePickWin->show();
243 }
244
245 void MainWin::ResizeMainWindow(void)
246 {
247         videoWidget->setFixedSize(zoomLevel * 320, zoomLevel * (vjs.hardwareTypeNTSC ? 240 : 256));
248         show();
249
250         for(int i=0; i<2; i++)
251         {
252                 resize(0, 0);
253                 usleep(2000);
254                 QApplication::processEvents();
255         }
256 }
257
258 void MainWin::ReadSettings(void)
259 {
260         QSettings settings("Underground Software", "Virtual Jaguar");
261         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
262         QSize size = settings.value("size", QSize(400, 400)).toSize();
263         resize(size);
264         move(pos);
265
266         zoomLevel = settings.value("zoom", 1).toInt();
267
268         vjs.useJoystick      = settings.value("useJoystick", false).toBool();
269         vjs.joyport          = settings.value("joyport", 0).toInt();
270         vjs.hardwareTypeNTSC = settings.value("hardwareTypeNTSC", true).toBool();
271         vjs.frameSkip        = settings.value("frameSkip", 0).toInt();
272         vjs.useJaguarBIOS    = settings.value("useJaguarBIOS", false).toBool();
273         vjs.DSPEnabled       = settings.value("DSPEnabled", false).toBool();
274         vjs.usePipelinedDSP  = settings.value("usePipelinedDSP", false).toBool();
275         vjs.fullscreen       = settings.value("fullscreen", false).toBool();
276         vjs.useOpenGL        = settings.value("useOpenGL", true).toBool();
277         vjs.glFilter         = settings.value("glFilterType", 0).toInt();
278         vjs.renderType       = settings.value("renderType", 0).toInt();
279
280         // Hardcoded, !!! FIX !!!
281         strcpy(vjs.ROMPath, "./roms");
282 }
283
284 void MainWin::WriteSettings(void)
285 {
286         QSettings settings("Underground Software", "Virtual Jaguar");
287         settings.setValue("pos", pos());
288         settings.setValue("size", size());
289
290         settings.setValue("zoom", zoomLevel);
291
292         settings.setValue("useJoystick", vjs.useJoystick);
293         settings.setValue("joyport", vjs.joyport);
294         settings.setValue("hardwareTypeNTSC", vjs.hardwareTypeNTSC);
295         settings.setValue("frameSkip", vjs.frameSkip);
296         settings.setValue("useJaguarBIOS", vjs.useJaguarBIOS);
297         settings.setValue("DSPEnabled", vjs.DSPEnabled);
298         settings.setValue("usePipelinedDSP", vjs.usePipelinedDSP);
299         settings.setValue("fullscreen", vjs.fullscreen);
300         settings.setValue("useOpenGL", vjs.useOpenGL);
301         settings.setValue("glFilterType", vjs.glFilter);
302         settings.setValue("renderType", vjs.renderType);
303 }
304
305 // Here's how Byuu does it...
306 // I think I have it working now... :-)
307 #if 0
308 void Utility::resizeMainWindow()
309 {
310   unsigned region = config().video.context->region;
311   unsigned multiplier = config().video.context->multiplier;
312   unsigned width = 256 * multiplier;
313   unsigned height = (region == 0 ? 224 : 239) * multiplier;
314
315   if(config().video.context->correctAspectRatio)
316   {
317     if(region == 0)
318         {
319       width = (double)width * config().video.ntscAspectRatio + 0.5;  //NTSC adjust
320     }
321         else
322         {
323       width = (double)width * config().video.palAspectRatio  + 0.5;  //PAL adjust
324     }
325   }
326
327   if(config().video.isFullscreen == false)
328   {
329     //get effective desktop work area region (ignore Windows taskbar, OS X dock, etc.)
330     QRect deskRect = QApplication::desktop()->availableGeometry(mainWindow);
331
332     //ensure window size will not be larger than viewable desktop area
333     constrainSize(height, width, deskRect.height()); //- frameHeight);
334     constrainSize(width, height, deskRect.width());  //- frameWidth );
335
336     mainWindow->canvas->setFixedSize(width, height);
337     mainWindow->show();
338   }
339   else
340   {
341     for(unsigned i = 0; i < 2; i++)
342         {
343       unsigned iWidth = width, iHeight = height;
344
345       constrainSize(iHeight, iWidth, mainWindow->canvasContainer->size().height());
346       constrainSize(iWidth, iHeight, mainWindow->canvasContainer->size().width());
347
348       //center canvas onscreen; ensure it is not larger than viewable area
349       mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
350       mainWindow->canvas->setFixedSize(iWidth, iHeight);
351       mainWindow->canvas->setMinimumSize(0, 0);
352
353       usleep(2000);
354       QApplication::processEvents();
355     }
356   }
357
358   //workaround for Qt/Xlib bug:
359   //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor;
360   //so force it to show Qt::ArrowCursor, as expected
361   mainWindow->setCursor(Qt::ArrowCursor);
362   mainWindow->canvasContainer->setCursor(Qt::ArrowCursor);
363   mainWindow->canvas->setCursor(Qt::ArrowCursor);
364
365   //workaround for DirectSound(?) bug:
366   //window resizing sometimes breaks audio sync, this call re-initializes it
367   updateAvSync();
368 }
369
370 void Utility::setScale(unsigned scale)
371 {
372   config().video.context->multiplier = scale;
373   resizeMainWindow();
374   mainWindow->shrink();
375   mainWindow->syncUi();
376 }
377
378 void QbWindow::shrink()
379 {
380   if(config().video.isFullscreen == false)
381   {
382     for(unsigned i = 0; i < 2; i++)
383         {
384       resize(0, 0);
385       usleep(2000);
386       QApplication::processEvents();
387     }
388   }
389 }
390 #endif