diff --git a/include/Nazara/Utility/Config.hpp b/include/Nazara/Utility/Config.hpp index ee77405be..39aa014af 100644 --- a/include/Nazara/Utility/Config.hpp +++ b/include/Nazara/Utility/Config.hpp @@ -38,9 +38,6 @@ // Lors du parsage d'une ressource, déclenche un avertissement si une erreur non-critique est repérée dans une ressource (Plus lent) #define NAZARA_UTILITY_STRICT_RESOURCE_PARSING 1 -// Fait tourner chaque fenêtre dans un thread séparé si le système le supporte -#define NAZARA_UTILITY_THREADED_WINDOW 0 - // Protège les classes des accès concurrentiels //#define NAZARA_UTILITY_THREADSAFE 1 diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 3f32945c2..b6c35c59e 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -432,12 +432,14 @@ namespace Nz enum WindowStyleFlags { - WindowStyle_None = 0x0, - WindowStyle_Fullscreen = 0x1, + WindowStyle_None = 0x0, ///< Window has no border nor titlebar. + WindowStyle_Fullscreen = 0x1, ///< At the window creation, the OS tries to set it in fullscreen. - WindowStyle_Closable = 0x2, - WindowStyle_Resizable = 0x4, - WindowStyle_Titlebar = 0x8, + WindowStyle_Closable = 0x2, ///< Allows the window to be closed by a button in the titlebar, generating a Quit event. + WindowStyle_Resizable = 0x4, ///< Allows the window to be resized by dragging its corners or by a button of the titlebar. + WindowStyle_Titlebar = 0x8, ///< Adds a titlebar to the window, this option is automatically enabled if buttons of the titlebar are enabled. + + WindowStyle_Threaded = 0x10, ///< Runs the window into a thread, allowing the application to keep updating while resizing/dragging the window. WindowStyle_Default = WindowStyle_Closable | WindowStyle_Resizable | WindowStyle_Titlebar, WindowStyle_Max = WindowStyle_Titlebar*2-1 diff --git a/include/Nazara/Utility/EventHandler.hpp b/include/Nazara/Utility/EventHandler.hpp index 2464d5e72..5bc47bdea 100644 --- a/include/Nazara/Utility/EventHandler.hpp +++ b/include/Nazara/Utility/EventHandler.hpp @@ -24,6 +24,9 @@ namespace Nz inline void Dispatch(const WindowEvent& event); + EventHandler& operator=(const EventHandler&) = delete; + EventHandler& operator=(EventHandler&&) = default; + NazaraSignal(OnEvent, const EventHandler* /*eventHandler*/, const WindowEvent& /*event*/); NazaraSignal(OnGainedFocus, const EventHandler* /*eventHandler*/); NazaraSignal(OnLostFocus, const EventHandler* /*eventHandler*/); diff --git a/include/Nazara/Utility/Window.hpp b/include/Nazara/Utility/Window.hpp index e544093c5..0f7688a81 100644 --- a/include/Nazara/Utility/Window.hpp +++ b/include/Nazara/Utility/Window.hpp @@ -10,6 +10,8 @@ #define NAZARA_WINDOW_HPP #include +#include +#include #include #include #include @@ -19,11 +21,6 @@ #include #include -#if NAZARA_UTILITY_THREADED_WINDOW -#include -#include -#endif - namespace Nz { class Cursor; @@ -114,23 +111,24 @@ namespace Nz private: void IgnoreNextMouseEvent(int mouseX, int mouseY) const; + inline void HandleEvent(const WindowEvent& event); inline void PushEvent(const WindowEvent& event); static bool Initialize(); static void Uninitialize(); std::queue m_events; - #if NAZARA_UTILITY_THREADED_WINDOW + std::vector m_pendingEvents; ConditionVariable m_eventCondition; + EventHandler m_eventHandler; Mutex m_eventMutex; Mutex m_eventConditionMutex; - bool m_waitForEvent; - #endif - EventHandler m_eventHandler; + bool m_asyncWindow; bool m_closed; bool m_closeOnQuit; bool m_eventPolling; bool m_ownsWindow; + bool m_waitForEvent; }; } diff --git a/include/Nazara/Utility/Window.inl b/include/Nazara/Utility/Window.inl index 08b23fc04..cceefebef 100644 --- a/include/Nazara/Utility/Window.inl +++ b/include/Nazara/Utility/Window.inl @@ -4,6 +4,7 @@ #include #include +#include #include namespace Nz @@ -13,11 +14,10 @@ namespace Nz */ inline Window::Window() : m_impl(nullptr), - #if NAZARA_UTILITY_THREADED_WINDOW - m_waitForEvent(false), - #endif + m_asyncWindow(false), m_closeOnQuit(true), - m_eventPolling(false) + m_eventPolling(false), + m_waitForEvent(false) { } @@ -41,16 +41,16 @@ namespace Nz inline Window::Window(Window&& window) noexcept : m_impl(window.m_impl), m_events(std::move(window.m_events)), - #if NAZARA_UTILITY_THREADED_WINDOW + m_pendingEvents(std::move(window.m_pendingEvents)), m_eventCondition(std::move(window.m_eventCondition)), + m_eventHandler(std::move(window.m_eventHandler)), m_eventMutex(std::move(window.m_eventMutex)), m_eventConditionMutex(std::move(window.m_eventConditionMutex)), - m_waitForEvent(window.m_waitForEvent), - #endif m_closed(window.m_closed), m_closeOnQuit(window.m_closeOnQuit), m_eventPolling(window.m_eventPolling), - m_ownsWindow(window.m_ownsWindow) + m_ownsWindow(window.m_ownsWindow), + m_waitForEvent(window.m_waitForEvent) { window.m_impl = nullptr; } @@ -104,12 +104,8 @@ namespace Nz return m_impl != nullptr; } - inline void Window::PushEvent(const WindowEvent& event) + inline void Window::HandleEvent(const WindowEvent& event) { - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Lock(); - #endif - if (m_eventPolling) m_events.push(event); @@ -120,17 +116,27 @@ namespace Nz if (event.type == WindowEventType_Quit && m_closeOnQuit) Close(); + } - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Unlock(); - - if (m_waitForEvent) + inline void Window::PushEvent(const WindowEvent& event) + { + if (!m_asyncWindow) + HandleEvent(event); + else { - m_eventConditionMutex.Lock(); - m_eventCondition.Signal(); - m_eventConditionMutex.Unlock(); + { + LockGuard eventLock(m_eventMutex); + + m_pendingEvents.push_back(event); + } + + if (m_waitForEvent) + { + m_eventConditionMutex.Lock(); + m_eventCondition.Signal(); + m_eventConditionMutex.Unlock(); + } } - #endif } /*! @@ -141,22 +147,21 @@ namespace Nz { Destroy(); - m_closed = window.m_closed; - m_closeOnQuit = window.m_closeOnQuit; - m_eventPolling = window.m_eventPolling; - m_impl = window.m_impl; - m_events = std::move(window.m_events); - m_ownsWindow = window.m_ownsWindow; + m_closed = window.m_closed; + m_closeOnQuit = window.m_closeOnQuit; + m_eventCondition = std::move(window.m_eventCondition); + m_eventConditionMutex = std::move(window.m_eventConditionMutex); + m_eventHandler = std::move(window.m_eventHandler); + m_eventMutex = std::move(window.m_eventMutex); + m_eventPolling = window.m_eventPolling; + m_impl = window.m_impl; + m_events = std::move(window.m_events); + m_pendingEvents = std::move(window.m_pendingEvents); + m_ownsWindow = window.m_ownsWindow; + m_waitForEvent = window.m_waitForEvent; window.m_impl = nullptr; - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventCondition = std::move(window.m_eventCondition); - m_eventMutex = std::move(window.m_eventMutex); - m_eventConditionMutex = std::move(window.m_eventConditionMutex); - m_waitForEvent = window.m_waitForEvent; - #endif - return *this; } } diff --git a/src/Nazara/Utility/Win32/WindowImpl.cpp b/src/Nazara/Utility/Win32/WindowImpl.cpp index 0c193cfaa..4a7f7eb9b 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.cpp +++ b/src/Nazara/Utility/Win32/WindowImpl.cpp @@ -83,6 +83,7 @@ namespace Nz bool WindowImpl::Create(const VideoMode& mode, const String& title, UInt32 style) { + bool async = (style & WindowStyle_Threaded) != 0; bool fullscreen = (style & WindowStyle_Fullscreen) != 0; DWORD win32Style, win32StyleEx; unsigned int x, y; @@ -147,19 +148,20 @@ namespace Nz m_callback = 0; - #if NAZARA_UTILITY_THREADED_WINDOW - Mutex mutex; - ConditionVariable condition; - m_threadActive = true; + if (async) + { + Mutex mutex; + ConditionVariable condition; + m_threadActive = true; - // On attend que la fenêtre soit créée - mutex.Lock(); - m_thread = Thread(WindowThread, &m_handle, win32StyleEx, title, win32Style, x, y, width, height, this, &mutex, &condition); - condition.Wait(&mutex); - mutex.Unlock(); - #else - m_handle = CreateWindowExW(win32StyleEx, className, title.GetWideString().data(), win32Style, x, y, width, height, nullptr, nullptr, GetModuleHandle(nullptr), this); - #endif + // On attend que la fenêtre soit créée + mutex.Lock(); + m_thread = Thread(WindowThread, &m_handle, win32StyleEx, title, win32Style, x, y, width, height, this, &mutex, &condition); + condition.Wait(&mutex); + mutex.Unlock(); + } + else + m_handle = CreateWindowExW(win32StyleEx, className, title.GetWideString().data(), win32Style, x, y, width, height, nullptr, nullptr, GetModuleHandle(nullptr), this); if (!m_handle) { @@ -175,9 +177,7 @@ namespace Nz m_eventListener = true; m_ownsWindow = true; - #if !NAZARA_UTILITY_THREADED_WINDOW m_sizemove = false; - #endif m_style = style; // Récupération de la position/taille de la fenêtre (Après sa création) @@ -203,9 +203,7 @@ namespace Nz m_eventListener = false; m_ownsWindow = false; - #if !NAZARA_UTILITY_THREADED_WINDOW m_sizemove = false; - #endif m_style = RetrieveStyle(m_handle); RECT clientRect, windowRect; @@ -222,18 +220,21 @@ namespace Nz { if (m_ownsWindow) { - #if NAZARA_UTILITY_THREADED_WINDOW - if (m_thread.IsJoinable()) + if (m_style & WindowStyle_Threaded) { - m_threadActive = false; - PostMessageW(m_handle, WM_NULL, 0, 0); // Pour réveiller le thread + if (m_thread.IsJoinable()) + { + m_threadActive = false; + PostMessageW(m_handle, WM_NULL, 0, 0); // Wake up our thread - m_thread.Join(); + m_thread.Join(); + } + } + else + { + if (m_handle) + DestroyWindow(m_handle); } - #else - if (m_handle) - DestroyWindow(m_handle); - #endif } else SetEventListener(false); @@ -280,7 +281,7 @@ namespace Nz if (titleSize == 0) return String(); - titleSize++; // Caractère nul + titleSize++; // \0 std::unique_ptr wTitle(new wchar_t[titleSize]); GetWindowTextW(m_handle, wTitle.get(), titleSize); @@ -320,7 +321,11 @@ namespace Nz if (m_ownsWindow) { if (block) + { + NazaraNotice("WaitMessage"); WaitMessage(); + NazaraNotice("~WaitMessage"); + } MSG message; while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) @@ -525,7 +530,6 @@ namespace Nz return true; // Afin que Windows ne ferme pas la fenêtre automatiquement } - #if !NAZARA_UTILITY_THREADED_WINDOW case WM_ENTERSIZEMOVE: { m_sizemove = true; @@ -536,6 +540,10 @@ namespace Nz { m_sizemove = false; + // In case of threaded window, size and move events are not blocked + if (m_style & WindowStyle_Threaded) + break; + // On vérifie ce qui a changé RECT clientRect, windowRect; GetClientRect(m_handle, &clientRect); @@ -565,7 +573,6 @@ namespace Nz m_parent->PushEvent(event); } } - #endif case WM_KEYDOWN: case WM_SYSKEYDOWN: @@ -789,10 +796,8 @@ namespace Nz case WM_MOVE: { - #if !NAZARA_UTILITY_THREADED_WINDOW - if (m_sizemove) + if (m_sizemove && (m_style & WindowStyle_Threaded) == 0) break; - #endif RECT windowRect; GetWindowRect(m_handle, &windowRect); @@ -862,27 +867,26 @@ namespace Nz case WM_SIZE: { - #if NAZARA_UTILITY_THREADED_WINDOW - if (wParam != SIZE_MINIMIZED) - #else - if (!m_sizemove && wParam != SIZE_MINIMIZED) - #endif - { - RECT rect; - GetClientRect(m_handle, &rect); + if (m_sizemove && (m_style & WindowStyle_Threaded) == 0) + break; - Vector2ui size(rect.right-rect.left, rect.bottom-rect.top); // On récupère uniquement la taille de la zone client - if (m_size == size) - break; + if (wParam == SIZE_MINIMIZED) + break; - m_size = size; + RECT rect; + GetClientRect(m_handle, &rect); - WindowEvent event; - event.type = WindowEventType_Resized; - event.size.width = size.x; - event.size.height = size.y; - m_parent->PushEvent(event); - } + Vector2ui size(rect.right-rect.left, rect.bottom-rect.top); // On récupère uniquement la taille de la zone client + if (m_size == size) + break; + + m_size = size; + + WindowEvent event; + event.type = WindowEventType_Resized; + event.size.width = size.x; + event.size.height = size.y; + m_parent->PushEvent(event); break; } @@ -1187,7 +1191,6 @@ namespace Nz return style; } - #if NAZARA_UTILITY_THREADED_WINDOW void WindowImpl::WindowThread(HWND* handle, DWORD styleEx, const String& title, DWORD style, unsigned int x, unsigned int y, unsigned int width, unsigned int height, WindowImpl* window, Mutex* mutex, ConditionVariable* condition) { HWND& winHandle = *handle; @@ -1195,15 +1198,16 @@ namespace Nz mutex->Lock(); condition->Signal(); - mutex->Unlock(); // mutex et condition sont considérés invalides à partir d'ici + mutex->Unlock(); // mutex and condition may be destroyed after this line if (!winHandle) return; + NazaraNotice("In"); while (window->m_threadActive) window->ProcessEvents(true); + NazaraNotice("Out"); DestroyWindow(winHandle); } -#endif } diff --git a/src/Nazara/Utility/Win32/WindowImpl.hpp b/src/Nazara/Utility/Win32/WindowImpl.hpp index 2e7078b32..a8a764319 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.hpp +++ b/src/Nazara/Utility/Win32/WindowImpl.hpp @@ -22,10 +22,8 @@ namespace Nz { - #if NAZARA_UTILITY_THREADED_WINDOW class ConditionVariable; class Mutex; - #endif class Window; #undef IsMinimized // Conflit avec la méthode du même nom @@ -88,9 +86,7 @@ namespace Nz static Keyboard::Key ConvertVirtualKey(WPARAM key, LPARAM flags); static LRESULT CALLBACK MessageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam); static UInt32 RetrieveStyle(HWND window); - #if NAZARA_UTILITY_THREADED_WINDOW static void WindowThread(HWND* handle, DWORD styleEx, const String& title, DWORD style, unsigned int x, unsigned int y, unsigned int width, unsigned int height, WindowImpl* window, Mutex* mutex, ConditionVariable* condition); - #endif HCURSOR m_cursor; HWND m_handle; @@ -101,21 +97,15 @@ namespace Nz Vector2i m_mousePos; Vector2i m_position; Vector2ui m_size; - #if NAZARA_UTILITY_THREADED_WINDOW Thread m_thread; - #endif Window* m_parent; bool m_eventListener; bool m_keyRepeat; bool m_mouseInside; bool m_ownsWindow; - #if !NAZARA_UTILITY_THREADED_WINDOW bool m_sizemove; - #endif bool m_smoothScrolling; - #if NAZARA_UTILITY_THREADED_WINDOW bool m_threadActive; - #endif short m_scrolling; }; } diff --git a/src/Nazara/Utility/Window.cpp b/src/Nazara/Utility/Window.cpp index 203ad7c71..b7f519ce6 100644 --- a/src/Nazara/Utility/Window.cpp +++ b/src/Nazara/Utility/Window.cpp @@ -66,6 +66,8 @@ namespace Nz else if (style & WindowStyle_Closable || style & WindowStyle_Resizable) style |= WindowStyle_Titlebar; + m_asyncWindow = (style & WindowStyle_Threaded) != 0; + std::unique_ptr impl = std::make_unique(this); if (!impl->Create(mode, title, style)) { @@ -313,11 +315,8 @@ namespace Nz } #endif - #if NAZARA_UTILITY_THREADED_WINDOW - LockGuard lock(m_eventMutex); - #else - m_impl->ProcessEvents(false); - #endif + if (!m_asyncWindow) + m_impl->ProcessEvents(false); if (!m_events.empty()) { @@ -337,9 +336,17 @@ namespace Nz NazaraAssert(m_impl, "Window not created"); NazaraUnused(block); - #if !NAZARA_UTILITY_THREADED_WINDOW - m_impl->ProcessEvents(block); - #endif + if (!m_asyncWindow) + m_impl->ProcessEvents(block); + else + { + LockGuard eventLock(m_eventMutex); + + for (const WindowEvent& event : m_pendingEvents) + PushEvent(event); + + m_pendingEvents.clear(); + } } void Window::SetCursor(WindowCursor cursor) @@ -384,25 +391,14 @@ namespace Nz } #endif - #if NAZARA_UTILITY_THREADED_WINDOW m_impl->SetEventListener(listener); if (!listener) { - // On vide la pile des évènements + // Empty the event queue LockGuard lock(m_eventMutex); while (!m_events.empty()) m_events.pop(); } - #else - if (m_ownsWindow) - { - // Inutile de transmettre l'ordre dans ce cas-là - if (!listener) - NazaraError("A non-threaded window needs to listen to events"); - } - else - m_impl->SetEventListener(listener); - #endif } void Window::SetFocus() @@ -590,22 +586,11 @@ namespace Nz } #endif - #if NAZARA_UTILITY_THREADED_WINDOW - LockGuard lock(m_eventMutex); - - if (m_events.empty()) + if (!m_asyncWindow) { - m_waitForEvent = true; - m_eventConditionMutex.Lock(); - m_eventMutex.Unlock(); - m_eventCondition.Wait(&m_eventConditionMutex); - m_eventMutex.Lock(); - m_eventConditionMutex.Unlock(); - m_waitForEvent = false; - } + while (m_events.empty()) + m_impl->ProcessEvents(true); - if (!m_events.empty()) - { if (event) *event = m_events.front(); @@ -613,19 +598,33 @@ namespace Nz return true; } + else + { + LockGuard lock(m_eventMutex); - return false; - #else - while (m_events.empty()) - m_impl->ProcessEvents(true); + if (m_events.empty()) + { + m_waitForEvent = true; + m_eventConditionMutex.Lock(); + m_eventMutex.Unlock(); + m_eventCondition.Wait(&m_eventConditionMutex); + m_eventMutex.Lock(); + m_eventConditionMutex.Unlock(); + m_waitForEvent = false; + } - if (event) - *event = m_events.front(); + if (!m_events.empty()) + { + if (event) + *event = m_events.front(); - m_events.pop(); + m_events.pop(); - return true; - #endif + return true; + } + + return false; + } } bool Window::OnWindowCreated() diff --git a/src/Nazara/Utility/X11/WindowImpl.cpp b/src/Nazara/Utility/X11/WindowImpl.cpp index 72b85912a..194999543 100644 --- a/src/Nazara/Utility/X11/WindowImpl.cpp +++ b/src/Nazara/Utility/X11/WindowImpl.cpp @@ -203,17 +203,18 @@ namespace Nz // Set the window's name SetTitle(title); - #if NAZARA_UTILITY_THREADED_WINDOW - Mutex mutex; - ConditionVariable condition; - m_threadActive = true; + if (m_style & WindowStyle_Threaded) + { + Mutex mutex; + ConditionVariable condition; + m_threadActive = true; - // We wait that thread is well launched - mutex.Lock(); - m_thread = Thread(WindowThread, this, &mutex, &condition); - condition.Wait(&mutex); - mutex.Unlock(); - #endif + // Wait until the thread is ready + mutex.Lock(); + m_thread = Thread(WindowThread, this, &mutex, &condition); + condition.Wait(&mutex); + mutex.Unlock(); + } // Set fullscreen video mode and switch to fullscreen if necessary if (fullscreen) @@ -275,13 +276,15 @@ namespace Nz { if (m_ownsWindow) { - #if NAZARA_UTILITY_THREADED_WINDOW - if (m_thread.IsJoinable()) + if (m_style & WindowStyle_Threaded) { - m_threadActive = false; - m_thread.Join(); + if (m_thread.IsJoinable()) + { + m_threadActive = false; + m_thread.Join(); + } } - #else + // Destroy the window if (m_window && m_ownsWindow) { @@ -293,8 +296,7 @@ namespace Nz xcb_destroy_window( connection, m_window - )) - ) + ))) NazaraError("Failed to destroy window"); xcb_flush(connection); @@ -1405,7 +1407,7 @@ namespace Nz // Catch reparent events to properly apply fullscreen on // some "strange" window managers (like Awesome) which // seem to make use of temporary parents during mapping - if (m_style & Nz::WindowStyle_Fullscreen) + if (m_style & WindowStyle_Fullscreen) SwitchToFullscreen(); break; @@ -1685,12 +1687,11 @@ namespace Nz )); } - #if NAZARA_UTILITY_THREADED_WINDOW void WindowImpl::WindowThread(WindowImpl* window, Mutex* mutex, ConditionVariable* condition) { mutex->Lock(); condition->Signal(); - mutex->Unlock(); // mutex et condition sont considérés invalides à partir d'ici + mutex->Unlock(); // mutex and condition may be destroyed after this line if (!window->m_window) return; @@ -1700,5 +1701,4 @@ namespace Nz window->Destroy(); } - #endif } diff --git a/src/Nazara/Utility/X11/WindowImpl.hpp b/src/Nazara/Utility/X11/WindowImpl.hpp index 6096dcac2..70a699421 100644 --- a/src/Nazara/Utility/X11/WindowImpl.hpp +++ b/src/Nazara/Utility/X11/WindowImpl.hpp @@ -20,10 +20,8 @@ namespace Nz { - #if NAZARA_UTILITY_THREADED_WINDOW class ConditionVariable; class Mutex; - #endif class Cursor; class Icon; class VideoMode; @@ -103,25 +101,19 @@ namespace Nz bool UpdateNormalHints(); void UpdateEventQueue(xcb_generic_event_t* event); - #if NAZARA_UTILITY_THREADED_WINDOW static void WindowThread(WindowImpl* window, Mutex* mutex, ConditionVariable* condition); - #endif xcb_window_t m_window; xcb_screen_t* m_screen; xcb_randr_get_screen_info_reply_t m_oldVideoMode; xcb_size_hints_t m_size_hints; - UInt32 m_style; - #if NAZARA_UTILITY_THREADED_WINDOW Thread m_thread; - #endif + UInt32 m_style; Window* m_parent; bool m_eventListener; bool m_ownsWindow; bool m_smoothScrolling; - #if NAZARA_UTILITY_THREADED_WINDOW bool m_threadActive; - #endif short m_scrolling; Vector2i m_mousePos; bool m_keyRepeat;