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 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 - Private Classes and Structs
65 : mFontHandle(
nullptr)
70 mFontHandle = AddFontMemResourceEx(data, resSize, NULL, &numFonts);
77 RemoveFontMemResourceEx(mFontHandle);
83 bool IsValid()
const {
return mFontHandle; }
93 LOGFONTW lFont = { 0 };
94 GetObjectW(hfont,
sizeof(LOGFONTW), &lFont);
95 mHFont = CreateFontIndirectW(&lFont);
101class IGraphicsWin::Font :
public PlatformFont
104 Font(HFONT font,
const char* styleName,
bool system)
105 : PlatformFont(system), mFont(font), mStyleName(styleName) {}
111 FontDescriptor GetDescriptor()
override {
return mFont; }
112 IFontDataPtr GetFontData()
override;
116 WDL_String mStyleName;
119IFontDataPtr IGraphicsWin::Font::GetFontData()
121 HDC hdc = CreateCompatibleDC(NULL);
122 IFontDataPtr fontData(
new IFontData());
126 SelectObject(hdc, mFont);
127 const size_t size = ::GetFontData(hdc, 0, 0, NULL, 0);
129 if (size != GDI_ERROR)
131 fontData = std::make_unique<IFontData>(size);
133 if (fontData->GetSize() == size)
135 size_t result = ::GetFontData(hdc, 0x66637474, 0, fontData->Get(), size);
136 if (result == GDI_ERROR)
137 result = ::GetFontData(hdc, 0, 0, fontData->Get(), size);
139 fontData->SetFaceIdx(GetFaceIdx(fontData->Get(), fontData->GetSize(), mStyleName.Get()));
149StaticStorage<IGraphicsWin::InstalledFont> IGraphicsWin::sPlatformFontCache;
150StaticStorage<IGraphicsWin::HFontHolder> IGraphicsWin::sHFontCache;
152#pragma mark - Mouse and tablet helpers
154extern float GetScaleForHWND(HWND hWnd);
156inline IMouseInfo IGraphicsWin::GetMouseInfo(LPARAM lParam, WPARAM wParam)
159 const float scale = GetTotalScale();
160 info.x = mCursorX = GET_X_LPARAM(lParam) / scale;
161 info.y = mCursorY = GET_Y_LPARAM(lParam) / scale;
162 info.ms =
IMouseMod((wParam & MK_LBUTTON), (wParam & MK_RBUTTON), (wParam & MK_SHIFT), (wParam & MK_CONTROL),
164 GetAsyncKeyState(VK_MENU) < 0
166 GetKeyState(VK_MENU) < 0
173void IGraphicsWin::CheckTabletInput(UINT msg)
175 if ((msg == WM_LBUTTONDOWN) || (msg == WM_RBUTTONDOWN) || (msg == WM_MBUTTONDOWN) || (msg == WM_MOUSEMOVE)
176 || (msg == WM_RBUTTONDBLCLK) || (msg == WM_LBUTTONDBLCLK) || (msg == WM_MBUTTONDBLCLK)
177 || (msg == WM_RBUTTONUP) || (msg == WM_LBUTTONUP) || (msg == WM_MBUTTONUP)
178 || (msg == WM_MOUSEHOVER) || (msg == WM_MOUSELEAVE))
180 const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00;
181 const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700;
183 LONG_PTR extraInfo = GetMessageExtraInfo();
184 SetTabletInput(((extraInfo & c_SIGNATURE_MASK) == c_MOUSEEVENTF_FROMTOUCH));
185 mCursorLock &= !mTabletInput;
189void IGraphicsWin::DestroyEditWindow()
193 SetWindowLongPtrW(mParamEditWnd, GWLP_WNDPROC, (LPARAM) mDefEditProc);
194 DestroyWindow(mParamEditWnd);
195 mParamEditWnd =
nullptr;
196 mDefEditProc =
nullptr;
197 DeleteObject(mEditFont);
202void IGraphicsWin::OnDisplayTimer(
int vBlankCount)
205 DWORD msgCount = vBlankCount;
206 DWORD curCount = mVBlankCount;
211 if (mVBlankSkipUntil != 0 && mVBlankSkipUntil > mVBlankCount)
216 mVBlankSkipUntil = 0;
218 if (msgCount != curCount)
226 if (mParamEditWnd && mParamEditMsg != kNone)
228 switch (mParamEditMsg)
232 WCHAR strWide[MAX_WIN32_PARAM_LEN];
233 SendMessageW(mParamEditWnd, WM_GETTEXT, MAX_WIN32_PARAM_LEN, (LPARAM) strWide);
234 SetControlValueAfterTextEdit(UTF16AsUTF8(strWide).Get());
240 ClearInTextEntryControl();
244 mParamEditMsg = kNone;
252 float scale = GetScaleForHWND(mPlugWnd);
253 if (scale != GetScreenScale())
254 SetScreenScale(scale);
260 const float totalScale = GetTotalScale();
263 SetAllControlsClean();
265 for (
int i = 0; i < rects.
Size(); i++)
268 dirtyR.
Scale(totalScale);
270 RECT r = { (LONG)dirtyR.L, (LONG)dirtyR.T, (LONG)dirtyR.R, (LONG)dirtyR.B };
271 InvalidateRect(mPlugWnd, &r, FALSE);
276 IRECT notDirtyR = mEditRECT;
277 notDirtyR.
Scale(totalScale);
279 RECT r2 = { (LONG)notDirtyR.L, (LONG)notDirtyR.T, (LONG)notDirtyR.R, (LONG)notDirtyR.B };
280 ValidateRect(mPlugWnd, &r2);
281 UpdateWindow(mPlugWnd);
282 mParamEditMsg = kUpdate;
287 UpdateWindow(mPlugWnd);
292 curCount = mVBlankCount;
293 if (msgCount != curCount)
296 mVBlankSkipUntil = curCount+1;
306LRESULT CALLBACK IGraphicsWin::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
308 if (msg == WM_CREATE)
310 CREATESTRUCTW* lpcs = (CREATESTRUCTW *) lParam;
311 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM) lpcs->lpCreateParams);
314 if (pGraphics->mVSYNCEnabled)
316 assert((pGraphics->FPS() == 60) &&
"If you want to run at frame rates other than 60FPS");
317 pGraphics->StartVBlankThread(hWnd);
321 int mSec =
static_cast<int>(std::floorf(1000.0f / (pGraphics->FPS())));
322 if (mSec < 20) mSec = 15;
323 SetTimer(hWnd, IPLUG_TIMER_ID, mSec, NULL);
327 DragAcceptFiles(hWnd,
true);
333 if (!pGraphics || hWnd != pGraphics->mPlugWnd)
335 return DefWindowProcW(hWnd, msg, wParam, lParam);
338 if (pGraphics->mParamEditWnd && pGraphics->mParamEditMsg == kEditing)
340 if (msg == WM_RBUTTONDOWN || (msg == WM_LBUTTONDOWN))
342 pGraphics->mParamEditMsg = kCancel;
345 return DefWindowProcW(hWnd, msg, wParam, lParam);
348 auto IsTouchEvent = []() {
349 const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00;
350 const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700;
351 LONG_PTR extraInfo = GetMessageExtraInfo();
352 return ((extraInfo & c_SIGNATURE_MASK) == c_MOUSEEVENTF_FROMTOUCH);
355 pGraphics->CheckTabletInput(msg);
360 pGraphics->OnDisplayTimer(wParam);
364 if (wParam == IPLUG_TIMER_ID)
365 pGraphics->OnDisplayTimer(0);
379 pGraphics->HideTooltip();
380 if (pGraphics->mParamEditWnd)
382 pGraphics->mParamEditMsg = kCommit;
387 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
388 std::vector<IMouseInfo> list{ info };
389 pGraphics->OnMouseDown(list);
394 pGraphics->OnSetCursor();
402 if (!(wParam & (MK_LBUTTON | MK_RBUTTON)))
404 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
405 if (pGraphics->OnMouseOver(info.x, info.y, info.ms))
407 TRACKMOUSEEVENT eventTrack = {
sizeof(TRACKMOUSEEVENT), TME_LEAVE, hWnd, HOVER_DEFAULT };
408 if (pGraphics->TooltipsEnabled())
410 int c = pGraphics->GetMouseOver();
411 if (c != pGraphics->mTooltipIdx)
413 if (c >= 0) eventTrack.dwFlags |= TME_HOVER;
414 pGraphics->mTooltipIdx = c;
415 pGraphics->HideTooltip();
419 TrackMouseEvent(&eventTrack);
422 else if (GetCapture() == hWnd && !pGraphics->IsInPlatformTextEntry())
424 float oldX = pGraphics->mCursorX;
425 float oldY = pGraphics->mCursorY;
427 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
429 info.dX = info.x - oldX;
430 info.dY = info.y - oldY;
432 if (info.dX || info.dY)
434 std::vector<IMouseInfo> list{ info };
435 pGraphics->OnMouseDrag(list);
437 if (pGraphics->MouseCursorIsLocked())
439 const float x = pGraphics->mHiddenCursorX;
440 const float y = pGraphics->mHiddenCursorY;
442 pGraphics->MoveMouseCursor(x, y);
443 pGraphics->mHiddenCursorX = x;
444 pGraphics->mHiddenCursorY = y;
453 pGraphics->ShowTooltip();
458 pGraphics->HideTooltip();
459 pGraphics->OnMouseOut();
466 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
467 std::vector<IMouseInfo> list{ info };
468 pGraphics->OnMouseUp(list);
471 case WM_LBUTTONDBLCLK:
472 case WM_RBUTTONDBLCLK:
477 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
478 if (pGraphics->OnMouseDblClick(info.x, info.y, info.ms))
486 if (pGraphics->mParamEditWnd)
488 pGraphics->mParamEditMsg = kCancel;
493 IMouseInfo info = pGraphics->GetMouseInfo(lParam, wParam);
494 float d = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
495 const float scale = pGraphics->GetTotalScale();
497 GetWindowRect(hWnd, &r);
498 pGraphics->OnMouseWheel(info.x - (r.left / scale), info.y - (r.top / scale), info.ms, d);
504 UINT nTouches = LOWORD(wParam);
508 WDL_TypedBuf<TOUCHINPUT> touches;
509 touches.Resize(nTouches);
510 HTOUCHINPUT hTouchInput = (HTOUCHINPUT) lParam;
511 std::vector<IMouseInfo> downlist;
512 std::vector<IMouseInfo> uplist;
513 std::vector<IMouseInfo> movelist;
514 const float scale = pGraphics->GetTotalScale();
516 GetTouchInputInfo(hTouchInput, nTouches, touches.Get(),
sizeof(TOUCHINPUT));
518 for (
int i = 0; i < nTouches; i++)
520 TOUCHINPUT* pTI = touches.Get() +i;
523 pt.x = TOUCH_COORD_TO_PIXEL(pTI->x);
524 pt.y = TOUCH_COORD_TO_PIXEL(pTI->y);
525 ScreenToClient(pGraphics->mPlugWnd, &pt);
528 info.x =
static_cast<float>(pt.x) / scale;
529 info.y =
static_cast<float>(pt.y) / scale;
532 info.ms.touchRadius = 0;
534 if (pTI->dwMask & TOUCHINPUTMASKF_CONTACTAREA)
536 info.ms.touchRadius = pTI->cxContact;
539 info.ms.touchID =
static_cast<ITouchID
>(pTI->dwID);
541 if (pTI->dwFlags & TOUCHEVENTF_DOWN)
543 downlist.push_back(info);
544 pGraphics->mDeltaCapture.insert(std::make_pair(info.ms.touchID, info));
546 else if (pTI->dwFlags & TOUCHEVENTF_UP)
548 pGraphics->mDeltaCapture.erase(info.ms.touchID);
549 uplist.push_back(info);
551 else if (pTI->dwFlags & TOUCHEVENTF_MOVE)
553 IMouseInfo previous = pGraphics->mDeltaCapture.find(info.ms.touchID)->second;
554 info.dX = info.x - previous.x;
555 info.dY = info.y - previous.y;
556 movelist.push_back(info);
557 pGraphics->mDeltaCapture[info.ms.touchID] = info;
562 pGraphics->OnMouseDown(downlist);
565 pGraphics->OnMouseUp(uplist);
568 pGraphics->OnMouseDrag(movelist);
570 CloseTouchInputHandle(hTouchInput);
575 return DLGC_WANTALLKEYS;
581 ScreenToClient(hWnd, &p);
583 BYTE keyboardState[256] = {};
584 GetKeyboardState(keyboardState);
585 const int keyboardScanCode = (lParam >> 16) & 0x00ff;
587 const int len = ToAscii(wParam, keyboardScanCode, keyboardState, &character, 0);
592 if (len == 0 || len == 1)
595 str[0] =
static_cast<char>(character);
598 IKeyPress keyPress{ str,
static_cast<int>(wParam),
599 static_cast<bool>(GetKeyState(VK_SHIFT) & 0x8000),
600 static_cast<bool>(GetKeyState(VK_CONTROL) & 0x8000),
601 static_cast<bool>(GetKeyState(VK_MENU) & 0x8000) };
603 const float scale = pGraphics->GetTotalScale();
605 if (msg == WM_KEYDOWN)
606 handle = pGraphics->OnKeyDown(p.x / scale, p.y / scale, keyPress);
608 handle = pGraphics->OnKeyUp(p.x / scale, p.y / scale, keyPress);
613 HWND rootHWnd = GetAncestor( hWnd, GA_ROOT);
614 SendMessageW(rootHWnd, msg, wParam, lParam);
615 return DefWindowProcW(hWnd, msg, wParam, lParam);
622 const float scale = pGraphics->GetTotalScale();
623 auto addDrawRect = [pGraphics, scale](
IRECTList& rects, RECT r) {
624 IRECT ir(r.left, r.top, r.right, r.bottom);
630 HRGN region = CreateRectRgn(0, 0, 0, 0);
631 int regionType = GetUpdateRgn(hWnd, region, FALSE);
633 if ((regionType == COMPLEXREGION) || (regionType == SIMPLEREGION))
636 const int bufferSize =
sizeof(RECT) * 64;
637 unsigned char stackBuffer[
sizeof(RGNDATA) + bufferSize];
638 RGNDATA* regionData = (RGNDATA*)stackBuffer;
640 if (regionType == COMPLEXREGION && GetRegionData(region, bufferSize, regionData))
642 for (
int i = 0; i < regionData->rdh.nCount; i++)
643 addDrawRect(rects, *(((RECT*)regionData->Buffer) + i));
648 GetRgnBox(region, &r);
649 addDrawRect(rects, r);
652#if defined IGRAPHICS_GL
654 BeginPaint(hWnd, &ps);
658 pGraphics->ActivateGLContext();
661 pGraphics->Draw(rects);
664 SwapBuffers((HDC) pGraphics->GetPlatformContext());
665 pGraphics->DeactivateGLContext();
668#if defined IGRAPHICS_GL || IGRAPHICS_D2D
676 ValidateRect(hWnd, 0);
678 DeleteObject(region);
683 case WM_CTLCOLOREDIT:
685 if (!pGraphics->mParamEditWnd)
688 const IText& text = pGraphics->mEditText;
689 HDC dc = (HDC) wParam;
690 SetBkColor(dc, RGB(text.mTextEntryBGColor.R, text.mTextEntryBGColor.G, text.mTextEntryBGColor.B));
691 SetTextColor(dc, RGB(text.mTextEntryFGColor.R, text.mTextEntryFGColor.G, text.mTextEntryFGColor.B));
692 SetBkMode(dc, OPAQUE);
693 SetDCBrushColor(dc, RGB(text.mTextEntryBGColor.R, text.mTextEntryBGColor.G, text.mTextEntryBGColor.B));
694 return (LRESULT)GetStockObject(DC_BRUSH);
698 HDROP hdrop = (HDROP) wParam;
700 int numDroppedFiles = DragQueryFileW(hdrop, -1,
nullptr, 0);
702 std::vector<std::vector<char>> pathBuffers(numDroppedFiles, std::vector<char>(1025, 0));
703 std::vector<const char*> pathPtrs(numDroppedFiles);
705 for (
int i = 0; i < numDroppedFiles; i++)
707 wchar_t pathBufferW[1025] = {
'\0'};
708 DragQueryFileW(hdrop, i, pathBufferW, 1024);
709 strncpy(pathBuffers[i].data(), UTF16AsUTF8(pathBufferW).Get(), 1024);
710 pathPtrs[i] = pathBuffers[i].data();
713 DragQueryPoint(hdrop, &p);
715 const float scale = pGraphics->GetTotalScale();
717 if (numDroppedFiles==1)
719 pGraphics->OnDrop(&pathPtrs[0][0], p.x / scale, p.y / scale);
723 pGraphics->OnDropMultiple(pathPtrs, p.x / scale, p.y / scale);
730 pGraphics->CloseWindow();
742 return DefWindowProcW(hWnd, msg, wParam, lParam);
746LRESULT CALLBACK IGraphicsWin::ParamEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
750 if (pGraphics && pGraphics->mParamEditWnd && pGraphics->mParamEditWnd == hWnd)
752 pGraphics->HideTooltip();
759 if (pGraphics->mEditParam)
763 if (c == 0x08)
break;
765 switch (pGraphics->mEditParam->
Type())
767 case IParam::kTypeEnum:
768 case IParam::kTypeInt:
769 case IParam::kTypeBool:
770 if (c >=
'0' && c <=
'9')
break;
771 else if (c ==
'-')
break;
772 else if (c ==
'+')
break;
774 case IParam::kTypeDouble:
775 if (c >=
'0' && c <=
'9')
break;
776 else if (c ==
'-')
break;
777 else if (c ==
'+')
break;
778 else if (c ==
'.')
break;
788 if (wParam == VK_RETURN)
790 pGraphics->mParamEditMsg = kCommit;
793 else if (wParam == VK_ESCAPE)
795 pGraphics->mParamEditMsg = kCancel;
802 pGraphics->mParamEditMsg = kEditing;
807 pGraphics->mParamEditMsg = kCommit;
816 lres = CallWindowProcW(pGraphics->mDefEditProc, hWnd, WM_GETDLGCODE, wParam, lParam);
818 if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN && wParam == VK_RETURN)
820 lres |= DLGC_WANTMESSAGE;
826 switch HIWORD(wParam)
830 if (pGraphics->mParamEditWnd)
832 pGraphics->mParamEditMsg = kCommit;
841 return CallWindowProcW(pGraphics->mDefEditProc, hWnd, msg, wParam, lParam);
843 return DefWindowProcW(hWnd, msg, wParam, lParam);
846IGraphicsWin::IGraphicsWin(
IGEditorDelegate& dlg,
int w,
int h,
int fps,
float scale)
847 : IGRAPHICS_DRAW_CLASS(dlg, w, h, fps, scale)
849 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
850 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
851 fontStorage.Retain();
852 hfontStorage.Retain();
854#ifndef IGRAPHICS_DISABLE_VSYNC
855 mVSYNCEnabled = IsWindows8OrGreater();
859IGraphicsWin::~IGraphicsWin()
861 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
862 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
863 fontStorage.Release();
864 hfontStorage.Release();
869static void GetWindowSize(HWND pWnd,
int* pW,
int* pH)
874 GetWindowRect(pWnd, &r);
875 *pW = r.right - r.left;
876 *pH = r.bottom - r.top;
884static bool IsChildWindow(HWND pWnd)
888 int style = GetWindowLongW(pWnd, GWL_STYLE);
889 int exStyle = GetWindowLongW(pWnd, GWL_EXSTYLE);
890 return ((style & WS_CHILD) && !(exStyle & WS_EX_MDICHILD));
895void IGraphicsWin::ForceEndUserEdit()
897 mParamEditMsg = kCancel;
900static UINT SETPOS_FLAGS = SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE;
902void IGraphicsWin::PlatformResize(
bool parentHasResized)
906 HWND pParent = 0, pGrandparent = 0;
907 int dlgW = 0, dlgH = 0, parentW = 0, parentH = 0, grandparentW = 0, grandparentH = 0;
908 GetWindowSize(mPlugWnd, &dlgW, &dlgH);
909 int dw = (WindowWidth() * GetScreenScale()) - dlgW, dh = (WindowHeight()* GetScreenScale()) - dlgH;
911 if (IsChildWindow(mPlugWnd))
913 pParent = GetParent(mPlugWnd);
914 GetWindowSize(pParent, &parentW, &parentH);
916 if (IsChildWindow(pParent))
918 pGrandparent = GetParent(pParent);
919 GetWindowSize(pGrandparent, &grandparentW, &grandparentH);
926 SetWindowPos(mPlugWnd, 0, 0, 0, dlgW + dw, dlgH + dh, SETPOS_FLAGS);
928 if (pParent && !parentHasResized)
930 SetWindowPos(pParent, 0, 0, 0, parentW + dw, parentH + dh, SETPOS_FLAGS);
933 if (pGrandparent && !parentHasResized)
935 SetWindowPos(pGrandparent, 0, 0, 0, grandparentW + dw, grandparentH + dh, SETPOS_FLAGS);
941void IGraphicsWin::DrawResize()
944 IGRAPHICS_DRAW_CLASS::DrawResize();
945 DeactivateGLContext();
949void IGraphicsWin::HideMouseCursor(
bool hide,
bool lock)
951 if (mCursorHidden == hide)
956 mHiddenCursorX = mCursorX;
957 mHiddenCursorY = mCursorY;
960 mCursorHidden =
true;
961 mCursorLock = lock && !mTabletInput;
966 MoveMouseCursor(mHiddenCursorX, mHiddenCursorY);
969 mCursorHidden =
false;
974void IGraphicsWin::MoveMouseCursor(
float x,
float y)
979 const float scale = GetTotalScale();
982 p.x = std::round(x * scale);
983 p.y = std::round(y * scale);
985 ::ClientToScreen(mPlugWnd, &p);
987 if (SetCursorPos(p.x, p.y))
990 ScreenToClient(mPlugWnd, &p);
992 mHiddenCursorX = mCursorX = p.x / scale;
993 mHiddenCursorY = mCursorY = p.y / scale;
997ECursor IGraphicsWin::SetMouseCursor(ECursor cursorType)
1003 case ECursor::ARROW: cursor = LoadCursor(NULL, IDC_ARROW);
break;
1004 case ECursor::IBEAM: cursor = LoadCursor(NULL, IDC_IBEAM);
break;
1005 case ECursor::WAIT: cursor = LoadCursor(NULL, IDC_WAIT);
break;
1006 case ECursor::CROSS: cursor = LoadCursor(NULL, IDC_CROSS);
break;
1007 case ECursor::UPARROW: cursor = LoadCursor(NULL, IDC_UPARROW);
break;
1008 case ECursor::SIZENWSE: cursor = LoadCursor(NULL, IDC_SIZENWSE);
break;
1009 case ECursor::SIZENESW: cursor = LoadCursor(NULL, IDC_SIZENESW);
break;
1010 case ECursor::SIZEWE: cursor = LoadCursor(NULL, IDC_SIZEWE);
break;
1011 case ECursor::SIZENS: cursor = LoadCursor(NULL, IDC_SIZENS);
break;
1012 case ECursor::SIZEALL: cursor = LoadCursor(NULL, IDC_SIZEALL);
break;
1013 case ECursor::INO: cursor = LoadCursor(NULL, IDC_NO);
break;
1014 case ECursor::HAND: cursor = LoadCursor(NULL, IDC_HAND);
break;
1015 case ECursor::APPSTARTING: cursor = LoadCursor(NULL, IDC_APPSTARTING);
break;
1016 case ECursor::HELP: cursor = LoadCursor(NULL, IDC_HELP);
break;
1018 cursor = LoadCursor(NULL, IDC_ARROW);
1025bool IGraphicsWin::MouseCursorIsLocked()
1030void IGraphicsWin::GetMouseLocation(
float& x,
float&y)
const
1034 ScreenToClient(mPlugWnd, &p);
1036 const float scale = GetTotalScale();
1043void IGraphicsWin::CreateGLContext()
1045 PIXELFORMATDESCRIPTOR pfd =
1047 sizeof(PIXELFORMATDESCRIPTOR),
1049 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1065 HDC dc = GetDC(mPlugWnd);
1066 int fmt = ChoosePixelFormat(dc, &pfd);
1067 SetPixelFormat(dc, fmt, &pfd);
1068 mHGLRC = wglCreateContext(dc);
1069 wglMakeCurrent(dc, mHGLRC);
1074 auto wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress(
"wglCreateContextAttribsARB");
1076 if (wglCreateContextAttribsARB)
1078 wglDeleteContext(mHGLRC);
1080 const int attribList[] = {
1081 WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
1082 WGL_CONTEXT_MINOR_VERSION_ARB, 3,
1083 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1087 mHGLRC = wglCreateContextAttribsARB(dc, 0, attribList);
1088 wglMakeCurrent(dc, mHGLRC);
1095 DBGMSG(
"Error initializing glad");
1099 ReleaseDC(mPlugWnd, dc);
1102void IGraphicsWin::DestroyGLContext()
1104 wglMakeCurrent(NULL, NULL);
1105 wglDeleteContext(mHGLRC);
1108void IGraphicsWin::ActivateGLContext()
1110 mStartHDC = wglGetCurrentDC();
1111 mStartHGLRC = wglGetCurrentContext();
1112 HDC dc = GetDC(mPlugWnd);
1113 wglMakeCurrent(dc, mHGLRC);
1116void IGraphicsWin::DeactivateGLContext()
1118 ReleaseDC(mPlugWnd, (HDC) GetPlatformContext());
1119 wglMakeCurrent(mStartHDC, mStartHGLRC);
1123EMsgBoxResult IGraphicsWin::ShowMessageBox(
const char* str,
const char* title, EMsgBoxType type, IMsgBoxCompletionHandlerFunc completionHandler)
1125 ReleaseMouseCapture();
1127 EMsgBoxResult result =
static_cast<EMsgBoxResult
>(MessageBoxW(GetMainWnd(), UTF8AsUTF16(str).Get(), UTF8AsUTF16(title).Get(),
static_cast<int>(type)));
1129 if (completionHandler)
1130 completionHandler(result);
1135void* IGraphicsWin::OpenWindow(
void* pParent)
1137 mParentWnd = (HWND) pParent;
1138 int screenScale = GetScaleForHWND(mParentWnd);
1139 int x = 0, y = 0, w = WindowWidth() * screenScale, h = WindowHeight() * screenScale;
1144 GetWindowRect((HWND) pParent, &pR);
1145 GetWindowRect(mPlugWnd, &cR);
1147 x = cR.left - pR.left;
1148 y = cR.top - pR.top;
1149 w = cR.right - cR.left;
1150 h = cR.bottom - cR.top;
1153 if (nWndClassReg++ == 0)
1155 WNDCLASSW wndClass = { CS_DBLCLKS | CS_OWNDC, WndProc, 0, 0, mHInstance, 0, 0, 0, 0, wndClassName };
1156 RegisterClassW(&wndClass);
1159 mPlugWnd = CreateWindowW(wndClassName, L
"IPlug", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, x, y, w, h, mParentWnd, 0, mHInstance,
this);
1161 HDC dc = GetDC(mPlugWnd);
1162 SetPlatformContext(dc);
1163 ReleaseDC(mPlugWnd, dc);
1169 OnViewInitialized((
void*) dc);
1171 SetScreenScale(screenScale);
1173 GetDelegate()->LayoutUI(
this);
1175 if (MultiTouchEnabled() && GetSystemMetrics(SM_DIGITIZER) & NID_MULTI_INPUT)
1177 RegisterTouchWindow(mPlugWnd, 0);
1180 if (!mPlugWnd && --nWndClassReg == 0)
1182 UnregisterClassW(wndClassName, mHInstance);
1186 SetAllControlsDirty();
1189 if (mPlugWnd && TooltipsEnabled())
1191 static const INITCOMMONCONTROLSEX iccex = {
sizeof(INITCOMMONCONTROLSEX), ICC_TAB_CLASSES };
1193 if (InitCommonControlsEx(&iccex))
1195 mTooltipWnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TTS_NOPREFIX | TTS_ALWAYSTIP,
1196 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, mPlugWnd, NULL, mHInstance, NULL);
1199 SetWindowPos(mTooltipWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1200 TOOLINFOW ti = { TTTOOLINFOW_V2_SIZE, TTF_IDISHWND | TTF_SUBCLASS, mPlugWnd, (UINT_PTR) mPlugWnd, {0, 0, 0, 0}, NULL, NULL, 0, NULL };
1201 SendMessageW(mTooltipWnd, TTM_ADDTOOLW, 0, (LPARAM) &ti);
1202 SendMessageW(mTooltipWnd, TTM_SETMAXTIPWIDTH, 0, TOOLTIPWND_MAXWIDTH);
1207 EnableTooltips(
false);
1210 wglMakeCurrent(NULL, NULL);
1214 GetDelegate()->OnUIOpen();
1219static void GetWndClassName(HWND hWnd, WDL_String* pStr)
1221 wchar_t cStrW[MAX_CLASSNAME_LEN] = {
'\0'};
1222 GetClassNameW(hWnd, cStrW, MAX_CLASSNAME_LEN);
1223 pStr->Set(UTF16AsUTF8(cStrW).Get());
1226BOOL CALLBACK IGraphicsWin::FindMainWindow(HWND hWnd, LPARAM lParam)
1232 GetWindowThreadProcessId(hWnd, &wPID);
1234 GetWndClassName(hWnd, &str);
1235 if (wPID == pGraphics->mPID && !strcmp(str.Get(), pGraphics->mMainWndClassName.Get()))
1237 pGraphics->mMainWnd = hWnd;
1244HWND IGraphicsWin::GetMainWnd()
1250 HWND parentWnd = mParentWnd;
1253 mMainWnd = parentWnd;
1254 parentWnd = GetParent(mMainWnd);
1257 GetWndClassName(mMainWnd, &mMainWndClassName);
1259 else if (CStringHasContents(mMainWndClassName.Get()))
1261 mPID = GetCurrentProcessId();
1262 EnumWindows(FindMainWindow, (LPARAM)
this);
1268IRECT IGraphicsWin::GetWindowRECT()
1273 GetWindowRect(mPlugWnd, &r);
1274 r.right -= TOOLWIN_BORDER_W;
1275 r.bottom -= TOOLWIN_BORDER_H;
1276 return IRECT(r.left, r.top, r.right, r.bottom);
1281void IGraphicsWin::CloseWindow()
1288 KillTimer(mPlugWnd, IPLUG_TIMER_ID);
1291 ActivateGLContext();
1297 DeactivateGLContext();
1301 SetPlatformContext(
nullptr);
1305 DestroyWindow(mTooltipWnd);
1307 mShowingTooltip =
false;
1311 DestroyWindow(mPlugWnd);
1314 if (--nWndClassReg == 0)
1316 UnregisterClassW(wndClassName, mHInstance);
1321bool IGraphicsWin::PlatformSupportsMultiTouch()
const
1323 return GetSystemMetrics(SM_DIGITIZER) & NID_MULTI_INPUT;
1326IPopupMenu* IGraphicsWin::GetItemMenu(
long idx,
long& idxInMenu,
long& offsetIdx,
IPopupMenu& baseMenu)
1328 long oldIDx = offsetIdx;
1329 offsetIdx += baseMenu.NItems();
1331 if (idx < offsetIdx)
1333 idxInMenu = idx - oldIDx;
1339 for (
int i = 0; i< baseMenu.NItems(); i++)
1342 if (pMenuItem->GetSubmenu())
1344 pMenu = GetItemMenu(idx, idxInMenu, offsetIdx, *pMenuItem->GetSubmenu());
1354HMENU IGraphicsWin::CreateMenu(
IPopupMenu& menu,
long* pOffsetIdx)
1356 HMENU hMenu = ::CreatePopupMenu();
1358 WDL_String escapedText;
1361 long offset = *pOffsetIdx;
1362 long nItems = menu.NItems();
1363 *pOffsetIdx += nItems;
1366 for (
int i = 0; i < nItems; i++)
1370 if (pMenuItem->GetIsSeparator())
1372 AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
1376 const char* str = pMenuItem->GetText();
1377 int maxlen = strlen(str) + menu.GetPrefix() ? 50 : 0;
1378 WDL_String entryText(str);
1380 if (menu.GetPrefix())
1382 switch (menu.GetPrefix())
1386 entryText.SetFormatted(maxlen,
"%1d: %s", i+1, str);
break;
1390 entryText.SetFormatted(maxlen,
"%02d: %s", i+1, str);
break;
1394 entryText.SetFormatted(maxlen,
"%03d: %s", i+1, str);
break;
1401 if (strchr(entryText.Get(),
'&'))
1403 for (
int c = 0; c < entryText.GetLength(); c++)
1404 if (entryText.Get()[c] ==
'&')
1405 entryText.Insert(
"&", c++);
1409 if (nItems < 160 && menu.NItemsPerColumn() > 0 && inc && !(inc % menu.NItemsPerColumn()))
1410 flags |= MF_MENUBARBREAK;
1412 if (pMenuItem->GetEnabled())
1413 flags |= MF_ENABLED;
1416 if (pMenuItem->GetIsTitle())
1417 flags |= MF_DISABLED;
1418 if (pMenuItem->GetChecked())
1419 flags |= MF_CHECKED;
1421 flags |= MF_UNCHECKED;
1423 if (pMenuItem->GetSubmenu())
1425 HMENU submenu = CreateMenu(*pMenuItem->GetSubmenu(), pOffsetIdx);
1428 AppendMenuW(hMenu, flags|MF_POPUP, (UINT_PTR)submenu, UTF8AsUTF16(entryText).Get());
1433 AppendMenuW(hMenu, flags, offset + inc, UTF8AsUTF16(entryText).Get());
1445 HMENU hMenu = CreateMenu(menu, &offsetIdx);
1452 const float scale = GetTotalScale();
1454 cPos.x = bounds.L * scale;
1455 cPos.y = bounds.B * scale;
1457 ::ClientToScreen(mPlugWnd, &cPos);
1459 if (TrackPopupMenu(hMenu, TPM_LEFTALIGN, cPos.x, cPos.y, 0, mPlugWnd, 0))
1462 if (PeekMessage(&msg, mPlugWnd, WM_COMMAND, WM_COMMAND, PM_REMOVE))
1464 if (HIWORD(msg.wParam) == 0)
1466 long res = LOWORD(msg.wParam);
1471 IPopupMenu* pReturnMenu = GetItemMenu(res, idx, offsetIdx, menu);
1474 result = pReturnMenu;
1475 result->SetChosenItemIdx(idx);
1478 if (pReturnMenu && pReturnMenu->GetFunction())
1479 pReturnMenu->ExecFunction();
1487 RECT r = { 0, 0,
static_cast<LONG
>(WindowWidth() * GetScreenScale()),
static_cast<LONG
>(WindowHeight() * GetScreenScale()) };
1488 InvalidateRect(mPlugWnd, &r, FALSE);
1496void IGraphicsWin::CreatePlatformTextEntry(
int paramIdx,
const IText& text,
const IRECT& bounds,
int length,
const char* str)
1503 switch (text.mAlign)
1505 case EAlign::Near: editStyle = ES_LEFT;
break;
1506 case EAlign::Far: editStyle = ES_RIGHT;
break;
1507 case EAlign::Center:
1508 default: editStyle = ES_CENTER;
break;
1511 const float scale = GetTotalScale();
1514 mParamEditWnd = CreateWindowW(L
"EDIT", UTF8AsUTF16(str).Get(), ES_AUTOHSCROLL | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | ES_MULTILINE | editStyle,
1515 scaledBounds.L, scaledBounds.T, scaledBounds.
W()+1, scaledBounds.
H()+1,
1516 mPlugWnd, (HMENU) PARAM_EDIT_ID, mHInstance, 0);
1518 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
1520 LOGFONTW lFont = { 0 };
1521 HFontHolder* hfontHolder = hfontStorage.Find(text.mFont);
1522 GetObjectW(hfontHolder->mHFont,
sizeof(LOGFONTW), &lFont);
1523 lFont.lfHeight = text.mSize * scale;
1524 mEditFont = CreateFontIndirectW(&lFont);
1526 assert(hfontHolder &&
"font not found - did you forget to load it?");
1528 mEditParam = paramIdx > kNoParameter ? GetDelegate()->GetParam(paramIdx) :
nullptr;
1532 SendMessageW(mParamEditWnd, EM_LIMITTEXT, (WPARAM) length, 0);
1533 SendMessageW(mParamEditWnd, WM_SETFONT, (WPARAM) mEditFont, 0);
1534 SendMessageW(mParamEditWnd, EM_SETSEL, 0, -1);
1536 if (text.mVAlign == EVAlign::Middle)
1538 double size = text.mSize * scale;
1539 double offset = (scaledBounds.
H() - size) / 2.0;
1540 RECT formatRect{0, (LONG) offset, (LONG) scaledBounds.
W() + 1, (LONG) scaledBounds.
H() + 1};
1541 SendMessageW(mParamEditWnd, EM_SETRECT, 0, (LPARAM) &formatRect);
1544 SetFocus(mParamEditWnd);
1546 mDefEditProc = (WNDPROC) SetWindowLongPtrW(mParamEditWnd, GWLP_WNDPROC, (LONG_PTR) ParamEditProc);
1547 SetWindowLongPtrW(mParamEditWnd, GWLP_USERDATA, 0xdeadf00b);
1550bool IGraphicsWin::RevealPathInExplorerOrFinder(WDL_String& path,
bool select)
1552 bool success =
false;
1554 if (path.GetLength())
1556 WCHAR winDir[IPLUG_WIN_MAX_WIDE_PATH];
1557 UINT len = GetSystemDirectoryW(winDir, IPLUG_WIN_MAX_WIDE_PATH);
1559 if (len && !(len > MAX_PATH - 2))
1561 winDir[len] = L
'\\';
1562 winDir[++len] = L
'\0';
1564 WDL_String explorerParams;
1567 explorerParams.Append(
"/select,");
1569 explorerParams.Append(
"\"");
1570 explorerParams.Append(path.Get());
1571 explorerParams.Append(
"\\\"");
1575 if ((result=::ShellExecuteW(NULL, L
"open", L
"explorer.exe", UTF8AsUTF16(explorerParams).Get(), winDir, SW_SHOWNORMAL)) <= (HINSTANCE) 32)
1583void IGraphicsWin::PromptForFile(WDL_String& fileName, WDL_String& path, EFileAction action,
const char* ext, IFileDialogCompletionHandlerFunc completionHandler)
1585 if (!WindowIsOpen())
1591 wchar_t fileNameWide[_MAX_PATH];
1593 UTF8ToUTF16(fileNameWide, fileName.Get(), _MAX_PATH);
1598 UTF8AsUTF16 directoryWide(path);
1601 memset(&ofn, 0,
sizeof(OPENFILENAMEW));
1603 ofn.lStructSize =
sizeof(OPENFILENAMEW);
1604 ofn.hwndOwner = (HWND) GetWindow();
1605 ofn.lpstrFile = fileNameWide;
1606 ofn.nMaxFile = _MAX_PATH - 1;
1607 ofn.lpstrInitialDir = directoryWide.Get();
1608 ofn.Flags = OFN_PATHMUSTEXIST;
1610 if (CStringHasContents(ext))
1612 wchar_t extStr[256];
1613 wchar_t defExtStr[16];
1614 int i, p, n = strlen(ext);
1615 bool separator =
true;
1617 for (i = 0, p = 0; i < n; ++i)
1632 extStr[p++] = ext[i];
1636 wcscpy(&extStr[p], extStr);
1637 extStr[p + p] =
'\0';
1638 ofn.lpstrFilter = extStr;
1640 for (i = 0, p = 0; i < n && ext[i] !=
' '; ++i)
1641 defExtStr[p++] = ext[i];
1643 defExtStr[p++] =
'\0';
1644 ofn.lpstrDefExt = defExtStr;
1651 case EFileAction::Save:
1652 ofn.Flags |= OFN_OVERWRITEPROMPT;
1653 rc = GetSaveFileNameW(&ofn);
1655 case EFileAction::Open:
1657 ofn.Flags |= OFN_FILEMUSTEXIST;
1658 rc = GetOpenFileNameW(&ofn);
1664 char drive[_MAX_DRIVE];
1665 char directoryOutCStr[_MAX_PATH];
1667 UTF16AsUTF8 tempUTF8(ofn.lpstrFile);
1669 if (_splitpath_s(tempUTF8.Get(), drive,
sizeof(drive), directoryOutCStr,
sizeof(directoryOutCStr), NULL, 0, NULL, 0) == 0)
1672 path.Append(directoryOutCStr);
1675 fileName.Set(tempUTF8.Get());
1683 if (completionHandler)
1685 completionHandler(fileName, path);
1688 ReleaseMouseCapture();
1691void IGraphicsWin::PromptForDirectory(WDL_String& dir, IFileDialogCompletionHandlerFunc completionHandler)
1694 memset(&bi, 0,
sizeof(bi));
1696 bi.ulFlags = BIF_USENEWUI;
1697 bi.hwndOwner = mPlugWnd;
1698 bi.lpszTitle = L
"Choose a Directory";
1701 ::OleInitialize(NULL);
1702 LPITEMIDLIST pIDL = ::SHBrowseForFolderW(&bi);
1706 wchar_t buffer[_MAX_PATH] = {
'\0'};
1708 if (::SHGetPathFromIDListW(pIDL, buffer) != 0)
1710 dir.Set(UTF16AsUTF8(buffer).Get());
1715 CoTaskMemFree(pIDL);
1722 if (completionHandler)
1724 WDL_String fileName;
1725 completionHandler(fileName, dir);
1728 ReleaseMouseCapture();
1730 ::OleUninitialize();
1733static UINT_PTR CALLBACK CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
1735 if (uiMsg == WM_INITDIALOG && lParam)
1737 CHOOSECOLORW* cc = (CHOOSECOLORW*) lParam;
1738 if (cc && cc->lCustData)
1740 const wchar_t* strWide = (
const wchar_t*) cc->lCustData;
1741 SetWindowTextW(hdlg, strWide);
1743 uiSetRGB = RegisterWindowMessageW(SETRGBSTRINGW);
1744 SendMessageW(hdlg, uiSetRGB, 0, (LPARAM) cc->rgbResult);
1750bool IGraphicsWin::PromptForColor(
IColor& color,
const char* prompt, IColorPickerHandlerFunc func)
1752 ReleaseMouseCapture();
1757 UTF8AsUTF16 promptWide(prompt);
1759 const COLORREF w = RGB(255, 255, 255);
1760 static COLORREF customColorStorage[16] = { w, w, w, w, w, w, w, w, w, w, w, w, w, w, w, w };
1763 memset(&cc, 0,
sizeof(CHOOSECOLORW));
1764 cc.lStructSize =
sizeof(CHOOSECOLORW);
1765 cc.hwndOwner = mPlugWnd;
1766 cc.rgbResult = RGB(color.R, color.G, color.B);
1767 cc.lpCustColors = customColorStorage;
1768 cc.lCustData = (LPARAM) promptWide.Get();
1769 cc.lpfnHook = CCHookProc;
1770 cc.Flags = CC_RGBINIT | CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK;
1772 if (ChooseColorW(&cc))
1774 color.R = GetRValue(cc.rgbResult);
1775 color.G = GetGValue(cc.rgbResult);
1776 color.B = GetBValue(cc.rgbResult);
1786bool IGraphicsWin::OpenURL(
const char* url,
const char* msgWindowTitle,
const char* confirmMsg,
const char* errMsgOnFailure)
1788 if (confirmMsg && MessageBoxW(mPlugWnd, UTF8AsUTF16(confirmMsg).Get(), UTF8AsUTF16(msgWindowTitle).Get(), MB_YESNO) != IDYES)
1792 DWORD inetStatus = 0;
1793 if (InternetGetConnectedState(&inetStatus, 0))
1795 if (ShellExecuteW(mPlugWnd, L
"open", UTF8AsUTF16(url).Get(), 0, 0, SW_SHOWNORMAL) > HINSTANCE(32))
1800 if (errMsgOnFailure)
1802 MessageBoxW(mPlugWnd, UTF8AsUTF16(errMsgOnFailure).Get(), UTF8AsUTF16(msgWindowTitle).Get(), MB_OK);
1807void IGraphicsWin::SetTooltip(
const char* tooltip)
1809 UTF8AsUTF16 tipWide(tooltip);
1810 TOOLINFOW ti = { TTTOOLINFOW_V2_SIZE, 0, mPlugWnd, (UINT_PTR) mPlugWnd, {0, 0, 0, 0}, NULL, NULL, 0, NULL };
1811 ti.lpszText =
const_cast<wchar_t*
>(tipWide.Get());
1812 SendMessageW(mTooltipWnd, TTM_UPDATETIPTEXTW, 0, (LPARAM) &ti);
1815void IGraphicsWin::ShowTooltip()
1817 if (mTooltipIdx > -1)
1819 const char* tooltip = GetControl(mTooltipIdx)->GetTooltip();
1822 SetTooltip(tooltip);
1823 mShowingTooltip =
true;
1828void IGraphicsWin::HideTooltip()
1830 if (mShowingTooltip)
1833 mShowingTooltip =
false;
1837bool IGraphicsWin::GetTextFromClipboard(WDL_String& str)
1839 bool result =
false;
1841 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1843 if (OpenClipboard(0))
1845 HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
1849 WCHAR *origStr = (WCHAR*) GlobalLock(hglb);
1853 UTF16ToUTF8(str, origStr);
1869bool IGraphicsWin::SetTextInClipboard(
const char* str)
1871 if (!OpenClipboard(mMainWnd))
1881 const int lenWide = UTF8ToUTF16Len(str);
1884 HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, lenWide*
sizeof(WCHAR));
1892 LPWSTR lpstrCopy = (LPWSTR) GlobalLock(hglbCopy);
1897 UTF8ToUTF16(lpstrCopy, str, lenWide);
1898 GlobalUnlock(hglbCopy);
1901 result = SetClipboardData(CF_UNICODETEXT, hglbCopy);
1905 GlobalFree(hglbCopy);
1914bool IGraphicsWin::SetFilePathInClipboard(
const char* path)
1916 if (!OpenClipboard(mMainWnd))
1921 UTF8AsUTF16 pathWide(path);
1925 HGLOBAL hGlobal = GlobalAlloc(GHND,
sizeof(DROPFILES) + (
sizeof(
wchar_t) * (pathWide.GetLength() + 1)));
1930 DROPFILES* pDropFiles = (DROPFILES*) GlobalLock(hGlobal);
1931 bool result =
false;
1937 pDropFiles->pFiles =
sizeof(DROPFILES);
1938 pDropFiles->pt = { 0, 0 };
1939 pDropFiles->fNC =
true;
1940 pDropFiles->fWide =
true;
1942 std::copy_n(pathWide.Get(), pathWide.GetLength(),
reinterpret_cast<wchar_t*
>(&pDropFiles[1]));
1944 GlobalUnlock(hGlobal);
1946 result = SetClipboardData(CF_HDROP, hGlobal);
1951 GlobalFree(hGlobal);
1957bool IGraphicsWin::InitiateExternalFileDragDrop(
const char* path,
const IRECT& )
1959 using namespace DragAndDropHelpers;
1960 OleInitialize(
nullptr);
1962 FORMATETC format = { CF_HDROP,
nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1968 HRESULT ret = DoDragDrop(dataObj, dropSource, DROPEFFECT_COPY, &dropEffect);
1969 bool success = SUCCEEDED(ret);
1972 dropSource->Release();
1976 ReleaseMouseCapture();
1981static HFONT GetHFont(
const char* fontName,
int weight,
bool italic,
bool underline, DWORD quality = DEFAULT_QUALITY,
bool enumerate =
false)
1983 HDC hdc = GetDC(NULL);
1984 HFONT font =
nullptr;
1989 lFont.lfEscapement = 0;
1990 lFont.lfOrientation = 0;
1991 lFont.lfWeight = weight;
1992 lFont.lfItalic = italic;
1993 lFont.lfUnderline = underline;
1994 lFont.lfStrikeOut =
false;
1995 lFont.lfCharSet = DEFAULT_CHARSET;
1996 lFont.lfOutPrecision = OUT_TT_PRECIS;
1997 lFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1998 lFont.lfQuality = quality;
1999 lFont.lfPitchAndFamily = DEFAULT_PITCH;
2001 wcsncpy(lFont.lfFaceName, UTF8AsUTF16(fontName).Get(), LF_FACESIZE);
2003 auto enumProc = [](
const LOGFONTW* pLFont,
const TEXTMETRICW* pTextMetric, DWORD FontType, LPARAM lParam)
2008 if ((!enumerate || EnumFontFamiliesExW(hdc, &lFont, enumProc, NULL, 0) == -1))
2009 font = CreateFontIndirectW(&lFont);
2013 wchar_t selectedFontName[64] = {
'\0'};
2015 SelectFont(hdc, font);
2016 GetTextFaceW(hdc, 64, selectedFontName);
2017 if (strcmp(UTF16AsUTF8(selectedFontName).Get(), fontName))
2024 ReleaseDC(NULL, hdc);
2029PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fileNameOrResID)
2031 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
2033 void* pFontMem =
nullptr;
2035 WDL_String fullPath;
2037 const EResourceLocation fontLocation =
LocateResource(fileNameOrResID,
"ttf", fullPath, GetBundleID(), GetWinModuleHandle(),
nullptr);
2039 if (fontLocation == kNotFound)
2042 switch (fontLocation)
2046 HANDLE file = CreateFileW(UTF8AsUTF16(fullPath).Get(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2047 PlatformFontPtr ret =
nullptr;
2050 HANDLE mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
2053 resSize = (int) GetFileSize(file,
nullptr);
2054 pFontMem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
2055 ret = LoadPlatformFont(fontID, pFontMem, resSize);
2056 UnmapViewOfFile(pFontMem);
2057 CloseHandle(mapping);
2066 pFontMem =
const_cast<void *
>(
LoadWinResource(fullPath.Get(),
"ttf", resSize, GetWinModuleHandle()));
2067 return LoadPlatformFont(fontID, pFontMem, resSize);
2075PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fontName, ETextStyle style)
2077 int weight = style == ETextStyle::Bold ? FW_BOLD : FW_REGULAR;
2078 bool italic = style == ETextStyle::Italic;
2079 bool underline =
false;
2080 DWORD quality = DEFAULT_QUALITY;
2082 HFONT font = GetHFont(fontName, weight, italic, underline, quality,
true);
2084 return PlatformFontPtr(font ?
new Font(font,
TextStyleString(style),
true) :
nullptr);
2087PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
void* pData,
int dataSize)
2089 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
2091 std::unique_ptr<InstalledFont> pFont;
2092 void* pFontMem = pData;
2093 int resSize = dataSize;
2095 pFont = std::make_unique<InstalledFont>(pFontMem, resSize);
2097 if (pFontMem && pFont && pFont->IsValid())
2099 IFontInfo fontInfo(pFontMem, resSize, 0);
2100 WDL_String family = fontInfo.GetFamily();
2101 int weight = fontInfo.IsBold() ? FW_BOLD : FW_REGULAR;
2102 bool italic = fontInfo.IsItalic();
2103 bool underline = fontInfo.IsUnderline();
2105 HFONT font = GetHFont(family.Get(), weight, italic, underline);
2109 fontStorage.Add(pFont.release(), fontID);
2110 return PlatformFontPtr(
new Font(font,
"",
false));
2117void IGraphicsWin::CachePlatformFont(
const char* fontID,
const PlatformFontPtr& font)
2119 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
2121 HFONT hfont = font->GetDescriptor();
2123 if (!hfontStorage.Find(fontID))
2124 hfontStorage.Add(
new HFontHolder(hfont), fontID);
2127DWORD WINAPI VBlankRun(LPVOID lpParam)
2130 return pGraphics->OnVBlankRun();
2133void IGraphicsWin::StartVBlankThread(HWND hWnd)
2135 mVBlankWindow = hWnd;
2136 mVBlankShutdown =
false;
2138 mVBlankThread = ::CreateThread(NULL, 0, VBlankRun,
this, 0, &threadId);
2141void IGraphicsWin::StopVBlankThread()
2143 if (mVBlankThread != INVALID_HANDLE_VALUE)
2145 mVBlankShutdown =
true;
2146 ::WaitForSingleObject(mVBlankThread, 10000);
2147 mVBlankThread = INVALID_HANDLE_VALUE;
2163typedef UINT32 D3DKMT_HANDLE;
2164typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
2166typedef struct _D3DKMT_OPENADAPTERFROMHDC
2169 D3DKMT_HANDLE hAdapter;
2171 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2172} D3DKMT_OPENADAPTERFROMHDC;
2174typedef struct _D3DKMT_CLOSEADAPTER
2176 D3DKMT_HANDLE hAdapter;
2177} D3DKMT_CLOSEADAPTER;
2179typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT
2181 D3DKMT_HANDLE hAdapter;
2182 D3DKMT_HANDLE hDevice;
2183 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2184} D3DKMT_WAITFORVERTICALBLANKEVENT;
2187typedef NTSTATUS(WINAPI* D3DKMTOpenAdapterFromHdc)(D3DKMT_OPENADAPTERFROMHDC* Arg1);
2188typedef NTSTATUS(WINAPI* D3DKMTCloseAdapter)(
const D3DKMT_CLOSEADAPTER* Arg1);
2189typedef NTSTATUS(WINAPI* D3DKMTWaitForVerticalBlankEvent)(
const D3DKMT_WAITFORVERTICALBLANKEVENT* Arg1);
2191DWORD IGraphicsWin::OnVBlankRun()
2193 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2197 float rateFallback = 60.0f;
2198 int rateMS = (int)(1000.0f / rateFallback);
2206 D3DKMTOpenAdapterFromHdc pOpen =
nullptr;
2207 D3DKMTCloseAdapter pClose =
nullptr;
2208 D3DKMTWaitForVerticalBlankEvent pWait =
nullptr;
2209 HINSTANCE hInst = LoadLibraryW(L
"gdi32.dll");
2211 if (hInst !=
nullptr)
2213 pOpen = (D3DKMTOpenAdapterFromHdc) GetProcAddress((HMODULE) hInst,
"D3DKMTOpenAdapterFromHdc");
2214 pClose = (D3DKMTCloseAdapter) GetProcAddress((HMODULE) hInst,
"D3DKMTCloseAdapter");
2215 pWait = (D3DKMTWaitForVerticalBlankEvent) GetProcAddress((HMODULE) hInst,
"D3DKMTWaitForVerticalBlankEvent");
2222 if (!pOpen || !pClose || !pWait)
2224 while (mVBlankShutdown ==
false)
2234 bool adapterIsOpen =
false;
2235 DWORD adapterLastFailTime = 0;
2236 _D3DKMT_WAITFORVERTICALBLANKEVENT we = { 0 };
2238 while (mVBlankShutdown ==
false)
2243 if (adapterLastFailTime < ::GetTickCount() - 1000)
2246 D3DKMT_OPENADAPTERFROMHDC openAdapterData = { 0 };
2247 HDC hDC = GetDC(mVBlankWindow);
2248 openAdapterData.hDc = hDC;
2249 NTSTATUS status = (*pOpen)(&openAdapterData);
2253 adapterLastFailTime = 0;
2254 adapterIsOpen =
true;
2255 we.hAdapter = openAdapterData.hAdapter;
2257 we.VidPnSourceId = openAdapterData.VidPnSourceId;
2262 adapterLastFailTime = ::GetTickCount();
2271 NTSTATUS status = (*pWait)(&we);
2275 _D3DKMT_CLOSEADAPTER ca;
2276 ca.hAdapter = we.hAdapter;
2278 adapterIsOpen =
false;
2295 _D3DKMT_CLOSEADAPTER ca;
2296 ca.hAdapter = we.hAdapter;
2298 adapterIsOpen =
false;
2303 if (hInst !=
nullptr)
2305 FreeLibrary((HMODULE)hInst);
2312void IGraphicsWin::VBlankNotify()
2315 ::PostMessageW(mVBlankWindow, WM_VBLANK, mVBlankCount, 0);
2319#if defined IGRAPHICS_SKIA
2320 #include "IGraphicsSkia.cpp"
2324#elif defined IGRAPHICS_NANOVG
2325 #include "IGraphicsNanoVG.cpp"
2326#ifdef IGRAPHICS_FREETYPE
2327#define FONS_USE_FREETYPE
2328 #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 for a SOMETHING that uses IGraphics for it's 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,...