diff --git a/doomsday/dep_deng2.pri b/doomsday/dep_deng2.pri index a2edd3c1dd..4b857bc8f1 100644 --- a/doomsday/dep_deng2.pri +++ b/doomsday/dep_deng2.pri @@ -1,10 +1,11 @@ include(dep_deng2_cwrapper.pri) # libdeng2's C++ API requires the following Qt modules. -QT += core network gui +QT += core network # Optional modules: deng_qtopengl: QT += opengl + deng_qtgui: QT += gui win32 { # Install the required Qt DLLs into the products dir. @@ -13,10 +14,9 @@ win32 { else: qtver = "4" qtlibs.files += \ $$[QT_INSTALL_BINS]/QtCore$${qtver}.dll \ - $$[QT_INSTALL_BINS]/QtNetwork$${qtver}.dll \ - $$[QT_INSTALL_BINS]/QtGui$${qtver}.dll - deng_qtopengl: qtlibs.files += \ - $$[QT_INSTALL_BINS]/QtOpenGL$${qtver}.dll + $$[QT_INSTALL_BINS]/QtNetwork$${qtver}.dll + deng_qtgui: qtlibs.files += $$[QT_INSTALL_BINS]/QtGui$${qtver}.dll + deng_qtopengl: qtlibs.files += $$[QT_INSTALL_BINS]/QtOpenGL$${qtver}.dll qtlibs.path = $$DENG_LIB_DIR qtplugins.files = $$[QT_INSTALL_PLUGINS]/imageformats/qjpeg4.dll @@ -28,7 +28,7 @@ macx { fixInstallName($$1, libdeng2.2.dylib, ..) fixInstallName($$1, QtCore.framework/Versions/4/QtCore, ..) fixInstallName($$1, QtNetwork.framework/Versions/4/QtNetwork, ..) - fixInstallName($$1, QtGui.framework/Versions/4/QtGui, ..) + deng_qtgui: fixInstallName($$1, QtGui.framework/Versions/4/QtGui, ..) deng_qtopengl: fixInstallName($$1, QtOpenGL.framework/Versions/4/QtOpenGL, ..) } defineTest(linkToBundledLibdeng2) { diff --git a/doomsday/dep_gui.pri b/doomsday/dep_gui.pri new file mode 100644 index 0000000000..bbcc60f21b --- /dev/null +++ b/doomsday/dep_gui.pri @@ -0,0 +1,22 @@ +# Build configuration for using the libdeng_gui library. +INCLUDEPATH += $$PWD/libgui/include + +# Use the appropriate library path. +!useLibDir($$OUT_PWD/../libgui) { + !useLibDir($$OUT_PWD/../../libgui) { + !useLibDir($$OUT_PWD/../../../libgui) { + useLibDir($$OUT_PWD/../../builddir/libgui) + } + } +} + +LIBS += -ldeng_gui + +macx { + defineTest(linkBinaryToBundledLibdengGui) { + fixInstallName($${1}, libdeng_gui.0.dylib, ..) + } + defineTest(linkToBundledLibdengGui) { + linkBinaryToBundledLibdengGui($${1}.bundle/$$1) + } +} diff --git a/doomsday/doomsday.pro b/doomsday/doomsday.pro index ffd399f748..eb6c12d1b9 100644 --- a/doomsday/doomsday.pro +++ b/doomsday/doomsday.pro @@ -6,6 +6,7 @@ CONFIG += ordered SUBDIRS = \ build \ libdeng2 \ + libgui \ libdeng1 \ libshell \ client \ diff --git a/doomsday/libdeng1/src/concurrency.cpp b/doomsday/libdeng1/src/concurrency.cpp index aee1d38507..d476aefa41 100644 --- a/doomsday/libdeng1/src/concurrency.cpp +++ b/doomsday/libdeng1/src/concurrency.cpp @@ -24,7 +24,7 @@ #include "de/concurrency.h" #include "de/garbage.h" #include -#include +#include #include #include #include diff --git a/doomsday/libdeng2/include/de/TextApp b/doomsday/libdeng2/include/de/TextApp new file mode 100644 index 0000000000..c83d4fef82 --- /dev/null +++ b/doomsday/libdeng2/include/de/TextApp @@ -0,0 +1 @@ +#include "core/textapp.h" diff --git a/doomsday/libdeng2/include/de/c_wrapper.h b/doomsday/libdeng2/include/de/c_wrapper.h index c8769cc30e..dd2be480b4 100644 --- a/doomsday/libdeng2/include/de/c_wrapper.h +++ b/doomsday/libdeng2/include/de/c_wrapper.h @@ -66,7 +66,7 @@ typedef enum legacycore_loglevel_e { DE2_LOG_CRITICAL } legacycore_loglevel_t; -DENG2_PUBLIC LegacyCore *LegacyCore_New(void *dengApp); +DENG2_PUBLIC LegacyCore *LegacyCore_New(); DENG2_PUBLIC void LegacyCore_Delete(LegacyCore *lc); DENG2_PUBLIC LegacyCore *LegacyCore_Instance(); DENG2_PUBLIC void LegacyCore_SetLoopRate(int freqHz); @@ -82,7 +82,6 @@ DENG2_PUBLIC int LegacyCore_SetLogFile(char const *filePath); DENG2_PUBLIC char const *LegacyCore_LogFile(); DENG2_PUBLIC void LegacyCore_PrintLogFragment(char const *text); DENG2_PUBLIC void LegacyCore_PrintfLogFragmentAtLevel(legacycore_loglevel_t level, char const *format, ...); -DENG2_PUBLIC void LegacyCore_SetTerminateFunc(void (*func)(char const *)); DENG2_PUBLIC void LegacyCore_FatalError(char const *msg); /* diff --git a/doomsday/libdeng2/include/de/core/app.h b/doomsday/libdeng2/include/de/core/app.h index ba64af66f2..d0a3e30b7c 100644 --- a/doomsday/libdeng2/include/de/core/app.h +++ b/doomsday/libdeng2/include/de/core/app.h @@ -29,34 +29,30 @@ #include "../Module" #include "../Config" #include "../UnixInfo" -#include /** * Macro for conveniently accessing the de::App singleton instance. */ -#define DENG2_APP (static_cast(qApp)) +#define DENG2_APP (&de::App::app()) namespace de { class Archive; /** - * Application whose event loop is protected against uncaught exceptions. - * Catches the exception and shuts down the app cleanly. + * Represents the application and its subsystems. This is the common + * denominator (and abstract base class) for GUI and non-GUI apps. de::App is + * not usable on its own; instead you must use one of the derived variants. + * @ingroup core + * + * @see GuiApp, TextApp */ -class DENG2_PUBLIC App : public QApplication +class DENG2_PUBLIC App { - Q_OBJECT - public: /// The object or resource that was being looked for was not found. @ingroup errors DENG2_ERROR(NotFoundError); - enum GUIMode { - GUIDisabled = 0, - GUIEnabled = 1 - }; - enum SubsystemInitFlag { DefaultSubsystems = 0x0, DisablePlugins = 0x1 @@ -67,18 +63,20 @@ class DENG2_PUBLIC App : public QApplication /** * Construct an App instance. The application will not be fully usable * until initSubsystems() has been called -- you should call - * initSubsystems() as soon as possible after construction. Never throws - * an exception. + * initSubsystems() as soon as possible after construction. Never throws an + * exception. * - * @param argc Argument count. Note that App holds the reference. - * @param argv Arguments. - * @param guiMode GUI can be enabled (client) or disabled (server). + * @param appFilePath Path of the application binary. + * @param args Arguments. */ - App(int &argc, char **argv, GUIMode guiMode); + App(NativePath const &appFilePath, QStringList args); ~App(); - bool notify(QObject *receiver, QEvent *event); + /** + * Sets a callback to be called when an uncaught exception occurs. + */ + void setTerminateFunc(void (*func)(char const *msg)); /** * Finishes App construction by initializing all the application's @@ -200,19 +198,42 @@ class DENG2_PUBLIC App : public QApplication static Record &importModule(String const &name, String const &fromPath = ""); /** - * Emits the displayModeChanged() signal. + * Starts the application's main loop. * - * @todo In the future when de::App (or a sub-object owned by it) is - * responsible for display modes, this should be handled internally and - * not via this public interface where anybody can call it. + * @return Return code after the loop exits. */ - void notifyDisplayModeChanged(); + virtual int execLoop() = 0; -signals: - void uncaughtException(QString message); + /** + * Stops the application's main loop. + * + * @param code Return code from the loop. + */ + virtual void stopLoop(int code) = 0; - /// Emitted when the display mode has changed. - void displayModeChanged(); + /** + * Requests engine shutdown by calling the specified termination callback + * (see setTerminateFunc()). Called when an exception is caught at the + * de::App level, at which point there is no way to gracefully handle it + * and the application has to be shut down. + * + * This should not be called directly. From C++ code, one should throw an + * exception in unrecoverable error situations. From C code, one should + * call the LegacyCore_FatalError() function. + * + * @param message Error message to be shown to the user. + */ + void handleUncaughtException(String message); + +protected: + /** + * Returns the native path of the directory where the application can store + * user-specific data. This is usually not the same as the user's native + * home folder. + * + * @return Application data path. + */ + virtual NativePath appDataPath() const = 0; private: struct Instance; diff --git a/doomsday/libdeng2/include/de/core/log.h b/doomsday/libdeng2/include/de/core/log.h index 2a27e4ad2e..19b1e76989 100644 --- a/doomsday/libdeng2/include/de/core/log.h +++ b/doomsday/libdeng2/include/de/core/log.h @@ -325,7 +325,9 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable /** * Copy constructor. - * @param other Log entry. + * + * @param other Log entry. + * @param extraFlags Additional flags to apply to the new entry. */ LogEntry(LogEntry const &other, Flags extraFlags = 0); diff --git a/doomsday/libdeng2/include/de/core/textapp.h b/doomsday/libdeng2/include/de/core/textapp.h new file mode 100644 index 0000000000..d6c514c19e --- /dev/null +++ b/doomsday/libdeng2/include/de/core/textapp.h @@ -0,0 +1,64 @@ +/** @file textapp.h Application with text-based/console interface. + * + * @authors Copyright © 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBDENG2_TEXTAPP_H +#define LIBDENG2_TEXTAPP_H + +#include +#include + +/** + * Macro for conveniently accessing the de::TextApp singleton instance. + */ +#define DENG2_TEXT_APP (static_cast(qApp)) + +namespace de { + +/** + * Application with a text-based/console UI. + * + * The event loop is protected against uncaught exceptions. Catches the + * exception and shuts down the app cleanly. + * + * @ingroup core + */ +class DENG2_PUBLIC TextApp : public QCoreApplication, public App +{ + Q_OBJECT + +public: + TextApp(int &argc, char **argv); + + ~TextApp(); + + bool notify(QObject *receiver, QEvent *event); + + int execLoop(); + void stopLoop(int code); + +protected: + NativePath appDataPath() const; + +private: + struct Instance; + Instance *d; +}; + +} // namespace de + +#endif // LIBDENG2_TEXTAPP_H diff --git a/doomsday/libdeng2/include/de/legacy/legacycore.h b/doomsday/libdeng2/include/de/legacy/legacycore.h index 0b06eab169..0e97fa9c3e 100644 --- a/doomsday/libdeng2/include/de/legacy/legacycore.h +++ b/doomsday/libdeng2/include/de/legacy/legacycore.h @@ -21,7 +21,6 @@ #define LIBDENG2_LEGACYCORE_H #include "../libdeng2.h" -#include "../App" #include "../Log" namespace de { @@ -33,6 +32,8 @@ class LegacyNetwork; * libdeng2 functionality. The legacy engine needs to construct one of these * via the deng2 C API and make sure it gets destroyed at shutdown. The C API * can be used to access functionality in LegacyCore. + * + * @todo Move the Loop into its own class and get rid of this one. */ class DENG2_PUBLIC LegacyCore : public QObject { @@ -41,10 +42,8 @@ class DENG2_PUBLIC LegacyCore : public QObject public: /** * Initializes the legacy core. - * - * @param dengApp Application instance. */ - LegacyCore(App *dengApp); + LegacyCore(); ~LegacyCore(); @@ -133,11 +132,6 @@ class DENG2_PUBLIC LegacyCore : public QObject */ void printLogFragment(char const *text, LogEntry::Level level = LogEntry::MESSAGE); - /** - * Sets a callback to be called when an uncaught exception occurs. - */ - void setTerminateFunc(void (*func)(char const *msg)); - /** * Returns the LegacyCore singleton instance. */ @@ -151,20 +145,6 @@ class DENG2_PUBLIC LegacyCore : public QObject public slots: void callback(); - /** - * Requests engine shutdown by calling the specified termination callback - * (see setTerminateFunc()). Called when an exception is caught at the - * de::App level, at which point there is no way to gracefully handle it - * and the application has to be shut down. - * - * This should not be called directly. From C++ code, one should throw an - * exception in unrecoverable error situations. From C code, one should - * call the LegacyCore_FatalError() function. - * - * @param message Error message to be shown to the user. - */ - void handleUncaughtException(QString message); - private: // Private instance data. struct Instance; diff --git a/doomsday/libdeng2/include/de/net/beacon.h b/doomsday/libdeng2/include/de/net/beacon.h index 2a342d505c..82de25b653 100644 --- a/doomsday/libdeng2/include/de/net/beacon.h +++ b/doomsday/libdeng2/include/de/net/beacon.h @@ -67,7 +67,6 @@ class DENG2_PUBLIC Beacon : public QObject /** * Looks for any beacons on all accessible networks. * - * @param port UDP port to discover on. * @param timeOut Maximum time to spend discovering. If the timeout * is zero or negative, discovery will not end. * @param interval Interval between query broadcasts. diff --git a/doomsday/libdeng2/libdeng2.pro b/doomsday/libdeng2/libdeng2.pro index 57792422e4..877109cefd 100644 --- a/doomsday/libdeng2/libdeng2.pro +++ b/doomsday/libdeng2/libdeng2.pro @@ -27,7 +27,7 @@ DEFINES += LIBDENG2_VERSION=\\\"$${LIBDENG2_MAJOR_VERSION}.$${LIBDENG2_MINOR_VER DEFINES += __DENG2__ # Using Qt. -QT += core network gui +QT += core network win32 { # Keep the version number out of the file name. @@ -77,6 +77,7 @@ HEADERS += \ include/de/LogSink \ include/de/MonospaceLogSinkFormatter \ include/de/Rectangle \ + include/de/TextApp \ include/de/TextStreamLogSink \ include/de/UnixInfo \ include/de/Vector \ @@ -101,6 +102,7 @@ HEADERS += \ include/de/core/logbuffer.h \ include/de/core/logsink.h \ include/de/core/monospacelogsinkformatter.h \ + include/de/core/textapp.h \ include/de/core/textstreamlogsink.h \ include/de/core/unixinfo.h @@ -125,8 +127,9 @@ SOURCES += \ src/core/logbuffer.cpp \ src/core/logsink.cpp \ src/core/monospacelogsinkformatter.cpp \ + src/core/textapp.cpp \ src/core/textstreamlogsink.cpp \ - src/core/unixinfo.cpp + src/core/unixinfo.cpp OTHER_FILES += \ modules/Config.de \ @@ -143,7 +146,6 @@ macx { doPostLink("install_name_tool -id @executable_path/../Frameworks/libdeng2.2.dylib libdeng2.2.dylib") fixInstallName("QtCore.framework/Versions/4/QtCore") fixInstallName("QtNetwork.framework/Versions/4/QtNetwork") - fixInstallName("QtGui.framework/Versions/4/QtGui") # Update the library included in the main app bundle. doPostLink("mkdir -p ../client/Doomsday.app/Contents/Frameworks") diff --git a/doomsday/libdeng2/src/c_wrapper.cpp b/doomsday/libdeng2/src/c_wrapper.cpp index 0c2a28e538..1a6340d220 100644 --- a/doomsday/libdeng2/src/c_wrapper.cpp +++ b/doomsday/libdeng2/src/c_wrapper.cpp @@ -34,11 +34,11 @@ #define DENG2_LEGACYCORE() de::LegacyCore::instance() #define DENG2_LEGACYNETWORK() DENG2_LEGACYCORE().network() -#define DENG2_COMMANDLINE() static_cast(qApp)->commandLine() +#define DENG2_COMMANDLINE() DENG2_APP->commandLine() -LegacyCore *LegacyCore_New(void *dengApp) +LegacyCore *LegacyCore_New() { - return reinterpret_cast(new de::LegacyCore(reinterpret_cast(dengApp))); + return reinterpret_cast(new de::LegacyCore()); } void LegacyCore_Delete(LegacyCore *lc) @@ -147,14 +147,9 @@ void LegacyCore_PrintfLogFragmentAtLevel(legacycore_loglevel_t level, char const DENG2_LEGACYCORE().printLogFragment(buffer, logLevel); } -void LegacyCore_SetTerminateFunc(void (*func)(char const *)) -{ - DENG2_LEGACYCORE().setTerminateFunc(func); -} - void LegacyCore_FatalError(char const *msg) { - DENG2_LEGACYCORE().handleUncaughtException(msg); + DENG2_APP->handleUncaughtException(msg); } void CommandLine_Alias(char const *longname, char const *shortname) diff --git a/doomsday/libdeng2/src/core/app.cpp b/doomsday/libdeng2/src/core/app.cpp index 7586b799a1..06965218fd 100644 --- a/doomsday/libdeng2/src/core/app.cpp +++ b/doomsday/libdeng2/src/core/app.cpp @@ -35,11 +35,12 @@ #include "de/ZipArchive" #include "de/math.h" -#include #include namespace de { +static App *singletonApp; + struct App::Instance : DENG2_OBSERVES(Record, Deletion) { App &app; @@ -80,8 +81,13 @@ struct App::Instance : DENG2_OBSERVES(Record, Deletion) typedef QMap Modules; Modules modules; - Instance(App &a, QStringList args) : app(a), cmdLine(args), persistentData(0), config(0) + void (*terminateFunc)(char const *); + + Instance(App &a, QStringList args) + : app(a), cmdLine(args), persistentData(0), config(0), terminateFunc(0) { + singletonApp = &a; + Clock::setAppClock(&clock); } @@ -152,9 +158,8 @@ struct App::Instance : DENG2_OBSERVES(Record, Deletion) } }; -App::App(int &argc, char **argv, GUIMode guiMode) - : QApplication(argc, argv, guiMode == GUIEnabled), - d(new Instance(*this, arguments())) +App::App(NativePath const &appFilePath, QStringList args) + : d(new Instance(*this, args)) { // Global time source for animations. Animation::setClock(&d->clock); @@ -185,10 +190,13 @@ App::App(int &argc, char **argv, GUIMode guiMode) qWarning("%s", er.asText().toAscii().constData()); } // Aliases have not been defined at this point. - level = qMax(LogEntry::TRACE, LogEntry::Level(level - d->cmdLine.has("-verbose") - d->cmdLine.has("-v"))); + level = de::max(LogEntry::TRACE, + LogEntry::Level(level + - d->cmdLine.has("-verbose") + - d->cmdLine.has("-v"))); d->logBuffer.enable(level); - d->appPath = applicationFilePath(); + d->appPath = appFilePath; LOG_INFO("Application path: ") << d->appPath; LOG_INFO("Enabled log entry level: ") << LogEntry::levelToText(level); @@ -222,6 +230,20 @@ App::~App() LOG_AS("~App"); delete d; + + singletonApp = 0; +} + +void App::setTerminateFunc(void (*func)(char const *)) +{ + d->terminateFunc = func; +} + +void App::handleUncaughtException(String message) +{ + LOG_CRITICAL(message); + + if(d->terminateFunc) d->terminateFunc(message.toUtf8().constData()); } NativePath App::nativePluginBinaryPath() @@ -255,13 +277,13 @@ NativePath App::nativeHomePath() } #ifdef MACOSX - NativePath nativeHome = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); + NativePath nativeHome = QDir::homePath(); nativeHome = nativeHome / "Library/Application Support/Doomsday Engine/runtime"; #elif WIN32 - NativePath nativeHome = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + NativePath nativeHome = appDataPath(); nativeHome = nativeHome / "runtime"; #else // UNIX - NativePath nativeHome = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); + NativePath nativeHome = QDir::homePath(); nativeHome = nativeHome / ".doomsday/runtime"; #endif return (d->cachedHomePath = nativeHome); @@ -380,26 +402,9 @@ void App::addNativeModule(String const &name, Record &module) module.audienceForDeletion += d; } -bool App::notify(QObject *receiver, QEvent *event) -{ - try - { - return QApplication::notify(receiver, event); - } - catch(std::exception const &error) - { - emit uncaughtException(error.what()); - } - catch(...) - { - emit uncaughtException("de::App caught exception of unknown type."); - } - return false; -} - App &App::app() { - return *DENG2_APP; + return *singletonApp; } CommandLine &App::commandLine() @@ -533,9 +538,4 @@ Record &App::importModule(String const &name, String const &fromPath) throw NotFoundError("App::importModule", "Cannot find module '" + name + "'"); } -void App::notifyDisplayModeChanged() -{ - emit displayModeChanged(); -} - } // namespace de diff --git a/doomsday/libdeng2/src/core/textapp.cpp b/doomsday/libdeng2/src/core/textapp.cpp new file mode 100644 index 0000000000..43bfa03197 --- /dev/null +++ b/doomsday/libdeng2/src/core/textapp.cpp @@ -0,0 +1,72 @@ +/** @file textapp.h Application with text-based/console interface. + * + * @authors Copyright © 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/TextApp" +#include +#include + +namespace de { + +struct TextApp::Instance +{}; + +TextApp::TextApp(int &argc, char **argv) + : QCoreApplication(argc, argv), + App(applicationFilePath(), arguments()), + d(new Instance) +{} + +TextApp::~TextApp() +{ + delete d; +} + +bool TextApp::notify(QObject *receiver, QEvent *event) +{ + try + { + return QCoreApplication::notify(receiver, event); + } + catch(std::exception const &error) + { + handleUncaughtException(error.what()); + } + catch(...) + { + handleUncaughtException("de::TextApp caught exception of unknown type."); + } + return false; +} + +int TextApp::execLoop() +{ + return QCoreApplication::exec(); +} + +void TextApp::stopLoop(int code) +{ + return QCoreApplication::exit(code); +} + +NativePath TextApp::appDataPath() const +{ + return NativePath(QDir::homePath()) / ".doomsday"; +} + +} // namespace de + diff --git a/doomsday/libdeng2/src/legacy/legacycore.cpp b/doomsday/libdeng2/src/legacy/legacycore.cpp index 5bebdc4169..9a070334d0 100644 --- a/doomsday/libdeng2/src/legacy/legacycore.cpp +++ b/doomsday/libdeng2/src/legacy/legacycore.cpp @@ -20,6 +20,7 @@ #include "de/LegacyCore" #include "de/LegacyNetwork" #include "de/LogBuffer" +#include "de/App" #include "../core/callbacktimer.h" @@ -42,7 +43,6 @@ struct LegacyCore::Instance Loop() : interval(1), paused(false), func(0) {} }; QList loopStack; - void (*terminateFunc)(char const *); App *app; QTimer *loopTimer; @@ -55,19 +55,17 @@ struct LegacyCore::Instance /// Current log output line being formed. std::string currentLogLine; - Instance() : terminateFunc(0), app(0) {} + Instance() : app(0) {} ~Instance() {} }; -LegacyCore::LegacyCore(App *dengApp) +LegacyCore::LegacyCore() { _appCore = this; d = new Instance; // Construct a new core application (must have one for the event loop). - d->app = dengApp; - - connect(d->app, SIGNAL(uncaughtException(QString)), this, SLOT(handleUncaughtException(QString))); + d->app = DENG2_APP; // This will trigger loop callbacks. d->loopTimer = new QTimer(this); @@ -161,7 +159,7 @@ int LegacyCore::runEventLoop() // Run the Qt event loop. In the future this will be replaced by the // application's main Qt event loop, where deng2 will hook into. - int code = d->app->exec(); + int code = d->app->execLoop(); LOG_MSG("Event loop exited with code %i.") << code; return code; @@ -186,7 +184,7 @@ void LegacyCore::setLoopRate(int freqHz) void LegacyCore::stop(int exitCode) { d->loopTimer->stop(); - d->app->exit(exitCode); + d->app->stopLoop(exitCode); } void LegacyCore::timer(duint32 milliseconds, void (*func)(void)) @@ -220,11 +218,6 @@ void LegacyCore::printLogFragment(char const *text, LogEntry::Level level) } } -void LegacyCore::setTerminateFunc(void (*func)(char const *)) -{ - d->terminateFunc = func; -} - void LegacyCore::callback() { // Update the application clock. @@ -236,11 +229,4 @@ void LegacyCore::callback() } } -void LegacyCore::handleUncaughtException(QString message) -{ - LOG_CRITICAL(message); - - if(d->terminateFunc) d->terminateFunc(message.toUtf8().constData()); -} - } // namespace de diff --git a/doomsday/libgui/include/de/GuiApp b/doomsday/libgui/include/de/GuiApp new file mode 100644 index 0000000000..e0e8877c30 --- /dev/null +++ b/doomsday/libgui/include/de/GuiApp @@ -0,0 +1 @@ +#include "gui/guiapp.h" diff --git a/doomsday/libgui/include/de/gui/guiapp.h b/doomsday/libgui/include/de/gui/guiapp.h new file mode 100644 index 0000000000..c62079c10c --- /dev/null +++ b/doomsday/libgui/include/de/gui/guiapp.h @@ -0,0 +1,78 @@ +/** @file guiapp.h Application with GUI support. + * + * @authors Copyright © 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBGUI_GUIAPP_H +#define LIBGUI_GUIAPP_H + +#include "libgui.h" +#include +#include + +/** + * Macro for conveniently accessing the de::GuiApp singleton instance. + */ +#define DENG2_GUI_APP (static_cast(qApp)) + +namespace de { + +/** + * Application with GUI support. + * + * The event loop is protected against uncaught exceptions. Catches the + * exception and shuts down the app cleanly. + * + * @ingroup core + */ +class LIBGUI_PUBLIC GuiApp : public QApplication, public App +{ + Q_OBJECT + +public: + GuiApp(int &argc, char **argv); + + ~GuiApp(); + + bool notify(QObject *receiver, QEvent *event); + + /** + * Emits the displayModeChanged() signal. + * + * @todo In the future when de::GuiApp (or a sub-object owned by it) is + * responsible for display modes, this should be handled internally and + * not via this public interface where anybody can call it. + */ + void notifyDisplayModeChanged(); + + int execLoop(); + void stopLoop(int code); + +protected: + NativePath appDataPath() const; + +signals: + /// Emitted when the display mode has changed. + void displayModeChanged(); + +private: + struct Instance; + Instance *d; +}; + +} // namespace de + +#endif // LIBGUI_GUIAPP_H diff --git a/doomsday/libgui/include/de/gui/libgui.h b/doomsday/libgui/include/de/gui/libgui.h new file mode 100644 index 0000000000..ebbe684399 --- /dev/null +++ b/doomsday/libgui/include/de/gui/libgui.h @@ -0,0 +1,43 @@ +/** @file libgui.h Common definitions for libgui. + * + * @authors Copyright © 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBGUI_MAIN_H +#define LIBGUI_MAIN_H + +/* + * The LIBGUI_PUBLIC macro is used for declaring exported symbols. It must be + * applied in all exported classes and functions. DEF files are not used for + * exporting symbols out of libshell. + */ +#if defined(_WIN32) && defined(_MSC_VER) +# ifdef __LIBGUI__ +// This is defined when compiling the library. +# define LIBGUI_PUBLIC __declspec(dllexport) +# else +# define LIBGUI_PUBLIC __declspec(dllimport) +# endif +#else +// No need to use any special declarators. +# define LIBGUI_PUBLIC +#endif + +namespace de { + +} // namespace de + +#endif // LIBGUI_MAIN_H diff --git a/doomsday/libgui/libgui.pro b/doomsday/libgui/libgui.pro new file mode 100644 index 0000000000..5300b4f40f --- /dev/null +++ b/doomsday/libgui/libgui.pro @@ -0,0 +1,51 @@ +# The Doomsday Engine Project: GUI extension for libdeng2 +# Copyright (c) 2013 Jaakko Keränen +# +# This program is distributed under the GNU General Public License +# version 2 (or, at your option, any later version). Please visit +# http://www.gnu.org/licenses/gpl.html for details. + +include(../config.pri) + +TEMPLATE = lib +TARGET = deng_gui +VERSION = 0.1.0 + +CONFIG += deng_qtgui +include(../dep_deng2.pri) + +win32 { + # Keep the version number out of the file name. + TARGET_EXT = .dll +} + +DEFINES += __LIBGUI__ + +INCLUDEPATH += include + +# Public headers. +HEADERS += \ + include/de/GuiApp \ + \ + include/de/gui/guiapp.h \ + include/de/gui/libgui.h + +# Sources and private headers. +SOURCES += \ + src/guiapp.cpp + +# Installation --------------------------------------------------------------- + +macx { + linkDylibToBundledLibdeng2(libdeng_gui) + + doPostLink("install_name_tool -id @executable_path/../Frameworks/libdeng_gui.0.dylib libdeng_gui.0.dylib") + + # Update the library included in the main app bundle. + doPostLink("mkdir -p ../client/Doomsday.app/Contents/Frameworks") + doPostLink("cp -fRp libdeng_gui*dylib ../client/Doomsday.app/Contents/Frameworks") +} +else { + INSTALLS += target + target.path = $$DENG_LIB_DIR +} diff --git a/doomsday/libgui/src/guiapp.cpp b/doomsday/libgui/src/guiapp.cpp new file mode 100644 index 0000000000..af81484a28 --- /dev/null +++ b/doomsday/libgui/src/guiapp.cpp @@ -0,0 +1,78 @@ +/** @file guiapp.cpp Application with GUI support. + * + * @authors Copyright © 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/GuiApp" +#include + +#include + +namespace de { + +struct GuiApp::Instance +{ +}; + +GuiApp::GuiApp(int &argc, char **argv) + : QApplication(argc, argv), + App(applicationFilePath(), arguments()), + d(new Instance) +{} + +GuiApp::~GuiApp() +{ + delete d; +} + +bool GuiApp::notify(QObject *receiver, QEvent *event) +{ + try + { + return QApplication::notify(receiver, event); + } + catch(std::exception const &error) + { + handleUncaughtException(error.what()); + } + catch(...) + { + handleUncaughtException("de::GuiApp caught exception of unknown type."); + } + return false; +} + +void GuiApp::notifyDisplayModeChanged() +{ + emit displayModeChanged(); +} + +int GuiApp::execLoop() +{ + return QApplication::exec(); +} + +void GuiApp::stopLoop(int code) +{ + return QApplication::exit(code); +} + +NativePath GuiApp::appDataPath() const +{ + return QDesktopServices::storageLocation(QDesktopServices::DataLocation); +} + +} // namespace de diff --git a/doomsday/postbuild/bundleapp.sh b/doomsday/postbuild/bundleapp.sh index 2df7caa67a..13320abc07 100755 --- a/doomsday/postbuild/bundleapp.sh +++ b/doomsday/postbuild/bundleapp.sh @@ -26,6 +26,7 @@ rm -rf $BUILDDIR/*.bundle echo "Copying shared libraries..." $CP $BUILDDIR/../libdeng2/libdeng2*dylib $APPDIR/Frameworks $CP $BUILDDIR/../libdeng1/libdeng1*dylib $APPDIR/Frameworks +$CP $BUILDDIR/../libgui/libdeng_gui*dylib $APPDIR/Frameworks $CP $BUILDDIR/../libshell/libdeng_shell*dylib $APPDIR/Frameworks echo "Copying server..." diff --git a/doomsday/tests/archive/main.cpp b/doomsday/tests/archive/main.cpp index 15b31f4fbf..55a3d01ffa 100644 --- a/doomsday/tests/archive/main.cpp +++ b/doomsday/tests/archive/main.cpp @@ -17,7 +17,7 @@ * along with this program; if not, see . */ -#include +#include #include #include #include @@ -34,7 +34,7 @@ int main(int argc, char **argv) { try { - App app(argc, argv, App::GUIDisabled); + TextApp app(argc, argv); app.initSubsystems(App::DisablePlugins); Block b; diff --git a/doomsday/tests/log/main.cpp b/doomsday/tests/log/main.cpp index e48555c4e8..673c10dcbd 100644 --- a/doomsday/tests/log/main.cpp +++ b/doomsday/tests/log/main.cpp @@ -17,7 +17,7 @@ * along with this program; if not, see . */ -#include +#include #include #include @@ -28,7 +28,7 @@ int main(int argc, char **argv) { try { - App app(argc, argv, App::GUIDisabled); + TextApp app(argc, argv); app.initSubsystems(App::DisablePlugins); for(int i = 0; i < LogEntry::MAX_LOG_LEVELS; ++i) diff --git a/doomsday/tests/record/main.cpp b/doomsday/tests/record/main.cpp index 9654bdfbd8..3b629a9e46 100644 --- a/doomsday/tests/record/main.cpp +++ b/doomsday/tests/record/main.cpp @@ -17,7 +17,7 @@ * along with this program; if not, see . */ -#include +#include #include #include #include @@ -34,7 +34,7 @@ int main(int argc, char **argv) { try { - App app(argc, argv, App::GUIDisabled); + TextApp app(argc, argv); app.initSubsystems(App::DisablePlugins); Record rec; diff --git a/doomsday/tests/script/main.cpp b/doomsday/tests/script/main.cpp index 7742148c8a..7e60c3d4ca 100644 --- a/doomsday/tests/script/main.cpp +++ b/doomsday/tests/script/main.cpp @@ -17,7 +17,7 @@ * along with this program; if not, see . */ -#include +#include #include #include #include @@ -30,7 +30,7 @@ int main(int argc, char **argv) { try { - App app(argc, argv, App::GUIDisabled); + TextApp app(argc, argv); app.initSubsystems(App::DisablePlugins); Script testScript(app.fileSystem().find("kitchen_sink.de")); diff --git a/doomsday/tools/shell/shell-gui/shell-gui.pro b/doomsday/tools/shell/shell-gui/shell-gui.pro index b01d248907..a34113ad96 100644 --- a/doomsday/tools/shell/shell-gui/shell-gui.pro +++ b/doomsday/tools/shell/shell-gui/shell-gui.pro @@ -16,6 +16,8 @@ VERSION = 0.1.0 # Build Configuration ------------------------------------------------------- +CONFIG += deng_qtgui + include(../../../dep_deng2.pri) include(../../../dep_shell.pri) diff --git a/doomsday/tools/shell/shell-text/src/main.cpp b/doomsday/tools/shell/shell-text/src/main.cpp index 36b9ce2659..6497272ba5 100644 --- a/doomsday/tools/shell/shell-text/src/main.cpp +++ b/doomsday/tools/shell/shell-text/src/main.cpp @@ -16,7 +16,6 @@ * http://www.gnu.org/licenses */ -#include #include #include #include "shellapp.h"