diff options
author | chai <chaifix@163.com> | 2018-08-07 21:53:55 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2018-08-07 21:53:55 +0800 |
commit | 926584873cd9a83458ac482c02902f4eedb1e9d3 (patch) | |
tree | 2ed019c5b8e6f391ae7a0268a200b51b7ffe293e /ui/win32.h |
Diffstat (limited to 'ui/win32.h')
-rw-r--r-- | ui/win32.h | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/ui/win32.h b/ui/win32.h new file mode 100644 index 0000000..ba9e43c --- /dev/null +++ b/ui/win32.h @@ -0,0 +1,331 @@ +// ui - Basic User Interface library to do experiments -*- C++ -*- +// Copyright (C) 2010, 2012 David Capello +// +// Distributed under the terms of the New BSD License, +// see LICENSE.md for more details. + +#ifndef UI_WIN32_HEADER_FILE_INCLUDED +#define UI_WIN32_HEADER_FILE_INCLUDED + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 // From Windows 2000 +#endif + +#include "mt/thread.h" +#include <exception> +#include <windows.h> + +namespace ui { + + namespace win32 { + + ////////////////////////////////////////////////////////////////////// + // win32_details namespace + + class win32_details { + + template<class WindowType> + static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WindowType* wnd = (WindowType*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (wnd != NULL) { + LRESULT result; + if (wnd->process_message(msg, wparam, lparam, result)) + return result; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + public: + + template<class WindowType> + static void register_window_class() { + HINSTANCE hinstance = ::GetModuleHandle(NULL); + LPCTSTR class_name = "ui_window_class"; + WNDCLASSEX wcex; + + if (!::GetClassInfoEx(hinstance, class_name, &wcex)) { + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = &wnd_proc<WindowType>; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hinstance; + wcex.hIcon = (HICON)NULL; + wcex.hCursor = (HCURSOR)::LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wcex.lpszMenuName = (LPCTSTR)NULL; + wcex.lpszClassName = class_name; + wcex.hIconSm = NULL; + + if (RegisterClassEx(&wcex) == 0) + //throw std::exception("Cannot register win32 window class"); + throw std::exception(); + } + } + + template<class WindowType> + static HWND create_window(WindowType* wnd, int width, int height) { + HINSTANCE hinstance = ::GetModuleHandle(NULL); + HWND hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, + "ui_window_class", "", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + // CW_USEDEFAULT, CW_USEDEFAULT, + width, height, + (HWND)NULL, + (HMENU)NULL, + hinstance, + NULL); + if (hwnd != NULL) { + // Set the pointer in the user-data field of the window (TODO: use an ATOM) + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)wnd); + } + return hwnd; + } + + }; + + ////////////////////////////////////////////////////////////////////// + // win32_bitmap class + + class win32_bitmap { + HBITMAP m_hbitmap; + HDC m_hdc; + + public: + + win32_bitmap(int width, int height) { + assert(width > 0 && height > 0); + + BITMAPINFOHEADER bhdr; + bhdr.biSize = sizeof(BITMAPINFOHEADER); + bhdr.biWidth = width; + bhdr.biHeight = -height; + bhdr.biPlanes = 1; + bhdr.biBitCount = 32; // 32 bpp + bhdr.biCompression = BI_RGB; + bhdr.biSizeImage = 0; + bhdr.biXPelsPerMeter = 0; + bhdr.biYPelsPerMeter = 0; + bhdr.biClrUsed = 0; + bhdr.biClrImportant = 0; + + BITMAPINFO binf; + RGBQUAD dummy = { 0, 0, 0, 0 }; + binf.bmiColors[0] = dummy; + binf.bmiHeader = bhdr; + + char* bits = NULL; + + HDC hdc = GetDC(GetDesktopWindow()); + m_hbitmap = CreateDIBSection(hdc, &binf, DIB_RGB_COLORS, + reinterpret_cast<void**>(&bits), + NULL, 0); + + if (m_hbitmap == NULL) + throw std::exception(); // TODO throw ui_exception + + m_hdc = CreateCompatibleDC(hdc); + SelectObject(m_hdc, m_hbitmap); + + // Clear the whole bitmap with a white background + { + HGDIOBJ oldPen = SelectObject(m_hdc, ::CreatePen(PS_NULL, 0, 0)); + HGDIOBJ oldBrush = SelectObject(m_hdc, ::CreateSolidBrush(RGB(255, 255, 255))); + + Rectangle(m_hdc, 0, 0, width, height); + + DeleteObject(SelectObject(m_hdc, oldPen)); + DeleteObject(SelectObject(m_hdc, oldBrush)); + } + } + + ~win32_bitmap() { + ::DeleteDC(m_hdc); + ::DeleteObject(m_hbitmap); + } + + HBITMAP hbitmap() { + return m_hbitmap; + } + + HDC hdc() { + return m_hdc; + } + + }; + + ////////////////////////////////////////////////////////////////////// + // win32_window class + + class win32_window { + HWND m_handle; + mt::thread* m_thread; + bool m_destroy; + bool m_keypressed; + bool m_closed; + win32_bitmap m_bitmap; + int m_textx, m_texty; + + struct create_window_wrapper { + win32_window* m_wnd; + int m_width; + int m_height; + + create_window_wrapper(win32_window* wnd, int width, int height) + : m_wnd(wnd) + , m_width(width) + , m_height(height) { + } + + void operator()() { + m_wnd->m_handle = win32_details::create_window(m_wnd, m_width, m_height); + + MSG msg; + + + ::ShowWindow(m_wnd->m_handle, SW_SHOWNORMAL); + ::UpdateWindow(m_wnd->m_handle); + + // Message loop while: + // 1) the window is not destroyed + // 2) the window is visible + // 3) the Windows message queue is still alive + while (!m_wnd->m_destroy && + ::IsWindowVisible(m_wnd->m_handle) && + ::GetMessage(&msg, m_wnd->m_handle, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + // Destroy the window + ::DestroyWindow(m_wnd->m_handle); + m_wnd->m_handle = NULL; + } + + }; + + friend struct create_window_wrapper; + friend class win32_details; + + public: + + win32_window(int width, int height) + : m_bitmap(width, height) + , m_textx(0) + , m_texty(0) { + win32_details::register_window_class<win32_window>(); + + m_destroy = false; + m_keypressed = false; + m_closed = false; + m_thread = new mt::thread(create_window_wrapper(this, width, height)); + + // TODO wait m_handle != NULL (use a notification, avoid 100% CPU wait) + while (m_handle == NULL) + ; + } + + ~win32_window() { + if (m_thread) { + m_destroy = true; + m_thread->join(); + delete m_thread; + } + } + + void waitkey() { + // // do something + while (!m_keypressed && !m_closed) // TODO avoid 100% CPU wait + // wait_message(); + ; + m_keypressed = false; + } + + size_t width() { + RECT rc; + ::GetWindowRect(m_handle, &rc); + return rc.right - rc.left; + } + + size_t height() { + RECT rc; + ::GetWindowRect(m_handle, &rc); + return rc.bottom - rc.top; + } + + void write(const std::string& text) { + HDC hdc = m_bitmap.hdc(); + + // Draw the text in the bitmap + TextOut(hdc, m_textx, m_texty, text.c_str(), static_cast<int>(text.size())); + + // Calculate the size of the string + RECT rc = { 0, 0, 32000, 0 }; + DrawText(hdc, text.c_str(), static_cast<int>(text.size()), &rc, DT_CALCRECT); + + m_textx += (rc.right - rc.left); // X = X + Text Width + if (m_textx > width()) { + m_texty += (rc.bottom - rc.top); + m_textx = 0; + } + + ::InvalidateRect(m_handle, NULL, FALSE); + } + + private: + + // This function is used to process + bool process_message(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result) + { + switch (msg) { + + case WM_KEYDOWN: + m_keypressed = true; + break; + + case WM_CLOSE: + m_closed = true; + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC window_hdc = ::BeginPaint(m_handle, &ps); + HDC bitmap_hdc = m_bitmap.hdc(); + + if (!::IsRectEmpty(&ps.rcPaint)) { + BitBlt(window_hdc, + ps.rcPaint.left, ps.rcPaint.top, // X, Y (dst) + ps.rcPaint.right - ps.rcPaint.left, // Width + ps.rcPaint.bottom - ps.rcPaint.top, // Height + bitmap_hdc, + ps.rcPaint.left, ps.rcPaint.top, // X, Y (src) + SRCCOPY); + } + + ::EndPaint(m_handle, &ps); + + result = TRUE; + } + return true; + } + return false; + } + + }; + + } // namespace win32 + + typedef win32::win32_window window_impl; + +} // namespace ui + +#define ui_main() \ + WINAPI WinMain(HINSTANCE hInstance, \ + HINSTANCE hPrevInstance, \ + LPSTR lpCmdLine, \ + int nCmdShow) \ + +#endif // UI_WIN32_HEADER_FILE_INCLUDED |