2 // mainwin.cpp - Qt-based GUI for Virtual Jaguar: Main Application Window
4 // (C) 2009 Underground Software
6 // JLH = James L. Hammons <jlhamm@acm.org>
9 // --- ---------- -------------------------------------------------------------
10 // JLH 12/23/2009 Created this file
11 // JLH 12/20/2010 Added settings, menus & toolbars
21 // Uncomment this for debugging...
23 //#define DEBUGFOO // Various tool debugging...
24 //#define DEBUGTP // Toolpalette debugging...
34 // The way BSNES controls things is by setting a timer with a zero
35 // timeout, sleeping if not emulating anything. Seems there has to be a
38 // It has a novel approach to plugging-in/using different video/audio/input
39 // methods, can we do something similar or should we just use the built-in
42 // We're going to try to use the built-in OpenGL support and see how it goes.
43 // We'll make the VJ core modular so that it doesn't matter what GUI is in
44 // use, we can drop it in anywhere and use it as-is.
48 videoWidget = new GLWidget(this);
49 setCentralWidget(videoWidget);
50 setWindowIcon(QIcon(":/res/vj.xpm"));
51 setWindowTitle("Virtual Jaguar v2.0.0");
53 aboutWin = new AboutWindow(this);
55 videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
56 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
60 quitAppAct = new QAction(tr("E&xit"), this);
61 quitAppAct->setShortcuts(QKeySequence::Quit);
62 quitAppAct->setStatusTip(tr("Quit Virtual Jaguar"));
63 connect(quitAppAct, SIGNAL(triggered()), this, SLOT(close()));
65 powerAct = new QAction(QIcon(":/res/power.png"), tr("&Power"), this);
66 powerAct->setStatusTip(tr("Toggle running state"));
67 powerAct->setCheckable(true);
68 connect(powerAct, SIGNAL(triggered()), this, SLOT(ToggleRunState()));
70 zoomActs = new QActionGroup(this);
72 x1Act = new QAction(QIcon(":/res/zoom100.png"), tr("Zoom 100%"), zoomActs);
73 x1Act->setStatusTip(tr("Set window zoom to 100%"));
74 x1Act->setCheckable(true);
75 connect(x1Act, SIGNAL(triggered()), this, SLOT(SetZoom100()));
77 x2Act = new QAction(QIcon(":/res/zoom200.png"), tr("Zoom 200%"), zoomActs);
78 x2Act->setStatusTip(tr("Set window zoom to 200%"));
79 x2Act->setCheckable(true);
80 connect(x2Act, SIGNAL(triggered()), this, SLOT(SetZoom200()));
82 x3Act = new QAction(QIcon(":/res/zoom300.png"), tr("Zoom 300%"), zoomActs);
83 x3Act->setStatusTip(tr("Set window zoom to 300%"));
84 x3Act->setCheckable(true);
85 connect(x3Act, SIGNAL(triggered()), this, SLOT(SetZoom300()));
87 tvTypeActs = new QActionGroup(this);
89 ntscAct = new QAction(QIcon(":/res/generic.png"), tr("NTSC"), tvTypeActs);
90 ntscAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
91 ntscAct->setCheckable(true);
92 connect(ntscAct, SIGNAL(triggered()), this, SLOT(SetNTSC()));
94 palAct = new QAction(QIcon(":/res/generic.png"), tr("PAL"), tvTypeActs);
95 palAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
96 palAct->setCheckable(true);
97 connect(palAct, SIGNAL(triggered()), this, SLOT(SetPAL()));
99 blurAct = new QAction(QIcon(":/res/generic.png"), tr("Blur"), this);
100 blurAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
101 blurAct->setCheckable(true);
102 connect(blurAct, SIGNAL(triggered()), this, SLOT(ToggleBlur()));
104 aboutAct = new QAction(QIcon(":/res/generic.png"), tr("&About..."), this);
105 aboutAct->setStatusTip(tr("Blatant self-promotion"));
106 connect(aboutAct, SIGNAL(triggered()), this, SLOT(ShowAboutWin()));
108 // Create menus & toolbars
110 fileMenu = menuBar()->addMenu(tr("&File"));
111 fileMenu->addAction(powerAct);
112 fileMenu->addAction(quitAppAct);
114 fileMenu = menuBar()->addMenu(tr("&Help"));
115 fileMenu->addAction(aboutAct);
117 QToolBar * toolbar = addToolBar(tr("Stuff"));
118 toolbar->addAction(powerAct);
119 toolbar->addAction(x1Act);
120 toolbar->addAction(x2Act);
121 toolbar->addAction(x3Act);
122 toolbar->addAction(ntscAct);
123 toolbar->addAction(palAct);
124 toolbar->addAction(blurAct);
127 statusBar()->showMessage(tr("Ready"));
130 setUnifiedTitleAndToolBarOnMac(true);
132 // Set toolbar button based on setting read in (sync the UI)...
133 blurAct->setChecked(vjs.glFilter);
134 x1Act->setChecked(zoomLevel == 1);
135 x2Act->setChecked(zoomLevel == 2);
136 x3Act->setChecked(zoomLevel == 3);
137 running = powerAct->isChecked();
138 ntscAct->setChecked(vjs.hardwareTypeNTSC);
139 palAct->setChecked(!vjs.hardwareTypeNTSC);
141 // Do this in case original size isn't correct (mostly for the first-run case)
144 // Set up timer based loop for animation...
145 timer = new QTimer(this);
146 connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
150 void MainWin::closeEvent(QCloseEvent * event)
153 event->accept(); // ignore() if can't close for some reason
156 void MainWin::Open(void)
160 void MainWin::Timer(void)
165 // Random hash & trash
166 // We try to simulate an untuned tank circuit here... :-)
167 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
169 for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
171 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24;// | (rand() & 0xFF);//0x000000FF;
172 // buffer[(y * textureWidth) + x] = x*y;
176 videoWidget->updateGL();
179 void MainWin::ToggleRunState(void)
185 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
186 for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
187 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = 0x00000000;
189 videoWidget->updateGL();
193 void MainWin::SetZoom100(void)
199 void MainWin::SetZoom200(void)
205 void MainWin::SetZoom300(void)
211 void MainWin::SetNTSC(void)
213 vjs.hardwareTypeNTSC = true;
217 void MainWin::SetPAL(void)
219 vjs.hardwareTypeNTSC = false;
223 void MainWin::ToggleBlur(void)
225 vjs.glFilter = !vjs.glFilter;
228 void MainWin::ShowAboutWin(void)
233 void MainWin::ResizeMainWindow(void)
235 videoWidget->setFixedSize(zoomLevel * 320, zoomLevel * (vjs.hardwareTypeNTSC ? 240 : 256));
238 for(int i=0; i<2; i++)
242 QApplication::processEvents();
246 void MainWin::ReadSettings(void)
248 QSettings settings("Underground Software", "Virtual Jaguar");
249 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
250 QSize size = settings.value("size", QSize(400, 400)).toSize();
254 zoomLevel = settings.value("zoom", 1).toInt();
256 vjs.useJoystick = settings.value("useJoystick", false).toBool();
257 vjs.joyport = settings.value("joyport", 0).toInt();
258 vjs.hardwareTypeNTSC = settings.value("hardwareTypeNTSC", true).toBool();
259 vjs.frameSkip = settings.value("frameSkip", 0).toInt();
260 vjs.useJaguarBIOS = settings.value("useJaguarBIOS", false).toBool();
261 vjs.DSPEnabled = settings.value("DSPEnabled", false).toBool();
262 vjs.usePipelinedDSP = settings.value("usePipelinedDSP", false).toBool();
263 vjs.fullscreen = settings.value("fullscreen", false).toBool();
264 vjs.useOpenGL = settings.value("useOpenGL", true).toBool();
265 vjs.glFilter = settings.value("glFilterType", 0).toInt();
266 vjs.renderType = settings.value("renderType", 0).toInt();
269 void MainWin::WriteSettings(void)
271 QSettings settings("Underground Software", "Virtual Jaguar");
272 settings.setValue("pos", pos());
273 settings.setValue("size", size());
275 settings.setValue("zoom", zoomLevel);
277 settings.setValue("useJoystick", vjs.useJoystick);
278 settings.setValue("joyport", vjs.joyport);
279 settings.setValue("hardwareTypeNTSC", vjs.hardwareTypeNTSC);
280 settings.setValue("frameSkip", vjs.frameSkip);
281 settings.setValue("useJaguarBIOS", vjs.useJaguarBIOS);
282 settings.setValue("DSPEnabled", vjs.DSPEnabled);
283 settings.setValue("usePipelinedDSP", vjs.usePipelinedDSP);
284 settings.setValue("fullscreen", vjs.fullscreen);
285 settings.setValue("useOpenGL", vjs.useOpenGL);
286 settings.setValue("glFilterType", vjs.glFilter);
287 settings.setValue("renderType", vjs.renderType);
290 // Here's how Byuu does it...
291 // I think I have it working now... :-)
293 void Utility::resizeMainWindow()
295 unsigned region = config().video.context->region;
296 unsigned multiplier = config().video.context->multiplier;
297 unsigned width = 256 * multiplier;
298 unsigned height = (region == 0 ? 224 : 239) * multiplier;
300 if(config().video.context->correctAspectRatio)
304 width = (double)width * config().video.ntscAspectRatio + 0.5; //NTSC adjust
308 width = (double)width * config().video.palAspectRatio + 0.5; //PAL adjust
312 if(config().video.isFullscreen == false)
314 //get effective desktop work area region (ignore Windows taskbar, OS X dock, etc.)
315 QRect deskRect = QApplication::desktop()->availableGeometry(mainWindow);
317 //ensure window size will not be larger than viewable desktop area
318 constrainSize(height, width, deskRect.height()); //- frameHeight);
319 constrainSize(width, height, deskRect.width()); //- frameWidth );
321 mainWindow->canvas->setFixedSize(width, height);
326 for(unsigned i = 0; i < 2; i++)
328 unsigned iWidth = width, iHeight = height;
330 constrainSize(iHeight, iWidth, mainWindow->canvasContainer->size().height());
331 constrainSize(iWidth, iHeight, mainWindow->canvasContainer->size().width());
333 //center canvas onscreen; ensure it is not larger than viewable area
334 mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
335 mainWindow->canvas->setFixedSize(iWidth, iHeight);
336 mainWindow->canvas->setMinimumSize(0, 0);
339 QApplication::processEvents();
343 //workaround for Qt/Xlib bug:
344 //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor;
345 //so force it to show Qt::ArrowCursor, as expected
346 mainWindow->setCursor(Qt::ArrowCursor);
347 mainWindow->canvasContainer->setCursor(Qt::ArrowCursor);
348 mainWindow->canvas->setCursor(Qt::ArrowCursor);
350 //workaround for DirectSound(?) bug:
351 //window resizing sometimes breaks audio sync, this call re-initializes it
355 void Utility::setScale(unsigned scale)
357 config().video.context->multiplier = scale;
359 mainWindow->shrink();
360 mainWindow->syncUi();
363 void QbWindow::shrink()
365 if(config().video.isFullscreen == false)
367 for(unsigned i = 0; i < 2; i++)
371 QApplication::processEvents();