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 - 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 if (
auto* pTooltipControl = GetControl(mTooltipIdx))
1821 const char* tooltipStr = pTooltipControl->GetTooltip();
1824 SetTooltip(tooltipStr);
1825 mShowingTooltip =
true;
1831void IGraphicsWin::HideTooltip()
1833 if (mShowingTooltip)
1836 mShowingTooltip =
false;
1840bool IGraphicsWin::GetTextFromClipboard(WDL_String& str)
1842 bool result =
false;
1844 if (IsClipboardFormatAvailable(CF_UNICODETEXT))
1846 if (OpenClipboard(0))
1848 HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
1852 WCHAR *origStr = (WCHAR*) GlobalLock(hglb);
1856 UTF16ToUTF8(str, origStr);
1872bool IGraphicsWin::SetTextInClipboard(
const char* str)
1874 if (!OpenClipboard(mMainWnd))
1884 const int lenWide = UTF8ToUTF16Len(str);
1887 HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, lenWide*
sizeof(WCHAR));
1895 LPWSTR lpstrCopy = (LPWSTR) GlobalLock(hglbCopy);
1900 UTF8ToUTF16(lpstrCopy, str, lenWide);
1901 GlobalUnlock(hglbCopy);
1904 result = SetClipboardData(CF_UNICODETEXT, hglbCopy);
1908 GlobalFree(hglbCopy);
1917bool IGraphicsWin::SetFilePathInClipboard(
const char* path)
1919 if (!OpenClipboard(mMainWnd))
1924 UTF8AsUTF16 pathWide(path);
1928 HGLOBAL hGlobal = GlobalAlloc(GHND,
sizeof(DROPFILES) + (
sizeof(
wchar_t) * (pathWide.GetLength() + 1)));
1933 DROPFILES* pDropFiles = (DROPFILES*) GlobalLock(hGlobal);
1934 bool result =
false;
1940 pDropFiles->pFiles =
sizeof(DROPFILES);
1941 pDropFiles->pt = { 0, 0 };
1942 pDropFiles->fNC =
true;
1943 pDropFiles->fWide =
true;
1945 std::copy_n(pathWide.Get(), pathWide.GetLength(),
reinterpret_cast<wchar_t*
>(&pDropFiles[1]));
1947 GlobalUnlock(hGlobal);
1949 result = SetClipboardData(CF_HDROP, hGlobal);
1954 GlobalFree(hGlobal);
1960bool IGraphicsWin::InitiateExternalFileDragDrop(
const char* path,
const IRECT& )
1962 using namespace DragAndDropHelpers;
1963 OleInitialize(
nullptr);
1965 FORMATETC format = { CF_HDROP,
nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1967 DataObject* dataObj =
new DataObject(&format, path);
1968 DropSource* dropSource =
new DropSource();
1971 HRESULT ret = DoDragDrop(dataObj, dropSource, DROPEFFECT_COPY, &dropEffect);
1972 bool success = SUCCEEDED(ret);
1975 dropSource->Release();
1979 ReleaseMouseCapture();
1984static HFONT GetHFont(
const char* fontName,
int weight,
bool italic,
bool underline, DWORD quality = DEFAULT_QUALITY,
bool enumerate =
false)
1986 HDC hdc = GetDC(NULL);
1987 HFONT font =
nullptr;
1992 lFont.lfEscapement = 0;
1993 lFont.lfOrientation = 0;
1994 lFont.lfWeight = weight;
1995 lFont.lfItalic = italic;
1996 lFont.lfUnderline = underline;
1997 lFont.lfStrikeOut =
false;
1998 lFont.lfCharSet = DEFAULT_CHARSET;
1999 lFont.lfOutPrecision = OUT_TT_PRECIS;
2000 lFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2001 lFont.lfQuality = quality;
2002 lFont.lfPitchAndFamily = DEFAULT_PITCH;
2004 wcsncpy(lFont.lfFaceName, UTF8AsUTF16(fontName).Get(), LF_FACESIZE);
2006 auto enumProc = [](
const LOGFONTW* pLFont,
const TEXTMETRICW* pTextMetric, DWORD FontType, LPARAM lParam)
2011 if ((!enumerate || EnumFontFamiliesExW(hdc, &lFont, enumProc, NULL, 0) == -1))
2012 font = CreateFontIndirectW(&lFont);
2016 wchar_t selectedFontName[64] = {
'\0'};
2018 SelectFont(hdc, font);
2019 GetTextFaceW(hdc, 64, selectedFontName);
2020 if (strcmp(UTF16AsUTF8(selectedFontName).Get(), fontName))
2027 ReleaseDC(NULL, hdc);
2032PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fileNameOrResID)
2034 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
2036 void* pFontMem =
nullptr;
2038 WDL_String fullPath;
2040 const EResourceLocation fontLocation =
LocateResource(fileNameOrResID,
"ttf", fullPath, GetBundleID(), GetWinModuleHandle(),
nullptr);
2042 if (fontLocation == kNotFound)
2045 switch (fontLocation)
2049 HANDLE file = CreateFileW(UTF8AsUTF16(fullPath).Get(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2050 PlatformFontPtr ret =
nullptr;
2053 HANDLE mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
2056 resSize = (int) GetFileSize(file,
nullptr);
2057 pFontMem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
2058 ret = LoadPlatformFont(fontID, pFontMem, resSize);
2059 UnmapViewOfFile(pFontMem);
2060 CloseHandle(mapping);
2069 pFontMem =
const_cast<void *
>(
LoadWinResource(fullPath.Get(),
"ttf", resSize, GetWinModuleHandle()));
2070 return LoadPlatformFont(fontID, pFontMem, resSize);
2078PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
const char* fontName, ETextStyle style)
2080 int weight = style == ETextStyle::Bold ? FW_BOLD : FW_REGULAR;
2081 bool italic = style == ETextStyle::Italic;
2082 bool underline =
false;
2083 DWORD quality = DEFAULT_QUALITY;
2085 HFONT font = GetHFont(fontName, weight, italic, underline, quality,
true);
2087 return PlatformFontPtr(font ?
new Font(font,
TextStyleString(style),
true) :
nullptr);
2090PlatformFontPtr IGraphicsWin::LoadPlatformFont(
const char* fontID,
void* pData,
int dataSize)
2092 StaticStorage<InstalledFont>::Accessor fontStorage(sPlatformFontCache);
2094 std::unique_ptr<InstalledFont> pFont;
2095 void* pFontMem = pData;
2096 int resSize = dataSize;
2098 pFont = std::make_unique<InstalledFont>(pFontMem, resSize);
2100 if (pFontMem && pFont && pFont->IsValid())
2102 IFontInfo fontInfo(pFontMem, resSize, 0);
2103 WDL_String family = fontInfo.GetFamily();
2104 int weight = fontInfo.IsBold() ? FW_BOLD : FW_REGULAR;
2105 bool italic = fontInfo.IsItalic();
2106 bool underline = fontInfo.IsUnderline();
2108 HFONT font = GetHFont(family.Get(), weight, italic, underline);
2112 fontStorage.Add(pFont.release(), fontID);
2113 return PlatformFontPtr(
new Font(font,
"",
false));
2120void IGraphicsWin::CachePlatformFont(
const char* fontID,
const PlatformFontPtr& font)
2122 StaticStorage<HFontHolder>::Accessor hfontStorage(sHFontCache);
2124 HFONT hfont = font->GetDescriptor();
2126 if (!hfontStorage.Find(fontID))
2127 hfontStorage.Add(
new HFontHolder(hfont), fontID);
2130DWORD WINAPI VBlankRun(LPVOID lpParam)
2133 return pGraphics->OnVBlankRun();
2136void IGraphicsWin::StartVBlankThread(HWND hWnd)
2138 mVBlankWindow = hWnd;
2139 mVBlankShutdown =
false;
2141 mVBlankThread = ::CreateThread(NULL, 0, VBlankRun,
this, 0, &threadId);
2144void IGraphicsWin::StopVBlankThread()
2146 if (mVBlankThread != INVALID_HANDLE_VALUE)
2148 mVBlankShutdown =
true;
2149 ::WaitForSingleObject(mVBlankThread, 10000);
2150 mVBlankThread = INVALID_HANDLE_VALUE;
2166typedef UINT32 D3DKMT_HANDLE;
2167typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
2169typedef struct _D3DKMT_OPENADAPTERFROMHDC
2172 D3DKMT_HANDLE hAdapter;
2174 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2175} D3DKMT_OPENADAPTERFROMHDC;
2177typedef struct _D3DKMT_CLOSEADAPTER
2179 D3DKMT_HANDLE hAdapter;
2180} D3DKMT_CLOSEADAPTER;
2182typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT
2184 D3DKMT_HANDLE hAdapter;
2185 D3DKMT_HANDLE hDevice;
2186 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
2187} D3DKMT_WAITFORVERTICALBLANKEVENT;
2190typedef NTSTATUS(WINAPI* D3DKMTOpenAdapterFromHdc)(D3DKMT_OPENADAPTERFROMHDC* Arg1);
2191typedef NTSTATUS(WINAPI* D3DKMTCloseAdapter)(
const D3DKMT_CLOSEADAPTER* Arg1);
2192typedef NTSTATUS(WINAPI* D3DKMTWaitForVerticalBlankEvent)(
const D3DKMT_WAITFORVERTICALBLANKEVENT* Arg1);
2194DWORD IGraphicsWin::OnVBlankRun()
2196 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2200 float rateFallback = 60.0f;
2201 int rateMS = (int)(1000.0f / rateFallback);
2209 D3DKMTOpenAdapterFromHdc pOpen =
nullptr;
2210 D3DKMTCloseAdapter pClose =
nullptr;
2211 D3DKMTWaitForVerticalBlankEvent pWait =
nullptr;
2212 HINSTANCE hInst = LoadLibraryW(L
"gdi32.dll");
2214 if (hInst !=
nullptr)
2216 pOpen = (D3DKMTOpenAdapterFromHdc) GetProcAddress((HMODULE) hInst,
"D3DKMTOpenAdapterFromHdc");
2217 pClose = (D3DKMTCloseAdapter) GetProcAddress((HMODULE) hInst,
"D3DKMTCloseAdapter");
2218 pWait = (D3DKMTWaitForVerticalBlankEvent) GetProcAddress((HMODULE) hInst,
"D3DKMTWaitForVerticalBlankEvent");
2225 if (!pOpen || !pClose || !pWait)
2227 while (mVBlankShutdown ==
false)
2237 bool adapterIsOpen =
false;
2238 DWORD adapterLastFailTime = 0;
2239 _D3DKMT_WAITFORVERTICALBLANKEVENT we = { 0 };
2241 while (mVBlankShutdown ==
false)
2246 if (adapterLastFailTime < ::GetTickCount() - 1000)
2249 D3DKMT_OPENADAPTERFROMHDC openAdapterData = { 0 };
2250 HDC hDC = GetDC(mVBlankWindow);
2251 openAdapterData.hDc = hDC;
2252 NTSTATUS status = (*pOpen)(&openAdapterData);
2256 adapterLastFailTime = 0;
2257 adapterIsOpen =
true;
2258 we.hAdapter = openAdapterData.hAdapter;
2260 we.VidPnSourceId = openAdapterData.VidPnSourceId;
2265 adapterLastFailTime = ::GetTickCount();
2274 NTSTATUS status = (*pWait)(&we);
2278 _D3DKMT_CLOSEADAPTER ca;
2279 ca.hAdapter = we.hAdapter;
2281 adapterIsOpen =
false;
2298 _D3DKMT_CLOSEADAPTER ca;
2299 ca.hAdapter = we.hAdapter;
2301 adapterIsOpen =
false;
2306 if (hInst !=
nullptr)
2308 FreeLibrary((HMODULE)hInst);
2315void IGraphicsWin::VBlankNotify()
2318 ::PostMessageW(mVBlankWindow, WM_VBLANK, mVBlankCount, 0);
2322#if defined IGRAPHICS_SKIA
2323 #include "IGraphicsSkia.cpp"
2327#elif defined IGRAPHICS_NANOVG
2328 #include "IGraphicsNanoVG.cpp"
2329#ifdef IGRAPHICS_FREETYPE
2330#define FONS_USE_FREETYPE
2331 #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,...