// Copyright (c) 2016-2020 Kiwano - Nomango // // 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: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // 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. #include #if defined(KGE_PLATFORM_WINDOWS) #include #include #include #include #include #include #include #include #include // GET_X_LPARAM, GET_Y_LPARAM #include // ImmAssociateContext #pragma comment(lib, "imm32.lib") #include // timeBeginPeriod, timeEndPeriod #pragma comment(lib, "winmm.lib") namespace kiwano { KGE_DECLARE_SMART_PTR(WindowWin32Impl); class KGE_API WindowWin32Impl : public Window { public: WindowWin32Impl(); virtual ~WindowWin32Impl(); void Init(const WindowConfig& config); void SetTitle(const String& title) override; void SetIcon(Icon icon) override; void SetMinimumSize(uint32_t width, uint32_t height) override; void SetMaximumSize(uint32_t width, uint32_t height) override; void SetCursor(CursorType cursor) override; void SetResolution(uint32_t width, uint32_t height, bool fullscreen) override; Vector GetResolutions() override; void PumpEvents() override; void SetImmEnabled(bool enable) override; DWORD GetStyle() const; void SetActive(bool active); void UpdateCursor(); LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM); static LRESULT CALLBACK StaticWndProc(HWND, UINT32, WPARAM, LPARAM); private: bool resizable_; bool is_moving_or_resizing_; bool is_minimized_; CursorType mouse_cursor_; HIMC imc_restore_; int mouse_flag_; String device_name_; Vector resolutions_; std::array key_map_; }; WindowPtr Window::Create(const WindowConfig& config) { WindowWin32ImplPtr ptr = MakePtr(); if (ptr) { ptr->Init(config); } return ptr; } } // 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 #define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP namespace { MONITORINFOEXA GetMoniterInfoEx(HWND hwnd) { HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFOEXA monitor_info; memset(&monitor_info, 0, sizeof(monitor_info)); monitor_info.cbSize = sizeof(monitor_info); ::GetMonitorInfoA(monitor, &monitor_info); 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); *win_width = rc.right - rc.left; *win_height = rc.bottom - rc.top; MONITORINFOEXA info = GetMoniterInfoEx(NULL); 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; } 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; } } // namespace WindowWin32Impl::WindowWin32Impl() : resizable_(false) , is_moving_or_resizing_(false) , is_minimized_(false) , mouse_cursor_(CursorType::Arrow) , key_map_{} , imc_restore_(nullptr) , mouse_flag_{} { // 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 for (size_t i = 0; i < 10; ++i) key_map_['0' + i] = KeyCode(size_t(KeyCode::Num0) + i); // Numpad 0 - 9 for (size_t i = 0; i < 10; ++i) 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); ::timeBeginPeriod(0); } WindowWin32Impl::~WindowWin32Impl() { if (handle_) { ::DestroyWindow(handle_); handle_ = nullptr; } ::timeEndPeriod(0); } void WindowWin32Impl::Init(const WindowConfig& config) { HINSTANCE hinst = GetModuleHandle(nullptr); WNDCLASSEXA wcex = { 0 }; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpszClassName = "KiwanoAppWnd"; wcex.style = CS_HREDRAW | CS_VREDRAW /* | CS_DBLCLKS */; wcex.lpfnWndProc = WindowWin32Impl::StaticWndProc; wcex.hIcon = Icon2HIcon(config.icon); wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(LONG_PTR); wcex.hInstance = hinst; wcex.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH); wcex.lpszMenuName = nullptr; wcex.hCursor = ::LoadCursor(hinst, IDC_ARROW); ::RegisterClassExA(&wcex); // Get the nearest monitor to this window HMONITOR monitor = ::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY); // Get the target monitor info MONITORINFOEXA monitor_info; memset(&monitor_info, 0, sizeof(MONITORINFOEXA)); monitor_info.cbSize = sizeof(MONITORINFOEXA); ::GetMonitorInfoA(monitor, &monitor_info); // Save the device name device_name_ = monitor_info.szDevice; uint32_t screenw = monitor_info.rcWork.right - monitor_info.rcWork.left; uint32_t screenh = monitor_info.rcWork.bottom - monitor_info.rcWork.top; uint32_t win_width, win_height; AdjustWindow(config.width, config.height, GetStyle(), &win_width, &win_height); int left = monitor_info.rcWork.left + (screenw - win_width) / 2; int top = monitor_info.rcWork.top + (screenh - win_height) / 2; width_ = win_width; height_ = win_height; resizable_ = config.resizable; is_fullscreen_ = config.fullscreen; handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", config.title.c_str(), GetStyle(), left, top, width_, height_, nullptr, nullptr, hinst, nullptr); if (handle_ == nullptr) { ::UnregisterClassA("KiwanoAppWnd", hinst); KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Create window failed"); } // disable imm SetImmEnabled(false); // use Application instance in message loop ::SetWindowLongPtrA(handle_, GWLP_USERDATA, LONG_PTR(this)); ::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); 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); } } void WindowWin32Impl::PumpEvents() { MSG msg; while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } void WindowWin32Impl::SetImmEnabled(bool enable) { const bool enabled = imc_restore_ == nullptr; if (enable != enabled) { if (enable) { ::ImmAssociateContext(handle_, imc_restore_); imc_restore_ = nullptr; } else { imc_restore_ = ::ImmAssociateContext(handle_, nullptr); } } } void WindowWin32Impl::SetTitle(const String& title) { KGE_ASSERT(handle_); ::SetWindowTextA(handle_, title.c_str()); } void WindowWin32Impl::SetIcon(Icon icon) { KGE_ASSERT(handle_); HICON hicon = Icon2HIcon(icon); ::SendMessage(handle_, WM_SETICON, ICON_BIG, (LPARAM)hicon); ::SendMessage(handle_, WM_SETICON, ICON_SMALL, (LPARAM)hicon); } void WindowWin32Impl::SetMinimumSize(uint32_t width, uint32_t height) { AdjustWindow(width, height, GetStyle(), &min_width_, &min_height_); } void WindowWin32Impl::SetMaximumSize(uint32_t width, uint32_t height) { AdjustWindow(width, height, GetStyle(), &max_width_, &max_height_); } void WindowWin32Impl::SetCursor(CursorType cursor) { mouse_cursor_ = cursor; } void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen) { if (is_fullscreen_ != fullscreen) { is_fullscreen_ = fullscreen; // 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 { // 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()); // Unpin the window ::SetWindowPos(handle_, HWND_NOTOPMOST, left, top, width, height, SWP_DRAWFRAME | SWP_FRAMECHANGED); ::ShowWindow(handle_, SW_SHOWNORMAL); } resolution_ = Resolution{ width, height, 0 }; // Resize render target Renderer::GetInstance().Resize(width, height); } Vector WindowWin32Impl::GetResolutions() { if (resolutions_.empty()) { Set resolution_list; DEVMODEA dmi; ZeroMemory(&dmi, sizeof(dmi)); dmi.dmSize = sizeof(dmi); DWORD mode_count = 0; while (EnumDisplaySettingsA(device_name_.c_str(), mode_count++, &dmi) != 0) { StringStream ss; ss << dmi.dmPelsWidth << 'x' << dmi.dmPelsHeight << ':' << dmi.dmDisplayFrequency; if (resolution_list.find(ss.str()) == resolution_list.end()) { resolution_list.insert(ss.str()); resolutions_.push_back(Resolution{ uint32_t(dmi.dmPelsWidth), uint32_t(dmi.dmPelsHeight), uint32_t(dmi.dmDisplayFrequency) }); } ZeroMemory(&dmi, sizeof(dmi)); } } return resolutions_; } DWORD WindowWin32Impl::GetStyle() const { 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() { 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; case CursorType::No: win32_cursor = IDC_NO; break; } ::SetCursor(::LoadCursor(nullptr, win32_cursor)); } LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_KEYDOWN: case WM_SYSKEYDOWN: { KeyCode key = this->key_map_[size_t(wparam)]; if (key != KeyCode::Unknown) { KeyDownEventPtr evt = new KeyDownEvent; evt->code = key; this->PushEvent(evt); } } break; case WM_KEYUP: case WM_SYSKEYUP: { KeyCode key = this->key_map_[size_t(wparam)]; if (key != KeyCode::Unknown) { KeyUpEventPtr evt = new KeyUpEvent; evt->code = key; this->PushEvent(evt); } } break; case WM_CHAR: { KeyCharEventPtr evt = new KeyCharEvent; evt->value = char(wparam); this->PushEvent(evt); } break; case WM_IME_CHAR: { // no need to handle return TRUE; } case WM_IME_COMPOSITION: { if (lparam & GCS_RESULTSTR) { const auto hIMC = ::ImmGetContext(hwnd); if (hIMC) { // Get the size of the result string. auto dwSize = ::ImmGetCompositionStringA(hIMC, GCS_RESULTSTR, NULL, 0); // increase buffer size for terminating null character dwSize += sizeof(char); // Get the result strings that is generated by IME. String buf(size_t(dwSize), 0); ::ImmGetCompositionStringA(hIMC, GCS_RESULTSTR, const_cast(buf.data()), dwSize); ::ImmReleaseContext(hwnd, hIMC); IMEInputEventPtr evt = new IMEInputEvent; evt->value = std::move(buf); this->PushEvent(evt); return TRUE; } } } 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; } if (mouse_flag_ == 0 && ::GetCapture() == nullptr) ::SetCapture(hwnd); mouse_flag_ |= 1 << int(evt->button); this->PushEvent(evt); } 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; } mouse_flag_ &= ~(1 << int(evt->button)); if (mouse_flag_ == 0 && ::GetCapture() == hwnd) ::ReleaseCapture(); this->PushEvent(evt); } break; case WM_MOUSEMOVE: { MouseMoveEventPtr evt = new MouseMoveEvent; evt->pos = Point((float)GET_X_LPARAM(lparam), (float)GET_Y_LPARAM(lparam)); this->PushEvent(evt); } 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; this->PushEvent(evt); } break; case WM_SIZE: { bool resized = false; if (SIZE_MAXHIDE == wparam || SIZE_MINIMIZED == wparam) { KGE_DEBUG_LOGF("Window minimized"); is_minimized_ = true; // Pause game when window is minimized Application::GetInstance().Pause(); } else if (SIZE_MAXIMIZED == wparam) { KGE_DEBUG_LOGF("Window maximized"); if (is_minimized_) { is_minimized_ = false; Application::GetInstance().Resume(); } else { resized = true; } } else if (wparam == SIZE_RESTORED) { if (is_minimized_) { KGE_DEBUG_LOGF("Window restored"); // the window was restored and was previously minimized is_minimized_ = false; Application::GetInstance().Resume(); } else if (is_moving_or_resizing_) { // 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)); WindowResizedEventPtr evt = new WindowResizedEvent; evt->window = this; evt->width = this->GetWidth(); evt->height = this->GetHeight(); this->PushEvent(evt); KGE_DEBUG_LOGF("Window resized to (%d, %d)", this->width_, this->height_); } } break; case WM_ENTERSIZEMOVE: { is_moving_or_resizing_ = true; Application::GetInstance().Pause(); return 0; } case WM_EXITSIZEMOVE: { is_moving_or_resizing_ = false; Application::GetInstance().Resume(); // 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); this->width_ = client_width; this->height_ = client_height; WindowResizedEventPtr evt = new WindowResizedEvent; 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; this->PushEvent(evt); } return 0; } case WM_GETMINMAXINFO: { 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_) }; ((MINMAXINFO*)lparam)->ptMaxSize = POINT{ LONG(max_width_), LONG(max_height_) }; } return 0; } case WM_MOVE: { 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); } } break; 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); } case WM_ACTIVATE: { bool active = (LOWORD(wparam) != WA_INACTIVE); WindowFocusChangedEventPtr evt = new WindowFocusChangedEvent; evt->window = this; evt->focus = active; this->PushEvent(evt); } break; case WM_SETFOCUS: { if (is_fullscreen_) { SetActive(true); } } break; case WM_KILLFOCUS: { if (is_fullscreen_) { SetActive(false); } } break; case WM_SETTEXT: { KGE_DEBUG_LOGF("Window title changed"); this->title_ = strings::WideToNarrow(reinterpret_cast(lparam)); WindowTitleChangedEventPtr evt = new WindowTitleChangedEvent; evt->window = this; evt->title = this->title_; this->PushEvent(evt); } break; case WM_SETICON: { KGE_DEBUG_LOGF("Window icon changed"); } break; case WM_DISPLAYCHANGE: { KGE_DEBUG_LOGF("The display resolution has changed"); } break; case WM_SETCURSOR: { this->UpdateCursor(); } break; case WM_CLOSE: { KGE_DEBUG_LOGF("Window is closing"); WindowClosedEventPtr evt = new WindowClosedEvent; evt->window = this; this->PushEvent(evt); this->SetShouldClose(true); return 0; } break; case WM_DESTROY: { KGE_DEBUG_LOGF("Window was destroyed"); ::PostQuitMessage(0); return 0; } break; } return ::DefWindowProcA(hwnd, msg, wparam, lparam); } LRESULT CALLBACK WindowWin32Impl::StaticWndProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARAM lparam) { LONG_PTR ptr = static_cast(::GetWindowLongPtrA(hwnd, GWLP_USERDATA)); if (ptr) { WindowWin32Impl* window = reinterpret_cast(ptr); return window->MessageProc(hwnd, msg, wparam, lparam); } return ::DefWindowProcA(hwnd, msg, wparam, lparam); } } // namespace kiwano #endif