1 #include <QTranslator>
2 #include <QIcon>
3 #include <QStyleFactory>
4 #include <QTimer>
5 #include <QString>
6 #include <QUrl>
7 #if QT_VERSION < 0x050000
8 #include <QApplication>
9 #else
10 #include <QGuiApplication>
11 #endif
12
13 #include "arcadesettings.h"
14 #include "tweakedqmlappviewer.h"
15 #include "consolewindow.h"
16 #include "macros.h"
17 #include "joystick.h"
18 #include "keyeventfilter.h"
19 #if defined(QMC2_ARCADE_OS_WIN)
20 #include "../windows_tools.h"
21 #endif
22 #if defined(QMC2_ARCADE_OS_MAC)
23 #include <mach-o/dyld.h>
24 #include <QFileInfo>
25 #include <QDir>
26 #endif
27
28 ArcadeSettings *globalConfig = 0;
29 ConsoleWindow *consoleWindow = 0;
30 int emulatorMode = QMC2_ARCADE_EMUMODE_MAME;
31 QStringList emulatorModes;
32 QStringList arcadeThemes;
33 QStringList mameThemes;
34 QStringList consoleModes;
35 #if QT_VERSION < 0x050000
36 QStringList graphicsSystems;
37 #endif
38 QStringList argumentList;
39 bool runApp = true;
40 bool debugKeys = false;
41 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
42 bool debugJoy = false;
43 #endif
44 bool debugQt = false;
45
46 #if QT_VERSION < 0x050000
qtMessageHandler(QtMsgType type,const char * msg)47 void qtMessageHandler(QtMsgType type, const char *msg)
48 #else
49 void qtMessageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg)
50 #endif
51 {
52 if ( !runApp )
53 return;
54
55 QString msgString;
56
57 switch ( type ) {
58 case QtDebugMsg:
59 if ( !debugQt )
60 return;
61 msgString = "QtDebugMsg: " + QString(msg);
62 break;
63 case QtWarningMsg:
64 msgString = "QtWarningMsg: " + QString(msg);
65 break;
66 case QtCriticalMsg:
67 msgString = "QtCriticalMsg: " + QString(msg);
68 break;
69 case QtFatalMsg:
70 msgString = "QtFatalMsg: " + QString(msg);
71 break;
72 default:
73 return;
74 }
75
76 QMC2_ARCADE_LOG_STR(msgString);
77 }
78
showHelp()79 void showHelp()
80 {
81 #if defined(QMC2_ARCADE_OS_WIN)
82 if ( !consoleWindow )
83 winAllocConsole();
84 #endif
85
86 QString defTheme(globalConfig->defaultTheme());
87 QString defConsole(globalConfig->defaultConsoleType());
88 #if QT_VERSION < 0x050000
89 QString defGSys(globalConfig->defaultGraphicsSystem());
90 #endif
91 QString defLang(globalConfig->defaultLanguage());
92 QString defVideo(globalConfig->defaultVideo());
93
94 QStringList themeList;
95 foreach (QString theme, arcadeThemes) {
96 if ( defTheme == theme )
97 themeList << "[" + theme + "]";
98 else
99 themeList << theme;
100 }
101 QString availableThemes(themeList.join(", "));
102
103 QStringList consoleList;
104 foreach (QString console, consoleModes) {
105 if ( defConsole == console )
106 consoleList << "[" + console + "]";
107 else
108 consoleList << console;
109 }
110 QString availableConsoles(consoleList.join(", "));
111
112 #if QT_VERSION < 0x050000
113 QStringList gSysList;
114 foreach (QString gSys, graphicsSystems) {
115 if ( defGSys == gSys )
116 gSysList << "[" + gSys + "]";
117 else
118 gSysList << gSys;
119 }
120 QString availableGraphicsSystems(gSysList.join(", "));
121 #endif
122
123 QStringList langList;
124 foreach (QString lang, globalConfig->languageMap.keys()) {
125 if ( defLang == lang )
126 langList << "[" + lang + "]";
127 else
128 langList << lang;
129 }
130 QString availableLanguages(langList.join(", "));
131
132 QStringList videoList;
133 foreach (QString v, QStringList() << "on" << "off") {
134 if ( defVideo == v )
135 videoList << "[" + v + "]";
136 else
137 videoList << v;
138 }
139 QString availableVideoSettings(videoList.join(", "));
140
141 QString helpMessage;
142 #if QT_VERSION < 0x050000
143 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
144 helpMessage = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-graphicssystem <engine>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-nojoy] [-joy <index>] [-debugjoy] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
145 #else
146 helpMessage = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-graphicssystem <engine>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
147 #endif
148 #else
149 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
150 helpMessage = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-nojoy] [-joy <index>] [-debugjoy] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
151 #else
152 helpMessage = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
153 #endif
154 #endif
155 helpMessage += "Option Meaning Possible values ([..] = default)\n"
156 "--------------- --------------------- --------------------------------------------------\n";
157 helpMessage += "-theme Theme selection " + availableThemes + "\n";
158 helpMessage += "-console Console type " + availableConsoles + "\n";
159 #if QT_VERSION < 0x050000
160 helpMessage += "-graphicssystem Graphics engine " + availableGraphicsSystems + "\n";
161 #endif
162 helpMessage += "-language Language selection " + availableLanguages + "\n";
163 helpMessage += "-video Video snap support " + availableVideoSettings + "\n";
164 helpMessage += QString("-config_path Configuration path [%1], ...\n").arg(QMC2_ARCADE_DOT_PATH);
165 helpMessage += "-fullscreen Full screen display N/A\n";
166 helpMessage += "-windowed Windowed display N/A\n";
167 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
168 helpMessage += "-nojoy Disable joystick N/A\n";
169 helpMessage += QString("-joy Use given joystick SDL joystick index number [%1]\n").arg(globalConfig->joystickIndex());
170 helpMessage += "-debugjoy Debug joy-mapping N/A\n";
171 #endif
172 helpMessage += "-debugkeys Debug key-mapping N/A\n";
173 helpMessage += "-debugqt Log Qt debug messages N/A\n";
174
175 #if defined(QMC2_ARCADE_OS_WIN)
176 if ( !consoleWindow )
177 helpMessage.remove(helpMessage.length() - 1, 1);
178 #endif
179
180 QMC2_ARCADE_LOG_STR_NT(helpMessage);
181 }
182
upgradeSettings()183 void upgradeSettings()
184 {
185 /*
186 QStringList verList = globalConfig->value("Version").toString().split(".", QString::SkipEmptyParts);
187 if ( verList.count() > 1 ) {
188 int omv = verList[1].toInt();
189 int osr = globalConfig->value("SVN_Revision").toInt();
190 if ( QMC2_ARCADE_TEST_VERSION(omv, 57, osr, 6989) ) {
191 QStringList oldKeys = QStringList() << "/MAME/DatInfoDatabase/GameInfoImportFiles"
192 << "/MAME/DatInfoDatabase/GameInfoImportDates";
193 QStringList newKeys = QStringList() << "/MAME/DatInfoDatabase/MachineInfoImportFiles"
194 << "/MAME/DatInfoDatabase/MachineInfoImportDates";
195 for (int i = 0; i < oldKeys.count(); i++) {
196 QString oldKey = oldKeys[i];
197 QString newKey = newKeys[i];
198 if ( globalConfig->contains(oldKey) ) {
199 globalConfig->setValue(newKey, globalConfig->value(oldKey));
200 globalConfig->remove(oldKey);
201 }
202 }
203 }
204 }
205 */
206 }
207
208 #if defined(QMC2_ARCADE_OS_WIN)
209 #if defined(TCOD_VISUAL_STUDIO)
SDL_main(int argc,char * argv[])210 int SDL_main(int argc, char *argv[])
211 {
212 return main(argc, argv);
213 }
214 #endif
215 #if defined(QMC2_ARCADE_MINGW)
216 #undef main
217 #endif
218 #endif
219
main(int argc,char * argv[])220 int main(int argc, char *argv[])
221 {
222 #if defined(QMC2_ARCADE_OS_MAC)
223 // this hack ensures that we're using the bundled plugins rather than the ones from a Qt SDK installation
224 char exec_path[4096];
225 uint32_t exec_path_size = sizeof(exec_path);
226 if ( _NSGetExecutablePath(exec_path, &exec_path_size) == 0 ) {
227 QFileInfo fi(exec_path);
228 QCoreApplication::addLibraryPath(fi.absoluteDir().absolutePath() + "/../PlugIns");
229 }
230 #endif
231
232 qsrand(QDateTime::currentDateTime().toTime_t());
233 #if QT_VERSION < 0x050000
234 qInstallMsgHandler(qtMessageHandler);
235 #else
236 qInstallMessageHandler(qtMessageHandler);
237 #endif
238
239 // available emulator-modes, themes, console-modes and graphics-systems
240 emulatorModes << "mame";
241 arcadeThemes << "ToxicWaste" << "darkone";
242 mameThemes << "ToxicWaste" << "darkone";
243 consoleModes << "terminal" << "window" << "window-minimized" << "none";
244 #if QT_VERSION < 0x050000
245 graphicsSystems << "raster" << "native" << "opengl" << "opengl1" << "opengl2" << "openvg";
246 #endif
247
248 // we have to make a copy of the command line arguments since QApplication's constructor "eats"
249 // -graphicssystem and its value (and we *really* need to know if it has been set or not!)
250 for (int i = 0; i < argc; i++)
251 argumentList << argv[i];
252
253 #if QT_VERSION < 0x050000 && !defined(QMC2_ARCADE_OS_MAC)
254 // work-around for a weird style-related issue with GTK (we actually don't need to set a GUI style, but
255 // somehow Qt and/or GTK do this implicitly, which may cause crashes when the automatically chosen style
256 // isn't bug-free, so we try to fall back to a safe built-in style to avoid this)
257 QStringList wantedStyles = QStringList() << "Plastique" << "Cleanlooks" << "Fusion" << "CDE" << "Motif" << "Windows";
258 QStringList availableStyles = QStyleFactory::keys();
259 int styleIndex = -1;
260 foreach (QString style, wantedStyles) {
261 styleIndex = availableStyles.indexOf(style);
262 if ( styleIndex >= 0 )
263 break;
264 }
265 if ( styleIndex >= 0 )
266 QApplication::setStyle(availableStyles[styleIndex]);
267 QApplication *tempApp = new QApplication(argc, argv);
268 #endif
269
270 QCoreApplication::setOrganizationName(QMC2_ARCADE_ORG_NAME);
271 QCoreApplication::setOrganizationDomain(QMC2_ARCADE_ORG_DOMAIN);
272 QCoreApplication::setApplicationName(QMC2_ARCADE_APP_NAME);
273 QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, ArcadeSettings::configPath());
274
275 #if QT_VERSION < 0x050000
276 globalConfig = new ArcadeSettings;
277
278 QString gSys = globalConfig->defaultGraphicsSystem();
279 if ( QMC2_ARCADE_CLI_GSYS_VAL )
280 gSys = QMC2_ARCADE_CLI_GSYS;
281
282 delete globalConfig;
283 globalConfig = 0;
284
285 #if !defined(QMC2_ARCADE_OS_MAC)
286 delete tempApp;
287 #endif
288
289 if ( !graphicsSystems.contains(gSys) ) {
290 QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid graphics-system - available graphics-systems: %2").arg(gSys).arg(graphicsSystems.join(", ")));
291 return 1;
292 }
293
294 QApplication::setGraphicsSystem(gSys);
295
296 // create the actual application instance
297 QScopedPointer<QApplication> app(createApplication(argc, argv));
298 #else
299 // create the actual application instance
300 QGuiApplication *app = new QGuiApplication(argc, argv);
301 #endif
302
303 if ( !QMC2_ARCADE_CLI_EMU_UNK ) {
304 emulatorMode = QMC2_ARCADE_EMUMODE_MAME;
305 } else if ( !emulatorModes.contains(QMC2_ARCADE_CLI_EMU) ) {
306 QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid emulator-mode - available emulator-modes: %2").arg(QMC2_ARCADE_CLI_EMU).arg(emulatorModes.join(", ")));
307 return 1;
308 }
309
310 globalConfig = new ArcadeSettings;
311
312 QString console(globalConfig->defaultConsoleType());
313 if ( QMC2_ARCADE_CLI_CONS_VAL )
314 console = QMC2_ARCADE_CLI_CONS;
315
316 if ( console == "window" || console == "window-minimized" ) {
317 TweakedQmlApplicationViewer::consoleMode = console == "window" ? QMC2_ARCADE_CONSOLE_WIN : QMC2_ARCADE_CONSOLE_WINMIN;
318 consoleWindow = new ConsoleWindow(0);
319 if ( TweakedQmlApplicationViewer::consoleMode == QMC2_ARCADE_CONSOLE_WINMIN )
320 consoleWindow->showMinimized();
321 else
322 consoleWindow->show();
323 } else if ( !consoleModes.contains(console) ) {
324 QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid console-mode - available console-modes: %2").arg(console).arg(consoleModes.join(", ")));
325 return 1;
326 } else
327 TweakedQmlApplicationViewer::consoleMode = console == "terminal" ? QMC2_ARCADE_CONSOLE_TERM : QMC2_ARCADE_CONSOLE_NONE;
328
329 if ( QMC2_ARCADE_CLI_HELP || QMC2_ARCADE_CLI_INVALID ) {
330 showHelp();
331 if ( !consoleWindow ) {
332 delete globalConfig;
333 return 1;
334 } else
335 runApp = false;
336 }
337
338 #if defined(QMC2_ARCADE_OS_WIN)
339 if ( console == "terminal" )
340 winAllocConsole();
341 #endif
342
343 QString theme(globalConfig->defaultTheme());
344 if ( QMC2_ARCADE_CLI_THEME_VAL )
345 theme = QMC2_ARCADE_CLI_THEME;
346
347 if ( !arcadeThemes.contains(theme) && runApp ) {
348 QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not valid theme - available themes: %2").arg(theme).arg(arcadeThemes.join(", ")));
349 if ( !consoleWindow ) {
350 delete globalConfig;
351 return 1;
352 } else
353 runApp = false;
354 }
355
356 delete globalConfig;
357 globalConfig = 0;
358
359 switch ( emulatorMode ) {
360 case QMC2_ARCADE_EMUMODE_MAME:
361 default:
362 if ( !mameThemes.contains(theme) && runApp ) {
363 QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid %2 theme - available %2 themes: %3").arg(theme).arg(emulatorModes[QMC2_ARCADE_EMUMODE_MAME]).arg(mameThemes.isEmpty() ? QObject::tr("(none)") : mameThemes.join(", ")));
364 if ( !consoleWindow )
365 return 1;
366 else
367 runApp = false;
368 }
369 break;
370 }
371
372 // create final instance of the global settings object
373 globalConfig = new ArcadeSettings(theme);
374 upgradeSettings();
375 globalConfig->setApplicationVersion(QMC2_ARCADE_APP_VERSION);
376
377 // set default font
378 QString font(globalConfig->defaultFont());
379 if ( !font.isEmpty() ) {
380 QFont f;
381 f.fromString(font);
382 app->setFont(f);
383 }
384
385 // set language
386 QString language(globalConfig->defaultLanguage());
387 if ( QMC2_ARCADE_CLI_LANG_VAL )
388 language = QMC2_ARCADE_CLI_LANG;
389 if ( !globalConfig->languageMap.contains(language) ) {
390 if ( QMC2_ARCADE_CLI_LANG_VAL ) {
391 QMC2_ARCADE_LOG_STR_NT(QString("%1 is not a valid language - available languages: %2").arg(language).arg(QStringList(globalConfig->languageMap.keys()).join(", ")));
392 delete globalConfig;
393 return 1;
394 } else
395 language = "us";
396 }
397
398 // load translator
399 QTranslator qmc2ArcadeTranslator;
400 if ( qmc2ArcadeTranslator.load(QString("qmc2-arcade_%1").arg(language), ":/translations") )
401 app->installTranslator(&qmc2ArcadeTranslator);
402
403 int returnCode;
404
405 if ( runApp ) {
406 // log banner message
407 QString bannerMessage = QString("%1 %2 (%3)").
408 arg(QMC2_ARCADE_APP_TITLE).
409 #if defined(QMC2_ARCADE_SVN_REV)
410 #if QMC2_ARCADE_SVN_REV > 0
411 arg(QMC2_ARCADE_APP_VERSION + QString(", SVN r%1").arg(XSTR(QMC2_ARCADE_SVN_REV))).
412 #else
413 arg(QMC2_ARCADE_APP_VERSION).
414 #endif
415 #else
416 arg(QMC2_ARCADE_APP_VERSION).
417 #endif
418 arg(QString("Qt") + " " + qVersion() + ", " +
419 QObject::tr("emulator-mode: %1").arg(emulatorModes[emulatorMode]) + ", " +
420 QObject::tr("console-mode: %1").arg(consoleModes[TweakedQmlApplicationViewer::consoleMode]) + ", " +
421 #if QT_VERSION < 0x050000
422 QObject::tr("graphics-system: %1").arg(gSys) + ", " +
423 #endif
424 QObject::tr("language: %1").arg(language) + ", " +
425 QObject::tr("theme: %1").arg(theme));
426
427 QMC2_ARCADE_LOG_STR(bannerMessage);
428
429 if ( consoleWindow )
430 consoleWindow->loadSettings();
431
432 // debug options
433 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
434 debugJoy = QMC2_ARCADE_CLI_DEBUG_JOY;
435 #endif
436 debugKeys = QMC2_ARCADE_CLI_DEBUG_KEYS;
437 debugQt = QMC2_ARCADE_CLI_DEBUG_QT;
438
439 // set up the main QML app viewer window
440 TweakedQmlApplicationViewer *viewer = new TweakedQmlApplicationViewer();
441
442 // install our key-event filter to remap key-sequences, if applicable
443 KeyEventFilter keyEventFilter(viewer->keySequenceMap);
444 app->installEventFilter(&keyEventFilter);
445
446 #if QT_VERSION < 0x050000
447 viewer->setWindowTitle(QMC2_ARCADE_APP_TITLE + " " + QMC2_ARCADE_APP_VERSION + " [Qt " + qVersion() + "]");
448 viewer->setWindowIcon(QIcon(QLatin1String(":/images/qmc2-arcade.png")));
449 viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
450 viewer->setStyleSheet("background-color: black");
451 #else
452 viewer->setTitle(QMC2_ARCADE_APP_TITLE + " " + QMC2_ARCADE_APP_VERSION + " [Qt " + qVersion() + "]");
453 viewer->winId(); // see QTBUG-33370 QQuickView does not set icon correctly
454 viewer->setIcon(QIcon(QLatin1String(":/images/qmc2-arcade.png")));
455 viewer->setColor(QColor(0, 0, 0, 255));
456 #endif
457
458 bool initialFullScreen = globalConfig->fullScreen();
459 if ( QMC2_ARCADE_CLI_FULLSCREEN )
460 initialFullScreen = true;
461 else if ( QMC2_ARCADE_CLI_WINDOWED )
462 initialFullScreen = false;
463
464 // setup viewer params
465 viewer->setInitialFullScreen(initialFullScreen);
466 viewer->setVideoEnabled(globalConfig->defaultVideo() == "on");
467 if ( QMC2_ARCADE_CLI_VIDEO_VAL )
468 viewer->setVideoEnabled(QMC2_ARCADE_CLI_VIDEO == "on");
469
470 QMC2_ARCADE_LOG_STR(QObject::tr("Starting QML viewer using theme '%1'").arg(theme) + " (" + QObject::tr("video snaps %1").arg(viewer->videoEnabled() ? QObject::tr("enabled") : QObject::tr("disabled")) + ")");
471
472 // load theme
473 QString themeUrl;
474 #if QT_VERSION < 0x050000
475 if ( viewer->videoEnabled() )
476 themeUrl = QString("qrc:/qml/%1/1.1/%1-video.qml").arg(theme);
477 else
478 themeUrl = QString("qrc:/qml/%1/1.1/%1.qml").arg(theme);
479 #else
480 if ( viewer->videoEnabled() )
481 themeUrl = QString("qrc:/qml/%1/2.0/%1-video.qml").arg(theme);
482 else
483 themeUrl = QString("qrc:/qml/%1/2.0/%1.qml").arg(theme);
484 #endif
485 viewer->setSource(QUrl(themeUrl));
486
487 // delayed setup of the initial display mode
488 QTimer::singleShot(100, viewer, SLOT(displayInit()));
489
490 // run the event loop
491 returnCode = app->exec();
492
493 // remove the key-event filter before destroying the viewer, otherwise there's a small possibility
494 // for an exit-crash because the event-filter uses the key-sequence-map instance from the viewer
495 app->removeEventFilter(&keyEventFilter);
496 delete viewer;
497 } else {
498 if ( consoleWindow ) {
499 consoleWindow->loadSettings();
500 QString consoleMessage(QObject::tr("QML viewer not started - please close the console window to exit"));
501 QMC2_ARCADE_LOG_STR_NT(QString("-").repeated(consoleMessage.length()));
502 QMC2_ARCADE_LOG_STR_NT(consoleMessage);
503 QMC2_ARCADE_LOG_STR_NT(QString("-").repeated(consoleMessage.length()));
504 consoleWindow->showNormal();
505 consoleWindow->raise();
506 app->exec();
507 }
508 returnCode = 1;
509 }
510
511 if ( consoleWindow ) {
512 consoleWindow->saveSettings();
513 delete consoleWindow;
514 }
515
516 delete globalConfig;
517
518 return returnCode;
519 }
520