// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Engine - Platform module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #if defined(NAZARA_PLATFORM_WINDOWS) #include #elif defined(NAZARA_PLATFORM_X11) #include #else #error Lack of implementation: Window #endif #include namespace Nz { namespace { Window* fullscreenWindow = nullptr; } Window::Window() : m_impl(nullptr), m_asyncWindow(false), m_closeOnQuit(true), m_eventPolling(false), m_waitForEvent(false) { ConnectSlots(); } Window::Window(Window&& window) : m_events(std::move(window.m_events)), m_pendingEvents(std::move(window.m_pendingEvents)), m_eventCondition(std::move(window.m_eventCondition)), m_cursorController(std::move(window.m_cursorController)), m_cursor(std::move(window.m_cursor)), m_eventHandler(std::move(window.m_eventHandler)), m_icon(std::move(window.m_icon)), m_eventMutex(std::move(window.m_eventMutex)), m_eventConditionMutex(std::move(window.m_eventConditionMutex)), m_asyncWindow(window.m_asyncWindow), m_closed(window.m_asyncWindow), m_closeOnQuit(window.m_closeOnQuit), m_eventPolling(window.m_eventPolling), m_ownsWindow(window.m_asyncWindow), m_waitForEvent(window.m_waitForEvent) { window.DisconnectSlots(); ConnectSlots(); } Window::~Window() { Destroy(); } bool Window::Create(VideoMode mode, const String& title, WindowStyleFlags style) { // If the window is already open, we keep its position bool opened = IsOpen(); Vector2i position; if (opened) position = m_impl->GetPosition(); Destroy(); // Inspired by the code of the SFML by Laurent Gomila (and its team) if (style & WindowStyle_Fullscreen) { if (fullscreenWindow) { NazaraError("Window " + String::Pointer(fullscreenWindow) + " already in fullscreen mode"); style &= ~WindowStyle_Fullscreen; } else { if (!mode.IsFullscreenValid()) { NazaraWarning("Video mode is not fullscreen valid"); mode = VideoMode::GetFullscreenModes()[0]; } fullscreenWindow = this; } } 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)) { NazaraError("Failed to create window implementation"); return false; } m_impl = impl.release(); CallOnExit destroyOnFailure([this] () { Destroy(); }); m_closed = false; m_ownsWindow = true; if (!OnWindowCreated()) { NazaraError("Failed to initialize window extension"); return false; } // Default parameters m_impl->EnableKeyRepeat(true); m_impl->EnableSmoothScrolling(false); m_impl->SetMaximumSize(-1, -1); m_impl->SetMinimumSize(-1, -1); m_impl->SetVisible(true); if (opened) m_impl->SetPosition(position.x, position.y); OnWindowResized(); destroyOnFailure.Reset(); return true; } bool Window::Create(WindowHandle handle) { Destroy(); m_asyncWindow = false; m_impl = new WindowImpl(this); if (!m_impl->Create(handle)) { NazaraError("Unable to create window implementation"); delete m_impl; m_impl = nullptr; return false; } m_closed = false; m_ownsWindow = false; if (!OnWindowCreated()) { NazaraError("Failed to initialize window's derivate"); delete m_impl; m_impl = nullptr; return false; } return true; } void Window::Destroy() { m_cursor.Reset(); if (m_impl) { OnWindowDestroy(); m_impl->Destroy(); delete m_impl; m_impl = nullptr; if (fullscreenWindow == this) fullscreenWindow = nullptr; } } void Window::EnableKeyRepeat(bool enable) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->EnableKeyRepeat(enable); } void Window::EnableSmoothScrolling(bool enable) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->EnableSmoothScrolling(enable); } WindowHandle Window::GetHandle() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return static_cast(0); } #endif return m_impl->GetHandle(); } Vector2i Window::GetPosition() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return Vector2i::Zero(); } #endif return m_impl->GetPosition(); } Vector2ui Window::GetSize() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return Vector2ui::Zero(); } #endif return m_impl->GetSize(); } WindowStyleFlags Window::GetStyle() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return 0; } #endif return m_impl->GetStyle(); } String Window::GetTitle() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return String(); } #endif return m_impl->GetTitle(); } bool Window::HasFocus() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return false; } #endif return m_impl->HasFocus(); } bool Window::IsMinimized() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return false; } #endif return m_impl->IsMinimized(); } bool Window::IsVisible() const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return false; } #endif return m_impl->IsVisible(); } bool Window::PollEvent(WindowEvent* event) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return false; } #endif if (!m_asyncWindow) m_impl->ProcessEvents(false); if (!m_events.empty()) { if (event) *event = m_events.front(); m_events.pop(); return true; } return false; } void Window::ProcessEvents(bool block) { NazaraAssert(m_impl, "Window not created"); NazaraUnused(block); if (!m_asyncWindow) m_impl->ProcessEvents(block); else { LockGuard eventLock(m_eventMutex); for (const WindowEvent& event : m_pendingEvents) HandleEvent(event); m_pendingEvents.clear(); } } void Window::SetCursor(CursorRef cursor) { NazaraAssert(m_impl, "Window not created"); NazaraAssert(cursor && cursor->IsValid(), "Invalid cursor"); m_cursor = std::move(cursor); m_impl->SetCursor(*m_cursor); } void Window::SetEventListener(bool listener) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetEventListener(listener); if (!listener) { // Empty the event queue while (!m_events.empty()) m_events.pop(); } } void Window::SetFocus() { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetFocus(); } void Window::SetIcon(IconRef icon) { NazaraAssert(m_impl, "Window not created"); NazaraAssert(icon && icon.IsValid(), "Invalid icon"); m_icon = std::move(icon); m_impl->SetIcon(*m_icon); } void Window::SetMaximumSize(const Vector2i& maxSize) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetMaximumSize(maxSize.x, maxSize.y); } void Window::SetMaximumSize(int width, int height) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetMaximumSize(width, height); } void Window::SetMinimumSize(const Vector2i& minSize) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetMinimumSize(minSize.x, minSize.y); } void Window::SetMinimumSize(int width, int height) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetMinimumSize(width, height); } void Window::SetPosition(const Vector2i& position) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetPosition(position.x, position.y); } void Window::SetPosition(int x, int y) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetPosition(x, y); } void Window::SetSize(const Vector2i& size) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetSize(size.x, size.y); } void Window::SetSize(unsigned int width, unsigned int height) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetSize(width, height); } void Window::SetStayOnTop(bool stayOnTop) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetStayOnTop(stayOnTop); } void Window::SetTitle(const String& title) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetTitle(title); } void Window::SetVisible(bool visible) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->SetVisible(visible); } bool Window::WaitEvent(WindowEvent* event) { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return false; } #endif if (!m_asyncWindow) { while (m_events.empty()) m_impl->ProcessEvents(true); if (event) *event = m_events.front(); m_events.pop(); return true; } else { LockGuard lock(m_eventMutex); 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 (!m_events.empty()) { if (event) *event = m_events.front(); m_events.pop(); return true; } return false; } } Window& Window::operator=(Window&& window) { m_events = std::move(window.m_events); m_pendingEvents = std::move(window.m_pendingEvents); m_eventCondition = std::move(window.m_eventCondition); m_cursorController = std::move(window.m_cursorController); m_cursor = std::move(window.m_cursor); m_eventHandler = std::move(window.m_eventHandler); m_icon = std::move(window.m_icon); m_eventMutex = std::move(window.m_eventMutex); m_eventConditionMutex = std::move(window.m_eventConditionMutex); m_asyncWindow = window.m_asyncWindow; m_closed = window.m_asyncWindow; m_closeOnQuit = window.m_closeOnQuit; m_eventPolling = window.m_eventPolling; m_ownsWindow = window.m_asyncWindow; m_waitForEvent = window.m_waitForEvent; window.DisconnectSlots(); ConnectSlots(); return *this; } bool Window::OnWindowCreated() { return true; } void Window::OnWindowDestroy() { } void Window::OnWindowResized() { } void Window::ConnectSlots() { m_cursorUpdateSlot.Connect(m_cursorController.OnCursorUpdated, [this](const CursorController*, const CursorRef& cursor) { if (IsValid()) SetCursor(cursor); }); } void Window::DisconnectSlots() { m_cursorUpdateSlot.Disconnect(); } void Window::IgnoreNextMouseEvent(int mouseX, int mouseY) const { #if NAZARA_PLATFORM_SAFE if (!m_impl) { NazaraError("Window not created"); return; } #endif m_impl->IgnoreNextMouseEvent(mouseX, mouseY); } void Window::HandleEvent(const WindowEvent& event) { if (m_eventPolling) m_events.push(event); m_eventHandler.Dispatch(event); switch (event.type) { case WindowEventType_MouseEntered: m_impl->RefreshCursor(); break; case WindowEventType_Resized: OnWindowResized(); break; case WindowEventType_Quit: if (m_closeOnQuit) Close(); break; default: break; } } bool Window::Initialize() { return WindowImpl::Initialize(); } void Window::Uninitialize() { WindowImpl::Uninitialize(); } }