From ff0234559bb1152601780f278cc71244ddd01be1 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 21 Jun 2020 01:56:02 +0800 Subject: [PATCH] Window fullscreen mode supported --- src/kiwano/platform/Runner.cpp | 4 +- src/kiwano/platform/Runner.h | 2 + src/kiwano/platform/Window.cpp | 5 + src/kiwano/platform/Window.h | 9 +- src/kiwano/platform/win32/WindowImpl.cpp | 178 +++++++++++------- .../render/DirectX/D3D10DeviceResources.cpp | 10 +- .../render/DirectX/D3D11DeviceResources.cpp | 10 +- .../render/DirectX/D3DDeviceResourcesBase.h | 2 +- src/kiwano/render/DirectX/RendererImpl.cpp | 9 +- 9 files changed, 136 insertions(+), 93 deletions(-) diff --git a/src/kiwano/platform/Runner.cpp b/src/kiwano/platform/Runner.cpp index 175a0c07..90099f3a 100644 --- a/src/kiwano/platform/Runner.cpp +++ b/src/kiwano/platform/Runner.cpp @@ -82,8 +82,8 @@ void Runner::InitSettings() } // Create game window - WindowPtr window = - Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon, settings_.resizable); + WindowPtr window = Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon, + settings_.resizable, settings_.fullscreen); SetWindow(window); // Update renderer settings diff --git a/src/kiwano/platform/Runner.h b/src/kiwano/platform/Runner.h index 20c0c4b7..68743408 100644 --- a/src/kiwano/platform/Runner.h +++ b/src/kiwano/platform/Runner.h @@ -45,6 +45,7 @@ struct Settings String title; ///< 窗口标题 uint32_t icon; ///< 窗口图标 bool resizable; ///< 窗口大小可调整 + bool fullscreen; ///< 窗口全屏 Color bg_color; ///< 窗口背景色 Duration frame_interval; ///< 帧间隔 bool vsync_enabled; ///< 垂直同步 @@ -56,6 +57,7 @@ struct Settings , title("Kiwano") , icon() , resizable(false) + , fullscreen(false) , bg_color(Color::Black) , frame_interval(16) , vsync_enabled(false) diff --git a/src/kiwano/platform/Window.cpp b/src/kiwano/platform/Window.cpp index e92b7212..3501ab6c 100644 --- a/src/kiwano/platform/Window.cpp +++ b/src/kiwano/platform/Window.cpp @@ -71,6 +71,11 @@ uint32_t Window::GetHeight() const return height_; } +Resolution Window::GetCurrentResolution() const +{ + return resolution_; +} + WindowHandle Window::GetHandle() const { return handle_; diff --git a/src/kiwano/platform/Window.h b/src/kiwano/platform/Window.h index 08b9d25a..5a6c89b6 100644 --- a/src/kiwano/platform/Window.h +++ b/src/kiwano/platform/Window.h @@ -80,7 +80,7 @@ public: * @throw kiwano::SystemError 窗口创建失败时抛出 */ static WindowPtr Create(const String& title, uint32_t width, uint32_t height, uint32_t icon = 0, - bool resizable = false); + bool resizable = false, bool fullscreen = false); /** * \~chinese @@ -110,6 +110,12 @@ public: */ uint32_t GetHeight() const; + /** + * \~chinese + * @brief 获取当前分辨率 + */ + Resolution GetCurrentResolution() const; + /** * \~chinese * @brief 获取窗口句柄 @@ -214,6 +220,7 @@ protected: uint32_t min_height_; uint32_t max_width_; uint32_t max_height_; + Resolution resolution_; WindowHandle handle_; String title_; std::queue event_queue_; diff --git a/src/kiwano/platform/win32/WindowImpl.cpp b/src/kiwano/platform/win32/WindowImpl.cpp index 6cba0482..21634135 100644 --- a/src/kiwano/platform/win32/WindowImpl.cpp +++ b/src/kiwano/platform/win32/WindowImpl.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include // GET_X_LPARAM, GET_Y_LPARAM #include // ImmAssociateContext #pragma comment(lib, "imm32.lib") @@ -49,7 +48,7 @@ public: virtual ~WindowWin32Impl(); - void Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable); + void Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, bool fullscreen); void SetTitle(const String& title) override; @@ -69,6 +68,8 @@ public: DWORD GetStyle() const; + void SetActive(bool active); + void UpdateCursor(); LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM); @@ -86,12 +87,13 @@ private: std::array key_map_; }; -WindowPtr Window::Create(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable) +WindowPtr Window::Create(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, + bool fullscreen) { WindowWin32ImplPtr ptr = memory::New(); if (ptr) { - ptr->Init(title, width, height, icon, resizable); + ptr->Init(title, width, height, icon, resizable, fullscreen); } return ptr; } @@ -103,6 +105,7 @@ namespace kiwano #define WINDOW_FIXED_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX #define WINDOW_RESIZABLE_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX +#define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP namespace { @@ -202,7 +205,8 @@ WindowWin32Impl::~WindowWin32Impl() ::timeEndPeriod(0); } -void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable) +void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, + bool fullscreen) { HINSTANCE hinst = GetModuleHandle(nullptr); WNDCLASSEXA wcex = { 0 }; @@ -249,10 +253,13 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, width = win_width; height = win_height; - width_ = width; - height_ = height; - resizable_ = resizable; - handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr, + width_ = width; + height_ = height; + resizable_ = resizable; + is_fullscreen_ = fullscreen; + resolution_ = Resolution{ width_, height_, 0 }; + + handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr, nullptr, hinst, nullptr); if (handle_ == nullptr) @@ -269,6 +276,19 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, ::ShowWindow(handle_, SW_SHOWNORMAL); ::UpdateWindow(handle_); + + if (is_fullscreen_) + { + MONITORINFOEXA info = GetMoniterInfoEx(handle_); + int x = (int)info.rcMonitor.left; + int y = (int)info.rcMonitor.top; + int cx = (int)(info.rcMonitor.right - info.rcMonitor.left); + int cy = (int)(info.rcMonitor.bottom - info.rcMonitor.top); + + // Top the window + ::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE); + ::ShowWindow(handle_, SW_SHOWNORMAL); + } } void WindowWin32Impl::PumpEvents() @@ -318,71 +338,78 @@ void WindowWin32Impl::SetCursor(CursorType cursor) void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen) { - auto d3d = kiwano::graphics::directx::GetD3DDeviceResources(); - - if (fullscreen) + if (is_fullscreen_ != fullscreen) { - HRESULT hr = d3d->ResizeTarget(width, height); - KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!"); + is_fullscreen_ = fullscreen; - hr = d3d->SetFullscreenState(fullscreen); - KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!"); + // Reset window style + ::SetWindowLongPtrA(handle_, GWL_STYLE, GetStyle()); + } + + if (is_fullscreen_) + { + MONITORINFOEXA info = GetMoniterInfoEx(handle_); + int x = (int)info.rcMonitor.left; + int y = (int)info.rcMonitor.top; + int cx = (int)(info.rcMonitor.right - info.rcMonitor.left); + int cy = (int)(info.rcMonitor.bottom - info.rcMonitor.top); + + // Top the window + ::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE); + ::ShowWindow(handle_, SW_SHOWNORMAL); } else { - HRESULT hr = d3d->SetFullscreenState(fullscreen); - KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!"); + // Adjust the rect of client area + RECT rc = { 0, 0, LONG(width), LONG(height) }; + ::AdjustWindowRect(&rc, GetStyle(), false); - hr = d3d->ResizeTarget(width, height); - KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!"); + width = uint32_t(rc.right - rc.left); + height = uint32_t(rc.bottom - rc.top); + + MONITORINFOEXA info = GetMoniterInfoEx(handle_); + uint32_t screenw = uint32_t(info.rcWork.right - info.rcWork.left); + uint32_t screenh = uint32_t(info.rcWork.bottom - info.rcWork.top); + int left = screenw > width ? ((screenw - width) / 2) : 0; + int top = screenh > height ? ((screenh - height) / 2) : 0; + + // Reset window style + ::SetWindowLongPtrA(handle_, GWL_STYLE, GetStyle()); + + // Unpin the window + ::SetWindowPos(handle_, HWND_NOTOPMOST, left, top, width, height, SWP_DRAWFRAME | SWP_FRAMECHANGED); + ::ShowWindow(handle_, SW_SHOWNORMAL); } - is_fullscreen_ = fullscreen; + resolution_ = Resolution{ width, height, 0 }; + + // Resize render target + Renderer::GetInstance().Resize(width, height); } Vector WindowWin32Impl::GetResolutions() { if (resolutions_.empty()) { - auto d3d = kiwano::graphics::directx::GetD3DDeviceResources(); + Set resolution_list; - DXGI_MODE_DESC* mode_descs = nullptr; - int mode_num = 0; + DEVMODEA dmi; + ZeroMemory(&dmi, sizeof(dmi)); + dmi.dmSize = sizeof(dmi); - HRESULT hr = d3d->GetDisplaySettings(&mode_descs, &mode_num); - if (SUCCEEDED(hr)) + DWORD mode_count = 0; + while (EnumDisplaySettingsA(device_name_.c_str(), mode_count++, &dmi) != 0) { - std::unique_ptr mode_list(mode_descs); + StringStream ss; + ss << dmi.dmPelsWidth << 'x' << dmi.dmPelsHeight << ':' << dmi.dmDisplayFrequency; - if (mode_list) + if (resolution_list.find(ss.str()) == resolution_list.end()) { - for (int i = 0; i < mode_num; i++) - { - Resolution res; - res.width = mode_descs[i].Width; - res.height = mode_descs[i].Height; - res.refresh_rate = 0; - - if (mode_descs[i].RefreshRate.Denominator > 0) - { - res.refresh_rate = mode_descs[i].RefreshRate.Numerator / mode_descs[i].RefreshRate.Denominator; - } - - if (!resolutions_.empty()) - { - auto& back = resolutions_.back(); - if (back.width == res.width && back.height == res.height - && back.refresh_rate == res.refresh_rate) - continue; - } - - resolutions_.push_back(res); - } + resolution_list.insert(ss.str()); + resolutions_.push_back(Resolution{ uint32_t(dmi.dmPelsWidth), uint32_t(dmi.dmPelsHeight), + uint32_t(dmi.dmDisplayFrequency) }); } - } - else - { - KGE_THROW_IF_FAILED(hr, "DXGI GetDisplaySettings failed!"); + ZeroMemory(&dmi, sizeof(dmi)); } } return resolutions_; @@ -390,7 +417,30 @@ Vector WindowWin32Impl::GetResolutions() DWORD WindowWin32Impl::GetStyle() const { - return (resizable_ ? (WINDOW_RESIZABLE_STYLE) : (WINDOW_FIXED_STYLE)); + if (is_fullscreen_) + return WINDOW_FULLSCREEN_STYLE; + if (resizable_) + return WINDOW_RESIZABLE_STYLE; + return WINDOW_FIXED_STYLE; +} + +void WindowWin32Impl::SetActive(bool active) +{ + if (!handle_) + return; + + if (is_fullscreen_) + { + // Hide window when it is not active + if (active) + { + ::SetWindowPos(handle_, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + else + { + ::SetWindowPos(handle_, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + } } void WindowWin32Impl::UpdateCursor() @@ -658,8 +708,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA { if (is_fullscreen_) { - // TODO restore to fullscreen mode - // SetResolution(); + SetActive(true); } } break; @@ -668,8 +717,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA { if (is_fullscreen_) { - // TODO exit fullscreen mode - // ::ShowWindow(handle_, SW_MINIMIZE); + SetActive(false); } } break; @@ -695,18 +743,6 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA case WM_DISPLAYCHANGE: { KGE_SYS_LOG("The display resolution has changed"); - - // Check fullscreen state - auto d3d_res = graphics::directx::GetD3DDeviceResources(); - auto swap_chain = d3d_res->GetDXGISwapChain(); - if (swap_chain) - { - BOOL is_fullscreen = FALSE; - if (SUCCEEDED(swap_chain->GetFullscreenState(&is_fullscreen, nullptr))) - { - is_fullscreen_ = !!is_fullscreen; - } - } } break; diff --git a/src/kiwano/render/DirectX/D3D10DeviceResources.cpp b/src/kiwano/render/DirectX/D3D10DeviceResources.cpp index e10427db..9b388d44 100644 --- a/src/kiwano/render/DirectX/D3D10DeviceResources.cpp +++ b/src/kiwano/render/DirectX/D3D10DeviceResources.cpp @@ -68,7 +68,7 @@ inline bool SdkLayersAvailable() struct D3D10DeviceResources : public ID3D10DeviceResources { public: - HRESULT Initialize(HWND hwnd) override; + HRESULT Initialize(HWND hwnd, Size logical_size) override; HRESULT Present(bool vsync) override; @@ -138,14 +138,10 @@ D3D10DeviceResources::~D3D10DeviceResources() DiscardResources(); } -HRESULT D3D10DeviceResources::Initialize(HWND hwnd) +HRESULT D3D10DeviceResources::Initialize(HWND hwnd, Size logical_size) { - RECT rc; - ::GetClientRect(hwnd, &rc); - this->hwnd_ = hwnd; - this->logical_size_.x = float(rc.right - rc.left); - this->logical_size_.y = float(rc.bottom - rc.top); + this->logical_size_ = logical_size; HRESULT hr = this->CreateDeviceResources(); diff --git a/src/kiwano/render/DirectX/D3D11DeviceResources.cpp b/src/kiwano/render/DirectX/D3D11DeviceResources.cpp index e767c848..31e0c3f4 100644 --- a/src/kiwano/render/DirectX/D3D11DeviceResources.cpp +++ b/src/kiwano/render/DirectX/D3D11DeviceResources.cpp @@ -60,7 +60,7 @@ inline bool SdkLayersAvailable() struct D3D11DeviceResources : public ID3D11DeviceResources { public: - HRESULT Initialize(HWND hwnd) override; + HRESULT Initialize(HWND hwnd, Size logical_size) override; HRESULT Present(bool vsync) override; @@ -133,14 +133,10 @@ D3D11DeviceResources::~D3D11DeviceResources() DiscardResources(); } -HRESULT D3D11DeviceResources::Initialize(HWND hwnd) +HRESULT D3D11DeviceResources::Initialize(HWND hwnd, Size logical_size) { - RECT rc; - ::GetClientRect(hwnd, &rc); - this->hwnd_ = hwnd; - this->logical_size_.x = float(rc.right - rc.left); - this->logical_size_.y = float(rc.bottom - rc.top); + this->logical_size_ = logical_size; HRESULT hr = this->CreateDeviceResources(); diff --git a/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h b/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h index a14ab67b..75961d89 100644 --- a/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h +++ b/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h @@ -32,7 +32,7 @@ MIDL_INTERFACE("fb99fa64-d9cf-4e0e-9c75-90514797b01d") ID3DDeviceResourcesBase : public IUnknown { public: - virtual HRESULT Initialize(HWND hwnd) = 0; + virtual HRESULT Initialize(HWND hwnd, Size logical_size) = 0; virtual HRESULT Present(bool vsync) = 0; diff --git a/src/kiwano/render/DirectX/RendererImpl.cpp b/src/kiwano/render/DirectX/RendererImpl.cpp index 571f9213..2e039848 100644 --- a/src/kiwano/render/DirectX/RendererImpl.cpp +++ b/src/kiwano/render/DirectX/RendererImpl.cpp @@ -55,17 +55,18 @@ void RendererImpl::MakeContextForWindow(WindowPtr window) KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed"); - HWND target_window = window->GetHandle(); - output_size_ = window->GetSize(); + HWND target_window = window->GetHandle(); + Resolution resolution = window->GetCurrentResolution(); + HRESULT hr = target_window ? S_OK : E_FAIL; - HRESULT hr = target_window ? S_OK : E_FAIL; + output_size_ = Size{ float(resolution.width), float(resolution.height) }; // Initialize Direct3D resources if (SUCCEEDED(hr)) { auto d3d_res = graphics::directx::GetD3DDeviceResources(); - hr = d3d_res->Initialize(target_window); + hr = d3d_res->Initialize(target_window, output_size_); if (FAILED(hr)) { d3d_res->DiscardResources();