Magic_Game/src/kiwano/platform/win32/WindowImpl.cpp

861 lines
24 KiB
C++
Raw Normal View History

2020-01-17 11:24:24 +08:00
// Copyright (c) 2016-2020 Kiwano - Nomango
2020-01-21 10:09:55 +08:00
//
2020-01-17 11:24:24 +08:00
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
2020-01-21 10:09:55 +08:00
//
2020-01-17 11:24:24 +08:00
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
2020-01-21 10:09:55 +08:00
//
2020-01-17 11:24:24 +08:00
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
2020-02-14 22:01:56 +08:00
#include <kiwano/platform/Window.h>
2020-01-17 11:24:24 +08:00
2020-02-15 17:32:32 +08:00
#if defined(KGE_PLATFORM_WINDOWS)
2020-01-17 11:24:24 +08:00
2020-02-22 19:44:50 +08:00
#include <memory>
2020-02-15 17:32:32 +08:00
#include <array>
2020-10-11 17:10:14 +08:00
#include <kiwano/platform/Keys.h>
2020-05-24 11:26:21 +08:00
#include <kiwano/utils/Logger.h>
#include <kiwano/event/Events.h>
2020-01-21 10:09:55 +08:00
#include <kiwano/platform/Application.h>
2020-06-22 21:34:27 +08:00
#include <kiwano/platform/FileSystem.h>
2020-05-17 21:02:43 +08:00
#include <kiwano/render/Renderer.h>
2020-02-14 22:01:56 +08:00
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <imm.h> // ImmAssociateContext
2020-01-17 11:24:24 +08:00
#pragma comment(lib, "imm32.lib")
2020-05-27 16:38:58 +08:00
#include <timeapi.h> // timeBeginPeriod, timeEndPeriod
#pragma comment(lib, "winmm.lib")
2020-01-17 11:24:24 +08:00
namespace kiwano
{
2020-02-14 22:01:56 +08:00
KGE_DECLARE_SMART_PTR(WindowWin32Impl);
class KGE_API WindowWin32Impl : public Window
{
2020-02-14 22:01:56 +08:00
public:
WindowWin32Impl();
virtual ~WindowWin32Impl();
2020-06-23 16:51:27 +08:00
void Init(const WindowConfig& config);
2020-02-14 22:01:56 +08:00
2020-02-19 12:09:50 +08:00
void SetTitle(const String& title) override;
2020-02-14 22:01:56 +08:00
2020-06-22 21:38:50 +08:00
void SetIcon(Icon icon) override;
2020-02-14 22:01:56 +08:00
2020-05-20 11:55:53 +08:00
void SetMinimumSize(uint32_t width, uint32_t height) override;
2020-05-20 23:38:24 +08:00
void SetMaximumSize(uint32_t width, uint32_t height) override;
2020-02-14 22:01:56 +08:00
void SetCursor(CursorType cursor) override;
2020-05-22 21:07:32 +08:00
void SetResolution(uint32_t width, uint32_t height, bool fullscreen) override;
Vector<Resolution> GetResolutions() override;
2020-02-14 22:01:56 +08:00
void PumpEvents() override;
DWORD GetStyle() const;
2020-06-21 01:56:02 +08:00
void SetActive(bool active);
2020-02-14 22:01:56 +08:00
void UpdateCursor();
2020-02-14 22:01:56 +08:00
LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM);
static LRESULT CALLBACK StaticWndProc(HWND, UINT32, WPARAM, LPARAM);
private:
bool resizable_;
2020-08-04 00:40:07 +08:00
bool is_moving_or_resizing_;
2020-05-20 11:55:53 +08:00
bool is_minimized_;
2020-02-14 22:01:56 +08:00
CursorType mouse_cursor_;
String device_name_;
2020-02-14 22:01:56 +08:00
2020-05-22 21:07:32 +08:00
Vector<Resolution> resolutions_;
std::array<KeyCode, 256> key_map_;
2020-02-14 22:01:56 +08:00
};
2020-06-23 16:51:27 +08:00
WindowPtr Window::Create(const WindowConfig& config)
2020-01-21 10:09:55 +08:00
{
2020-07-22 21:08:48 +08:00
WindowWin32ImplPtr ptr = MakePtr<WindowWin32Impl>();
2020-02-14 22:01:56 +08:00
if (ptr)
{
2020-06-23 16:51:27 +08:00
ptr->Init(config);
2020-02-14 22:01:56 +08:00
}
return ptr;
}
2020-02-14 22:01:56 +08:00
} // namespace kiwano
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
2020-06-21 01:56:02 +08:00
#define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP
2020-02-14 22:01:56 +08:00
2020-01-21 10:09:55 +08:00
namespace
{
MONITORINFOEXA GetMoniterInfoEx(HWND hwnd)
2020-01-21 10:09:55 +08:00
{
HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
MONITORINFOEXA monitor_info;
2020-01-21 10:09:55 +08:00
2020-04-14 17:52:38 +08:00
memset(&monitor_info, 0, sizeof(monitor_info));
monitor_info.cbSize = sizeof(monitor_info);
::GetMonitorInfoA(monitor, &monitor_info);
2020-01-21 10:09:55 +08:00
return monitor_info;
}
void AdjustWindow(uint32_t width, uint32_t height, DWORD style, uint32_t* win_width, uint32_t* win_height)
{
RECT rc;
::SetRect(&rc, 0, 0, (int)width, (int)height);
::AdjustWindowRect(&rc, style, false);
2020-01-17 11:24:24 +08:00
2020-01-21 10:09:55 +08:00
*win_width = rc.right - rc.left;
*win_height = rc.bottom - rc.top;
2020-01-17 11:24:24 +08:00
MONITORINFOEXA info = GetMoniterInfoEx(NULL);
2020-01-21 10:09:55 +08:00
uint32_t screenw = info.rcWork.right - info.rcWork.left;
uint32_t screenh = info.rcWork.bottom - info.rcWork.top;
if (*win_width > screenw)
*win_width = screenw;
if (*win_height > screenh)
*win_height = screenh;
}
2020-06-23 16:51:27 +08:00
HICON Icon2HIcon(const Icon& icon)
{
HICON hicon = NULL;
if (icon.resource_id != 0 && IS_INTRESOURCE(icon.resource_id))
{
HINSTANCE hinstance = ::GetModuleHandle(nullptr);
hicon = (HICON)::LoadImage(hinstance, MAKEINTRESOURCE(icon.resource_id), IMAGE_ICON, 0, 0,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
}
else
{
String full_path = FileSystem::GetInstance().GetFullPathForFile(icon.file_path);
if (!full_path.empty())
{
hicon = (HICON)::LoadImageA(NULL, full_path.c_str(), IMAGE_ICON, 0, 0,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
}
}
return hicon;
}
2020-01-21 10:09:55 +08:00
} // namespace
2020-02-14 22:01:56 +08:00
WindowWin32Impl::WindowWin32Impl()
: resizable_(false)
2020-08-04 00:40:07 +08:00
, is_moving_or_resizing_(false)
2020-05-20 11:55:53 +08:00
, is_minimized_(false)
2020-01-21 10:09:55 +08:00
, mouse_cursor_(CursorType::Arrow)
, key_map_{}
{
// Keys
key_map_[VK_UP] = KeyCode::Up;
key_map_[VK_LEFT] = KeyCode::Left;
key_map_[VK_RIGHT] = KeyCode::Right;
key_map_[VK_DOWN] = KeyCode::Down;
key_map_[VK_RETURN] = KeyCode::Enter;
key_map_[VK_SPACE] = KeyCode::Space;
key_map_[VK_ESCAPE] = KeyCode::Esc;
key_map_[VK_CONTROL] = KeyCode::Ctrl;
key_map_[VK_SHIFT] = KeyCode::Shift;
key_map_[VK_MENU] = KeyCode::Alt;
key_map_[VK_TAB] = KeyCode::Tab;
key_map_[VK_DELETE] = KeyCode::Delete;
key_map_[VK_BACK] = KeyCode::Back;
// VK_L* and VK_R*
key_map_[VK_LCONTROL] = KeyCode::Ctrl;
key_map_[VK_RCONTROL] = KeyCode::Ctrl;
key_map_[VK_LSHIFT] = KeyCode::Shift;
key_map_[VK_RSHIFT] = KeyCode::Shift;
key_map_[VK_LMENU] = KeyCode::Alt;
key_map_[VK_RMENU] = KeyCode::Alt;
key_map_[VK_LWIN] = KeyCode::Super;
key_map_[VK_RWIN] = KeyCode::Super;
// A - Z
for (size_t i = 0, size = ('Z' - 'A'); i <= size; ++i)
key_map_['A' + i] = KeyCode(size_t(KeyCode::A) + i);
// Num 0 - 9
2020-04-14 17:08:15 +08:00
for (size_t i = 0; i < 10; ++i)
2020-01-21 10:09:55 +08:00
key_map_['0' + i] = KeyCode(size_t(KeyCode::Num0) + i);
// Numpad 0 - 9
2020-04-14 17:08:15 +08:00
for (size_t i = 0; i < 10; ++i)
2020-01-21 10:09:55 +08:00
key_map_[VK_NUMPAD0 + i] = KeyCode(size_t(KeyCode::Numpad0) + i);
// F1 - F12
for (size_t i = 0; i < 12; ++i)
key_map_[VK_F1 + i] = KeyCode(size_t(KeyCode::F1) + i);
2020-05-27 16:38:58 +08:00
::timeBeginPeriod(0);
2020-01-21 10:09:55 +08:00
}
2020-03-31 14:07:13 +08:00
WindowWin32Impl::~WindowWin32Impl()
{
if (handle_)
{
::DestroyWindow(handle_);
handle_ = nullptr;
}
2020-05-27 16:38:58 +08:00
::timeEndPeriod(0);
2020-03-31 14:07:13 +08:00
}
2020-01-21 10:09:55 +08:00
2020-06-23 16:51:27 +08:00
void WindowWin32Impl::Init(const WindowConfig& config)
2020-01-21 10:09:55 +08:00
{
2020-06-23 16:51:27 +08:00
HINSTANCE hinst = GetModuleHandle(nullptr);
WNDCLASSEXA wcex = { 0 };
2020-01-21 10:09:55 +08:00
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpszClassName = "KiwanoAppWnd";
2020-01-21 10:09:55 +08:00
wcex.style = CS_HREDRAW | CS_VREDRAW /* | CS_DBLCLKS */;
2020-02-14 22:01:56 +08:00
wcex.lpfnWndProc = WindowWin32Impl::StaticWndProc;
2020-06-23 16:51:27 +08:00
wcex.hIcon = Icon2HIcon(config.icon);
2020-01-21 10:09:55 +08:00
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = hinst;
wcex.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
2020-01-21 10:09:55 +08:00
wcex.lpszMenuName = nullptr;
wcex.hCursor = ::LoadCursor(hinst, IDC_ARROW);
2020-01-21 10:09:55 +08:00
::RegisterClassExA(&wcex);
2020-01-21 10:09:55 +08:00
// Get the nearest monitor to this window
HMONITOR monitor = ::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
// Get the target monitor info
2020-06-24 17:33:27 +08:00
MONITORINFOEXA monitor_info;
memset(&monitor_info, 0, sizeof(MONITORINFOEXA));
monitor_info.cbSize = sizeof(MONITORINFOEXA);
::GetMonitorInfoA(monitor, &monitor_info);
2020-01-21 10:09:55 +08:00
// Save the device name
2020-06-24 17:33:27 +08:00
device_name_ = monitor_info.szDevice;
2020-01-21 10:09:55 +08:00
2020-06-24 17:33:27 +08:00
uint32_t screenw = monitor_info.rcWork.right - monitor_info.rcWork.left;
uint32_t screenh = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
2020-01-21 10:09:55 +08:00
2020-05-26 00:12:36 +08:00
uint32_t win_width, win_height;
2020-06-23 16:51:27 +08:00
AdjustWindow(config.width, config.height, GetStyle(), &win_width, &win_height);
2020-01-21 10:09:55 +08:00
2020-06-24 17:33:27 +08:00
int left = monitor_info.rcWork.left + (screenw - win_width) / 2;
int top = monitor_info.rcWork.top + (screenh - win_height) / 2;
2020-01-21 10:09:55 +08:00
2020-06-23 16:51:27 +08:00
width_ = win_width;
height_ = win_height;
resizable_ = config.resizable;
is_fullscreen_ = config.fullscreen;
2020-06-21 01:56:02 +08:00
2020-06-23 16:51:27 +08:00
handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", config.title.c_str(), GetStyle(), left, top, width_, height_,
nullptr, nullptr, hinst, nullptr);
2020-01-21 10:09:55 +08:00
if (handle_ == nullptr)
{
::UnregisterClassA("KiwanoAppWnd", hinst);
2020-02-13 22:35:04 +08:00
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Create window failed");
2020-01-21 10:09:55 +08:00
}
// disable imm
::ImmAssociateContext(handle_, nullptr);
// use Application instance in message loop
::SetWindowLongPtrA(handle_, GWLP_USERDATA, LONG_PTR(this));
2020-01-21 10:09:55 +08:00
::ShowWindow(handle_, SW_SHOWNORMAL);
::UpdateWindow(handle_);
2020-06-21 01:56:02 +08:00
if (is_fullscreen_)
{
MONITORINFOEXA info = GetMoniterInfoEx(handle_);
2020-06-24 17:33:27 +08:00
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);
2020-06-21 01:56:02 +08:00
// Top the window
::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE);
::ShowWindow(handle_, SW_SHOWNORMAL);
2020-06-24 17:33:27 +08:00
resolution_.width = config.width;
resolution_.height = config.height;
}
else
{
RECT client_area;
::GetClientRect(handle_, &client_area);
resolution_.width = uint32_t(client_area.right - client_area.left);
resolution_.height = uint32_t(client_area.bottom - client_area.top);
2020-06-21 01:56:02 +08:00
}
2020-01-21 10:09:55 +08:00
}
2020-01-17 11:24:24 +08:00
2020-02-14 22:01:56 +08:00
void WindowWin32Impl::PumpEvents()
2020-01-21 10:09:55 +08:00
{
MSG msg;
while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
2020-01-21 10:09:55 +08:00
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
2020-01-21 10:09:55 +08:00
}
}
2020-02-19 12:09:50 +08:00
void WindowWin32Impl::SetTitle(const String& title)
2020-01-21 10:09:55 +08:00
{
KGE_ASSERT(handle_);
::SetWindowTextA(handle_, title.c_str());
2020-01-21 10:09:55 +08:00
}
2020-06-22 21:38:50 +08:00
void WindowWin32Impl::SetIcon(Icon icon)
2020-01-21 10:09:55 +08:00
{
KGE_ASSERT(handle_);
2020-01-21 10:09:55 +08:00
2020-06-23 16:51:27 +08:00
HICON hicon = Icon2HIcon(icon);
2020-06-22 21:38:50 +08:00
::SendMessage(handle_, WM_SETICON, ICON_BIG, (LPARAM)hicon);
::SendMessage(handle_, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
2020-01-21 10:09:55 +08:00
}
2020-05-20 11:55:53 +08:00
void WindowWin32Impl::SetMinimumSize(uint32_t width, uint32_t height)
{
min_width_ = width;
min_height_ = height;
}
2020-05-20 23:38:24 +08:00
void WindowWin32Impl::SetMaximumSize(uint32_t width, uint32_t height)
{
max_width_ = width;
max_height_ = height;
}
2020-02-14 22:01:56 +08:00
void WindowWin32Impl::SetCursor(CursorType cursor)
2020-01-21 10:09:55 +08:00
{
mouse_cursor_ = cursor;
}
2020-05-22 21:07:32 +08:00
void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen)
{
2020-06-21 01:56:02 +08:00
if (is_fullscreen_ != fullscreen)
{
is_fullscreen_ = fullscreen;
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
// Reset window style
::SetWindowLongPtrA(handle_, GWL_STYLE, GetStyle());
}
if (is_fullscreen_)
2020-05-22 21:07:32 +08:00
{
2020-06-21 01:56:02 +08:00
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);
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
// Top the window
::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE);
::ShowWindow(handle_, SW_SHOWNORMAL);
2020-05-22 21:07:32 +08:00
}
else
{
2020-06-21 01:56:02 +08:00
// Adjust the rect of client area
RECT rc = { 0, 0, LONG(width), LONG(height) };
::AdjustWindowRect(&rc, GetStyle(), false);
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());
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
// Unpin the window
::SetWindowPos(handle_, HWND_NOTOPMOST, left, top, width, height, SWP_DRAWFRAME | SWP_FRAMECHANGED);
::ShowWindow(handle_, SW_SHOWNORMAL);
2020-05-22 21:07:32 +08:00
}
2020-06-21 01:56:02 +08:00
resolution_ = Resolution{ width, height, 0 };
// Resize render target
Renderer::GetInstance().Resize(width, height);
2020-05-22 21:07:32 +08:00
}
Vector<Resolution> WindowWin32Impl::GetResolutions()
{
if (resolutions_.empty())
{
2020-06-21 01:56:02 +08:00
Set<String> resolution_list;
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
DEVMODEA dmi;
ZeroMemory(&dmi, sizeof(dmi));
dmi.dmSize = sizeof(dmi);
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
DWORD mode_count = 0;
while (EnumDisplaySettingsA(device_name_.c_str(), mode_count++, &dmi) != 0)
2020-05-22 21:07:32 +08:00
{
2020-06-21 01:56:02 +08:00
StringStream ss;
ss << dmi.dmPelsWidth << 'x' << dmi.dmPelsHeight << ':' << dmi.dmDisplayFrequency;
2020-05-22 21:07:32 +08:00
2020-06-21 01:56:02 +08:00
if (resolution_list.find(ss.str()) == resolution_list.end())
2020-05-22 21:07:32 +08:00
{
2020-06-21 01:56:02 +08:00
resolution_list.insert(ss.str());
resolutions_.push_back(Resolution{ uint32_t(dmi.dmPelsWidth), uint32_t(dmi.dmPelsHeight),
uint32_t(dmi.dmDisplayFrequency) });
2020-05-22 21:07:32 +08:00
}
2020-06-21 01:56:02 +08:00
ZeroMemory(&dmi, sizeof(dmi));
2020-05-22 21:07:32 +08:00
}
}
return resolutions_;
}
2020-02-14 22:01:56 +08:00
DWORD WindowWin32Impl::GetStyle() const
2020-01-21 10:09:55 +08:00
{
2020-06-21 01:56:02 +08:00
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);
}
}
2020-01-21 10:09:55 +08:00
}
2020-02-14 22:01:56 +08:00
void WindowWin32Impl::UpdateCursor()
2020-01-21 10:09:55 +08:00
{
LPTSTR win32_cursor = IDC_ARROW;
switch (mouse_cursor_)
{
case CursorType::Arrow:
win32_cursor = IDC_ARROW;
break;
case CursorType::TextInput:
win32_cursor = IDC_IBEAM;
break;
case CursorType::SizeAll:
win32_cursor = IDC_SIZEALL;
break;
case CursorType::SizeWE:
win32_cursor = IDC_SIZEWE;
break;
case CursorType::SizeNS:
win32_cursor = IDC_SIZENS;
break;
case CursorType::SizeNESW:
win32_cursor = IDC_SIZENESW;
break;
case CursorType::SizeNWSE:
win32_cursor = IDC_SIZENWSE;
break;
case CursorType::Hand:
win32_cursor = IDC_HAND;
break;
}
::SetCursor(::LoadCursor(nullptr, win32_cursor));
2020-01-21 10:09:55 +08:00
}
2020-02-14 22:01:56 +08:00
LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam)
2020-01-21 10:09:55 +08:00
{
switch (msg)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
{
2020-02-14 17:12:13 +08:00
KeyCode key = this->key_map_[size_t(wparam)];
2020-01-21 10:09:55 +08:00
if (key != KeyCode::Unknown)
{
KeyDownEventPtr evt = new KeyDownEvent;
evt->code = key;
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
{
2020-02-14 17:12:13 +08:00
KeyCode key = this->key_map_[size_t(wparam)];
2020-01-21 10:09:55 +08:00
if (key != KeyCode::Unknown)
{
KeyUpEventPtr evt = new KeyUpEvent;
evt->code = key;
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
}
break;
case WM_CHAR:
{
KeyCharEventPtr evt = new KeyCharEvent;
evt->value = char(wparam);
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
{
MouseDownEventPtr evt = new MouseDownEvent;
evt->pos = Point((float)GET_X_LPARAM(lparam), (float)GET_Y_LPARAM(lparam));
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK)
{
evt->button = MouseButton::Left;
}
else if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK)
{
evt->button = MouseButton::Right;
}
else if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK)
{
evt->button = MouseButton::Middle;
}
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
{
MouseUpEventPtr evt = new MouseUpEvent;
evt->pos = Point((float)GET_X_LPARAM(lparam), (float)GET_Y_LPARAM(lparam));
if (msg == WM_LBUTTONUP)
{
evt->button = MouseButton::Left;
}
else if (msg == WM_RBUTTONUP)
{
evt->button = MouseButton::Right;
}
else if (msg == WM_MBUTTONUP)
{
evt->button = MouseButton::Middle;
}
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_MOUSEMOVE:
{
MouseMoveEventPtr evt = new MouseMoveEvent;
evt->pos = Point((float)GET_X_LPARAM(lparam), (float)GET_Y_LPARAM(lparam));
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_MOUSEWHEEL:
{
MouseWheelEventPtr evt = new MouseWheelEvent;
evt->pos = Point((float)GET_X_LPARAM(lparam), (float)GET_Y_LPARAM(lparam));
evt->wheel = GET_WHEEL_DELTA_WPARAM(wparam) / (float)WHEEL_DELTA;
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_SIZE:
{
bool resized = false;
2020-01-21 10:09:55 +08:00
if (SIZE_MAXHIDE == wparam || SIZE_MINIMIZED == wparam)
{
KGE_DEBUG_LOGF("Window minimized");
2020-05-20 11:55:53 +08:00
is_minimized_ = true;
// Pause game when window is minimized
Application::GetInstance().Pause();
2020-05-20 11:55:53 +08:00
}
else if (SIZE_MAXIMIZED == wparam)
{
KGE_DEBUG_LOGF("Window maximized");
2020-05-20 23:38:24 +08:00
2020-05-20 11:55:53 +08:00
if (is_minimized_)
{
is_minimized_ = false;
Application::GetInstance().Resume();
2020-05-20 11:55:53 +08:00
}
else
{
resized = true;
}
2020-01-21 10:09:55 +08:00
}
2020-05-20 11:55:53 +08:00
else if (wparam == SIZE_RESTORED)
2020-01-21 10:09:55 +08:00
{
2020-05-20 11:55:53 +08:00
if (is_minimized_)
{
KGE_DEBUG_LOGF("Window restored");
2020-05-20 11:55:53 +08:00
// the window was restored and was previously minimized
is_minimized_ = false;
Application::GetInstance().Resume();
2020-05-20 11:55:53 +08:00
}
2020-08-04 00:40:07 +08:00
else if (is_moving_or_resizing_)
2020-05-20 11:55:53 +08:00
{
// DO NOTHING until the dragging / resizing has stopped.
}
else
{
resized = true;
}
}
if (resized)
{
this->width_ = ((uint32_t)(short)LOWORD(lparam));
this->height_ = ((uint32_t)(short)HIWORD(lparam));
2020-01-21 10:09:55 +08:00
WindowResizedEventPtr evt = new WindowResizedEvent;
evt->window = this;
evt->width = this->GetWidth();
evt->height = this->GetHeight();
this->PushEvent(evt);
2020-05-20 23:38:24 +08:00
KGE_DEBUG_LOGF("Window resized to (%d, %d)", this->width_, this->height_);
2020-05-20 11:55:53 +08:00
}
}
break;
case WM_ENTERSIZEMOVE:
{
2020-08-04 00:40:07 +08:00
is_moving_or_resizing_ = true;
Application::GetInstance().Pause();
2020-05-20 11:55:53 +08:00
return 0;
2020-01-21 10:09:55 +08:00
}
2020-05-20 11:55:53 +08:00
case WM_EXITSIZEMOVE:
{
2020-08-04 00:40:07 +08:00
is_moving_or_resizing_ = false;
Application::GetInstance().Resume();
2020-05-20 23:38:24 +08:00
// Send window resized event when client size changed
RECT client_rect = { 0 };
::GetClientRect(hwnd, &client_rect);
uint32_t client_width = uint32_t(client_rect.right - client_rect.left);
uint32_t client_height = uint32_t(client_rect.bottom - client_rect.top);
if (client_width != this->GetWidth() || client_height != this->GetHeight())
{
KGE_DEBUG_LOGF("Window resized to (%d, %d)", client_width, client_height);
2020-05-20 23:38:24 +08:00
this->width_ = client_width;
this->height_ = client_height;
WindowResizedEventPtr evt = new WindowResizedEvent;
2020-08-04 00:40:07 +08:00
evt->window = this;
evt->width = client_width;
evt->height = client_height;
this->PushEvent(evt);
}
RECT window_rect = { 0 };
::GetWindowRect(hwnd, &window_rect);
int window_x = int(window_rect.left);
int window_y = int(window_rect.top);
if (window_x != this->GetPosX() || window_y != this->GetPosY())
{
KGE_DEBUG_LOGF("Window moved to (%d, %d)", window_x, window_y);
this->pos_x_ = window_x;
this->pos_y_ = window_y;
WindowMovedEventPtr evt = new WindowMovedEvent;
evt->window = this;
evt->x = window_x;
evt->y = window_y;
2020-05-20 23:38:24 +08:00
this->PushEvent(evt);
}
2020-05-20 11:55:53 +08:00
return 0;
}
case WM_GETMINMAXINFO:
{
2020-05-20 23:38:24 +08:00
if (min_width_ || min_height_)
{
((MINMAXINFO*)lparam)->ptMinTrackSize = POINT{ LONG(min_width_), LONG(min_height_) };
}
if (max_width_ || max_height_)
{
((MINMAXINFO*)lparam)->ptMaxTrackSize = POINT{ LONG(max_width_), LONG(max_height_) };
}
return 0;
2020-05-20 11:55:53 +08:00
}
2020-01-21 10:09:55 +08:00
case WM_MOVE:
{
2020-08-04 00:40:07 +08:00
if (is_moving_or_resizing_)
{
// DO NOTHING until the dragging / resizing has stopped.
}
else
{
int window_x = GET_X_LPARAM(lparam);
int window_y = GET_Y_LPARAM(lparam);
KGE_DEBUG_LOGF("Window moved to (%d, %d)", window_x, window_y);
this->pos_x_ = window_x;
this->pos_y_ = window_y;
WindowMovedEventPtr evt = new WindowMovedEvent;
evt->window = this;
evt->x = window_x;
evt->y = window_y;
this->PushEvent(evt);
}
2020-01-21 10:09:55 +08:00
}
break;
2020-05-20 11:55:53 +08:00
case WM_MENUCHAR:
{
// Disables the crazy beeping sound when pressing a mnemonic key.
// Simply tell Windows that we want the menu closed.
return MAKELRESULT(0, MNC_CLOSE);
}
2020-01-21 10:09:55 +08:00
case WM_ACTIVATE:
{
2020-05-20 00:55:51 +08:00
bool active = (LOWORD(wparam) != WA_INACTIVE);
2020-01-21 10:09:55 +08:00
WindowFocusChangedEventPtr evt = new WindowFocusChangedEvent;
2020-08-04 00:40:07 +08:00
evt->window = this;
2020-05-20 00:55:51 +08:00
evt->focus = active;
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
2020-05-17 21:02:43 +08:00
case WM_SETFOCUS:
{
if (is_fullscreen_)
{
2020-06-21 01:56:02 +08:00
SetActive(true);
2020-05-17 21:02:43 +08:00
}
}
break;
case WM_KILLFOCUS:
{
if (is_fullscreen_)
{
2020-06-21 01:56:02 +08:00
SetActive(false);
2020-05-17 21:02:43 +08:00
}
}
break;
2020-01-21 10:09:55 +08:00
case WM_SETTEXT:
{
KGE_DEBUG_LOGF("Window title changed");
2020-01-21 10:09:55 +08:00
2020-04-14 12:28:29 +08:00
this->title_ = strings::WideToNarrow(reinterpret_cast<LPCWSTR>(lparam));
2020-01-21 10:09:55 +08:00
WindowTitleChangedEventPtr evt = new WindowTitleChangedEvent;
2020-08-04 00:40:07 +08:00
evt->window = this;
2020-02-14 17:12:13 +08:00
evt->title = this->title_;
this->PushEvent(evt);
2020-01-21 10:09:55 +08:00
}
break;
case WM_SETICON:
{
KGE_DEBUG_LOGF("Window icon changed");
2020-01-21 10:09:55 +08:00
}
break;
case WM_DISPLAYCHANGE:
{
KGE_DEBUG_LOGF("The display resolution has changed");
2020-01-21 10:09:55 +08:00
}
break;
case WM_SETCURSOR:
{
2020-02-14 17:12:13 +08:00
this->UpdateCursor();
2020-01-21 10:09:55 +08:00
}
break;
case WM_CLOSE:
{
KGE_DEBUG_LOGF("Window is closing");
2020-01-21 10:09:55 +08:00
WindowClosedEventPtr evt = new WindowClosedEvent;
2020-08-04 00:40:07 +08:00
evt->window = this;
2020-02-14 17:12:13 +08:00
this->PushEvent(evt);
this->SetShouldClose(true);
return 0;
2020-01-21 10:09:55 +08:00
}
break;
case WM_DESTROY:
{
KGE_DEBUG_LOGF("Window was destroyed");
2020-01-21 10:09:55 +08:00
::PostQuitMessage(0);
return 0;
}
break;
}
return ::DefWindowProcA(hwnd, msg, wparam, lparam);
2020-01-21 10:09:55 +08:00
}
2020-02-14 22:01:56 +08:00
LRESULT CALLBACK WindowWin32Impl::StaticWndProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam)
2020-02-14 17:12:13 +08:00
{
LONG_PTR ptr = static_cast<LONG_PTR>(::GetWindowLongPtrA(hwnd, GWLP_USERDATA));
2020-02-14 17:12:13 +08:00
if (ptr)
{
2020-02-14 22:01:56 +08:00
WindowWin32Impl* window = reinterpret_cast<WindowWin32Impl*>(ptr);
2020-02-14 17:12:13 +08:00
return window->MessageProc(hwnd, msg, wparam, lparam);
}
return ::DefWindowProcA(hwnd, msg, wparam, lparam);
2020-02-14 17:12:13 +08:00
}
2020-01-21 10:09:55 +08:00
} // namespace kiwano
2020-01-17 11:24:24 +08:00
#endif