// cl/nologo /EHsc /O2 win.cpp user32.lib gdi32.lib msimg32.lib // g++ -Wall -Wextra -std=c++0x -pedantic -O2 win.cpp -mwindows -o win.exe -luser32 -lgdi32 -lmsimg32 #define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x601 #define WINVER 0x601 #define UNICODE #include // some hacks for our dear friend MinGW #ifdef __GNUC__ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #include #include int swprintf(wchar_t *, size_t, const wchar_t *, ...); int vswprintf(wchar_t *, size_t, const wchar_t *, va_list); int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR, int nCmdShow) { return wWinMain(hInstance, hPrevInstance, GetCommandLine(), nCmdShow); } #if GCC_VERSION < 40600 || GCC_VERSION == 40600 && defined __MINGW64_VERSION_MAJOR static const struct { template operator T*() const { return 0; } template operator T C::*() const { return 0; } private: void operator &() const; } nullptr = {}; #endif // GCC_VERSION < 40600 #endif // defined __GNUC__ #include #include #if !defined __GNUC__ || defined __MINGW64_VERSION_MAJOR #include #else #define StringCbCat(a, b, c) wcscat(a, c) #define StringCbPrintf(a, b, ...) wsprintf(a, __VA_ARGS__) #endif using namespace std; #define STRINGIZE2(x) #x #define STRINGIZE(x) STRINGIZE2(x) #define TOUNICODE2(b) L ## b #define TOUNICODE(x) TOUNICODE2(x) #define dbg(msg) OutputDebugString(TOUNICODE(__FILE__) L":" TOUNICODE(STRINGIZE(__LINE__)) L": " msg L"\r\n") #define dbgFmt(fmt, ...) do { wchar_t buf[256]; StringCbPrintf(buf, sizeof buf, TOUNICODE(__FILE__) L":" TOUNICODE(STRINGIZE(__LINE__)) L": " fmt L"\r\n", __VA_ARGS__); OutputDebugString(buf); } while (0) #define dbgStr(msg) dbgFmt(L"%s", msg) class Problem { wstring Message_; public: Problem(const wstring &msg = wstring()) { Create(GetLastError(), msg); } Problem(HRESULT hr, const wstring &msg = wstring()) { Create(hr, msg); } operator const wstring &() const { return Message_; } private: void Create(DWORD err, const wstring &msg) { wchar_t *buf; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&buf, 0, nullptr); if (msg.empty()) Message_ = buf; else Message_ = msg + L": " + buf; LocalFree(buf); } }; class WindowClass { ATOM Atom_; public: WindowClass(HINSTANCE hInstance, const wstring &className, WNDPROC wndProc) throw(Problem) { WNDCLASSEX wcex = { /*.cbSize = */sizeof wcex, /*.style = */CS_OWNDC, //| CS_HREDRAW | CS_VREDRAW, /*.lpfnWndProc = */wndProc, /*.cbClsExtra = */0, /*.cbWndExtra = */0, /*.hInstance = */hInstance, /*.hIcon = */nullptr, /*.hCursor = */LoadCursor(nullptr, IDC_ARROW), /*.hbrBackground = */nullptr,//reinterpret_cast(COLOR_BTNFACE + 1), /*.lpszMenuName = */nullptr, /*.lpszClassName = */className.c_str(), /*.hIconSm = */nullptr, }; if (!(Atom_ = RegisterClassEx(&wcex))) throw Problem(L"WindowClass::ctor"); } operator ATOM() const { return Atom_; } operator const wchar_t *() const { return reinterpret_cast(Atom_); } }; class Window { HWND Handle_; public: Window(HINSTANCE hInstance, const wchar_t *wc, const wstring &caption = wstring()) throw(Problem) { Handle_ = CreateWindowEx(WS_EX_LAYERED, wc, caption.c_str(), WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr); if (!Handle_) throw Problem(L"Window::ctor"); } void Show(int cmd = SW_SHOW) { ShowWindow(Handle_, cmd); } void Update() { UpdateWindow(Handle_); } operator HWND() const { return Handle_; } }; class MessageLoop { public: class ExitCode { int Code_; public: ExitCode(int code) : Code_(code) { } operator int() const { return Code_; } }; MessageLoop(HWND hWnd) throw(Problem, ExitCode) { MSG msg; while (BOOL ret = GetMessage(&msg, hWnd, 0, 0)) { if (ret == -1) throw Problem(L"MessageLoop::ctor"); else { if (msg.message == WM_QUIT) dbg(L"WM_QUIT received"); else if (msg.message == WM_CLOSE) dbg(L"WM_CLOSE received"); else if (msg.message == WM_DESTROY) dbg(L"WM_DESTROY received"); else if (msg.message == WM_LBUTTONDOWN) dbg(L"WM_LBUTTONDOWN received"); TranslateMessage(&msg); DispatchMessage(&msg); } } throw ExitCode(msg.wParam); } }; class Color { int Red_, Green_, Blue_; public: Color() : Red_(0), Green_(0), Blue_(0) { } Color(int red, int green, int blue) : Red_(red), Green_(green), Blue_(blue) { } RGBQUAD &Set(RGBQUAD &q) const { q.rgbBlue = Blue_; q.rgbGreen = Green_; q.rgbRed = Red_; q.rgbReserved = 0; return q; } operator COLORREF() const { return RGB(Red_, Green_, Blue_); } static Color Blue() { return Color(0, 0, 255); } static Color Green() { return Color(0, 255, 0); } static Color Red() { return Color(255, 0, 0); } static Color White() { return Color(255, 255, 255); } }; class Brush { HBRUSH Handle_; public: Brush(Color c) throw(Problem) { Handle_ = CreateSolidBrush(c); if (!Handle_) throw Problem(L"Brush::ctor(Color)"); } ~Brush() { DeleteObject(Handle_); } operator HBRUSH() const { return Handle_; } }; static Color GetColor(int x, int y) { int X = x / 256; x = x % 256; int Y = y / 256; y = y % 256; int r = 0, g = 0, b = 0; switch (X % 6) { case 0: r = x; g = 0; break; case 1: r = 255; g = x; break; case 2: r = 255; g = 255 - x; break; case 3: r = 255 - x; g = 0; break; case 4: r = 0; g = x; break; case 5: r = 0; g = 255 - x; break; } switch (Y % 4) { case 0: b = y; break; case 1: b = 255; break; case 2: b = 255 - y; break; case 3: b = 0; break; } if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; return Color(r, g, b); } static bool doSetDIBits = true; static void Paint(HDC hDC, int x0, int y0, int x1, int y1) { dbgFmt(L"Painting; hDC=%p; rect: (%d, %d) - (%d, %d); SetDIBitsToDevice %s", hDC, x0, y0, x1, y1, doSetDIBits ? L"enabled" : L"disabled"); int w = x1 - x0, h = y1 - y0; BITMAPINFO bmi = { { /*biSize = */sizeof bmi.bmiHeader, /*biWidth = */w, /*biHeight = */h, /*biPlanes = */1, /*biBitCount = */32, /*biCompression = */BI_RGB, /*biSizeImage = */BI_RGB, /*biXPelsPerMeter = */0, /*biYPelsPerMeter = */0, /*biClrUsed = */0, /*biClrImportant = */0 }, { { 0, 0, 0, 0 } } }; vector bits(4 * w * h); for (int y = y0; y < y1; ++y) for (int x = x0; x < x1; ++x) GetColor(x, y).Set(bits[(h - 1 - y + y0) * w + (x - x0)]); if (doSetDIBits) SetDIBitsToDevice(hDC, x0, y0, w, h, 0, 0, 0, h, &bits[0], &bmi, DIB_RGB_COLORS); // all the BitBlt ROPs DWORD ops[] = { BLACKNESS, CAPTUREBLT, DSTINVERT, MERGECOPY, MERGEPAINT, NOMIRRORBITMAP, NOTSRCCOPY, NOTSRCERASE, PATCOPY, PATINVERT, PATPAINT, SRCAND, SRCCOPY, SRCERASE, SRCINVERT, SRCPAINT, WHITENESS }; int n = sizeof ops / sizeof ops[0]; for (int i = 0; i < n/2; ++i) for (int y = 1; y < 4; y += 2) BitBlt(hDC, 80 * (i + 1), 100 * y, 60, 60, hDC, 512, 0, ops[i]); for (int i = n/2; i < n; ++i) for (int y = 2; y < 5; y += 2) BitBlt(hDC, 80 * (i - n / 2 + 1), 100 * y, 60, 60, hDC, 512, 0, ops[i]); // test AlphaBlend for (int i = 0; i < 8; ++i) { BLENDFUNCTION ftn = { AC_SRC_OVER, 0, static_cast(31 + i * 32), 0 }; AlphaBlend(hDC, 850, 20+70*i, 60, 60, hDC, 512, 0, 60, 60, ftn); } // some simple GradientFill calls TRIVERTEX rect[2] = { { 100, 500, 0, 0, 0, 255 }, { 200, 600, 0xff00, 0xff00, 0xff00, 0xff00 } }; GRADIENT_RECT r = { 0, 1 }; GradientFill(hDC, rect, 2, &r, 1, GRADIENT_FILL_RECT_H); rect[0].x += 120; rect[1].x += 120; GradientFill(hDC, rect, 2, &r, 1, GRADIENT_FILL_RECT_V); TRIVERTEX triang[3] = { { 340, 550, 0xff00, 0, 0, 255 }, { 500, 500, 0, 0xff00, 0, 0xff00 }, { 500, 600, 0, 0xff00, 0, 0xff00 } }; GRADIENT_TRIANGLE t = { 0, 1, 2 }; GradientFill(hDC, triang, 3, &t, 1, GRADIENT_FILL_TRIANGLE); // test DrawText Brush br(Color::White()); RECT rt1 = { 700, 500, 750, 550 }; FillRect(hDC, &rt1, br); DrawText(hDC, L"1", 1, &rt1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); RECT rt2 = { 810, 510, 830, 530 }; FillRect(hDC, &rt2, br); DrawText(hDC, L"1", 1, &rt2, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int opac = 100; switch (uMsg) { /*case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = BeginPaint(hWnd, &ps); Paint(hDC, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); EndPaint(hWnd, &ps); return 0; }*/ case WM_ERASEBKGND: { HDC hDC = reinterpret_cast(wParam); RECT rc; GetClientRect(hWnd, &rc); Paint(hDC, rc.left, rc.top, rc.right, rc.bottom); return 0; } case WM_RBUTTONDOWN: { opac -= 20; if (opac <= 0) opac = 100; try { if (!SetLayeredWindowAttributes(hWnd, 0, opac * 255 / 100, LWA_ALPHA)) throw Problem(L"Transparency"); } catch (Problem p) { MessageBoxEx(nullptr, static_cast(p).c_str(), nullptr, MB_OK, 0); } return 0; } case WM_KEYDOWN: { if (wParam == 'X') { doSetDIBits = !doSetDIBits; SetWindowText(hWnd, doSetDIBits ? L"Color Test" : L"Color Test (no SetDIBits)"); } return 0; } case WM_LBUTTONDOWN: { dbg(L"left click!"); return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int) { try { WindowClass wc(hInstance, L"OMGawesome", WndProc); Window wnd(hInstance, wc, L"Color Test"); SetLayeredWindowAttributes(wnd, 0, 255, LWA_ALPHA); wnd.Show(); wnd.Update(); MessageLoop loop(wnd); } catch (Problem p) { MessageBoxExW(nullptr, static_cast(p).c_str(), nullptr, MB_OK | MB_ICONERROR, 0); } catch (MessageLoop::ExitCode e) { return e; } return -1; }