diff options
Diffstat (limited to 'src/libui_sdl/libui/windows/d2dscratch.cpp')
-rw-r--r-- | src/libui_sdl/libui/windows/d2dscratch.cpp | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/windows/d2dscratch.cpp b/src/libui_sdl/libui/windows/d2dscratch.cpp new file mode 100644 index 0000000..6dc2ba5 --- /dev/null +++ b/src/libui_sdl/libui/windows/d2dscratch.cpp @@ -0,0 +1,166 @@ +// 17 april 2016 +#include "uipriv_windows.hpp" + +// The Direct2D scratch window is a utility for libui internal use to do quick things with Direct2D. +// To use, call newD2DScratch() passing in a subclass procedure. This subclass procedure should handle the msgD2DScratchPaint message, which has the following usage: +// - wParam - 0 +// - lParam - ID2D1RenderTarget * +// - lResult - 0 +// You can optionally also handle msgD2DScratchLButtonDown, which is sent when the left mouse button is either pressed for the first time or held while the mouse is moving. +// - wParam - position in DIPs, as D2D1_POINT_2F * +// - lParam - size of render target in DIPs, as D2D1_SIZE_F * +// - lResult - 0 +// Other messages can also be handled here. + +// TODO allow resize + +#define d2dScratchClass L"libui_d2dScratchClass" + +// TODO clip rect +static HRESULT d2dScratchDoPaint(HWND hwnd, ID2D1RenderTarget *rt) +{ + COLORREF bgcolorref; + D2D1_COLOR_F bgcolor; + + rt->BeginDraw(); + + // TODO only clear the clip area + // TODO clear with actual background brush + bgcolorref = GetSysColor(COLOR_BTNFACE); + bgcolor.r = ((float) GetRValue(bgcolorref)) / 255.0; + // due to utter apathy on Microsoft's part, GetGValue() does not work with MSVC's Run-Time Error Checks + // it has not worked since 2008 and they have *never* fixed it + // TODO now that -RTCc has just been deprecated entirely, should we switch back? + bgcolor.g = ((float) ((BYTE) ((bgcolorref & 0xFF00) >> 8))) / 255.0; + bgcolor.b = ((float) GetBValue(bgcolorref)) / 255.0; + bgcolor.a = 1.0; + rt->Clear(&bgcolor); + + SendMessageW(hwnd, msgD2DScratchPaint, 0, (LPARAM) rt); + + return rt->EndDraw(NULL, NULL); +} + +static void d2dScratchDoLButtonDown(HWND hwnd, ID2D1RenderTarget *rt, LPARAM lParam) +{ + double xpix, ypix; + FLOAT dpix, dpiy; + D2D1_POINT_2F pos; + D2D1_SIZE_F size; + + xpix = (double) GET_X_LPARAM(lParam); + ypix = (double) GET_Y_LPARAM(lParam); + // these are in pixels; we need points + // TODO separate the function from areautil.cpp? + rt->GetDpi(&dpix, &dpiy); + pos.x = (xpix * 96) / dpix; + pos.y = (ypix * 96) / dpiy; + + size = realGetSize(rt); + + SendMessageW(hwnd, msgD2DScratchLButtonDown, (WPARAM) (&pos), (LPARAM) (&size)); +} + +static LRESULT CALLBACK d2dScratchWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LONG_PTR init; + ID2D1HwndRenderTarget *rt; + ID2D1DCRenderTarget *dcrt; + RECT client; + HRESULT hr; + + init = GetWindowLongPtrW(hwnd, 0); + if (!init) { + if (uMsg == WM_CREATE) + SetWindowLongPtrW(hwnd, 0, (LONG_PTR) TRUE); + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + + rt = (ID2D1HwndRenderTarget *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); + if (rt == NULL) { + rt = makeHWNDRenderTarget(hwnd); + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) rt); + } + + switch (uMsg) { + case WM_DESTROY: + rt->Release(); + SetWindowLongPtrW(hwnd, 0, (LONG_PTR) FALSE); + break; + case WM_PAINT: + hr = d2dScratchDoPaint(hwnd, rt); + switch (hr) { + case S_OK: + if (ValidateRect(hwnd, NULL) == 0) + logLastError(L"error validating D2D scratch control rect"); + break; + case D2DERR_RECREATE_TARGET: + // DON'T validate the rect + // instead, simply drop the render target + // we'll get another WM_PAINT and make the render target again + // TODO would this require us to invalidate the entire client area? + rt->Release(); + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) NULL); + break; + default: + logHRESULT(L"error drawing D2D scratch window", hr); + } + return 0; + case WM_PRINTCLIENT: + uiWindowsEnsureGetClientRect(hwnd, &client); + dcrt = makeHDCRenderTarget((HDC) wParam, &client); + hr = d2dScratchDoPaint(hwnd, dcrt); + if (hr != S_OK) + logHRESULT(L"error printing D2D scratch window client area", hr); + dcrt->Release(); + return 0; + case WM_LBUTTONDOWN: + d2dScratchDoLButtonDown(hwnd, rt, lParam); + return 0; + case WM_MOUSEMOVE: + // also send LButtonDowns when dragging + if ((wParam & MK_LBUTTON) != 0) + d2dScratchDoLButtonDown(hwnd, rt, lParam); + return 0; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +ATOM registerD2DScratchClass(HICON hDefaultIcon, HCURSOR hDefaultCursor) +{ + WNDCLASSW wc; + + ZeroMemory(&wc, sizeof (WNDCLASSW)); + wc.lpszClassName = d2dScratchClass; + wc.lpfnWndProc = d2dScratchWndProc; + wc.hInstance = hInstance; + wc.hIcon = hDefaultIcon; + wc.hCursor = hDefaultCursor; + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.cbWndExtra = sizeof (LONG_PTR); // for the init status + return RegisterClassW(&wc); +} + +void unregisterD2DScratchClass(void) +{ + if (UnregisterClassW(d2dScratchClass, hInstance) == 0) + logLastError(L"error unregistering D2D scratch window class"); +} + +HWND newD2DScratch(HWND parent, RECT *rect, HMENU controlID, SUBCLASSPROC subclass, DWORD_PTR subclassData) +{ + HWND hwnd; + + hwnd = CreateWindowExW(0, + d2dScratchClass, L"", + WS_CHILD | WS_VISIBLE, + rect->left, rect->top, + rect->right - rect->left, rect->bottom - rect->top, + parent, controlID, hInstance, NULL); + if (hwnd == NULL) + // TODO return decoy window + logLastError(L"error creating D2D scratch window"); + if (SetWindowSubclass(hwnd, subclass, 0, subclassData) == FALSE) + logLastError(L"error subclassing D2D scratch window"); + return hwnd; +} |