// 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 // Un grand merci à Laurent Gomila pour la SFML qui m'aura bien aidé à réaliser cette implémentation #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN64 #define GCL_HCURSOR GCLP_HCURSOR #define GWL_USERDATA GWLP_USERDATA #endif // N'est pas défini avec MinGW #ifndef MAPVK_VK_TO_VSC #define MAPVK_VK_TO_VSC 0 #endif #undef IsMinimized // Conflit avec la méthode du même nom namespace Nz { namespace { const wchar_t* className = L"Nazara Window"; WindowImpl* fullscreenWindow = nullptr; } WindowImpl::WindowImpl(Window* parent) : m_cursor(nullptr), m_handle(nullptr), m_callback(0), m_style(0), m_maxSize(-1), m_minSize(-1), m_parent(parent), m_keyRepeat(true), m_mouseInside(false), m_smoothScrolling(false), m_scrolling(0) { m_cursor = static_cast(LoadImage(nullptr, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED)); } bool WindowImpl::Create(const VideoMode& mode, const String& title, WindowStyleFlags style) { bool async = (style & WindowStyle_Threaded) != 0; bool fullscreen = (style & WindowStyle_Fullscreen) != 0; DWORD win32Style, win32StyleEx; unsigned int x, y; unsigned int width = mode.width; unsigned int height = mode.height; if (fullscreen) { DEVMODE win32Mode; std::memset(&win32Mode, 0, sizeof(DEVMODE)); win32Mode.dmBitsPerPel = mode.bitsPerPixel; win32Mode.dmPelsHeight = mode.height; win32Mode.dmPelsWidth = mode.width; win32Mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; win32Mode.dmSize = sizeof(DEVMODE); if (ChangeDisplaySettings(&win32Mode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { // Situation extrêmement rare grâce à VideoMode::IsValid appelé par Window NazaraError("Failed to change display settings for fullscreen, this video mode is not supported by your computer"); fullscreen = false; } } // Testé une seconde fois car sa valeur peut changer if (fullscreen) { x = 0; y = 0; win32Style = WS_CLIPCHILDREN | WS_POPUP; // Pour cacher la barre des tâches // http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx win32StyleEx = WS_EX_APPWINDOW; fullscreenWindow = this; } else { win32Style = WS_VISIBLE; if (style & WindowStyle_Titlebar) { win32Style |= WS_CAPTION | WS_MINIMIZEBOX; if (style & WindowStyle_Closable) win32Style |= WS_SYSMENU; if (style & WindowStyle_Resizable) win32Style |= WS_MAXIMIZEBOX | WS_SIZEBOX; } else win32Style |= WS_POPUP; win32StyleEx = 0; RECT rect = {0, 0, static_cast(width), static_cast(height)}; AdjustWindowRect(&rect, win32Style, false); width = rect.right-rect.left; height = rect.bottom-rect.top; x = (GetSystemMetrics(SM_CXSCREEN) - width)/2; y = (GetSystemMetrics(SM_CYSCREEN) - height)/2; } m_callback = 0; m_eventListener = true; m_ownsWindow = true; m_sizemove = false; m_style = style; if (async) { Mutex mutex; ConditionVariable condition; m_threadActive = true; // On attend que la fenêtre soit créée mutex.Lock(); m_thread = Thread(WindowThread, &m_handle, win32StyleEx, title, win32Style, fullscreen, Rectui(x, y, width, height), this, &mutex, &condition); condition.Wait(&mutex); mutex.Unlock(); } else m_handle = CreateWindowExW(win32StyleEx, className, title.GetWideString().data(), win32Style, x, y, width, height, nullptr, nullptr, GetModuleHandle(nullptr), this); if (!m_handle) { NazaraError("Failed to create window: " + Error::GetLastSystemError()); return false; } if (!async) PrepareWindow(fullscreen); return true; } bool WindowImpl::Create(WindowHandle handle) { m_handle = static_cast(handle); if (!m_handle || !IsWindow(m_handle)) { NazaraError("Invalid handle"); return false; } m_eventListener = false; m_ownsWindow = false; m_sizemove = false; m_style = RetrieveStyle(m_handle); RECT clientRect, windowRect; GetClientRect(m_handle, &clientRect); GetWindowRect(m_handle, &windowRect); m_position.Set(windowRect.left, windowRect.top); m_size.Set(clientRect.right-clientRect.left, clientRect.bottom-clientRect.top); return true; } void WindowImpl::Destroy() { if (m_ownsWindow) { if (m_style & WindowStyle_Threaded) { if (m_thread.IsJoinable()) { m_threadActive = false; PostMessageW(m_handle, WM_NULL, 0, 0); // Wake up our thread m_thread.Join(); } } else { if (m_handle) DestroyWindow(m_handle); } } else SetEventListener(false); } void WindowImpl::EnableKeyRepeat(bool enable) { m_keyRepeat = enable; } void WindowImpl::EnableSmoothScrolling(bool enable) { m_smoothScrolling = enable; } WindowHandle WindowImpl::GetHandle() const { return m_handle; } Vector2i WindowImpl::GetPosition() const { return m_position; } Vector2ui WindowImpl::GetSize() const { return m_size; } WindowStyleFlags WindowImpl::GetStyle() const { return m_style; } String WindowImpl::GetTitle() const { unsigned int titleSize = GetWindowTextLengthW(m_handle); if (titleSize == 0) return String(); titleSize++; // \0 std::unique_ptr wTitle(new wchar_t[titleSize]); GetWindowTextW(m_handle, wTitle.get(), titleSize); return String::Unicode(wTitle.get()); } bool WindowImpl::HasFocus() const { return GetForegroundWindow() == m_handle; } void WindowImpl::IgnoreNextMouseEvent(int mouseX, int mouseY) { // Petite astuce ... m_mousePos.x = mouseX; m_mousePos.y = mouseY; } bool WindowImpl::IsMinimized() const { return IsIconic(m_handle) == TRUE; } bool WindowImpl::IsVisible() const { return IsWindowVisible(m_handle) == TRUE; } void WindowImpl::RefreshCursor() { ::SetCursor(m_cursor); } void WindowImpl::ProcessEvents(bool block) { if (m_ownsWindow) { if (block) WaitMessage(); MSG message; while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&message); DispatchMessageW(&message); } } } void WindowImpl::SetCursor(const Cursor& cursor) { m_cursor = cursor.m_impl->GetCursor(); if (HasFocus()) RefreshCursor(); } void WindowImpl::SetEventListener(bool listener) { if (m_ownsWindow) m_eventListener = listener; else if (listener != m_eventListener) { if (listener) { SetWindowLongPtr(m_handle, GWLP_USERDATA, reinterpret_cast(this)); m_callback = SetWindowLongPtr(m_handle, GWLP_WNDPROC, reinterpret_cast(MessageHandler)); m_eventListener = true; } else if (m_eventListener) { SetWindowLongPtr(m_handle, GWLP_WNDPROC, m_callback); m_eventListener = false; } } } void WindowImpl::SetFocus() { SetForegroundWindow(m_handle); } void WindowImpl::SetIcon(const Icon& icon) { HICON iconHandle = icon.m_impl->GetIcon(); SendMessage(m_handle, WM_SETICON, ICON_BIG, reinterpret_cast(iconHandle)); SendMessage(m_handle, WM_SETICON, ICON_SMALL, reinterpret_cast(iconHandle)); } void WindowImpl::SetMaximumSize(int width, int height) { RECT rect = {0, 0, width, height}; AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); if (width != -1) m_maxSize.x = rect.right-rect.left; else m_maxSize.x = -1; if (height != -1) m_maxSize.y = rect.bottom-rect.top; else m_maxSize.y = -1; } void WindowImpl::SetMinimumSize(int width, int height) { RECT rect = {0, 0, width, height}; AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); if (width != -1) m_minSize.x = rect.right-rect.left; else m_minSize.x = -1; if (height != -1) m_minSize.y = rect.bottom-rect.top; else m_minSize.y = -1; } void WindowImpl::SetPosition(int x, int y) { SetWindowPos(m_handle, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_ASYNCWINDOWPOS); } void WindowImpl::SetSize(unsigned int width, unsigned int height) { // SetWindowPos demande la taille totale de la fenêtre RECT rect = {0, 0, static_cast(width), static_cast(height)}; AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); SetWindowPos(m_handle, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER | SWP_ASYNCWINDOWPOS); } void WindowImpl::SetStayOnTop(bool stayOnTop) { if (stayOnTop) SetWindowPos(m_handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS); else SetWindowPos(m_handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); } void WindowImpl::SetTitle(const String& title) { SetWindowTextW(m_handle, title.GetWideString().data()); } void WindowImpl::SetVisible(bool visible) { ShowWindow(m_handle, (visible) ? SW_SHOW : SW_HIDE); } bool WindowImpl::HandleMessage(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { // Inutile de récupérer des évènements ne venant pas de notre fenêtre if (m_handle != window) return false; switch (message) { case WM_DESTROY: if (fullscreenWindow == this) ChangeDisplaySettings(nullptr, 0); break; /*case WM_SETCURSOR: // http://msdn.microsoft.com/en-us/library/windows/desktop/ms648382(v=vs.85).aspx if (LOWORD(lParam) == HTCLIENT) ::SetCursor(m_cursor); break;*/ case WM_WINDOWPOSCHANGING: { WINDOWPOS* pos = reinterpret_cast(lParam); pos->cx = std::max(pos->cx, m_minSize.x); pos->cy = std::max(pos->cy, m_minSize.y); if (m_maxSize.x >= 0) pos->cx = std::min(pos->cx, m_maxSize.x); if (m_maxSize.y >= 0) pos->cy = std::min(pos->cy, m_maxSize.y); break; } default: break; } if (m_eventListener) { switch (message) { case WM_CHAR: { // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx bool repeated = ((HIWORD(lParam) & KF_REPEAT) != 0); if (m_keyRepeat || !repeated) { WindowEvent event; event.type = WindowEventType_TextEntered; event.text.character = static_cast(wParam); event.text.repeated = repeated; m_parent->PushEvent(event); } break; } case WM_CLOSE: { WindowEvent event; event.type = WindowEventType_Quit; m_parent->PushEvent(event); return true; // Afin que Windows ne ferme pas la fenêtre automatiquement } case WM_ENTERSIZEMOVE: { m_sizemove = true; break; } case WM_EXITSIZEMOVE: { m_sizemove = false; // In case of threaded window, size and move events are not blocked if (m_style & WindowStyle_Threaded) break; // On vérifie ce qui a changé RECT clientRect, windowRect; GetClientRect(m_handle, &clientRect); GetWindowRect(m_handle, &windowRect); Vector2i position(windowRect.left, windowRect.top); if (m_position != position) { m_position = position; WindowEvent event; event.type = WindowEventType_Moved; event.position.x = position.x; event.position.y = position.y; m_parent->PushEvent(event); } Vector2ui size(clientRect.right-clientRect.left, clientRect.bottom-clientRect.top); if (m_size != size) { m_size = size; WindowEvent event; event.type = WindowEventType_Resized; event.size.width = size.x; event.size.height = size.y; m_parent->PushEvent(event); } break; } case WM_KEYDOWN: case WM_SYSKEYDOWN: { // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx bool repeated = ((HIWORD(lParam) & KF_REPEAT) != 0); if (m_keyRepeat || !repeated) { WindowEvent event; event.type = WindowEventType_KeyPressed; event.key.code = ConvertVirtualKey(wParam, lParam); event.key.alt = ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0); event.key.control = ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0); event.key.repeated = repeated; event.key.shift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0); event.key.system = (((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0) || ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0)); m_parent->PushEvent(event); } break; } case WM_KEYUP: case WM_SYSKEYUP: { // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx WindowEvent event; event.type = WindowEventType_KeyReleased; event.key.code = ConvertVirtualKey(wParam, lParam); event.key.alt = ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0); event.key.control = ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0); event.key.shift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0); event.key.system = ((GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000)); m_parent->PushEvent(event); break; } case WM_KILLFOCUS: { WindowEvent event; event.type = WindowEventType_LostFocus; m_parent->PushEvent(event); break; } case WM_LBUTTONDBLCLK: { // Cet évènement est généré à la place d'un WM_LBUTTONDOWN lors d'un double-clic. // Comme nous désirons quand même notifier chaque clic, nous envoyons les deux évènements. WindowEvent event; event.mouseButton.button = Mouse::Left; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); event.type = WindowEventType_MouseButtonDoubleClicked; m_parent->PushEvent(event); event.type = WindowEventType_MouseButtonPressed; m_parent->PushEvent(event); break; } case WM_LBUTTONDOWN: { WindowEvent event; event.type = WindowEventType_MouseButtonPressed; event.mouseButton.button = Mouse::Left; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_LBUTTONUP: { WindowEvent event; event.type = WindowEventType_MouseButtonReleased; event.mouseButton.button = Mouse::Left; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_MBUTTONDBLCLK: { WindowEvent event; event.mouseButton.button = Mouse::Middle; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); event.type = WindowEventType_MouseButtonDoubleClicked; m_parent->PushEvent(event); event.type = WindowEventType_MouseButtonPressed; m_parent->PushEvent(event); break; } case WM_MBUTTONDOWN: { WindowEvent event; event.type = WindowEventType_MouseButtonPressed; event.mouseButton.button = Mouse::Middle; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_MBUTTONUP: { WindowEvent event; event.type = WindowEventType_MouseButtonReleased; event.mouseButton.button = Mouse::Middle; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } // Nécessite un appel précédent à TrackMouseEvent (Fait dans WM_MOUSEMOVE) // http://msdn.microsoft.com/en-us/library/windows/desktop/ms645615(v=vs.85).aspx case WM_MOUSELEAVE: { m_mouseInside = false; WindowEvent event; event.type = WindowEventType_MouseLeft; m_parent->PushEvent(event); break; } case WM_MOUSEMOVE: { int currentX = GET_X_LPARAM(lParam); int currentY = GET_Y_LPARAM(lParam); if (!m_mouseInside) { m_mouseInside = true; // Track mouse event to be notified when mouse leaves window TRACKMOUSEEVENT mouseEvent; mouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); mouseEvent.dwFlags = TME_LEAVE; mouseEvent.hwndTrack = m_handle; TrackMouseEvent(&mouseEvent); WindowEvent event; event.type = WindowEventType_MouseEntered; m_parent->PushEvent(event); event.type = WindowEventType_MouseMoved; // Le delta sera 0 event.mouseMove.deltaX = 0; event.mouseMove.deltaY = 0; event.mouseMove.x = currentX; event.mouseMove.y = currentY; m_mousePos.x = currentX; m_mousePos.y = currentY; m_parent->PushEvent(event); break; } // Si la souris n'a pas bougé (Ou qu'on veut ignorer l'évènement) if (m_mousePos.x == currentX && m_mousePos.y == currentY) break; WindowEvent event; event.type = WindowEventType_MouseMoved; event.mouseMove.deltaX = currentX - m_mousePos.x; event.mouseMove.deltaY = currentY - m_mousePos.y; event.mouseMove.x = currentX; event.mouseMove.y = currentY; m_mousePos.x = currentX; m_mousePos.y = currentY; m_parent->PushEvent(event); break; } case WM_MOUSEWHEEL: { if (m_smoothScrolling) { WindowEvent event; event.type = WindowEventType_MouseWheelMoved; event.mouseWheel.delta = static_cast(GET_WHEEL_DELTA_WPARAM(wParam)) / WHEEL_DELTA; event.mouseWheel.x = GET_X_LPARAM(lParam); event.mouseWheel.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); } else { m_scrolling += GET_WHEEL_DELTA_WPARAM(wParam); if (std::abs(m_scrolling) >= WHEEL_DELTA) { WindowEvent event; event.type = WindowEventType_MouseWheelMoved; event.mouseWheel.delta = static_cast(m_scrolling / WHEEL_DELTA); event.mouseWheel.x = GET_X_LPARAM(lParam); event.mouseWheel.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); m_scrolling %= WHEEL_DELTA; } } break; } case WM_MOVE: { if (m_sizemove && (m_style & WindowStyle_Threaded) == 0) break; RECT windowRect; GetWindowRect(m_handle, &windowRect); Vector2i position(windowRect.left, windowRect.top); if (m_position != position) { m_position = position; WindowEvent event; event.type = WindowEventType_Moved; event.position.x = position.x; event.position.y = position.y; m_parent->PushEvent(event); } break; } case WM_RBUTTONDBLCLK: { WindowEvent event; event.mouseButton.button = Mouse::Right; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); event.type = WindowEventType_MouseButtonDoubleClicked; m_parent->PushEvent(event); event.type = WindowEventType_MouseButtonPressed; m_parent->PushEvent(event); break; } case WM_RBUTTONDOWN: { WindowEvent event; event.type = WindowEventType_MouseButtonPressed; event.mouseButton.button = Mouse::Right; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_RBUTTONUP: { WindowEvent event; event.type = WindowEventType_MouseButtonReleased; event.mouseButton.button = Mouse::Right; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_SETFOCUS: { WindowEvent event; event.type = WindowEventType_GainedFocus; m_parent->PushEvent(event); break; } case WM_SIZE: { if (m_sizemove && (m_style & WindowStyle_Threaded) == 0) break; if (wParam == SIZE_MINIMIZED) break; RECT rect; GetClientRect(m_handle, &rect); Vector2ui size(rect.right-rect.left, rect.bottom-rect.top); // On récupère uniquement la taille de la zone client if (m_size == size) break; m_size = size; WindowEvent event; event.type = WindowEventType_Resized; event.size.width = size.x; event.size.height = size.y; m_parent->PushEvent(event); break; } case WM_UNICHAR: { // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646288(v=vs.85).aspx if (wParam != UNICODE_NOCHAR) { bool repeated = ((HIWORD(lParam) & KF_REPEAT) != 0); if (m_keyRepeat || !repeated) { WindowEvent event; event.type = WindowEventType_TextEntered; event.text.character = static_cast(wParam); event.text.repeated = repeated; m_parent->PushEvent(event); } return true; } } case WM_XBUTTONDBLCLK: { WindowEvent event; if (HIWORD(wParam) == XBUTTON1) event.mouseButton.button = Mouse::XButton1; else event.mouseButton.button = Mouse::XButton2; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); event.type = WindowEventType_MouseButtonDoubleClicked; m_parent->PushEvent(event); event.type = WindowEventType_MouseButtonPressed; m_parent->PushEvent(event); break; } case WM_XBUTTONDOWN: { WindowEvent event; event.type = WindowEventType_MouseButtonPressed; if (HIWORD(wParam) == XBUTTON1) event.mouseButton.button = Mouse::XButton1; else event.mouseButton.button = Mouse::XButton2; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } case WM_XBUTTONUP: { WindowEvent event; event.type = WindowEventType_MouseButtonReleased; if (HIWORD(wParam) == XBUTTON1) event.mouseButton.button = Mouse::XButton1; else event.mouseButton.button = Mouse::XButton2; event.mouseButton.x = GET_X_LPARAM(lParam); event.mouseButton.y = GET_Y_LPARAM(lParam); m_parent->PushEvent(event); break; } default: break; } } #if NAZARA_PLATFORM_WINDOWS_DISABLE_MENU_KEYS // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx if (message == WM_SYSCOMMAND && wParam == SC_KEYMENU) return true; #endif return false; } void WindowImpl::PrepareWindow(bool fullscreen) { if (fullscreen) { SetForegroundWindow(m_handle); ShowWindow(m_handle, SW_SHOW); } // Cache window position/size after creation RECT clientRect, windowRect; GetClientRect(m_handle, &clientRect); GetWindowRect(m_handle, &windowRect); m_position.Set(windowRect.left, windowRect.top); m_size.Set(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top); } bool WindowImpl::Initialize() { // Nous devons faire un type Unicode pour que la fenêtre le soit également // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633574(v=vs.85).aspx WNDCLASSW windowClass; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hbrBackground = nullptr; windowClass.hCursor = nullptr; // Le curseur est définit dynamiquement windowClass.hIcon = nullptr; // L'icône est définie dynamiquement windowClass.hInstance = GetModuleHandle(nullptr); windowClass.lpfnWndProc = MessageHandler; windowClass.lpszClassName = className; windowClass.lpszMenuName = nullptr; windowClass.style = CS_DBLCLKS; // Gestion du double-clic return RegisterClassW(&windowClass) != 0; } void WindowImpl::Uninitialize() { UnregisterClassW(className, GetModuleHandle(nullptr)); } Keyboard::Key WindowImpl::ConvertVirtualKey(WPARAM key, LPARAM flags) { switch (key) { case VK_CONTROL: return (HIWORD(flags) & KF_EXTENDED) ? Keyboard::RControl : Keyboard::LControl; case VK_MENU: return (HIWORD(flags) & KF_EXTENDED) ? Keyboard::RAlt : Keyboard::LAlt; case VK_SHIFT: { static UINT scancode = MapVirtualKey(VK_SHIFT, MAPVK_VK_TO_VSC); return (((flags >> 16) & 0xFF) == scancode) ? Keyboard::LShift : Keyboard::RShift; } case 0x30: return Keyboard::Num0; case 0x31: return Keyboard::Num1; case 0x32: return Keyboard::Num2; case 0x33: return Keyboard::Num3; case 0x34: return Keyboard::Num4; case 0x35: return Keyboard::Num5; case 0x36: return Keyboard::Num6; case 0x37: return Keyboard::Num7; case 0x38: return Keyboard::Num8; case 0x39: return Keyboard::Num9; case 0x41: return Keyboard::A; case 0x42: return Keyboard::B; case 0x43: return Keyboard::C; case 0x44: return Keyboard::D; case 0x45: return Keyboard::E; case 0x46: return Keyboard::F; case 0x47: return Keyboard::G; case 0x48: return Keyboard::H; case 0x49: return Keyboard::I; case 0x4A: return Keyboard::J; case 0x4B: return Keyboard::K; case 0x4C: return Keyboard::L; case 0x4D: return Keyboard::M; case 0x4E: return Keyboard::N; case 0x4F: return Keyboard::O; case 0x50: return Keyboard::P; case 0x51: return Keyboard::Q; case 0x52: return Keyboard::R; case 0x53: return Keyboard::S; case 0x54: return Keyboard::T; case 0x55: return Keyboard::U; case 0x56: return Keyboard::V; case 0x57: return Keyboard::W; case 0x58: return Keyboard::X; case 0x59: return Keyboard::Y; case 0x5A: return Keyboard::Z; case VK_ADD: return Keyboard::Add; case VK_BACK: return Keyboard::Backspace; case VK_BROWSER_BACK: return Keyboard::Browser_Back; case VK_BROWSER_FAVORITES: return Keyboard::Browser_Favorites; case VK_BROWSER_FORWARD: return Keyboard::Browser_Forward; case VK_BROWSER_HOME: return Keyboard::Browser_Home; case VK_BROWSER_REFRESH: return Keyboard::Browser_Refresh; case VK_BROWSER_SEARCH: return Keyboard::Browser_Search; case VK_BROWSER_STOP: return Keyboard::Browser_Stop; case VK_CAPITAL: return Keyboard::CapsLock; case VK_CLEAR: return Keyboard::Clear; case VK_DECIMAL: return Keyboard::Decimal; case VK_DELETE: return Keyboard::Delete; case VK_DIVIDE: return Keyboard::Divide; case VK_DOWN: return Keyboard::Down; case VK_END: return Keyboard::End; case VK_ESCAPE: return Keyboard::Escape; case VK_F1: return Keyboard::F1; case VK_F2: return Keyboard::F2; case VK_F3: return Keyboard::F3; case VK_F4: return Keyboard::F4; case VK_F5: return Keyboard::F5; case VK_F6: return Keyboard::F6; case VK_F7: return Keyboard::F7; case VK_F8: return Keyboard::F8; case VK_F9: return Keyboard::F9; case VK_F10: return Keyboard::F10; case VK_F11: return Keyboard::F11; case VK_F12: return Keyboard::F12; case VK_F13: return Keyboard::F13; case VK_F14: return Keyboard::F14; case VK_F15: return Keyboard::F15; case VK_HOME: return Keyboard::Home; case VK_INSERT: return Keyboard::Insert; case VK_LEFT: return Keyboard::Left; case VK_LWIN: return Keyboard::LSystem; case VK_MEDIA_NEXT_TRACK: return Keyboard::Media_Next; case VK_MEDIA_PLAY_PAUSE: return Keyboard::Media_Play; case VK_MEDIA_PREV_TRACK: return Keyboard::Media_Previous; case VK_MEDIA_STOP: return Keyboard::Media_Stop; case VK_MULTIPLY: return Keyboard::Multiply; case VK_NEXT: return Keyboard::PageDown; case VK_NUMPAD0: return Keyboard::Numpad0; case VK_NUMPAD1: return Keyboard::Numpad1; case VK_NUMPAD2: return Keyboard::Numpad2; case VK_NUMPAD3: return Keyboard::Numpad3; case VK_NUMPAD4: return Keyboard::Numpad4; case VK_NUMPAD5: return Keyboard::Numpad5; case VK_NUMPAD6: return Keyboard::Numpad6; case VK_NUMPAD7: return Keyboard::Numpad7; case VK_NUMPAD8: return Keyboard::Numpad8; case VK_NUMPAD9: return Keyboard::Numpad9; case VK_NUMLOCK: return Keyboard::NumLock; case VK_OEM_1: return Keyboard::Semicolon; case VK_OEM_2: return Keyboard::Slash; case VK_OEM_3: return Keyboard::Tilde; case VK_OEM_4: return Keyboard::LBracket; case VK_OEM_5: return Keyboard::Backslash; case VK_OEM_6: return Keyboard::RBracket; case VK_OEM_7: return Keyboard::Quote; case VK_OEM_COMMA: return Keyboard::Comma; case VK_OEM_MINUS: return Keyboard::Dash; case VK_OEM_PERIOD: return Keyboard::Period; case VK_OEM_PLUS: return Keyboard::Equal; case VK_RIGHT: return Keyboard::Right; case VK_PRIOR: return Keyboard::PageUp; case VK_PAUSE: return Keyboard::Pause; case VK_PRINT: return Keyboard::Print; case VK_SCROLL: return Keyboard::ScrollLock; case VK_SNAPSHOT: return Keyboard::PrintScreen; case VK_SUBTRACT: return Keyboard::Subtract; case VK_RETURN: return Keyboard::Return; case VK_RWIN: return Keyboard::RSystem; case VK_SPACE: return Keyboard::Space; case VK_TAB: return Keyboard::Tab; case VK_UP: return Keyboard::Up; case VK_VOLUME_DOWN: return Keyboard::Volume_Down; case VK_VOLUME_MUTE: return Keyboard::Volume_Mute; case VK_VOLUME_UP: return Keyboard::Volume_Up; default: return Keyboard::Undefined; } } LRESULT CALLBACK WindowImpl::MessageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { WindowImpl* me; if (message == WM_CREATE) { me = static_cast(reinterpret_cast(lParam)->lpCreateParams); SetWindowLongPtr(window, GWL_USERDATA, reinterpret_cast(me)); } else me = reinterpret_cast(GetWindowLongPtr(window, GWL_USERDATA)); if (me) { if (me->HandleMessage(window, message, wParam, lParam)) return 0; else if (me->m_callback) return CallWindowProcW(reinterpret_cast(me->m_callback), window, message, wParam, lParam); } return DefWindowProcW(window, message, wParam, lParam); } UInt32 WindowImpl::RetrieveStyle(HWND handle) { UInt32 style = 0; LONG_PTR winStyle = GetWindowLongPtr(handle, GWL_STYLE); // http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx if (winStyle & WS_CAPTION) { style |= WindowStyle_Titlebar; if (winStyle & WS_SYSMENU) style |= WindowStyle_Closable; if (winStyle & WS_MAXIMIZEBOX) style |= WindowStyle_Resizable; } if (winStyle & WS_SIZEBOX) style |= WindowStyle_Resizable; // Pour déterminer si la fenêtre est en plein écran, il suffit de vérifier si elle recouvre l'écran DEVMODE mode; mode.dmSize = sizeof(DEVMODE); EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mode); RECT rect; if (GetWindowRect(handle, &rect)) { if (static_cast(rect.right-rect.left) == mode.dmPelsWidth && static_cast(rect.bottom-rect.top) == mode.dmPelsHeight) style |= WindowStyle_Fullscreen; } return style; } void WindowImpl::WindowThread(HWND* handle, DWORD styleEx, const String& title, DWORD style, bool fullscreen, const Rectui& dimensions, WindowImpl* window, Mutex* mutex, ConditionVariable* condition) { HWND& winHandle = *handle; winHandle = CreateWindowExW(styleEx, className, title.GetWideString().data(), style, dimensions.x, dimensions.y, dimensions.width, dimensions.height, nullptr, nullptr, GetModuleHandle(nullptr), window); if (winHandle) window->PrepareWindow(fullscreen); mutex->Lock(); condition->Signal(); mutex->Unlock(); // mutex and condition may be destroyed after this line if (!winHandle) return; while (window->m_threadActive) window->ProcessEvents(true); DestroyWindow(winHandle); } }