2016-04-17 10:53:24 -05:00
// 17 april 2016
2016-04-23 10:50:47 -05:00
# include "uipriv_windows.hpp"
2016-04-17 10:53:24 -05:00
// 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
2016-05-17 13:44:57 -05:00
// 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
2016-04-17 10:53:24 -05:00
// 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 ;
2016-04-23 10:50:47 -05:00
rt - > BeginDraw ( ) ;
2016-04-17 10:53:24 -05:00
// 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
2016-05-13 14:13:41 -05:00
// TODO now that -RTCc has just been deprecated entirely, should we switch back?
2016-04-17 10:53:24 -05:00
bgcolor . g = ( ( float ) ( ( BYTE ) ( ( bgcolorref & 0xFF00 ) > > 8 ) ) ) / 255.0 ;
bgcolor . b = ( ( float ) GetBValue ( bgcolorref ) ) / 255.0 ;
bgcolor . a = 1.0 ;
2016-04-23 10:50:47 -05:00
rt - > Clear ( & bgcolor ) ;
2016-04-17 10:53:24 -05:00
SendMessageW ( hwnd , msgD2DScratchPaint , 0 , ( LPARAM ) rt ) ;
2016-04-23 10:50:47 -05:00
return rt - > EndDraw ( NULL , NULL ) ;
2016-04-17 10:53:24 -05:00
}
2016-05-17 13:44:57 -05:00
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 = rt - > GetSize ( ) ;
SendMessageW ( hwnd , msgD2DScratchLButtonDown , ( WPARAM ) ( & pos ) , ( LPARAM ) ( & size ) ) ;
}
2016-04-17 10:53:24 -05:00
static LRESULT CALLBACK d2dScratchWndProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
LONG_PTR init ;
ID2D1HwndRenderTarget * rt ;
2016-05-22 15:16:20 -05:00
ID2D1DCRenderTarget * dcrt ;
RECT client ;
2016-04-17 10:53:24 -05:00
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 :
2016-04-23 10:50:47 -05:00
rt - > Release ( ) ;
2016-04-17 10:53:24 -05:00
SetWindowLongPtrW ( hwnd , 0 , ( LONG_PTR ) FALSE ) ;
break ;
case WM_PAINT :
2016-04-23 10:50:47 -05:00
hr = d2dScratchDoPaint ( hwnd , rt ) ;
2016-04-17 10:53:24 -05:00
switch ( hr ) {
case S_OK :
if ( ValidateRect ( hwnd , NULL ) = = 0 )
2016-04-23 10:50:47 -05:00
logLastError ( L " error validating D2D scratch control rect " ) ;
2016-04-17 10:53:24 -05:00
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?
2016-04-23 10:50:47 -05:00
rt - > Release ( ) ;
2016-04-17 10:53:24 -05:00
SetWindowLongPtrW ( hwnd , GWLP_USERDATA , ( LONG_PTR ) NULL ) ;
break ;
default :
2016-04-23 10:50:47 -05:00
logHRESULT ( L " error drawing D2D scratch window " , hr ) ;
2016-04-17 10:53:24 -05:00
}
return 0 ;
case WM_PRINTCLIENT :
2016-05-22 15:16:20 -05:00
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 ;
2016-05-17 13:44:57 -05:00
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 ;
2016-04-17 10:53:24 -05:00
}
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 ) ;
2016-04-17 12:06:45 -05:00
wc . cbWndExtra = sizeof ( LONG_PTR ) ; // for the init status
2016-04-17 10:53:24 -05:00
return RegisterClassW ( & wc ) ;
}
void unregisterD2DScratchClass ( void )
{
if ( UnregisterClassW ( d2dScratchClass , hInstance ) = = 0 )
2016-04-23 10:50:47 -05:00
logLastError ( L " error unregistering D2D scratch window class " ) ;
2016-04-17 10:53:24 -05:00
}
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 )
2016-04-23 10:50:47 -05:00
// TODO return decoy window
logLastError ( L " error creating D2D scratch window " ) ;
2016-04-17 10:53:24 -05:00
if ( SetWindowSubclass ( hwnd , subclass , 0 , subclassData ) = = FALSE )
2016-04-23 10:50:47 -05:00
logLastError ( L " error subclassing D2D scratch window " ) ;
2016-04-17 10:53:24 -05:00
return hwnd ;
}