diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index d8245b0d52..2c7dddbfcd 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -360,6 +360,7 @@ DENG_HEADERS += \ include/ui/ui_main.h \ include/ui/ui_panel.h \ include/ui/window.h \ + include/ui/windowsystem.h \ include/ui/zonedebug.h \ include/updater.h \ include/uri.hh \ @@ -636,6 +637,7 @@ SOURCES += \ src/ui/ui_main.cpp \ src/ui/ui_panel.cpp \ src/ui/window.cpp \ + src/ui/windowsystem.cpp \ src/ui/zonedebug.cpp \ src/updater/downloaddialog.cpp \ src/updater/processcheckdialog.cpp \ diff --git a/doomsday/client/include/dd_loop.h b/doomsday/client/include/dd_loop.h index 6ad298dad0..7bdf8b6d62 100644 --- a/doomsday/client/include/dd_loop.h +++ b/doomsday/client/include/dd_loop.h @@ -47,6 +47,13 @@ int DD_GameLoop(void); */ void DD_GameLoopCallback(void); +/** + * Runs one or more tics depending on how much time has passed since the + * previous call to this function. This gets called once per each main loop + * iteration. Finishes as quickly as possible. + */ +void Loop_RunTics(void); + /** * Window drawing callback. * diff --git a/doomsday/client/include/ui/canvas.h b/doomsday/client/include/ui/canvas.h index 2bf6a6b794..d092d9fe94 100644 --- a/doomsday/client/include/ui/canvas.h +++ b/doomsday/client/include/ui/canvas.h @@ -74,13 +74,6 @@ class Canvas : public QGLWidget */ void setDrawFunc(void (*canvasDrawFunc)(Canvas&)); - /** - * Sets the callback function that is called after the size of the canvas changes. - * - * @param canvasResizedFunc Callback. - */ - void setResizedFunc(void (*canvasResizedFunc)(Canvas&)); - /** * Sets the callback function that is called when the window's focus state changes. * The callback is given @c true or @c false as argument, with diff --git a/doomsday/client/include/ui/window.h b/doomsday/client/include/ui/window.h index 55322f03ff..21ecc0a32f 100644 --- a/doomsday/client/include/ui/window.h +++ b/doomsday/client/include/ui/window.h @@ -344,4 +344,15 @@ QWidget* Window_Widget(Window* wnd); CanvasWindow *Window_CanvasWindow(Window *wnd); +/** + * Utility to call after changing the size of a CanvasWindow. This will update + * the Window state. + * + * @param win Window instance. + * + * @deprecated In the future, size management will be done internally in + * CanvasWindow/WindowSystem. + */ +void Window_UpdateAfterResize(Window *win); + #endif /* LIBDENG_SYS_WINDOW_H */ diff --git a/doomsday/client/include/ui/windowsystem.h b/doomsday/client/include/ui/windowsystem.h new file mode 100644 index 0000000000..08df8ba4ef --- /dev/null +++ b/doomsday/client/include/ui/windowsystem.h @@ -0,0 +1,47 @@ +/** @file windowsystem.h Window management subsystem. + * + * @authors Copyright (c) 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 CLIENT_WINDOWSYSTEM_H +#define CLIENT_WINDOWSYSTEM_H + +#include + +/** + * Window management subsystem. + * + * The window system processes events produced by the input drivers. In + * practice, the events are passed to the widgets in the windows. + * + * @ingroup gui + */ +class WindowSystem : public de::System +{ +public: + WindowSystem(); + + ~WindowSystem(); + + bool processEvent(de::Event const &); + + void timeChanged(de::Clock const &); + +private: + DENG2_PRIVATE(d) +}; + +#endif // CLIENT_WINDOWSYSTEM_H diff --git a/doomsday/client/src/clientapp.cpp b/doomsday/client/src/clientapp.cpp index 48ee07a8ef..e6c0753df6 100644 --- a/doomsday/client/src/clientapp.cpp +++ b/doomsday/client/src/clientapp.cpp @@ -34,6 +34,7 @@ #include "con_main.h" #include "ui/displaymode.h" #include "sys_system.h" +#include "ui/windowsystem.h" #include "ui/window.h" #include "updater.h" @@ -66,6 +67,7 @@ DENG2_PIMPL(ClientApp) { LegacyCore *de2LegacyCore; QMenuBar *menuBar; + WindowSystem winSys; ServerLink *svLink; Instance(Public *i) @@ -124,6 +126,9 @@ ClientApp::ClientApp(int &argc, char **argv) QCoreApplication::setApplicationVersion (DOOMSDAY_VERSION_BASE); setTerminateFunc(handleLegacyCoreTerminate); + + // Subsystems. + addSystem(d->winSys); } ClientApp::~ClientApp() diff --git a/doomsday/client/src/dd_loop.cpp b/doomsday/client/src/dd_loop.cpp index 5d748f080e..3cec92bdf3 100644 --- a/doomsday/client/src/dd_loop.cpp +++ b/doomsday/client/src/dd_loop.cpp @@ -47,13 +47,6 @@ */ #define MAX_FRAME_TIME (1.0/MIN_TIC_RATE) -/** - * Maximum number of milliseconds spent uploading textures at the beginning - * of a frame. Note that non-uploaded textures will appear as pure white - * until their content gets uploaded (you should precache them). - */ -#define FRAME_DEFERRED_UPLOAD_TIMEOUT 20 - int maxFrameRate = 120; // Zero means 'unlimited'. // Refresh frame count (independant of the viewport-specific frameCount). int rFrameCount = 0; @@ -81,8 +74,6 @@ static int timeDeltasIndex = 0; static float realFrameTimePos = 0; -static void runTics(void); - void DD_RegisterLoop(void) { C_VAR_BYTE("input-sharp-lateprocessing", &processSharpEventsAfterTickers, 0, 0, 1); @@ -130,7 +121,7 @@ void DD_GameLoopCallback(void) LegacyCore_SetLoopRate(count || !noninteractive? 35 : 3); - runTics(); + Loop_RunTics(); // Update clients at regular intervals. Sv_TransmitFrame(); @@ -139,27 +130,18 @@ void DD_GameLoopCallback(void) #ifdef __CLIENT__ { - // Normal client-side/singleplayer mode. - //assert(!novideo); - - // We may be performing GL operations. - Window_GLActivate(Window_Main()); - - // Run at least one (fractional) tic. - runTics(); - - // We may have received a Quit message from the windowing system - // during events/tics processing. - if(Sys_IsShuttingDown()) - return; - - GL_ProcessDeferredTasks(FRAME_DEFERRED_UPLOAD_TIMEOUT); + /** + * @todo The appropriate way to update the window is to have a + * repeating GuiApp::refresh() method posting window update events + * continually. We are drawing from here because LegacyCore is still + * controlling the main loop. + */ // Request update of window contents. Window_Draw(Window_Main()); // After the first frame, start timedemo. - DD_CheckTimeDemo(); + //DD_CheckTimeDemo(); } #endif } @@ -516,12 +498,7 @@ timespan_t DD_LatestRunTicsStartTime(void) return lastRunTicsTime; } -/** - * Runs one or more tics depending on how much time has passed since the - * previous call to this function. This gets called once per each main loop - * iteration. Finishes as quickly as possible. - */ -static void runTics(void) +void Loop_RunTics(void) { double elapsedTime, ticLength, nowTime; diff --git a/doomsday/client/src/gl/gl_defer.cpp b/doomsday/client/src/gl/gl_defer.cpp index fc0dd89fd1..c0f98c1766 100644 --- a/doomsday/client/src/gl/gl_defer.cpp +++ b/doomsday/client/src/gl/gl_defer.cpp @@ -407,10 +407,7 @@ void GL_ProcessDeferredTasks(uint timeOutMilliSeconds) deferredtask_t* d; uint startTime; - if(novideo) return; - - if(!inited) - Con_Error("GL_ProcessDeferredTasks: Deferred GL task system not initialized."); + if(novideo || !inited) return; LIBDENG_ASSERT_IN_MAIN_THREAD(); LIBDENG_ASSERT_GL_CONTEXT_ACTIVE(); diff --git a/doomsday/client/src/network/net_buf.cpp b/doomsday/client/src/network/net_buf.cpp index 7f6f698a0b..8cacf15cbf 100644 --- a/doomsday/client/src/network/net_buf.cpp +++ b/doomsday/client/src/network/net_buf.cpp @@ -210,6 +210,8 @@ void N_ReleaseMessage(netmessage_t *msg) */ void N_ClearMessages(void) { + if(!msgMutex) return; // Not initialized yet. + netmessage_t *msg; float oldSim = netSimulatedLatencySeconds; diff --git a/doomsday/client/src/network/sys_network.cpp b/doomsday/client/src/network/sys_network.cpp index fb92402a6a..d52473f2e8 100644 --- a/doomsday/client/src/network/sys_network.cpp +++ b/doomsday/client/src/network/sys_network.cpp @@ -1,4 +1,4 @@ -/** @file sys_network.cpp Low-level network socket routines. +/** @file sys_network.cpp Low-level network socket routines (deprecated). * @ingroup network * * @todo Remove this source file entirely once dependent code is revised. diff --git a/doomsday/client/src/ui/canvas.cpp b/doomsday/client/src/ui/canvas.cpp index 06f4162711..ea39fd4a6a 100644 --- a/doomsday/client/src/ui/canvas.cpp +++ b/doomsday/client/src/ui/canvas.cpp @@ -69,7 +69,6 @@ struct Canvas::Instance QSize currentSize; void (*initCallback)(Canvas&); void (*drawCallback)(Canvas&); - void (*resizedCallback)(Canvas&); void (*focusCallback)(Canvas&, bool); bool cursorHidden; bool mouseGrabbed; @@ -84,7 +83,6 @@ struct Canvas::Instance : self(c), initNotified(false), initCallback(0), drawCallback(0), - resizedCallback(0), focusCallback(0), cursorHidden(false), mouseGrabbed(false) @@ -197,11 +195,6 @@ void Canvas::setDrawFunc(void (*canvasDrawFunc)(Canvas&)) d->drawCallback = canvasDrawFunc; } -void Canvas::setResizedFunc(void (*canvasResizedFunc)(Canvas&)) -{ - d->resizedCallback = canvasResizedFunc; -} - void Canvas::setFocusFunc(void (*canvasFocusChanged)(Canvas&, bool)) { d->focusCallback = canvasFocusChanged; @@ -211,7 +204,6 @@ void Canvas::useCallbacksFrom(Canvas &other) { d->drawCallback = other.d->drawCallback; d->focusCallback = other.d->focusCallback; - d->resizedCallback = other.d->resizedCallback; } QImage Canvas::grabImage(const QSize& outputSize) @@ -296,11 +288,6 @@ void Canvas::resizeGL(int w, int h) if(d->currentSize != newSize) { d->currentSize = newSize; - - if(d->resizedCallback) - { - d->resizedCallback(*this); - } } } diff --git a/doomsday/client/src/ui/canvaswindow.cpp b/doomsday/client/src/ui/canvaswindow.cpp index ddc0706daa..949d637c60 100644 --- a/doomsday/client/src/ui/canvaswindow.cpp +++ b/doomsday/client/src/ui/canvaswindow.cpp @@ -219,13 +219,17 @@ void CanvasWindow::moveEvent(QMoveEvent *ev) } } -void CanvasWindow::resizeEvent(QResizeEvent *) +void CanvasWindow::resizeEvent(QResizeEvent *ev) { + QMainWindow::resizeEvent(ev); + LOG_AS("CanvasWindow"); de::Vector2i size(width(), height()); LOG_DEBUG("Resized ") << size.asText(); + Window_UpdateAfterResize(Window_Main()); /// @todo remove this + d->rootWidget.setViewSize(size); } diff --git a/doomsday/client/src/ui/legacywidget.cpp b/doomsday/client/src/ui/legacywidget.cpp index f840460fa7..f4cc2cba36 100644 --- a/doomsday/client/src/ui/legacywidget.cpp +++ b/doomsday/client/src/ui/legacywidget.cpp @@ -22,12 +22,22 @@ #include "ui/ui2_main.h" #include "ui/busyvisual.h" #include "dd_main.h" +#include "dd_loop.h" +#include "sys_system.h" #include "map/gamemap.h" #include "network/net_main.h" #include "render/rend_list.h" #include "render/rend_console.h" #include "audio/s_main.h" #include "gl/sys_opengl.h" +#include "gl/gl_defer.h" + +/** + * Maximum number of milliseconds spent uploading textures at the beginning + * of a frame. Note that non-uploaded textures will appear as pure white + * until their content gets uploaded (you should precache them). + */ +#define FRAME_DEFERRED_UPLOAD_TIMEOUT 20 boolean drawGame = true; // If false the game viewport won't be rendered @@ -50,10 +60,44 @@ LegacyWidget::~LegacyWidget() void LegacyWidget::viewResized() { + LOG_AS("LegacyWidget"); + LOG_DEBUG("View resized to ") << root().viewSize().asText(); + + // Update viewports. + R_SetViewGrid(0, 0); + if(BusyMode_Active() || UI_IsActive() || !App_GameLoaded()) + { + // Update for busy mode. + R_UseViewPort(0); + } + R_LoadSystemFonts(); + if(UI_IsActive()) + { + UI_UpdatePageLayout(); + } } void LegacyWidget::update() -{ +{ + if(BusyMode_Active()) + { + /// @todo During busy mode, a BusyWidget should be in the widget + /// tree instead of a LegacyWidget. + return; + } + + // We may be performing GL operations. + Window_GLActivate(Window_Main()); + + // Run at least one (fractional) tic. + Loop_RunTics(); + + // We may have received a Quit message from the windowing system + // during events/tics processing. + if(Sys_IsShuttingDown()) + return; + + GL_ProcessDeferredTasks(FRAME_DEFERRED_UPLOAD_TIMEOUT); } void LegacyWidget::draw() diff --git a/doomsday/client/src/ui/window.cpp b/doomsday/client/src/ui/window.cpp index 67e4ca683d..3ec7e2f56f 100644 --- a/doomsday/client/src/ui/window.cpp +++ b/doomsday/client/src/ui/window.cpp @@ -253,7 +253,7 @@ struct ddwindow_s { geometry.size.width = DisplayMode_Current()->width; geometry.size.height = DisplayMode_Current()->height; -#if defined MACOSX && defined __CLIENT__ +#if defined MACOSX // Pull the window again over the shield after the mode change. DisplayMode_Native_Raise(Window_NativeHandle(this)); #endif @@ -472,7 +472,6 @@ struct ddwindow_s bool applyAttributes(int* attribs) { -#ifdef __CLIENT__ LOG_AS("applyAttributes"); bool changed = false; @@ -572,14 +571,11 @@ struct ddwindow_s // Seems ok, apply them. applyWindowGeometry(); -#endif // __CLIENT__ return true; } void updateLayout() { - //qDebug() << "Window::updateLayout:" << widget->geometry() << widget->canvas().geometry(); - setFlag(DDWF_MAXIMIZE, widget->isMaximized()); geometry.size.width = widget->width(); @@ -587,37 +583,24 @@ struct ddwindow_s if(!(flags & DDWF_FULLSCREEN)) { - DEBUG_Message(("Updating current view geometry for window, fetched %i x %i.\n", width(), height())); + LOG_DEBUG("Updating current view geometry for window, fetched %i x %i") + << width() << height(); if(!(flags & DDWF_MAXIMIZE) && !isBeingAdjusted()) { // Update the normal-mode geometry (not fullscreen, not maximized). normalGeometry.size.width = geometry.size.width; - DEBUG_Message(("ngw=%i [C]\n", normalGeometry.size.width)); normalGeometry.size.height = geometry.size.height; - DEBUG_Message(("Updating normal view geometry for window, fetched %i x %i.\n", width(), height())); + LOG_DEBUG("Updating normal view geometry for window, fetched %i x %i") + << width() << height(); } } else { - DEBUG_Message(("Updating view geometry for fullscreen (%i x %i).\n", width(), height())); - } - -#ifdef __CLIENT__ - // Update viewports. - R_SetViewGrid(0, 0); - if(BusyMode_Active() || UI_IsActive() || !App_GameLoaded()) - { - // Update for busy mode. - R_UseViewPort(0); + LOG_DEBUG("Updating view geometry for fullscreen (%i x %i)") + << width() << height(); } - R_LoadSystemFonts(); - if(UI_IsActive()) - { - UI_UpdatePageLayout(); - } -#endif } }; @@ -650,7 +633,7 @@ static void updateMainWindowLayout(void) if(win->flags & DDWF_FULLSCREEN) { -#if defined MACOSX && defined __CLIENT__ +#if defined MACOSX // For some interesting reason, we have to scale the window twice in fullscreen mode // or the resulting layout won't be correct. win->widget->setGeometry(QRect(0, 0, 320, 240)); @@ -1063,7 +1046,7 @@ static void finishMainWindowInit(Canvas& canvas) Window* win = canvasToWindow(canvas); assert(win == &mainWindow); -#if defined MACOSX && defined __CLIENT__ +#if defined MACOSX if(Window_IsFullscreen(win)) { // The window must be manually raised above the shielding window put up by @@ -1139,9 +1122,8 @@ static void windowWasMoved(CanvasWindow& cw) } } -static void windowWasResized(Canvas& canvas) +void Window_UpdateAfterResize(Window *win) { - Window* win = canvasToWindow(canvas); win->assertWindow(); win->updateLayout(); } @@ -1168,7 +1150,6 @@ static Window* createWindow(const char* title) mainWindow.widget->setCloseFunc(windowIsClosing); mainWindow.widget->setMoveFunc(windowWasMoved); - mainWindow.widget->canvas().setResizedFunc(windowWasResized); // Let's see if there are command line options overriding the previous state. mainWindow.modifyAccordingToOptions(); @@ -1203,7 +1184,6 @@ void Window_Delete(Window* wnd) wnd->assertWindow(); wnd->widget->canvas().setFocusFunc(0); - wnd->widget->canvas().setResizedFunc(0); // Make sure we'll remember the config. Window_SaveState(wnd); @@ -1319,8 +1299,6 @@ void Window_SetDrawFunc(Window* win, void (*drawFunc)(void)) void Window_Draw(Window* win) { -#ifdef __CLIENT__ - assert(win); assert(win->widget); @@ -1348,8 +1326,6 @@ void Window_Draw(Window* win) // Request update at the earliest convenience. win->widget->canvas().update(); } - -#endif // __CLIENT__ } void Window_Show(Window *wnd, boolean show) @@ -1555,28 +1531,16 @@ void GL_AssertContextActive(void) void Window_GLActivate(Window* wnd) { -#ifdef __CLIENT__ - wnd->assertWindow(); wnd->widget->canvas().makeCurrent(); LIBDENG_ASSERT_GL_CONTEXT_ACTIVE(); - -#else - DENG_UNUSED(wnd); -#endif } void Window_GLDone(Window* wnd) { -#ifdef __CLIENT__ - wnd->assertWindow(); wnd->widget->canvas().doneCurrent(); - -#else - DENG_UNUSED(wnd); -#endif } QWidget* Window_Widget(Window* wnd) diff --git a/doomsday/client/src/ui/windowsystem.cpp b/doomsday/client/src/ui/windowsystem.cpp new file mode 100644 index 0000000000..7a03b7239d --- /dev/null +++ b/doomsday/client/src/ui/windowsystem.cpp @@ -0,0 +1,54 @@ +/** @file windowsystem.cpp Window management subsystem. + * + * @authors Copyright (c) 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 "ui/windowsystem.h" +#include "ui/window.h" + +using namespace de; + +DENG2_PIMPL(WindowSystem) +{ + /// @todo Creation and ownership of windows belongs here (from window.cpp). + + Instance(Public *i) : Base(i) + {} +}; + +WindowSystem::WindowSystem() : System(), d(new Instance(this)) +{} + +WindowSystem::~WindowSystem() +{ + delete d; +} + +bool WindowSystem::processEvent(Event const &event) +{ + CanvasWindow *win = Window_CanvasWindow(Window_Main()); + DENG2_ASSERT(win != 0); + + return win->root().processEvent(event); +} + +void WindowSystem::timeChanged(Clock const &/*clock*/) +{ + CanvasWindow *win = Window_CanvasWindow(Window_Main()); + DENG2_ASSERT(win != 0); + + win->root().update(); +}