#include "ehs/io/Window_W32.h" #include "ehs/io/hid/Keyboard.h" #include "ehs/io/hid/Mouse.h" #include "ehs/system/Thread.h" #include namespace ehs { Array Window::windows; LRESULT Window::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Window* win = nullptr; for (UInt_64 i = 0; i < windows.Size(); ++i) { if (hWnd == windows[i]->hdl) win = windows[i]; } if (uMsg == WM_DESTROY) { PostQuitMessage(0); return 0; } if (!win) return DefWindowProcW(hWnd, uMsg, wParam, lParam); if (uMsg == WM_CLOSE) { for (UInt_64 i = 0; i < windows.Size(); ++i) { if (windows[i]->hdl != hWnd) continue; if (windows.Size() > 1) windows.Swap(i, windows.Size() - 1); windows.Pop(); break; } DestroyWindow(hWnd); win->hdl = nullptr; win->Close(); return 0; //return DefWindowProcW(hWnd, WM_QUIT, 0, lParam); } else if (uMsg == WM_SIZE) { Vec2 newSize; newSize[0] = static_cast(lParam); newSize[1] = static_cast(lParam >> 16); win->OnResized(newSize); } else if (uMsg == WM_HIDE) { ShowWindow(hWnd, SW_HIDE); } else if (uMsg == WM_SHOW) { ShowWindow(hWnd, SW_SHOW); } else if (uMsg == WM_HIDE_CURSOR) { while (::ShowCursor(false) >= 0); } else if (uMsg == WM_SHOW_CURSOR) { while (::ShowCursor(true) < 0); } else if (uMsg == WM_PAINT) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW)); EndPaint(hWnd, &ps); } else if (win->HasFocus() && uMsg == WM_INPUT) { UINT dwSize = 0; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)); if (dwSize) { Byte* data = new Byte[dwSize]; if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) { EHS_LOG_INT(LogType::ERR, 0, "Getting raw input returned incorrect size."); delete[] data; return DefWindowProcW(hWnd, uMsg, wParam, lParam); } RAWINPUT* raw = (RAWINPUT*)data; if (raw->header.dwType == RIM_TYPEKEYBOARD) { Keyboard* keyboard = (Keyboard*)win->ih.GetDevice((UInt_64)raw->header.hDevice); if (!keyboard) { UInt_32 bufferSize; GetRawInputDeviceInfoW(raw->header.hDevice, RIDI_DEVICENAME, nullptr, &bufferSize); Char_16* deviceName = new Char_16[bufferSize]; if (GetRawInputDeviceInfoW(raw->header.hDevice, RIDI_DEVICENAME, deviceName, &bufferSize) < 0) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve device name."); return 0; } keyboard = new Keyboard(UTF::To_8(deviceName, (UInt_64)bufferSize), (UInt_64)raw->header.hDevice); win->ih.AddDevice(keyboard); } const Button button = Keyboard::TranslateScanCode(raw->data.keyboard.MakeCode); if (raw->data.keyboard.Message == WM_KEYDOWN || raw->data.keyboard.Message == WM_SYSKEYDOWN) keyboard->ButtonDown(button); else if (raw->data.keyboard.Message == WM_KEYUP || raw->data.keyboard.Message == WM_SYSKEYUP) keyboard->ButtonUp(button); } else if (raw->header.dwType == RIM_TYPEMOUSE) { Mouse* mouse = (Mouse*)win->ih.GetDevice((UInt_64)raw->header.hDevice); if (!mouse) { UInt_32 bufferSize; GetRawInputDeviceInfoW(raw->header.hDevice, RIDI_DEVICENAME, nullptr, &bufferSize); Char_16* deviceName = new Char_16[bufferSize]; if (GetRawInputDeviceInfoW(raw->header.hDevice, RIDI_DEVICENAME, deviceName, &bufferSize) < 0) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve device name."); return 0; } mouse = new Mouse(UTF::To_8(deviceName, (UInt_64)bufferSize), (UInt_64)raw->header.hDevice); win->ih.AddDevice(mouse); } mouse->SetDelta({raw->data.mouse.lLastX, raw->data.mouse.lLastY}); UInt_16 code = raw->data.mouse.usButtonFlags; if (code == RI_MOUSE_LEFT_BUTTON_DOWN) mouse->ButtonDown(Mouse::LMB); else if (code == RI_MOUSE_LEFT_BUTTON_UP) mouse->ButtonUp(Mouse::LMB); else if (code == RI_MOUSE_RIGHT_BUTTON_DOWN) mouse->ButtonDown(Mouse::RMB); else if (code == RI_MOUSE_RIGHT_BUTTON_UP) mouse->ButtonUp(Mouse::RMB); else if (code == RI_MOUSE_MIDDLE_BUTTON_DOWN) mouse->ButtonDown(Mouse::MMB); else if (code == RI_MOUSE_MIDDLE_BUTTON_UP) mouse->ButtonUp(Mouse::MMB); else if (code == RI_MOUSE_BUTTON_4_DOWN) mouse->ButtonDown(Mouse::Four); else if (code == RI_MOUSE_BUTTON_4_UP) mouse->ButtonUp(Mouse::Four); else if (code == RI_MOUSE_BUTTON_5_DOWN) mouse->ButtonDown(Mouse::Five); else if (code == RI_MOUSE_BUTTON_5_UP) mouse->ButtonUp(Mouse::Five); else if (code == RI_MOUSE_WHEEL) { if (raw->data.mouse.usButtonData < 0) mouse->ButtonDown(Mouse::ScrollDown); else if (raw->data.mouse.usButtonData > 0) mouse->ButtonDown(Mouse::ScrollUp); } else if (code == RI_MOUSE_HWHEEL) { if (raw->data.mouse.usButtonData < 0) mouse->ButtonDown(Mouse::ScrollLeft); else if (raw->data.mouse.usButtonData > 0) mouse->ButtonDown(Mouse::ScrollRight); } } delete[] data; } } else if (uMsg == WM_KILLFOCUS) { win->focused = false; if (!win->cursorVisible) { win->SendMsg(WM_SHOW_CURSOR, 0, 0); } if (win->cursorConstrained) { if (!ClipCursor(nullptr)) EHS_LOG_INT(LogType::ERR, 0, "Failed to free cursor after losing focus with error #" + Str_8::FromNum(GetLastError()) + "."); } win->ih.ResetAllStates(); } else if (uMsg == WM_SETFOCUS) { win->focused = true; if (!win->cursorVisible) win->SendMsg(WM_HIDE_CURSOR, 0, 0); if (win->cursorConstrained) { RECT client = {}; if (!GetClientRect(win->GetHdl(), &client)) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve client scale with error #" + Str_8::FromNum(GetLastError()) + "."); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } POINT pos = {}; if (!ClientToScreen(win->GetHdl(), &pos)) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve client's absolute position with error #" + Str_8::FromNum(GetLastError()) + "."); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } client.left = pos.x; client.top = pos.y; POINT scale = {client.right, client.bottom}; if (!ClientToScreen(win->GetHdl(), &scale)) { EHS_LOG_INT(LogType::ERR, 2, "Failed to retrieve client's absolute scale with error #" + Str_8::FromNum(GetLastError()) + "."); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } client.right = scale.x; client.bottom = scale.y; if (!ClipCursor(&client)) { EHS_LOG_INT(LogType::ERR, 3, "Failed to confine cursor with error #" + Str_8::FromNum(GetLastError()) + "."); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } } } else if (uMsg == WM_MOUSEMOVE) { win->cursorPos.x = ((SInt_16*)&lParam)[0]; win->cursorPos.y = ((SInt_16*)&lParam)[1]; } return DefWindowProcW(hWnd, uMsg, wParam, lParam); } Window::~Window() { if (!created) return; SendMsg(WM_CLOSE, 0, 0); } Window::Window() : owner(0), instance(nullptr), hdl(nullptr) { } Window::Window(const Window& win) : BaseWindow(win), owner(0), instance(nullptr), hdl(nullptr) { } Window& Window::operator=(const Window& win) { if (&win == this) return *this; BaseWindow::operator=(win); owner = 0; created = false; focused = false; instance = nullptr; hdl = nullptr; return* this; } bool Window::Poll() { ih.Poll(); MSG msg = {}; while (PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) { if (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } else return false; } BaseWindow::Poll(); return true; } void Window::Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) { Create_16(UTF::To_16(title), pos, scale); } void Window::Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) { if (created) return; owner = Thread::GetCurrentId(); instance = GetModuleHandleW(nullptr); WNDCLASSEXW wcex = {}; wcex.cbSize = sizeof(wcex); wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wcex.lpfnWndProc = &Window::WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = instance; wcex.hIcon = LoadIcon(nullptr, IDI_APPLICATION); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hIconSm = LoadIcon(nullptr, IDI_WINLOGO); wcex.hbrBackground = nullptr; wcex.lpszMenuName = nullptr; wcex.lpszClassName = title; if (!RegisterClassExW(&wcex)) EHS_LOG_INT(LogType::ERR, 0, "Failed to register window."); hdl = CreateWindowExW( 0, title, title, WS_OVERLAPPEDWINDOW, pos.x, pos.y, scale.x, scale.y, nullptr, nullptr, instance, nullptr ); if (!hdl) { EHS_LOG_INT(LogType::ERR, 1, "Failed to create window."); return; } RECT tmp = { 0, 0, static_cast(scale.x), static_cast(scale.y) }; AdjustWindowRectEx(&tmp, WS_OVERLAPPEDWINDOW, false, 0); SetWindowPos(hdl, nullptr, 0, 0, tmp.right - tmp.left, tmp.bottom - tmp.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); RAWINPUTDEVICE rid[2]; rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; rid[0].dwFlags = 0; rid[0].hwndTarget = hdl; rid[1].usUsagePage = HID_USAGE_PAGE_GENERIC; rid[1].usUsage = HID_USAGE_GENERIC_KEYBOARD; rid[1].dwFlags = 0; rid[1].hwndTarget = hdl; if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == false) { EHS_LOG_INT(LogType::ERR, 2, "Failed to register raw input devices."); return; } windows.Push(this); created = true; OnCreated(); } void Window::Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) { Create_16(UTF::To_16(title), pos, scale); } void Window::Use(HWND windowHdl) { hdl = windowHdl; } void Window::Close() { if (hdl) SendMsg(WM_CLOSE, 0, 0); created = false; } void Window::Show() { SendMsg(WM_SHOW, 0, 0); } void Window::Hide() { SendMsg(WM_HIDE, 0, 0); } void Window::SetTitle_32(const Str_32& title) { if (!SetWindowTextW(hdl, UTF::To_16(title))) EHS_LOG_INT(LogType::ERR, 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); } Str_32 Window::GetTitle_32() const { int size = GetWindowTextLengthW(hdl); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } Char_16* buffer = new Char_16[size]; size = GetWindowTextW(hdl, buffer, size); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } return UTF::To_32(buffer, Str_16::Len(buffer)); } void Window::SetTitle_16(const Str_16& title) { if (!SetWindowTextW(hdl, title)) EHS_LOG_INT(LogType::ERR, 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); } Str_16 Window::GetTitle_16() const { int size = GetWindowTextLengthW(hdl); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } Char_16* buffer = new Char_16[size]; size = GetWindowTextW(hdl, buffer, size); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } return {buffer}; } void Window::SetTitle_8(const Str_8& title) { if (!SetWindowTextW(hdl, UTF::To_16(title))) EHS_LOG_INT(LogType::ERR, 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); } Str_8 Window::GetTitle_8() const { int size = GetWindowTextLengthW(hdl); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } Char_16* buffer = new Char_16[size]; size = GetWindowTextW(hdl, buffer, size); if (!size) { DWORD err = GetLastError(); if (err) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); return {}; } } return UTF::To_8(buffer, Str_16::Len(buffer)); } void Window::SetIcon(const Str_8& filePath) { Handle icon = LoadImageW(nullptr, UTF::To_16(filePath), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); if (!icon) { EHS_LOG_INT(LogType::ERR, 0, "Failed to load icon at file path, \"" + filePath + "\" with error #" + Str_8::FromNum(GetLastError()) + "."); return; } SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)icon); SendMsg(WM_SETICON, ICON_BIG, (LPARAM)icon); } HWND Window::GetHdl() const { return hdl; } HWND Window::GetAvailableHdl() { for (UInt_64 i = 0; i < windows.Size(); ++i) if (windows[i]) return windows[i]->hdl; return nullptr; } HINSTANCE Window::GetInst() const { return instance; } void Window::ToggleEnabled(bool toggle) { if (!created) return; EnableWindow(hdl, toggle); } bool Window::IsEnabled() { if (!created) return false; return IsWindowEnabled(hdl); } void Window::SetPos(const Vec2_s32& newPos) { if (!created) return; SetWindowPos(hdl, nullptr, newPos.x, newPos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } Vec2_s32 Window::GetPos() const { if (!created) return {}; RECT tmp = {}; GetWindowRect(hdl, &tmp); return {(Int_32)tmp.left, (Int_32)tmp.top}; } void Window::OnResized(const Vec2& newSize) { } void Window::SetScale(const Vec2_u32& newScale) { if (!created) return; RECT rect = { 0, 0, static_cast(newScale[0]), static_cast(newScale[1]) }; DWORD exStyle = (DWORD)GetWindowLongPtr(hdl, GWL_EXSTYLE); DWORD style = (DWORD)GetWindowLongPtr(hdl, GWL_STYLE); AdjustWindowRectEx(&rect, style, false, exStyle); SetWindowPos(hdl, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } Vec2_u32 Window::GetScale() const { if (!created) return {}; RECT tmp = {}; GetClientRect(hdl, &tmp); return {(UInt_32)(tmp.right - tmp.left), (UInt_32)(tmp.bottom - tmp.top)}; } void Window::ShowCursor(bool toggle) { SendMsg(toggle ? WM_SHOW_CURSOR : WM_HIDE_CURSOR, 0, 0); cursorVisible = toggle; } void Window::ConstrainCursor(const bool toggle) { if (toggle) { RECT client = {}; if (!GetClientRect(GetHdl(), &client)) { EHS_LOG_INT(LogType::ERR, 0, "Failed to retrieve client scale with error #" + Str_8::FromNum(GetLastError()) + "."); return; } POINT pos = {}; if (!ClientToScreen(GetHdl(), &pos)) { EHS_LOG_INT(LogType::ERR, 1, "Failed to retrieve client's absolute position with error #" + Str_8::FromNum(GetLastError()) + "."); return; } client.left = pos.x; client.top = pos.y; POINT scale = {client.right, client.bottom}; if (!ClientToScreen(GetHdl(), &scale)) { EHS_LOG_INT(LogType::ERR, 2, "Failed to retrieve client's absolute scale with error #" + Str_8::FromNum(GetLastError()) + "."); return; } client.right = scale.x; client.bottom = scale.y; if (!ClipCursor(&client)) { EHS_LOG_INT(LogType::ERR, 3, "Failed to confine cursor with error #" + Str_8::FromNum(GetLastError()) + "."); return; } } else { ClipCursor(nullptr); } cursorConstrained = toggle; } Serializer Window::GetClipboard() { if (!OpenClipboard(hdl)) { EHS_LOG_INT(LogType::ERR, 0, "Failed to open Clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return {}; } HANDLE hData = GetClipboardData(CF_TEXT); if (!hData) { EHS_LOG_INT(LogType::ERR, 1, "Failed to get clipboard handle with error #" + Str_8::FromNum(GetLastError()) + "."); if (!CloseClipboard()) EHS_LOG_INT(LogType::ERR, 4, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return {}; } Char_8* text = (Char_8 *)GlobalLock(hData); if (!text) { EHS_LOG_INT(LogType::ERR, 2, "Failed to get clipboard data with error #" + Str_8::FromNum(GetLastError()) + "."); return {}; } Serializer data(Endianness::LE, (Byte *)text, Str_8::Len(text)); if (!GlobalUnlock(hData)) { EHS_LOG_INT(LogType::ERR, 3, "Failed to free clipboard data with error #" + Str_8::FromNum(GetLastError()) + "."); return data; } if (!CloseClipboard()) { EHS_LOG_INT(LogType::ERR, 4, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return data; } EHS_LOG_SUCCESS(); return data; } void Window::SetClipboard(Serializer data) { if (!OpenClipboard(hdl)) { EHS_LOG_INT(LogType::ERR, 0, "Failed to open Clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } if (!EmptyClipboard()) { EHS_LOG_INT(LogType::ERR, 1, "Failed to empty clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); if (!CloseClipboard()) EHS_LOG_INT(LogType::ERR, 6, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, data.Size()); if (!hGlob) { EHS_LOG_INT(LogType::ERR, 2, "Failed to allocate clipboard data with error #" + Str_8::FromNum(GetLastError()) + "."); if (!CloseClipboard()) EHS_LOG_INT(LogType::ERR, 6, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } Byte* result = (Byte *)GlobalLock(hGlob); if (!result) { EHS_LOG_INT(LogType::ERR, 3, "Failed to lock data with error #" + Str_8::FromNum(GetLastError()) + "."); GlobalFree(hGlob); if (!CloseClipboard()) EHS_LOG_INT(LogType::ERR, 6, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } Util::Copy(result, &data[0], data.Size()); GlobalUnlock(hGlob); HANDLE hData = SetClipboardData(CF_TEXT, hGlob); if (!hData) { EHS_LOG_INT(LogType::ERR, 5, "Failed to get clipboard handle with error #" + Str_8::FromNum(GetLastError()) + "."); GlobalFree(hGlob); if (!CloseClipboard()) EHS_LOG_INT(LogType::ERR, 6, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } if (!CloseClipboard()) { EHS_LOG_INT(LogType::ERR, 6, "Failed to close clipboard with error #" + Str_8::FromNum(GetLastError()) + "."); return; } EHS_LOG_SUCCESS(); } void Window::SetCursorImg(CursorImg img) { } void Window::SendMsg(const UINT msg, const WPARAM wParam, const LPARAM lParam) { if (!hdl) return; if (Thread::GetCurrentId() == owner) PostMessageW(hdl, msg, wParam, lParam); else SendMessageW(hdl, msg, wParam, lParam); } }