#include #include #include #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); } Vector2u Win32Display::getPosition() const { RECT r; GetWindowRect(m_handle, &r); return Vector2u(r.left, r.top); } 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(&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(&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