Window fullscreen mode supported

This commit is contained in:
Nomango 2020-06-21 01:56:02 +08:00
parent 4946246f0f
commit ff0234559b
9 changed files with 136 additions and 93 deletions

View File

@ -82,8 +82,8 @@ void Runner::InitSettings()
} }
// Create game window // Create game window
WindowPtr window = WindowPtr window = Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon,
Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon, settings_.resizable); settings_.resizable, settings_.fullscreen);
SetWindow(window); SetWindow(window);
// Update renderer settings // Update renderer settings

View File

@ -45,6 +45,7 @@ struct Settings
String title; ///< 窗口标题 String title; ///< 窗口标题
uint32_t icon; ///< 窗口图标 uint32_t icon; ///< 窗口图标
bool resizable; ///< 窗口大小可调整 bool resizable; ///< 窗口大小可调整
bool fullscreen; ///< 窗口全屏
Color bg_color; ///< 窗口背景色 Color bg_color; ///< 窗口背景色
Duration frame_interval; ///< 帧间隔 Duration frame_interval; ///< 帧间隔
bool vsync_enabled; ///< 垂直同步 bool vsync_enabled; ///< 垂直同步
@ -56,6 +57,7 @@ struct Settings
, title("Kiwano") , title("Kiwano")
, icon() , icon()
, resizable(false) , resizable(false)
, fullscreen(false)
, bg_color(Color::Black) , bg_color(Color::Black)
, frame_interval(16) , frame_interval(16)
, vsync_enabled(false) , vsync_enabled(false)

View File

@ -71,6 +71,11 @@ uint32_t Window::GetHeight() const
return height_; return height_;
} }
Resolution Window::GetCurrentResolution() const
{
return resolution_;
}
WindowHandle Window::GetHandle() const WindowHandle Window::GetHandle() const
{ {
return handle_; return handle_;

View File

@ -80,7 +80,7 @@ public:
* @throw kiwano::SystemError * @throw kiwano::SystemError
*/ */
static WindowPtr Create(const String& title, uint32_t width, uint32_t height, uint32_t icon = 0, 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 * \~chinese
@ -110,6 +110,12 @@ public:
*/ */
uint32_t GetHeight() const; uint32_t GetHeight() const;
/**
* \~chinese
* @brief
*/
Resolution GetCurrentResolution() const;
/** /**
* \~chinese * \~chinese
* @brief * @brief
@ -214,6 +220,7 @@ protected:
uint32_t min_height_; uint32_t min_height_;
uint32_t max_width_; uint32_t max_width_;
uint32_t max_height_; uint32_t max_height_;
Resolution resolution_;
WindowHandle handle_; WindowHandle handle_;
String title_; String title_;
std::queue<EventPtr> event_queue_; std::queue<EventPtr> event_queue_;

View File

@ -30,7 +30,6 @@
#include <kiwano/event/Events.h> #include <kiwano/event/Events.h>
#include <kiwano/platform/Application.h> #include <kiwano/platform/Application.h>
#include <kiwano/render/Renderer.h> #include <kiwano/render/Renderer.h>
#include <kiwano/render/DirectX/D3DDeviceResources.h>
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM #include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <imm.h> // ImmAssociateContext #include <imm.h> // ImmAssociateContext
#pragma comment(lib, "imm32.lib") #pragma comment(lib, "imm32.lib")
@ -49,7 +48,7 @@ public:
virtual ~WindowWin32Impl(); 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; void SetTitle(const String& title) override;
@ -69,6 +68,8 @@ public:
DWORD GetStyle() const; DWORD GetStyle() const;
void SetActive(bool active);
void UpdateCursor(); void UpdateCursor();
LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM); LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM);
@ -86,12 +87,13 @@ private:
std::array<KeyCode, 256> key_map_; std::array<KeyCode, 256> 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<WindowWin32Impl>(); WindowWin32ImplPtr ptr = memory::New<WindowWin32Impl>();
if (ptr) if (ptr)
{ {
ptr->Init(title, width, height, icon, resizable); ptr->Init(title, width, height, icon, resizable, fullscreen);
} }
return ptr; return ptr;
} }
@ -103,6 +105,7 @@ namespace kiwano
#define WINDOW_FIXED_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX #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_RESIZABLE_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX
#define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP
namespace namespace
{ {
@ -202,7 +205,8 @@ WindowWin32Impl::~WindowWin32Impl()
::timeEndPeriod(0); ::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); HINSTANCE hinst = GetModuleHandle(nullptr);
WNDCLASSEXA wcex = { 0 }; WNDCLASSEXA wcex = { 0 };
@ -249,10 +253,13 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height,
width = win_width; width = win_width;
height = win_height; height = win_height;
width_ = width; width_ = width;
height_ = height; height_ = height;
resizable_ = resizable; resizable_ = resizable;
handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr, is_fullscreen_ = fullscreen;
resolution_ = Resolution{ width_, height_, 0 };
handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr,
nullptr, hinst, nullptr); nullptr, hinst, nullptr);
if (handle_ == nullptr) if (handle_ == nullptr)
@ -269,6 +276,19 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height,
::ShowWindow(handle_, SW_SHOWNORMAL); ::ShowWindow(handle_, SW_SHOWNORMAL);
::UpdateWindow(handle_); ::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() void WindowWin32Impl::PumpEvents()
@ -318,71 +338,78 @@ void WindowWin32Impl::SetCursor(CursorType cursor)
void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen) void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen)
{ {
auto d3d = kiwano::graphics::directx::GetD3DDeviceResources(); if (is_fullscreen_ != fullscreen)
if (fullscreen)
{ {
HRESULT hr = d3d->ResizeTarget(width, height); is_fullscreen_ = fullscreen;
KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!");
hr = d3d->SetFullscreenState(fullscreen); // Reset window style
KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!"); ::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 else
{ {
HRESULT hr = d3d->SetFullscreenState(fullscreen); // Adjust the rect of client area
KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!"); RECT rc = { 0, 0, LONG(width), LONG(height) };
::AdjustWindowRect(&rc, GetStyle(), false);
hr = d3d->ResizeTarget(width, height); width = uint32_t(rc.right - rc.left);
KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!"); 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<Resolution> WindowWin32Impl::GetResolutions() Vector<Resolution> WindowWin32Impl::GetResolutions()
{ {
if (resolutions_.empty()) if (resolutions_.empty())
{ {
auto d3d = kiwano::graphics::directx::GetD3DDeviceResources(); Set<String> resolution_list;
DXGI_MODE_DESC* mode_descs = nullptr; DEVMODEA dmi;
int mode_num = 0; ZeroMemory(&dmi, sizeof(dmi));
dmi.dmSize = sizeof(dmi);
HRESULT hr = d3d->GetDisplaySettings(&mode_descs, &mode_num); DWORD mode_count = 0;
if (SUCCEEDED(hr)) while (EnumDisplaySettingsA(device_name_.c_str(), mode_count++, &dmi) != 0)
{ {
std::unique_ptr<DXGI_MODE_DESC[]> 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_list.insert(ss.str());
{ resolutions_.push_back(Resolution{ uint32_t(dmi.dmPelsWidth), uint32_t(dmi.dmPelsHeight),
Resolution res; uint32_t(dmi.dmDisplayFrequency) });
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);
}
} }
} ZeroMemory(&dmi, sizeof(dmi));
else
{
KGE_THROW_IF_FAILED(hr, "DXGI GetDisplaySettings failed!");
} }
} }
return resolutions_; return resolutions_;
@ -390,7 +417,30 @@ Vector<Resolution> WindowWin32Impl::GetResolutions()
DWORD WindowWin32Impl::GetStyle() const 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() void WindowWin32Impl::UpdateCursor()
@ -658,8 +708,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{ {
if (is_fullscreen_) if (is_fullscreen_)
{ {
// TODO restore to fullscreen mode SetActive(true);
// SetResolution();
} }
} }
break; break;
@ -668,8 +717,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{ {
if (is_fullscreen_) if (is_fullscreen_)
{ {
// TODO exit fullscreen mode SetActive(false);
// ::ShowWindow(handle_, SW_MINIMIZE);
} }
} }
break; break;
@ -695,18 +743,6 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_DISPLAYCHANGE: case WM_DISPLAYCHANGE:
{ {
KGE_SYS_LOG("The display resolution has changed"); 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; break;

View File

@ -68,7 +68,7 @@ inline bool SdkLayersAvailable()
struct D3D10DeviceResources : public ID3D10DeviceResources struct D3D10DeviceResources : public ID3D10DeviceResources
{ {
public: public:
HRESULT Initialize(HWND hwnd) override; HRESULT Initialize(HWND hwnd, Size logical_size) override;
HRESULT Present(bool vsync) override; HRESULT Present(bool vsync) override;
@ -138,14 +138,10 @@ D3D10DeviceResources::~D3D10DeviceResources()
DiscardResources(); DiscardResources();
} }
HRESULT D3D10DeviceResources::Initialize(HWND hwnd) HRESULT D3D10DeviceResources::Initialize(HWND hwnd, Size logical_size)
{ {
RECT rc;
::GetClientRect(hwnd, &rc);
this->hwnd_ = hwnd; this->hwnd_ = hwnd;
this->logical_size_.x = float(rc.right - rc.left); this->logical_size_ = logical_size;
this->logical_size_.y = float(rc.bottom - rc.top);
HRESULT hr = this->CreateDeviceResources(); HRESULT hr = this->CreateDeviceResources();

View File

@ -60,7 +60,7 @@ inline bool SdkLayersAvailable()
struct D3D11DeviceResources : public ID3D11DeviceResources struct D3D11DeviceResources : public ID3D11DeviceResources
{ {
public: public:
HRESULT Initialize(HWND hwnd) override; HRESULT Initialize(HWND hwnd, Size logical_size) override;
HRESULT Present(bool vsync) override; HRESULT Present(bool vsync) override;
@ -133,14 +133,10 @@ D3D11DeviceResources::~D3D11DeviceResources()
DiscardResources(); DiscardResources();
} }
HRESULT D3D11DeviceResources::Initialize(HWND hwnd) HRESULT D3D11DeviceResources::Initialize(HWND hwnd, Size logical_size)
{ {
RECT rc;
::GetClientRect(hwnd, &rc);
this->hwnd_ = hwnd; this->hwnd_ = hwnd;
this->logical_size_.x = float(rc.right - rc.left); this->logical_size_ = logical_size;
this->logical_size_.y = float(rc.bottom - rc.top);
HRESULT hr = this->CreateDeviceResources(); HRESULT hr = this->CreateDeviceResources();

View File

@ -32,7 +32,7 @@ MIDL_INTERFACE("fb99fa64-d9cf-4e0e-9c75-90514797b01d")
ID3DDeviceResourcesBase : public IUnknown ID3DDeviceResourcesBase : public IUnknown
{ {
public: public:
virtual HRESULT Initialize(HWND hwnd) = 0; virtual HRESULT Initialize(HWND hwnd, Size logical_size) = 0;
virtual HRESULT Present(bool vsync) = 0; virtual HRESULT Present(bool vsync) = 0;

View File

@ -55,17 +55,18 @@ void RendererImpl::MakeContextForWindow(WindowPtr window)
KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed"); KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed");
HWND target_window = window->GetHandle(); HWND target_window = window->GetHandle();
output_size_ = window->GetSize(); 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 // Initialize Direct3D resources
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
auto d3d_res = graphics::directx::GetD3DDeviceResources(); auto d3d_res = graphics::directx::GetD3DDeviceResources();
hr = d3d_res->Initialize(target_window); hr = d3d_res->Initialize(target_window, output_size_);
if (FAILED(hr)) if (FAILED(hr))
{ {
d3d_res->DiscardResources(); d3d_res->DiscardResources();