EHS/src/io/Window_W32.cpp

834 lines
19 KiB
C++

#include "ehs/io/Window_W32.h"
#include "ehs/io/hid/Keyboard.h"
#include "ehs/io/hid/Mouse.h"
#include "ehs/system/Thread.h"
#include <hidusage.h>
namespace ehs
{
Array<Window*> 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<UInt_16> newSize;
newSize[0] = static_cast<UInt_16>(lParam);
newSize[1] = static_cast<UInt_16>(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<LONG>(scale.x),
static_cast<LONG>(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<UInt_32>& newSize)
{
}
void Window::SetScale(const Vec2_u32& newScale)
{
if (!created)
return;
RECT rect = {
0,
0,
static_cast<LONG>(newScale[0]),
static_cast<LONG>(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<UInt_64> 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<UInt_64> 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<UInt_64> 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);
}
}