325 lines
7 KiB
C++
325 lines
7 KiB
C++
|
|
#include <Windows.h>
|
|
|
|
|
|
#include <Spectre/Display/Display.h>
|
|
#include <Spectre/System/Log.h>
|
|
#include "Win32Application.h"
|
|
#include "Win32Internal.h"
|
|
#include "Win32Display.h"
|
|
|
|
namespace sp {
|
|
|
|
#define WND_CLASSNAME "SPECTRE_WIN32_WNDCLASS"
|
|
|
|
static bool firstTime = true;
|
|
|
|
Win32Display::Win32Display() :
|
|
m_handle (NULL),
|
|
m_icon (0),
|
|
m_cursor (0),
|
|
m_inResizeModalLoop (false),
|
|
m_minSize (200, 200)
|
|
{
|
|
}
|
|
|
|
Win32Display::~Win32Display()
|
|
{
|
|
if (m_icon) {
|
|
DestroyIcon(m_icon);
|
|
}
|
|
|
|
if (m_handle) {
|
|
DestroyWindow(m_handle);
|
|
}
|
|
}
|
|
|
|
bool Win32Display::create(DisplayDescription description)
|
|
{
|
|
DWORD flags = getWin32Flags(description.decoration);
|
|
int x, y;
|
|
|
|
if (firstTime) {
|
|
registerClass();
|
|
firstTime = false;
|
|
}
|
|
|
|
centerWindow(x, y, description.mode.width, description.mode.height);
|
|
|
|
// Create window.
|
|
m_handle = CreateWindowExA(0, WND_CLASSNAME, "", flags,
|
|
x, y, description.mode.width, description.mode.height,
|
|
NULL, NULL, GetModuleHandle(NULL), (LPVOID) this);
|
|
|
|
if (!m_handle) {
|
|
Log::error("Win32 - Could not create window: %s", Win32GetMessage(GetLastError()));
|
|
return false;
|
|
}
|
|
|
|
setSize(description.mode.width, description.mode.height);
|
|
|
|
// Store the size for use later.
|
|
m_size = getSize();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Win32Display::destroy()
|
|
{
|
|
if (m_handle) {
|
|
DestroyWindow(m_handle);
|
|
m_handle = NULL;
|
|
}
|
|
}
|
|
|
|
void* Win32Display::getHandle() const
|
|
{
|
|
return m_handle;
|
|
}
|
|
|
|
bool Win32Display::isValid()
|
|
{
|
|
return m_handle != NULL;
|
|
}
|
|
|
|
void Win32Display::setSize(unsigned int width, unsigned int height)
|
|
{
|
|
int w, h;
|
|
RECT rect = {0, 0, (LONG) width, (LONG) height};
|
|
AdjustWindowRect(&rect, GetWindowLong(m_handle, GWL_STYLE), false);
|
|
|
|
w = rect.right - rect.left;
|
|
h = rect.bottom - rect.top;
|
|
|
|
::SetWindowPos(m_handle, NULL, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
|
|
Vector2u Win32Display::getSize() const
|
|
{
|
|
RECT rect;
|
|
Vector2u size;
|
|
|
|
if (GetClientRect(m_handle, &rect)) {
|
|
size.x = rect.right - rect.left;
|
|
size.y = rect.bottom - rect.top;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void Win32Display::setPosition(unsigned int x, unsigned int y)
|
|
{
|
|
::SetWindowPos(m_handle, NULL, x, y, 0, 0, SWP_NOSIZE);
|
|
}
|
|
|
|
void Win32Display::setCaption(const std::string& caption)
|
|
{
|
|
::SetWindowText(m_handle, caption.c_str());
|
|
}
|
|
|
|
void Win32Display::showCursor(bool value)
|
|
{
|
|
if (value) {
|
|
m_cursor = ::LoadCursor(NULL, IDC_ARROW);
|
|
} else {
|
|
m_cursor = 0;
|
|
}
|
|
|
|
::SetCursor(m_cursor);
|
|
}
|
|
|
|
void Win32Display::grabCursor(bool value)
|
|
{
|
|
if (value) {
|
|
RECT rect;
|
|
GetClientRect(m_handle, &rect);
|
|
MapWindowPoints(m_handle, NULL, reinterpret_cast<LPPOINT>(&rect), 2);
|
|
ClipCursor(&rect);
|
|
} else {
|
|
ClipCursor(NULL);
|
|
}
|
|
}
|
|
|
|
void Win32Display::setIcon(unsigned int width, unsigned int height, const uint8_t *pixels)
|
|
{
|
|
::HDC hdc;
|
|
::ICONINFO ii;
|
|
::BITMAPV5HEADER bi;
|
|
::HBITMAP bmp_color, bmp_mask;
|
|
unsigned char *bmp_data = NULL;
|
|
|
|
::ZeroMemory(&bi, sizeof(bi));
|
|
bi.bV5Size = sizeof(bi);
|
|
bi.bV5Width = width;
|
|
bi.bV5Height = height;
|
|
bi.bV5Planes = 1;
|
|
bi.bV5BitCount = 32;
|
|
bi.bV5Compression = BI_RGB;
|
|
bi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
|
|
bi.bV5Intent = LCS_GM_IMAGES;
|
|
|
|
hdc = ::GetDC(NULL);
|
|
bmp_color = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bi),
|
|
DIB_RGB_COLORS, (void **) &bmp_data, NULL, 0);
|
|
::ReleaseDC(NULL, hdc);
|
|
|
|
if (!bmp_color) {
|
|
Log::error("Win32Display::setIcon() - Failed to create RGBA bitmap");
|
|
return;
|
|
}
|
|
|
|
bmp_mask = CreateBitmap(width, height, 1, 1, NULL);
|
|
if (!bmp_mask) {
|
|
Log::error("Win32Display::setIcon() - Failed to create mask bitmap");
|
|
::DeleteObject(bmp_color);
|
|
return;
|
|
}
|
|
|
|
// pixels are always RGBA, WinApi wants BGRA
|
|
for(int i = 0; i < width * height; i++) {
|
|
bmp_data[i * 4 + 0] = pixels[i * 4 + 2]; // R <- B
|
|
bmp_data[i * 4 + 1] = pixels[i * 4 + 1]; // G <- G
|
|
bmp_data[i * 4 + 2] = pixels[i * 4 + 0]; // B <- R
|
|
bmp_data[i * 4 + 3] = pixels[i * 4 + 3]; // A <- A
|
|
}
|
|
|
|
::ZeroMemory(&ii, sizeof(ii));
|
|
ii.fIcon = TRUE;
|
|
ii.xHotspot = 0;
|
|
ii.yHotspot = 0;
|
|
ii.hbmColor = bmp_color;
|
|
ii.hbmMask = bmp_mask;
|
|
|
|
if (m_icon) {
|
|
::DestroyIcon(m_icon);
|
|
}
|
|
|
|
m_icon = CreateIconIndirect(&ii);
|
|
|
|
if (m_icon) {
|
|
::SendMessage(m_handle, WM_SETICON, ICON_SMALL, (LPARAM) m_icon);
|
|
::SendMessage(m_handle, WM_SETICON, ICON_BIG, (LPARAM) m_icon);
|
|
} else {
|
|
Log::error("Win32Display::setIcon() - Failed to create icon");
|
|
}
|
|
|
|
::DeleteObject(bmp_color);
|
|
::DeleteObject(bmp_mask);
|
|
}
|
|
|
|
void Win32Display::registerClass()
|
|
{
|
|
WNDCLASSA wndcl;
|
|
|
|
ZeroMemory(&wndcl, sizeof(wndcl));
|
|
|
|
wndcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
|
wndcl.lpfnWndProc = Win32Display::staticWndProc;
|
|
wndcl.hInstance = ::GetModuleHandle(NULL);
|
|
wndcl.lpszClassName = WND_CLASSNAME;
|
|
|
|
::RegisterClass(&wndcl);
|
|
}
|
|
|
|
DWORD Win32Display::getWin32Flags(unsigned int flags)
|
|
{
|
|
DWORD win32_flags = WS_VISIBLE;
|
|
|
|
if (flags == DisplayDecorate::None) {
|
|
win32_flags |= WS_POPUP;
|
|
} else {
|
|
if (flags & DisplayDecorate::Menu) {
|
|
win32_flags |= WS_CAPTION | WS_MINIMIZEBOX;
|
|
}
|
|
|
|
if (flags & DisplayDecorate::Resize) {
|
|
win32_flags |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
|
}
|
|
|
|
if (flags & DisplayDecorate::Close) {
|
|
win32_flags |= WS_SYSMENU;
|
|
}
|
|
}
|
|
return win32_flags;
|
|
}
|
|
|
|
void Win32Display::centerWindow(int &x, int &y, int width, int height)
|
|
{
|
|
x = (::GetSystemMetrics(SM_CXSCREEN) - width) / 2;
|
|
y = (::GetSystemMetrics(SM_CYSCREEN) - height) / 2;
|
|
}
|
|
|
|
void Win32Display::processResizeMessage(const Vector2u& new_size)
|
|
{
|
|
// Check if the size has actually changed.
|
|
if (m_size != new_size) {
|
|
|
|
// Update the size and notify the higher layer.
|
|
m_size = new_size;
|
|
onReshape(m_size.x, m_size.y);
|
|
}
|
|
}
|
|
|
|
void Win32Display::processMessage(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message) {
|
|
case WM_SETCURSOR :
|
|
if (LOWORD(lParam) == HTCLIENT) {
|
|
::SetCursor(m_cursor);
|
|
}
|
|
break;
|
|
case WM_DESTROY :
|
|
destroy();
|
|
PostQuitMessage(0);
|
|
break;
|
|
case WM_SETFOCUS :
|
|
OutputDebugString("WM_SETFOCUS\n");
|
|
break;
|
|
case WM_KILLFOCUS :
|
|
OutputDebugString("WM_KILLFOCUS\n");
|
|
break;
|
|
case WM_SIZE :
|
|
|
|
if (!m_inResizeModalLoop && (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)) {
|
|
processResizeMessage(getSize());
|
|
}
|
|
break;
|
|
case WM_GETMINMAXINFO :
|
|
{
|
|
MINMAXINFO* info = (MINMAXINFO*)lParam;
|
|
info->ptMinTrackSize.x = m_minSize.x;
|
|
info->ptMinTrackSize.y = m_minSize.y;
|
|
info->ptMaxTrackSize.x = 99999;
|
|
info->ptMaxTrackSize.y = 99999;
|
|
break;
|
|
}
|
|
case WM_ENTERSIZEMOVE :
|
|
m_inResizeModalLoop = true;
|
|
break;
|
|
case WM_EXITSIZEMOVE :
|
|
m_inResizeModalLoop = false;
|
|
processResizeMessage(getSize());
|
|
break;
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK Win32Display::staticWndProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
Win32Display *display;
|
|
|
|
if (message == WM_NCCREATE) {
|
|
|
|
LONG_PTR ptr = (LONG_PTR) ((LPCREATESTRUCT)lParam)->lpCreateParams;
|
|
::SetWindowLong(handle, GWL_USERDATA, ptr);
|
|
|
|
display = (Win32Display*) ptr;
|
|
} else {
|
|
display = (Win32Display*) ::GetWindowLong(handle, GWL_USERDATA);
|
|
}
|
|
|
|
if (display) {
|
|
display->processMessage(message, wParam, lParam);
|
|
}
|
|
return DefWindowProc(handle, message, wParam, lParam);
|
|
}
|
|
|
|
} // namespace sp
|