18#include "IGraphicsWin.h"
19#include "IGraphicsWin_dnd.h"
24#include <VersionHelpers.h>
28#define CCSIZEOF_STRUCT(structname, member) (__builtin_offsetof(structname, member) + sizeof(((structname*)0)->member))
32using namespace igraphics;
34#pragma warning(disable:4244)
35#pragma warning(disable:4312)
36#pragma warning(disable:4311)
38static int nWndClassReg = 0;
39static const wchar_t* wndClassName = L
"IPlugWndClass";
40static double sFPS = 0.0;
42#define PARAM_EDIT_ID 99
43#define IPLUG_TIMER_ID 2
45#define TOOLTIPWND_MAXWIDTH 250
47#define WM_VBLANK (WM_USER+1)
50typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext,
const int* attribList);
51#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
52#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
53#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
54#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
57#pragma mark - Static storage
59StaticStorage<IGraphicsWin::InstalledFont> IGraphicsWin::sPlatformFontCache;
60StaticStorage<HFontHolder> IGraphicsWin::sHFontCache;
62#pragma mark - Mouse and tablet helpers
64extern float GetScaleForHWND(HWND hWnd);
66inline IMouseInfo IGraphicsWin::GetMouseInfo(LPARAM lParam, WPARAM wParam)
69 const float scale = GetTotalScale();
70 info.x = mCursorX = GET_X_LPARAM(lParam) / scale;
71 info.y = mCursorY = GET_Y_LPARAM(lParam) / scale;
72 info.ms =
IMouseMod((wParam & MK_LBUTTON), (wParam & MK_RBUTTON), (wParam & MK_SHIFT), (wParam & MK_CONTROL),
74 GetAsyncKeyState(VK_MENU) < 0
76 GetKeyState(VK_MENU) < 0
83void IGraphicsWin::CheckTabletInput(UINT msg)
85 if ((msg == WM_LBUTTONDOWN) || (msg == WM_RBUTTONDOWN) || (msg == WM_MBUTTONDOWN) || (msg == WM_MOUSEMOVE)
86 || (msg == WM_RBUTTONDBLCLK) || (msg == WM_LBUTTONDBLCLK) || (msg == WM_MBUTTONDBLCLK)
87 || (msg == WM_RBUTTONUP) || (msg == WM_LBUTTONUP) || (msg == WM_MBUTTONUP)
88 || (msg == WM_MOUSEHOVER) || (msg == WM_MOUSELEAVE))
90 const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00;
91 const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700;
93 LONG_PTR extraInfo = GetMessageExtraInfo();
94 SetTabletInput(((extraInfo & c_SIGNATURE_MASK) == c_MOUSEEVENTF_FROMTOUCH));
95 mCursorLock &= !mTabletInput;
99void IGraphicsWin::DestroyEditWindow()
103 SetWindowLongPtrW(mParamEditWnd, GWLP_WNDPROC, (LPARAM) mDefEditProc);
104 DestroyWindow(mParamEditWnd);
105 mParamEditWnd =
nullptr;
106 mDefEditProc =
nullptr;
107 DeleteObject(mEditFont);
112void IGraphicsWin::OnDisplayTimer(
int vBlankCount)
115 DWORD msgCount = vBlankCount;
116 DWORD curCount = mVBlankCount;
121 if (mVBlankSkipUntil != 0 && mVBlankSkipUntil > mVBlankCount)
126 mVBlankSkipUntil = 0;
128 if (msgCount != curCount)
136 if (mParamEditWnd && mParamEditMsg != kNone)
138 switch (mParamEditMsg)
142 WCHAR strWide[MAX_WIN32_PARAM_LEN];
143 SendMessageW(mParamEditWnd, WM_GETTEXT, MAX_WIN32_PARAM_LEN, (LPARAM) strWide);
144 SetControlValueAfterTextEdit(UTF16AsUTF8(strWide).Get());
150 ClearInTextEntryControl();
154 mParamEditMsg = kNone;
162 float scale = GetScaleForHWND(mPlugWnd);
163 if (scale != GetScreenScale())
164 SetScreenScale(scale);
170 const float totalScale = GetTotalScale();
173 SetAllControlsClean();
175 for (
int i = 0; i < rects.
Size(); i++)
178 dirtyR.
Scale(totalScale);
180 RECT r = { (LONG)dirtyR.L, (LONG)dirtyR.T, (LONG)dirtyR.R, (LONG)dirtyR.B };
181 InvalidateRect(mPlugWnd, &r, FALSE);
186 IRECT notDirtyR = mEditRECT;
187 notDirtyR.
Scale(totalScale);
189 RECT r2 = { (LONG)notDirtyR.L, (LONG)notDirtyR.T, (LONG)notDirtyR.R, (LONG)notDirtyR.B };
190 ValidateRect(mPlugWnd, &r2);
191 UpdateWindow(mPlugWnd);
192 mParamEditMsg = kUpdate;
197 UpdateWindow(mPlugWnd);
202 curCount = mVBlankCount;
203 if (msgCount != curCount)
206 mVBlankSkipUntil = curCount+1;
216LRESULT CALLBACK IGraphicsWin::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
218 if (msg == WM_CREATE)
220 CREATESTRUCTW* lpcs = (CREATESTRUCTW *) lParam;
221 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM) lpcs->lpCreateParams);
224 if (pGraphics->mVSYNCEnabled)
226 assert((pGraphics->FPS() == 60) &&
"If you want to run at frame rates other than 60FPS");
227 pGraphics->StartVBlankThread(hWnd);
231 int mSec =
static_cast<int>(std::floorf(1000.0f / (pGraphics->FPS())));
232 if (mSec < 20) mSec = 15;
233 SetTimer(hWnd, IPLUG_TIMER_ID, mSec, NULL);
237 DragAcceptFiles(hWnd,
true);
243 if (!pGraphics || hWnd != pGraphics->mPlugWnd)
245 return DefWindowProcW(hWnd, msg, wParam, lParam);
248 if (pGraphics->mParamEditWnd && pGraphics->mParamEditMsg == kEditing)
250 if (msg == WM_RBUTTONDOWN || (msg == WM_LBUTTONDOWN))
252 pGraphics->mParamEditMsg = kCancel;
255 return DefWindowProcW(hWnd, msg, wParam, lParam);
258 auto IsTouchEvent = []() {
259 const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00;
260 const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700;
261 LONG_PTR extraInfo = GetMessageExtraInfo();
262 return ((extraInfo & c_SIGNATURE_MASK) == c_MOUSEEVENTF_FROMTOUCH);
265 pGraphics->CheckTabletInput(msg);
270 pGraphics->OnDisplayTimer(wParam);
274 if (wParam == IPLUG_TIMER_ID)
275 pGraphics->OnDisplayTimer(0);
289 pGraphics->HideTooltip();
290 if (pGraphics->mParamEditWnd)
292 pGraphics->mParamEditMsg = kCommit;
297 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
298 std::vector<IMouseInfo> list{ info };
299 pGraphics->OnMouseDown(list);
304 pGraphics->OnSetCursor();
312 if (!(wParam & (MK_LBUTTON | MK_RBUTTON)))
314 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
315 if (pGraphics->OnMouseOver(info.x, info.y, info.ms))
317 TRACKMOUSEEVENT eventTrack = {
sizeof(TRACKMOUSEEVENT), TME_LEAVE, hWnd, HOVER_DEFAULT };
318 if (pGraphics->TooltipsEnabled())
320 int c = pGraphics->GetMouseOver();
321 if (c != pGraphics->mTooltipIdx)
323 if (c >= 0) eventTrack.dwFlags |= TME_HOVER;
324 pGraphics->mTooltipIdx = c;
325 pGraphics->HideTooltip();
329 TrackMouseEvent(&eventTrack);
332 else if (GetCapture() == hWnd && !pGraphics->IsInPlatformTextEntry())
334 float oldX = pGraphics->mCursorX;
335 float oldY = pGraphics->mCursorY;
337 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
339 info.dX = info.x - oldX;
340 info.dY = info.y - oldY;
342 if (info.dX || info.dY)
344 std::vector<IMouseInfo> list{ info };
345 pGraphics->OnMouseDrag(list);
347 if (pGraphics->MouseCursorIsLocked())
349 const float x = pGraphics->mHiddenCursorX;
350 const float y = pGraphics->mHiddenCursorY;
352 pGraphics->MoveMouseCursor(x, y);
353 pGraphics->mHiddenCursorX = x;
354 pGraphics->mHiddenCursorY = y;
363 pGraphics->ShowTooltip();
368 pGraphics->HideTooltip();
369 pGraphics->OnMouseOut();
376 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
377 std::vector<IMouseInfo> list{ info };
378 pGraphics->OnMouseUp(list);
381 case WM_LBUTTONDBLCLK:
382 case WM_RBUTTONDBLCLK:
387 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
388 if (pGraphics->OnMouseDblClick(info.x, info.y, info.ms))
396 if (pGraphics->mParamEditWnd)
398 pGraphics->mParamEditMsg = kCancel;
403 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
404 float d = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
405 const float scale = pGraphics->GetTotalScale();
407 GetWindowRect(hWnd, &r);
408 pGraphics->OnMouseWheel(info.x - (r.left / scale), info.y - (r.top / scale), info.ms, d);
414 UINT nTouches = LOWORD(wParam);
418 WDL_TypedBuf<TOUCHINPUT> touches;
419 touches.Resize(nTouches);
420 HTOUCHINPUT hTouchInput = (HTOUCHINPUT) lParam;
421 std::vector<IMouseInfo> downlist;
422 std::vector<IMouseInfo> uplist;
423 std::vector<IMouseInfo> movelist;
424 const float scale = pGraphics->GetTotalScale();
426 GetTouchInputInfo(hTouchInput, nTouches, touches.Get(),
sizeof(TOUCHINPUT));
428 for (
int i = 0; i < nTouches; i++)
430 TOUCHINPUT* pTI = touches.Get() +i;
433 pt.x = TOUCH_COORD_TO_PIXEL(pTI->x);
434 pt.y = TOUCH_COORD_TO_PIXEL(pTI->y);
435 ScreenToClient(pGraphics->mPlugWnd, &pt);
438 info.x =
static_cast<float>(pt.x) / scale;
439 info.y =
static_cast<float>(pt.y) / scale;
442 info.ms.touchRadius = 0;
444 if (pTI->dwMask & TOUCHINPUTMASKF_CONTACTAREA)
446 info.ms.touchRadius = pTI->cxContact;
449 info.ms.touchID =
static_cast<ITouchID
>(pTI->dwID);
451 if (pTI->dwFlags & TOUCHEVENTF_DOWN)
453 downlist.push_back(info);
454 pGraphics->mDeltaCapture.insert(std::make_pair(info.ms.touchID, info));
456 else if (pTI->dwFlags & TOUCHEVENTF_UP)
458 pGraphics->mDeltaCapture.erase(info.ms.touchID);
459 uplist.push_back(info);
461 else if (pTI->dwFlags & TOUCHEVENTF_MOVE)
463 IMouseInfo previous = pGraphics->mDeltaCapture.find(info.ms.touchID)->second;
464 info.dX = info.x - previous.x;
465 info.dY = info.y - previous.y;
466 movelist.push_back(info);
467 pGraphics->mDeltaCapture[info.ms.touchID] = info;
472 pGraphics->OnMouseDown(downlist);
475 pGraphics->OnMouseUp(uplist);
478 pGraphics->OnMouseDrag(movelist);
480 CloseTouchInputHandle(hTouchInput);
485 return DLGC_WANTALLKEYS;
491 ScreenToClient(hWnd, &p);
493 BYTE keyboardState[256] = {};
494 GetKeyboardState(keyboardState);
495 const int keyboardScanCode = (lParam >> 16) & 0x00ff;
497 const int len = ToAscii(wParam, keyboardScanCode, keyboardState, &character, 0);
502 if (len == 0 || len == 1)
505 str[0] =
static_cast<char>(character);
508 IKeyPress keyPress{ str,
static_cast<int>(wParam),
509 static_cast<bool>(GetKeyState(VK_SHIFT) & 0x8000),
510 static_cast<bool>(GetKeyState(VK_CONTROL) & 0x8000),
511 static_cast<bool>(GetKeyState(VK_MENU) & 0x8000) };
513 const float scale = pGraphics->GetTotalScale();
515 if (msg == WM_KEYDOWN)
516 handle = pGraphics->OnKeyDown(p.x / scale, p.y / scale, keyPress);
518 handle = pGraphics->OnKeyUp(p.x / scale, p.y / scale, keyPress);
523 HWND rootHWnd = GetAncestor( hWnd, GA_ROOT);
524 SendMessageW(rootHWnd, msg, wParam, lParam);
525 return DefWindowProcW(hWnd, msg, wParam, lParam);
532 const float scale = pGraphics->GetTotalScale();
533 auto addDrawRect = [pGraphics, scale](
IRECTList& rects, RECT r) {
534 IRECT ir(r.left, r.top, r.right, r.bottom);
540 HRGN region = CreateRectRgn(0, 0, 0, 0);
541 int regionType = GetUpdateRgn(hWnd, region, FALSE);
543 if ((regionType == COMPLEXREGION) || (regionType == SIMPLEREGION))
546 const int bufferSize =
sizeof(RECT) * 64;
547 unsigned char stackBuffer[
sizeof(RGNDATA) + bufferSize];
548 RGNDATA* regionData = (RGNDATA*)stackBuffer;
550 if (regionType == COMPLEXREGION && GetRegionData(region, bufferSize, regionData))
552 for (
int i = 0; i < regionData->rdh.nCount; i++)
553 addDrawRect(rects, *(((RECT*)regionData->Buffer) + i));
558 GetRgnBox(region, &r);
559 addDrawRect(rects, r);
562#if defined IGRAPHICS_GL
564 BeginPaint(hWnd, &ps);
568 ScopedGLContext scopedGLCtx {pGraphics};
569 pGraphics->Draw(rects);
570 SwapBuffers((HDC) pGraphics->GetPlatformContext());
573#if defined IGRAPHICS_GL || IGRAPHICS_D2D
581 ValidateRect(hWnd, 0);
583 DeleteObject(region);
588 case WM_CTLCOLOREDIT:
590 if (!pGraphics->mParamEditWnd)
593 const IText& text = pGraphics->mEditText;
594 HDC dc = (HDC) wParam;
595 SetBkColor(dc, RGB(text.mTextEntryBGColor.R, text.mTextEntryBGColor.G, text.mTextEntryBGColor.B));
596 SetTextColor(dc, RGB(text.mTextEntryFGColor.R, text.mTextEntryFGColor.G, text.mTextEntryFGColor.B));
597 SetBkMode(dc, OPAQUE);
598 SetDCBrushColor(dc, RGB(text.mTextEntryBGColor.R, text.mTextEntryBGColor.G, text.mTextEntryBGColor.B));
599 return (LRESULT)GetStockObject(DC_BRUSH);
603 HDROP hdrop = (HDROP) wParam;
605 int numDroppedFiles = DragQueryFileW(hdrop, -1,
nullptr, 0);
607 std::vector<std::vector<char>> pathBuffers(numDroppedFiles, std::vector<char>(1025, 0));
608 std::vector<const char*> pathPtrs(numDroppedFiles);
610 for (
int i = 0; i < numDroppedFiles; i++)
612 wchar_t pathBufferW[1025] = {
'\0'};
613 DragQueryFileW(hdrop, i, pathBufferW, 1024);
614 strncpy(pathBuffers[i].data(), UTF16AsUTF8(pathBufferW).Get(), 1024);
615 pathPtrs[i] = pathBuffers[i].data();
618 DragQueryPoint(hdrop, &p);
620 const float scale = pGraphics->GetTotalScale();
622 if (numDroppedFiles==1)
624 pGraphics->OnDrop(&pathPtrs[0][0], p.x / scale, p.y / scale);
628 pGraphics->OnDropMultiple(pathPtrs, p.x / scale, p.y / scale);
635 pGraphics->CloseWindow();
647 return DefWindowProcW(hWnd, msg, wParam, lParam);
651LRESULT CALLBACK IGraphicsWin::ParamEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
655 if (pGraphics && pGraphics->mParamEditWnd && pGraphics->mParamEditWnd == hWnd)
657 pGraphics->HideTooltip();
664 if (pGraphics->mEditParam)
668 if (c == 0x08)
break;
670 switch (pGraphics->mEditParam->
Type())
672 case IParam::kTypeEnum:
673 case IParam::kTypeInt:
674 case IParam::kTypeBool:
675 if (c >=
'0' && c <=
'9')
break;
676 else if (c ==
'-')
break;
677 else if (c ==
'+')
break;
679 case IParam::kTypeDouble:
680 if (c >=
'0' && c <=
'9')
break;
681 else if (c ==
'-')
break;
682 else if (c ==
'+')
break;
683 else if (c ==
'.')
break;
693 if (wParam == VK_RETURN)
695 pGraphics->mParamEditMsg = kCommit;
698 else if (wParam == VK_ESCAPE)
700 pGraphics->mParamEditMsg = kCancel;
707 pGraphics->mParamEditMsg = kEditing;
712 pGraphics->mParamEditMsg = kCommit;
721 lres = CallWindowProcW(pGraphics->mDefEditProc, hWnd, WM_GETDLGCODE, wParam, lParam);
723 if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN && wParam == VK_RETURN)
725 lres |= DLGC_WANTMESSAGE;
731 switch HIWORD(wParam)
735 if (pGraphics->mParamEditWnd)
737 pGraphics->mParamEditMsg = kCommit;
746 return CallWindowProcW(pGraphics->mDefEditProc, hWnd, msg, wParam, lParam);
748 return DefWindowProcW(hWnd, msg, wParam, lParam);
751IGraphicsWin::IGraphicsWin(
IGEditorDelegate& dlg,
int w,
int h,
int fps,
float scale)
752 : IGRAPHICS_DRAW_CLASS(dlg, w, h, fps, scale)
754 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
755 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
756 fontStorage.Retain();
757 hfontStorage.Retain();
759#ifndef IGRAPHICS_DISABLE_VSYNC
760 mVSYNCEnabled = IsWindows8OrGreater();
764IGraphicsWin::~IGraphicsWin()
766 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
767 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
768 fontStorage.Release();
769 hfontStorage.Release();
774static void GetWindowSize(HWND pWnd,
int* pW,
int* pH)
779 GetWindowRect(pWnd, &r);
780 *pW = r.right - r.left;
781 *pH = r.bottom - r.top;
789static bool IsChildWindow(HWND pWnd)
793 int style = GetWindowLongW(pWnd, GWL_STYLE);
794 int exStyle = GetWindowLongW(pWnd, GWL_EXSTYLE);
795 return ((style & WS_CHILD) && !(exStyle & WS_EX_MDICHILD));
800void IGraphicsWin::ForceEndUserEdit()
802 mParamEditMsg = kCancel;
805static UINT SETPOS_FLAGS = SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE;
807void IGraphicsWin::PlatformResize(
bool parentHasResized)
811 HWND pParent = 0, pGrandparent = 0;
812 int dlgW = 0, dlgH = 0, parentW = 0, parentH = 0, grandparentW = 0, grandparentH = 0;
813 GetWindowSize(mPlugWnd, &dlgW, &dlgH);
814 int dw = (WindowWidth() * GetScreenScale()) - dlgW, dh = (WindowHeight()* GetScreenScale()) - dlgH;
816 if (IsChildWindow(mPlugWnd))
818 pParent = GetParent(mPlugWnd);
819 GetWindowSize(pParent, &parentW, &parentH);
821 if (IsChildWindow(pParent))
823 pGrandparent = GetParent(pParent);
824 GetWindowSize(pGrandparent, &grandparentW, &grandparentH);
831 SetWindowPos(mPlugWnd, 0, 0, 0, dlgW + dw, dlgH + dh, SETPOS_FLAGS);
833 if (pParent && !parentHasResized)
835 SetWindowPos(pParent, 0, 0, 0, parentW + dw, parentH + dh, SETPOS_FLAGS);
838 if (pGrandparent && !parentHasResized)
840 SetWindowPos(pGrandparent, 0, 0, 0, grandparentW + dw, grandparentH + dh, SETPOS_FLAGS);
845void IGraphicsWin::HideMouseCursor(
bool hide,
bool lock)
847 if (mCursorHidden == hide)
852 mHiddenCursorX = mCursorX;
853 mHiddenCursorY = mCursorY;
856 mCursorHidden =
true;
857 mCursorLock = lock && !mTabletInput;
862 MoveMouseCursor(mHiddenCursorX, mHiddenCursorY);
865 mCursorHidden =
false;
870void IGraphicsWin::MoveMouseCursor(
float x,
float y)
875 const float scale = GetTotalScale();
878 p.x = std::round(x * scale);
879 p.y = std::round(y * scale);
881 ::ClientToScreen(mPlugWnd, &p);
883 if (SetCursorPos(p.x, p.y))
886 ScreenToClient(mPlugWnd, &p);
888 mHiddenCursorX = mCursorX = p.x / scale;
889 mHiddenCursorY = mCursorY = p.y / scale;
893ECursor IGraphicsWin::SetMouseCursor(ECursor cursorType)
899 case ECursor::ARROW: cursor = LoadCursor(NULL, IDC_ARROW);
break;
900 case ECursor::IBEAM: cursor = LoadCursor(NULL, IDC_IBEAM);
break;
901 case ECursor::WAIT: cursor = LoadCursor(NULL, IDC_WAIT);
break;
902 case ECursor::CROSS: cursor = LoadCursor(NULL, IDC_CROSS);
break;
903 case ECursor::UPARROW: cursor = LoadCursor(NULL, IDC_UPARROW);
break;
904 case ECursor::SIZENWSE: cursor = LoadCursor(NULL, IDC_SIZENWSE);
break;
905 case ECursor::SIZENESW: cursor = LoadCursor(NULL, IDC_SIZENESW);
break;
906 case ECursor::SIZEWE: cursor = LoadCursor(NULL, IDC_SIZEWE);
break;
907 case ECursor::SIZENS: cursor = LoadCursor(NULL, IDC_SIZENS);
break;
908 case ECursor::SIZEALL: cursor = LoadCursor(NULL, IDC_SIZEALL);
break;
909 case ECursor::INO: cursor = LoadCursor(NULL, IDC_NO);
break;
910 case ECursor::HAND: cursor = LoadCursor(NULL, IDC_HAND);
break;
911 case ECursor::APPSTARTING: cursor = LoadCursor(NULL, IDC_APPSTARTING);
break;
912 case ECursor::HELP: cursor = LoadCursor(NULL, IDC_HELP);
break;
914 cursor = LoadCursor(NULL, IDC_ARROW);
921bool IGraphicsWin::MouseCursorIsLocked()
926void IGraphicsWin::GetMouseLocation(
float& x,
float&y)
const
930 ScreenToClient(mPlugWnd, &p);
932 const float scale = GetTotalScale();
939void IGraphicsWin::CreateGLContext()
941 PIXELFORMATDESCRIPTOR pfd =
943 sizeof(PIXELFORMATDESCRIPTOR),
945 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
961 HDC dc = GetDC(mPlugWnd);
962 int fmt = ChoosePixelFormat(dc, &pfd);
963 SetPixelFormat(dc, fmt, &pfd);
964 mHGLRC = wglCreateContext(dc);
965 wglMakeCurrent(dc, mHGLRC);
970 auto wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress(
"wglCreateContextAttribsARB");
972 if (wglCreateContextAttribsARB)
974 wglDeleteContext(mHGLRC);
976 const int attribList[] = {
977 WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
978 WGL_CONTEXT_MINOR_VERSION_ARB, 3,
979 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
983 mHGLRC = wglCreateContextAttribsARB(dc, 0, attribList);
984 wglMakeCurrent(dc, mHGLRC);
991 DBGMSG(
"Error initializing glad");
995 ReleaseDC(mPlugWnd, dc);
998void IGraphicsWin::DestroyGLContext()
1000 wglMakeCurrent(NULL, NULL);
1001 wglDeleteContext(mHGLRC);
1005void IGraphicsWin::ActivateGLContext()
1008 mStartHDC = wglGetCurrentDC();
1009 mStartHGLRC = wglGetCurrentContext();
1010 HDC dc = GetDC(mPlugWnd);
1011 wglMakeCurrent(dc, mHGLRC);
1015void IGraphicsWin::DeactivateGLContext()
1018 ReleaseDC(mPlugWnd, (HDC) GetPlatformContext());
1019 wglMakeCurrent(mStartHDC, mStartHGLRC);
1023EMsgBoxResult IGraphicsWin::ShowMessageBox(
const char* str,
const char* title, EMsgBoxType type, IMsgBoxCompletionHandlerFunc completionHandler)
1025 ReleaseMouseCapture();
1027 EMsgBoxResult result =
static_cast<EMsgBoxResult
>(MessageBoxW(GetMainWnd(), UTF8AsUTF16(str).Get(), UTF8AsUTF16(title).Get(),
static_cast<int>(type)));
1029 if (completionHandler)
1030 completionHandler(result);
1035void* IGraphicsWin::OpenWindow(
void* pParent)
1037 mParentWnd = (HWND) pParent;
1038 int screenScale = GetScaleForHWND(mParentWnd);
1039 int x = 0, y = 0, w = WindowWidth() * screenScale, h = WindowHeight() * screenScale;
1044 GetWindowRect((HWND) pParent, &pR);
1045 GetWindowRect(mPlugWnd, &cR);
1047 x = cR.left - pR.left;
1048 y = cR.top - pR.top;
1049 w = cR.right - cR.left;
1050 h = cR.bottom - cR.top;
1053 if (nWndClassReg++ == 0)
1055 WNDCLASSW wndClass = { CS_DBLCLKS | CS_OWNDC, WndProc, 0, 0, mHInstance, 0, 0, 0, 0, wndClassName };
1056 RegisterClassW(&wndClass);
1059 mPlugWnd = CreateWindowW(wndClassName, L
"IPlug", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, x, y, w, h, mParentWnd, 0, mHInstance,
this);
1061 HDC dc = GetDC(mPlugWnd);
1062 SetPlatformContext(dc);
1063 ReleaseDC(mPlugWnd, dc);
1069 OnViewInitialized((
void*) dc);
1071 SetScreenScale(screenScale);
1073 GetDelegate()->LayoutUI(
this);
1075 if (MultiTouchEnabled() && GetSystemMetrics(SM_DIGITIZER) & NID_MULTI_INPUT)
1077 RegisterTouchWindow(mPlugWnd, 0);
1080 if (!mPlugWnd && --nWndClassReg == 0)
1082 UnregisterClassW(wndClassName, mHInstance);
1086 SetAllControlsDirty();
1089 if (mPlugWnd && TooltipsEnabled())
1091 static const INITCOMMONCONTROLSEX iccex = {
sizeof(INITCOMMONCONTROLSEX), ICC_TAB_CLASSES };
1093 if (InitCommonControlsEx(&iccex))
1095 mTooltipWnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TTS_NOPREFIX | TTS_ALWAYSTIP,
1096 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, mPlugWnd, NULL, mHInstance, NULL);
1099 SetWindowPos(mTooltipWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1100 TOOLINFOW ti = { TTTOOLINFOW_V2_SIZE, TTF_IDISHWND | TTF_SUBCLASS, mPlugWnd, (UINT_PTR) mPlugWnd, {0, 0, 0, 0}, NULL, NULL, 0, NULL };
1101 SendMessageW(mTooltipWnd, TTM_ADDTOOLW, 0, (LPARAM) &ti);
1102 SendMessageW(mTooltipWnd, TTM_SETMAXTIPWIDTH, 0, TOOLTIPWND_MAXWIDTH);
1107 EnableTooltips(
false);
1110 wglMakeCurrent(NULL, NULL);
1114 GetDelegate()->OnUIOpen();
1119static void GetWndClassName(HWND hWnd, WDL_String* pStr)
1121 wchar_t cStrW[MAX_CLASSNAME_LEN] = {
'\0'};
1122 GetClassNameW(hWnd, cStrW, MAX_CLASSNAME_LEN);
1123 pStr->Set(UTF16AsUTF8(cStrW).Get());
1126BOOL CALLBACK IGraphicsWin::FindMainWindow(HWND hWnd, LPARAM lParam)
1132 GetWindowThreadProcessId(hWnd, &wPID);
1134 GetWndClassName(hWnd, &str);
1135 if (wPID == pGraphics->mPID && !strcmp(str.Get(), pGraphics->mMainWndClassName.Get()))
1137 pGraphics->mMainWnd = hWnd;
1144HWND IGraphicsWin::GetMainWnd()
1150 HWND parentWnd = mParentWnd;
1153 mMainWnd = parentWnd;
1154 parentWnd = GetParent(mMainWnd);
1157 GetWndClassName(mMainWnd, &mMainWndClassName);
1159 else if (CStringHasContents(mMainWndClassName.Get()))
1161 mPID = GetCurrentProcessId();
1162 EnumWindows(FindMainWindow, (LPARAM)
this);
1168IRECT IGraphicsWin::GetWindowRECT()
1173 GetWindowRect(mPlugWnd, &r);
1174 r.right -= TOOLWIN_BORDER_W;
1175 r.bottom -= TOOLWIN_BORDER_H;
1176 return IRECT(r.left, r.top, r.right, r.bottom);
1181void IGraphicsWin::CloseWindow()
1188 KillTimer(mPlugWnd, IPLUG_TIMER_ID);
1191 ScopedGLContext scopedGLCtx {
this};
1199 SetPlatformContext(
nullptr);
1203 DestroyWindow(mTooltipWnd);
1205 mShowingTooltip =
false;
1209 DestroyWindow(mPlugWnd);
1212 if (--nWndClassReg == 0)
1214 UnregisterClassW(wndClassName, mHInstance);
1219bool IGraphicsWin::PlatformSupportsMultiTouch()
const
1221 return GetSystemMetrics(SM_DIGITIZER) & NID_MULTI_INPUT;
1224IPopupMenu* IGraphicsWin::GetItemMenu(
long idx,
long& idxInMenu,
long& offsetIdx,
IPopupMenu& baseMenu)
1226 long oldIDx = offsetIdx;
1227 offsetIdx += baseMenu.NItems();
1229 if (idx < offsetIdx)
1231 idxInMenu = idx - oldIDx;
1237 for (
int i = 0; i< baseMenu.NItems(); i++)
1240 if (pMenuItem->GetSubmenu())
1242 pMenu = GetItemMenu(idx, idxInMenu, offsetIdx, *pMenuItem->GetSubmenu());
1252HMENU IGraphicsWin::CreateMenu(
IPopupMenu& menu,
long* pOffsetIdx)
1254 HMENU hMenu = ::CreatePopupMenu();
1256 WDL_String escapedText;
1259 long offset = *pOffsetIdx;
1260 long nItems = menu.NItems();
1261 *pOffsetIdx += nItems;
1264 for (
int i = 0; i < nItems; i++)
1268 if (pMenuItem->GetIsSeparator())
1270 AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
1274 const char* str = pMenuItem->GetText();
1275 int maxlen = strlen(str) + menu.GetPrefix() ? 50 : 0;
1276 WDL_String entryText(str);
1278 if (menu.GetPrefix())
1280 switch (menu.GetPrefix())
1284 entryText.SetFormatted(maxlen,
"%1d: %s", i+1, str);
break;
1288 entryText.SetFormatted(maxlen,
"%02d: %s", i+1, str);
break;
1292 entryText.SetFormatted(maxlen,
"%03d: %s", i+1, str);
break;
1299 if (strchr(entryText.Get(),
'&'))
1301 for (
int c = 0; c < entryText.GetLength(); c++)
1302 if (entryText.Get()[c] ==
'&')
1303 entryText.Insert(
"&", c++);
1307 if (nItems < 160 && menu.NItemsPerColumn() > 0 && inc && !(inc % menu.NItemsPerColumn()))
1308 flags |= MF_MENUBARBREAK;
1310 if (pMenuItem->GetEnabled())
1311 flags |= MF_ENABLED;
1314 if (pMenuItem->GetIsTitle())
1315 flags |= MF_DISABLED;
1316 if (pMenuItem->GetChecked())
1317 flags |= MF_CHECKED;
1319 flags |= MF_UNCHECKED;
1321 if (pMenuItem->GetSubmenu())
1323 HMENU submenu = CreateMenu(*pMenuItem->GetSubmenu(), pOffsetIdx);
1326 AppendMenuW(hMenu, flags|MF_POPUP, (UINT_PTR)submenu, UTF8AsUTF16(entryText).Get());
1331 AppendMenuW(hMenu, flags, offset + inc, UTF8AsUTF16(entryText).Get());
1343 HMENU hMenu = CreateMenu(menu, &offsetIdx);
1350 const float scale = GetTotalScale();
1352 cPos.x = bounds.L * scale;
1353 cPos.y = bounds.B * scale;
1355 ::ClientToScreen(mPlugWnd, &cPos);
1357 if (TrackPopupMenu(hMenu, TPM_LEFTALIGN, cPos.x, cPos.y, 0, mPlugWnd, 0))
1360 if (PeekMessage(&msg, mPlugWnd, WM_COMMAND, WM_COMMAND, PM_REMOVE))
1362 if (HIWORD(msg.wParam) == 0)
1364 long res = LOWORD(msg.wParam);
1369 IPopupMenu* pReturnMenu = GetItemMenu(res, idx, offsetIdx, menu);
1372 result = pReturnMenu;
1373 result->SetChosenItemIdx(idx);
1376 if (pReturnMenu && pReturnMenu->GetFunction())
1377 pReturnMenu->ExecFunction();
1385 RECT r = { 0, 0,
static_cast<LONG
>(WindowWidth() * GetScreenScale()),
static_cast<LONG
>(WindowHeight() * GetScreenScale()) };
1386 InvalidateRect(mPlugWnd, &r, FALSE);
1394void IGraphicsWin::CreatePlatformTextEntry(
int paramIdx,
const IText& text,
const IRECT& bounds,
int length,
const char* str)
1401 switch (text.mAlign)
1403 case EAlign::Near: editStyle = ES_LEFT;
break;
1404 case EAlign::Far: editStyle = ES_RIGHT;
break;
1405 case EAlign::Center:
1406 default: editStyle = ES_CENTER;
break;
1409 const float scale = GetTotalScale();
1412 mParamEditWnd = CreateWindowW(L
"EDIT", UTF8AsUTF16(str).Get(), ES_AUTOHSCROLL | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | ES_MULTILINE | editStyle,
1413 scaledBounds.L, scaledBounds.T, scaledBounds.
W()+1, scaledBounds.
H()+1,
1414 mPlugWnd, (HMENU) PARAM_EDIT_ID, mHInstance, 0);
1416 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
1418 LOGFONTW lFont = { 0 };
1419 HFontHolder* hfontHolder = hfontStorage.Find(text.mFont);
1420 GetObjectW(hfontHolder->mHFont,
sizeof(LOGFONTW), &lFont);
1421 lFont.lfHeight = text.mSize * scale;
1422 mEditFont = CreateFontIndirectW(&lFont);
1424 assert(hfontHolder &&
"font not found - did you forget to load it?");
1426 mEditParam = paramIdx > kNoParameter ? GetDelegate()->GetParam(paramIdx) :
nullptr;
1430 SendMessageW(mParamEditWnd, EM_LIMITTEXT, (WPARAM) length, 0);
1431 SendMessageW(mParamEditWnd, WM_SETFONT, (WPARAM) mEditFont, 0);
1432 SendMessageW(mParamEditWnd, EM_SETSEL, 0, -1);
1434 if (text.mVAlign == EVAlign::Middle)
1436 double size = text.mSize * scale;
1437 double offset = (scaledBounds.
H() - size) / 2.0;
1438 RECT formatRect{0, (LONG) offset, (LONG) scaledBounds.
W() + 1, (LONG) scaledBounds.
H() + 1};
1439 SendMessageW(mParamEditWnd, EM_SETRECT, 0, (LPARAM) &formatRect);
1442 SetFocus(mParamEditWnd);
1444 mDefEditProc = (WNDPROC) SetWindowLongPtrW(mParamEditWnd, GWLP_WNDPROC, (LONG_PTR) ParamEditProc);
1445 SetWindowLongPtrW(mParamEditWnd, GWLP_USERDATA, 0xdeadf00b);
1448bool IGraphicsWin::RevealPathInExplorerOrFinder(WDL_String& path,
bool select)
1450 bool success =
false;
1452 if (path.GetLength())
1454 WCHAR winDir[IPLUG_WIN_MAX_WIDE_PATH];
1455 UINT len = GetSystemDirectoryW(winDir, IPLUG_WIN_MAX_WIDE_PATH);
1457 if (len && !(len > MAX_PATH - 2))
1459 winDir[len] = L
'\\';
1460 winDir[++len] = L
'\0';
1462 WDL_String explorerParams;
1465 explorerParams.Append(
"/select,");
1467 explorerParams.Append(
"\"");
1468 explorerParams.Append(path.Get());
1469 explorerParams.Append(
"\\\"");
1473 if ((result=::ShellExecuteW(NULL, L
"open", L
"explorer.exe", UTF8AsUTF16(explorerParams).Get(), winDir, SW_SHOWNORMAL)) <= (HINSTANCE) 32)
1481void IGraphicsWin::PromptForFile(WDL_String& fileName, WDL_String& path, EFileAction action,
const char* ext, IFileDialogCompletionHandlerFunc completionHandler)
1483 if (!WindowIsOpen())
1489 wchar_t fileNameWide[_MAX_PATH];
1491 UTF8ToUTF16(fileNameWide, fileName.Get(), _MAX_PATH);
1496 UTF8AsUTF16 directoryWide(path);
1499 memset(&ofn, 0,
sizeof(OPENFILENAMEW));
1501 ofn.lStructSize =
sizeof(OPENFILENAMEW);
1502 ofn.hwndOwner = (HWND) GetWindow();
1503 ofn.lpstrFile = fileNameWide;
1504 ofn.nMaxFile = _MAX_PATH - 1;
1505 ofn.lpstrInitialDir = directoryWide.Get();
1506 ofn.Flags = OFN_PATHMUSTEXIST;
1508 if (CStringHasContents(ext))
1510 wchar_t extStr[256];
1511 wchar_t defExtStr[16];
1512 int i, p, n = strlen(ext);
1513 bool separator =
true;
1515 for (i = 0, p = 0; i < n; ++i)
1530 extStr[p++] = ext[i];
1534 wcscpy(&extStr[p], extStr);
1535 extStr[p + p] =
'\0';
1536 ofn.lpstrFilter = extStr;
1538 for (i = 0, p = 0; i < n && ext[i] !=
' '; ++i)
1539 defExtStr[p++] = ext[i];
1541 defExtStr[p++] =
'\0';
1542 ofn.lpstrDefExt = defExtStr;
1549 case EFileAction::Save:
1550 ofn.Flags |= OFN_OVERWRITEPROMPT;
1551 rc = GetSaveFileNameW(&ofn);
1553 case EFileAction::Open:
1555 ofn.Flags |= OFN_FILEMUSTEXIST;
1556 rc = GetOpenFileNameW(&ofn);
1562 char drive[_MAX_DRIVE];
1563 char directoryOutCStr[_MAX_PATH];
1565 UTF16AsUTF8 tempUTF8(ofn.lpstrFile);
1567 if (_splitpath_s(tempUTF8.Get(), drive,
sizeof(drive), directoryOutCStr,
sizeof(directoryOutCStr), NULL, 0, NULL, 0) == 0)
1570 path.Append(directoryOutCStr);
1573 fileName.Set(tempUTF8.Get());
1581 if (completionHandler)
1583 completionHandler(fileName, path);
1586 ReleaseMouseCapture();
1589void IGraphicsWin::PromptForDirectory(WDL_String& dir, IFileDialogCompletionHandlerFunc completionHandler)
1592 memset(&bi, 0,
sizeof(bi));
1594 bi.ulFlags = BIF_USENEWUI;
1595 bi.hwndOwner = mPlugWnd;
1596 bi.lpszTitle = L
"Choose a Directory";
1599 ::OleInitialize(NULL);
1600 LPITEMIDLIST pIDL = ::SHBrowseForFolderW(&bi);
1604 wchar_t buffer[_MAX_PATH] = {
'\0'};
1606 if (::SHGetPathFromIDListW(pIDL, buffer) != 0)
1608 dir.Set(UTF16AsUTF8(buffer).Get());
1613 CoTaskMemFree(pIDL);
1620 if (completionHandler)
1622 WDL_String fileName;
1623 completionHandler(fileName, dir);
1626 ReleaseMouseCapture();
1628 ::OleUninitialize();
1631static UINT_PTR CALLBACK CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
1633 if (uiMsg == WM_INITDIALOG && lParam)
1635 CHOOSECOLORW* cc = (CHOOSECOLORW*) lParam;
1636 if (cc && cc->lCustData)
1638 const wchar_t* strWide = (
const wchar_t*) cc->lCustData;
1639 SetWindowTextW(hdlg, strWide);
1641 uiSetRGB = RegisterWindowMessageW(SETRGBSTRINGW);
1642 SendMessageW(hdlg, uiSetRGB, 0, (LPARAM) cc->rgbResult);
1648bool IGraphicsWin::PromptForColor(
IColor& color,
const char* prompt, IColorPickerHandlerFunc func)
1650 ReleaseMouseCapture();
1655 UTF8AsUTF16 promptWide(prompt);
1657 const COLORREF w = RGB(255, 255, 255);
1658 static COLORREF customColorStorage[16] = { w, w, w, w, w, w, w, w, w, w, w, w, w, w, w, w };
1661 memset(&cc, 0,
sizeof(CHOOSECOLORW));
1662 cc.lStructSize =
sizeof(CHOOSECOLORW);
1663 cc.hwndOwner = mPlugWnd;
1664 cc.rgbResult = RGB(color.R, color.G, color.B);
1665 cc.lpCustColors = customColorStorage;
1666 cc.lCustData = (LPARAM) promptWide.Get();
1667 cc.lpfnHook = CCHookProc;
1668 cc.Flags = CC_RGBINIT | CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK;
1670 if (ChooseColorW(&cc))
1672 color.R = GetRValue(cc.rgbResult);
1673 color.G = GetGValue(cc.rgbResult);
1674 color.B = GetBValue(cc.rgbResult);
1684bool IGraphicsWin::OpenURL(
const char* url,
const char* msgWindowTitle,
const char* confirmMsg,
const char* errMsgOnFailure)
1686 if (confirmMsg && MessageBoxW(mPlugWnd, UTF8AsUTF16(confirmMsg).Get(), UTF8AsUTF16(msgWindowTitle).Get(), MB_YESNO) != IDYES)
1690 DWORD inetStatus = 0;
1691 if (InternetGetConnectedState(&inetStatus, 0))
1693 if (ShellExecuteW(mPlugWnd, L
"open", UTF8AsUTF16(url).Get(), 0, 0, SW_SHOWNORMAL) > HINSTANCE(32))
1698 if (errMsgOnFailure)
1700 MessageBoxW(mPlugWnd, UTF8AsUTF16(errMsgOnFailure).Get(), UTF8AsUTF16(msgWindowTitle).Get(), MB_OK);
1705void IGraphicsWin::SetTooltip(
const char* tooltip)
1707 UTF8AsUTF16 tipWide(tooltip);
1708 TOOLINFOW ti = { TTTOOLINFOW_V2_SIZE, 0, mPlugWnd, (UINT_PTR) mPlugWnd, {0, 0, 0, 0}, NULL, NULL, 0, NULL };
1709 ti.lpszText =
const_cast<wchar_t*
>(tipWide.Get());
1710 SendMessageW(mTooltipWnd, TTM_UPDATETIPTEXTW, 0, (LPARAM) &ti);
1713void IGraphicsWin::ShowTooltip()
1715 if (mTooltipIdx > -1)
1717 if (
auto* pTooltipControl = GetControl(mTooltipIdx))
1719 const char* tooltipStr = pTooltipControl->GetTooltip();
1722 SetTooltip(tooltipStr);
1723 mShowingTooltip =
true;
1729void IGraphicsWin::HideTooltip()
1731 if (mShowingTooltip)
1734 mShowingTooltip =
false;
1738bool IGraphicsWin::GetTextFromClipboard(WDL_String& str)
1740 bool result =
false;
1742 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1744 if (OpenClipboard(0))
1746 HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
1750 WCHAR *origStr = (WCHAR*) GlobalLock(hglb);
1754 UTF16ToUTF8(str, origStr);
1770bool IGraphicsWin::SetTextInClipboard(
const char* str)
1772 if (!OpenClipboard(mMainWnd))
1782 const int lenWide = UTF8ToUTF16Len(str);
1785 HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, lenWide*
sizeof(WCHAR));
1793 LPWSTR lpstrCopy = (LPWSTR) GlobalLock(hglbCopy);
1798 UTF8ToUTF16(lpstrCopy, str, lenWide);
1799 GlobalUnlock(hglbCopy);
1802 result = SetClipboardData(CF_UNICODETEXT, hglbCopy);
1806 GlobalFree(hglbCopy);
1815bool IGraphicsWin::SetFilePathInClipboard(
const char* path)
1817 if (!OpenClipboard(mMainWnd))
1822 UTF8AsUTF16 pathWide(path);
1826 HGLOBAL hGlobal = GlobalAlloc(GHND,
sizeof(DROPFILES) + (
sizeof(
wchar_t) * (pathWide.GetLength() + 1)));
1831 DROPFILES* pDropFiles = (DROPFILES*) GlobalLock(hGlobal);
1832 bool result =
false;
1838 pDropFiles->pFiles =
sizeof(DROPFILES);
1839 pDropFiles->pt = { 0, 0 };
1840 pDropFiles->fNC =
true;
1841 pDropFiles->fWide =
true;
1843 std::copy_n(pathWide.Get(), pathWide.GetLength(),
reinterpret_cast<wchar_t*
>(&pDropFiles[1]));
1845 GlobalUnlock(hGlobal);
1847 result = SetClipboardData(CF_HDROP, hGlobal);
1852 GlobalFree(hGlobal);
1858bool IGraphicsWin::InitiateExternalFileDragDrop(
const char* path,
const IRECT& )
1860 using namespace DragAndDropHelpers;
1861 OleInitialize(
nullptr);
1863 FORMATETC format = { CF_HDROP,
nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1865 DataObject* dataObj =
new DataObject(&format, path);
1866 DropSource* dropSource =
new DropSource();
1869 HRESULT ret = DoDragDrop(dataObj, dropSource, DROPEFFECT_COPY, &dropEffect);
1870 bool success = SUCCEEDED(ret);
1873 dropSource->Release();
1877 ReleaseMouseCapture();
1882static HFONT GetHFont(
const char* fontName,
int weight,
bool italic,
bool underline, DWORD quality = DEFAULT_QUALITY,
bool enumerate =
false)
1884 HDC hdc = GetDC(NULL);
1885 HFONT font =
nullptr;
1890 lFont.lfEscapement = 0;
1891 lFont.lfOrientation = 0;
1892 lFont.lfWeight = weight;
1893 lFont.lfItalic = italic;
1894 lFont.lfUnderline = underline;
1895 lFont.lfStrikeOut =
false;
1896 lFont.lfCharSet = DEFAULT_CHARSET;
1897 lFont.lfOutPrecision = OUT_TT_PRECIS;
1898 lFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1899 lFont.lfQuality = quality;
1900 lFont.lfPitchAndFamily = DEFAULT_PITCH;
1902 wcsncpy(lFont.lfFaceName, UTF8AsUTF16(fontName).Get(), LF_FACESIZE);
1904 auto enumProc = [](
const LOGFONTW* pLFont,
const TEXTMETRICW* pTextMetric, DWORD FontType, LPARAM lParam)
1909 if ((!enumerate || EnumFontFamiliesExW(hdc, &lFont, enumProc, NULL, 0) == -1))
1910 font = CreateFontIndirectW(&lFont);
1914 wchar_t selectedFontName[64] = {
'\0'};
1916 SelectFont(hdc, font);
1917 GetTextFaceW(hdc, 64, selectedFontName);
1918 if (strcmp(UTF16AsUTF8(selectedFontName).Get(), fontName))
1925 ReleaseDC(NULL, hdc);
1930PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fileNameOrResID)
1932 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
1934 void* pFontMem =
nullptr;
1936 WDL_String fullPath;
1938 const EResourceLocation fontLocation =
LocateResource(fileNameOrResID,
"ttf", fullPath, GetBundleID(), GetWinModuleHandle(),
nullptr);
1940 if (fontLocation == kNotFound)
1943 switch (fontLocation)
1947 HANDLE file = CreateFileW(UTF8AsUTF16(fullPath).Get(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1948 PlatformFontPtr ret =
nullptr;
1951 HANDLE mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
1954 resSize = (int) GetFileSize(file,
nullptr);
1955 pFontMem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
1956 ret = LoadPlatformFont(fontID, pFontMem, resSize);
1957 UnmapViewOfFile(pFontMem);
1958 CloseHandle(mapping);
1967 pFontMem =
const_cast<void *
>(
LoadWinResource(fullPath.Get(),
"ttf", resSize, GetWinModuleHandle()));
1968 return LoadPlatformFont(fontID, pFontMem, resSize);
1976PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fontName, ETextStyle style)
1978 int weight = style == ETextStyle::Bold ? FW_BOLD : FW_REGULAR;
1979 bool italic = style == ETextStyle::Italic;
1980 bool underline =
false;
1981 DWORD quality = DEFAULT_QUALITY;
1983 HFONT font = GetHFont(fontName, weight, italic, underline, quality,
true);
1985 return PlatformFontPtr(font ?
new Font(font,
TextStyleString(style),
true) :
nullptr);
1988PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
void* pData,
int dataSize)
1990 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
1992 std::unique_ptr<InstalledFont> pFont;
1993 void* pFontMem = pData;
1994 int resSize = dataSize;
1996 pFont = std::make_unique<InstalledFont>(pFontMem, resSize);
1998 if (pFontMem && pFont && pFont->IsValid())
2000 IFontInfo fontInfo(pFontMem, resSize, 0);
2001 WDL_String family = fontInfo.GetFamily();
2002 int weight = fontInfo.IsBold() ? FW_BOLD : FW_REGULAR;
2003 bool italic = fontInfo.IsItalic();
2004 bool underline = fontInfo.IsUnderline();
2006 HFONT font = GetHFont(family.Get(), weight, italic, underline);
2010 fontStorage.Add(pFont.release(), fontID);
2011 return PlatformFontPtr(
new Font(font,
"",
false));
2018void IGraphicsWin::CachePlatformFont(
const char* fontID,
const PlatformFontPtr& font)
2020 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
2022 HFONT hfont = font->GetDescriptor();
2024 if (!hfontStorage.Find(fontID))
2028DWORD WINAPI VBlankRun(LPVOID lpParam)
2031 return pGraphics->OnVBlankRun();
2034void IGraphicsWin::StartVBlankThread(HWND hWnd)
2036 mVBlankWindow = hWnd;
2037 mVBlankShutdown =
false;
2039 mVBlankThread = ::CreateThread(NULL, 0, VBlankRun,
this, 0, &threadId);
2042void IGraphicsWin::StopVBlankThread()
2044 if (mVBlankThread != INVALID_HANDLE_VALUE)
2046 mVBlankShutdown =
true;
2047 ::WaitForSingleObject(mVBlankThread, 10000);
2048 mVBlankThread = INVALID_HANDLE_VALUE;
2064typedef UINT32 D3DKMT_HANDLE;
2065typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
2067typedef struct _D3DKMT_OPENADAPTERFROMHDC
2070 D3DKMT_HANDLE hAdapter;
2072 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2073} D3DKMT_OPENADAPTERFROMHDC;
2075typedef struct _D3DKMT_CLOSEADAPTER
2077 D3DKMT_HANDLE hAdapter;
2078} D3DKMT_CLOSEADAPTER;
2080typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT
2082 D3DKMT_HANDLE hAdapter;
2083 D3DKMT_HANDLE hDevice;
2084 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2085} D3DKMT_WAITFORVERTICALBLANKEVENT;
2088typedef NTSTATUS(WINAPI* D3DKMTOpenAdapterFromHdc)(D3DKMT_OPENADAPTERFROMHDC* Arg1);
2089typedef NTSTATUS(WINAPI* D3DKMTCloseAdapter)(
const D3DKMT_CLOSEADAPTER* Arg1);
2090typedef NTSTATUS(WINAPI* D3DKMTWaitForVerticalBlankEvent)(
const D3DKMT_WAITFORVERTICALBLANKEVENT* Arg1);
2092DWORD IGraphicsWin::OnVBlankRun()
2094 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2098 float rateFallback = 60.0f;
2099 int rateMS = (int)(1000.0f / rateFallback);
2107 D3DKMTOpenAdapterFromHdc pOpen =
nullptr;
2108 D3DKMTCloseAdapter pClose =
nullptr;
2109 D3DKMTWaitForVerticalBlankEvent pWait =
nullptr;
2110 HINSTANCE hInst = LoadLibraryW(L
"gdi32.dll");
2112 if (hInst !=
nullptr)
2114 pOpen = (D3DKMTOpenAdapterFromHdc) GetProcAddress((HMODULE) hInst,
"D3DKMTOpenAdapterFromHdc");
2115 pClose = (D3DKMTCloseAdapter) GetProcAddress((HMODULE) hInst,
"D3DKMTCloseAdapter");
2116 pWait = (D3DKMTWaitForVerticalBlankEvent) GetProcAddress((HMODULE) hInst,
"D3DKMTWaitForVerticalBlankEvent");
2123 if (!pOpen || !pClose || !pWait)
2125 while (mVBlankShutdown ==
false)
2135 bool adapterIsOpen =
false;
2136 DWORD adapterLastFailTime = 0;
2137 _D3DKMT_WAITFORVERTICALBLANKEVENT we = { 0 };
2139 while (mVBlankShutdown ==
false)
2144 if (adapterLastFailTime < ::GetTickCount() - 1000)
2147 D3DKMT_OPENADAPTERFROMHDC openAdapterData = { 0 };
2148 HDC hDC = GetDC(mVBlankWindow);
2149 openAdapterData.hDc = hDC;
2150 NTSTATUS status = (*pOpen)(&openAdapterData);
2154 adapterLastFailTime = 0;
2155 adapterIsOpen =
true;
2156 we.hAdapter = openAdapterData.hAdapter;
2158 we.VidPnSourceId = openAdapterData.VidPnSourceId;
2163 adapterLastFailTime = ::GetTickCount();
2172 NTSTATUS status = (*pWait)(&we);
2176 _D3DKMT_CLOSEADAPTER ca;
2177 ca.hAdapter = we.hAdapter;
2179 adapterIsOpen =
false;
2196 _D3DKMT_CLOSEADAPTER ca;
2197 ca.hAdapter = we.hAdapter;
2199 adapterIsOpen =
false;
2204 if (hInst !=
nullptr)
2206 FreeLibrary((HMODULE)hInst);
2213void IGraphicsWin::VBlankNotify()
2216 ::PostMessageW(mVBlankWindow, WM_VBLANK, mVBlankCount, 0);
2220#if defined IGRAPHICS_SKIA
2221 #include "IGraphicsSkia.cpp"
2225#elif defined IGRAPHICS_NANOVG
2226 #include "IGraphicsNanoVG.cpp"
2227#ifdef IGRAPHICS_FREETYPE
2228#define FONS_USE_FREETYPE
2229 #pragma comment(lib, "freetype.lib")
Common paths useful for plug-ins.
const void * LoadWinResource(const char *resID, const char *type, int &sizeInBytes, void *pHInstance)
Load a resource from the binary (windows only).
EResourceLocation LocateResource(const char *fileNameOrResID, const char *type, WDL_String &result, const char *bundleID, void *pHInstance, const char *sharedResourcesSubPath)
Find the absolute path of a resource based on it's file name (e.g.
An editor delegate base class that uses IGraphics for the UI.
virtual ECursor SetMouseCursor(ECursor cursorType=ECursor::ARROW)
Sets the mouse cursor to one of ECursor (implementations should return the result of the base impleme...
IGraphics platform class for Windows.
EParamType Type() const
Get the parameter's type.
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
const IRECT & Get(int idx) const
Get an IRECT from the list (will crash if idx is invalid)
static const char * TextStyleString(ETextStyle style)
Helper to get a CString based on ETextStyle.
Used to group mouse coordinates with mouse modifier information.
Used to manage color data, independent of draw class/platform.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Used to manage mouse modifiers i.e.
Used to manage a rectangular area, independent of draw class/platform.
void PixelAlign()
Pixel aligns the rect in an inclusive manner (moves all points outwards)
IRECT GetScaled(float scale) const
Get a copy of this IRECT with all values multiplied by scale.
void Scale(float scale)
Multiply each field of this IRECT by scale.
IText is used to manage font and text/text entry style for a piece of text on the UI,...