The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5690 lines
200KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  14. #include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h>
  15. #endif
  16. namespace juce
  17. {
  18. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type")
  19. #undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
  20. // these are in the windows SDK, but need to be repeated here for GCC..
  21. #ifndef GET_APPCOMMAND_LPARAM
  22. #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
  23. #define FAPPCOMMAND_MASK 0xF000
  24. #define APPCOMMAND_MEDIA_NEXTTRACK 11
  25. #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
  26. #define APPCOMMAND_MEDIA_STOP 13
  27. #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
  28. #endif
  29. #ifndef WM_APPCOMMAND
  30. #define WM_APPCOMMAND 0x0319
  31. #endif
  32. void juce_repeatLastProcessPriority();
  33. bool juce_isRunningInWine();
  34. using CheckEventBlockedByModalComps = bool (*) (const MSG&);
  35. extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
  36. static bool shouldDeactivateTitleBar = true;
  37. void* getUser32Function (const char*);
  38. #if JUCE_DEBUG
  39. int numActiveScopedDpiAwarenessDisablers = 0;
  40. static bool isInScopedDPIAwarenessDisabler() { return numActiveScopedDpiAwarenessDisablers > 0; }
  41. extern HWND juce_messageWindowHandle;
  42. #endif
  43. struct ScopedDeviceContext
  44. {
  45. explicit ScopedDeviceContext (HWND h)
  46. : hwnd (h), dc (GetDC (hwnd))
  47. {
  48. }
  49. ~ScopedDeviceContext()
  50. {
  51. ReleaseDC (hwnd, dc);
  52. }
  53. HWND hwnd;
  54. HDC dc;
  55. JUCE_DECLARE_NON_COPYABLE (ScopedDeviceContext)
  56. JUCE_DECLARE_NON_MOVEABLE (ScopedDeviceContext)
  57. };
  58. //==============================================================================
  59. #ifndef WM_TOUCH
  60. enum
  61. {
  62. WM_TOUCH = 0x0240,
  63. TOUCHEVENTF_MOVE = 0x0001,
  64. TOUCHEVENTF_DOWN = 0x0002,
  65. TOUCHEVENTF_UP = 0x0004
  66. };
  67. typedef HANDLE HTOUCHINPUT;
  68. typedef HANDLE HGESTUREINFO;
  69. struct TOUCHINPUT
  70. {
  71. LONG x;
  72. LONG y;
  73. HANDLE hSource;
  74. DWORD dwID;
  75. DWORD dwFlags;
  76. DWORD dwMask;
  77. DWORD dwTime;
  78. ULONG_PTR dwExtraInfo;
  79. DWORD cxContact;
  80. DWORD cyContact;
  81. };
  82. struct GESTUREINFO
  83. {
  84. UINT cbSize;
  85. DWORD dwFlags;
  86. DWORD dwID;
  87. HWND hwndTarget;
  88. POINTS ptsLocation;
  89. DWORD dwInstanceID;
  90. DWORD dwSequenceID;
  91. ULONGLONG ullArguments;
  92. UINT cbExtraArgs;
  93. };
  94. #endif
  95. #ifndef WM_NCPOINTERUPDATE
  96. enum
  97. {
  98. WM_NCPOINTERUPDATE = 0x241,
  99. WM_NCPOINTERDOWN = 0x242,
  100. WM_NCPOINTERUP = 0x243,
  101. WM_POINTERUPDATE = 0x245,
  102. WM_POINTERDOWN = 0x246,
  103. WM_POINTERUP = 0x247,
  104. WM_POINTERENTER = 0x249,
  105. WM_POINTERLEAVE = 0x24A,
  106. WM_POINTERACTIVATE = 0x24B,
  107. WM_POINTERCAPTURECHANGED = 0x24C,
  108. WM_TOUCHHITTESTING = 0x24D,
  109. WM_POINTERWHEEL = 0x24E,
  110. WM_POINTERHWHEEL = 0x24F,
  111. WM_POINTERHITTEST = 0x250
  112. };
  113. enum
  114. {
  115. PT_TOUCH = 0x00000002,
  116. PT_PEN = 0x00000003
  117. };
  118. enum POINTER_BUTTON_CHANGE_TYPE
  119. {
  120. POINTER_CHANGE_NONE,
  121. POINTER_CHANGE_FIRSTBUTTON_DOWN,
  122. POINTER_CHANGE_FIRSTBUTTON_UP,
  123. POINTER_CHANGE_SECONDBUTTON_DOWN,
  124. POINTER_CHANGE_SECONDBUTTON_UP,
  125. POINTER_CHANGE_THIRDBUTTON_DOWN,
  126. POINTER_CHANGE_THIRDBUTTON_UP,
  127. POINTER_CHANGE_FOURTHBUTTON_DOWN,
  128. POINTER_CHANGE_FOURTHBUTTON_UP,
  129. POINTER_CHANGE_FIFTHBUTTON_DOWN,
  130. POINTER_CHANGE_FIFTHBUTTON_UP
  131. };
  132. enum
  133. {
  134. PEN_MASK_NONE = 0x00000000,
  135. PEN_MASK_PRESSURE = 0x00000001,
  136. PEN_MASK_ROTATION = 0x00000002,
  137. PEN_MASK_TILT_X = 0x00000004,
  138. PEN_MASK_TILT_Y = 0x00000008
  139. };
  140. enum
  141. {
  142. TOUCH_MASK_NONE = 0x00000000,
  143. TOUCH_MASK_CONTACTAREA = 0x00000001,
  144. TOUCH_MASK_ORIENTATION = 0x00000002,
  145. TOUCH_MASK_PRESSURE = 0x00000004
  146. };
  147. enum
  148. {
  149. POINTER_FLAG_NONE = 0x00000000,
  150. POINTER_FLAG_NEW = 0x00000001,
  151. POINTER_FLAG_INRANGE = 0x00000002,
  152. POINTER_FLAG_INCONTACT = 0x00000004,
  153. POINTER_FLAG_FIRSTBUTTON = 0x00000010,
  154. POINTER_FLAG_SECONDBUTTON = 0x00000020,
  155. POINTER_FLAG_THIRDBUTTON = 0x00000040,
  156. POINTER_FLAG_FOURTHBUTTON = 0x00000080,
  157. POINTER_FLAG_FIFTHBUTTON = 0x00000100,
  158. POINTER_FLAG_PRIMARY = 0x00002000,
  159. POINTER_FLAG_CONFIDENCE = 0x00004000,
  160. POINTER_FLAG_CANCELED = 0x00008000,
  161. POINTER_FLAG_DOWN = 0x00010000,
  162. POINTER_FLAG_UPDATE = 0x00020000,
  163. POINTER_FLAG_UP = 0x00040000,
  164. POINTER_FLAG_WHEEL = 0x00080000,
  165. POINTER_FLAG_HWHEEL = 0x00100000,
  166. POINTER_FLAG_CAPTURECHANGED = 0x00200000,
  167. POINTER_FLAG_HASTRANSFORM = 0x00400000
  168. };
  169. typedef DWORD POINTER_INPUT_TYPE;
  170. typedef UINT32 POINTER_FLAGS;
  171. typedef UINT32 PEN_FLAGS;
  172. typedef UINT32 PEN_MASK;
  173. typedef UINT32 TOUCH_FLAGS;
  174. typedef UINT32 TOUCH_MASK;
  175. struct POINTER_INFO
  176. {
  177. POINTER_INPUT_TYPE pointerType;
  178. UINT32 pointerId;
  179. UINT32 frameId;
  180. POINTER_FLAGS pointerFlags;
  181. HANDLE sourceDevice;
  182. HWND hwndTarget;
  183. POINT ptPixelLocation;
  184. POINT ptHimetricLocation;
  185. POINT ptPixelLocationRaw;
  186. POINT ptHimetricLocationRaw;
  187. DWORD dwTime;
  188. UINT32 historyCount;
  189. INT32 InputData;
  190. DWORD dwKeyStates;
  191. UINT64 PerformanceCount;
  192. POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
  193. };
  194. struct POINTER_TOUCH_INFO
  195. {
  196. POINTER_INFO pointerInfo;
  197. TOUCH_FLAGS touchFlags;
  198. TOUCH_MASK touchMask;
  199. RECT rcContact;
  200. RECT rcContactRaw;
  201. UINT32 orientation;
  202. UINT32 pressure;
  203. };
  204. struct POINTER_PEN_INFO
  205. {
  206. POINTER_INFO pointerInfo;
  207. PEN_FLAGS penFlags;
  208. PEN_MASK penMask;
  209. UINT32 pressure;
  210. UINT32 rotation;
  211. INT32 tiltX;
  212. INT32 tiltY;
  213. };
  214. #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
  215. #endif
  216. #ifndef MONITOR_DPI_TYPE
  217. enum Monitor_DPI_Type
  218. {
  219. MDT_Effective_DPI = 0,
  220. MDT_Angular_DPI = 1,
  221. MDT_Raw_DPI = 2,
  222. MDT_Default = MDT_Effective_DPI
  223. };
  224. #endif
  225. #ifndef DPI_AWARENESS
  226. enum DPI_Awareness
  227. {
  228. DPI_Awareness_Invalid = -1,
  229. DPI_Awareness_Unaware = 0,
  230. DPI_Awareness_System_Aware = 1,
  231. DPI_Awareness_Per_Monitor_Aware = 2
  232. };
  233. #endif
  234. #ifndef USER_DEFAULT_SCREEN_DPI
  235. #define USER_DEFAULT_SCREEN_DPI 96
  236. #endif
  237. #ifndef _DPI_AWARENESS_CONTEXTS_
  238. typedef HANDLE DPI_AWARENESS_CONTEXT;
  239. #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT) - 1)
  240. #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT) - 2)
  241. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT) - 3)
  242. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
  243. #endif
  244. // Some versions of the Windows 10 SDK define _DPI_AWARENESS_CONTEXTS_ but not
  245. // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  246. #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  247. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) - 4)
  248. #endif
  249. //==============================================================================
  250. using RegisterTouchWindowFunc = BOOL (WINAPI*) (HWND, ULONG);
  251. using GetTouchInputInfoFunc = BOOL (WINAPI*) (HTOUCHINPUT, UINT, TOUCHINPUT*, int);
  252. using CloseTouchInputHandleFunc = BOOL (WINAPI*) (HTOUCHINPUT);
  253. using GetGestureInfoFunc = BOOL (WINAPI*) (HGESTUREINFO, GESTUREINFO*);
  254. static RegisterTouchWindowFunc registerTouchWindow = nullptr;
  255. static GetTouchInputInfoFunc getTouchInputInfo = nullptr;
  256. static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr;
  257. static GetGestureInfoFunc getGestureInfo = nullptr;
  258. static bool hasCheckedForMultiTouch = false;
  259. static bool canUseMultiTouch()
  260. {
  261. if (registerTouchWindow == nullptr && ! hasCheckedForMultiTouch)
  262. {
  263. hasCheckedForMultiTouch = true;
  264. registerTouchWindow = (RegisterTouchWindowFunc) getUser32Function ("RegisterTouchWindow");
  265. getTouchInputInfo = (GetTouchInputInfoFunc) getUser32Function ("GetTouchInputInfo");
  266. closeTouchInputHandle = (CloseTouchInputHandleFunc) getUser32Function ("CloseTouchInputHandle");
  267. getGestureInfo = (GetGestureInfoFunc) getUser32Function ("GetGestureInfo");
  268. }
  269. return registerTouchWindow != nullptr;
  270. }
  271. //==============================================================================
  272. using GetPointerTypeFunc = BOOL (WINAPI*) (UINT32, POINTER_INPUT_TYPE*);
  273. using GetPointerTouchInfoFunc = BOOL (WINAPI*) (UINT32, POINTER_TOUCH_INFO*);
  274. using GetPointerPenInfoFunc = BOOL (WINAPI*) (UINT32, POINTER_PEN_INFO*);
  275. static GetPointerTypeFunc getPointerTypeFunction = nullptr;
  276. static GetPointerTouchInfoFunc getPointerTouchInfo = nullptr;
  277. static GetPointerPenInfoFunc getPointerPenInfo = nullptr;
  278. static bool canUsePointerAPI = false;
  279. static void checkForPointerAPI()
  280. {
  281. getPointerTypeFunction = (GetPointerTypeFunc) getUser32Function ("GetPointerType");
  282. getPointerTouchInfo = (GetPointerTouchInfoFunc) getUser32Function ("GetPointerTouchInfo");
  283. getPointerPenInfo = (GetPointerPenInfoFunc) getUser32Function ("GetPointerPenInfo");
  284. canUsePointerAPI = (getPointerTypeFunction != nullptr
  285. && getPointerTouchInfo != nullptr
  286. && getPointerPenInfo != nullptr);
  287. }
  288. //==============================================================================
  289. using SetProcessDPIAwareFunc = BOOL (WINAPI*) ();
  290. using SetProcessDPIAwarenessContextFunc = BOOL (WINAPI*) (DPI_AWARENESS_CONTEXT);
  291. using SetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (DPI_Awareness);
  292. using SetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (DPI_AWARENESS_CONTEXT);
  293. using GetDPIForWindowFunc = UINT (WINAPI*) (HWND);
  294. using GetDPIForMonitorFunc = HRESULT (WINAPI*) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*);
  295. using GetSystemMetricsForDpiFunc = int (WINAPI*) (int, UINT);
  296. using GetProcessDPIAwarenessFunc = HRESULT (WINAPI*) (HANDLE, DPI_Awareness*);
  297. using GetWindowDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) (HWND);
  298. using GetThreadDPIAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*) ();
  299. using GetAwarenessFromDpiAwarenessContextFunc = DPI_Awareness (WINAPI*) (DPI_AWARENESS_CONTEXT);
  300. using EnableNonClientDPIScalingFunc = BOOL (WINAPI*) (HWND);
  301. static SetProcessDPIAwareFunc setProcessDPIAware = nullptr;
  302. static SetProcessDPIAwarenessContextFunc setProcessDPIAwarenessContext = nullptr;
  303. static SetProcessDPIAwarenessFunc setProcessDPIAwareness = nullptr;
  304. static SetThreadDPIAwarenessContextFunc setThreadDPIAwarenessContext = nullptr;
  305. static GetDPIForMonitorFunc getDPIForMonitor = nullptr;
  306. static GetDPIForWindowFunc getDPIForWindow = nullptr;
  307. static GetProcessDPIAwarenessFunc getProcessDPIAwareness = nullptr;
  308. static GetWindowDPIAwarenessContextFunc getWindowDPIAwarenessContext = nullptr;
  309. static GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContext = nullptr;
  310. static GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContext = nullptr;
  311. static EnableNonClientDPIScalingFunc enableNonClientDPIScaling = nullptr;
  312. static bool hasCheckedForDPIAwareness = false;
  313. static void loadDPIAwarenessFunctions()
  314. {
  315. setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware");
  316. constexpr auto shcore = "SHCore.dll";
  317. LoadLibraryA (shcore);
  318. const auto shcoreModule = GetModuleHandleA (shcore);
  319. if (shcoreModule == nullptr)
  320. return;
  321. getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor");
  322. setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness");
  323. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  324. getDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow");
  325. getProcessDPIAwareness = (GetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "GetProcessDpiAwareness");
  326. getWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext");
  327. setThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  328. getThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext");
  329. getAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
  330. setProcessDPIAwarenessContext = (SetProcessDPIAwarenessContextFunc) getUser32Function ("SetProcessDpiAwarenessContext");
  331. enableNonClientDPIScaling = (EnableNonClientDPIScalingFunc) getUser32Function ("EnableNonClientDpiScaling");
  332. #endif
  333. }
  334. static void setDPIAwareness()
  335. {
  336. if (hasCheckedForDPIAwareness)
  337. return;
  338. hasCheckedForDPIAwareness = true;
  339. if (! JUCEApplicationBase::isStandaloneApp())
  340. return;
  341. loadDPIAwarenessFunctions();
  342. if (setProcessDPIAwarenessContext != nullptr
  343. && setProcessDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
  344. return;
  345. if (setProcessDPIAwareness != nullptr && enableNonClientDPIScaling != nullptr
  346. && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_Per_Monitor_Aware)))
  347. return;
  348. if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr
  349. && SUCCEEDED (setProcessDPIAwareness (DPI_Awareness::DPI_Awareness_System_Aware)))
  350. return;
  351. if (setProcessDPIAware != nullptr)
  352. setProcessDPIAware();
  353. }
  354. static bool isPerMonitorDPIAwareProcess()
  355. {
  356. #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
  357. return false;
  358. #else
  359. static bool dpiAware = []() -> bool
  360. {
  361. setDPIAwareness();
  362. if (! JUCEApplication::isStandaloneApp())
  363. return false;
  364. if (getProcessDPIAwareness == nullptr)
  365. return false;
  366. DPI_Awareness context;
  367. getProcessDPIAwareness (nullptr, &context);
  368. return context == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware;
  369. }();
  370. return dpiAware;
  371. #endif
  372. }
  373. static bool isPerMonitorDPIAwareWindow (HWND nativeWindow)
  374. {
  375. #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
  376. ignoreUnused (nativeWindow);
  377. return false;
  378. #else
  379. setDPIAwareness();
  380. if (getWindowDPIAwarenessContext != nullptr
  381. && getAwarenessFromDPIAwarenessContext != nullptr)
  382. {
  383. return (getAwarenessFromDPIAwarenessContext (getWindowDPIAwarenessContext (nativeWindow))
  384. == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware);
  385. }
  386. return isPerMonitorDPIAwareProcess();
  387. #endif
  388. }
  389. static bool isPerMonitorDPIAwareThread (GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContextIn = getThreadDPIAwarenessContext,
  390. GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContextIn = getAwarenessFromDPIAwarenessContext)
  391. {
  392. #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
  393. return false;
  394. #else
  395. setDPIAwareness();
  396. if (getThreadDPIAwarenessContextIn != nullptr
  397. && getAwarenessFromDPIAwarenessContextIn != nullptr)
  398. {
  399. return (getAwarenessFromDPIAwarenessContextIn (getThreadDPIAwarenessContextIn())
  400. == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware);
  401. }
  402. return isPerMonitorDPIAwareProcess();
  403. #endif
  404. }
  405. static double getGlobalDPI()
  406. {
  407. setDPIAwareness();
  408. ScopedDeviceContext deviceContext { nullptr };
  409. return (GetDeviceCaps (deviceContext.dc, LOGPIXELSX) + GetDeviceCaps (deviceContext.dc, LOGPIXELSY)) / 2.0;
  410. }
  411. //==============================================================================
  412. class ScopedThreadDPIAwarenessSetter::NativeImpl
  413. {
  414. public:
  415. explicit NativeImpl (HWND nativeWindow)
  416. {
  417. ignoreUnused (nativeWindow);
  418. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  419. if (auto* functionSingleton = FunctionSingleton::getInstance())
  420. {
  421. if (! functionSingleton->isLoaded())
  422. return;
  423. auto dpiAwareWindow = (functionSingleton->getAwarenessFromContext (functionSingleton->getWindowAwareness (nativeWindow))
  424. == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware);
  425. auto dpiAwareThread = (functionSingleton->getAwarenessFromContext (functionSingleton->getThreadAwareness())
  426. == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware);
  427. if (dpiAwareWindow && ! dpiAwareThread)
  428. oldContext = functionSingleton->setThreadAwareness (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
  429. else if (! dpiAwareWindow && dpiAwareThread)
  430. oldContext = functionSingleton->setThreadAwareness (DPI_AWARENESS_CONTEXT_UNAWARE);
  431. }
  432. #endif
  433. }
  434. ~NativeImpl()
  435. {
  436. if (oldContext != nullptr)
  437. if (auto* functionSingleton = FunctionSingleton::getInstance())
  438. functionSingleton->setThreadAwareness (oldContext);
  439. }
  440. private:
  441. struct FunctionSingleton : public DeletedAtShutdown
  442. {
  443. FunctionSingleton() = default;
  444. ~FunctionSingleton() override { clearSingletonInstance(); }
  445. SetThreadDPIAwarenessContextFunc setThreadAwareness = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  446. GetWindowDPIAwarenessContextFunc getWindowAwareness = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext");
  447. GetThreadDPIAwarenessContextFunc getThreadAwareness = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext");
  448. GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
  449. bool isLoaded() const noexcept
  450. {
  451. return setThreadAwareness != nullptr
  452. && getWindowAwareness != nullptr
  453. && getThreadAwareness != nullptr
  454. && getAwarenessFromContext != nullptr;
  455. }
  456. JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FunctionSingleton)
  457. JUCE_DECLARE_NON_COPYABLE (FunctionSingleton)
  458. JUCE_DECLARE_NON_MOVEABLE (FunctionSingleton)
  459. };
  460. DPI_AWARENESS_CONTEXT oldContext = nullptr;
  461. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeImpl)
  462. JUCE_DECLARE_NON_MOVEABLE (NativeImpl)
  463. };
  464. JUCE_IMPLEMENT_SINGLETON (ScopedThreadDPIAwarenessSetter::NativeImpl::FunctionSingleton)
  465. ScopedThreadDPIAwarenessSetter::ScopedThreadDPIAwarenessSetter (void* nativeWindow)
  466. {
  467. pimpl = std::make_unique<NativeImpl> ((HWND) nativeWindow);
  468. }
  469. ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() = default;
  470. ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler()
  471. {
  472. static auto localGetThreadDpiAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext");
  473. static auto localGetAwarenessFromDpiAwarenessContextFunc = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext");
  474. if (! isPerMonitorDPIAwareThread (localGetThreadDpiAwarenessContext, localGetAwarenessFromDpiAwarenessContextFunc))
  475. return;
  476. static auto localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  477. if (localSetThreadDPIAwarenessContext != nullptr)
  478. {
  479. previousContext = localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE);
  480. #if JUCE_DEBUG
  481. ++numActiveScopedDpiAwarenessDisablers;
  482. #endif
  483. }
  484. }
  485. ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler()
  486. {
  487. if (previousContext != nullptr)
  488. {
  489. static auto localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext");
  490. if (localSetThreadDPIAwarenessContext != nullptr)
  491. localSetThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext);
  492. #if JUCE_DEBUG
  493. --numActiveScopedDpiAwarenessDisablers;
  494. #endif
  495. }
  496. }
  497. //==============================================================================
  498. using SettingChangeCallbackFunc = void (*)(void);
  499. extern SettingChangeCallbackFunc settingChangeCallback;
  500. //==============================================================================
  501. static Rectangle<int> rectangleFromRECT (RECT r) noexcept { return { r.left, r.top, r.right - r.left, r.bottom - r.top }; }
  502. static RECT RECTFromRectangle (Rectangle<int> r) noexcept { return { r.getX(), r.getY(), r.getRight(), r.getBottom() }; }
  503. static Point<int> pointFromPOINT (POINT p) noexcept { return { p.x, p.y }; }
  504. static POINT POINTFromPoint (Point<int> p) noexcept { return { p.x, p.y }; }
  505. //==============================================================================
  506. static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd);
  507. template <typename ValueType>
  508. static Rectangle<ValueType> convertPhysicalScreenRectangleToLogical (Rectangle<ValueType> r, HWND h) noexcept
  509. {
  510. if (isPerMonitorDPIAwareWindow (h))
  511. return Desktop::getInstance().getDisplays().physicalToLogical (r, getCurrentDisplayFromScaleFactor (h));
  512. return r;
  513. }
  514. template <typename ValueType>
  515. static Rectangle<ValueType> convertLogicalScreenRectangleToPhysical (Rectangle<ValueType> r, HWND h) noexcept
  516. {
  517. if (isPerMonitorDPIAwareWindow (h))
  518. return Desktop::getInstance().getDisplays().logicalToPhysical (r, getCurrentDisplayFromScaleFactor (h));
  519. return r;
  520. }
  521. static Point<int> convertPhysicalScreenPointToLogical (Point<int> p, HWND h) noexcept
  522. {
  523. if (isPerMonitorDPIAwareWindow (h))
  524. return Desktop::getInstance().getDisplays().physicalToLogical (p, getCurrentDisplayFromScaleFactor (h));
  525. return p;
  526. }
  527. static Point<int> convertLogicalScreenPointToPhysical (Point<int> p, HWND h) noexcept
  528. {
  529. if (isPerMonitorDPIAwareWindow (h))
  530. return Desktop::getInstance().getDisplays().logicalToPhysical (p, getCurrentDisplayFromScaleFactor (h));
  531. return p;
  532. }
  533. JUCE_API double getScaleFactorForWindow (HWND h);
  534. JUCE_API double getScaleFactorForWindow (HWND h)
  535. {
  536. // NB. Using a local function here because we need to call this method from the plug-in wrappers
  537. // which don't load the DPI-awareness functions on startup
  538. static GetDPIForWindowFunc localGetDPIForWindow = nullptr;
  539. static bool hasChecked = false;
  540. if (! hasChecked)
  541. {
  542. hasChecked = true;
  543. if (localGetDPIForWindow == nullptr)
  544. localGetDPIForWindow = (GetDPIForWindowFunc) getUser32Function ("GetDpiForWindow");
  545. }
  546. if (localGetDPIForWindow != nullptr)
  547. return (double) localGetDPIForWindow (h) / USER_DEFAULT_SCREEN_DPI;
  548. return 1.0;
  549. }
  550. //==============================================================================
  551. static void setWindowPos (HWND hwnd, Rectangle<int> bounds, UINT flags, bool adjustTopLeft = false)
  552. {
  553. ScopedThreadDPIAwarenessSetter setter { hwnd };
  554. if (isPerMonitorDPIAwareWindow (hwnd))
  555. {
  556. if (adjustTopLeft)
  557. bounds = convertLogicalScreenRectangleToPhysical (bounds, hwnd)
  558. .withPosition (Desktop::getInstance().getDisplays().logicalToPhysical (bounds.getTopLeft()));
  559. else
  560. bounds = convertLogicalScreenRectangleToPhysical (bounds, hwnd);
  561. }
  562. SetWindowPos (hwnd, nullptr, bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), flags);
  563. }
  564. static RECT getWindowScreenRect (HWND hwnd)
  565. {
  566. ScopedThreadDPIAwarenessSetter setter { hwnd };
  567. RECT rect;
  568. GetWindowRect (hwnd, &rect);
  569. return rect;
  570. }
  571. static RECT getWindowClientRect (HWND hwnd)
  572. {
  573. auto rect = getWindowScreenRect (hwnd);
  574. if (auto parentH = GetParent (hwnd))
  575. {
  576. ScopedThreadDPIAwarenessSetter setter { hwnd };
  577. MapWindowPoints (HWND_DESKTOP, parentH, (LPPOINT) &rect, 2);
  578. }
  579. return rect;
  580. }
  581. static void setWindowZOrder (HWND hwnd, HWND insertAfter)
  582. {
  583. SetWindowPos (hwnd, insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  584. }
  585. //==============================================================================
  586. #if ! JUCE_MINGW
  587. extern RTL_OSVERSIONINFOW getWindowsVersionInfo();
  588. #endif
  589. double Desktop::getDefaultMasterScale()
  590. {
  591. if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess())
  592. return 1.0;
  593. return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI;
  594. }
  595. bool Desktop::canUseSemiTransparentWindows() noexcept
  596. {
  597. return true;
  598. }
  599. class Desktop::NativeDarkModeChangeDetectorImpl
  600. {
  601. public:
  602. NativeDarkModeChangeDetectorImpl()
  603. {
  604. #if ! JUCE_MINGW
  605. const auto winVer = getWindowsVersionInfo();
  606. if (winVer.dwMajorVersion >= 10 && winVer.dwBuildNumber >= 17763)
  607. {
  608. const auto uxtheme = "uxtheme.dll";
  609. LoadLibraryA (uxtheme);
  610. const auto uxthemeModule = GetModuleHandleA (uxtheme);
  611. if (uxthemeModule != nullptr)
  612. {
  613. shouldAppsUseDarkMode = (ShouldAppsUseDarkModeFunc) GetProcAddress (uxthemeModule, MAKEINTRESOURCEA (132));
  614. if (shouldAppsUseDarkMode != nullptr)
  615. darkModeEnabled = shouldAppsUseDarkMode() && ! isHighContrast();
  616. }
  617. }
  618. #endif
  619. }
  620. ~NativeDarkModeChangeDetectorImpl()
  621. {
  622. UnhookWindowsHookEx (hook);
  623. }
  624. bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
  625. private:
  626. static bool isHighContrast()
  627. {
  628. HIGHCONTRASTW highContrast {};
  629. if (SystemParametersInfoW (SPI_GETHIGHCONTRAST, sizeof (highContrast), &highContrast, false))
  630. return highContrast.dwFlags & HCF_HIGHCONTRASTON;
  631. return false;
  632. }
  633. static LRESULT CALLBACK callWndProc (int nCode, WPARAM wParam, LPARAM lParam)
  634. {
  635. auto* params = reinterpret_cast<CWPSTRUCT*> (lParam);
  636. if (nCode >= 0
  637. && params != nullptr
  638. && params->message == WM_SETTINGCHANGE
  639. && params->lParam != 0
  640. && CompareStringOrdinal (reinterpret_cast<LPWCH> (params->lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL)
  641. {
  642. Desktop::getInstance().nativeDarkModeChangeDetectorImpl->colourSetChanged();
  643. }
  644. return CallNextHookEx ({}, nCode, wParam, lParam);
  645. }
  646. void colourSetChanged()
  647. {
  648. if (shouldAppsUseDarkMode != nullptr)
  649. {
  650. const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, shouldAppsUseDarkMode() && ! isHighContrast());
  651. if (darkModeEnabled != wasDarkModeEnabled)
  652. Desktop::getInstance().darkModeChanged();
  653. }
  654. }
  655. using ShouldAppsUseDarkModeFunc = bool (WINAPI*)();
  656. ShouldAppsUseDarkModeFunc shouldAppsUseDarkMode = nullptr;
  657. bool darkModeEnabled = false;
  658. HHOOK hook { SetWindowsHookEx (WH_CALLWNDPROC,
  659. callWndProc,
  660. (HINSTANCE) juce::Process::getCurrentModuleInstanceHandle(),
  661. GetCurrentThreadId()) };
  662. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  663. };
  664. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  665. {
  666. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  667. }
  668. bool Desktop::isDarkModeActive() const
  669. {
  670. return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled();
  671. }
  672. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  673. {
  674. return upright;
  675. }
  676. int64 getMouseEventTime();
  677. int64 getMouseEventTime()
  678. {
  679. static int64 eventTimeOffset = 0;
  680. static LONG lastMessageTime = 0;
  681. const LONG thisMessageTime = GetMessageTime();
  682. if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
  683. {
  684. lastMessageTime = thisMessageTime;
  685. eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
  686. }
  687. return eventTimeOffset + thisMessageTime;
  688. }
  689. //==============================================================================
  690. const int extendedKeyModifier = 0x10000;
  691. const int KeyPress::spaceKey = VK_SPACE;
  692. const int KeyPress::returnKey = VK_RETURN;
  693. const int KeyPress::escapeKey = VK_ESCAPE;
  694. const int KeyPress::backspaceKey = VK_BACK;
  695. const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
  696. const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
  697. const int KeyPress::tabKey = VK_TAB;
  698. const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
  699. const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
  700. const int KeyPress::upKey = VK_UP | extendedKeyModifier;
  701. const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
  702. const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
  703. const int KeyPress::endKey = VK_END | extendedKeyModifier;
  704. const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
  705. const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
  706. const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
  707. const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
  708. const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
  709. const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
  710. const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
  711. const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
  712. const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
  713. const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
  714. const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
  715. const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
  716. const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
  717. const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
  718. const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
  719. const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
  720. const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
  721. const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
  722. const int KeyPress::F17Key = VK_F17 | extendedKeyModifier;
  723. const int KeyPress::F18Key = VK_F18 | extendedKeyModifier;
  724. const int KeyPress::F19Key = VK_F19 | extendedKeyModifier;
  725. const int KeyPress::F20Key = VK_F20 | extendedKeyModifier;
  726. const int KeyPress::F21Key = VK_F21 | extendedKeyModifier;
  727. const int KeyPress::F22Key = VK_F22 | extendedKeyModifier;
  728. const int KeyPress::F23Key = VK_F23 | extendedKeyModifier;
  729. const int KeyPress::F24Key = VK_F24 | extendedKeyModifier;
  730. const int KeyPress::F25Key = 0x31000; // Windows doesn't support F-keys 25 or higher
  731. const int KeyPress::F26Key = 0x31001;
  732. const int KeyPress::F27Key = 0x31002;
  733. const int KeyPress::F28Key = 0x31003;
  734. const int KeyPress::F29Key = 0x31004;
  735. const int KeyPress::F30Key = 0x31005;
  736. const int KeyPress::F31Key = 0x31006;
  737. const int KeyPress::F32Key = 0x31007;
  738. const int KeyPress::F33Key = 0x31008;
  739. const int KeyPress::F34Key = 0x31009;
  740. const int KeyPress::F35Key = 0x3100a;
  741. const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
  742. const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
  743. const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
  744. const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
  745. const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
  746. const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
  747. const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
  748. const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
  749. const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
  750. const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
  751. const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
  752. const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
  753. const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
  754. const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
  755. const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
  756. const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
  757. const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
  758. const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
  759. const int KeyPress::playKey = 0x30000;
  760. const int KeyPress::stopKey = 0x30001;
  761. const int KeyPress::fastForwardKey = 0x30002;
  762. const int KeyPress::rewindKey = 0x30003;
  763. //==============================================================================
  764. class WindowsBitmapImage : public ImagePixelData
  765. {
  766. public:
  767. WindowsBitmapImage (const Image::PixelFormat format,
  768. const int w, const int h, const bool clearImage)
  769. : ImagePixelData (format, w, h)
  770. {
  771. jassert (format == Image::RGB || format == Image::ARGB);
  772. static bool alwaysUse32Bits = isGraphicsCard32Bit(); // NB: for 32-bit cards, it's faster to use a 32-bit image.
  773. pixelStride = (alwaysUse32Bits || format == Image::ARGB) ? 4 : 3;
  774. lineStride = -((w * pixelStride + 3) & ~3);
  775. zerostruct (bitmapInfo);
  776. bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
  777. bitmapInfo.bV4Width = w;
  778. bitmapInfo.bV4Height = h;
  779. bitmapInfo.bV4Planes = 1;
  780. bitmapInfo.bV4CSType = 1;
  781. bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
  782. if (format == Image::ARGB)
  783. {
  784. bitmapInfo.bV4AlphaMask = 0xff000000;
  785. bitmapInfo.bV4RedMask = 0xff0000;
  786. bitmapInfo.bV4GreenMask = 0xff00;
  787. bitmapInfo.bV4BlueMask = 0xff;
  788. bitmapInfo.bV4V4Compression = BI_BITFIELDS;
  789. }
  790. else
  791. {
  792. bitmapInfo.bV4V4Compression = BI_RGB;
  793. }
  794. {
  795. ScopedDeviceContext deviceContext { nullptr };
  796. hdc = CreateCompatibleDC (deviceContext.dc);
  797. }
  798. SetMapMode (hdc, MM_TEXT);
  799. hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
  800. (void**) &bitmapData, nullptr, 0);
  801. if (hBitmap != nullptr)
  802. previousBitmap = SelectObject (hdc, hBitmap);
  803. if (format == Image::ARGB && clearImage)
  804. zeromem (bitmapData, (size_t) std::abs (h * lineStride));
  805. imageData = bitmapData - (lineStride * (h - 1));
  806. }
  807. ~WindowsBitmapImage() override
  808. {
  809. SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
  810. DeleteDC (hdc);
  811. DeleteObject (hBitmap);
  812. }
  813. std::unique_ptr<ImageType> createType() const override { return std::make_unique<NativeImageType>(); }
  814. std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
  815. {
  816. sendDataChangeMessage();
  817. return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
  818. }
  819. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
  820. {
  821. const auto offset = (size_t) (x * pixelStride + y * lineStride);
  822. bitmap.data = imageData + offset;
  823. bitmap.size = (size_t) (lineStride * height) - offset;
  824. bitmap.pixelFormat = pixelFormat;
  825. bitmap.lineStride = lineStride;
  826. bitmap.pixelStride = pixelStride;
  827. if (mode != Image::BitmapData::readOnly)
  828. sendDataChangeMessage();
  829. }
  830. ImagePixelData::Ptr clone() override
  831. {
  832. auto im = new WindowsBitmapImage (pixelFormat, width, height, false);
  833. for (int i = 0; i < height; ++i)
  834. memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
  835. return im;
  836. }
  837. void blitToWindow (HWND hwnd, HDC dc, bool transparent, int x, int y, uint8 updateLayeredWindowAlpha) noexcept
  838. {
  839. SetMapMode (dc, MM_TEXT);
  840. if (transparent)
  841. {
  842. auto windowBounds = getWindowScreenRect (hwnd);
  843. POINT p = { -x, -y };
  844. POINT pos = { windowBounds.left, windowBounds.top };
  845. SIZE size = { windowBounds.right - windowBounds.left,
  846. windowBounds.bottom - windowBounds.top };
  847. BLENDFUNCTION bf;
  848. bf.AlphaFormat = 1 /*AC_SRC_ALPHA*/;
  849. bf.BlendFlags = 0;
  850. bf.BlendOp = AC_SRC_OVER;
  851. bf.SourceConstantAlpha = updateLayeredWindowAlpha;
  852. UpdateLayeredWindow (hwnd, nullptr, &pos, &size, hdc, &p, 0, &bf, 2 /*ULW_ALPHA*/);
  853. }
  854. else
  855. {
  856. StretchDIBits (dc,
  857. x, y, width, height,
  858. 0, 0, width, height,
  859. bitmapData, (const BITMAPINFO*) &bitmapInfo,
  860. DIB_RGB_COLORS, SRCCOPY);
  861. }
  862. }
  863. HBITMAP hBitmap;
  864. HGDIOBJ previousBitmap;
  865. BITMAPV4HEADER bitmapInfo;
  866. HDC hdc;
  867. uint8* bitmapData;
  868. int pixelStride, lineStride;
  869. uint8* imageData;
  870. private:
  871. static bool isGraphicsCard32Bit()
  872. {
  873. ScopedDeviceContext deviceContext { nullptr };
  874. return GetDeviceCaps (deviceContext.dc, BITSPIXEL) > 24;
  875. }
  876. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage)
  877. };
  878. //==============================================================================
  879. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  880. {
  881. auto hwnd = (HWND) nativeWindowHandle;
  882. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd);
  883. const auto w = r.getWidth();
  884. const auto h = r.getHeight();
  885. auto nativeBitmap = new WindowsBitmapImage (Image::RGB, w, h, true);
  886. Image bitmap (nativeBitmap);
  887. ScopedDeviceContext deviceContext { hwnd };
  888. if (isPerMonitorDPIAwareProcess())
  889. {
  890. auto scale = getScaleFactorForWindow (hwnd);
  891. auto prevStretchMode = SetStretchBltMode (nativeBitmap->hdc, HALFTONE);
  892. SetBrushOrgEx (nativeBitmap->hdc, 0, 0, nullptr);
  893. StretchBlt (nativeBitmap->hdc, 0, 0, w, h,
  894. deviceContext.dc, 0, 0, roundToInt (w * scale), roundToInt (h * scale),
  895. SRCCOPY);
  896. SetStretchBltMode (nativeBitmap->hdc, prevStretchMode);
  897. }
  898. else
  899. {
  900. BitBlt (nativeBitmap->hdc, 0, 0, w, h, deviceContext.dc, 0, 0, SRCCOPY);
  901. }
  902. return SoftwareImageType().convert (bitmap);
  903. }
  904. //==============================================================================
  905. namespace IconConverters
  906. {
  907. static Image createImageFromHICON (HICON icon)
  908. {
  909. if (icon == nullptr)
  910. return {};
  911. struct ScopedICONINFO : public ICONINFO
  912. {
  913. ScopedICONINFO()
  914. {
  915. hbmColor = nullptr;
  916. hbmMask = nullptr;
  917. }
  918. ~ScopedICONINFO()
  919. {
  920. if (hbmColor != nullptr)
  921. ::DeleteObject (hbmColor);
  922. if (hbmMask != nullptr)
  923. ::DeleteObject (hbmMask);
  924. }
  925. };
  926. ScopedICONINFO info;
  927. if (! ::GetIconInfo (icon, &info))
  928. return {};
  929. BITMAP bm;
  930. if (! (::GetObject (info.hbmColor, sizeof (BITMAP), &bm)
  931. && bm.bmWidth > 0 && bm.bmHeight > 0))
  932. return {};
  933. ScopedDeviceContext deviceContext { nullptr };
  934. if (auto* dc = ::CreateCompatibleDC (deviceContext.dc))
  935. {
  936. BITMAPV5HEADER header = {};
  937. header.bV5Size = sizeof (BITMAPV5HEADER);
  938. header.bV5Width = bm.bmWidth;
  939. header.bV5Height = -bm.bmHeight;
  940. header.bV5Planes = 1;
  941. header.bV5Compression = BI_RGB;
  942. header.bV5BitCount = 32;
  943. header.bV5RedMask = 0x00FF0000;
  944. header.bV5GreenMask = 0x0000FF00;
  945. header.bV5BlueMask = 0x000000FF;
  946. header.bV5AlphaMask = 0xFF000000;
  947. header.bV5CSType = 0x57696E20; // 'Win '
  948. header.bV5Intent = LCS_GM_IMAGES;
  949. uint32* bitmapImageData = nullptr;
  950. if (auto* dib = ::CreateDIBSection (deviceContext.dc, (BITMAPINFO*) &header, DIB_RGB_COLORS,
  951. (void**) &bitmapImageData, nullptr, 0))
  952. {
  953. auto oldObject = ::SelectObject (dc, dib);
  954. auto numPixels = bm.bmWidth * bm.bmHeight;
  955. auto numColourComponents = (size_t) numPixels * 4;
  956. // Windows icon data comes as two layers, an XOR mask which contains the bulk
  957. // of the image data and an AND mask which provides the transparency. Annoyingly
  958. // the XOR mask can also contain an alpha channel, in which case the transparency
  959. // mask should not be applied, but there's no way to find out a priori if the XOR
  960. // mask contains an alpha channel.
  961. HeapBlock<bool> opacityMask (numPixels);
  962. memset (bitmapImageData, 0, numColourComponents);
  963. ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_MASK);
  964. for (int i = 0; i < numPixels; ++i)
  965. opacityMask[i] = (bitmapImageData[i] == 0);
  966. Image result = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
  967. Image::BitmapData imageData (result, Image::BitmapData::readWrite);
  968. memset (bitmapImageData, 0, numColourComponents);
  969. ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NORMAL);
  970. memcpy (imageData.data, bitmapImageData, numColourComponents);
  971. auto imageHasAlphaChannel = [&imageData, numPixels]()
  972. {
  973. for (int i = 0; i < numPixels; ++i)
  974. if (imageData.data[i * 4] != 0)
  975. return true;
  976. return false;
  977. };
  978. if (! imageHasAlphaChannel())
  979. for (int i = 0; i < numPixels; ++i)
  980. imageData.data[i * 4] = opacityMask[i] ? 0xff : 0x00;
  981. ::SelectObject (dc, oldObject);
  982. ::DeleteObject (dib);
  983. ::DeleteDC (dc);
  984. return result;
  985. }
  986. ::DeleteDC (dc);
  987. }
  988. return {};
  989. }
  990. HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY);
  991. HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
  992. {
  993. auto nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
  994. Image bitmap (nativeBitmap);
  995. {
  996. Graphics g (bitmap);
  997. g.drawImageAt (image, 0, 0);
  998. }
  999. auto mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, nullptr);
  1000. ICONINFO info;
  1001. info.fIcon = isIcon;
  1002. info.xHotspot = (DWORD) hotspotX;
  1003. info.yHotspot = (DWORD) hotspotY;
  1004. info.hbmMask = mask;
  1005. info.hbmColor = nativeBitmap->hBitmap;
  1006. auto hi = CreateIconIndirect (&info);
  1007. DeleteObject (mask);
  1008. return hi;
  1009. }
  1010. } // namespace IconConverters
  1011. //==============================================================================
  1012. JUCE_IUNKNOWNCLASS (ITipInvocation, "37c994e7-432b-4834-a2f7-dce1f13b834b")
  1013. {
  1014. static CLSID getCLSID() noexcept { return { 0x4ce576fa, 0x83dc, 0x4f88, { 0x95, 0x1c, 0x9d, 0x07, 0x82, 0xb4, 0xe3, 0x76 } }; }
  1015. JUCE_COMCALL Toggle (HWND) = 0;
  1016. };
  1017. } // namespace juce
  1018. #ifdef __CRT_UUID_DECL
  1019. __CRT_UUID_DECL (juce::ITipInvocation, 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b)
  1020. #endif
  1021. namespace juce
  1022. {
  1023. struct OnScreenKeyboard : public DeletedAtShutdown,
  1024. private Timer
  1025. {
  1026. void activate()
  1027. {
  1028. shouldBeActive = true;
  1029. startTimer (10);
  1030. }
  1031. void deactivate()
  1032. {
  1033. shouldBeActive = false;
  1034. startTimer (10);
  1035. }
  1036. JUCE_DECLARE_SINGLETON_SINGLETHREADED (OnScreenKeyboard, false)
  1037. private:
  1038. OnScreenKeyboard()
  1039. {
  1040. tipInvocation.CoCreateInstance (ITipInvocation::getCLSID(), CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER);
  1041. }
  1042. ~OnScreenKeyboard() override
  1043. {
  1044. clearSingletonInstance();
  1045. }
  1046. void timerCallback() override
  1047. {
  1048. stopTimer();
  1049. if (reentrant || tipInvocation == nullptr)
  1050. return;
  1051. const ScopedValueSetter<bool> setter (reentrant, true, false);
  1052. auto isActive = isKeyboardVisible();
  1053. if (isActive != shouldBeActive)
  1054. {
  1055. if (! isActive)
  1056. {
  1057. tipInvocation->Toggle (GetDesktopWindow());
  1058. }
  1059. else
  1060. {
  1061. if (auto hwnd = FindWindow (L"IPTip_Main_Window", nullptr))
  1062. PostMessage (hwnd, WM_SYSCOMMAND, (int) SC_CLOSE, 0);
  1063. }
  1064. }
  1065. }
  1066. bool isVisible()
  1067. {
  1068. if (auto hwnd = FindWindowEx (nullptr, nullptr, L"ApplicationFrameWindow", nullptr))
  1069. return FindWindowEx (hwnd, nullptr, L"Windows.UI.Core.CoreWindow", L"Microsoft Text Input Application") != nullptr;
  1070. return false;
  1071. }
  1072. bool isVisibleLegacy()
  1073. {
  1074. if (auto hwnd = FindWindow (L"IPTip_Main_Window", nullptr))
  1075. {
  1076. auto style = GetWindowLong (hwnd, GWL_STYLE);
  1077. return (style & WS_DISABLED) == 0 && (style & WS_VISIBLE) != 0;
  1078. }
  1079. return false;
  1080. }
  1081. bool isKeyboardVisible()
  1082. {
  1083. if (isVisible())
  1084. return true;
  1085. // isVisible() may fail on Win10 versions < 1709 so try the old method too
  1086. return isVisibleLegacy();
  1087. }
  1088. bool shouldBeActive = false, reentrant = false;
  1089. ComSmartPtr<ITipInvocation> tipInvocation;
  1090. };
  1091. JUCE_IMPLEMENT_SINGLETON (OnScreenKeyboard)
  1092. //==============================================================================
  1093. struct HSTRING_PRIVATE;
  1094. typedef HSTRING_PRIVATE* HSTRING;
  1095. struct IInspectable : public IUnknown
  1096. {
  1097. JUCE_COMCALL GetIids (ULONG* ,IID**) = 0;
  1098. JUCE_COMCALL GetRuntimeClassName (HSTRING*) = 0;
  1099. JUCE_COMCALL GetTrustLevel (void*) = 0;
  1100. };
  1101. JUCE_COMCLASS (IUIViewSettingsInterop, "3694dbf9-8f68-44be-8ff5-195c98ede8a6") : public IInspectable
  1102. {
  1103. JUCE_COMCALL GetForWindow (HWND, REFIID, void**) = 0;
  1104. };
  1105. JUCE_COMCLASS (IUIViewSettings, "c63657f6-8850-470d-88f8-455e16ea2c26") : public IInspectable
  1106. {
  1107. enum UserInteractionMode
  1108. {
  1109. Mouse = 0,
  1110. Touch = 1
  1111. };
  1112. JUCE_COMCALL GetUserInteractionMode (UserInteractionMode*) = 0;
  1113. };
  1114. } // namespace juce
  1115. #ifdef __CRT_UUID_DECL
  1116. __CRT_UUID_DECL (juce::IUIViewSettingsInterop, 0x3694dbf9, 0x8f68, 0x44be, 0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6)
  1117. __CRT_UUID_DECL (juce::IUIViewSettings, 0xc63657f6, 0x8850, 0x470d, 0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26)
  1118. #endif
  1119. namespace juce
  1120. {
  1121. struct UWPUIViewSettings
  1122. {
  1123. UWPUIViewSettings()
  1124. {
  1125. ComBaseModule dll (L"api-ms-win-core-winrt-l1-1-0");
  1126. if (dll.h != nullptr)
  1127. {
  1128. roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (dll.h, "RoInitialize");
  1129. roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (dll.h, "RoGetActivationFactory");
  1130. createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (dll.h, "WindowsCreateString");
  1131. deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (dll.h, "WindowsDeleteString");
  1132. if (roInitialize == nullptr || roGetActivationFactory == nullptr
  1133. || createHString == nullptr || deleteHString == nullptr)
  1134. return;
  1135. auto status = roInitialize (1);
  1136. if (status != S_OK && status != S_FALSE && (unsigned) status != 0x80010106L)
  1137. return;
  1138. LPCWSTR uwpClassName = L"Windows.UI.ViewManagement.UIViewSettings";
  1139. HSTRING uwpClassId = nullptr;
  1140. if (createHString (uwpClassName, (::UINT32) wcslen (uwpClassName), &uwpClassId) != S_OK
  1141. || uwpClassId == nullptr)
  1142. return;
  1143. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
  1144. status = roGetActivationFactory (uwpClassId, __uuidof (IUIViewSettingsInterop),
  1145. (void**) viewSettingsInterop.resetAndGetPointerAddress());
  1146. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1147. deleteHString (uwpClassId);
  1148. if (status != S_OK || viewSettingsInterop == nullptr)
  1149. return;
  1150. // move dll into member var
  1151. comBaseDLL = std::move (dll);
  1152. }
  1153. }
  1154. bool isTabletModeActivatedForWindow (::HWND hWnd) const
  1155. {
  1156. if (viewSettingsInterop == nullptr)
  1157. return false;
  1158. ComSmartPtr<IUIViewSettings> viewSettings;
  1159. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
  1160. if (viewSettingsInterop->GetForWindow (hWnd, __uuidof (IUIViewSettings),
  1161. (void**) viewSettings.resetAndGetPointerAddress()) == S_OK
  1162. && viewSettings != nullptr)
  1163. {
  1164. IUIViewSettings::UserInteractionMode mode;
  1165. if (viewSettings->GetUserInteractionMode (&mode) == S_OK)
  1166. return mode == IUIViewSettings::Touch;
  1167. }
  1168. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1169. return false;
  1170. }
  1171. private:
  1172. //==============================================================================
  1173. struct ComBaseModule
  1174. {
  1175. ComBaseModule() = default;
  1176. ComBaseModule (LPCWSTR libraryName) : h (::LoadLibrary (libraryName)) {}
  1177. ComBaseModule (ComBaseModule&& o) : h (o.h) { o.h = nullptr; }
  1178. ~ComBaseModule() { release(); }
  1179. void release() { if (h != nullptr) ::FreeLibrary (h); h = nullptr; }
  1180. ComBaseModule& operator= (ComBaseModule&& o) { release(); h = o.h; o.h = nullptr; return *this; }
  1181. HMODULE h = {};
  1182. };
  1183. using RoInitializeFuncPtr = HRESULT (WINAPI*) (int);
  1184. using RoGetActivationFactoryFuncPtr = HRESULT (WINAPI*) (HSTRING, REFIID, void**);
  1185. using WindowsCreateStringFuncPtr = HRESULT (WINAPI*) (LPCWSTR,UINT32, HSTRING*);
  1186. using WindowsDeleteStringFuncPtr = HRESULT (WINAPI*) (HSTRING);
  1187. ComBaseModule comBaseDLL;
  1188. ComSmartPtr<IUIViewSettingsInterop> viewSettingsInterop;
  1189. RoInitializeFuncPtr roInitialize;
  1190. RoGetActivationFactoryFuncPtr roGetActivationFactory;
  1191. WindowsCreateStringFuncPtr createHString;
  1192. WindowsDeleteStringFuncPtr deleteHString;
  1193. };
  1194. //==============================================================================
  1195. static HMONITOR getMonitorFromOutput (ComSmartPtr<IDXGIOutput> output)
  1196. {
  1197. DXGI_OUTPUT_DESC desc = {};
  1198. return (FAILED (output->GetDesc (&desc)) || ! desc.AttachedToDesktop)
  1199. ? nullptr
  1200. : desc.Monitor;
  1201. }
  1202. struct VBlankListener
  1203. {
  1204. virtual void onVBlank() = 0;
  1205. };
  1206. //==============================================================================
  1207. class VSyncThread : private Thread,
  1208. private AsyncUpdater
  1209. {
  1210. public:
  1211. VSyncThread (ComSmartPtr<IDXGIOutput> out,
  1212. HMONITOR mon,
  1213. VBlankListener& listener)
  1214. : Thread ("VSyncThread"),
  1215. output (out),
  1216. monitor (mon)
  1217. {
  1218. listeners.push_back (listener);
  1219. startThread (10);
  1220. }
  1221. ~VSyncThread() override
  1222. {
  1223. stopThread (-1);
  1224. cancelPendingUpdate();
  1225. }
  1226. void updateMonitor()
  1227. {
  1228. monitor = getMonitorFromOutput (output);
  1229. }
  1230. HMONITOR getMonitor() const noexcept { return monitor; }
  1231. void addListener (VBlankListener& listener)
  1232. {
  1233. listeners.push_back (listener);
  1234. }
  1235. bool removeListener (const VBlankListener& listener)
  1236. {
  1237. auto it = std::find_if (listeners.cbegin(),
  1238. listeners.cend(),
  1239. [&listener] (const auto& l) { return &(l.get()) == &listener; });
  1240. if (it != listeners.cend())
  1241. {
  1242. listeners.erase (it);
  1243. return true;
  1244. }
  1245. return false;
  1246. }
  1247. bool hasNoListeners() const noexcept
  1248. {
  1249. return listeners.empty();
  1250. }
  1251. bool hasListener (const VBlankListener& listener) const noexcept
  1252. {
  1253. return std::any_of (listeners.cbegin(),
  1254. listeners.cend(),
  1255. [&listener] (const auto& l) { return &(l.get()) == &listener; });
  1256. }
  1257. private:
  1258. //==============================================================================
  1259. void run() override
  1260. {
  1261. while (! threadShouldExit())
  1262. {
  1263. if (output->WaitForVBlank() == S_OK)
  1264. triggerAsyncUpdate();
  1265. else
  1266. Thread::sleep (1);
  1267. }
  1268. }
  1269. void handleAsyncUpdate() override
  1270. {
  1271. for (auto& listener : listeners)
  1272. listener.get().onVBlank();
  1273. }
  1274. //==============================================================================
  1275. ComSmartPtr<IDXGIOutput> output;
  1276. HMONITOR monitor = nullptr;
  1277. std::vector<std::reference_wrapper<VBlankListener>> listeners;
  1278. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSyncThread)
  1279. JUCE_DECLARE_NON_MOVEABLE (VSyncThread)
  1280. };
  1281. //==============================================================================
  1282. class VBlankDispatcher : public DeletedAtShutdown
  1283. {
  1284. public:
  1285. void updateDisplay (VBlankListener& listener, HMONITOR monitor)
  1286. {
  1287. if (monitor == nullptr)
  1288. {
  1289. removeListener (listener);
  1290. return;
  1291. }
  1292. auto threadWithListener = threads.end();
  1293. auto threadWithMonitor = threads.end();
  1294. for (auto it = threads.begin(); it != threads.end(); ++it)
  1295. {
  1296. if ((*it)->hasListener (listener))
  1297. threadWithListener = it;
  1298. if ((*it)->getMonitor() == monitor)
  1299. threadWithMonitor = it;
  1300. if (threadWithListener != threads.end()
  1301. && threadWithMonitor != threads.end())
  1302. {
  1303. if (threadWithListener == threadWithMonitor)
  1304. return;
  1305. (*threadWithMonitor)->addListener (listener);
  1306. // This may invalidate iterators, so be careful!
  1307. removeListener (threadWithListener, listener);
  1308. return;
  1309. }
  1310. }
  1311. if (threadWithMonitor != threads.end())
  1312. {
  1313. (*threadWithMonitor)->addListener (listener);
  1314. return;
  1315. }
  1316. if (threadWithListener != threads.end())
  1317. removeListener (threadWithListener, listener);
  1318. for (auto adapter : adapters)
  1319. {
  1320. UINT i = 0;
  1321. ComSmartPtr<IDXGIOutput> output;
  1322. while (adapter->EnumOutputs (i, output.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
  1323. {
  1324. if (getMonitorFromOutput (output) == monitor)
  1325. {
  1326. threads.emplace_back (std::make_unique<VSyncThread> (output, monitor, listener));
  1327. return;
  1328. }
  1329. ++i;
  1330. }
  1331. }
  1332. }
  1333. void removeListener (const VBlankListener& listener)
  1334. {
  1335. for (auto it = threads.begin(); it != threads.end(); ++it)
  1336. if (removeListener (it, listener))
  1337. return;
  1338. }
  1339. void reconfigureDisplays()
  1340. {
  1341. adapters.clear();
  1342. ComSmartPtr<IDXGIFactory> factory;
  1343. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
  1344. CreateDXGIFactory (__uuidof (IDXGIFactory), (void**)factory.resetAndGetPointerAddress());
  1345. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1346. UINT i = 0;
  1347. ComSmartPtr<IDXGIAdapter> adapter;
  1348. while (factory->EnumAdapters (i, adapter.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
  1349. {
  1350. adapters.push_back (adapter);
  1351. ++i;
  1352. }
  1353. for (auto& thread : threads)
  1354. thread->updateMonitor();
  1355. threads.erase (std::remove_if (threads.begin(),
  1356. threads.end(),
  1357. [] (const auto& thread) { return thread->getMonitor() == nullptr; }),
  1358. threads.end());
  1359. }
  1360. JUCE_DECLARE_SINGLETON_SINGLETHREADED (VBlankDispatcher, true)
  1361. private:
  1362. //==============================================================================
  1363. using Threads = std::vector<std::unique_ptr<VSyncThread>>;
  1364. VBlankDispatcher()
  1365. {
  1366. reconfigureDisplays();
  1367. }
  1368. ~VBlankDispatcher() override
  1369. {
  1370. threads.clear();
  1371. clearSingletonInstance();
  1372. }
  1373. // This may delete the corresponding thread and invalidate iterators,
  1374. // so be careful!
  1375. bool removeListener (Threads::iterator it, const VBlankListener& listener)
  1376. {
  1377. if ((*it)->removeListener (listener))
  1378. {
  1379. if ((*it)->hasNoListeners())
  1380. threads.erase (it);
  1381. return true;
  1382. }
  1383. return false;
  1384. }
  1385. //==============================================================================
  1386. std::vector<ComSmartPtr<IDXGIAdapter>> adapters;
  1387. Threads threads;
  1388. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher)
  1389. JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher)
  1390. };
  1391. JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher)
  1392. //==============================================================================
  1393. class HWNDComponentPeer : public ComponentPeer,
  1394. private VBlankListener,
  1395. private Timer
  1396. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1397. , public ModifierKeyReceiver
  1398. #endif
  1399. {
  1400. public:
  1401. enum RenderingEngineType
  1402. {
  1403. softwareRenderingEngine = 0,
  1404. direct2DRenderingEngine
  1405. };
  1406. //==============================================================================
  1407. HWNDComponentPeer (Component& comp, int windowStyleFlags, HWND parent, bool nonRepainting)
  1408. : ComponentPeer (comp, windowStyleFlags),
  1409. dontRepaint (nonRepainting),
  1410. parentToAddTo (parent),
  1411. currentRenderingEngine (softwareRenderingEngine)
  1412. {
  1413. callFunctionIfNotLocked (&createWindowCallback, this);
  1414. setTitle (component.getName());
  1415. updateShadower();
  1416. OnScreenKeyboard::getInstance();
  1417. getNativeRealtimeModifiers = []
  1418. {
  1419. HWNDComponentPeer::updateKeyModifiers();
  1420. int mouseMods = 0;
  1421. if (HWNDComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
  1422. if (HWNDComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
  1423. if (HWNDComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
  1424. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  1425. return ModifierKeys::currentModifiers;
  1426. };
  1427. if (updateCurrentMonitor())
  1428. VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
  1429. }
  1430. ~HWNDComponentPeer() override
  1431. {
  1432. VBlankDispatcher::getInstance()->removeListener (*this);
  1433. // do this first to avoid messages arriving for this window before it's destroyed
  1434. JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
  1435. if (isAccessibilityActive)
  1436. WindowsAccessibility::revokeUIAMapEntriesForWindow (hwnd);
  1437. shadower = nullptr;
  1438. currentTouches.deleteAllTouchesForPeer (this);
  1439. callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
  1440. if (currentWindowIcon != nullptr)
  1441. DestroyIcon (currentWindowIcon);
  1442. if (dropTarget != nullptr)
  1443. {
  1444. dropTarget->peerIsDeleted = true;
  1445. dropTarget->Release();
  1446. dropTarget = nullptr;
  1447. }
  1448. #if JUCE_DIRECT2D
  1449. direct2DContext = nullptr;
  1450. #endif
  1451. }
  1452. //==============================================================================
  1453. void* getNativeHandle() const override { return hwnd; }
  1454. void setVisible (bool shouldBeVisible) override
  1455. {
  1456. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1457. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  1458. if (shouldBeVisible)
  1459. InvalidateRect (hwnd, nullptr, 0);
  1460. else
  1461. lastPaintTime = 0;
  1462. }
  1463. void setTitle (const String& title) override
  1464. {
  1465. // Unfortunately some ancient bits of win32 mean you can only perform this operation from the message thread.
  1466. JUCE_ASSERT_MESSAGE_THREAD
  1467. SetWindowText (hwnd, title.toWideCharPointer());
  1468. }
  1469. void repaintNowIfTransparent()
  1470. {
  1471. if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
  1472. handlePaintMessage();
  1473. }
  1474. void updateBorderSize()
  1475. {
  1476. WINDOWINFO info;
  1477. info.cbSize = sizeof (info);
  1478. if (GetWindowInfo (hwnd, &info))
  1479. windowBorder = BorderSize<int> (roundToInt ((info.rcClient.top - info.rcWindow.top) / scaleFactor),
  1480. roundToInt ((info.rcClient.left - info.rcWindow.left) / scaleFactor),
  1481. roundToInt ((info.rcWindow.bottom - info.rcClient.bottom) / scaleFactor),
  1482. roundToInt ((info.rcWindow.right - info.rcClient.right) / scaleFactor));
  1483. #if JUCE_DIRECT2D
  1484. if (direct2DContext != nullptr)
  1485. direct2DContext->resized();
  1486. #endif
  1487. }
  1488. void setBounds (const Rectangle<int>& bounds, bool isNowFullScreen) override
  1489. {
  1490. // If we try to set new bounds while handling an existing position change,
  1491. // Windows may get confused about our current scale and size.
  1492. // This can happen when moving a window between displays, because the mouse-move
  1493. // generator in handlePositionChanged can cause the window to move again.
  1494. if (inHandlePositionChanged)
  1495. return;
  1496. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1497. fullScreen = isNowFullScreen;
  1498. auto newBounds = windowBorder.addedTo (bounds);
  1499. if (isUsingUpdateLayeredWindow())
  1500. {
  1501. if (auto parentHwnd = GetParent (hwnd))
  1502. {
  1503. auto parentRect = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (parentHwnd)), hwnd);
  1504. newBounds.translate (parentRect.getX(), parentRect.getY());
  1505. }
  1506. }
  1507. auto oldBounds = getBounds();
  1508. const bool hasMoved = (oldBounds.getPosition() != bounds.getPosition());
  1509. const bool hasResized = (oldBounds.getWidth() != bounds.getWidth()
  1510. || oldBounds.getHeight() != bounds.getHeight());
  1511. DWORD flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
  1512. if (! hasMoved) flags |= SWP_NOMOVE;
  1513. if (! hasResized) flags |= SWP_NOSIZE;
  1514. setWindowPos (hwnd, newBounds, flags, ! inDpiChange);
  1515. if (hasResized && isValidPeer (this))
  1516. {
  1517. updateBorderSize();
  1518. repaintNowIfTransparent();
  1519. }
  1520. }
  1521. Rectangle<int> getBounds() const override
  1522. {
  1523. auto bounds = [this]
  1524. {
  1525. if (parentToAddTo == nullptr)
  1526. return convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd);
  1527. auto localBounds = rectangleFromRECT (getWindowClientRect (hwnd));
  1528. if (isPerMonitorDPIAwareWindow (hwnd))
  1529. return (localBounds.toDouble() / getPlatformScaleFactor()).toNearestInt();
  1530. return localBounds;
  1531. }();
  1532. return windowBorder.subtractedFrom (bounds);
  1533. }
  1534. Point<int> getScreenPosition() const
  1535. {
  1536. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd);
  1537. return { r.getX() + windowBorder.getLeft(),
  1538. r.getY() + windowBorder.getTop() };
  1539. }
  1540. Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getScreenPosition().toFloat(); }
  1541. Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getScreenPosition().toFloat(); }
  1542. using ComponentPeer::localToGlobal;
  1543. using ComponentPeer::globalToLocal;
  1544. void setAlpha (float newAlpha) override
  1545. {
  1546. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1547. auto intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
  1548. if (component.isOpaque())
  1549. {
  1550. if (newAlpha < 1.0f)
  1551. {
  1552. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  1553. SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
  1554. }
  1555. else
  1556. {
  1557. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
  1558. RedrawWindow (hwnd, nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
  1559. }
  1560. }
  1561. else
  1562. {
  1563. updateLayeredWindowAlpha = intAlpha;
  1564. component.repaint();
  1565. }
  1566. }
  1567. void setMinimised (bool shouldBeMinimised) override
  1568. {
  1569. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1570. if (shouldBeMinimised != isMinimised())
  1571. ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_RESTORE);
  1572. }
  1573. bool isMinimised() const override
  1574. {
  1575. WINDOWPLACEMENT wp;
  1576. wp.length = sizeof (WINDOWPLACEMENT);
  1577. GetWindowPlacement (hwnd, &wp);
  1578. return wp.showCmd == SW_SHOWMINIMIZED;
  1579. }
  1580. void setFullScreen (bool shouldBeFullScreen) override
  1581. {
  1582. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1583. setMinimised (false);
  1584. if (isFullScreen() != shouldBeFullScreen)
  1585. {
  1586. if (constrainer != nullptr)
  1587. constrainer->resizeStart();
  1588. fullScreen = shouldBeFullScreen;
  1589. const WeakReference<Component> deletionChecker (&component);
  1590. if (! fullScreen)
  1591. {
  1592. auto boundsCopy = lastNonFullscreenBounds;
  1593. if (hasTitleBar())
  1594. ShowWindow (hwnd, SW_SHOWNORMAL);
  1595. if (! boundsCopy.isEmpty())
  1596. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, boundsCopy), false);
  1597. }
  1598. else
  1599. {
  1600. if (hasTitleBar())
  1601. ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  1602. else
  1603. SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
  1604. }
  1605. if (deletionChecker != nullptr)
  1606. handleMovedOrResized();
  1607. if (constrainer != nullptr)
  1608. constrainer->resizeEnd();
  1609. }
  1610. }
  1611. bool isFullScreen() const override
  1612. {
  1613. if (! hasTitleBar())
  1614. return fullScreen;
  1615. WINDOWPLACEMENT wp;
  1616. wp.length = sizeof (wp);
  1617. GetWindowPlacement (hwnd, &wp);
  1618. return wp.showCmd == SW_SHOWMAXIMIZED;
  1619. }
  1620. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  1621. {
  1622. auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd);
  1623. if (! r.withZeroOrigin().contains (localPos))
  1624. return false;
  1625. auto w = WindowFromPoint (POINTFromPoint (convertLogicalScreenPointToPhysical (localPos + getScreenPosition(),
  1626. hwnd)));
  1627. return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
  1628. }
  1629. OptionalBorderSize getFrameSizeIfPresent() const override
  1630. {
  1631. return ComponentPeer::OptionalBorderSize { windowBorder };
  1632. }
  1633. BorderSize<int> getFrameSize() const override
  1634. {
  1635. return windowBorder;
  1636. }
  1637. bool setAlwaysOnTop (bool alwaysOnTop) override
  1638. {
  1639. const bool oldDeactivate = shouldDeactivateTitleBar;
  1640. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1641. setWindowZOrder (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST);
  1642. shouldDeactivateTitleBar = oldDeactivate;
  1643. if (shadower != nullptr)
  1644. handleBroughtToFront();
  1645. return true;
  1646. }
  1647. void toFront (bool makeActive) override
  1648. {
  1649. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1650. setMinimised (false);
  1651. const bool oldDeactivate = shouldDeactivateTitleBar;
  1652. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1653. callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
  1654. shouldDeactivateTitleBar = oldDeactivate;
  1655. if (! makeActive)
  1656. {
  1657. // in this case a broughttofront call won't have occurred, so do it now..
  1658. handleBroughtToFront();
  1659. }
  1660. }
  1661. void toBehind (ComponentPeer* other) override
  1662. {
  1663. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1664. if (auto* otherPeer = dynamic_cast<HWNDComponentPeer*> (other))
  1665. {
  1666. setMinimised (false);
  1667. // Must be careful not to try to put a topmost window behind a normal one, or Windows
  1668. // promotes the normal one to be topmost!
  1669. if (component.isAlwaysOnTop() == otherPeer->getComponent().isAlwaysOnTop())
  1670. setWindowZOrder (hwnd, otherPeer->hwnd);
  1671. else if (otherPeer->getComponent().isAlwaysOnTop())
  1672. setWindowZOrder (hwnd, HWND_TOP);
  1673. }
  1674. else
  1675. {
  1676. jassertfalse; // wrong type of window?
  1677. }
  1678. }
  1679. bool isFocused() const override
  1680. {
  1681. return callFunctionIfNotLocked (&getFocusCallback, nullptr) == (void*) hwnd;
  1682. }
  1683. void grabFocus() override
  1684. {
  1685. const ScopedValueSetter<bool> scope (shouldIgnoreModalDismiss, true);
  1686. const bool oldDeactivate = shouldDeactivateTitleBar;
  1687. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1688. callFunctionIfNotLocked (&setFocusCallback, hwnd);
  1689. shouldDeactivateTitleBar = oldDeactivate;
  1690. }
  1691. void textInputRequired (Point<int>, TextInputTarget&) override
  1692. {
  1693. if (! hasCreatedCaret)
  1694. {
  1695. hasCreatedCaret = true;
  1696. CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
  1697. }
  1698. ShowCaret (hwnd);
  1699. SetCaretPos (0, 0);
  1700. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1701. OnScreenKeyboard::getInstance()->activate();
  1702. }
  1703. void closeInputMethodContext() override
  1704. {
  1705. imeHandler.handleSetContext (hwnd, false);
  1706. }
  1707. void dismissPendingTextInput() override
  1708. {
  1709. closeInputMethodContext();
  1710. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1711. OnScreenKeyboard::getInstance()->deactivate();
  1712. }
  1713. void repaint (const Rectangle<int>& area) override
  1714. {
  1715. deferredRepaints.add ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
  1716. }
  1717. void dispatchDeferredRepaints()
  1718. {
  1719. for (auto deferredRect : deferredRepaints)
  1720. {
  1721. auto r = RECTFromRectangle (deferredRect);
  1722. InvalidateRect (hwnd, &r, FALSE);
  1723. }
  1724. deferredRepaints.clear();
  1725. }
  1726. void performAnyPendingRepaintsNow() override
  1727. {
  1728. if (component.isVisible())
  1729. {
  1730. dispatchDeferredRepaints();
  1731. WeakReference<Component> localRef (&component);
  1732. MSG m;
  1733. if (isUsingUpdateLayeredWindow() || PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE))
  1734. if (localRef != nullptr) // (the PeekMessage call can dispatch messages, which may delete this comp)
  1735. handlePaintMessage();
  1736. }
  1737. }
  1738. //==============================================================================
  1739. void onVBlank() override
  1740. {
  1741. dispatchDeferredRepaints();
  1742. }
  1743. //==============================================================================
  1744. static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
  1745. {
  1746. if (h != nullptr && JuceWindowIdentifier::isJUCEWindow (h))
  1747. return (HWNDComponentPeer*) GetWindowLongPtr (h, 8);
  1748. return nullptr;
  1749. }
  1750. //==============================================================================
  1751. bool isInside (HWND h) const noexcept
  1752. {
  1753. return GetAncestor (hwnd, GA_ROOT) == h;
  1754. }
  1755. //==============================================================================
  1756. static bool isKeyDown (const int key) noexcept { return (GetAsyncKeyState (key) & 0x8000) != 0; }
  1757. static void updateKeyModifiers() noexcept
  1758. {
  1759. int keyMods = 0;
  1760. if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
  1761. if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
  1762. if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
  1763. // workaround: Windows maps AltGr to left-Ctrl + right-Alt.
  1764. if (isKeyDown (VK_RMENU) && !isKeyDown (VK_RCONTROL))
  1765. {
  1766. keyMods = (keyMods & ~ModifierKeys::ctrlModifier) | ModifierKeys::altModifier;
  1767. }
  1768. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
  1769. }
  1770. static void updateModifiersFromWParam (const WPARAM wParam)
  1771. {
  1772. int mouseMods = 0;
  1773. if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
  1774. if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
  1775. if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
  1776. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  1777. updateKeyModifiers();
  1778. }
  1779. //==============================================================================
  1780. bool dontRepaint;
  1781. static ModifierKeys modifiersAtLastCallback;
  1782. //==============================================================================
  1783. struct FileDropTarget : public ComBaseClassHelper<IDropTarget>
  1784. {
  1785. FileDropTarget (HWNDComponentPeer& p) : peer (p) {}
  1786. JUCE_COMRESULT DragEnter (IDataObject* pDataObject, DWORD grfKeyState, POINTL mousePos, DWORD* pdwEffect) override
  1787. {
  1788. auto hr = updateFileList (pDataObject);
  1789. if (FAILED (hr))
  1790. return hr;
  1791. return DragOver (grfKeyState, mousePos, pdwEffect);
  1792. }
  1793. JUCE_COMRESULT DragLeave() override
  1794. {
  1795. if (peerIsDeleted)
  1796. return S_FALSE;
  1797. peer.handleDragExit (dragInfo);
  1798. return S_OK;
  1799. }
  1800. JUCE_COMRESULT DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) override
  1801. {
  1802. if (peerIsDeleted)
  1803. return S_FALSE;
  1804. dragInfo.position = getMousePos (mousePos).roundToInt();
  1805. *pdwEffect = peer.handleDragMove (dragInfo) ? (DWORD) DROPEFFECT_COPY
  1806. : (DWORD) DROPEFFECT_NONE;
  1807. return S_OK;
  1808. }
  1809. JUCE_COMRESULT Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) override
  1810. {
  1811. auto hr = updateFileList (pDataObject);
  1812. if (FAILED (hr))
  1813. return hr;
  1814. dragInfo.position = getMousePos (mousePos).roundToInt();
  1815. *pdwEffect = peer.handleDragDrop (dragInfo) ? (DWORD) DROPEFFECT_COPY
  1816. : (DWORD) DROPEFFECT_NONE;
  1817. return S_OK;
  1818. }
  1819. HWNDComponentPeer& peer;
  1820. ComponentPeer::DragInfo dragInfo;
  1821. bool peerIsDeleted = false;
  1822. private:
  1823. Point<float> getMousePos (POINTL mousePos) const
  1824. {
  1825. const auto originalPos = pointFromPOINT ({ mousePos.x, mousePos.y });
  1826. const auto logicalPos = convertPhysicalScreenPointToLogical (originalPos, peer.hwnd);
  1827. return ScalingHelpers::screenPosToLocalPos (peer.component, logicalPos.toFloat());
  1828. }
  1829. struct DroppedData
  1830. {
  1831. DroppedData (IDataObject* dataObject, CLIPFORMAT type)
  1832. {
  1833. FORMATETC format = { type, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1834. if (SUCCEEDED (error = dataObject->GetData (&format, &medium)) && medium.hGlobal != nullptr)
  1835. {
  1836. dataSize = GlobalSize (medium.hGlobal);
  1837. data = GlobalLock (medium.hGlobal);
  1838. }
  1839. }
  1840. ~DroppedData()
  1841. {
  1842. if (data != nullptr && medium.hGlobal != nullptr)
  1843. GlobalUnlock (medium.hGlobal);
  1844. }
  1845. HRESULT error;
  1846. STGMEDIUM medium { TYMED_HGLOBAL, { nullptr }, nullptr };
  1847. void* data = {};
  1848. SIZE_T dataSize;
  1849. };
  1850. void parseFileList (HDROP dropFiles)
  1851. {
  1852. dragInfo.files.clearQuick();
  1853. std::vector<TCHAR> nameBuffer;
  1854. const auto numFiles = DragQueryFile (dropFiles, ~(UINT) 0, nullptr, 0);
  1855. for (UINT i = 0; i < numFiles; ++i)
  1856. {
  1857. const auto bufferSize = DragQueryFile (dropFiles, i, nullptr, 0);
  1858. nameBuffer.clear();
  1859. nameBuffer.resize (bufferSize + 1, 0); // + 1 for the null terminator
  1860. const auto readCharacters = DragQueryFile (dropFiles, i, nameBuffer.data(), (UINT) nameBuffer.size());
  1861. ignoreUnused (readCharacters);
  1862. jassert (readCharacters == bufferSize);
  1863. dragInfo.files.add (String (nameBuffer.data()));
  1864. }
  1865. }
  1866. HRESULT updateFileList (IDataObject* const dataObject)
  1867. {
  1868. if (peerIsDeleted)
  1869. return S_FALSE;
  1870. dragInfo.clear();
  1871. {
  1872. DroppedData fileData (dataObject, CF_HDROP);
  1873. if (SUCCEEDED (fileData.error))
  1874. {
  1875. parseFileList (static_cast<HDROP> (fileData.data));
  1876. return S_OK;
  1877. }
  1878. }
  1879. DroppedData textData (dataObject, CF_UNICODETEXT);
  1880. if (SUCCEEDED (textData.error))
  1881. {
  1882. dragInfo.text = String (CharPointer_UTF16 ((const WCHAR*) textData.data),
  1883. CharPointer_UTF16 ((const WCHAR*) addBytesToPointer (textData.data, textData.dataSize)));
  1884. return S_OK;
  1885. }
  1886. return textData.error;
  1887. }
  1888. JUCE_DECLARE_NON_COPYABLE (FileDropTarget)
  1889. };
  1890. static bool offerKeyMessageToJUCEWindow (MSG& m)
  1891. {
  1892. if (m.message == WM_KEYDOWN || m.message == WM_KEYUP)
  1893. {
  1894. if (Component::getCurrentlyFocusedComponent() != nullptr)
  1895. {
  1896. if (auto* peer = getOwnerOfWindow (m.hwnd))
  1897. {
  1898. ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { m.hwnd };
  1899. return m.message == WM_KEYDOWN ? peer->doKeyDown (m.wParam)
  1900. : peer->doKeyUp (m.wParam);
  1901. }
  1902. }
  1903. }
  1904. return false;
  1905. }
  1906. double getPlatformScaleFactor() const noexcept override
  1907. {
  1908. #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE
  1909. return 1.0;
  1910. #else
  1911. if (! isPerMonitorDPIAwareWindow (hwnd))
  1912. return 1.0;
  1913. if (auto* parentHWND = GetParent (hwnd))
  1914. {
  1915. if (auto* parentPeer = getOwnerOfWindow (parentHWND))
  1916. return parentPeer->getPlatformScaleFactor();
  1917. if (getDPIForWindow != nullptr)
  1918. return getScaleFactorForWindow (parentHWND);
  1919. }
  1920. return scaleFactor;
  1921. #endif
  1922. }
  1923. private:
  1924. HWND hwnd, parentToAddTo;
  1925. std::unique_ptr<DropShadower> shadower;
  1926. RenderingEngineType currentRenderingEngine;
  1927. #if JUCE_DIRECT2D
  1928. std::unique_ptr<Direct2DLowLevelGraphicsContext> direct2DContext;
  1929. #endif
  1930. uint32 lastPaintTime = 0;
  1931. ULONGLONG lastMagnifySize = 0;
  1932. bool fullScreen = false, isDragging = false, isMouseOver = false,
  1933. hasCreatedCaret = false, constrainerIsResizing = false;
  1934. BorderSize<int> windowBorder;
  1935. HICON currentWindowIcon = nullptr;
  1936. FileDropTarget* dropTarget = nullptr;
  1937. uint8 updateLayeredWindowAlpha = 255;
  1938. UWPUIViewSettings uwpViewSettings;
  1939. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1940. ModifierKeyProvider* modProvider = nullptr;
  1941. #endif
  1942. double scaleFactor = 1.0;
  1943. bool inDpiChange = 0, inHandlePositionChanged = 0;
  1944. HMONITOR currentMonitor = nullptr;
  1945. bool isAccessibilityActive = false;
  1946. //==============================================================================
  1947. static MultiTouchMapper<DWORD> currentTouches;
  1948. //==============================================================================
  1949. struct TemporaryImage : private Timer
  1950. {
  1951. TemporaryImage() {}
  1952. Image& getImage (bool transparent, int w, int h)
  1953. {
  1954. auto format = transparent ? Image::ARGB : Image::RGB;
  1955. if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
  1956. image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
  1957. startTimer (3000);
  1958. return image;
  1959. }
  1960. void timerCallback() override
  1961. {
  1962. stopTimer();
  1963. image = {};
  1964. }
  1965. private:
  1966. Image image;
  1967. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage)
  1968. };
  1969. TemporaryImage offscreenImageGenerator;
  1970. //==============================================================================
  1971. class WindowClassHolder : private DeletedAtShutdown
  1972. {
  1973. public:
  1974. WindowClassHolder()
  1975. {
  1976. // this name has to be different for each app/dll instance because otherwise poor old Windows can
  1977. // get a bit confused (even despite it not being a process-global window class).
  1978. String windowClassName ("JUCE_");
  1979. windowClassName << String::toHexString (Time::currentTimeMillis());
  1980. auto moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  1981. TCHAR moduleFile[1024] = {};
  1982. GetModuleFileName (moduleHandle, moduleFile, 1024);
  1983. WORD iconNum = 0;
  1984. WNDCLASSEX wcex = {};
  1985. wcex.cbSize = sizeof (wcex);
  1986. wcex.style = CS_OWNDC;
  1987. wcex.lpfnWndProc = (WNDPROC) windowProc;
  1988. wcex.lpszClassName = windowClassName.toWideCharPointer();
  1989. wcex.cbWndExtra = 32;
  1990. wcex.hInstance = moduleHandle;
  1991. wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1992. iconNum = 1;
  1993. wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1994. atom = RegisterClassEx (&wcex);
  1995. jassert (atom != 0);
  1996. isEventBlockedByModalComps = checkEventBlockedByModalComps;
  1997. }
  1998. ~WindowClassHolder()
  1999. {
  2000. if (ComponentPeer::getNumPeers() == 0)
  2001. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  2002. clearSingletonInstance();
  2003. }
  2004. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
  2005. JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (WindowClassHolder)
  2006. private:
  2007. ATOM atom;
  2008. static bool isHWNDBlockedByModalComponents (HWND h)
  2009. {
  2010. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  2011. if (auto* c = Desktop::getInstance().getComponent (i))
  2012. if ((! c->isCurrentlyBlockedByAnotherModalComponent())
  2013. && IsChild ((HWND) c->getWindowHandle(), h))
  2014. return false;
  2015. return true;
  2016. }
  2017. static bool checkEventBlockedByModalComps (const MSG& m)
  2018. {
  2019. if (Component::getNumCurrentlyModalComponents() == 0 || JuceWindowIdentifier::isJUCEWindow (m.hwnd))
  2020. return false;
  2021. switch (m.message)
  2022. {
  2023. case WM_MOUSEMOVE:
  2024. case WM_NCMOUSEMOVE:
  2025. case 0x020A: /* WM_MOUSEWHEEL */
  2026. case 0x020E: /* WM_MOUSEHWHEEL */
  2027. case WM_KEYUP:
  2028. case WM_SYSKEYUP:
  2029. case WM_CHAR:
  2030. case WM_APPCOMMAND:
  2031. case WM_LBUTTONUP:
  2032. case WM_MBUTTONUP:
  2033. case WM_RBUTTONUP:
  2034. case WM_MOUSEACTIVATE:
  2035. case WM_NCMOUSEHOVER:
  2036. case WM_MOUSEHOVER:
  2037. case WM_TOUCH:
  2038. case WM_POINTERUPDATE:
  2039. case WM_NCPOINTERUPDATE:
  2040. case WM_POINTERWHEEL:
  2041. case WM_POINTERHWHEEL:
  2042. case WM_POINTERUP:
  2043. case WM_POINTERACTIVATE:
  2044. return isHWNDBlockedByModalComponents(m.hwnd);
  2045. case WM_NCLBUTTONDOWN:
  2046. case WM_NCLBUTTONDBLCLK:
  2047. case WM_NCRBUTTONDOWN:
  2048. case WM_NCRBUTTONDBLCLK:
  2049. case WM_NCMBUTTONDOWN:
  2050. case WM_NCMBUTTONDBLCLK:
  2051. case WM_LBUTTONDOWN:
  2052. case WM_LBUTTONDBLCLK:
  2053. case WM_MBUTTONDOWN:
  2054. case WM_MBUTTONDBLCLK:
  2055. case WM_RBUTTONDOWN:
  2056. case WM_RBUTTONDBLCLK:
  2057. case WM_KEYDOWN:
  2058. case WM_SYSKEYDOWN:
  2059. case WM_NCPOINTERDOWN:
  2060. case WM_POINTERDOWN:
  2061. if (isHWNDBlockedByModalComponents (m.hwnd))
  2062. {
  2063. if (auto* modal = Component::getCurrentlyModalComponent (0))
  2064. modal->inputAttemptWhenModal();
  2065. return true;
  2066. }
  2067. break;
  2068. default:
  2069. break;
  2070. }
  2071. return false;
  2072. }
  2073. JUCE_DECLARE_NON_COPYABLE (WindowClassHolder)
  2074. };
  2075. //==============================================================================
  2076. static void* createWindowCallback (void* userData)
  2077. {
  2078. static_cast<HWNDComponentPeer*> (userData)->createWindow();
  2079. return nullptr;
  2080. }
  2081. void createWindow()
  2082. {
  2083. DWORD exstyle = 0;
  2084. DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  2085. if (hasTitleBar())
  2086. {
  2087. type |= WS_OVERLAPPED;
  2088. if ((styleFlags & windowHasCloseButton) != 0)
  2089. {
  2090. type |= WS_SYSMENU;
  2091. }
  2092. else
  2093. {
  2094. // annoyingly, windows won't let you have a min/max button without a close button
  2095. jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
  2096. }
  2097. if ((styleFlags & windowIsResizable) != 0)
  2098. type |= WS_THICKFRAME;
  2099. }
  2100. else if (parentToAddTo != nullptr)
  2101. {
  2102. type |= WS_CHILD;
  2103. }
  2104. else
  2105. {
  2106. type |= WS_POPUP | WS_SYSMENU;
  2107. }
  2108. if ((styleFlags & windowAppearsOnTaskbar) == 0)
  2109. exstyle |= WS_EX_TOOLWINDOW;
  2110. else
  2111. exstyle |= WS_EX_APPWINDOW;
  2112. if ((styleFlags & windowHasMinimiseButton) != 0) type |= WS_MINIMIZEBOX;
  2113. if ((styleFlags & windowHasMaximiseButton) != 0) type |= WS_MAXIMIZEBOX;
  2114. if ((styleFlags & windowIgnoresMouseClicks) != 0) exstyle |= WS_EX_TRANSPARENT;
  2115. if ((styleFlags & windowIsSemiTransparent) != 0) exstyle |= WS_EX_LAYERED;
  2116. hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
  2117. L"", type, 0, 0, 0, 0, parentToAddTo, nullptr,
  2118. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), nullptr);
  2119. #if JUCE_DEBUG
  2120. // The DPI-awareness context of this window and JUCE's hidden message window are different.
  2121. // You normally want these to match otherwise timer events and async messages will happen
  2122. // in a different context to normal HWND messages which can cause issues with UI scaling.
  2123. jassert (isPerMonitorDPIAwareWindow (hwnd) == isPerMonitorDPIAwareWindow (juce_messageWindowHandle)
  2124. || isInScopedDPIAwarenessDisabler());
  2125. #endif
  2126. if (hwnd != nullptr)
  2127. {
  2128. SetWindowLongPtr (hwnd, 0, 0);
  2129. SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
  2130. JuceWindowIdentifier::setAsJUCEWindow (hwnd, true);
  2131. if (dropTarget == nullptr)
  2132. {
  2133. HWNDComponentPeer* peer = nullptr;
  2134. if (dontRepaint)
  2135. peer = getOwnerOfWindow (parentToAddTo);
  2136. if (peer == nullptr)
  2137. peer = this;
  2138. dropTarget = new FileDropTarget (*peer);
  2139. }
  2140. RegisterDragDrop (hwnd, dropTarget);
  2141. if (canUseMultiTouch())
  2142. registerTouchWindow (hwnd, 0);
  2143. setDPIAwareness();
  2144. if (isPerMonitorDPIAwareThread())
  2145. scaleFactor = getScaleFactorForWindow (hwnd);
  2146. setMessageFilter();
  2147. updateBorderSize();
  2148. checkForPointerAPI();
  2149. // This is needed so that our plugin window gets notified of WM_SETTINGCHANGE messages
  2150. // and can respond to display scale changes
  2151. if (! JUCEApplication::isStandaloneApp())
  2152. settingChangeCallback = ComponentPeer::forceDisplayUpdate;
  2153. // Calling this function here is (for some reason) necessary to make Windows
  2154. // correctly enable the menu items that we specify in the wm_initmenu message.
  2155. GetSystemMenu (hwnd, false);
  2156. auto alpha = component.getAlpha();
  2157. if (alpha < 1.0f)
  2158. setAlpha (alpha);
  2159. }
  2160. else
  2161. {
  2162. TCHAR messageBuffer[256] = {};
  2163. FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  2164. nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  2165. messageBuffer, (DWORD) numElementsInArray (messageBuffer) - 1, nullptr);
  2166. DBG (messageBuffer);
  2167. jassertfalse;
  2168. }
  2169. }
  2170. static BOOL CALLBACK revokeChildDragDropCallback (HWND hwnd, LPARAM) { RevokeDragDrop (hwnd); return TRUE; }
  2171. static void* destroyWindowCallback (void* handle)
  2172. {
  2173. auto hwnd = reinterpret_cast<HWND> (handle);
  2174. if (IsWindow (hwnd))
  2175. {
  2176. RevokeDragDrop (hwnd);
  2177. // NB: we need to do this before DestroyWindow() as child HWNDs will be invalid after
  2178. EnumChildWindows (hwnd, revokeChildDragDropCallback, 0);
  2179. DestroyWindow (hwnd);
  2180. }
  2181. return nullptr;
  2182. }
  2183. static void* toFrontCallback1 (void* h)
  2184. {
  2185. BringWindowToTop ((HWND) h);
  2186. return nullptr;
  2187. }
  2188. static void* toFrontCallback2 (void* h)
  2189. {
  2190. setWindowZOrder ((HWND) h, HWND_TOP);
  2191. return nullptr;
  2192. }
  2193. static void* setFocusCallback (void* h)
  2194. {
  2195. SetFocus ((HWND) h);
  2196. return nullptr;
  2197. }
  2198. static void* getFocusCallback (void*)
  2199. {
  2200. return GetFocus();
  2201. }
  2202. bool isUsingUpdateLayeredWindow() const
  2203. {
  2204. return ! component.isOpaque();
  2205. }
  2206. bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; }
  2207. void updateShadower()
  2208. {
  2209. if (! component.isCurrentlyModal() && (styleFlags & windowHasDropShadow) != 0
  2210. && ((! hasTitleBar()) || SystemStats::getOperatingSystemType() < SystemStats::WinVista))
  2211. {
  2212. shadower = component.getLookAndFeel().createDropShadowerForComponent (component);
  2213. if (shadower != nullptr)
  2214. shadower->setOwner (&component);
  2215. }
  2216. }
  2217. void setIcon (const Image& newIcon) override
  2218. {
  2219. if (auto hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0))
  2220. {
  2221. SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
  2222. SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
  2223. if (currentWindowIcon != nullptr)
  2224. DestroyIcon (currentWindowIcon);
  2225. currentWindowIcon = hicon;
  2226. }
  2227. }
  2228. void setMessageFilter()
  2229. {
  2230. using ChangeWindowMessageFilterExFunc = BOOL (WINAPI*) (HWND, UINT, DWORD, PVOID);
  2231. if (auto changeMessageFilter = (ChangeWindowMessageFilterExFunc) getUser32Function ("ChangeWindowMessageFilterEx"))
  2232. {
  2233. changeMessageFilter (hwnd, WM_DROPFILES, 1 /*MSGFLT_ALLOW*/, nullptr);
  2234. changeMessageFilter (hwnd, WM_COPYDATA, 1 /*MSGFLT_ALLOW*/, nullptr);
  2235. changeMessageFilter (hwnd, 0x49, 1 /*MSGFLT_ALLOW*/, nullptr);
  2236. }
  2237. }
  2238. struct ChildWindowClippingInfo
  2239. {
  2240. HDC dc;
  2241. HWNDComponentPeer* peer;
  2242. RectangleList<int>* clip;
  2243. Point<int> origin;
  2244. int savedDC;
  2245. };
  2246. static BOOL CALLBACK clipChildWindowCallback (HWND hwnd, LPARAM context)
  2247. {
  2248. if (IsWindowVisible (hwnd))
  2249. {
  2250. auto& info = *(ChildWindowClippingInfo*) context;
  2251. if (GetParent (hwnd) == info.peer->hwnd)
  2252. {
  2253. auto clip = rectangleFromRECT (getWindowClientRect (hwnd));
  2254. info.clip->subtract (clip - info.origin);
  2255. if (info.savedDC == 0)
  2256. info.savedDC = SaveDC (info.dc);
  2257. ExcludeClipRect (info.dc, clip.getX(), clip.getY(), clip.getRight(), clip.getBottom());
  2258. }
  2259. }
  2260. return TRUE;
  2261. }
  2262. //==============================================================================
  2263. void handlePaintMessage()
  2264. {
  2265. #if JUCE_DIRECT2D
  2266. if (direct2DContext != nullptr)
  2267. {
  2268. RECT r;
  2269. if (GetUpdateRect (hwnd, &r, false))
  2270. {
  2271. direct2DContext->start();
  2272. direct2DContext->clipToRectangle (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r), hwnd));
  2273. handlePaint (*direct2DContext);
  2274. direct2DContext->end();
  2275. ValidateRect (hwnd, &r);
  2276. }
  2277. }
  2278. else
  2279. #endif
  2280. {
  2281. HRGN rgn = CreateRectRgn (0, 0, 0, 0);
  2282. const int regionType = GetUpdateRgn (hwnd, rgn, false);
  2283. PAINTSTRUCT paintStruct;
  2284. HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
  2285. // message and become re-entrant, but that's OK
  2286. // if something in a paint handler calls, e.g. a message box, this can become reentrant and
  2287. // corrupt the image it's using to paint into, so do a check here.
  2288. static bool reentrant = false;
  2289. if (! reentrant)
  2290. {
  2291. const ScopedValueSetter<bool> setter (reentrant, true, false);
  2292. if (dontRepaint)
  2293. component.handleCommandMessage (0); // (this triggers a repaint in the openGL context)
  2294. else
  2295. performPaint (dc, rgn, regionType, paintStruct);
  2296. }
  2297. DeleteObject (rgn);
  2298. EndPaint (hwnd, &paintStruct);
  2299. #if JUCE_MSVC
  2300. _fpreset(); // because some graphics cards can unmask FP exceptions
  2301. #endif
  2302. }
  2303. lastPaintTime = Time::getMillisecondCounter();
  2304. }
  2305. void performPaint (HDC dc, HRGN rgn, int regionType, PAINTSTRUCT& paintStruct)
  2306. {
  2307. int x = paintStruct.rcPaint.left;
  2308. int y = paintStruct.rcPaint.top;
  2309. int w = paintStruct.rcPaint.right - x;
  2310. int h = paintStruct.rcPaint.bottom - y;
  2311. const bool transparent = isUsingUpdateLayeredWindow();
  2312. if (transparent)
  2313. {
  2314. // it's not possible to have a transparent window with a title bar at the moment!
  2315. jassert (! hasTitleBar());
  2316. auto r = getWindowScreenRect (hwnd);
  2317. x = y = 0;
  2318. w = r.right - r.left;
  2319. h = r.bottom - r.top;
  2320. }
  2321. if (w > 0 && h > 0)
  2322. {
  2323. Image& offscreenImage = offscreenImageGenerator.getImage (transparent, w, h);
  2324. RectangleList<int> contextClip;
  2325. const Rectangle<int> clipBounds (w, h);
  2326. bool needToPaintAll = true;
  2327. if (regionType == COMPLEXREGION && ! transparent)
  2328. {
  2329. HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
  2330. CombineRgn (rgn, rgn, clipRgn, RGN_AND);
  2331. DeleteObject (clipRgn);
  2332. std::aligned_storage<8192, alignof (RGNDATA)>::type rgnData;
  2333. const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) &rgnData);
  2334. if (res > 0 && res <= sizeof (rgnData))
  2335. {
  2336. const RGNDATAHEADER* const hdr = &(((const RGNDATA*) &rgnData)->rdh);
  2337. if (hdr->iType == RDH_RECTANGLES
  2338. && hdr->rcBound.right - hdr->rcBound.left >= w
  2339. && hdr->rcBound.bottom - hdr->rcBound.top >= h)
  2340. {
  2341. needToPaintAll = false;
  2342. auto rects = unalignedPointerCast<const RECT*> ((char*) &rgnData + sizeof (RGNDATAHEADER));
  2343. for (int i = (int) ((RGNDATA*) &rgnData)->rdh.nCount; --i >= 0;)
  2344. {
  2345. if (rects->right <= x + w && rects->bottom <= y + h)
  2346. {
  2347. const int cx = jmax (x, (int) rects->left);
  2348. contextClip.addWithoutMerging (Rectangle<int> (cx - x, rects->top - y,
  2349. rects->right - cx, rects->bottom - rects->top)
  2350. .getIntersection (clipBounds));
  2351. }
  2352. else
  2353. {
  2354. needToPaintAll = true;
  2355. break;
  2356. }
  2357. ++rects;
  2358. }
  2359. }
  2360. }
  2361. }
  2362. if (needToPaintAll)
  2363. {
  2364. contextClip.clear();
  2365. contextClip.addWithoutMerging (Rectangle<int> (w, h));
  2366. }
  2367. ChildWindowClippingInfo childClipInfo = { dc, this, &contextClip, Point<int> (x, y), 0 };
  2368. EnumChildWindows (hwnd, clipChildWindowCallback, (LPARAM) &childClipInfo);
  2369. if (! contextClip.isEmpty())
  2370. {
  2371. if (transparent)
  2372. for (auto& i : contextClip)
  2373. offscreenImage.clear (i);
  2374. {
  2375. auto context = component.getLookAndFeel()
  2376. .createGraphicsContext (offscreenImage, { -x, -y }, contextClip);
  2377. context->addTransform (AffineTransform::scale ((float) getPlatformScaleFactor()));
  2378. handlePaint (*context);
  2379. }
  2380. static_cast<WindowsBitmapImage*> (offscreenImage.getPixelData())
  2381. ->blitToWindow (hwnd, dc, transparent, x, y, updateLayeredWindowAlpha);
  2382. }
  2383. if (childClipInfo.savedDC != 0)
  2384. RestoreDC (dc, childClipInfo.savedDC);
  2385. }
  2386. }
  2387. //==============================================================================
  2388. void doMouseEvent (Point<float> position, float pressure, float orientation = 0.0f, ModifierKeys mods = ModifierKeys::currentModifiers)
  2389. {
  2390. handleMouseEvent (MouseInputSource::InputSourceType::mouse, position, mods, pressure, orientation, getMouseEventTime());
  2391. }
  2392. StringArray getAvailableRenderingEngines() override
  2393. {
  2394. StringArray s ("Software Renderer");
  2395. #if JUCE_DIRECT2D
  2396. if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
  2397. s.add ("Direct2D");
  2398. #endif
  2399. return s;
  2400. }
  2401. int getCurrentRenderingEngine() const override { return currentRenderingEngine; }
  2402. #if JUCE_DIRECT2D
  2403. void updateDirect2DContext()
  2404. {
  2405. if (currentRenderingEngine != direct2DRenderingEngine)
  2406. direct2DContext = nullptr;
  2407. else if (direct2DContext == nullptr)
  2408. direct2DContext.reset (new Direct2DLowLevelGraphicsContext (hwnd));
  2409. }
  2410. #endif
  2411. void setCurrentRenderingEngine (int index) override
  2412. {
  2413. ignoreUnused (index);
  2414. #if JUCE_DIRECT2D
  2415. if (getAvailableRenderingEngines().size() > 1)
  2416. {
  2417. currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
  2418. updateDirect2DContext();
  2419. repaint (component.getLocalBounds());
  2420. }
  2421. #endif
  2422. }
  2423. static uint32 getMinTimeBetweenMouseMoves()
  2424. {
  2425. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  2426. return 0;
  2427. return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP..
  2428. }
  2429. bool isTouchEvent() noexcept
  2430. {
  2431. if (registerTouchWindow == nullptr)
  2432. return false;
  2433. // Relevant info about touch/pen detection flags:
  2434. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
  2435. // http://www.petertissen.de/?p=4
  2436. return ((uint32_t) GetMessageExtraInfo() & 0xFFFFFF80 /*SIGNATURE_MASK*/) == 0xFF515780 /*MI_WP_SIGNATURE*/;
  2437. }
  2438. static bool areOtherTouchSourcesActive()
  2439. {
  2440. for (auto& ms : Desktop::getInstance().getMouseSources())
  2441. if (ms.isDragging() && (ms.getType() == MouseInputSource::InputSourceType::touch
  2442. || ms.getType() == MouseInputSource::InputSourceType::pen))
  2443. return true;
  2444. return false;
  2445. }
  2446. void doMouseMove (Point<float> position, bool isMouseDownEvent)
  2447. {
  2448. ModifierKeys modsToSend (ModifierKeys::currentModifiers);
  2449. // this will be handled by WM_TOUCH
  2450. if (isTouchEvent() || areOtherTouchSourcesActive())
  2451. return;
  2452. if (! isMouseOver)
  2453. {
  2454. isMouseOver = true;
  2455. // This avoids a rare stuck-button problem when focus is lost unexpectedly, but must
  2456. // not be called as part of a move, in case it's actually a mouse-drag from another
  2457. // app which ends up here when we get focus before the mouse is released..
  2458. if (isMouseDownEvent && getNativeRealtimeModifiers != nullptr)
  2459. getNativeRealtimeModifiers();
  2460. updateKeyModifiers();
  2461. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2462. if (modProvider != nullptr)
  2463. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2464. #endif
  2465. TRACKMOUSEEVENT tme;
  2466. tme.cbSize = sizeof (tme);
  2467. tme.dwFlags = TME_LEAVE;
  2468. tme.hwndTrack = hwnd;
  2469. tme.dwHoverTime = 0;
  2470. if (! TrackMouseEvent (&tme))
  2471. jassertfalse;
  2472. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  2473. }
  2474. else if (! isDragging)
  2475. {
  2476. if (! contains (position.roundToInt(), false))
  2477. return;
  2478. }
  2479. static uint32 lastMouseTime = 0;
  2480. static auto minTimeBetweenMouses = getMinTimeBetweenMouseMoves();
  2481. auto now = Time::getMillisecondCounter();
  2482. if (! Desktop::getInstance().getMainMouseSource().isDragging())
  2483. modsToSend = modsToSend.withoutMouseButtons();
  2484. if (now >= lastMouseTime + minTimeBetweenMouses)
  2485. {
  2486. lastMouseTime = now;
  2487. doMouseEvent (position, MouseInputSource::defaultPressure,
  2488. MouseInputSource::defaultOrientation, modsToSend);
  2489. }
  2490. }
  2491. void doMouseDown (Point<float> position, const WPARAM wParam)
  2492. {
  2493. // this will be handled by WM_TOUCH
  2494. if (isTouchEvent() || areOtherTouchSourcesActive())
  2495. return;
  2496. if (GetCapture() != hwnd)
  2497. SetCapture (hwnd);
  2498. doMouseMove (position, true);
  2499. if (isValidPeer (this))
  2500. {
  2501. updateModifiersFromWParam (wParam);
  2502. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2503. if (modProvider != nullptr)
  2504. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2505. #endif
  2506. isDragging = true;
  2507. doMouseEvent (position, MouseInputSource::defaultPressure);
  2508. }
  2509. }
  2510. void doMouseUp (Point<float> position, const WPARAM wParam)
  2511. {
  2512. // this will be handled by WM_TOUCH
  2513. if (isTouchEvent() || areOtherTouchSourcesActive())
  2514. return;
  2515. updateModifiersFromWParam (wParam);
  2516. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2517. if (modProvider != nullptr)
  2518. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (modProvider->getWin32Modifiers());
  2519. #endif
  2520. const bool wasDragging = isDragging;
  2521. isDragging = false;
  2522. // release the mouse capture if the user has released all buttons
  2523. if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
  2524. ReleaseCapture();
  2525. // NB: under some circumstances (e.g. double-clicking a native title bar), a mouse-up can
  2526. // arrive without a mouse-down, so in that case we need to avoid sending a message.
  2527. if (wasDragging)
  2528. doMouseEvent (position, MouseInputSource::defaultPressure);
  2529. }
  2530. void doCaptureChanged()
  2531. {
  2532. if (constrainerIsResizing)
  2533. {
  2534. if (constrainer != nullptr)
  2535. constrainer->resizeEnd();
  2536. constrainerIsResizing = false;
  2537. }
  2538. if (isDragging)
  2539. doMouseUp (getCurrentMousePos(), (WPARAM) 0);
  2540. }
  2541. void doMouseExit()
  2542. {
  2543. isMouseOver = false;
  2544. if (! areOtherTouchSourcesActive())
  2545. doMouseEvent (getCurrentMousePos(), MouseInputSource::defaultPressure);
  2546. }
  2547. ComponentPeer* findPeerUnderMouse (Point<float>& localPos)
  2548. {
  2549. auto currentMousePos = getPOINTFromLParam ((LPARAM) GetMessagePos());
  2550. // Because Windows stupidly sends all wheel events to the window with the keyboard
  2551. // focus, we have to redirect them here according to the mouse pos..
  2552. auto* peer = getOwnerOfWindow (WindowFromPoint (currentMousePos));
  2553. if (peer == nullptr)
  2554. peer = this;
  2555. localPos = peer->globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (currentMousePos), hwnd).toFloat());
  2556. return peer;
  2557. }
  2558. static MouseInputSource::InputSourceType getPointerType (WPARAM wParam)
  2559. {
  2560. if (getPointerTypeFunction != nullptr)
  2561. {
  2562. POINTER_INPUT_TYPE pointerType;
  2563. if (getPointerTypeFunction (GET_POINTERID_WPARAM (wParam), &pointerType))
  2564. {
  2565. if (pointerType == 2)
  2566. return MouseInputSource::InputSourceType::touch;
  2567. if (pointerType == 3)
  2568. return MouseInputSource::InputSourceType::pen;
  2569. }
  2570. }
  2571. return MouseInputSource::InputSourceType::mouse;
  2572. }
  2573. void doMouseWheel (const WPARAM wParam, const bool isVertical)
  2574. {
  2575. updateKeyModifiers();
  2576. const float amount = jlimit (-1000.0f, 1000.0f, 0.5f * (short) HIWORD (wParam));
  2577. MouseWheelDetails wheel;
  2578. wheel.deltaX = isVertical ? 0.0f : amount / -256.0f;
  2579. wheel.deltaY = isVertical ? amount / 256.0f : 0.0f;
  2580. wheel.isReversed = false;
  2581. wheel.isSmooth = false;
  2582. wheel.isInertial = false;
  2583. Point<float> localPos;
  2584. if (auto* peer = findPeerUnderMouse (localPos))
  2585. peer->handleMouseWheel (getPointerType (wParam), localPos, getMouseEventTime(), wheel);
  2586. }
  2587. bool doGestureEvent (LPARAM lParam)
  2588. {
  2589. GESTUREINFO gi;
  2590. zerostruct (gi);
  2591. gi.cbSize = sizeof (gi);
  2592. if (getGestureInfo != nullptr && getGestureInfo ((HGESTUREINFO) lParam, &gi))
  2593. {
  2594. updateKeyModifiers();
  2595. Point<float> localPos;
  2596. if (auto* peer = findPeerUnderMouse (localPos))
  2597. {
  2598. switch (gi.dwID)
  2599. {
  2600. case 3: /*GID_ZOOM*/
  2601. if (gi.dwFlags != 1 /*GF_BEGIN*/ && lastMagnifySize > 0)
  2602. peer->handleMagnifyGesture (MouseInputSource::InputSourceType::touch, localPos, getMouseEventTime(),
  2603. (float) ((double) gi.ullArguments / (double) lastMagnifySize));
  2604. lastMagnifySize = gi.ullArguments;
  2605. return true;
  2606. case 4: /*GID_PAN*/
  2607. case 5: /*GID_ROTATE*/
  2608. case 6: /*GID_TWOFINGERTAP*/
  2609. case 7: /*GID_PRESSANDTAP*/
  2610. default:
  2611. break;
  2612. }
  2613. }
  2614. }
  2615. return false;
  2616. }
  2617. LRESULT doTouchEvent (const int numInputs, HTOUCHINPUT eventHandle)
  2618. {
  2619. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  2620. if (auto* parent = getOwnerOfWindow (GetParent (hwnd)))
  2621. if (parent != this)
  2622. return parent->doTouchEvent (numInputs, eventHandle);
  2623. HeapBlock<TOUCHINPUT> inputInfo (numInputs);
  2624. if (getTouchInputInfo (eventHandle, (UINT) numInputs, inputInfo, sizeof (TOUCHINPUT)))
  2625. {
  2626. for (int i = 0; i < numInputs; ++i)
  2627. {
  2628. auto flags = inputInfo[i].dwFlags;
  2629. if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0)
  2630. if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0))
  2631. return 0; // abandon method if this window was deleted by the callback
  2632. }
  2633. }
  2634. closeTouchInputHandle (eventHandle);
  2635. return 0;
  2636. }
  2637. bool handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp,
  2638. const float touchPressure = MouseInputSource::defaultPressure,
  2639. const float orientation = 0.0f)
  2640. {
  2641. auto isCancel = false;
  2642. const auto touchIndex = currentTouches.getIndexOfTouch (this, touch.dwID);
  2643. const auto time = getMouseEventTime();
  2644. const auto pos = globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT ({ roundToInt (touch.x / 100.0f),
  2645. roundToInt (touch.y / 100.0f) }), hwnd).toFloat());
  2646. const auto pressure = touchPressure;
  2647. auto modsToSend = ModifierKeys::currentModifiers;
  2648. if (isDown)
  2649. {
  2650. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2651. modsToSend = ModifierKeys::currentModifiers;
  2652. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  2653. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend.withoutMouseButtons(),
  2654. pressure, orientation, time, {}, touchIndex);
  2655. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2656. return false;
  2657. }
  2658. else if (isUp)
  2659. {
  2660. modsToSend = modsToSend.withoutMouseButtons();
  2661. ModifierKeys::currentModifiers = modsToSend;
  2662. currentTouches.clearTouch (touchIndex);
  2663. if (! currentTouches.areAnyTouchesActive())
  2664. isCancel = true;
  2665. }
  2666. else
  2667. {
  2668. modsToSend = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2669. }
  2670. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend,
  2671. pressure, orientation, time, {}, touchIndex);
  2672. if (! isValidPeer (this))
  2673. return false;
  2674. if (isUp)
  2675. {
  2676. handleMouseEvent (MouseInputSource::InputSourceType::touch, MouseInputSource::offscreenMousePos, ModifierKeys::currentModifiers.withoutMouseButtons(),
  2677. pressure, orientation, time, {}, touchIndex);
  2678. if (! isValidPeer (this))
  2679. return false;
  2680. if (isCancel)
  2681. {
  2682. currentTouches.clear();
  2683. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  2684. }
  2685. }
  2686. return true;
  2687. }
  2688. bool handlePointerInput (WPARAM wParam, LPARAM lParam, const bool isDown, const bool isUp)
  2689. {
  2690. if (! canUsePointerAPI)
  2691. return false;
  2692. auto pointerType = getPointerType (wParam);
  2693. if (pointerType == MouseInputSource::InputSourceType::touch)
  2694. {
  2695. POINTER_TOUCH_INFO touchInfo;
  2696. if (! getPointerTouchInfo (GET_POINTERID_WPARAM (wParam), &touchInfo))
  2697. return false;
  2698. const auto pressure = touchInfo.touchMask & TOUCH_MASK_PRESSURE ? static_cast<float> (touchInfo.pressure)
  2699. : MouseInputSource::defaultPressure;
  2700. const auto orientation = touchInfo.touchMask & TOUCH_MASK_ORIENTATION ? degreesToRadians (static_cast<float> (touchInfo.orientation))
  2701. : MouseInputSource::defaultOrientation;
  2702. if (! handleTouchInput (emulateTouchEventFromPointer (touchInfo.pointerInfo.ptPixelLocationRaw, wParam),
  2703. isDown, isUp, pressure, orientation))
  2704. return false;
  2705. }
  2706. else if (pointerType == MouseInputSource::InputSourceType::pen)
  2707. {
  2708. POINTER_PEN_INFO penInfo;
  2709. if (! getPointerPenInfo (GET_POINTERID_WPARAM (wParam), &penInfo))
  2710. return false;
  2711. const auto pressure = (penInfo.penMask & PEN_MASK_PRESSURE) ? (float) penInfo.pressure / 1024.0f : MouseInputSource::defaultPressure;
  2712. if (! handlePenInput (penInfo, globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam (lParam)), hwnd).toFloat()),
  2713. pressure, isDown, isUp))
  2714. return false;
  2715. }
  2716. else
  2717. {
  2718. return false;
  2719. }
  2720. return true;
  2721. }
  2722. TOUCHINPUT emulateTouchEventFromPointer (POINT p, WPARAM wParam)
  2723. {
  2724. TOUCHINPUT touchInput;
  2725. touchInput.dwID = GET_POINTERID_WPARAM (wParam);
  2726. touchInput.x = p.x * 100;
  2727. touchInput.y = p.y * 100;
  2728. return touchInput;
  2729. }
  2730. bool handlePenInput (POINTER_PEN_INFO penInfo, Point<float> pos, const float pressure, bool isDown, bool isUp)
  2731. {
  2732. const auto time = getMouseEventTime();
  2733. ModifierKeys modsToSend (ModifierKeys::currentModifiers);
  2734. PenDetails penDetails;
  2735. penDetails.rotation = (penInfo.penMask & PEN_MASK_ROTATION) ? degreesToRadians (static_cast<float> (penInfo.rotation)) : MouseInputSource::defaultRotation;
  2736. penDetails.tiltX = (penInfo.penMask & PEN_MASK_TILT_X) ? (float) penInfo.tiltX / 90.0f : MouseInputSource::defaultTiltX;
  2737. penDetails.tiltY = (penInfo.penMask & PEN_MASK_TILT_Y) ? (float) penInfo.tiltY / 90.0f : MouseInputSource::defaultTiltY;
  2738. auto pInfoFlags = penInfo.pointerInfo.pointerFlags;
  2739. if ((pInfoFlags & POINTER_FLAG_FIRSTBUTTON) != 0)
  2740. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2741. else if ((pInfoFlags & POINTER_FLAG_SECONDBUTTON) != 0)
  2742. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::rightButtonModifier);
  2743. if (isDown)
  2744. {
  2745. modsToSend = ModifierKeys::currentModifiers;
  2746. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  2747. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend.withoutMouseButtons(),
  2748. pressure, MouseInputSource::defaultOrientation, time, penDetails);
  2749. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2750. return false;
  2751. }
  2752. else if (isUp || ! (pInfoFlags & POINTER_FLAG_INCONTACT))
  2753. {
  2754. modsToSend = modsToSend.withoutMouseButtons();
  2755. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  2756. }
  2757. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend, pressure,
  2758. MouseInputSource::defaultOrientation, time, penDetails);
  2759. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2760. return false;
  2761. if (isUp)
  2762. {
  2763. handleMouseEvent (MouseInputSource::InputSourceType::pen, MouseInputSource::offscreenMousePos, ModifierKeys::currentModifiers,
  2764. pressure, MouseInputSource::defaultOrientation, time, penDetails);
  2765. if (! isValidPeer (this))
  2766. return false;
  2767. }
  2768. return true;
  2769. }
  2770. //==============================================================================
  2771. void sendModifierKeyChangeIfNeeded()
  2772. {
  2773. if (modifiersAtLastCallback != ModifierKeys::currentModifiers)
  2774. {
  2775. modifiersAtLastCallback = ModifierKeys::currentModifiers;
  2776. handleModifierKeysChange();
  2777. }
  2778. }
  2779. bool doKeyUp (const WPARAM key)
  2780. {
  2781. updateKeyModifiers();
  2782. switch (key)
  2783. {
  2784. case VK_SHIFT:
  2785. case VK_CONTROL:
  2786. case VK_MENU:
  2787. case VK_CAPITAL:
  2788. case VK_LWIN:
  2789. case VK_RWIN:
  2790. case VK_APPS:
  2791. case VK_NUMLOCK:
  2792. case VK_SCROLL:
  2793. case VK_LSHIFT:
  2794. case VK_RSHIFT:
  2795. case VK_LCONTROL:
  2796. case VK_LMENU:
  2797. case VK_RCONTROL:
  2798. case VK_RMENU:
  2799. sendModifierKeyChangeIfNeeded();
  2800. }
  2801. return handleKeyUpOrDown (false)
  2802. || Component::getCurrentlyModalComponent() != nullptr;
  2803. }
  2804. bool doKeyDown (const WPARAM key)
  2805. {
  2806. updateKeyModifiers();
  2807. bool used = false;
  2808. switch (key)
  2809. {
  2810. case VK_SHIFT:
  2811. case VK_LSHIFT:
  2812. case VK_RSHIFT:
  2813. case VK_CONTROL:
  2814. case VK_LCONTROL:
  2815. case VK_RCONTROL:
  2816. case VK_MENU:
  2817. case VK_LMENU:
  2818. case VK_RMENU:
  2819. case VK_LWIN:
  2820. case VK_RWIN:
  2821. case VK_CAPITAL:
  2822. case VK_NUMLOCK:
  2823. case VK_SCROLL:
  2824. case VK_APPS:
  2825. used = handleKeyUpOrDown (true);
  2826. sendModifierKeyChangeIfNeeded();
  2827. break;
  2828. case VK_LEFT:
  2829. case VK_RIGHT:
  2830. case VK_UP:
  2831. case VK_DOWN:
  2832. case VK_PRIOR:
  2833. case VK_NEXT:
  2834. case VK_HOME:
  2835. case VK_END:
  2836. case VK_DELETE:
  2837. case VK_INSERT:
  2838. case VK_F1:
  2839. case VK_F2:
  2840. case VK_F3:
  2841. case VK_F4:
  2842. case VK_F5:
  2843. case VK_F6:
  2844. case VK_F7:
  2845. case VK_F8:
  2846. case VK_F9:
  2847. case VK_F10:
  2848. case VK_F11:
  2849. case VK_F12:
  2850. case VK_F13:
  2851. case VK_F14:
  2852. case VK_F15:
  2853. case VK_F16:
  2854. case VK_F17:
  2855. case VK_F18:
  2856. case VK_F19:
  2857. case VK_F20:
  2858. case VK_F21:
  2859. case VK_F22:
  2860. case VK_F23:
  2861. case VK_F24:
  2862. used = handleKeyUpOrDown (true);
  2863. used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
  2864. break;
  2865. default:
  2866. used = handleKeyUpOrDown (true);
  2867. {
  2868. MSG msg;
  2869. if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
  2870. {
  2871. // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
  2872. // manually generate the key-press event that matches this key-down.
  2873. const UINT keyChar = MapVirtualKey ((UINT) key, 2);
  2874. const UINT scanCode = MapVirtualKey ((UINT) key, 0);
  2875. BYTE keyState[256];
  2876. ignoreUnused (GetKeyboardState (keyState));
  2877. WCHAR text[16] = { 0 };
  2878. if (ToUnicode ((UINT) key, scanCode, keyState, text, 8, 0) != 1)
  2879. text[0] = 0;
  2880. used = handleKeyPress ((int) LOWORD (keyChar), (juce_wchar) text[0]) || used;
  2881. }
  2882. }
  2883. break;
  2884. }
  2885. return used || (Component::getCurrentlyModalComponent() != nullptr);
  2886. }
  2887. bool doKeyChar (int key, const LPARAM flags)
  2888. {
  2889. updateKeyModifiers();
  2890. auto textChar = (juce_wchar) key;
  2891. const int virtualScanCode = (flags >> 16) & 0xff;
  2892. if (key >= '0' && key <= '9')
  2893. {
  2894. switch (virtualScanCode) // check for a numeric keypad scan-code
  2895. {
  2896. case 0x52:
  2897. case 0x4f:
  2898. case 0x50:
  2899. case 0x51:
  2900. case 0x4b:
  2901. case 0x4c:
  2902. case 0x4d:
  2903. case 0x47:
  2904. case 0x48:
  2905. case 0x49:
  2906. key = (key - '0') + KeyPress::numberPad0;
  2907. break;
  2908. default:
  2909. break;
  2910. }
  2911. }
  2912. else
  2913. {
  2914. // convert the scan code to an unmodified character code..
  2915. const UINT virtualKey = MapVirtualKey ((UINT) virtualScanCode, 1);
  2916. UINT keyChar = MapVirtualKey (virtualKey, 2);
  2917. keyChar = LOWORD (keyChar);
  2918. if (keyChar != 0)
  2919. key = (int) keyChar;
  2920. // avoid sending junk text characters for some control-key combinations
  2921. if (textChar < ' ' && ModifierKeys::currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
  2922. textChar = 0;
  2923. }
  2924. return handleKeyPress (key, textChar);
  2925. }
  2926. void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
  2927. {
  2928. if (HWND parentH = GetParent (hwnd))
  2929. PostMessage (parentH, message, wParam, lParam);
  2930. }
  2931. bool doAppCommand (const LPARAM lParam)
  2932. {
  2933. int key = 0;
  2934. switch (GET_APPCOMMAND_LPARAM (lParam))
  2935. {
  2936. case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
  2937. case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
  2938. case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
  2939. case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
  2940. default: break;
  2941. }
  2942. if (key != 0)
  2943. {
  2944. updateKeyModifiers();
  2945. if (hwnd == GetActiveWindow())
  2946. return handleKeyPress (key, 0);
  2947. }
  2948. return false;
  2949. }
  2950. bool isConstrainedNativeWindow() const
  2951. {
  2952. return constrainer != nullptr
  2953. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)
  2954. && ! isKioskMode();
  2955. }
  2956. Rectangle<int> getCurrentScaledBounds() const
  2957. {
  2958. return ScalingHelpers::unscaledScreenPosToScaled (component, windowBorder.addedTo (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBounds())));
  2959. }
  2960. LRESULT handleSizeConstraining (RECT& r, const WPARAM wParam)
  2961. {
  2962. if (isConstrainedNativeWindow())
  2963. {
  2964. const auto logicalBounds = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (r).toFloat(), hwnd);
  2965. auto pos = ScalingHelpers::unscaledScreenPosToScaled (component, logicalBounds).toNearestInt();
  2966. const auto original = getCurrentScaledBounds();
  2967. constrainer->checkBounds (pos, original,
  2968. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2969. wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
  2970. wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
  2971. wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
  2972. wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
  2973. r = RECTFromRectangle (convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component, pos.toFloat()).toNearestInt(), hwnd));
  2974. }
  2975. return TRUE;
  2976. }
  2977. LRESULT handlePositionChanging (WINDOWPOS& wp)
  2978. {
  2979. if (isConstrainedNativeWindow() && ! isFullScreen())
  2980. {
  2981. if ((wp.flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
  2982. && (wp.x > -32000 && wp.y > -32000)
  2983. && ! Component::isMouseButtonDownAnywhere())
  2984. {
  2985. const auto logicalBounds = convertPhysicalScreenRectangleToLogical (rectangleFromRECT ({ wp.x, wp.y, wp.x + wp.cx, wp.y + wp.cy }).toFloat(), hwnd);
  2986. auto pos = ScalingHelpers::unscaledScreenPosToScaled (component, logicalBounds).toNearestInt();
  2987. const auto original = getCurrentScaledBounds();
  2988. constrainer->checkBounds (pos, original,
  2989. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2990. pos.getY() != original.getY() && pos.getBottom() == original.getBottom(),
  2991. pos.getX() != original.getX() && pos.getRight() == original.getRight(),
  2992. pos.getY() == original.getY() && pos.getBottom() != original.getBottom(),
  2993. pos.getX() == original.getX() && pos.getRight() != original.getRight());
  2994. auto physicalBounds = convertLogicalScreenRectangleToPhysical (ScalingHelpers::scaledScreenPosToUnscaled (component, pos.toFloat()), hwnd);
  2995. auto getNewPositionIfNotRoundingError = [] (int posIn, float newPos)
  2996. {
  2997. return (std::abs ((float) posIn - newPos) >= 1.0f) ? roundToInt (newPos) : posIn;
  2998. };
  2999. wp.x = getNewPositionIfNotRoundingError (wp.x, physicalBounds.getX());
  3000. wp.y = getNewPositionIfNotRoundingError (wp.y, physicalBounds.getY());
  3001. wp.cx = getNewPositionIfNotRoundingError (wp.cx, physicalBounds.getWidth());
  3002. wp.cy = getNewPositionIfNotRoundingError (wp.cy, physicalBounds.getHeight());
  3003. }
  3004. }
  3005. if (((wp.flags & SWP_SHOWWINDOW) != 0 && ! component.isVisible()))
  3006. component.setVisible (true);
  3007. else if (((wp.flags & SWP_HIDEWINDOW) != 0 && component.isVisible()))
  3008. component.setVisible (false);
  3009. return 0;
  3010. }
  3011. bool updateCurrentMonitor()
  3012. {
  3013. auto monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONULL);
  3014. return std::exchange (currentMonitor, monitor) != monitor;
  3015. }
  3016. bool handlePositionChanged()
  3017. {
  3018. auto pos = getCurrentMousePos();
  3019. if (contains (pos.roundToInt(), false))
  3020. {
  3021. const ScopedValueSetter<bool> scope (inHandlePositionChanged, true);
  3022. if (! areOtherTouchSourcesActive())
  3023. doMouseEvent (pos, MouseInputSource::defaultPressure);
  3024. if (! isValidPeer (this))
  3025. return true;
  3026. }
  3027. handleMovedOrResized();
  3028. if (updateCurrentMonitor())
  3029. VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
  3030. return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly.
  3031. }
  3032. //==============================================================================
  3033. LRESULT handleDPIChanging (int newDPI, RECT newRect)
  3034. {
  3035. // Sometimes, windows that should not be automatically scaled (secondary windows in plugins)
  3036. // are sent WM_DPICHANGED. The size suggested by the OS is incorrect for our unscaled
  3037. // window, so we should ignore it.
  3038. if (! isPerMonitorDPIAwareWindow (hwnd))
  3039. return 0;
  3040. const auto newScale = (double) newDPI / USER_DEFAULT_SCREEN_DPI;
  3041. if (approximatelyEqual (scaleFactor, newScale))
  3042. return 0;
  3043. scaleFactor = newScale;
  3044. {
  3045. const ScopedValueSetter<bool> setter (inDpiChange, true);
  3046. SetWindowPos (hwnd,
  3047. nullptr,
  3048. newRect.left,
  3049. newRect.top,
  3050. newRect.right - newRect.left,
  3051. newRect.bottom - newRect.top,
  3052. SWP_NOZORDER | SWP_NOACTIVATE);
  3053. }
  3054. // This is to handle reentrancy. If responding to a DPI change triggers further DPI changes,
  3055. // we should only notify listeners and resize windows once all of the DPI changes have
  3056. // resolved.
  3057. if (inDpiChange)
  3058. {
  3059. // Danger! Re-entrant call to handleDPIChanging.
  3060. // Please report this issue on the JUCE forum, along with instructions
  3061. // so that a JUCE developer can reproduce the issue.
  3062. jassertfalse;
  3063. return 0;
  3064. }
  3065. updateShadower();
  3066. InvalidateRect (hwnd, nullptr, FALSE);
  3067. scaleFactorListeners.call ([this] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (scaleFactor); });
  3068. return 0;
  3069. }
  3070. //==============================================================================
  3071. void handleAppActivation (const WPARAM wParam)
  3072. {
  3073. modifiersAtLastCallback = -1;
  3074. updateKeyModifiers();
  3075. if (isMinimised())
  3076. {
  3077. component.repaint();
  3078. handleMovedOrResized();
  3079. if (! isValidPeer (this))
  3080. return;
  3081. }
  3082. auto* underMouse = component.getComponentAt (component.getMouseXYRelative());
  3083. if (underMouse == nullptr)
  3084. underMouse = &component;
  3085. if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
  3086. {
  3087. if (LOWORD (wParam) == WA_CLICKACTIVE)
  3088. Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
  3089. else
  3090. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  3091. }
  3092. else
  3093. {
  3094. handleBroughtToFront();
  3095. }
  3096. }
  3097. void handlePowerBroadcast (WPARAM wParam)
  3098. {
  3099. if (auto* app = JUCEApplicationBase::getInstance())
  3100. {
  3101. switch (wParam)
  3102. {
  3103. case PBT_APMSUSPEND: app->suspended(); break;
  3104. case PBT_APMQUERYSUSPENDFAILED:
  3105. case PBT_APMRESUMECRITICAL:
  3106. case PBT_APMRESUMESUSPEND:
  3107. case PBT_APMRESUMEAUTOMATIC: app->resumed(); break;
  3108. default: break;
  3109. }
  3110. }
  3111. }
  3112. void handleLeftClickInNCArea (WPARAM wParam)
  3113. {
  3114. if (! sendInputAttemptWhenModalMessage())
  3115. {
  3116. switch (wParam)
  3117. {
  3118. case HTBOTTOM:
  3119. case HTBOTTOMLEFT:
  3120. case HTBOTTOMRIGHT:
  3121. case HTGROWBOX:
  3122. case HTLEFT:
  3123. case HTRIGHT:
  3124. case HTTOP:
  3125. case HTTOPLEFT:
  3126. case HTTOPRIGHT:
  3127. if (isConstrainedNativeWindow())
  3128. {
  3129. constrainerIsResizing = true;
  3130. constrainer->resizeStart();
  3131. }
  3132. break;
  3133. default:
  3134. break;
  3135. }
  3136. }
  3137. }
  3138. void initialiseSysMenu (HMENU menu) const
  3139. {
  3140. if (! hasTitleBar())
  3141. {
  3142. if (isFullScreen())
  3143. {
  3144. EnableMenuItem (menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  3145. EnableMenuItem (menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  3146. }
  3147. else if (! isMinimised())
  3148. {
  3149. EnableMenuItem (menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
  3150. }
  3151. }
  3152. }
  3153. void doSettingChange()
  3154. {
  3155. forceDisplayUpdate();
  3156. if (fullScreen && ! isMinimised())
  3157. setWindowPos (hwnd, ScalingHelpers::scaledScreenPosToUnscaled (component, Desktop::getInstance().getDisplays()
  3158. .getDisplayForRect (component.getScreenBounds())->userArea),
  3159. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
  3160. auto* dispatcher = VBlankDispatcher::getInstance();
  3161. dispatcher->reconfigureDisplays();
  3162. updateCurrentMonitor();
  3163. dispatcher->updateDisplay (*this, currentMonitor);
  3164. }
  3165. //==============================================================================
  3166. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  3167. void setModifierKeyProvider (ModifierKeyProvider* provider) override
  3168. {
  3169. modProvider = provider;
  3170. }
  3171. void removeModifierKeyProvider() override
  3172. {
  3173. modProvider = nullptr;
  3174. }
  3175. #endif
  3176. public:
  3177. static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  3178. {
  3179. // Ensure that non-client areas are scaled for per-monitor DPI awareness v1 - can't
  3180. // do this in peerWindowProc as we have no window at this point
  3181. if (message == WM_NCCREATE && enableNonClientDPIScaling != nullptr)
  3182. enableNonClientDPIScaling (h);
  3183. if (auto* peer = getOwnerOfWindow (h))
  3184. {
  3185. jassert (isValidPeer (peer));
  3186. return peer->peerWindowProc (h, message, wParam, lParam);
  3187. }
  3188. return DefWindowProcW (h, message, wParam, lParam);
  3189. }
  3190. private:
  3191. static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
  3192. {
  3193. auto& mm = *MessageManager::getInstance();
  3194. if (mm.currentThreadHasLockedMessageManager())
  3195. return callback (userData);
  3196. return mm.callFunctionOnMessageThread (callback, userData);
  3197. }
  3198. static POINT getPOINTFromLParam (LPARAM lParam) noexcept
  3199. {
  3200. return { GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam) };
  3201. }
  3202. Point<float> getPointFromLocalLParam (LPARAM lParam) noexcept
  3203. {
  3204. auto p = pointFromPOINT (getPOINTFromLParam (lParam));
  3205. if (isPerMonitorDPIAwareWindow (hwnd))
  3206. {
  3207. // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the
  3208. // physical screen position and then convert this to local logical coordinates
  3209. auto r = getWindowScreenRect (hwnd);
  3210. return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (pointFromPOINT ({ r.left + p.x + roundToInt (windowBorder.getLeft() * scaleFactor),
  3211. r.top + p.y + roundToInt (windowBorder.getTop() * scaleFactor) })).toFloat());
  3212. }
  3213. return p.toFloat();
  3214. }
  3215. Point<float> getCurrentMousePos() noexcept
  3216. {
  3217. return globalToLocal (convertPhysicalScreenPointToLogical (pointFromPOINT (getPOINTFromLParam ((LPARAM) GetMessagePos())), hwnd).toFloat());
  3218. }
  3219. LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  3220. {
  3221. switch (message)
  3222. {
  3223. //==============================================================================
  3224. case WM_NCHITTEST:
  3225. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  3226. return HTTRANSPARENT;
  3227. if (! hasTitleBar())
  3228. return HTCLIENT;
  3229. break;
  3230. //==============================================================================
  3231. case WM_PAINT:
  3232. handlePaintMessage();
  3233. return 0;
  3234. case WM_NCPAINT:
  3235. handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
  3236. if (hasTitleBar())
  3237. break; // let the DefWindowProc handle drawing the frame.
  3238. return 0;
  3239. case WM_ERASEBKGND:
  3240. case WM_NCCALCSIZE:
  3241. if (hasTitleBar())
  3242. break;
  3243. return 1;
  3244. //==============================================================================
  3245. case WM_POINTERUPDATE:
  3246. if (handlePointerInput (wParam, lParam, false, false))
  3247. return 0;
  3248. break;
  3249. case WM_POINTERDOWN:
  3250. if (handlePointerInput (wParam, lParam, true, false))
  3251. return 0;
  3252. break;
  3253. case WM_POINTERUP:
  3254. if (handlePointerInput (wParam, lParam, false, true))
  3255. return 0;
  3256. break;
  3257. //==============================================================================
  3258. case WM_MOUSEMOVE: doMouseMove (getPointFromLocalLParam (lParam), false); return 0;
  3259. case WM_POINTERLEAVE:
  3260. case WM_MOUSELEAVE: doMouseExit(); return 0;
  3261. case WM_LBUTTONDOWN:
  3262. case WM_MBUTTONDOWN:
  3263. case WM_RBUTTONDOWN: doMouseDown (getPointFromLocalLParam (lParam), wParam); return 0;
  3264. case WM_LBUTTONUP:
  3265. case WM_MBUTTONUP:
  3266. case WM_RBUTTONUP: doMouseUp (getPointFromLocalLParam (lParam), wParam); return 0;
  3267. case WM_POINTERWHEEL:
  3268. case 0x020A: /* WM_MOUSEWHEEL */ doMouseWheel (wParam, true); return 0;
  3269. case WM_POINTERHWHEEL:
  3270. case 0x020E: /* WM_MOUSEHWHEEL */ doMouseWheel (wParam, false); return 0;
  3271. case WM_CAPTURECHANGED: doCaptureChanged(); return 0;
  3272. case WM_NCPOINTERUPDATE:
  3273. case WM_NCMOUSEMOVE:
  3274. if (hasTitleBar())
  3275. break;
  3276. return 0;
  3277. case WM_TOUCH:
  3278. if (getTouchInputInfo != nullptr)
  3279. return doTouchEvent ((int) wParam, (HTOUCHINPUT) lParam);
  3280. break;
  3281. case 0x119: /* WM_GESTURE */
  3282. if (doGestureEvent (lParam))
  3283. return 0;
  3284. break;
  3285. //==============================================================================
  3286. case WM_SIZING: return handleSizeConstraining (*(RECT*) lParam, wParam);
  3287. case WM_WINDOWPOSCHANGING: return handlePositionChanging (*(WINDOWPOS*) lParam);
  3288. case 0x2e0: /* WM_DPICHANGED */ return handleDPIChanging ((int) HIWORD (wParam), *(RECT*) lParam);
  3289. case WM_WINDOWPOSCHANGED:
  3290. {
  3291. const WINDOWPOS& wPos = *reinterpret_cast<WINDOWPOS*> (lParam);
  3292. if ((wPos.flags & SWP_NOMOVE) != 0 && (wPos.flags & SWP_NOSIZE) != 0)
  3293. startTimer (100);
  3294. else
  3295. if (handlePositionChanged())
  3296. return 0;
  3297. }
  3298. break;
  3299. //==============================================================================
  3300. case WM_KEYDOWN:
  3301. case WM_SYSKEYDOWN:
  3302. if (doKeyDown (wParam))
  3303. return 0;
  3304. forwardMessageToParent (message, wParam, lParam);
  3305. break;
  3306. case WM_KEYUP:
  3307. case WM_SYSKEYUP:
  3308. if (doKeyUp (wParam))
  3309. return 0;
  3310. forwardMessageToParent (message, wParam, lParam);
  3311. break;
  3312. case WM_CHAR:
  3313. if (doKeyChar ((int) wParam, lParam))
  3314. return 0;
  3315. forwardMessageToParent (message, wParam, lParam);
  3316. break;
  3317. case WM_APPCOMMAND:
  3318. if (doAppCommand (lParam))
  3319. return TRUE;
  3320. break;
  3321. case WM_MENUCHAR: // triggered when alt+something is pressed
  3322. return MNC_CLOSE << 16; // (avoids making the default system beep)
  3323. //==============================================================================
  3324. case WM_SETFOCUS:
  3325. updateKeyModifiers();
  3326. handleFocusGain();
  3327. break;
  3328. case WM_KILLFOCUS:
  3329. if (hasCreatedCaret)
  3330. {
  3331. hasCreatedCaret = false;
  3332. DestroyCaret();
  3333. }
  3334. handleFocusLoss();
  3335. if (auto* modal = Component::getCurrentlyModalComponent())
  3336. if (auto* peer = modal->getPeer())
  3337. if ((peer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0)
  3338. sendInputAttemptWhenModalMessage();
  3339. break;
  3340. case WM_ACTIVATEAPP:
  3341. // Windows does weird things to process priority when you swap apps,
  3342. // so this forces an update when the app is brought to the front
  3343. if (wParam != FALSE)
  3344. juce_repeatLastProcessPriority();
  3345. else
  3346. Desktop::getInstance().setKioskModeComponent (nullptr); // turn kiosk mode off if we lose focus
  3347. juce_checkCurrentlyFocusedTopLevelWindow();
  3348. modifiersAtLastCallback = -1;
  3349. return 0;
  3350. case WM_ACTIVATE:
  3351. if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
  3352. {
  3353. handleAppActivation (wParam);
  3354. return 0;
  3355. }
  3356. break;
  3357. case WM_NCACTIVATE:
  3358. // while a temporary window is being shown, prevent Windows from deactivating the
  3359. // title bars of our main windows.
  3360. if (wParam == 0 && ! shouldDeactivateTitleBar)
  3361. wParam = TRUE; // change this and let it get passed to the DefWindowProc.
  3362. break;
  3363. case WM_POINTERACTIVATE:
  3364. case WM_MOUSEACTIVATE:
  3365. if (! component.getMouseClickGrabsKeyboardFocus())
  3366. return MA_NOACTIVATE;
  3367. break;
  3368. case WM_SHOWWINDOW:
  3369. if (wParam != 0)
  3370. {
  3371. component.setVisible (true);
  3372. handleBroughtToFront();
  3373. }
  3374. break;
  3375. case WM_CLOSE:
  3376. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  3377. handleUserClosingWindow();
  3378. return 0;
  3379. #if JUCE_REMOVE_COMPONENT_FROM_DESKTOP_ON_WM_DESTROY
  3380. case WM_DESTROY:
  3381. getComponent().removeFromDesktop();
  3382. return 0;
  3383. #endif
  3384. case WM_QUERYENDSESSION:
  3385. if (auto* app = JUCEApplicationBase::getInstance())
  3386. {
  3387. app->systemRequestedQuit();
  3388. return MessageManager::getInstance()->hasStopMessageBeenSent();
  3389. }
  3390. return TRUE;
  3391. case WM_POWERBROADCAST:
  3392. handlePowerBroadcast (wParam);
  3393. break;
  3394. case WM_SYNCPAINT:
  3395. return 0;
  3396. case WM_DISPLAYCHANGE:
  3397. InvalidateRect (h, nullptr, 0);
  3398. // intentional fall-through...
  3399. JUCE_FALLTHROUGH
  3400. case WM_SETTINGCHANGE: // note the fall-through in the previous case!
  3401. doSettingChange();
  3402. break;
  3403. case WM_INITMENU:
  3404. initialiseSysMenu ((HMENU) wParam);
  3405. break;
  3406. case WM_SYSCOMMAND:
  3407. switch (wParam & 0xfff0)
  3408. {
  3409. case SC_CLOSE:
  3410. if (sendInputAttemptWhenModalMessage())
  3411. return 0;
  3412. if (hasTitleBar())
  3413. {
  3414. PostMessage (h, WM_CLOSE, 0, 0);
  3415. return 0;
  3416. }
  3417. break;
  3418. case SC_KEYMENU:
  3419. #if ! JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU
  3420. // This test prevents a press of the ALT key from triggering the ancient top-left window menu.
  3421. // By default we suppress this behaviour because it's unlikely that more than a tiny subset of
  3422. // our users will actually want it, and it causes problems if you're trying to use the ALT key
  3423. // as a modifier for mouse actions. If you really need the old behaviour, then just define
  3424. // JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU=1 in your app.
  3425. if ((lParam >> 16) <= 0) // Values above zero indicate that a mouse-click triggered the menu
  3426. return 0;
  3427. #endif
  3428. // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
  3429. // situations that can arise if a modal loop is started from an alt-key keypress).
  3430. if (hasTitleBar() && h == GetCapture())
  3431. ReleaseCapture();
  3432. break;
  3433. case SC_MAXIMIZE:
  3434. if (! sendInputAttemptWhenModalMessage())
  3435. setFullScreen (true);
  3436. return 0;
  3437. case SC_MINIMIZE:
  3438. if (sendInputAttemptWhenModalMessage())
  3439. return 0;
  3440. if (! hasTitleBar())
  3441. {
  3442. setMinimised (true);
  3443. return 0;
  3444. }
  3445. break;
  3446. case SC_RESTORE:
  3447. if (sendInputAttemptWhenModalMessage())
  3448. return 0;
  3449. if (hasTitleBar())
  3450. {
  3451. if (isFullScreen())
  3452. {
  3453. setFullScreen (false);
  3454. return 0;
  3455. }
  3456. }
  3457. else
  3458. {
  3459. if (isMinimised())
  3460. setMinimised (false);
  3461. else if (isFullScreen())
  3462. setFullScreen (false);
  3463. return 0;
  3464. }
  3465. break;
  3466. }
  3467. break;
  3468. case WM_NCPOINTERDOWN:
  3469. case WM_NCLBUTTONDOWN:
  3470. handleLeftClickInNCArea (wParam);
  3471. break;
  3472. case WM_NCRBUTTONDOWN:
  3473. case WM_NCMBUTTONDOWN:
  3474. sendInputAttemptWhenModalMessage();
  3475. break;
  3476. case WM_IME_SETCONTEXT:
  3477. imeHandler.handleSetContext (h, wParam == TRUE);
  3478. lParam &= ~(LPARAM) ISC_SHOWUICOMPOSITIONWINDOW;
  3479. break;
  3480. case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
  3481. case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
  3482. case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
  3483. case WM_GETDLGCODE:
  3484. return DLGC_WANTALLKEYS;
  3485. case WM_GETOBJECT:
  3486. {
  3487. if (static_cast<long> (lParam) == WindowsAccessibility::getUiaRootObjectId())
  3488. {
  3489. if (auto* handler = component.getAccessibilityHandler())
  3490. {
  3491. LRESULT res = 0;
  3492. if (WindowsAccessibility::handleWmGetObject (handler, wParam, lParam, &res))
  3493. {
  3494. isAccessibilityActive = true;
  3495. return res;
  3496. }
  3497. }
  3498. }
  3499. break;
  3500. }
  3501. default:
  3502. break;
  3503. }
  3504. return DefWindowProcW (h, message, wParam, lParam);
  3505. }
  3506. bool sendInputAttemptWhenModalMessage()
  3507. {
  3508. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  3509. return false;
  3510. if (auto* current = Component::getCurrentlyModalComponent())
  3511. if (auto* owner = getOwnerOfWindow ((HWND) current->getWindowHandle()))
  3512. if (! owner->shouldIgnoreModalDismiss)
  3513. current->inputAttemptWhenModal();
  3514. return true;
  3515. }
  3516. //==============================================================================
  3517. struct IMEHandler
  3518. {
  3519. IMEHandler()
  3520. {
  3521. reset();
  3522. }
  3523. void handleSetContext (HWND hWnd, const bool windowIsActive)
  3524. {
  3525. if (compositionInProgress && ! windowIsActive)
  3526. {
  3527. compositionInProgress = false;
  3528. if (HIMC hImc = ImmGetContext (hWnd))
  3529. {
  3530. ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  3531. ImmReleaseContext (hWnd, hImc);
  3532. }
  3533. }
  3534. }
  3535. void handleStartComposition (ComponentPeer& owner)
  3536. {
  3537. reset();
  3538. if (auto* target = owner.findCurrentTextInputTarget())
  3539. target->insertTextAtCaret (String());
  3540. }
  3541. void handleEndComposition (ComponentPeer& owner, HWND hWnd)
  3542. {
  3543. if (compositionInProgress)
  3544. {
  3545. // If this occurs, the user has cancelled the composition, so clear their changes..
  3546. if (auto* target = owner.findCurrentTextInputTarget())
  3547. {
  3548. target->setHighlightedRegion (compositionRange);
  3549. target->insertTextAtCaret (String());
  3550. compositionRange.setLength (0);
  3551. target->setHighlightedRegion (Range<int>::emptyRange (compositionRange.getEnd()));
  3552. target->setTemporaryUnderlining ({});
  3553. }
  3554. if (auto hImc = ImmGetContext (hWnd))
  3555. {
  3556. ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
  3557. ImmReleaseContext (hWnd, hImc);
  3558. }
  3559. }
  3560. reset();
  3561. }
  3562. void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
  3563. {
  3564. if (auto* target = owner.findCurrentTextInputTarget())
  3565. {
  3566. if (auto hImc = ImmGetContext (hWnd))
  3567. {
  3568. if (compositionRange.getStart() < 0)
  3569. compositionRange = Range<int>::emptyRange (target->getHighlightedRegion().getStart());
  3570. if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
  3571. {
  3572. replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
  3573. Range<int>::emptyRange (-1));
  3574. reset();
  3575. target->setTemporaryUnderlining ({});
  3576. }
  3577. else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
  3578. {
  3579. replaceCurrentSelection (target, getCompositionString (hImc, GCS_COMPSTR),
  3580. getCompositionSelection (hImc, lParam));
  3581. target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
  3582. compositionInProgress = true;
  3583. }
  3584. moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
  3585. ImmReleaseContext (hWnd, hImc);
  3586. }
  3587. }
  3588. }
  3589. private:
  3590. //==============================================================================
  3591. Range<int> compositionRange; // The range being modified in the TextInputTarget
  3592. bool compositionInProgress;
  3593. //==============================================================================
  3594. void reset()
  3595. {
  3596. compositionRange = Range<int>::emptyRange (-1);
  3597. compositionInProgress = false;
  3598. }
  3599. String getCompositionString (HIMC hImc, const DWORD type) const
  3600. {
  3601. jassert (hImc != HIMC{});
  3602. const auto stringSizeBytes = ImmGetCompositionString (hImc, type, nullptr, 0);
  3603. if (stringSizeBytes > 0)
  3604. {
  3605. HeapBlock<TCHAR> buffer;
  3606. buffer.calloc ((size_t) stringSizeBytes / sizeof (TCHAR) + 1);
  3607. ImmGetCompositionString (hImc, type, buffer, (DWORD) stringSizeBytes);
  3608. return String (buffer.get());
  3609. }
  3610. return {};
  3611. }
  3612. int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
  3613. {
  3614. jassert (hImc != HIMC{});
  3615. if ((lParam & CS_NOMOVECARET) != 0)
  3616. return compositionRange.getStart();
  3617. if ((lParam & GCS_CURSORPOS) != 0)
  3618. {
  3619. const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, nullptr, 0);
  3620. return compositionRange.getStart() + jmax (0, localCaretPos);
  3621. }
  3622. return compositionRange.getStart() + currentIMEString.length();
  3623. }
  3624. // Get selected/highlighted range while doing composition:
  3625. // returned range is relative to beginning of TextInputTarget, not composition string
  3626. Range<int> getCompositionSelection (HIMC hImc, LPARAM lParam) const
  3627. {
  3628. jassert (hImc != HIMC{});
  3629. int selectionStart = 0;
  3630. int selectionEnd = 0;
  3631. if ((lParam & GCS_COMPATTR) != 0)
  3632. {
  3633. // Get size of attributes array:
  3634. const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, nullptr, 0);
  3635. if (attributeSizeBytes > 0)
  3636. {
  3637. // Get attributes (8 bit flag per character):
  3638. HeapBlock<char> attributes (attributeSizeBytes);
  3639. ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, (DWORD) attributeSizeBytes);
  3640. selectionStart = 0;
  3641. for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
  3642. if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
  3643. break;
  3644. for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
  3645. if (attributes[selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
  3646. break;
  3647. }
  3648. }
  3649. return Range<int> (selectionStart, selectionEnd) + compositionRange.getStart();
  3650. }
  3651. void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, Range<int> newSelection)
  3652. {
  3653. if (compositionInProgress)
  3654. target->setHighlightedRegion (compositionRange);
  3655. target->insertTextAtCaret (newContent);
  3656. compositionRange.setLength (newContent.length());
  3657. if (newSelection.getStart() < 0)
  3658. newSelection = Range<int>::emptyRange (compositionRange.getEnd());
  3659. target->setHighlightedRegion (newSelection);
  3660. }
  3661. Array<Range<int>> getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
  3662. {
  3663. Array<Range<int>> result;
  3664. if (hImc != HIMC{} && (lParam & GCS_COMPCLAUSE) != 0)
  3665. {
  3666. auto clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, nullptr, 0);
  3667. if (clauseDataSizeBytes > 0)
  3668. {
  3669. const auto numItems = (size_t) clauseDataSizeBytes / sizeof (uint32);
  3670. HeapBlock<uint32> clauseData (numItems);
  3671. if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, (DWORD) clauseDataSizeBytes) > 0)
  3672. for (size_t i = 0; i + 1 < numItems; ++i)
  3673. result.add (Range<int> ((int) clauseData[i], (int) clauseData[i + 1]) + compositionRange.getStart());
  3674. }
  3675. }
  3676. return result;
  3677. }
  3678. void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
  3679. {
  3680. if (auto* targetComp = dynamic_cast<Component*> (target))
  3681. {
  3682. auto area = peer.getComponent().getLocalArea (targetComp, target->getCaretRectangle());
  3683. CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
  3684. ImmSetCandidateWindow (hImc, &pos);
  3685. }
  3686. }
  3687. JUCE_DECLARE_NON_COPYABLE (IMEHandler)
  3688. };
  3689. void timerCallback() override
  3690. {
  3691. handlePositionChanged();
  3692. stopTimer();
  3693. }
  3694. static bool isAncestor (HWND outer, HWND inner)
  3695. {
  3696. if (outer == nullptr || inner == nullptr)
  3697. return false;
  3698. if (outer == inner)
  3699. return true;
  3700. return isAncestor (outer, GetAncestor (inner, GA_PARENT));
  3701. }
  3702. void windowShouldDismissModals (HWND originator)
  3703. {
  3704. if (shouldIgnoreModalDismiss)
  3705. return;
  3706. if (isAncestor (originator, hwnd))
  3707. sendInputAttemptWhenModalMessage();
  3708. }
  3709. // Unfortunately SetWindowsHookEx only allows us to register a static function as a hook.
  3710. // To get around this, we keep a static list of listeners which are interested in
  3711. // top-level window events, and notify all of these listeners from the callback.
  3712. class TopLevelModalDismissBroadcaster
  3713. {
  3714. public:
  3715. TopLevelModalDismissBroadcaster()
  3716. : hook (SetWindowsHookEx (WH_CALLWNDPROC,
  3717. callWndProc,
  3718. (HINSTANCE) juce::Process::getCurrentModuleInstanceHandle(),
  3719. GetCurrentThreadId()))
  3720. {}
  3721. ~TopLevelModalDismissBroadcaster() noexcept
  3722. {
  3723. UnhookWindowsHookEx (hook);
  3724. }
  3725. private:
  3726. static void processMessage (int nCode, const CWPSTRUCT* info)
  3727. {
  3728. if (nCode < 0 || info == nullptr)
  3729. return;
  3730. constexpr UINT events[] { WM_MOVE,
  3731. WM_SIZE,
  3732. WM_WINDOWPOSCHANGING,
  3733. WM_NCPOINTERDOWN,
  3734. WM_NCLBUTTONDOWN,
  3735. WM_NCRBUTTONDOWN,
  3736. WM_NCMBUTTONDOWN };
  3737. if (std::find (std::begin (events), std::end (events), info->message) == std::end (events))
  3738. return;
  3739. if (info->message == WM_WINDOWPOSCHANGING)
  3740. {
  3741. const auto* windowPos = reinterpret_cast<const WINDOWPOS*> (info->lParam);
  3742. const auto windowPosFlags = windowPos->flags;
  3743. constexpr auto maskToCheck = SWP_NOMOVE | SWP_NOSIZE;
  3744. if ((windowPosFlags & maskToCheck) == maskToCheck)
  3745. return;
  3746. }
  3747. // windowMayDismissModals could affect the number of active ComponentPeer instances
  3748. for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
  3749. if (i < ComponentPeer::getNumPeers())
  3750. if (auto* hwndPeer = dynamic_cast<HWNDComponentPeer*> (ComponentPeer::getPeer (i)))
  3751. hwndPeer->windowShouldDismissModals (info->hwnd);
  3752. }
  3753. static LRESULT CALLBACK callWndProc (int nCode, WPARAM wParam, LPARAM lParam)
  3754. {
  3755. processMessage (nCode, reinterpret_cast<CWPSTRUCT*> (lParam));
  3756. return CallNextHookEx ({}, nCode, wParam, lParam);
  3757. }
  3758. HHOOK hook;
  3759. };
  3760. SharedResourcePointer<TopLevelModalDismissBroadcaster> modalDismissBroadcaster;
  3761. IMEHandler imeHandler;
  3762. bool shouldIgnoreModalDismiss = false;
  3763. RectangleList<int> deferredRepaints;
  3764. //==============================================================================
  3765. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer)
  3766. };
  3767. MultiTouchMapper<DWORD> HWNDComponentPeer::currentTouches;
  3768. ModifierKeys HWNDComponentPeer::modifiersAtLastCallback;
  3769. ComponentPeer* Component::createNewPeer (int styleFlags, void* parentHWND)
  3770. {
  3771. return new HWNDComponentPeer (*this, styleFlags, (HWND) parentHWND, false);
  3772. }
  3773. JUCE_API ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component& component, void* parentHWND);
  3774. JUCE_API ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component& component, void* parentHWND)
  3775. {
  3776. return new HWNDComponentPeer (component, ComponentPeer::windowIgnoresMouseClicks,
  3777. (HWND) parentHWND, true);
  3778. }
  3779. JUCE_IMPLEMENT_SINGLETON (HWNDComponentPeer::WindowClassHolder)
  3780. //==============================================================================
  3781. bool KeyPress::isKeyCurrentlyDown (const int keyCode)
  3782. {
  3783. auto k = (SHORT) keyCode;
  3784. if ((keyCode & extendedKeyModifier) == 0)
  3785. {
  3786. if (k >= (SHORT) 'a' && k <= (SHORT) 'z')
  3787. k += (SHORT) 'A' - (SHORT) 'a';
  3788. // Only translate if extendedKeyModifier flag is not set
  3789. const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
  3790. (SHORT) '+', VK_OEM_PLUS,
  3791. (SHORT) '-', VK_OEM_MINUS,
  3792. (SHORT) '.', VK_OEM_PERIOD,
  3793. (SHORT) ';', VK_OEM_1,
  3794. (SHORT) ':', VK_OEM_1,
  3795. (SHORT) '/', VK_OEM_2,
  3796. (SHORT) '?', VK_OEM_2,
  3797. (SHORT) '[', VK_OEM_4,
  3798. (SHORT) ']', VK_OEM_6 };
  3799. for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
  3800. if (k == translatedValues[i])
  3801. k = translatedValues[i + 1];
  3802. }
  3803. return HWNDComponentPeer::isKeyDown (k);
  3804. }
  3805. // (This internal function is used by the plugin client module)
  3806. bool offerKeyMessageToJUCEWindow (MSG& m);
  3807. bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); }
  3808. //==============================================================================
  3809. static DWORD getProcess (HWND hwnd)
  3810. {
  3811. DWORD result = 0;
  3812. GetWindowThreadProcessId (hwnd, &result);
  3813. return result;
  3814. }
  3815. /* Returns true if the viewComponent is embedded into a window
  3816. owned by the foreground process.
  3817. */
  3818. bool isEmbeddedInForegroundProcess (Component* c)
  3819. {
  3820. if (c == nullptr)
  3821. return false;
  3822. auto* peer = c->getPeer();
  3823. auto* hwnd = peer != nullptr ? static_cast<HWND> (peer->getNativeHandle()) : nullptr;
  3824. if (hwnd == nullptr)
  3825. return true;
  3826. const auto fgProcess = getProcess (GetForegroundWindow());
  3827. const auto ownerProcess = getProcess (GetAncestor (hwnd, GA_ROOTOWNER));
  3828. return fgProcess == ownerProcess;
  3829. }
  3830. bool JUCE_CALLTYPE Process::isForegroundProcess()
  3831. {
  3832. if (auto fg = GetForegroundWindow())
  3833. return getProcess (fg) == GetCurrentProcessId();
  3834. return true;
  3835. }
  3836. // N/A on Windows as far as I know.
  3837. void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  3838. void JUCE_CALLTYPE Process::hide() {}
  3839. //==============================================================================
  3840. static BOOL CALLBACK enumAlwaysOnTopWindows (HWND hwnd, LPARAM lParam)
  3841. {
  3842. if (IsWindowVisible (hwnd))
  3843. {
  3844. DWORD processID = 0;
  3845. GetWindowThreadProcessId (hwnd, &processID);
  3846. if (processID == GetCurrentProcessId())
  3847. {
  3848. WINDOWINFO info{};
  3849. if (GetWindowInfo (hwnd, &info)
  3850. && (info.dwExStyle & WS_EX_TOPMOST) != 0)
  3851. {
  3852. *reinterpret_cast<bool*> (lParam) = true;
  3853. return FALSE;
  3854. }
  3855. }
  3856. }
  3857. return TRUE;
  3858. }
  3859. bool juce_areThereAnyAlwaysOnTopWindows()
  3860. {
  3861. bool anyAlwaysOnTopFound = false;
  3862. EnumWindows (&enumAlwaysOnTopWindows, (LPARAM) &anyAlwaysOnTopFound);
  3863. return anyAlwaysOnTopFound;
  3864. }
  3865. //==============================================================================
  3866. #if JUCE_MSVC
  3867. // required to enable the newer dialog box on vista and above
  3868. #pragma comment(linker, \
  3869. "\"/MANIFESTDEPENDENCY:type='Win32' " \
  3870. "name='Microsoft.Windows.Common-Controls' " \
  3871. "version='6.0.0.0' " \
  3872. "processorArchitecture='*' " \
  3873. "publicKeyToken='6595b64144ccf1df' " \
  3874. "language='*'\"" \
  3875. )
  3876. #endif
  3877. class WindowsMessageBoxBase : private AsyncUpdater
  3878. {
  3879. public:
  3880. WindowsMessageBoxBase (Component* comp,
  3881. std::unique_ptr<ModalComponentManager::Callback>&& cb)
  3882. : associatedComponent (comp),
  3883. callback (std::move (cb))
  3884. {
  3885. }
  3886. virtual int getResult() = 0;
  3887. HWND getParentHWND() const
  3888. {
  3889. if (associatedComponent != nullptr)
  3890. return (HWND) associatedComponent->getWindowHandle();
  3891. return nullptr;
  3892. }
  3893. using AsyncUpdater::triggerAsyncUpdate;
  3894. private:
  3895. void handleAsyncUpdate() override
  3896. {
  3897. const auto result = getResult();
  3898. if (callback != nullptr)
  3899. callback->modalStateFinished (result);
  3900. delete this;
  3901. }
  3902. Component::SafePointer<Component> associatedComponent;
  3903. std::unique_ptr<ModalComponentManager::Callback> callback;
  3904. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsMessageBoxBase)
  3905. };
  3906. class PreVistaMessageBox : public WindowsMessageBoxBase
  3907. {
  3908. public:
  3909. PreVistaMessageBox (const MessageBoxOptions& opts,
  3910. UINT extraFlags,
  3911. std::unique_ptr<ModalComponentManager::Callback>&& cb)
  3912. : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (cb)),
  3913. flags (extraFlags | getMessageBoxFlags (opts.getIconType())),
  3914. title (opts.getTitle()), message (opts.getMessage())
  3915. {
  3916. }
  3917. int getResult() override
  3918. {
  3919. const auto result = MessageBox (getParentHWND(), message.toWideCharPointer(), title.toWideCharPointer(), flags);
  3920. if (result == IDYES || result == IDOK) return 0;
  3921. if (result == IDNO && ((flags & 1) != 0)) return 1;
  3922. return 2;
  3923. }
  3924. private:
  3925. static UINT getMessageBoxFlags (MessageBoxIconType iconType) noexcept
  3926. {
  3927. // this window can get lost behind JUCE windows which are set to be alwaysOnTop
  3928. // so if there are any set it to be topmost
  3929. const auto topmostFlag = juce_areThereAnyAlwaysOnTopWindows() ? MB_TOPMOST : 0;
  3930. const auto iconFlags = [&]() -> decltype (topmostFlag)
  3931. {
  3932. switch (iconType)
  3933. {
  3934. case MessageBoxIconType::QuestionIcon: return MB_ICONQUESTION;
  3935. case MessageBoxIconType::WarningIcon: return MB_ICONWARNING;
  3936. case MessageBoxIconType::InfoIcon: return MB_ICONINFORMATION;
  3937. case MessageBoxIconType::NoIcon: break;
  3938. }
  3939. return 0;
  3940. }();
  3941. return static_cast<UINT> (MB_TASKMODAL | MB_SETFOREGROUND | topmostFlag | iconFlags);
  3942. }
  3943. const UINT flags;
  3944. const String title, message;
  3945. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreVistaMessageBox)
  3946. };
  3947. using TaskDialogIndirectFunc = HRESULT (WINAPI*) (const TASKDIALOGCONFIG*, INT*, INT*, BOOL*);
  3948. static TaskDialogIndirectFunc taskDialogIndirect = nullptr;
  3949. class WindowsTaskDialog : public WindowsMessageBoxBase
  3950. {
  3951. public:
  3952. WindowsTaskDialog (const MessageBoxOptions& opts,
  3953. std::unique_ptr<ModalComponentManager::Callback>&& cb)
  3954. : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (cb)),
  3955. iconType (opts.getIconType()),
  3956. title (opts.getTitle()), message (opts.getMessage()),
  3957. button1 (opts.getButtonText (0)), button2 (opts.getButtonText (1)), button3 (opts.getButtonText (2))
  3958. {
  3959. }
  3960. int getResult() override
  3961. {
  3962. TASKDIALOGCONFIG config{};
  3963. config.cbSize = sizeof (config);
  3964. config.hwndParent = getParentHWND();
  3965. config.pszWindowTitle = title.toWideCharPointer();
  3966. config.pszContent = message.toWideCharPointer();
  3967. config.hInstance = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  3968. if (iconType == MessageBoxIconType::QuestionIcon)
  3969. {
  3970. if (auto* questionIcon = LoadIcon (nullptr, IDI_QUESTION))
  3971. {
  3972. config.hMainIcon = questionIcon;
  3973. config.dwFlags |= TDF_USE_HICON_MAIN;
  3974. }
  3975. }
  3976. else
  3977. {
  3978. auto icon = [this]() -> LPWSTR
  3979. {
  3980. switch (iconType)
  3981. {
  3982. case MessageBoxIconType::WarningIcon: return TD_WARNING_ICON;
  3983. case MessageBoxIconType::InfoIcon: return TD_INFORMATION_ICON;
  3984. case MessageBoxIconType::QuestionIcon: JUCE_FALLTHROUGH
  3985. case MessageBoxIconType::NoIcon:
  3986. break;
  3987. }
  3988. return nullptr;
  3989. }();
  3990. if (icon != nullptr)
  3991. config.pszMainIcon = icon;
  3992. }
  3993. std::vector<TASKDIALOG_BUTTON> buttons;
  3994. for (const auto* buttonText : { &button1, &button2, &button3 })
  3995. if (buttonText->isNotEmpty())
  3996. buttons.push_back ({ (int) buttons.size(), buttonText->toWideCharPointer() });
  3997. config.pButtons = buttons.data();
  3998. config.cButtons = (UINT) buttons.size();
  3999. int buttonIndex = 0;
  4000. taskDialogIndirect (&config, &buttonIndex, nullptr, nullptr);
  4001. return buttonIndex;
  4002. }
  4003. static bool loadTaskDialog()
  4004. {
  4005. static bool hasChecked = false;
  4006. if (! hasChecked)
  4007. {
  4008. hasChecked = true;
  4009. const auto comctl = "Comctl32.dll";
  4010. LoadLibraryA (comctl);
  4011. const auto comctlModule = GetModuleHandleA (comctl);
  4012. if (comctlModule != nullptr)
  4013. taskDialogIndirect = (TaskDialogIndirectFunc) GetProcAddress (comctlModule, "TaskDialogIndirect");
  4014. }
  4015. return taskDialogIndirect != nullptr;
  4016. }
  4017. private:
  4018. MessageBoxIconType iconType;
  4019. String title, message, button1, button2, button3;
  4020. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTaskDialog)
  4021. };
  4022. static std::unique_ptr<WindowsMessageBoxBase> createMessageBox (const MessageBoxOptions& options,
  4023. std::unique_ptr<ModalComponentManager::Callback> callback)
  4024. {
  4025. const auto useTaskDialog =
  4026. #if JUCE_MODAL_LOOPS_PERMITTED
  4027. callback != nullptr &&
  4028. #endif
  4029. SystemStats::getOperatingSystemType() >= SystemStats::WinVista
  4030. && WindowsTaskDialog::loadTaskDialog();
  4031. if (useTaskDialog)
  4032. return std::make_unique<WindowsTaskDialog> (options, std::move (callback));
  4033. const auto extraFlags = [&options]
  4034. {
  4035. const auto numButtons = options.getNumButtons();
  4036. if (numButtons == 3)
  4037. return MB_YESNOCANCEL;
  4038. if (numButtons == 2)
  4039. return options.getButtonText (0) == "OK" ? MB_OKCANCEL
  4040. : MB_YESNO;
  4041. return MB_OK;
  4042. }();
  4043. return std::make_unique<PreVistaMessageBox> (options, (UINT) extraFlags, std::move (callback));
  4044. }
  4045. static int showDialog (const MessageBoxOptions& options,
  4046. ModalComponentManager::Callback* callbackIn,
  4047. AlertWindowMappings::MapFn mapFn)
  4048. {
  4049. #if JUCE_MODAL_LOOPS_PERMITTED
  4050. if (callbackIn == nullptr)
  4051. {
  4052. jassert (mapFn != nullptr);
  4053. auto messageBox = createMessageBox (options, nullptr);
  4054. return mapFn (messageBox->getResult());
  4055. }
  4056. #endif
  4057. auto messageBox = createMessageBox (options,
  4058. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn));
  4059. messageBox->triggerAsyncUpdate();
  4060. messageBox.release();
  4061. return 0;
  4062. }
  4063. #if JUCE_MODAL_LOOPS_PERMITTED
  4064. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  4065. const String& title, const String& message,
  4066. Component* associatedComponent)
  4067. {
  4068. showDialog (MessageBoxOptions()
  4069. .withIconType (iconType)
  4070. .withTitle (title)
  4071. .withMessage (message)
  4072. .withButton (TRANS("OK"))
  4073. .withAssociatedComponent (associatedComponent),
  4074. nullptr, AlertWindowMappings::messageBox);
  4075. }
  4076. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  4077. {
  4078. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  4079. }
  4080. #endif
  4081. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  4082. const String& title, const String& message,
  4083. Component* associatedComponent,
  4084. ModalComponentManager::Callback* callback)
  4085. {
  4086. showDialog (MessageBoxOptions()
  4087. .withIconType (iconType)
  4088. .withTitle (title)
  4089. .withMessage (message)
  4090. .withButton (TRANS("OK"))
  4091. .withAssociatedComponent (associatedComponent),
  4092. callback, AlertWindowMappings::messageBox);
  4093. }
  4094. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  4095. const String& title, const String& message,
  4096. Component* associatedComponent,
  4097. ModalComponentManager::Callback* callback)
  4098. {
  4099. return showDialog (MessageBoxOptions()
  4100. .withIconType (iconType)
  4101. .withTitle (title)
  4102. .withMessage (message)
  4103. .withButton (TRANS("OK"))
  4104. .withButton (TRANS("Cancel"))
  4105. .withAssociatedComponent (associatedComponent),
  4106. callback, AlertWindowMappings::okCancel) != 0;
  4107. }
  4108. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  4109. const String& title, const String& message,
  4110. Component* associatedComponent,
  4111. ModalComponentManager::Callback* callback)
  4112. {
  4113. return showDialog (MessageBoxOptions()
  4114. .withIconType (iconType)
  4115. .withTitle (title)
  4116. .withMessage (message)
  4117. .withButton (TRANS("Yes"))
  4118. .withButton (TRANS("No"))
  4119. .withButton (TRANS("Cancel"))
  4120. .withAssociatedComponent (associatedComponent),
  4121. callback, AlertWindowMappings::yesNoCancel);
  4122. }
  4123. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  4124. const String& title, const String& message,
  4125. Component* associatedComponent,
  4126. ModalComponentManager::Callback* callback)
  4127. {
  4128. return showDialog (MessageBoxOptions()
  4129. .withIconType (iconType)
  4130. .withTitle (title)
  4131. .withMessage (message)
  4132. .withButton (TRANS("Yes"))
  4133. .withButton (TRANS("No"))
  4134. .withAssociatedComponent (associatedComponent),
  4135. callback, AlertWindowMappings::okCancel);
  4136. }
  4137. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  4138. ModalComponentManager::Callback* callback)
  4139. {
  4140. showDialog (options, callback, AlertWindowMappings::noMapping);
  4141. }
  4142. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  4143. std::function<void (int)> callback)
  4144. {
  4145. showAsync (options, ModalCallbackFunction::create (callback));
  4146. }
  4147. //==============================================================================
  4148. bool MouseInputSource::SourceList::addSource()
  4149. {
  4150. auto numSources = sources.size();
  4151. if (numSources == 0 || canUseMultiTouch())
  4152. {
  4153. addSource (numSources, numSources == 0 ? MouseInputSource::InputSourceType::mouse
  4154. : MouseInputSource::InputSourceType::touch);
  4155. return true;
  4156. }
  4157. return false;
  4158. }
  4159. bool MouseInputSource::SourceList::canUseTouch()
  4160. {
  4161. return canUseMultiTouch();
  4162. }
  4163. Point<float> MouseInputSource::getCurrentRawMousePosition()
  4164. {
  4165. POINT mousePos;
  4166. GetCursorPos (&mousePos);
  4167. auto p = pointFromPOINT (mousePos);
  4168. if (isPerMonitorDPIAwareThread())
  4169. p = Desktop::getInstance().getDisplays().physicalToLogical (p);
  4170. return p.toFloat();
  4171. }
  4172. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  4173. {
  4174. auto newPositionInt = newPosition.roundToInt();
  4175. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  4176. if (isPerMonitorDPIAwareThread())
  4177. newPositionInt = Desktop::getInstance().getDisplays().logicalToPhysical (newPositionInt);
  4178. #endif
  4179. auto point = POINTFromPoint (newPositionInt);
  4180. SetCursorPos (point.x, point.y);
  4181. }
  4182. //==============================================================================
  4183. class ScreenSaverDefeater : public Timer
  4184. {
  4185. public:
  4186. ScreenSaverDefeater()
  4187. {
  4188. startTimer (10000);
  4189. timerCallback();
  4190. }
  4191. void timerCallback() override
  4192. {
  4193. if (Process::isForegroundProcess())
  4194. {
  4195. INPUT input = {};
  4196. input.type = INPUT_MOUSE;
  4197. input.mi.mouseData = MOUSEEVENTF_MOVE;
  4198. SendInput (1, &input, sizeof (INPUT));
  4199. }
  4200. }
  4201. };
  4202. static std::unique_ptr<ScreenSaverDefeater> screenSaverDefeater;
  4203. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  4204. {
  4205. if (isEnabled)
  4206. screenSaverDefeater = nullptr;
  4207. else if (screenSaverDefeater == nullptr)
  4208. screenSaverDefeater.reset (new ScreenSaverDefeater());
  4209. }
  4210. bool Desktop::isScreenSaverEnabled()
  4211. {
  4212. return screenSaverDefeater == nullptr;
  4213. }
  4214. //==============================================================================
  4215. void LookAndFeel::playAlertSound()
  4216. {
  4217. MessageBeep (MB_OK);
  4218. }
  4219. //==============================================================================
  4220. void SystemClipboard::copyTextToClipboard (const String& text)
  4221. {
  4222. if (OpenClipboard (nullptr) != 0)
  4223. {
  4224. if (EmptyClipboard() != 0)
  4225. {
  4226. auto bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4;
  4227. if (bytesNeeded > 0)
  4228. {
  4229. if (auto bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bytesNeeded + sizeof (WCHAR)))
  4230. {
  4231. if (auto* data = static_cast<WCHAR*> (GlobalLock (bufH)))
  4232. {
  4233. text.copyToUTF16 (data, bytesNeeded);
  4234. GlobalUnlock (bufH);
  4235. SetClipboardData (CF_UNICODETEXT, bufH);
  4236. }
  4237. }
  4238. }
  4239. }
  4240. CloseClipboard();
  4241. }
  4242. }
  4243. String SystemClipboard::getTextFromClipboard()
  4244. {
  4245. String result;
  4246. if (OpenClipboard (nullptr) != 0)
  4247. {
  4248. if (auto bufH = GetClipboardData (CF_UNICODETEXT))
  4249. {
  4250. if (auto* data = (const WCHAR*) GlobalLock (bufH))
  4251. {
  4252. result = String (data, (size_t) (GlobalSize (bufH) / sizeof (WCHAR)));
  4253. GlobalUnlock (bufH);
  4254. }
  4255. }
  4256. CloseClipboard();
  4257. }
  4258. return result;
  4259. }
  4260. //==============================================================================
  4261. void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable, bool /*allowMenusAndBars*/)
  4262. {
  4263. if (auto* tlw = dynamic_cast<TopLevelWindow*> (kioskModeComp))
  4264. tlw->setUsingNativeTitleBar (! enableOrDisable);
  4265. if (kioskModeComp != nullptr && enableOrDisable)
  4266. kioskModeComp->setBounds (getDisplays().getDisplayForRect (kioskModeComp->getScreenBounds())->totalArea);
  4267. }
  4268. void Desktop::allowedOrientationsChanged() {}
  4269. //==============================================================================
  4270. static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd)
  4271. {
  4272. Array<const Displays::Display*> candidateDisplays;
  4273. const auto scaleToLookFor = [&]
  4274. {
  4275. if (auto* peer = HWNDComponentPeer::getOwnerOfWindow (hwnd))
  4276. return peer->getPlatformScaleFactor();
  4277. return getScaleFactorForWindow (hwnd);
  4278. }();
  4279. auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
  4280. for (auto& d : Desktop::getInstance().getDisplays().displays)
  4281. if (approximatelyEqual (d.scale / globalScale, scaleToLookFor))
  4282. candidateDisplays.add (&d);
  4283. if (candidateDisplays.size() > 0)
  4284. {
  4285. if (candidateDisplays.size() == 1)
  4286. return candidateDisplays[0];
  4287. const auto bounds = [&]
  4288. {
  4289. if (auto* peer = HWNDComponentPeer::getOwnerOfWindow (hwnd))
  4290. return peer->getComponent().getTopLevelComponent()->getBounds();
  4291. return Desktop::getInstance().getDisplays().physicalToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)));
  4292. }();
  4293. const Displays::Display* retVal = nullptr;
  4294. int maxArea = -1;
  4295. for (auto* d : candidateDisplays)
  4296. {
  4297. auto intersection = d->totalArea.getIntersection (bounds);
  4298. auto area = intersection.getWidth() * intersection.getHeight();
  4299. if (area > maxArea)
  4300. {
  4301. maxArea = area;
  4302. retVal = d;
  4303. }
  4304. }
  4305. if (retVal != nullptr)
  4306. return retVal;
  4307. }
  4308. return Desktop::getInstance().getDisplays().getPrimaryDisplay();
  4309. }
  4310. //==============================================================================
  4311. struct MonitorInfo
  4312. {
  4313. MonitorInfo (bool main, RECT totalArea, RECT workArea, double d) noexcept
  4314. : isMain (main),
  4315. totalAreaRect (totalArea),
  4316. workAreaRect (workArea),
  4317. dpi (d)
  4318. {
  4319. }
  4320. bool isMain;
  4321. RECT totalAreaRect, workAreaRect;
  4322. double dpi;
  4323. };
  4324. static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT, LPARAM userInfo)
  4325. {
  4326. MONITORINFO info = {};
  4327. info.cbSize = sizeof (info);
  4328. GetMonitorInfo (hm, &info);
  4329. auto isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0;
  4330. auto dpi = 0.0;
  4331. if (getDPIForMonitor != nullptr)
  4332. {
  4333. UINT dpiX = 0, dpiY = 0;
  4334. if (SUCCEEDED (getDPIForMonitor (hm, MDT_Default, &dpiX, &dpiY)))
  4335. dpi = (dpiX + dpiY) / 2.0;
  4336. }
  4337. ((Array<MonitorInfo>*) userInfo)->add ({ isMain, info.rcMonitor, info.rcWork, dpi });
  4338. return TRUE;
  4339. }
  4340. void Displays::findDisplays (float masterScale)
  4341. {
  4342. setDPIAwareness();
  4343. Array<MonitorInfo> monitors;
  4344. EnumDisplayMonitors (nullptr, nullptr, &enumMonitorsProc, (LPARAM) &monitors);
  4345. auto globalDPI = getGlobalDPI();
  4346. if (monitors.size() == 0)
  4347. {
  4348. auto windowRect = getWindowScreenRect (GetDesktopWindow());
  4349. monitors.add ({ true, windowRect, windowRect, globalDPI });
  4350. }
  4351. // make sure the first in the list is the main monitor
  4352. for (int i = 1; i < monitors.size(); ++i)
  4353. if (monitors.getReference (i).isMain)
  4354. monitors.swap (i, 0);
  4355. for (auto& monitor : monitors)
  4356. {
  4357. Display d;
  4358. d.isMain = monitor.isMain;
  4359. d.dpi = monitor.dpi;
  4360. if (d.dpi == 0)
  4361. {
  4362. d.dpi = globalDPI;
  4363. d.scale = masterScale;
  4364. }
  4365. else
  4366. {
  4367. d.scale = (d.dpi / USER_DEFAULT_SCREEN_DPI) * (masterScale / Desktop::getDefaultMasterScale());
  4368. }
  4369. d.totalArea = rectangleFromRECT (monitor.totalAreaRect);
  4370. d.userArea = rectangleFromRECT (monitor.workAreaRect);
  4371. displays.add (d);
  4372. }
  4373. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  4374. if (isPerMonitorDPIAwareThread())
  4375. updateToLogical();
  4376. else
  4377. #endif
  4378. {
  4379. for (auto& d : displays)
  4380. {
  4381. d.totalArea /= masterScale;
  4382. d.userArea /= masterScale;
  4383. }
  4384. }
  4385. }
  4386. //==============================================================================
  4387. static HICON extractFileHICON (const File& file)
  4388. {
  4389. WORD iconNum = 0;
  4390. WCHAR name[MAX_PATH * 2];
  4391. file.getFullPathName().copyToUTF16 (name, sizeof (name));
  4392. return ExtractAssociatedIcon ((HINSTANCE) Process::getCurrentModuleInstanceHandle(),
  4393. name, &iconNum);
  4394. }
  4395. Image juce_createIconForFile (const File& file)
  4396. {
  4397. Image image;
  4398. if (auto icon = extractFileHICON (file))
  4399. {
  4400. image = IconConverters::createImageFromHICON (icon);
  4401. DestroyIcon (icon);
  4402. }
  4403. return image;
  4404. }
  4405. //==============================================================================
  4406. class MouseCursor::PlatformSpecificHandle
  4407. {
  4408. public:
  4409. explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type)
  4410. : impl (makeHandle (type)) {}
  4411. explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info)
  4412. : impl (makeHandle (info)) {}
  4413. static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer)
  4414. {
  4415. SetCursor ([&]
  4416. {
  4417. if (handle != nullptr && handle->impl != nullptr && peer != nullptr)
  4418. return handle->impl->getCursor (*peer);
  4419. return LoadCursor (nullptr, IDC_ARROW);
  4420. }());
  4421. }
  4422. private:
  4423. struct Impl
  4424. {
  4425. virtual ~Impl() = default;
  4426. virtual HCURSOR getCursor (ComponentPeer&) = 0;
  4427. };
  4428. class BuiltinImpl : public Impl
  4429. {
  4430. public:
  4431. explicit BuiltinImpl (HCURSOR cursorIn)
  4432. : cursor (cursorIn) {}
  4433. HCURSOR getCursor (ComponentPeer&) override { return cursor; }
  4434. private:
  4435. HCURSOR cursor;
  4436. };
  4437. class ImageImpl : public Impl
  4438. {
  4439. public:
  4440. explicit ImageImpl (const CustomMouseCursorInfo& infoIn) : info (infoIn) {}
  4441. ~ImageImpl() override
  4442. {
  4443. for (auto& pair : cursorsBySize)
  4444. DestroyCursor (pair.second);
  4445. }
  4446. HCURSOR getCursor (ComponentPeer& peer) override
  4447. {
  4448. JUCE_ASSERT_MESSAGE_THREAD;
  4449. static auto getCursorSize = getCursorSizeForPeerFunction();
  4450. const auto size = getCursorSize (peer);
  4451. const auto iter = cursorsBySize.find (size);
  4452. if (iter != cursorsBySize.end())
  4453. return iter->second;
  4454. const auto logicalSize = info.image.getScaledBounds();
  4455. const auto scale = (float) size / (float) unityCursorSize;
  4456. const auto physicalSize = logicalSize * scale;
  4457. const auto& image = info.image.getImage();
  4458. const auto rescaled = image.rescaled (roundToInt ((float) physicalSize.getWidth()),
  4459. roundToInt ((float) physicalSize.getHeight()));
  4460. const auto effectiveScale = rescaled.getWidth() / logicalSize.getWidth();
  4461. const auto hx = jlimit (0, rescaled.getWidth(), roundToInt ((float) info.hotspot.x * effectiveScale));
  4462. const auto hy = jlimit (0, rescaled.getHeight(), roundToInt ((float) info.hotspot.y * effectiveScale));
  4463. return cursorsBySize.emplace (size, IconConverters::createHICONFromImage (rescaled, false, hx, hy)).first->second;
  4464. }
  4465. private:
  4466. const CustomMouseCursorInfo info;
  4467. std::map<int, HCURSOR> cursorsBySize;
  4468. };
  4469. static auto getCursorSizeForPeerFunction() -> int (*) (ComponentPeer&)
  4470. {
  4471. static const auto getDpiForMonitor = []() -> GetDPIForMonitorFunc
  4472. {
  4473. constexpr auto library = "SHCore.dll";
  4474. LoadLibraryA (library);
  4475. if (auto* handle = GetModuleHandleA (library))
  4476. return (GetDPIForMonitorFunc) GetProcAddress (handle, "GetDpiForMonitor");
  4477. return nullptr;
  4478. }();
  4479. static const auto getSystemMetricsForDpi = []() -> GetSystemMetricsForDpiFunc
  4480. {
  4481. constexpr auto library = "User32.dll";
  4482. LoadLibraryA (library);
  4483. if (auto* handle = GetModuleHandleA (library))
  4484. return (GetSystemMetricsForDpiFunc) GetProcAddress (handle, "GetSystemMetricsForDpi");
  4485. return nullptr;
  4486. }();
  4487. if (getDpiForMonitor == nullptr || getSystemMetricsForDpi == nullptr)
  4488. return [] (ComponentPeer&) { return unityCursorSize; };
  4489. return [] (ComponentPeer& p)
  4490. {
  4491. const ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { p.getNativeHandle() };
  4492. UINT dpiX = 0, dpiY = 0;
  4493. if (auto* monitor = MonitorFromWindow ((HWND) p.getNativeHandle(), MONITOR_DEFAULTTONULL))
  4494. if (SUCCEEDED (getDpiForMonitor (monitor, MDT_Default, &dpiX, &dpiY)))
  4495. return getSystemMetricsForDpi (SM_CXCURSOR, dpiX);
  4496. return unityCursorSize;
  4497. };
  4498. }
  4499. static constexpr auto unityCursorSize = 32;
  4500. static std::unique_ptr<Impl> makeHandle (const CustomMouseCursorInfo& info)
  4501. {
  4502. return std::make_unique<ImageImpl> (info);
  4503. }
  4504. static std::unique_ptr<Impl> makeHandle (const MouseCursor::StandardCursorType type)
  4505. {
  4506. LPCTSTR cursorName = IDC_ARROW;
  4507. switch (type)
  4508. {
  4509. case NormalCursor:
  4510. case ParentCursor: break;
  4511. case NoCursor: return std::make_unique<BuiltinImpl> (nullptr);
  4512. case WaitCursor: cursorName = IDC_WAIT; break;
  4513. case IBeamCursor: cursorName = IDC_IBEAM; break;
  4514. case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
  4515. case CrosshairCursor: cursorName = IDC_CROSS; break;
  4516. case LeftRightResizeCursor:
  4517. case LeftEdgeResizeCursor:
  4518. case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
  4519. case UpDownResizeCursor:
  4520. case TopEdgeResizeCursor:
  4521. case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
  4522. case TopLeftCornerResizeCursor:
  4523. case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
  4524. case TopRightCornerResizeCursor:
  4525. case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
  4526. case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
  4527. case DraggingHandCursor:
  4528. {
  4529. static const unsigned char dragHandData[]
  4530. { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
  4531. 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,
  4532. 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
  4533. return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData))), { 8, 7 } });
  4534. }
  4535. case CopyingCursor:
  4536. {
  4537. static const unsigned char copyCursorData[]
  4538. { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0,
  4539. 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,86,91,202,142,
  4540. 12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,252,114,147,74,83,
  4541. 5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 };
  4542. return makeHandle ({ ScaledImage (ImageFileFormat::loadFrom (copyCursorData, sizeof (copyCursorData))), { 1, 3 } });
  4543. }
  4544. case NumStandardCursorTypes: JUCE_FALLTHROUGH
  4545. default:
  4546. jassertfalse; break;
  4547. }
  4548. return std::make_unique<BuiltinImpl> ([&]
  4549. {
  4550. if (auto* c = LoadCursor (nullptr, cursorName))
  4551. return c;
  4552. return LoadCursor (nullptr, IDC_ARROW);
  4553. }());
  4554. }
  4555. std::unique_ptr<Impl> impl;
  4556. };
  4557. //==============================================================================
  4558. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  4559. } // namespace juce