diff --git a/doomsday/libdeng2/include/de/core/app.h b/doomsday/libdeng2/include/de/core/app.h index 0cdfb94506..9ea3936bd6 100644 --- a/doomsday/libdeng2/include/de/core/app.h +++ b/doomsday/libdeng2/include/de/core/app.h @@ -25,6 +25,7 @@ #include "../CommandLine" #include "../NativePath" #include "../LogBuffer" +#include "../System" #include "../FS" #include "../Module" #include "../Config" @@ -47,7 +48,7 @@ class Archive; * * @see GuiApp, TextApp */ -class DENG2_PUBLIC App +class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange) { public: /// The object or resource that was being looked for was not found. @ingroup errors @@ -88,6 +89,24 @@ class DENG2_PUBLIC App */ void initSubsystems(SubsystemInitFlags flags = DefaultSubsystems); + /** + * Adds a system to the application. The order of systems is preserved; the + * system added last will be notified of time changes last and will receive + * input events last (if others don't eat them). + * + * @param system System. Ownership kept by caller. The caller is + * responsible for making sure the system has been + * initialized properly. + */ + void addSystem(System &system); + + /** + * Removes a system from the application. + * + * @param system System to remove. + */ + void removeSystem(System &system); + /** * Adds a native module to the set of modules that can be imported in * scripts. @@ -225,6 +244,14 @@ class DENG2_PUBLIC App */ void handleUncaughtException(String message); + /** + * Events received from the operating system should be passed here; the + * application will make sure all subsystems get a chance to process them. + */ + virtual bool processEvent(Event const &); + + void timeChanged(Clock const &); + protected: /** * Returns the native path of the directory where the application can store diff --git a/doomsday/libdeng2/src/core/app.cpp b/doomsday/libdeng2/src/core/app.cpp index 53e5e5ed51..b4bdc96ca7 100644 --- a/doomsday/libdeng2/src/core/app.cpp +++ b/doomsday/libdeng2/src/core/app.cpp @@ -57,6 +57,9 @@ DENG2_PIMPL(App), DENG2_OBSERVES(Record, Deletion) /// Primary (wall) clock. Clock clock; + /// Subsystems (not owned). + QList systems; + /// The file system. FS fs; @@ -91,6 +94,8 @@ DENG2_PIMPL(App), DENG2_OBSERVES(Record, Deletion) ~Instance() { + clock.audienceForTimeChange -= &self; + DENG2_FOR_EACH(NativeModules, i, nativeModules) { i.value()->audienceForDeletion -= this; @@ -244,6 +249,30 @@ void App::handleUncaughtException(String message) if(d->terminateFunc) d->terminateFunc(message.toUtf8().constData()); } +bool App::processEvent(Event const &ev) +{ + foreach(System *sys, d->systems) + { + if(sys->behavior() & System::ReceivesInputEvents) + { + if(sys->processEvent(ev)) + return true; + } + } + return false; +} + +void App::timeChanged(Clock const &clock) +{ + foreach(System *sys, d->systems) + { + if(sys->behavior() & System::ObservesTime) + { + sys->timeChanged(clock); + } + } +} + NativePath App::nativePluginBinaryPath() { if(!d->cachedPluginBinaryPath.isEmpty()) return d->cachedPluginBinaryPath; @@ -391,9 +420,23 @@ void App::initSubsystems(SubsystemInitFlags flags) // Update the wall clock time. d->clock.setTime(Time()); + // Now we can start observing progress of time. + d->clock.audienceForTimeChange += this; + LOG_VERBOSE("libdeng2::App %s subsystems initialized.") << Version().asText(); } +void App::addSystem(System &system) +{ + d->systems.removeAll(&system); + d->systems.append(&system); +} + +void App::removeSystem(System &system) +{ + d->systems.removeAll(&system); +} + void App::addNativeModule(String const &name, Record &module) { d->nativeModules.insert(name, &module); diff --git a/doomsday/libgui/include/de/gui/guiapp.h b/doomsday/libgui/include/de/gui/guiapp.h index c62079c10c..1b5b84bb11 100644 --- a/doomsday/libgui/include/de/gui/guiapp.h +++ b/doomsday/libgui/include/de/gui/guiapp.h @@ -68,6 +68,9 @@ class LIBGUI_PUBLIC GuiApp : public QApplication, public App /// Emitted when the display mode has changed. void displayModeChanged(); +protected slots: + void refresh(); + private: struct Instance; Instance *d; diff --git a/doomsday/libgui/src/guiapp.cpp b/doomsday/libgui/src/guiapp.cpp index af81484a28..9c1cfd4f18 100644 --- a/doomsday/libgui/src/guiapp.cpp +++ b/doomsday/libgui/src/guiapp.cpp @@ -18,19 +18,28 @@ #include "de/GuiApp" #include +#include +#include #include namespace de { -struct GuiApp::Instance +DENG2_PIMPL(GuiApp) { + TimeDelta refreshInterval; + bool loopRunning; + + Instance(Public *i) : Base(*i), + refreshInterval(0), + loopRunning(false) + {} }; GuiApp::GuiApp(int &argc, char **argv) : QApplication(argc, argv), App(applicationFilePath(), arguments()), - d(new Instance) + d(new Instance(this)) {} GuiApp::~GuiApp() @@ -62,14 +71,28 @@ void GuiApp::notifyDisplayModeChanged() int GuiApp::execLoop() { + d->loopRunning = true; return QApplication::exec(); } void GuiApp::stopLoop(int code) { + d->loopRunning = false; return QApplication::exit(code); } +void GuiApp::refresh() +{ + if(!d->loopRunning) return; + + // Update the clock time. App listens to this clock and will inform + // subsystems in the order they've been added in. + Clock::appClock().setTime(Time()); + + // Schedule the next refresh. + QTimer::singleShot(de::max(duint64(1), d->refreshInterval.asMilliSeconds()), this, SLOT(refresh())); +} + NativePath GuiApp::appDataPath() const { return QDesktopServices::storageLocation(QDesktopServices::DataLocation); diff --git a/doomsday/tools/shell/shell-gui/src/qtguiapp.cpp b/doomsday/tools/shell/shell-gui/src/qtguiapp.cpp index 63a733ee45..5890ed2b22 100644 --- a/doomsday/tools/shell/shell-gui/src/qtguiapp.cpp +++ b/doomsday/tools/shell/shell-gui/src/qtguiapp.cpp @@ -65,4 +65,3 @@ bool QtGuiApp::notify(QObject *receiver, QEvent *event) } return false; } -