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.

4194 lines
145KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
  18. // these are in the windows SDK, but need to be repeated here for GCC..
  19. #ifndef GET_APPCOMMAND_LPARAM
  20. #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
  21. #define FAPPCOMMAND_MASK 0xF000
  22. #define APPCOMMAND_MEDIA_NEXTTRACK 11
  23. #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
  24. #define APPCOMMAND_MEDIA_STOP 13
  25. #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
  26. #endif
  27. #ifndef WM_APPCOMMAND
  28. #define WM_APPCOMMAND 0x0319
  29. #endif
  30. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  31. #include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h>
  32. #endif
  33. extern void juce_repeatLastProcessPriority();
  34. extern void juce_checkCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp
  35. extern bool juce_isRunningInWine();
  36. typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
  37. extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
  38. static bool shouldDeactivateTitleBar = true;
  39. extern void* getUser32Function (const char*);
  40. //==============================================================================
  41. typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
  42. static UpdateLayeredWinFunc updateLayeredWindow = nullptr;
  43. bool Desktop::canUseSemiTransparentWindows() noexcept
  44. {
  45. if (updateLayeredWindow == nullptr && ! juce_isRunningInWine())
  46. updateLayeredWindow = (UpdateLayeredWinFunc) getUser32Function ("UpdateLayeredWindow");
  47. return updateLayeredWindow != nullptr;
  48. }
  49. //==============================================================================
  50. #ifndef WM_NCPOINTERUPDATE
  51. enum
  52. {
  53. WM_NCPOINTERUPDATE = 0x241,
  54. WM_NCPOINTERDOWN = 0x242,
  55. WM_NCPOINTERUP = 0x243,
  56. WM_POINTERUPDATE = 0x245,
  57. WM_POINTERDOWN = 0x246,
  58. WM_POINTERUP = 0x247,
  59. WM_POINTERENTER = 0x249,
  60. WM_POINTERLEAVE = 0x24A,
  61. WM_POINTERACTIVATE = 0x24B,
  62. WM_POINTERCAPTURECHANGED = 0x24C,
  63. WM_TOUCHHITTESTING = 0x24D,
  64. WM_POINTERWHEEL = 0x24E,
  65. WM_POINTERHWHEEL = 0x24F,
  66. WM_POINTERHITTEST = 0x250
  67. };
  68. enum
  69. {
  70. PT_TOUCH = 0x00000002,
  71. PT_PEN = 0x00000003
  72. };
  73. enum POINTER_BUTTON_CHANGE_TYPE
  74. {
  75. POINTER_CHANGE_NONE,
  76. POINTER_CHANGE_FIRSTBUTTON_DOWN,
  77. POINTER_CHANGE_FIRSTBUTTON_UP,
  78. POINTER_CHANGE_SECONDBUTTON_DOWN,
  79. POINTER_CHANGE_SECONDBUTTON_UP,
  80. POINTER_CHANGE_THIRDBUTTON_DOWN,
  81. POINTER_CHANGE_THIRDBUTTON_UP,
  82. POINTER_CHANGE_FOURTHBUTTON_DOWN,
  83. POINTER_CHANGE_FOURTHBUTTON_UP,
  84. POINTER_CHANGE_FIFTHBUTTON_DOWN,
  85. POINTER_CHANGE_FIFTHBUTTON_UP
  86. };
  87. enum
  88. {
  89. PEN_MASK_NONE = 0x00000000,
  90. PEN_MASK_PRESSURE = 0x00000001,
  91. PEN_MASK_ROTATION = 0x00000002,
  92. PEN_MASK_TILT_X = 0x00000004,
  93. PEN_MASK_TILT_Y = 0x00000008
  94. };
  95. enum
  96. {
  97. TOUCH_MASK_NONE = 0x00000000,
  98. TOUCH_MASK_CONTACTAREA = 0x00000001,
  99. TOUCH_MASK_ORIENTATION = 0x00000002,
  100. TOUCH_MASK_PRESSURE = 0x00000004
  101. };
  102. enum
  103. {
  104. POINTER_FLAG_NONE = 0x00000000,
  105. POINTER_FLAG_NEW = 0x00000001,
  106. POINTER_FLAG_INRANGE = 0x00000002,
  107. POINTER_FLAG_INCONTACT = 0x00000004,
  108. POINTER_FLAG_FIRSTBUTTON = 0x00000010,
  109. POINTER_FLAG_SECONDBUTTON = 0x00000020,
  110. POINTER_FLAG_THIRDBUTTON = 0x00000040,
  111. POINTER_FLAG_FOURTHBUTTON = 0x00000080,
  112. POINTER_FLAG_FIFTHBUTTON = 0x00000100,
  113. POINTER_FLAG_PRIMARY = 0x00002000,
  114. POINTER_FLAG_CONFIDENCE = 0x00004000,
  115. POINTER_FLAG_CANCELED = 0x00008000,
  116. POINTER_FLAG_DOWN = 0x00010000,
  117. POINTER_FLAG_UPDATE = 0x00020000,
  118. POINTER_FLAG_UP = 0x00040000,
  119. POINTER_FLAG_WHEEL = 0x00080000,
  120. POINTER_FLAG_HWHEEL = 0x00100000,
  121. POINTER_FLAG_CAPTURECHANGED = 0x00200000,
  122. POINTER_FLAG_HASTRANSFORM = 0x00400000
  123. };
  124. typedef DWORD POINTER_INPUT_TYPE;
  125. typedef UINT32 POINTER_FLAGS;
  126. typedef UINT32 PEN_FLAGS;
  127. typedef UINT32 PEN_MASK;
  128. typedef UINT32 TOUCH_FLAGS;
  129. typedef UINT32 TOUCH_MASK;
  130. struct POINTER_INFO
  131. {
  132. POINTER_INPUT_TYPE pointerType;
  133. UINT32 pointerId;
  134. UINT32 frameId;
  135. POINTER_FLAGS pointerFlags;
  136. HANDLE sourceDevice;
  137. HWND hwndTarget;
  138. POINT ptPixelLocation;
  139. POINT ptHimetricLocation;
  140. POINT ptPixelLocationRaw;
  141. POINT ptHimetricLocationRaw;
  142. DWORD dwTime;
  143. UINT32 historyCount;
  144. INT32 InputData;
  145. DWORD dwKeyStates;
  146. UINT64 PerformanceCount;
  147. POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
  148. };
  149. struct POINTER_TOUCH_INFO
  150. {
  151. POINTER_INFO pointerInfo;
  152. TOUCH_FLAGS touchFlags;
  153. TOUCH_MASK touchMask;
  154. RECT rcContact;
  155. RECT rcContactRaw;
  156. UINT32 orientation;
  157. UINT32 pressure;
  158. };
  159. struct POINTER_PEN_INFO
  160. {
  161. POINTER_INFO pointerInfo;
  162. PEN_FLAGS penFlags;
  163. PEN_MASK penMask;
  164. UINT32 pressure;
  165. UINT32 rotation;
  166. INT32 tiltX;
  167. INT32 tiltY;
  168. };
  169. #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
  170. #endif
  171. #ifndef MONITOR_DPI_TYPE
  172. enum Monitor_DPI_Type
  173. {
  174. MDT_Effective_DPI = 0,
  175. MDT_Angular_DPI = 1,
  176. MDT_Raw_DPI = 2,
  177. MDT_Default = MDT_Effective_DPI
  178. };
  179. enum Process_DPI_Awareness
  180. {
  181. Process_DPI_Unaware = 0,
  182. Process_System_DPI_Aware = 1,
  183. Process_Per_Monitor_DPI_Aware = 2
  184. };
  185. #endif
  186. typedef BOOL (WINAPI* RegisterTouchWindowFunc) (HWND, ULONG);
  187. typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, TOUCHINPUT*, int);
  188. typedef BOOL (WINAPI* CloseTouchInputHandleFunc) (HTOUCHINPUT);
  189. typedef BOOL (WINAPI* GetGestureInfoFunc) (HGESTUREINFO, GESTUREINFO*);
  190. typedef BOOL (WINAPI* SetProcessDPIAwareFunc)();
  191. typedef BOOL (WINAPI* SetProcessDPIAwarenessFunc) (Process_DPI_Awareness);
  192. typedef HRESULT (WINAPI* GetDPIForMonitorFunc) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*);
  193. static RegisterTouchWindowFunc registerTouchWindow = nullptr;
  194. static GetTouchInputInfoFunc getTouchInputInfo = nullptr;
  195. static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr;
  196. static GetGestureInfoFunc getGestureInfo = nullptr;
  197. static SetProcessDPIAwareFunc setProcessDPIAware = nullptr;
  198. static SetProcessDPIAwarenessFunc setProcessDPIAwareness = nullptr;
  199. static GetDPIForMonitorFunc getDPIForMonitor = nullptr;
  200. static bool hasCheckedForMultiTouch = false;
  201. static bool canUseMultiTouch()
  202. {
  203. if (registerTouchWindow == nullptr && ! hasCheckedForMultiTouch)
  204. {
  205. hasCheckedForMultiTouch = true;
  206. registerTouchWindow = (RegisterTouchWindowFunc) getUser32Function ("RegisterTouchWindow");
  207. getTouchInputInfo = (GetTouchInputInfoFunc) getUser32Function ("GetTouchInputInfo");
  208. closeTouchInputHandle = (CloseTouchInputHandleFunc) getUser32Function ("CloseTouchInputHandle");
  209. getGestureInfo = (GetGestureInfoFunc) getUser32Function ("GetGestureInfo");
  210. }
  211. return registerTouchWindow != nullptr;
  212. }
  213. typedef BOOL (WINAPI* GetPointerTypeFunc) (UINT32, POINTER_INPUT_TYPE*);
  214. typedef BOOL (WINAPI* GetPointerTouchInfoFunc) (UINT32, POINTER_TOUCH_INFO*);
  215. typedef BOOL (WINAPI* GetPointerPenInfoFunc) (UINT32, POINTER_PEN_INFO*);
  216. static GetPointerTypeFunc getPointerTypeFunction = nullptr;
  217. static GetPointerTouchInfoFunc getPointerTouchInfo = nullptr;
  218. static GetPointerPenInfoFunc getPointerPenInfo = nullptr;
  219. static bool canUsePointerAPI = false;
  220. static void checkForPointerAPI()
  221. {
  222. getPointerTypeFunction = (GetPointerTypeFunc) getUser32Function ("GetPointerType");
  223. getPointerTouchInfo = (GetPointerTouchInfoFunc) getUser32Function ("GetPointerTouchInfo");
  224. getPointerPenInfo = (GetPointerPenInfoFunc) getUser32Function ("GetPointerPenInfo");
  225. canUsePointerAPI = (getPointerTypeFunction != nullptr
  226. && getPointerTouchInfo != nullptr
  227. && getPointerPenInfo != nullptr);
  228. }
  229. static Rectangle<int> rectangleFromRECT (const RECT& r) noexcept
  230. {
  231. return Rectangle<int>::leftTopRightBottom ((int) r.left, (int) r.top, (int) r.right, (int) r.bottom);
  232. }
  233. static void setWindowPos (HWND hwnd, Rectangle<int> bounds, UINT flags)
  234. {
  235. SetWindowPos (hwnd, 0, bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), flags);
  236. }
  237. static RECT getWindowRect (HWND hwnd)
  238. {
  239. RECT r;
  240. GetWindowRect (hwnd, &r);
  241. return r;
  242. }
  243. static void setWindowZOrder (HWND hwnd, HWND insertAfter)
  244. {
  245. SetWindowPos (hwnd, insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  246. }
  247. //==============================================================================
  248. static void setDPIAwareness()
  249. {
  250. #if ! JUCE_DISABLE_WIN32_DPI_AWARENESS
  251. if (JUCEApplicationBase::isStandaloneApp())
  252. {
  253. if (setProcessDPIAwareness == nullptr)
  254. {
  255. HMODULE shcoreModule = GetModuleHandleA ("SHCore.dll");
  256. if (shcoreModule != 0)
  257. {
  258. setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness");
  259. getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor");
  260. if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr
  261. // && SUCCEEDED (setProcessDPIAwareness (Process_Per_Monitor_DPI_Aware)))
  262. && SUCCEEDED (setProcessDPIAwareness (Process_System_DPI_Aware))) // (keep using this mode temporarily..)
  263. return;
  264. }
  265. if (setProcessDPIAware == nullptr)
  266. {
  267. setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware");
  268. if (setProcessDPIAware != nullptr)
  269. setProcessDPIAware();
  270. }
  271. }
  272. }
  273. #endif
  274. }
  275. static double getGlobalDPI()
  276. {
  277. setDPIAwareness();
  278. HDC dc = GetDC (0);
  279. const double dpi = (GetDeviceCaps (dc, LOGPIXELSX)
  280. + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0;
  281. ReleaseDC (0, dc);
  282. return dpi;
  283. }
  284. double Desktop::getDefaultMasterScale()
  285. {
  286. return JUCEApplicationBase::isStandaloneApp() ? getGlobalDPI() / 96.0
  287. : 1.0;
  288. }
  289. //==============================================================================
  290. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  291. {
  292. return upright;
  293. }
  294. int64 getMouseEventTime()
  295. {
  296. static int64 eventTimeOffset = 0;
  297. static LONG lastMessageTime = 0;
  298. const LONG thisMessageTime = GetMessageTime();
  299. if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
  300. {
  301. lastMessageTime = thisMessageTime;
  302. eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
  303. }
  304. return eventTimeOffset + thisMessageTime;
  305. }
  306. //==============================================================================
  307. const int extendedKeyModifier = 0x10000;
  308. const int KeyPress::spaceKey = VK_SPACE;
  309. const int KeyPress::returnKey = VK_RETURN;
  310. const int KeyPress::escapeKey = VK_ESCAPE;
  311. const int KeyPress::backspaceKey = VK_BACK;
  312. const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
  313. const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
  314. const int KeyPress::tabKey = VK_TAB;
  315. const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
  316. const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
  317. const int KeyPress::upKey = VK_UP | extendedKeyModifier;
  318. const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
  319. const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
  320. const int KeyPress::endKey = VK_END | extendedKeyModifier;
  321. const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
  322. const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
  323. const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
  324. const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
  325. const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
  326. const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
  327. const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
  328. const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
  329. const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
  330. const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
  331. const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
  332. const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
  333. const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
  334. const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
  335. const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
  336. const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
  337. const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
  338. const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
  339. const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
  340. const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
  341. const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
  342. const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
  343. const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
  344. const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
  345. const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
  346. const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
  347. const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
  348. const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
  349. const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
  350. const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
  351. const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
  352. const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
  353. const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
  354. const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
  355. const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
  356. const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
  357. const int KeyPress::playKey = 0x30000;
  358. const int KeyPress::stopKey = 0x30001;
  359. const int KeyPress::fastForwardKey = 0x30002;
  360. const int KeyPress::rewindKey = 0x30003;
  361. //==============================================================================
  362. class WindowsBitmapImage : public ImagePixelData
  363. {
  364. public:
  365. WindowsBitmapImage (const Image::PixelFormat format,
  366. const int w, const int h, const bool clearImage)
  367. : ImagePixelData (format, w, h)
  368. {
  369. jassert (format == Image::RGB || format == Image::ARGB);
  370. static bool alwaysUse32Bits = isGraphicsCard32Bit(); // NB: for 32-bit cards, it's faster to use a 32-bit image.
  371. pixelStride = (alwaysUse32Bits || format == Image::ARGB) ? 4 : 3;
  372. lineStride = -((w * pixelStride + 3) & ~3);
  373. zerostruct (bitmapInfo);
  374. bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
  375. bitmapInfo.bV4Width = w;
  376. bitmapInfo.bV4Height = h;
  377. bitmapInfo.bV4Planes = 1;
  378. bitmapInfo.bV4CSType = 1;
  379. bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
  380. if (format == Image::ARGB)
  381. {
  382. bitmapInfo.bV4AlphaMask = 0xff000000;
  383. bitmapInfo.bV4RedMask = 0xff0000;
  384. bitmapInfo.bV4GreenMask = 0xff00;
  385. bitmapInfo.bV4BlueMask = 0xff;
  386. bitmapInfo.bV4V4Compression = BI_BITFIELDS;
  387. }
  388. else
  389. {
  390. bitmapInfo.bV4V4Compression = BI_RGB;
  391. }
  392. HDC dc = GetDC (0);
  393. hdc = CreateCompatibleDC (dc);
  394. ReleaseDC (0, dc);
  395. SetMapMode (hdc, MM_TEXT);
  396. hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
  397. (void**) &bitmapData, 0, 0);
  398. previousBitmap = SelectObject (hdc, hBitmap);
  399. if (format == Image::ARGB && clearImage)
  400. zeromem (bitmapData, (size_t) std::abs (h * lineStride));
  401. imageData = bitmapData - (lineStride * (h - 1));
  402. }
  403. ~WindowsBitmapImage()
  404. {
  405. SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
  406. DeleteDC (hdc);
  407. DeleteObject (hBitmap);
  408. }
  409. ImageType* createType() const override { return new NativeImageType(); }
  410. LowLevelGraphicsContext* createLowLevelContext() override
  411. {
  412. sendDataChangeMessage();
  413. return new LowLevelGraphicsSoftwareRenderer (Image (this));
  414. }
  415. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
  416. {
  417. bitmap.data = imageData + x * pixelStride + y * lineStride;
  418. bitmap.pixelFormat = pixelFormat;
  419. bitmap.lineStride = lineStride;
  420. bitmap.pixelStride = pixelStride;
  421. if (mode != Image::BitmapData::readOnly)
  422. sendDataChangeMessage();
  423. }
  424. ImagePixelData::Ptr clone() override
  425. {
  426. WindowsBitmapImage* im = new WindowsBitmapImage (pixelFormat, width, height, false);
  427. for (int i = 0; i < height; ++i)
  428. memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
  429. return im;
  430. }
  431. void blitToWindow (HWND hwnd, HDC dc, const bool transparent,
  432. const int x, const int y,
  433. const uint8 updateLayeredWindowAlpha) noexcept
  434. {
  435. SetMapMode (dc, MM_TEXT);
  436. if (transparent)
  437. {
  438. RECT windowBounds = getWindowRect (hwnd);
  439. POINT p = { -x, -y };
  440. POINT pos = { windowBounds.left, windowBounds.top };
  441. SIZE size = { windowBounds.right - windowBounds.left,
  442. windowBounds.bottom - windowBounds.top };
  443. BLENDFUNCTION bf;
  444. bf.AlphaFormat = 1 /*AC_SRC_ALPHA*/;
  445. bf.BlendFlags = 0;
  446. bf.BlendOp = AC_SRC_OVER;
  447. bf.SourceConstantAlpha = updateLayeredWindowAlpha;
  448. updateLayeredWindow (hwnd, 0, &pos, &size, hdc, &p, 0, &bf, 2 /*ULW_ALPHA*/);
  449. }
  450. else
  451. {
  452. StretchDIBits (dc,
  453. x, y, width, height,
  454. 0, 0, width, height,
  455. bitmapData, (const BITMAPINFO*) &bitmapInfo,
  456. DIB_RGB_COLORS, SRCCOPY);
  457. }
  458. }
  459. HBITMAP hBitmap;
  460. HGDIOBJ previousBitmap;
  461. BITMAPV4HEADER bitmapInfo;
  462. HDC hdc;
  463. uint8* bitmapData;
  464. int pixelStride, lineStride;
  465. uint8* imageData;
  466. private:
  467. static bool isGraphicsCard32Bit()
  468. {
  469. HDC dc = GetDC (0);
  470. const int bitsPerPixel = GetDeviceCaps (dc, BITSPIXEL);
  471. ReleaseDC (0, dc);
  472. return bitsPerPixel > 24;
  473. }
  474. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage)
  475. };
  476. //==============================================================================
  477. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  478. {
  479. HWND hwnd = (HWND) nativeWindowHandle;
  480. RECT r = getWindowRect (hwnd);
  481. const int w = r.right - r.left;
  482. const int h = r.bottom - r.top;
  483. WindowsBitmapImage* nativeBitmap = new WindowsBitmapImage (Image::RGB, w, h, true);
  484. Image bitmap (nativeBitmap);
  485. HDC dc = GetDC (hwnd);
  486. BitBlt (nativeBitmap->hdc, 0, 0, w, h, dc, 0, 0, SRCCOPY);
  487. ReleaseDC (hwnd, dc);
  488. return SoftwareImageType().convert (bitmap);
  489. }
  490. //==============================================================================
  491. namespace IconConverters
  492. {
  493. Image createImageFromHBITMAP (HBITMAP bitmap)
  494. {
  495. Image im;
  496. if (bitmap != 0)
  497. {
  498. BITMAP bm;
  499. if (GetObject (bitmap, sizeof (BITMAP), &bm)
  500. && bm.bmWidth > 0 && bm.bmHeight > 0)
  501. {
  502. HDC tempDC = GetDC (0);
  503. HDC dc = CreateCompatibleDC (tempDC);
  504. ReleaseDC (0, tempDC);
  505. SelectObject (dc, bitmap);
  506. im = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
  507. Image::BitmapData imageData (im, Image::BitmapData::writeOnly);
  508. for (int y = bm.bmHeight; --y >= 0;)
  509. {
  510. for (int x = bm.bmWidth; --x >= 0;)
  511. {
  512. COLORREF col = GetPixel (dc, x, y);
  513. imageData.setPixelColour (x, y, Colour ((uint8) GetRValue (col),
  514. (uint8) GetGValue (col),
  515. (uint8) GetBValue (col)));
  516. }
  517. }
  518. DeleteDC (dc);
  519. }
  520. }
  521. return im;
  522. }
  523. Image createImageFromHICON (HICON icon)
  524. {
  525. ICONINFO info;
  526. if (GetIconInfo (icon, &info))
  527. {
  528. Image mask (createImageFromHBITMAP (info.hbmMask));
  529. Image image (createImageFromHBITMAP (info.hbmColor));
  530. if (mask.isValid() && image.isValid())
  531. {
  532. for (int y = image.getHeight(); --y >= 0;)
  533. {
  534. for (int x = image.getWidth(); --x >= 0;)
  535. {
  536. const float brightness = mask.getPixelAt (x, y).getBrightness();
  537. if (brightness > 0.0f)
  538. image.multiplyAlphaAt (x, y, 1.0f - brightness);
  539. }
  540. }
  541. return image;
  542. }
  543. }
  544. return Image();
  545. }
  546. HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
  547. {
  548. WindowsBitmapImage* nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
  549. Image bitmap (nativeBitmap);
  550. {
  551. Graphics g (bitmap);
  552. g.drawImageAt (image, 0, 0);
  553. }
  554. HBITMAP mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, 0);
  555. ICONINFO info;
  556. info.fIcon = isIcon;
  557. info.xHotspot = (DWORD) hotspotX;
  558. info.yHotspot = (DWORD) hotspotY;
  559. info.hbmMask = mask;
  560. info.hbmColor = nativeBitmap->hBitmap;
  561. HICON hi = CreateIconIndirect (&info);
  562. DeleteObject (mask);
  563. return hi;
  564. }
  565. }
  566. //==============================================================================
  567. class
  568. #if (! JUCE_MINGW)
  569. __declspec (uuid ("37c994e7-432b-4834-a2f7-dce1f13b834b"))
  570. #endif
  571. ITipInvocation : public IUnknown
  572. {
  573. public:
  574. static const CLSID clsid;
  575. virtual ::HRESULT STDMETHODCALLTYPE Toggle (::HWND wnd) = 0;
  576. };
  577. #if JUCE_MINGW || (! (defined (_MSC_VER) || defined (__uuidof)))
  578. template <>
  579. struct UUIDGetter<ITipInvocation>
  580. {
  581. static CLSID get()
  582. {
  583. GUID g = {0x37c994e7, 0x432b, 0x4834, {0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b}};
  584. return g;
  585. }
  586. };
  587. #endif
  588. const CLSID ITipInvocation::clsid = {0x4CE576FA, 0x83DC, 0x4f88, {0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76}};
  589. //==============================================================================
  590. class OnScreenKeyboard : public DeletedAtShutdown,
  591. private Timer
  592. {
  593. public:
  594. void activate()
  595. {
  596. shouldBeActive = true;
  597. startTimer (10);
  598. }
  599. void deactivate()
  600. {
  601. shouldBeActive = false;
  602. startTimer (10);
  603. }
  604. juce_DeclareSingleton_SingleThreaded (OnScreenKeyboard, true)
  605. private:
  606. OnScreenKeyboard()
  607. : shouldBeActive (false), reentrant (false)
  608. {
  609. tipInvocation.CoCreateInstance (ITipInvocation::clsid, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER);
  610. }
  611. void timerCallback() override
  612. {
  613. stopTimer();
  614. if (reentrant || tipInvocation == nullptr) return;
  615. const ScopedValueSetter<bool> setter (reentrant, true, false);
  616. const bool isActive = isVisible();
  617. if (isActive == shouldBeActive) return;
  618. if (! isActive)
  619. {
  620. tipInvocation->Toggle(::GetDesktopWindow());
  621. }
  622. else
  623. {
  624. ::HWND hwnd = ::FindWindow (L"IPTip_Main_Window", NULL);
  625. if (hwnd != nullptr)
  626. ::PostMessage(hwnd, WM_SYSCOMMAND, (int) SC_CLOSE, 0);
  627. }
  628. }
  629. bool isVisible()
  630. {
  631. ::HWND hwnd = ::FindWindow (L"IPTip_Main_Window", NULL);
  632. if (hwnd != nullptr)
  633. {
  634. ::LONG style = ::GetWindowLong (hwnd, GWL_STYLE);
  635. return ((style & WS_DISABLED) == 0 && (style & WS_VISIBLE) != 0);
  636. }
  637. return false;
  638. }
  639. bool shouldBeActive, reentrant;
  640. ComSmartPtr<ITipInvocation> tipInvocation;
  641. };
  642. juce_ImplementSingleton_SingleThreaded (OnScreenKeyboard)
  643. //==============================================================================
  644. struct HSTRING_PRIVATE;
  645. typedef HSTRING_PRIVATE* HSTRING;
  646. class IInspectable : public IUnknown
  647. {
  648. public:
  649. virtual ::HRESULT STDMETHODCALLTYPE GetIids (ULONG* ,IID**) = 0;
  650. virtual ::HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) = 0;
  651. virtual ::HRESULT STDMETHODCALLTYPE GetTrustLevel(void*) = 0;
  652. };
  653. class
  654. #if (! JUCE_MINGW)
  655. __declspec (uuid ("3694dbf9-8f68-44be-8ff5-195c98ede8a6"))
  656. #endif
  657. IUIViewSettingsInterop : public IInspectable
  658. {
  659. public:
  660. virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND hwnd, REFIID riid, void **ppv) = 0;
  661. };
  662. class
  663. #if (! JUCE_MINGW)
  664. __declspec (uuid ("C63657F6-8850-470D-88F8-455E16EA2C26"))
  665. #endif
  666. IUIViewSettings : public IInspectable
  667. {
  668. public:
  669. enum UserInteractionMode
  670. {
  671. Mouse = 0,
  672. Touch = 1
  673. };
  674. virtual HRESULT STDMETHODCALLTYPE GetUserInteractionMode (UserInteractionMode *value) = 0;
  675. };
  676. #if JUCE_MINGW || (! (defined (_MSC_VER) || defined (__uuidof)))
  677. template <>
  678. struct UUIDGetter<IUIViewSettingsInterop>
  679. {
  680. static CLSID get()
  681. {
  682. GUID g = {0x3694dbf9, 0x8f68, 0x44be, {0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}};
  683. return g;
  684. }
  685. };
  686. template <>
  687. struct UUIDGetter<IUIViewSettings>
  688. {
  689. static CLSID get()
  690. {
  691. GUID g = {0xC63657F6, 0x8850, 0x470D, {0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26}};
  692. return g;
  693. }
  694. };
  695. #endif
  696. class UWPUIViewSettings
  697. {
  698. public:
  699. UWPUIViewSettings()
  700. {
  701. ComBaseModule dll (L"api-ms-win-core-winrt-l1-1-0");
  702. if (dll.h != nullptr)
  703. {
  704. roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (dll.h, "RoInitialize");
  705. roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (dll.h, "RoGetActivationFactory");
  706. createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (dll.h, "WindowsCreateString");
  707. deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (dll.h, "WindowsDeleteString");
  708. if (roInitialize == nullptr || roGetActivationFactory == nullptr
  709. || createHString == nullptr || deleteHString == nullptr)
  710. return;
  711. ::HRESULT status = roInitialize (1);
  712. if (status != S_OK && status != S_FALSE && status != 0x80010106L)
  713. return;
  714. LPCWSTR uwpClassName = L"Windows.UI.ViewManagement.UIViewSettings";
  715. HSTRING uwpClassId;
  716. if (createHString (uwpClassName, (::UINT32) wcslen (uwpClassName), &uwpClassId) != S_OK
  717. || uwpClassId == nullptr)
  718. return;
  719. status = roGetActivationFactory (uwpClassId, __uuidof (IUIViewSettingsInterop), (void**) viewSettingsInterop.resetAndGetPointerAddress());
  720. deleteHString (uwpClassId);
  721. if (status != S_OK || viewSettingsInterop == nullptr)
  722. return;
  723. // move dll into member var
  724. comBaseDLL = static_cast<ComBaseModule&&> (dll);
  725. }
  726. }
  727. bool isTabletModeActivatedForWindow (::HWND hWnd) const
  728. {
  729. if (viewSettingsInterop == nullptr)
  730. return false;
  731. ComSmartPtr<IUIViewSettings> viewSettings;
  732. if (viewSettingsInterop->GetForWindow(hWnd, __uuidof (IUIViewSettings), (void**) viewSettings.resetAndGetPointerAddress()) == S_OK
  733. && viewSettings != nullptr)
  734. {
  735. IUIViewSettings::UserInteractionMode mode;
  736. if (viewSettings->GetUserInteractionMode (&mode) == S_OK)
  737. return (mode == IUIViewSettings::Touch);
  738. }
  739. return false;
  740. }
  741. private:
  742. //==============================================================================
  743. struct ComBaseModule
  744. {
  745. ::HMODULE h;
  746. ComBaseModule() : h (nullptr) {}
  747. ComBaseModule(LPCWSTR libraryName) : h (::LoadLibrary (libraryName)) {}
  748. ComBaseModule(ComBaseModule&& o) : h (o.h) { o.h = nullptr; }
  749. ~ComBaseModule() { if (h != nullptr) ::FreeLibrary (h); h = nullptr; }
  750. ComBaseModule& operator=(ComBaseModule&& o) { h = o.h; o.h = nullptr; return *this; }
  751. };
  752. typedef HRESULT (WINAPI* RoInitializeFuncPtr) (int);
  753. typedef HRESULT (WINAPI* RoGetActivationFactoryFuncPtr) (HSTRING, REFIID, void**);
  754. typedef HRESULT (WINAPI* WindowsCreateStringFuncPtr) (LPCWSTR,UINT32, HSTRING*);
  755. typedef HRESULT (WINAPI* WindowsDeleteStringFuncPtr) (HSTRING);
  756. ComBaseModule comBaseDLL;
  757. ComSmartPtr<IUIViewSettingsInterop> viewSettingsInterop;
  758. RoInitializeFuncPtr roInitialize;
  759. RoGetActivationFactoryFuncPtr roGetActivationFactory;
  760. WindowsCreateStringFuncPtr createHString;
  761. WindowsDeleteStringFuncPtr deleteHString;
  762. };
  763. //==============================================================================
  764. class HWNDComponentPeer : public ComponentPeer,
  765. private Timer
  766. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  767. , public ModifierKeyReceiver
  768. #endif
  769. {
  770. public:
  771. enum RenderingEngineType
  772. {
  773. softwareRenderingEngine = 0,
  774. direct2DRenderingEngine
  775. };
  776. //==============================================================================
  777. HWNDComponentPeer (Component& comp, const int windowStyleFlags, HWND parent, bool nonRepainting)
  778. : ComponentPeer (comp, windowStyleFlags),
  779. dontRepaint (nonRepainting),
  780. parentToAddTo (parent),
  781. currentRenderingEngine (softwareRenderingEngine),
  782. lastPaintTime (0),
  783. lastMagnifySize (0),
  784. fullScreen (false),
  785. isDragging (false),
  786. isMouseOver (false),
  787. hasCreatedCaret (false),
  788. constrainerIsResizing (false),
  789. currentWindowIcon (0),
  790. dropTarget (nullptr),
  791. updateLayeredWindowAlpha (255)
  792. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  793. , modProvider (nullptr)
  794. #endif
  795. {
  796. callFunctionIfNotLocked (&createWindowCallback, this);
  797. setTitle (component.getName());
  798. if ((windowStyleFlags & windowHasDropShadow) != 0
  799. && Desktop::canUseSemiTransparentWindows()
  800. && ((! hasTitleBar()) || SystemStats::getOperatingSystemType() < SystemStats::WinVista))
  801. {
  802. shadower = component.getLookAndFeel().createDropShadowerForComponent (&component);
  803. if (shadower != nullptr)
  804. shadower->setOwner (&component);
  805. }
  806. // make sure that the on-screen keyboard code is loaded
  807. OnScreenKeyboard::getInstance();
  808. }
  809. ~HWNDComponentPeer()
  810. {
  811. shadower = nullptr;
  812. // do this before the next bit to avoid messages arriving for this window
  813. // before it's destroyed
  814. JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
  815. callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
  816. if (currentWindowIcon != 0)
  817. DestroyIcon (currentWindowIcon);
  818. if (dropTarget != nullptr)
  819. {
  820. dropTarget->clear();
  821. dropTarget->Release();
  822. dropTarget = nullptr;
  823. }
  824. #if JUCE_DIRECT2D
  825. direct2DContext = nullptr;
  826. #endif
  827. }
  828. //==============================================================================
  829. void* getNativeHandle() const override { return hwnd; }
  830. void setVisible (bool shouldBeVisible) override
  831. {
  832. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  833. if (shouldBeVisible)
  834. InvalidateRect (hwnd, 0, 0);
  835. else
  836. lastPaintTime = 0;
  837. }
  838. void setTitle (const String& title) override
  839. {
  840. // Unfortunately some ancient bits of win32 mean you can only perform this operation from the message thread.
  841. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  842. SetWindowText (hwnd, title.toWideCharPointer());
  843. }
  844. void repaintNowIfTransparent()
  845. {
  846. if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
  847. handlePaintMessage();
  848. }
  849. void updateBorderSize()
  850. {
  851. WINDOWINFO info;
  852. info.cbSize = sizeof (info);
  853. if (GetWindowInfo (hwnd, &info))
  854. windowBorder = BorderSize<int> (info.rcClient.top - info.rcWindow.top,
  855. info.rcClient.left - info.rcWindow.left,
  856. info.rcWindow.bottom - info.rcClient.bottom,
  857. info.rcWindow.right - info.rcClient.right);
  858. #if JUCE_DIRECT2D
  859. if (direct2DContext != nullptr)
  860. direct2DContext->resized();
  861. #endif
  862. }
  863. void setBounds (const Rectangle<int>& bounds, bool isNowFullScreen) override
  864. {
  865. fullScreen = isNowFullScreen;
  866. Rectangle<int> newBounds (windowBorder.addedTo (bounds));
  867. if (isUsingUpdateLayeredWindow())
  868. {
  869. if (HWND parentHwnd = GetParent (hwnd))
  870. {
  871. RECT parentRect = getWindowRect (parentHwnd);
  872. newBounds.translate (parentRect.left, parentRect.top);
  873. }
  874. }
  875. const Rectangle<int> oldBounds (getBounds());
  876. const bool hasMoved = (oldBounds.getPosition() != bounds.getPosition());
  877. const bool hasResized = (oldBounds.getWidth() != bounds.getWidth()
  878. || oldBounds.getHeight() != bounds.getHeight());
  879. DWORD flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
  880. if (! hasMoved) flags |= SWP_NOMOVE;
  881. if (! hasResized) flags |= SWP_NOSIZE;
  882. setWindowPos (hwnd, newBounds, flags);
  883. if (hasResized && isValidPeer (this))
  884. {
  885. updateBorderSize();
  886. repaintNowIfTransparent();
  887. }
  888. }
  889. Rectangle<int> getBounds() const override
  890. {
  891. Rectangle<int> bounds (rectangleFromRECT (getWindowRect (hwnd)));
  892. if (HWND parentH = GetParent (hwnd))
  893. {
  894. RECT r = getWindowRect (parentH);
  895. bounds.translate (-r.left, -r.top);
  896. }
  897. return windowBorder.subtractedFrom (bounds);
  898. }
  899. Point<int> getScreenPosition() const
  900. {
  901. RECT r = getWindowRect (hwnd);
  902. return Point<int> (r.left + windowBorder.getLeft(),
  903. r.top + windowBorder.getTop());
  904. }
  905. Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getScreenPosition().toFloat(); }
  906. Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getScreenPosition().toFloat(); }
  907. void setAlpha (float newAlpha) override
  908. {
  909. const uint8 intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
  910. if (component.isOpaque())
  911. {
  912. if (newAlpha < 1.0f)
  913. {
  914. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  915. SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
  916. }
  917. else
  918. {
  919. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
  920. RedrawWindow (hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
  921. }
  922. }
  923. else
  924. {
  925. updateLayeredWindowAlpha = intAlpha;
  926. component.repaint();
  927. }
  928. }
  929. void setMinimised (bool shouldBeMinimised) override
  930. {
  931. if (shouldBeMinimised != isMinimised())
  932. ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL);
  933. }
  934. bool isMinimised() const override
  935. {
  936. WINDOWPLACEMENT wp;
  937. wp.length = sizeof (WINDOWPLACEMENT);
  938. GetWindowPlacement (hwnd, &wp);
  939. return wp.showCmd == SW_SHOWMINIMIZED;
  940. }
  941. void setFullScreen (bool shouldBeFullScreen) override
  942. {
  943. setMinimised (false);
  944. if (isFullScreen() != shouldBeFullScreen)
  945. {
  946. if (constrainer != nullptr)
  947. constrainer->resizeStart();
  948. fullScreen = shouldBeFullScreen;
  949. const WeakReference<Component> deletionChecker (&component);
  950. if (! fullScreen)
  951. {
  952. const Rectangle<int> boundsCopy (lastNonFullscreenBounds);
  953. if (hasTitleBar())
  954. ShowWindow (hwnd, SW_SHOWNORMAL);
  955. if (! boundsCopy.isEmpty())
  956. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, boundsCopy), false);
  957. }
  958. else
  959. {
  960. if (hasTitleBar())
  961. ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  962. else
  963. SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
  964. }
  965. if (deletionChecker != nullptr)
  966. handleMovedOrResized();
  967. if (constrainer != nullptr)
  968. constrainer->resizeEnd();
  969. }
  970. }
  971. bool isFullScreen() const override
  972. {
  973. if (! hasTitleBar())
  974. return fullScreen;
  975. WINDOWPLACEMENT wp;
  976. wp.length = sizeof (wp);
  977. GetWindowPlacement (hwnd, &wp);
  978. return wp.showCmd == SW_SHOWMAXIMIZED;
  979. }
  980. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  981. {
  982. RECT r = getWindowRect (hwnd);
  983. if (! (isPositiveAndBelow (localPos.x, (int) (r.right - r.left))
  984. && isPositiveAndBelow (localPos.y, (int) (r.bottom - r.top))))
  985. return false;
  986. POINT p = { localPos.x + r.left + windowBorder.getLeft(),
  987. localPos.y + r.top + windowBorder.getTop() };
  988. HWND w = WindowFromPoint (p);
  989. return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
  990. }
  991. BorderSize<int> getFrameSize() const override
  992. {
  993. return windowBorder;
  994. }
  995. bool setAlwaysOnTop (bool alwaysOnTop) override
  996. {
  997. const bool oldDeactivate = shouldDeactivateTitleBar;
  998. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  999. setWindowZOrder (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST);
  1000. shouldDeactivateTitleBar = oldDeactivate;
  1001. if (shadower != nullptr)
  1002. handleBroughtToFront();
  1003. return true;
  1004. }
  1005. void toFront (bool makeActive) override
  1006. {
  1007. setMinimised (false);
  1008. const bool oldDeactivate = shouldDeactivateTitleBar;
  1009. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1010. callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
  1011. shouldDeactivateTitleBar = oldDeactivate;
  1012. if (! makeActive)
  1013. {
  1014. // in this case a broughttofront call won't have occurred, so do it now..
  1015. handleBroughtToFront();
  1016. }
  1017. }
  1018. void toBehind (ComponentPeer* other) override
  1019. {
  1020. if (HWNDComponentPeer* const otherPeer = dynamic_cast<HWNDComponentPeer*> (other))
  1021. {
  1022. setMinimised (false);
  1023. // Must be careful not to try to put a topmost window behind a normal one, or Windows
  1024. // promotes the normal one to be topmost!
  1025. if (component.isAlwaysOnTop() == otherPeer->getComponent().isAlwaysOnTop())
  1026. setWindowZOrder (hwnd, otherPeer->hwnd);
  1027. else if (otherPeer->getComponent().isAlwaysOnTop())
  1028. setWindowZOrder (hwnd, HWND_TOP);
  1029. }
  1030. else
  1031. {
  1032. jassertfalse; // wrong type of window?
  1033. }
  1034. }
  1035. bool isFocused() const override
  1036. {
  1037. return callFunctionIfNotLocked (&getFocusCallback, 0) == (void*) hwnd;
  1038. }
  1039. void grabFocus() override
  1040. {
  1041. const bool oldDeactivate = shouldDeactivateTitleBar;
  1042. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  1043. callFunctionIfNotLocked (&setFocusCallback, hwnd);
  1044. shouldDeactivateTitleBar = oldDeactivate;
  1045. }
  1046. void textInputRequired (Point<int>, TextInputTarget&) override
  1047. {
  1048. if (! hasCreatedCaret)
  1049. {
  1050. hasCreatedCaret = true;
  1051. CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
  1052. }
  1053. ShowCaret (hwnd);
  1054. SetCaretPos (0, 0);
  1055. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1056. OnScreenKeyboard::getInstance()->activate();
  1057. }
  1058. void dismissPendingTextInput() override
  1059. {
  1060. imeHandler.handleSetContext (hwnd, false);
  1061. if (uwpViewSettings.isTabletModeActivatedForWindow (hwnd))
  1062. OnScreenKeyboard::getInstance()->deactivate();
  1063. }
  1064. void repaint (const Rectangle<int>& area) override
  1065. {
  1066. const RECT r = { area.getX(), area.getY(), area.getRight(), area.getBottom() };
  1067. InvalidateRect (hwnd, &r, FALSE);
  1068. }
  1069. void performAnyPendingRepaintsNow() override
  1070. {
  1071. if (component.isVisible())
  1072. {
  1073. WeakReference<Component> localRef (&component);
  1074. MSG m;
  1075. if (isUsingUpdateLayeredWindow() || PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE))
  1076. if (localRef != nullptr) // (the PeekMessage call can dispatch messages, which may delete this comp)
  1077. handlePaintMessage();
  1078. }
  1079. }
  1080. //==============================================================================
  1081. static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
  1082. {
  1083. if (h != 0 && JuceWindowIdentifier::isJUCEWindow (h))
  1084. return (HWNDComponentPeer*) GetWindowLongPtr (h, 8);
  1085. return nullptr;
  1086. }
  1087. //==============================================================================
  1088. bool isInside (HWND h) const noexcept
  1089. {
  1090. return GetAncestor (hwnd, GA_ROOT) == h;
  1091. }
  1092. //==============================================================================
  1093. static bool isKeyDown (const int key) noexcept { return (GetAsyncKeyState (key) & 0x8000) != 0; }
  1094. static void updateKeyModifiers() noexcept
  1095. {
  1096. int keyMods = 0;
  1097. if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
  1098. if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
  1099. if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
  1100. // workaround: Windows maps AltGr to left-Ctrl + right-Alt.
  1101. if (isKeyDown (VK_RMENU) && !isKeyDown (VK_RCONTROL))
  1102. {
  1103. keyMods = (keyMods & ~ModifierKeys::ctrlModifier) | ModifierKeys::altModifier;
  1104. }
  1105. currentModifiers = currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
  1106. }
  1107. static void updateModifiersFromWParam (const WPARAM wParam)
  1108. {
  1109. int mouseMods = 0;
  1110. if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
  1111. if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
  1112. if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
  1113. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  1114. updateKeyModifiers();
  1115. }
  1116. //==============================================================================
  1117. bool dontRepaint;
  1118. static ModifierKeys currentModifiers;
  1119. static ModifierKeys modifiersAtLastCallback;
  1120. //==============================================================================
  1121. class JuceDropTarget : public ComBaseClassHelper<IDropTarget>
  1122. {
  1123. public:
  1124. JuceDropTarget (HWNDComponentPeer& p) : ownerInfo (new OwnerInfo (p)) {}
  1125. void clear()
  1126. {
  1127. ownerInfo = nullptr;
  1128. }
  1129. JUCE_COMRESULT DragEnter (IDataObject* pDataObject, DWORD grfKeyState, POINTL mousePos, DWORD* pdwEffect)
  1130. {
  1131. HRESULT hr = updateFileList (pDataObject);
  1132. if (FAILED (hr))
  1133. return hr;
  1134. return DragOver (grfKeyState, mousePos, pdwEffect);
  1135. }
  1136. JUCE_COMRESULT DragLeave()
  1137. {
  1138. if (ownerInfo == nullptr)
  1139. return S_FALSE;
  1140. ownerInfo->owner.handleDragExit (ownerInfo->dragInfo);
  1141. return S_OK;
  1142. }
  1143. JUCE_COMRESULT DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
  1144. {
  1145. if (ownerInfo == nullptr)
  1146. return S_FALSE;
  1147. ownerInfo->dragInfo.position = ownerInfo->getMousePos (mousePos).roundToInt();
  1148. const bool wasWanted = ownerInfo->owner.handleDragMove (ownerInfo->dragInfo);
  1149. *pdwEffect = wasWanted ? (DWORD) DROPEFFECT_COPY : (DWORD) DROPEFFECT_NONE;
  1150. return S_OK;
  1151. }
  1152. JUCE_COMRESULT Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
  1153. {
  1154. HRESULT hr = updateFileList (pDataObject);
  1155. if (SUCCEEDED (hr))
  1156. {
  1157. ownerInfo->dragInfo.position = ownerInfo->getMousePos (mousePos).roundToInt();
  1158. const bool wasWanted = ownerInfo->owner.handleDragDrop (ownerInfo->dragInfo);
  1159. *pdwEffect = wasWanted ? (DWORD) DROPEFFECT_COPY : (DWORD) DROPEFFECT_NONE;
  1160. hr = S_OK;
  1161. }
  1162. return hr;
  1163. }
  1164. private:
  1165. struct OwnerInfo
  1166. {
  1167. OwnerInfo (HWNDComponentPeer& p) : owner (p) {}
  1168. Point<float> getMousePos (const POINTL& mousePos) const
  1169. {
  1170. return owner.globalToLocal (ScalingHelpers::unscaledScreenPosToScaled (owner.getComponent().getDesktopScaleFactor(),
  1171. Point<float> (static_cast<float> (mousePos.x),
  1172. static_cast<float> (mousePos.y))));
  1173. }
  1174. template <typename CharType>
  1175. void parseFileList (const CharType* names, const SIZE_T totalLen)
  1176. {
  1177. unsigned int i = 0;
  1178. for (;;)
  1179. {
  1180. unsigned int len = 0;
  1181. while (i + len < totalLen && names [i + len] != 0)
  1182. ++len;
  1183. if (len == 0)
  1184. break;
  1185. dragInfo.files.add (String (names + i, len));
  1186. i += len + 1;
  1187. }
  1188. }
  1189. HWNDComponentPeer& owner;
  1190. ComponentPeer::DragInfo dragInfo;
  1191. JUCE_DECLARE_NON_COPYABLE (OwnerInfo)
  1192. };
  1193. ScopedPointer<OwnerInfo> ownerInfo;
  1194. struct DroppedData
  1195. {
  1196. DroppedData (IDataObject* const dataObject, const CLIPFORMAT type)
  1197. : data (nullptr)
  1198. {
  1199. FORMATETC format = { type, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1200. STGMEDIUM resetMedium = { TYMED_HGLOBAL, { 0 }, 0 };
  1201. medium = resetMedium;
  1202. if (SUCCEEDED (error = dataObject->GetData (&format, &medium)))
  1203. {
  1204. dataSize = GlobalSize (medium.hGlobal);
  1205. data = GlobalLock (medium.hGlobal);
  1206. }
  1207. }
  1208. ~DroppedData()
  1209. {
  1210. if (data != nullptr)
  1211. GlobalUnlock (medium.hGlobal);
  1212. }
  1213. HRESULT error;
  1214. STGMEDIUM medium;
  1215. void* data;
  1216. SIZE_T dataSize;
  1217. };
  1218. HRESULT updateFileList (IDataObject* const dataObject)
  1219. {
  1220. if (ownerInfo == nullptr)
  1221. return S_FALSE;
  1222. ownerInfo->dragInfo.clear();
  1223. {
  1224. DroppedData fileData (dataObject, CF_HDROP);
  1225. if (SUCCEEDED (fileData.error))
  1226. {
  1227. const LPDROPFILES dropFiles = static_cast<const LPDROPFILES> (fileData.data);
  1228. const void* const names = addBytesToPointer (dropFiles, sizeof (DROPFILES));
  1229. if (dropFiles->fWide)
  1230. ownerInfo->parseFileList (static_cast<const WCHAR*> (names), fileData.dataSize);
  1231. else
  1232. ownerInfo->parseFileList (static_cast<const char*> (names), fileData.dataSize);
  1233. return S_OK;
  1234. }
  1235. }
  1236. DroppedData textData (dataObject, CF_UNICODETEXT);
  1237. if (SUCCEEDED (textData.error))
  1238. {
  1239. ownerInfo->dragInfo.text = String (CharPointer_UTF16 ((const WCHAR*) textData.data),
  1240. CharPointer_UTF16 ((const WCHAR*) addBytesToPointer (textData.data, textData.dataSize)));
  1241. return S_OK;
  1242. }
  1243. return textData.error;
  1244. }
  1245. JUCE_DECLARE_NON_COPYABLE (JuceDropTarget)
  1246. };
  1247. static bool offerKeyMessageToJUCEWindow (MSG& m)
  1248. {
  1249. if (m.message == WM_KEYDOWN || m.message == WM_KEYUP)
  1250. if (Component::getCurrentlyFocusedComponent() != nullptr)
  1251. if (HWNDComponentPeer* h = getOwnerOfWindow (m.hwnd))
  1252. return m.message == WM_KEYDOWN ? h->doKeyDown (m.wParam)
  1253. : h->doKeyUp (m.wParam);
  1254. return false;
  1255. }
  1256. private:
  1257. HWND hwnd, parentToAddTo;
  1258. ScopedPointer<DropShadower> shadower;
  1259. RenderingEngineType currentRenderingEngine;
  1260. #if JUCE_DIRECT2D
  1261. ScopedPointer<Direct2DLowLevelGraphicsContext> direct2DContext;
  1262. #endif
  1263. uint32 lastPaintTime;
  1264. ULONGLONG lastMagnifySize;
  1265. bool fullScreen, isDragging, isMouseOver, hasCreatedCaret, constrainerIsResizing;
  1266. BorderSize<int> windowBorder;
  1267. HICON currentWindowIcon;
  1268. JuceDropTarget* dropTarget;
  1269. uint8 updateLayeredWindowAlpha;
  1270. UWPUIViewSettings uwpViewSettings;
  1271. MultiTouchMapper<DWORD> currentTouches;
  1272. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1273. ModifierKeyProvider* modProvider;
  1274. #endif
  1275. //==============================================================================
  1276. class TemporaryImage : public Timer
  1277. {
  1278. public:
  1279. TemporaryImage() {}
  1280. Image& getImage (const bool transparent, const int w, const int h)
  1281. {
  1282. const Image::PixelFormat format = transparent ? Image::ARGB : Image::RGB;
  1283. if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
  1284. image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
  1285. startTimer (3000);
  1286. return image;
  1287. }
  1288. void timerCallback() override
  1289. {
  1290. stopTimer();
  1291. image = Image();
  1292. }
  1293. private:
  1294. Image image;
  1295. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage)
  1296. };
  1297. TemporaryImage offscreenImageGenerator;
  1298. //==============================================================================
  1299. class WindowClassHolder : private DeletedAtShutdown
  1300. {
  1301. public:
  1302. WindowClassHolder()
  1303. {
  1304. // this name has to be different for each app/dll instance because otherwise poor old Windows can
  1305. // get a bit confused (even despite it not being a process-global window class).
  1306. String windowClassName ("JUCE_");
  1307. windowClassName << String::toHexString (Time::currentTimeMillis());
  1308. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  1309. TCHAR moduleFile [1024] = { 0 };
  1310. GetModuleFileName (moduleHandle, moduleFile, 1024);
  1311. WORD iconNum = 0;
  1312. WNDCLASSEX wcex = { 0 };
  1313. wcex.cbSize = sizeof (wcex);
  1314. wcex.style = CS_OWNDC;
  1315. wcex.lpfnWndProc = (WNDPROC) windowProc;
  1316. wcex.lpszClassName = windowClassName.toWideCharPointer();
  1317. wcex.cbWndExtra = 32;
  1318. wcex.hInstance = moduleHandle;
  1319. wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1320. iconNum = 1;
  1321. wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  1322. atom = RegisterClassEx (&wcex);
  1323. jassert (atom != 0);
  1324. isEventBlockedByModalComps = checkEventBlockedByModalComps;
  1325. }
  1326. ~WindowClassHolder()
  1327. {
  1328. if (ComponentPeer::getNumPeers() == 0)
  1329. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  1330. clearSingletonInstance();
  1331. }
  1332. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
  1333. juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder)
  1334. private:
  1335. ATOM atom;
  1336. static bool isHWNDBlockedByModalComponents (HWND h)
  1337. {
  1338. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  1339. if (auto* c = Desktop::getInstance().getComponent (i))
  1340. if ((! c->isCurrentlyBlockedByAnotherModalComponent())
  1341. && IsChild ((HWND) c->getWindowHandle(), h))
  1342. return false;
  1343. return true;
  1344. }
  1345. static bool checkEventBlockedByModalComps (const MSG& m)
  1346. {
  1347. if (Component::getNumCurrentlyModalComponents() == 0 || JuceWindowIdentifier::isJUCEWindow (m.hwnd))
  1348. return false;
  1349. switch (m.message)
  1350. {
  1351. case WM_MOUSEMOVE:
  1352. case WM_NCMOUSEMOVE:
  1353. case 0x020A: /* WM_MOUSEWHEEL */
  1354. case 0x020E: /* WM_MOUSEHWHEEL */
  1355. case WM_KEYUP:
  1356. case WM_SYSKEYUP:
  1357. case WM_CHAR:
  1358. case WM_APPCOMMAND:
  1359. case WM_LBUTTONUP:
  1360. case WM_MBUTTONUP:
  1361. case WM_RBUTTONUP:
  1362. case WM_MOUSEACTIVATE:
  1363. case WM_NCMOUSEHOVER:
  1364. case WM_MOUSEHOVER:
  1365. case WM_TOUCH:
  1366. case WM_POINTERUPDATE:
  1367. case WM_NCPOINTERUPDATE:
  1368. case WM_POINTERWHEEL:
  1369. case WM_POINTERHWHEEL:
  1370. case WM_POINTERUP:
  1371. case WM_POINTERACTIVATE:
  1372. return isHWNDBlockedByModalComponents(m.hwnd);
  1373. case WM_NCLBUTTONDOWN:
  1374. case WM_NCLBUTTONDBLCLK:
  1375. case WM_NCRBUTTONDOWN:
  1376. case WM_NCRBUTTONDBLCLK:
  1377. case WM_NCMBUTTONDOWN:
  1378. case WM_NCMBUTTONDBLCLK:
  1379. case WM_LBUTTONDOWN:
  1380. case WM_LBUTTONDBLCLK:
  1381. case WM_MBUTTONDOWN:
  1382. case WM_MBUTTONDBLCLK:
  1383. case WM_RBUTTONDOWN:
  1384. case WM_RBUTTONDBLCLK:
  1385. case WM_KEYDOWN:
  1386. case WM_SYSKEYDOWN:
  1387. case WM_NCPOINTERDOWN:
  1388. case WM_POINTERDOWN:
  1389. if (isHWNDBlockedByModalComponents (m.hwnd))
  1390. {
  1391. if (auto* modal = Component::getCurrentlyModalComponent (0))
  1392. modal->inputAttemptWhenModal();
  1393. return true;
  1394. }
  1395. break;
  1396. default:
  1397. break;
  1398. }
  1399. return false;
  1400. }
  1401. JUCE_DECLARE_NON_COPYABLE (WindowClassHolder)
  1402. };
  1403. //==============================================================================
  1404. static void* createWindowCallback (void* userData)
  1405. {
  1406. static_cast<HWNDComponentPeer*> (userData)->createWindow();
  1407. return nullptr;
  1408. }
  1409. void createWindow()
  1410. {
  1411. DWORD exstyle = 0;
  1412. DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  1413. if (hasTitleBar())
  1414. {
  1415. type |= WS_OVERLAPPED;
  1416. if ((styleFlags & windowHasCloseButton) != 0)
  1417. {
  1418. type |= WS_SYSMENU;
  1419. }
  1420. else
  1421. {
  1422. // annoyingly, windows won't let you have a min/max button without a close button
  1423. jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
  1424. }
  1425. if ((styleFlags & windowIsResizable) != 0)
  1426. type |= WS_THICKFRAME;
  1427. }
  1428. else if (parentToAddTo != 0)
  1429. {
  1430. type |= WS_CHILD;
  1431. }
  1432. else
  1433. {
  1434. type |= WS_POPUP | WS_SYSMENU;
  1435. }
  1436. if ((styleFlags & windowAppearsOnTaskbar) == 0)
  1437. exstyle |= WS_EX_TOOLWINDOW;
  1438. else
  1439. exstyle |= WS_EX_APPWINDOW;
  1440. if ((styleFlags & windowHasMinimiseButton) != 0) type |= WS_MINIMIZEBOX;
  1441. if ((styleFlags & windowHasMaximiseButton) != 0) type |= WS_MAXIMIZEBOX;
  1442. if ((styleFlags & windowIgnoresMouseClicks) != 0) exstyle |= WS_EX_TRANSPARENT;
  1443. if ((styleFlags & windowIsSemiTransparent) != 0 && Desktop::canUseSemiTransparentWindows())
  1444. exstyle |= WS_EX_LAYERED;
  1445. hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
  1446. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  1447. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  1448. if (hwnd != 0)
  1449. {
  1450. SetWindowLongPtr (hwnd, 0, 0);
  1451. SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
  1452. JuceWindowIdentifier::setAsJUCEWindow (hwnd, true);
  1453. if (dropTarget == nullptr)
  1454. {
  1455. HWNDComponentPeer* peer = nullptr;
  1456. if (dontRepaint)
  1457. peer = getOwnerOfWindow (parentToAddTo);
  1458. if (peer == nullptr)
  1459. peer = this;
  1460. dropTarget = new JuceDropTarget (*peer);
  1461. }
  1462. RegisterDragDrop (hwnd, dropTarget);
  1463. if (canUseMultiTouch())
  1464. registerTouchWindow (hwnd, 0);
  1465. setDPIAwareness();
  1466. setMessageFilter();
  1467. updateBorderSize();
  1468. checkForPointerAPI();
  1469. // Calling this function here is (for some reason) necessary to make Windows
  1470. // correctly enable the menu items that we specify in the wm_initmenu message.
  1471. GetSystemMenu (hwnd, false);
  1472. const float alpha = component.getAlpha();
  1473. if (alpha < 1.0f)
  1474. setAlpha (alpha);
  1475. }
  1476. else
  1477. {
  1478. jassertfalse;
  1479. }
  1480. }
  1481. static void* destroyWindowCallback (void* handle)
  1482. {
  1483. RevokeDragDrop ((HWND) handle);
  1484. DestroyWindow ((HWND) handle);
  1485. return nullptr;
  1486. }
  1487. static void* toFrontCallback1 (void* h)
  1488. {
  1489. SetForegroundWindow ((HWND) h);
  1490. return nullptr;
  1491. }
  1492. static void* toFrontCallback2 (void* h)
  1493. {
  1494. setWindowZOrder ((HWND) h, HWND_TOP);
  1495. return nullptr;
  1496. }
  1497. static void* setFocusCallback (void* h)
  1498. {
  1499. SetFocus ((HWND) h);
  1500. return nullptr;
  1501. }
  1502. static void* getFocusCallback (void*)
  1503. {
  1504. return GetFocus();
  1505. }
  1506. bool isUsingUpdateLayeredWindow() const
  1507. {
  1508. return ! component.isOpaque();
  1509. }
  1510. bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; }
  1511. void setIcon (const Image& newIcon)
  1512. {
  1513. HICON hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0);
  1514. if (hicon != 0)
  1515. {
  1516. SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
  1517. SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
  1518. if (currentWindowIcon != 0)
  1519. DestroyIcon (currentWindowIcon);
  1520. currentWindowIcon = hicon;
  1521. }
  1522. }
  1523. void setMessageFilter()
  1524. {
  1525. typedef BOOL (WINAPI* ChangeWindowMessageFilterExFunc) (HWND, UINT, DWORD, PVOID);
  1526. if (ChangeWindowMessageFilterExFunc changeMessageFilter
  1527. = (ChangeWindowMessageFilterExFunc) getUser32Function ("ChangeWindowMessageFilterEx"))
  1528. {
  1529. changeMessageFilter (hwnd, WM_DROPFILES, 1 /*MSGFLT_ALLOW*/, nullptr);
  1530. changeMessageFilter (hwnd, WM_COPYDATA, 1 /*MSGFLT_ALLOW*/, nullptr);
  1531. changeMessageFilter (hwnd, 0x49, 1 /*MSGFLT_ALLOW*/, nullptr);
  1532. }
  1533. }
  1534. struct ChildWindowClippingInfo
  1535. {
  1536. HDC dc;
  1537. HWNDComponentPeer* peer;
  1538. RectangleList<int>* clip;
  1539. Point<int> origin;
  1540. int savedDC;
  1541. };
  1542. static BOOL CALLBACK clipChildWindowCallback (HWND hwnd, LPARAM context)
  1543. {
  1544. if (IsWindowVisible (hwnd))
  1545. {
  1546. ChildWindowClippingInfo& info = *(ChildWindowClippingInfo*) context;
  1547. HWND parent = GetParent (hwnd);
  1548. if (parent == info.peer->hwnd)
  1549. {
  1550. RECT r = getWindowRect (hwnd);
  1551. POINT pos = { r.left, r.top };
  1552. ScreenToClient (GetParent (hwnd), &pos);
  1553. Rectangle<int> clip (Rectangle<int> (pos.x, pos.y,
  1554. r.right - r.left,
  1555. r.bottom - r.top));
  1556. info.clip->subtract (clip - info.origin);
  1557. if (info.savedDC == 0)
  1558. info.savedDC = SaveDC (info.dc);
  1559. ExcludeClipRect (info.dc, clip.getX(), clip.getY(), clip.getRight(), clip.getBottom());
  1560. }
  1561. }
  1562. return TRUE;
  1563. }
  1564. //==============================================================================
  1565. void handlePaintMessage()
  1566. {
  1567. #if JUCE_DIRECT2D
  1568. if (direct2DContext != nullptr)
  1569. {
  1570. RECT r;
  1571. if (GetUpdateRect (hwnd, &r, false))
  1572. {
  1573. direct2DContext->start();
  1574. direct2DContext->clipToRectangle (rectangleFromRECT (r));
  1575. handlePaint (*direct2DContext);
  1576. direct2DContext->end();
  1577. }
  1578. }
  1579. else
  1580. #endif
  1581. HRGN rgn = CreateRectRgn (0, 0, 0, 0);
  1582. const int regionType = GetUpdateRgn (hwnd, rgn, false);
  1583. PAINTSTRUCT paintStruct;
  1584. HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
  1585. // message and become re-entrant, but that's OK
  1586. // if something in a paint handler calls, e.g. a message box, this can become reentrant and
  1587. // corrupt the image it's using to paint into, so do a check here.
  1588. static bool reentrant = false;
  1589. if (! reentrant)
  1590. {
  1591. const ScopedValueSetter<bool> setter (reentrant, true, false);
  1592. if (dontRepaint)
  1593. component.handleCommandMessage (0); // (this triggers a repaint in the openGL context)
  1594. else
  1595. performPaint (dc, rgn, regionType, paintStruct);
  1596. }
  1597. DeleteObject (rgn);
  1598. EndPaint (hwnd, &paintStruct);
  1599. #if JUCE_MSVC
  1600. _fpreset(); // because some graphics cards can unmask FP exceptions
  1601. #endif
  1602. lastPaintTime = Time::getMillisecondCounter();
  1603. }
  1604. void performPaint (HDC dc, HRGN rgn, int regionType, PAINTSTRUCT& paintStruct)
  1605. {
  1606. int x = paintStruct.rcPaint.left;
  1607. int y = paintStruct.rcPaint.top;
  1608. int w = paintStruct.rcPaint.right - x;
  1609. int h = paintStruct.rcPaint.bottom - y;
  1610. const bool transparent = isUsingUpdateLayeredWindow();
  1611. if (transparent)
  1612. {
  1613. // it's not possible to have a transparent window with a title bar at the moment!
  1614. jassert (! hasTitleBar());
  1615. RECT r = getWindowRect (hwnd);
  1616. x = y = 0;
  1617. w = r.right - r.left;
  1618. h = r.bottom - r.top;
  1619. }
  1620. if (w > 0 && h > 0)
  1621. {
  1622. Image& offscreenImage = offscreenImageGenerator.getImage (transparent, w, h);
  1623. RectangleList<int> contextClip;
  1624. const Rectangle<int> clipBounds (w, h);
  1625. bool needToPaintAll = true;
  1626. if (regionType == COMPLEXREGION && ! transparent)
  1627. {
  1628. HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
  1629. CombineRgn (rgn, rgn, clipRgn, RGN_AND);
  1630. DeleteObject (clipRgn);
  1631. char rgnData [8192];
  1632. const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData);
  1633. if (res > 0 && res <= sizeof (rgnData))
  1634. {
  1635. const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh);
  1636. if (hdr->iType == RDH_RECTANGLES
  1637. && hdr->rcBound.right - hdr->rcBound.left >= w
  1638. && hdr->rcBound.bottom - hdr->rcBound.top >= h)
  1639. {
  1640. needToPaintAll = false;
  1641. const RECT* rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER));
  1642. for (int i = (int) ((RGNDATA*) rgnData)->rdh.nCount; --i >= 0;)
  1643. {
  1644. if (rects->right <= x + w && rects->bottom <= y + h)
  1645. {
  1646. const int cx = jmax (x, (int) rects->left);
  1647. contextClip.addWithoutMerging (Rectangle<int> (cx - x, rects->top - y,
  1648. rects->right - cx, rects->bottom - rects->top)
  1649. .getIntersection (clipBounds));
  1650. }
  1651. else
  1652. {
  1653. needToPaintAll = true;
  1654. break;
  1655. }
  1656. ++rects;
  1657. }
  1658. }
  1659. }
  1660. }
  1661. if (needToPaintAll)
  1662. {
  1663. contextClip.clear();
  1664. contextClip.addWithoutMerging (Rectangle<int> (w, h));
  1665. }
  1666. ChildWindowClippingInfo childClipInfo = { dc, this, &contextClip, Point<int> (x, y), 0 };
  1667. EnumChildWindows (hwnd, clipChildWindowCallback, (LPARAM) &childClipInfo);
  1668. if (! contextClip.isEmpty())
  1669. {
  1670. if (transparent)
  1671. for (const Rectangle<int>* i = contextClip.begin(), * const e = contextClip.end(); i != e; ++i)
  1672. offscreenImage.clear (*i);
  1673. // if the component's not opaque, this won't draw properly unless the platform can support this
  1674. jassert (Desktop::canUseSemiTransparentWindows() || component.isOpaque());
  1675. {
  1676. ScopedPointer<LowLevelGraphicsContext> context (component.getLookAndFeel()
  1677. .createGraphicsContext (offscreenImage, Point<int> (-x, -y), contextClip));
  1678. handlePaint (*context);
  1679. }
  1680. static_cast<WindowsBitmapImage*> (offscreenImage.getPixelData())
  1681. ->blitToWindow (hwnd, dc, transparent, x, y, updateLayeredWindowAlpha);
  1682. }
  1683. if (childClipInfo.savedDC != 0)
  1684. RestoreDC (dc, childClipInfo.savedDC);
  1685. }
  1686. }
  1687. //==============================================================================
  1688. void doMouseEvent (Point<float> position, float pressure, float orientation = 0.0f, ModifierKeys mods = currentModifiers)
  1689. {
  1690. handleMouseEvent (MouseInputSource::InputSourceType::mouse, position, mods, pressure, orientation, getMouseEventTime());
  1691. }
  1692. StringArray getAvailableRenderingEngines() override
  1693. {
  1694. StringArray s ("Software Renderer");
  1695. #if JUCE_DIRECT2D
  1696. if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
  1697. s.add ("Direct2D");
  1698. #endif
  1699. return s;
  1700. }
  1701. int getCurrentRenderingEngine() const override { return currentRenderingEngine; }
  1702. #if JUCE_DIRECT2D
  1703. void updateDirect2DContext()
  1704. {
  1705. if (currentRenderingEngine != direct2DRenderingEngine)
  1706. direct2DContext = 0;
  1707. else if (direct2DContext == 0)
  1708. direct2DContext = new Direct2DLowLevelGraphicsContext (hwnd);
  1709. }
  1710. #endif
  1711. void setCurrentRenderingEngine (int index) override
  1712. {
  1713. ignoreUnused (index);
  1714. #if JUCE_DIRECT2D
  1715. if (getAvailableRenderingEngines().size() > 1)
  1716. {
  1717. currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
  1718. updateDirect2DContext();
  1719. repaint (component.getLocalBounds());
  1720. }
  1721. #endif
  1722. }
  1723. static int getMinTimeBetweenMouseMoves()
  1724. {
  1725. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  1726. return 0;
  1727. return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP..
  1728. }
  1729. bool isTouchEvent() noexcept
  1730. {
  1731. if (registerTouchWindow == nullptr)
  1732. return false;
  1733. // Relevant info about touch/pen detection flags:
  1734. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
  1735. // http://www.petertissen.de/?p=4
  1736. return (GetMessageExtraInfo() & 0xFFFFFF80 /*SIGNATURE_MASK*/) == 0xFF515780 /*MI_WP_SIGNATURE*/;
  1737. }
  1738. static bool areOtherTouchSourcesActive()
  1739. {
  1740. for (auto& ms : Desktop::getInstance().getMouseSources())
  1741. if (ms.isDragging() && (ms.getType() == MouseInputSource::InputSourceType::touch
  1742. || ms.getType() == MouseInputSource::InputSourceType::pen))
  1743. return true;
  1744. return false;
  1745. }
  1746. void doMouseMove (Point<float> position, bool isMouseDownEvent)
  1747. {
  1748. ModifierKeys modsToSend (currentModifiers);
  1749. // this will be handled by WM_TOUCH
  1750. if (isTouchEvent() || areOtherTouchSourcesActive())
  1751. return;
  1752. if (! isMouseOver)
  1753. {
  1754. isMouseOver = true;
  1755. // This avoids a rare stuck-button problem when focus is lost unexpectedly, but must
  1756. // not be called as part of a move, in case it's actually a mouse-drag from another
  1757. // app which ends up here when we get focus before the mouse is released..
  1758. if (isMouseDownEvent)
  1759. ModifierKeys::getCurrentModifiersRealtime();
  1760. updateKeyModifiers();
  1761. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1762. if (modProvider != nullptr)
  1763. currentModifiers = currentModifiers.withFlags (modProvider->getWin32Modifiers());
  1764. #endif
  1765. TRACKMOUSEEVENT tme;
  1766. tme.cbSize = sizeof (tme);
  1767. tme.dwFlags = TME_LEAVE;
  1768. tme.hwndTrack = hwnd;
  1769. tme.dwHoverTime = 0;
  1770. if (! TrackMouseEvent (&tme))
  1771. jassertfalse;
  1772. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  1773. }
  1774. else if (! isDragging)
  1775. {
  1776. if (! contains (position.roundToInt(), false))
  1777. return;
  1778. }
  1779. static uint32 lastMouseTime = 0;
  1780. static int minTimeBetweenMouses = getMinTimeBetweenMouseMoves();
  1781. const uint32 now = Time::getMillisecondCounter();
  1782. if (! Desktop::getInstance().getMainMouseSource().isDragging())
  1783. modsToSend = modsToSend.withoutMouseButtons();
  1784. if (now >= lastMouseTime + minTimeBetweenMouses)
  1785. {
  1786. lastMouseTime = now;
  1787. doMouseEvent (position, MouseInputSource::invalidPressure,
  1788. MouseInputSource::invalidOrientation, modsToSend);
  1789. }
  1790. }
  1791. void doMouseDown (Point<float> position, const WPARAM wParam)
  1792. {
  1793. // this will be handled by WM_TOUCH
  1794. if (isTouchEvent() || areOtherTouchSourcesActive())
  1795. return;
  1796. if (GetCapture() != hwnd)
  1797. SetCapture (hwnd);
  1798. doMouseMove (position, true);
  1799. if (isValidPeer (this))
  1800. {
  1801. updateModifiersFromWParam (wParam);
  1802. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1803. if (modProvider != nullptr)
  1804. currentModifiers = currentModifiers.withFlags (modProvider->getWin32Modifiers());
  1805. #endif
  1806. isDragging = true;
  1807. doMouseEvent (position, MouseInputSource::invalidPressure);
  1808. }
  1809. }
  1810. void doMouseUp (Point<float> position, const WPARAM wParam)
  1811. {
  1812. // this will be handled by WM_TOUCH
  1813. if (isTouchEvent() || areOtherTouchSourcesActive())
  1814. return;
  1815. updateModifiersFromWParam (wParam);
  1816. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  1817. if (modProvider != nullptr)
  1818. currentModifiers = currentModifiers.withFlags (modProvider->getWin32Modifiers());
  1819. #endif
  1820. const bool wasDragging = isDragging;
  1821. isDragging = false;
  1822. // release the mouse capture if the user has released all buttons
  1823. if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
  1824. ReleaseCapture();
  1825. // NB: under some circumstances (e.g. double-clicking a native title bar), a mouse-up can
  1826. // arrive without a mouse-down, so in that case we need to avoid sending a message.
  1827. if (wasDragging)
  1828. doMouseEvent (position, MouseInputSource::invalidPressure);
  1829. }
  1830. void doCaptureChanged()
  1831. {
  1832. if (constrainerIsResizing)
  1833. {
  1834. if (constrainer != nullptr)
  1835. constrainer->resizeEnd();
  1836. constrainerIsResizing = false;
  1837. }
  1838. if (isDragging)
  1839. doMouseUp (getCurrentMousePos(), (WPARAM) 0);
  1840. }
  1841. void doMouseExit()
  1842. {
  1843. isMouseOver = false;
  1844. if (! areOtherTouchSourcesActive())
  1845. doMouseEvent (getCurrentMousePos(), MouseInputSource::invalidPressure);
  1846. }
  1847. ComponentPeer* findPeerUnderMouse (Point<float>& localPos)
  1848. {
  1849. const Point<int> globalPos (getCurrentMousePosGlobal().roundToInt());
  1850. // Because Windows stupidly sends all wheel events to the window with the keyboard
  1851. // focus, we have to redirect them here according to the mouse pos..
  1852. POINT p = { globalPos.x, globalPos.y };
  1853. HWNDComponentPeer* peer = getOwnerOfWindow (WindowFromPoint (p));
  1854. if (peer == nullptr)
  1855. peer = this;
  1856. localPos = peer->globalToLocal (globalPos.toFloat());
  1857. return peer;
  1858. }
  1859. static MouseInputSource::InputSourceType getPointerType (WPARAM wParam)
  1860. {
  1861. if (getPointerTypeFunction != nullptr)
  1862. {
  1863. POINTER_INPUT_TYPE pointerType;
  1864. if (getPointerTypeFunction (GET_POINTERID_WPARAM (wParam), &pointerType))
  1865. {
  1866. if (pointerType == 2)
  1867. return MouseInputSource::InputSourceType::touch;
  1868. if (pointerType == 3)
  1869. return MouseInputSource::InputSourceType::pen;
  1870. }
  1871. }
  1872. return MouseInputSource::InputSourceType::mouse;
  1873. }
  1874. void doMouseWheel (const WPARAM wParam, const bool isVertical)
  1875. {
  1876. updateKeyModifiers();
  1877. const float amount = jlimit (-1000.0f, 1000.0f, 0.5f * (short) HIWORD (wParam));
  1878. MouseWheelDetails wheel;
  1879. wheel.deltaX = isVertical ? 0.0f : amount / -256.0f;
  1880. wheel.deltaY = isVertical ? amount / 256.0f : 0.0f;
  1881. wheel.isReversed = false;
  1882. wheel.isSmooth = false;
  1883. wheel.isInertial = false;
  1884. Point<float> localPos;
  1885. if (auto* peer = findPeerUnderMouse (localPos))
  1886. peer->handleMouseWheel (getPointerType (wParam), localPos, getMouseEventTime(), wheel);
  1887. }
  1888. bool doGestureEvent (LPARAM lParam)
  1889. {
  1890. GESTUREINFO gi;
  1891. zerostruct (gi);
  1892. gi.cbSize = sizeof (gi);
  1893. if (getGestureInfo != nullptr && getGestureInfo ((HGESTUREINFO) lParam, &gi))
  1894. {
  1895. updateKeyModifiers();
  1896. Point<float> localPos;
  1897. if (ComponentPeer* const peer = findPeerUnderMouse (localPos))
  1898. {
  1899. switch (gi.dwID)
  1900. {
  1901. case 3: /*GID_ZOOM*/
  1902. if (gi.dwFlags != 1 /*GF_BEGIN*/ && lastMagnifySize > 0)
  1903. peer->handleMagnifyGesture (MouseInputSource::InputSourceType::touch, localPos, getMouseEventTime(),
  1904. (float) (gi.ullArguments / (double) lastMagnifySize));
  1905. lastMagnifySize = gi.ullArguments;
  1906. return true;
  1907. case 4: /*GID_PAN*/
  1908. case 5: /*GID_ROTATE*/
  1909. case 6: /*GID_TWOFINGERTAP*/
  1910. case 7: /*GID_PRESSANDTAP*/
  1911. default:
  1912. break;
  1913. }
  1914. }
  1915. }
  1916. return false;
  1917. }
  1918. LRESULT doTouchEvent (const int numInputs, HTOUCHINPUT eventHandle)
  1919. {
  1920. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  1921. if (auto* parent = getOwnerOfWindow (GetParent (hwnd)))
  1922. if (parent != this)
  1923. return parent->doTouchEvent (numInputs, eventHandle);
  1924. HeapBlock<TOUCHINPUT> inputInfo (numInputs);
  1925. if (getTouchInputInfo (eventHandle, numInputs, inputInfo, sizeof (TOUCHINPUT)))
  1926. {
  1927. for (int i = 0; i < numInputs; ++i)
  1928. {
  1929. const DWORD flags = inputInfo[i].dwFlags;
  1930. if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0)
  1931. if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0))
  1932. return 0; // abandon method if this window was deleted by the callback
  1933. }
  1934. }
  1935. closeTouchInputHandle (eventHandle);
  1936. return 0;
  1937. }
  1938. bool handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp,
  1939. const float touchPressure = MouseInputSource::invalidPressure,
  1940. const float orientation = 0.0f)
  1941. {
  1942. bool isCancel = false;
  1943. const int touchIndex = currentTouches.getIndexOfTouch (touch.dwID);
  1944. const int64 time = getMouseEventTime();
  1945. const Point<float> pos (globalToLocal (Point<float> (touch.x / 100.0f,
  1946. touch.y / 100.0f)));
  1947. const float pressure = touchPressure;
  1948. ModifierKeys modsToSend (currentModifiers);
  1949. if (isDown)
  1950. {
  1951. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  1952. modsToSend = currentModifiers;
  1953. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  1954. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend.withoutMouseButtons(), pressure, orientation, time, PenDetails(), touchIndex);
  1955. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  1956. return false;
  1957. }
  1958. else if (isUp)
  1959. {
  1960. modsToSend = modsToSend.withoutMouseButtons();
  1961. currentTouches.clearTouch (touchIndex);
  1962. if (! currentTouches.areAnyTouchesActive())
  1963. isCancel = true;
  1964. }
  1965. else
  1966. {
  1967. modsToSend = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  1968. }
  1969. if (isCancel)
  1970. {
  1971. currentTouches.clear();
  1972. currentModifiers = currentModifiers.withoutMouseButtons();
  1973. }
  1974. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend, pressure, orientation, time, PenDetails(), touchIndex);
  1975. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  1976. return false;
  1977. if (isUp || isCancel)
  1978. {
  1979. handleMouseEvent (MouseInputSource::InputSourceType::touch, Point<float> (-10.0f, -10.0f), currentModifiers, pressure, orientation, time, PenDetails(), touchIndex);
  1980. if (! isValidPeer (this))
  1981. return false;
  1982. }
  1983. return true;
  1984. }
  1985. bool handlePointerInput (WPARAM wParam, LPARAM lParam, const bool isDown, const bool isUp)
  1986. {
  1987. if (! canUsePointerAPI)
  1988. return false;
  1989. auto pointerType = getPointerType (wParam);
  1990. if (pointerType == MouseInputSource::InputSourceType::touch)
  1991. {
  1992. POINTER_TOUCH_INFO touchInfo;
  1993. if (! getPointerTouchInfo (GET_POINTERID_WPARAM (wParam), &touchInfo))
  1994. return false;
  1995. const auto pressure = touchInfo.touchMask & TOUCH_MASK_PRESSURE ? touchInfo.pressure
  1996. : MouseInputSource::invalidPressure;
  1997. const auto orientation = touchInfo.touchMask & TOUCH_MASK_ORIENTATION ? degreesToRadians (static_cast<float> (touchInfo.orientation))
  1998. : MouseInputSource::invalidOrientation;
  1999. if (! handleTouchInput (emulateTouchEventFromPointer (lParam, wParam),
  2000. isDown, isUp, pressure, orientation))
  2001. return false;
  2002. }
  2003. else if (pointerType == MouseInputSource::InputSourceType::pen)
  2004. {
  2005. POINTER_PEN_INFO penInfo;
  2006. if (! getPointerPenInfo (GET_POINTERID_WPARAM (wParam), &penInfo))
  2007. return false;
  2008. const auto pressure = (penInfo.penMask & PEN_MASK_PRESSURE) ? penInfo.pressure / 1024.0f : MouseInputSource::invalidPressure;
  2009. if (! handlePenInput (penInfo, globalToLocal (Point<float> (static_cast<float> (GET_X_LPARAM(lParam)),
  2010. static_cast<float> (GET_Y_LPARAM(lParam)))),
  2011. pressure, isDown, isUp))
  2012. return false;
  2013. }
  2014. else
  2015. {
  2016. return false;
  2017. }
  2018. return true;
  2019. }
  2020. TOUCHINPUT emulateTouchEventFromPointer (LPARAM lParam, WPARAM wParam)
  2021. {
  2022. TOUCHINPUT touchInput;
  2023. touchInput.dwID = GET_POINTERID_WPARAM (wParam);
  2024. touchInput.x = GET_X_LPARAM (lParam) * 100;
  2025. touchInput.y = GET_Y_LPARAM (lParam) * 100;
  2026. return touchInput;
  2027. }
  2028. bool handlePenInput (POINTER_PEN_INFO penInfo, Point<float> pos, const float pressure, bool isDown, bool isUp)
  2029. {
  2030. const auto time = getMouseEventTime();
  2031. ModifierKeys modsToSend (currentModifiers);
  2032. PenDetails penDetails;
  2033. penDetails.rotation = (penInfo.penMask & PEN_MASK_ROTATION) ? degreesToRadians (static_cast<float> (penInfo.rotation)) : MouseInputSource::invalidRotation;
  2034. penDetails.tiltX = (penInfo.penMask & PEN_MASK_TILT_X) ? penInfo.tiltX / 90.0f : MouseInputSource::invalidTiltX;
  2035. penDetails.tiltY = (penInfo.penMask & PEN_MASK_TILT_Y) ? penInfo.tiltY / 90.0f : MouseInputSource::invalidTiltY;
  2036. auto pInfoFlags = penInfo.pointerInfo.pointerFlags;
  2037. if ((pInfoFlags & POINTER_FLAG_FIRSTBUTTON) != 0)
  2038. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  2039. else if ((pInfoFlags & POINTER_FLAG_SECONDBUTTON) != 0)
  2040. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::rightButtonModifier);
  2041. if (isDown)
  2042. {
  2043. modsToSend = currentModifiers;
  2044. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  2045. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend.withoutMouseButtons(),
  2046. pressure, MouseInputSource::invalidOrientation, time, penDetails);
  2047. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2048. return false;
  2049. }
  2050. else if (isUp || ! (pInfoFlags & POINTER_FLAG_INCONTACT))
  2051. {
  2052. modsToSend = modsToSend.withoutMouseButtons();
  2053. }
  2054. handleMouseEvent (MouseInputSource::InputSourceType::pen, pos, modsToSend, pressure,
  2055. MouseInputSource::invalidOrientation, time, penDetails);
  2056. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  2057. return false;
  2058. if (isUp)
  2059. {
  2060. handleMouseEvent (MouseInputSource::InputSourceType::pen, { -10.0f, -10.0f }, currentModifiers,
  2061. pressure, MouseInputSource::invalidOrientation, time, penDetails);
  2062. if (! isValidPeer (this))
  2063. return false;
  2064. }
  2065. return true;
  2066. }
  2067. //==============================================================================
  2068. void sendModifierKeyChangeIfNeeded()
  2069. {
  2070. if (modifiersAtLastCallback != currentModifiers)
  2071. {
  2072. modifiersAtLastCallback = currentModifiers;
  2073. handleModifierKeysChange();
  2074. }
  2075. }
  2076. bool doKeyUp (const WPARAM key)
  2077. {
  2078. updateKeyModifiers();
  2079. switch (key)
  2080. {
  2081. case VK_SHIFT:
  2082. case VK_CONTROL:
  2083. case VK_MENU:
  2084. case VK_CAPITAL:
  2085. case VK_LWIN:
  2086. case VK_RWIN:
  2087. case VK_APPS:
  2088. case VK_NUMLOCK:
  2089. case VK_SCROLL:
  2090. case VK_LSHIFT:
  2091. case VK_RSHIFT:
  2092. case VK_LCONTROL:
  2093. case VK_LMENU:
  2094. case VK_RCONTROL:
  2095. case VK_RMENU:
  2096. sendModifierKeyChangeIfNeeded();
  2097. }
  2098. return handleKeyUpOrDown (false)
  2099. || Component::getCurrentlyModalComponent() != nullptr;
  2100. }
  2101. bool doKeyDown (const WPARAM key)
  2102. {
  2103. updateKeyModifiers();
  2104. bool used = false;
  2105. switch (key)
  2106. {
  2107. case VK_SHIFT:
  2108. case VK_LSHIFT:
  2109. case VK_RSHIFT:
  2110. case VK_CONTROL:
  2111. case VK_LCONTROL:
  2112. case VK_RCONTROL:
  2113. case VK_MENU:
  2114. case VK_LMENU:
  2115. case VK_RMENU:
  2116. case VK_LWIN:
  2117. case VK_RWIN:
  2118. case VK_CAPITAL:
  2119. case VK_NUMLOCK:
  2120. case VK_SCROLL:
  2121. case VK_APPS:
  2122. sendModifierKeyChangeIfNeeded();
  2123. break;
  2124. case VK_LEFT:
  2125. case VK_RIGHT:
  2126. case VK_UP:
  2127. case VK_DOWN:
  2128. case VK_PRIOR:
  2129. case VK_NEXT:
  2130. case VK_HOME:
  2131. case VK_END:
  2132. case VK_DELETE:
  2133. case VK_INSERT:
  2134. case VK_F1:
  2135. case VK_F2:
  2136. case VK_F3:
  2137. case VK_F4:
  2138. case VK_F5:
  2139. case VK_F6:
  2140. case VK_F7:
  2141. case VK_F8:
  2142. case VK_F9:
  2143. case VK_F10:
  2144. case VK_F11:
  2145. case VK_F12:
  2146. case VK_F13:
  2147. case VK_F14:
  2148. case VK_F15:
  2149. case VK_F16:
  2150. used = handleKeyUpOrDown (true);
  2151. used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
  2152. break;
  2153. default:
  2154. used = handleKeyUpOrDown (true);
  2155. {
  2156. MSG msg;
  2157. if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
  2158. {
  2159. // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
  2160. // manually generate the key-press event that matches this key-down.
  2161. const UINT keyChar = MapVirtualKey ((UINT) key, 2);
  2162. const UINT scanCode = MapVirtualKey ((UINT) key, 0);
  2163. BYTE keyState[256];
  2164. GetKeyboardState (keyState);
  2165. WCHAR text[16] = { 0 };
  2166. if (ToUnicode ((UINT) key, scanCode, keyState, text, 8, 0) != 1)
  2167. text[0] = 0;
  2168. used = handleKeyPress ((int) LOWORD (keyChar), (juce_wchar) text[0]) || used;
  2169. }
  2170. }
  2171. break;
  2172. }
  2173. return used || (Component::getCurrentlyModalComponent() != nullptr);
  2174. }
  2175. bool doKeyChar (int key, const LPARAM flags)
  2176. {
  2177. updateKeyModifiers();
  2178. juce_wchar textChar = (juce_wchar) key;
  2179. const int virtualScanCode = (flags >> 16) & 0xff;
  2180. if (key >= '0' && key <= '9')
  2181. {
  2182. switch (virtualScanCode) // check for a numeric keypad scan-code
  2183. {
  2184. case 0x52:
  2185. case 0x4f:
  2186. case 0x50:
  2187. case 0x51:
  2188. case 0x4b:
  2189. case 0x4c:
  2190. case 0x4d:
  2191. case 0x47:
  2192. case 0x48:
  2193. case 0x49:
  2194. key = (key - '0') + KeyPress::numberPad0;
  2195. break;
  2196. default:
  2197. break;
  2198. }
  2199. }
  2200. else
  2201. {
  2202. // convert the scan code to an unmodified character code..
  2203. const UINT virtualKey = MapVirtualKey ((UINT) virtualScanCode, 1);
  2204. UINT keyChar = MapVirtualKey (virtualKey, 2);
  2205. keyChar = LOWORD (keyChar);
  2206. if (keyChar != 0)
  2207. key = (int) keyChar;
  2208. // avoid sending junk text characters for some control-key combinations
  2209. if (textChar < ' ' && currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
  2210. textChar = 0;
  2211. }
  2212. return handleKeyPress (key, textChar);
  2213. }
  2214. void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
  2215. {
  2216. if (HWND parentH = GetParent (hwnd))
  2217. PostMessage (parentH, message, wParam, lParam);
  2218. }
  2219. bool doAppCommand (const LPARAM lParam)
  2220. {
  2221. int key = 0;
  2222. switch (GET_APPCOMMAND_LPARAM (lParam))
  2223. {
  2224. case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
  2225. case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
  2226. case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
  2227. case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
  2228. default: break;
  2229. }
  2230. if (key != 0)
  2231. {
  2232. updateKeyModifiers();
  2233. if (hwnd == GetActiveWindow())
  2234. {
  2235. handleKeyPress (key, 0);
  2236. return true;
  2237. }
  2238. }
  2239. return false;
  2240. }
  2241. bool isConstrainedNativeWindow() const
  2242. {
  2243. return constrainer != nullptr
  2244. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)
  2245. && ! isKioskMode();
  2246. }
  2247. Rectangle<int> getCurrentScaledBounds (float scale) const
  2248. {
  2249. return ScalingHelpers::unscaledScreenPosToScaled (scale, windowBorder.addedTo (ScalingHelpers::scaledScreenPosToUnscaled (scale, component.getBounds())));
  2250. }
  2251. LRESULT handleSizeConstraining (RECT& r, const WPARAM wParam)
  2252. {
  2253. if (isConstrainedNativeWindow())
  2254. {
  2255. const float scale = getComponent().getDesktopScaleFactor();
  2256. Rectangle<int> pos (ScalingHelpers::unscaledScreenPosToScaled (scale, rectangleFromRECT (r)));
  2257. const Rectangle<int> current (getCurrentScaledBounds (scale));
  2258. constrainer->checkBounds (pos, current,
  2259. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2260. wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
  2261. wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
  2262. wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
  2263. wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
  2264. pos = ScalingHelpers::scaledScreenPosToUnscaled (scale, pos);
  2265. r.left = pos.getX();
  2266. r.top = pos.getY();
  2267. r.right = pos.getRight();
  2268. r.bottom = pos.getBottom();
  2269. }
  2270. return TRUE;
  2271. }
  2272. LRESULT handlePositionChanging (WINDOWPOS& wp)
  2273. {
  2274. if (isConstrainedNativeWindow())
  2275. {
  2276. if ((wp.flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
  2277. && ! Component::isMouseButtonDownAnywhere())
  2278. {
  2279. const float scale = getComponent().getDesktopScaleFactor();
  2280. Rectangle<int> pos (ScalingHelpers::unscaledScreenPosToScaled (scale, Rectangle<int> (wp.x, wp.y, wp.cx, wp.cy)));
  2281. const Rectangle<int> current (getCurrentScaledBounds (scale));
  2282. constrainer->checkBounds (pos, current,
  2283. Desktop::getInstance().getDisplays().getTotalBounds (true),
  2284. pos.getY() != current.getY() && pos.getBottom() == current.getBottom(),
  2285. pos.getX() != current.getX() && pos.getRight() == current.getRight(),
  2286. pos.getY() == current.getY() && pos.getBottom() != current.getBottom(),
  2287. pos.getX() == current.getX() && pos.getRight() != current.getRight());
  2288. pos = ScalingHelpers::scaledScreenPosToUnscaled (scale, pos);
  2289. wp.x = pos.getX();
  2290. wp.y = pos.getY();
  2291. wp.cx = pos.getWidth();
  2292. wp.cy = pos.getHeight();
  2293. }
  2294. }
  2295. if (((wp.flags & SWP_SHOWWINDOW) != 0 && ! component.isVisible()))
  2296. component.setVisible (true);
  2297. else if (((wp.flags & SWP_HIDEWINDOW) != 0 && component.isVisible()))
  2298. component.setVisible (false);
  2299. return 0;
  2300. }
  2301. bool handlePositionChanged()
  2302. {
  2303. const Point<float> pos (getCurrentMousePos());
  2304. if (contains (pos.roundToInt(), false))
  2305. {
  2306. if (! areOtherTouchSourcesActive())
  2307. doMouseEvent (pos, MouseInputSource::invalidPressure);
  2308. if (! isValidPeer (this))
  2309. return true;
  2310. }
  2311. handleMovedOrResized();
  2312. return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly..
  2313. }
  2314. void handleAppActivation (const WPARAM wParam)
  2315. {
  2316. modifiersAtLastCallback = -1;
  2317. updateKeyModifiers();
  2318. if (isMinimised())
  2319. {
  2320. component.repaint();
  2321. handleMovedOrResized();
  2322. if (! isValidPeer (this))
  2323. return;
  2324. }
  2325. Component* underMouse = component.getComponentAt (component.getMouseXYRelative());
  2326. if (underMouse == nullptr)
  2327. underMouse = &component;
  2328. if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
  2329. {
  2330. if (LOWORD (wParam) == WA_CLICKACTIVE)
  2331. Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
  2332. else
  2333. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  2334. }
  2335. else
  2336. {
  2337. handleBroughtToFront();
  2338. }
  2339. }
  2340. void handlePowerBroadcast (WPARAM wParam)
  2341. {
  2342. if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
  2343. {
  2344. switch (wParam)
  2345. {
  2346. case PBT_APMSUSPEND: app->suspended(); break;
  2347. case PBT_APMQUERYSUSPENDFAILED:
  2348. case PBT_APMRESUMECRITICAL:
  2349. case PBT_APMRESUMESUSPEND:
  2350. case PBT_APMRESUMEAUTOMATIC: app->resumed(); break;
  2351. default: break;
  2352. }
  2353. }
  2354. }
  2355. void handleLeftClickInNCArea (WPARAM wParam)
  2356. {
  2357. if (! sendInputAttemptWhenModalMessage())
  2358. {
  2359. switch (wParam)
  2360. {
  2361. case HTBOTTOM:
  2362. case HTBOTTOMLEFT:
  2363. case HTBOTTOMRIGHT:
  2364. case HTGROWBOX:
  2365. case HTLEFT:
  2366. case HTRIGHT:
  2367. case HTTOP:
  2368. case HTTOPLEFT:
  2369. case HTTOPRIGHT:
  2370. if (isConstrainedNativeWindow())
  2371. {
  2372. constrainerIsResizing = true;
  2373. constrainer->resizeStart();
  2374. }
  2375. break;
  2376. default:
  2377. break;
  2378. }
  2379. }
  2380. }
  2381. void initialiseSysMenu (HMENU menu) const
  2382. {
  2383. if (! hasTitleBar())
  2384. {
  2385. if (isFullScreen())
  2386. {
  2387. EnableMenuItem (menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  2388. EnableMenuItem (menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  2389. }
  2390. else if (! isMinimised())
  2391. {
  2392. EnableMenuItem (menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
  2393. }
  2394. }
  2395. }
  2396. void doSettingChange()
  2397. {
  2398. Desktop& desktop = Desktop::getInstance();
  2399. const_cast<Desktop::Displays&> (desktop.getDisplays()).refresh();
  2400. if (fullScreen && ! isMinimised())
  2401. {
  2402. const Desktop::Displays::Display& display
  2403. = desktop.getDisplays().getDisplayContaining (component.getScreenBounds().getCentre());
  2404. setWindowPos (hwnd, display.userArea * display.scale,
  2405. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
  2406. }
  2407. }
  2408. void handleDPIChange() // happens when a window moves to a screen with a different DPI.
  2409. {
  2410. }
  2411. //==============================================================================
  2412. #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
  2413. void setModifierKeyProvider (ModifierKeyProvider* provider) override
  2414. {
  2415. modProvider = provider;
  2416. }
  2417. void removeModifierKeyProvider() override
  2418. {
  2419. modProvider = nullptr;
  2420. }
  2421. #endif
  2422. //==============================================================================
  2423. public:
  2424. static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  2425. {
  2426. if (auto* peer = getOwnerOfWindow (h))
  2427. {
  2428. jassert (isValidPeer (peer));
  2429. return peer->peerWindowProc (h, message, wParam, lParam);
  2430. }
  2431. return DefWindowProcW (h, message, wParam, lParam);
  2432. }
  2433. private:
  2434. static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
  2435. {
  2436. if (MessageManager::getInstance()->currentThreadHasLockedMessageManager())
  2437. return callback (userData);
  2438. return MessageManager::getInstance()->callFunctionOnMessageThread (callback, userData);
  2439. }
  2440. static Point<float> getPointFromLParam (LPARAM lParam) noexcept
  2441. {
  2442. return Point<float> (static_cast<float> (GET_X_LPARAM (lParam)),
  2443. static_cast<float> (GET_Y_LPARAM (lParam)));
  2444. }
  2445. static Point<float> getCurrentMousePosGlobal() noexcept
  2446. {
  2447. return getPointFromLParam (GetMessagePos());
  2448. }
  2449. Point<float> getCurrentMousePos() noexcept
  2450. {
  2451. return globalToLocal (getCurrentMousePosGlobal());
  2452. }
  2453. LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  2454. {
  2455. switch (message)
  2456. {
  2457. //==============================================================================
  2458. case WM_NCHITTEST:
  2459. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  2460. return HTTRANSPARENT;
  2461. if (! hasTitleBar())
  2462. return HTCLIENT;
  2463. break;
  2464. //==============================================================================
  2465. case WM_PAINT:
  2466. handlePaintMessage();
  2467. return 0;
  2468. case WM_NCPAINT:
  2469. if (wParam != 1) // (1 = a repaint of the entire NC region)
  2470. handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
  2471. if (hasTitleBar())
  2472. break; // let the DefWindowProc handle drawing the frame.
  2473. return 0;
  2474. case WM_ERASEBKGND:
  2475. case WM_NCCALCSIZE:
  2476. if (hasTitleBar())
  2477. break;
  2478. return 1;
  2479. //==============================================================================
  2480. case WM_POINTERUPDATE:
  2481. if (handlePointerInput (wParam, lParam, false, false))
  2482. return 0;
  2483. break;
  2484. case WM_POINTERDOWN:
  2485. if (handlePointerInput (wParam, lParam, true, false))
  2486. return 0;
  2487. break;
  2488. case WM_POINTERUP:
  2489. if (handlePointerInput (wParam, lParam, false, true))
  2490. return 0;
  2491. break;
  2492. //==============================================================================
  2493. case WM_MOUSEMOVE: doMouseMove (getPointFromLParam (lParam), false); return 0;
  2494. case WM_POINTERLEAVE:
  2495. case WM_MOUSELEAVE: doMouseExit(); return 0;
  2496. case WM_LBUTTONDOWN:
  2497. case WM_MBUTTONDOWN:
  2498. case WM_RBUTTONDOWN: doMouseDown (getPointFromLParam (lParam), wParam); return 0;
  2499. case WM_LBUTTONUP:
  2500. case WM_MBUTTONUP:
  2501. case WM_RBUTTONUP: doMouseUp (getPointFromLParam (lParam), wParam); return 0;
  2502. case WM_POINTERWHEEL:
  2503. case 0x020A: /* WM_MOUSEWHEEL */ doMouseWheel (wParam, true); return 0;
  2504. case WM_POINTERHWHEEL:
  2505. case 0x020E: /* WM_MOUSEHWHEEL */ doMouseWheel (wParam, false); return 0;
  2506. case WM_CAPTURECHANGED: doCaptureChanged(); return 0;
  2507. case WM_NCPOINTERUPDATE:
  2508. case WM_NCMOUSEMOVE:
  2509. if (hasTitleBar())
  2510. break;
  2511. return 0;
  2512. case WM_TOUCH:
  2513. if (getTouchInputInfo != nullptr)
  2514. return doTouchEvent ((int) wParam, (HTOUCHINPUT) lParam);
  2515. break;
  2516. case 0x119: /* WM_GESTURE */
  2517. if (doGestureEvent (lParam))
  2518. return 0;
  2519. break;
  2520. //==============================================================================
  2521. case WM_SIZING: return handleSizeConstraining (*(RECT*) lParam, wParam);
  2522. case WM_WINDOWPOSCHANGING: return handlePositionChanging (*(WINDOWPOS*) lParam);
  2523. case WM_WINDOWPOSCHANGED:
  2524. {
  2525. const WINDOWPOS& wPos = *reinterpret_cast<WINDOWPOS*> (lParam);
  2526. if ((wPos.flags & SWP_NOMOVE) != 0 && (wPos.flags & SWP_NOSIZE) != 0)
  2527. startTimer(100);
  2528. else
  2529. if (handlePositionChanged())
  2530. return 0;
  2531. }
  2532. break;
  2533. //==============================================================================
  2534. case WM_KEYDOWN:
  2535. case WM_SYSKEYDOWN:
  2536. if (doKeyDown (wParam))
  2537. return 0;
  2538. forwardMessageToParent (message, wParam, lParam);
  2539. break;
  2540. case WM_KEYUP:
  2541. case WM_SYSKEYUP:
  2542. if (doKeyUp (wParam))
  2543. return 0;
  2544. forwardMessageToParent (message, wParam, lParam);
  2545. break;
  2546. case WM_CHAR:
  2547. if (doKeyChar ((int) wParam, lParam))
  2548. return 0;
  2549. forwardMessageToParent (message, wParam, lParam);
  2550. break;
  2551. case WM_APPCOMMAND:
  2552. if (doAppCommand (lParam))
  2553. return TRUE;
  2554. break;
  2555. case WM_MENUCHAR: // triggered when alt+something is pressed
  2556. return MNC_CLOSE << 16; // (avoids making the default system beep)
  2557. //==============================================================================
  2558. case WM_SETFOCUS:
  2559. updateKeyModifiers();
  2560. handleFocusGain();
  2561. break;
  2562. case WM_KILLFOCUS:
  2563. if (hasCreatedCaret)
  2564. {
  2565. hasCreatedCaret = false;
  2566. DestroyCaret();
  2567. }
  2568. handleFocusLoss();
  2569. break;
  2570. case WM_ACTIVATEAPP:
  2571. // Windows does weird things to process priority when you swap apps,
  2572. // so this forces an update when the app is brought to the front
  2573. if (wParam != FALSE)
  2574. juce_repeatLastProcessPriority();
  2575. else
  2576. Desktop::getInstance().setKioskModeComponent (nullptr); // turn kiosk mode off if we lose focus
  2577. juce_checkCurrentlyFocusedTopLevelWindow();
  2578. modifiersAtLastCallback = -1;
  2579. return 0;
  2580. case WM_ACTIVATE:
  2581. if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
  2582. {
  2583. handleAppActivation (wParam);
  2584. return 0;
  2585. }
  2586. break;
  2587. case WM_NCACTIVATE:
  2588. // while a temporary window is being shown, prevent Windows from deactivating the
  2589. // title bars of our main windows.
  2590. if (wParam == 0 && ! shouldDeactivateTitleBar)
  2591. wParam = TRUE; // change this and let it get passed to the DefWindowProc.
  2592. break;
  2593. case WM_POINTERACTIVATE:
  2594. case WM_MOUSEACTIVATE:
  2595. if (! component.getMouseClickGrabsKeyboardFocus())
  2596. return MA_NOACTIVATE;
  2597. break;
  2598. case WM_SHOWWINDOW:
  2599. if (wParam != 0)
  2600. {
  2601. component.setVisible (true);
  2602. handleBroughtToFront();
  2603. }
  2604. break;
  2605. case WM_CLOSE:
  2606. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  2607. handleUserClosingWindow();
  2608. return 0;
  2609. case WM_QUERYENDSESSION:
  2610. if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
  2611. {
  2612. app->systemRequestedQuit();
  2613. return MessageManager::getInstance()->hasStopMessageBeenSent();
  2614. }
  2615. return TRUE;
  2616. case WM_POWERBROADCAST:
  2617. handlePowerBroadcast (wParam);
  2618. break;
  2619. case WM_SYNCPAINT:
  2620. return 0;
  2621. case WM_DISPLAYCHANGE:
  2622. InvalidateRect (h, 0, 0);
  2623. // intentional fall-through...
  2624. case WM_SETTINGCHANGE: // note the fall-through in the previous case!
  2625. doSettingChange();
  2626. break;
  2627. case 0x2e0: // WM_DPICHANGED
  2628. handleDPIChange();
  2629. break;
  2630. case WM_INITMENU:
  2631. initialiseSysMenu ((HMENU) wParam);
  2632. break;
  2633. case WM_SYSCOMMAND:
  2634. switch (wParam & 0xfff0)
  2635. {
  2636. case SC_CLOSE:
  2637. if (sendInputAttemptWhenModalMessage())
  2638. return 0;
  2639. if (hasTitleBar())
  2640. {
  2641. PostMessage (h, WM_CLOSE, 0, 0);
  2642. return 0;
  2643. }
  2644. break;
  2645. case SC_KEYMENU:
  2646. #if ! JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU
  2647. // This test prevents a press of the ALT key from triggering the ancient top-left window menu.
  2648. // By default we suppress this behaviour because it's unlikely that more than a tiny subset of
  2649. // our users will actually want it, and it causes problems if you're trying to use the ALT key
  2650. // as a modifier for mouse actions. If you really need the old behaviour, then just define
  2651. // JUCE_WINDOWS_ALT_KEY_TRIGGERS_MENU=1 in your app.
  2652. if ((lParam >> 16) <= 0) // Values above zero indicate that a mouse-click triggered the menu
  2653. return 0;
  2654. #endif
  2655. // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
  2656. // situations that can arise if a modal loop is started from an alt-key keypress).
  2657. if (hasTitleBar() && h == GetCapture())
  2658. ReleaseCapture();
  2659. break;
  2660. case SC_MAXIMIZE:
  2661. if (! sendInputAttemptWhenModalMessage())
  2662. setFullScreen (true);
  2663. return 0;
  2664. case SC_MINIMIZE:
  2665. if (sendInputAttemptWhenModalMessage())
  2666. return 0;
  2667. if (! hasTitleBar())
  2668. {
  2669. setMinimised (true);
  2670. return 0;
  2671. }
  2672. break;
  2673. case SC_RESTORE:
  2674. if (sendInputAttemptWhenModalMessage())
  2675. return 0;
  2676. if (hasTitleBar())
  2677. {
  2678. if (isFullScreen())
  2679. {
  2680. setFullScreen (false);
  2681. return 0;
  2682. }
  2683. }
  2684. else
  2685. {
  2686. if (isMinimised())
  2687. setMinimised (false);
  2688. else if (isFullScreen())
  2689. setFullScreen (false);
  2690. return 0;
  2691. }
  2692. break;
  2693. }
  2694. break;
  2695. case WM_NCPOINTERDOWN:
  2696. case WM_NCLBUTTONDOWN:
  2697. handleLeftClickInNCArea (wParam);
  2698. break;
  2699. case WM_NCRBUTTONDOWN:
  2700. case WM_NCMBUTTONDOWN:
  2701. sendInputAttemptWhenModalMessage();
  2702. break;
  2703. case WM_IME_SETCONTEXT:
  2704. imeHandler.handleSetContext (h, wParam == TRUE);
  2705. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  2706. break;
  2707. case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
  2708. case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
  2709. case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
  2710. case WM_GETDLGCODE:
  2711. return DLGC_WANTALLKEYS;
  2712. default:
  2713. break;
  2714. }
  2715. return DefWindowProcW (h, message, wParam, lParam);
  2716. }
  2717. bool sendInputAttemptWhenModalMessage()
  2718. {
  2719. if (component.isCurrentlyBlockedByAnotherModalComponent())
  2720. {
  2721. if (Component* const current = Component::getCurrentlyModalComponent())
  2722. current->inputAttemptWhenModal();
  2723. return true;
  2724. }
  2725. return false;
  2726. }
  2727. //==============================================================================
  2728. class IMEHandler
  2729. {
  2730. public:
  2731. IMEHandler()
  2732. {
  2733. reset();
  2734. }
  2735. void handleSetContext (HWND hWnd, const bool windowIsActive)
  2736. {
  2737. if (compositionInProgress && ! windowIsActive)
  2738. {
  2739. compositionInProgress = false;
  2740. if (HIMC hImc = ImmGetContext (hWnd))
  2741. {
  2742. ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  2743. ImmReleaseContext (hWnd, hImc);
  2744. }
  2745. }
  2746. }
  2747. void handleStartComposition (ComponentPeer& owner)
  2748. {
  2749. reset();
  2750. if (TextInputTarget* const target = owner.findCurrentTextInputTarget())
  2751. target->insertTextAtCaret (String());
  2752. }
  2753. void handleEndComposition (ComponentPeer& owner, HWND hWnd)
  2754. {
  2755. if (compositionInProgress)
  2756. {
  2757. // If this occurs, the user has cancelled the composition, so clear their changes..
  2758. if (TextInputTarget* const target = owner.findCurrentTextInputTarget())
  2759. {
  2760. target->setHighlightedRegion (compositionRange);
  2761. target->insertTextAtCaret (String());
  2762. compositionRange.setLength (0);
  2763. target->setHighlightedRegion (Range<int>::emptyRange (compositionRange.getEnd()));
  2764. target->setTemporaryUnderlining (Array<Range<int> >());
  2765. }
  2766. if (HIMC hImc = ImmGetContext (hWnd))
  2767. {
  2768. ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
  2769. ImmReleaseContext (hWnd, hImc);
  2770. }
  2771. }
  2772. reset();
  2773. }
  2774. void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
  2775. {
  2776. TextInputTarget* const target = owner.findCurrentTextInputTarget();
  2777. HIMC hImc = ImmGetContext (hWnd);
  2778. if (target == nullptr || hImc == 0)
  2779. return;
  2780. if (compositionRange.getStart() < 0)
  2781. compositionRange = Range<int>::emptyRange (target->getHighlightedRegion().getStart());
  2782. if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
  2783. {
  2784. replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
  2785. Range<int>::emptyRange (-1));
  2786. reset();
  2787. target->setTemporaryUnderlining (Array<Range<int> >());
  2788. }
  2789. else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
  2790. {
  2791. replaceCurrentSelection (target, getCompositionString (hImc, GCS_COMPSTR),
  2792. getCompositionSelection (hImc, lParam));
  2793. target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
  2794. compositionInProgress = true;
  2795. }
  2796. moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
  2797. ImmReleaseContext (hWnd, hImc);
  2798. }
  2799. private:
  2800. //==============================================================================
  2801. Range<int> compositionRange; // The range being modified in the TextInputTarget
  2802. bool compositionInProgress;
  2803. //==============================================================================
  2804. void reset()
  2805. {
  2806. compositionRange = Range<int>::emptyRange (-1);
  2807. compositionInProgress = false;
  2808. }
  2809. String getCompositionString (HIMC hImc, const DWORD type) const
  2810. {
  2811. jassert (hImc != 0);
  2812. const int stringSizeBytes = ImmGetCompositionString (hImc, type, 0, 0);
  2813. if (stringSizeBytes > 0)
  2814. {
  2815. HeapBlock<TCHAR> buffer;
  2816. buffer.calloc (stringSizeBytes / sizeof (TCHAR) + 1);
  2817. ImmGetCompositionString (hImc, type, buffer, (DWORD) stringSizeBytes);
  2818. return String (buffer);
  2819. }
  2820. return {};
  2821. }
  2822. int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
  2823. {
  2824. jassert (hImc != 0);
  2825. if ((lParam & CS_NOMOVECARET) != 0)
  2826. return compositionRange.getStart();
  2827. if ((lParam & GCS_CURSORPOS) != 0)
  2828. {
  2829. const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, 0, 0);
  2830. return compositionRange.getStart() + jmax (0, localCaretPos);
  2831. }
  2832. return compositionRange.getStart() + currentIMEString.length();
  2833. }
  2834. // Get selected/highlighted range while doing composition:
  2835. // returned range is relative to beginning of TextInputTarget, not composition string
  2836. Range<int> getCompositionSelection (HIMC hImc, LPARAM lParam) const
  2837. {
  2838. jassert (hImc != 0);
  2839. int selectionStart = 0;
  2840. int selectionEnd = 0;
  2841. if ((lParam & GCS_COMPATTR) != 0)
  2842. {
  2843. // Get size of attributes array:
  2844. const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, 0, 0);
  2845. if (attributeSizeBytes > 0)
  2846. {
  2847. // Get attributes (8 bit flag per character):
  2848. HeapBlock<char> attributes ((size_t) attributeSizeBytes);
  2849. ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, (DWORD) attributeSizeBytes);
  2850. selectionStart = 0;
  2851. for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
  2852. if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
  2853. break;
  2854. for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
  2855. if (attributes [selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
  2856. break;
  2857. }
  2858. }
  2859. return Range<int> (selectionStart, selectionEnd) + compositionRange.getStart();
  2860. }
  2861. void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, Range<int> newSelection)
  2862. {
  2863. if (compositionInProgress)
  2864. target->setHighlightedRegion (compositionRange);
  2865. target->insertTextAtCaret (newContent);
  2866. compositionRange.setLength (newContent.length());
  2867. if (newSelection.getStart() < 0)
  2868. newSelection = Range<int>::emptyRange (compositionRange.getEnd());
  2869. target->setHighlightedRegion (newSelection);
  2870. }
  2871. Array<Range<int> > getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
  2872. {
  2873. Array<Range<int> > result;
  2874. if (hImc != 0 && (lParam & GCS_COMPCLAUSE) != 0)
  2875. {
  2876. const int clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, 0, 0);
  2877. if (clauseDataSizeBytes > 0)
  2878. {
  2879. const size_t numItems = clauseDataSizeBytes / sizeof (uint32);
  2880. HeapBlock<uint32> clauseData (numItems);
  2881. if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, (DWORD) clauseDataSizeBytes) > 0)
  2882. for (size_t i = 0; i + 1 < numItems; ++i)
  2883. result.add (Range<int> ((int) clauseData [i], (int) clauseData [i + 1]) + compositionRange.getStart());
  2884. }
  2885. }
  2886. return result;
  2887. }
  2888. void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
  2889. {
  2890. if (Component* const targetComp = dynamic_cast<Component*> (target))
  2891. {
  2892. const Rectangle<int> area (peer.getComponent().getLocalArea (targetComp, target->getCaretRectangle()));
  2893. CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
  2894. ImmSetCandidateWindow (hImc, &pos);
  2895. }
  2896. }
  2897. JUCE_DECLARE_NON_COPYABLE (IMEHandler)
  2898. };
  2899. void timerCallback() override
  2900. {
  2901. handlePositionChanged();
  2902. stopTimer();
  2903. }
  2904. IMEHandler imeHandler;
  2905. //==============================================================================
  2906. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer)
  2907. };
  2908. ModifierKeys HWNDComponentPeer::currentModifiers;
  2909. ModifierKeys HWNDComponentPeer::modifiersAtLastCallback;
  2910. ComponentPeer* Component::createNewPeer (int styleFlags, void* parentHWND)
  2911. {
  2912. return new HWNDComponentPeer (*this, styleFlags, (HWND) parentHWND, false);
  2913. }
  2914. JUCE_API ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component& component, void* parentHWND)
  2915. {
  2916. return new HWNDComponentPeer (component, ComponentPeer::windowIgnoresMouseClicks,
  2917. (HWND) parentHWND, true);
  2918. }
  2919. juce_ImplementSingleton_SingleThreaded (HWNDComponentPeer::WindowClassHolder)
  2920. //==============================================================================
  2921. void ModifierKeys::updateCurrentModifiers() noexcept
  2922. {
  2923. currentModifiers = HWNDComponentPeer::currentModifiers;
  2924. }
  2925. ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept
  2926. {
  2927. HWNDComponentPeer::updateKeyModifiers();
  2928. int mouseMods = 0;
  2929. if (HWNDComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
  2930. if (HWNDComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
  2931. if (HWNDComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
  2932. HWNDComponentPeer::currentModifiers
  2933. = HWNDComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  2934. return HWNDComponentPeer::currentModifiers;
  2935. }
  2936. //==============================================================================
  2937. bool KeyPress::isKeyCurrentlyDown (const int keyCode)
  2938. {
  2939. SHORT k = (SHORT) keyCode;
  2940. if ((keyCode & extendedKeyModifier) == 0)
  2941. {
  2942. if (k >= (SHORT) 'a' && k <= (SHORT) 'z')
  2943. k += (SHORT) 'A' - (SHORT) 'a';
  2944. // Only translate if extendedKeyModifier flag is not set
  2945. const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
  2946. (SHORT) '+', VK_OEM_PLUS,
  2947. (SHORT) '-', VK_OEM_MINUS,
  2948. (SHORT) '.', VK_OEM_PERIOD,
  2949. (SHORT) ';', VK_OEM_1,
  2950. (SHORT) ':', VK_OEM_1,
  2951. (SHORT) '/', VK_OEM_2,
  2952. (SHORT) '?', VK_OEM_2,
  2953. (SHORT) '[', VK_OEM_4,
  2954. (SHORT) ']', VK_OEM_6 };
  2955. for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
  2956. if (k == translatedValues [i])
  2957. k = translatedValues [i + 1];
  2958. }
  2959. return HWNDComponentPeer::isKeyDown (k);
  2960. }
  2961. // (This internal function is used by the plugin client module)
  2962. bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); }
  2963. //==============================================================================
  2964. bool JUCE_CALLTYPE Process::isForegroundProcess()
  2965. {
  2966. HWND fg = GetForegroundWindow();
  2967. if (fg == 0)
  2968. return true;
  2969. DWORD processID = 0;
  2970. GetWindowThreadProcessId (fg, &processID);
  2971. return (processID == GetCurrentProcessId());
  2972. }
  2973. // N/A on Windows as far as I know.
  2974. void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  2975. void JUCE_CALLTYPE Process::hide() {}
  2976. //==============================================================================
  2977. static BOOL CALLBACK enumAlwaysOnTopWindows (HWND hwnd, LPARAM lParam)
  2978. {
  2979. if (IsWindowVisible (hwnd))
  2980. {
  2981. DWORD processID = 0;
  2982. GetWindowThreadProcessId (hwnd, &processID);
  2983. if (processID == GetCurrentProcessId())
  2984. {
  2985. WINDOWINFO info;
  2986. if (GetWindowInfo (hwnd, &info)
  2987. && (info.dwExStyle & WS_EX_TOPMOST) != 0)
  2988. {
  2989. *reinterpret_cast<bool*> (lParam) = true;
  2990. return FALSE;
  2991. }
  2992. }
  2993. }
  2994. return TRUE;
  2995. }
  2996. bool juce_areThereAnyAlwaysOnTopWindows()
  2997. {
  2998. bool anyAlwaysOnTopFound = false;
  2999. EnumWindows (&enumAlwaysOnTopWindows, (LPARAM) &anyAlwaysOnTopFound);
  3000. return anyAlwaysOnTopFound;
  3001. }
  3002. //==============================================================================
  3003. class WindowsMessageBox : public AsyncUpdater
  3004. {
  3005. public:
  3006. WindowsMessageBox (AlertWindow::AlertIconType iconType,
  3007. const String& boxTitle, const String& m,
  3008. Component* associatedComponent, UINT extraFlags,
  3009. ModalComponentManager::Callback* cb, const bool runAsync)
  3010. : flags (extraFlags | getMessageBoxFlags (iconType)),
  3011. owner (getWindowForMessageBox (associatedComponent)),
  3012. title (boxTitle), message (m), callback (cb)
  3013. {
  3014. if (runAsync)
  3015. triggerAsyncUpdate();
  3016. }
  3017. int getResult() const
  3018. {
  3019. const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags);
  3020. return (r == IDYES || r == IDOK) ? 1 : (r == IDNO && (flags & 1) != 0 ? 2 : 0);
  3021. }
  3022. void handleAsyncUpdate() override
  3023. {
  3024. const int result = getResult();
  3025. if (callback != nullptr)
  3026. callback->modalStateFinished (result);
  3027. delete this;
  3028. }
  3029. private:
  3030. UINT flags;
  3031. HWND owner;
  3032. String title, message;
  3033. ScopedPointer<ModalComponentManager::Callback> callback;
  3034. static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) noexcept
  3035. {
  3036. UINT flags = MB_TASKMODAL | MB_SETFOREGROUND;
  3037. switch (iconType)
  3038. {
  3039. case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break;
  3040. case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break;
  3041. case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break;
  3042. default: break;
  3043. }
  3044. return flags;
  3045. }
  3046. static HWND getWindowForMessageBox (Component* associatedComponent)
  3047. {
  3048. return associatedComponent != nullptr ? (HWND) associatedComponent->getWindowHandle() : 0;
  3049. }
  3050. };
  3051. #if JUCE_MODAL_LOOPS_PERMITTED
  3052. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  3053. const String& title, const String& message,
  3054. Component* associatedComponent)
  3055. {
  3056. WindowsMessageBox box (iconType, title, message, associatedComponent, MB_OK, 0, false);
  3057. (void) box.getResult();
  3058. }
  3059. #endif
  3060. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  3061. const String& title, const String& message,
  3062. Component* associatedComponent,
  3063. ModalComponentManager::Callback* callback)
  3064. {
  3065. new WindowsMessageBox (iconType, title, message, associatedComponent, MB_OK, callback, true);
  3066. }
  3067. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
  3068. const String& title, const String& message,
  3069. Component* associatedComponent,
  3070. ModalComponentManager::Callback* callback)
  3071. {
  3072. ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3073. MB_OKCANCEL, callback, callback != nullptr));
  3074. if (callback == nullptr)
  3075. return mb->getResult() != 0;
  3076. mb.release();
  3077. return false;
  3078. }
  3079. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  3080. const String& title, const String& message,
  3081. Component* associatedComponent,
  3082. ModalComponentManager::Callback* callback)
  3083. {
  3084. ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3085. MB_YESNOCANCEL, callback, callback != nullptr));
  3086. if (callback == nullptr)
  3087. return mb->getResult();
  3088. mb.release();
  3089. return 0;
  3090. }
  3091. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType,
  3092. const String& title, const String& message,
  3093. Component* associatedComponent,
  3094. ModalComponentManager::Callback* callback)
  3095. {
  3096. ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  3097. MB_YESNO, callback, callback != nullptr));
  3098. if (callback == nullptr)
  3099. return mb->getResult();
  3100. mb.release();
  3101. return 0;
  3102. }
  3103. //==============================================================================
  3104. bool MouseInputSource::SourceList::addSource()
  3105. {
  3106. const int numSources = sources.size();
  3107. if (numSources == 0 || canUseMultiTouch())
  3108. {
  3109. addSource (numSources, numSources == 0 ? MouseInputSource::InputSourceType::mouse
  3110. : MouseInputSource::InputSourceType::touch);
  3111. return true;
  3112. }
  3113. return false;
  3114. }
  3115. bool MouseInputSource::SourceList::canUseTouch()
  3116. {
  3117. return canUseMultiTouch();
  3118. }
  3119. Point<float> MouseInputSource::getCurrentRawMousePosition()
  3120. {
  3121. POINT mousePos;
  3122. GetCursorPos (&mousePos);
  3123. return Point<float> ((float) mousePos.x, (float) mousePos.y);
  3124. }
  3125. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  3126. {
  3127. SetCursorPos (roundToInt (newPosition.x),
  3128. roundToInt (newPosition.y));
  3129. }
  3130. //==============================================================================
  3131. class ScreenSaverDefeater : public Timer
  3132. {
  3133. public:
  3134. ScreenSaverDefeater()
  3135. {
  3136. startTimer (10000);
  3137. timerCallback();
  3138. }
  3139. void timerCallback() override
  3140. {
  3141. if (Process::isForegroundProcess())
  3142. {
  3143. INPUT input = { 0 };
  3144. input.type = INPUT_MOUSE;
  3145. input.mi.mouseData = MOUSEEVENTF_MOVE;
  3146. SendInput (1, &input, sizeof (INPUT));
  3147. }
  3148. }
  3149. };
  3150. static ScopedPointer<ScreenSaverDefeater> screenSaverDefeater;
  3151. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  3152. {
  3153. if (isEnabled)
  3154. screenSaverDefeater = nullptr;
  3155. else if (screenSaverDefeater == nullptr)
  3156. screenSaverDefeater = new ScreenSaverDefeater();
  3157. }
  3158. bool Desktop::isScreenSaverEnabled()
  3159. {
  3160. return screenSaverDefeater == nullptr;
  3161. }
  3162. //==============================================================================
  3163. void LookAndFeel::playAlertSound()
  3164. {
  3165. MessageBeep (MB_OK);
  3166. }
  3167. //==============================================================================
  3168. void SystemClipboard::copyTextToClipboard (const String& text)
  3169. {
  3170. if (OpenClipboard (0) != 0)
  3171. {
  3172. if (EmptyClipboard() != 0)
  3173. {
  3174. const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4;
  3175. if (bytesNeeded > 0)
  3176. {
  3177. if (HGLOBAL bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bytesNeeded + sizeof (WCHAR)))
  3178. {
  3179. if (WCHAR* const data = static_cast<WCHAR*> (GlobalLock (bufH)))
  3180. {
  3181. text.copyToUTF16 (data, bytesNeeded);
  3182. GlobalUnlock (bufH);
  3183. SetClipboardData (CF_UNICODETEXT, bufH);
  3184. }
  3185. }
  3186. }
  3187. }
  3188. CloseClipboard();
  3189. }
  3190. }
  3191. String SystemClipboard::getTextFromClipboard()
  3192. {
  3193. String result;
  3194. if (OpenClipboard (0) != 0)
  3195. {
  3196. if (HANDLE bufH = GetClipboardData (CF_UNICODETEXT))
  3197. {
  3198. if (const WCHAR* const data = (const WCHAR*) GlobalLock (bufH))
  3199. {
  3200. result = String (data, (size_t) (GlobalSize (bufH) / sizeof (WCHAR)));
  3201. GlobalUnlock (bufH);
  3202. }
  3203. }
  3204. CloseClipboard();
  3205. }
  3206. return result;
  3207. }
  3208. //==============================================================================
  3209. void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable, bool /*allowMenusAndBars*/)
  3210. {
  3211. if (TopLevelWindow* tlw = dynamic_cast<TopLevelWindow*> (kioskModeComp))
  3212. tlw->setUsingNativeTitleBar (! enableOrDisable);
  3213. if (enableOrDisable)
  3214. kioskModeComp->setBounds (getDisplays().getMainDisplay().totalArea);
  3215. }
  3216. void Desktop::allowedOrientationsChanged() {}
  3217. //==============================================================================
  3218. struct MonitorInfo
  3219. {
  3220. MonitorInfo (Rectangle<int> rect, bool main, double d) noexcept
  3221. : bounds (rect), dpi (d), isMain (main) {}
  3222. Rectangle<int> bounds;
  3223. double dpi;
  3224. bool isMain;
  3225. };
  3226. static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userInfo)
  3227. {
  3228. MONITORINFO info = { 0 };
  3229. info.cbSize = sizeof (info);
  3230. GetMonitorInfo (hm, &info);
  3231. const bool isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0;
  3232. double dpi = 0;
  3233. if (getDPIForMonitor != nullptr)
  3234. {
  3235. UINT dpiX = 0, dpiY = 0;
  3236. if (SUCCEEDED (getDPIForMonitor (hm, MDT_Default, &dpiX, &dpiY)))
  3237. dpi = (dpiX + dpiY) / 2.0;
  3238. }
  3239. ((Array<MonitorInfo>*) userInfo)->add (MonitorInfo (rectangleFromRECT (*r), isMain, dpi));
  3240. return TRUE;
  3241. }
  3242. void Desktop::Displays::findDisplays (float masterScale)
  3243. {
  3244. setDPIAwareness();
  3245. Array<MonitorInfo> monitors;
  3246. EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors);
  3247. const double globalDPI = getGlobalDPI();
  3248. if (monitors.size() == 0)
  3249. monitors.add (MonitorInfo (rectangleFromRECT (getWindowRect (GetDesktopWindow())), true, globalDPI));
  3250. // make sure the first in the list is the main monitor
  3251. for (int i = 1; i < monitors.size(); ++i)
  3252. if (monitors.getReference(i).isMain)
  3253. monitors.swap (i, 0);
  3254. RECT workArea;
  3255. SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0);
  3256. for (int i = 0; i < monitors.size(); ++i)
  3257. {
  3258. Display d;
  3259. d.userArea = d.totalArea = monitors.getReference(i).bounds / masterScale;
  3260. d.isMain = monitors.getReference(i).isMain;
  3261. d.dpi = monitors.getReference(i).dpi;
  3262. if (d.dpi == 0)
  3263. {
  3264. d.scale = masterScale;
  3265. d.dpi = globalDPI;
  3266. }
  3267. else
  3268. {
  3269. d.scale = d.dpi / 96.0;
  3270. }
  3271. if (d.isMain)
  3272. d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale);
  3273. displays.add (d);
  3274. }
  3275. }
  3276. //==============================================================================
  3277. static HICON extractFileHICON (const File& file)
  3278. {
  3279. WORD iconNum = 0;
  3280. WCHAR name [MAX_PATH * 2];
  3281. file.getFullPathName().copyToUTF16 (name, sizeof (name));
  3282. return ExtractAssociatedIcon ((HINSTANCE) Process::getCurrentModuleInstanceHandle(),
  3283. name, &iconNum);
  3284. }
  3285. Image juce_createIconForFile (const File& file)
  3286. {
  3287. Image image;
  3288. if (HICON icon = extractFileHICON (file))
  3289. {
  3290. image = IconConverters::createImageFromHICON (icon);
  3291. DestroyIcon (icon);
  3292. }
  3293. return image;
  3294. }
  3295. //==============================================================================
  3296. void* CustomMouseCursorInfo::create() const
  3297. {
  3298. const int maxW = GetSystemMetrics (SM_CXCURSOR);
  3299. const int maxH = GetSystemMetrics (SM_CYCURSOR);
  3300. Image im (image);
  3301. int hotspotX = hotspot.x;
  3302. int hotspotY = hotspot.y;
  3303. if (im.getWidth() > maxW || im.getHeight() > maxH)
  3304. {
  3305. im = im.rescaled (maxW, maxH);
  3306. hotspotX = (hotspotX * maxW) / image.getWidth();
  3307. hotspotY = (hotspotY * maxH) / image.getHeight();
  3308. }
  3309. return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY);
  3310. }
  3311. void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStandard)
  3312. {
  3313. if (cursorHandle != nullptr && ! isStandard)
  3314. DestroyCursor ((HCURSOR) cursorHandle);
  3315. }
  3316. enum
  3317. {
  3318. hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor)
  3319. };
  3320. void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type)
  3321. {
  3322. LPCTSTR cursorName = IDC_ARROW;
  3323. switch (type)
  3324. {
  3325. case NormalCursor:
  3326. case ParentCursor: break;
  3327. case NoCursor: return (void*) hiddenMouseCursorHandle;
  3328. case WaitCursor: cursorName = IDC_WAIT; break;
  3329. case IBeamCursor: cursorName = IDC_IBEAM; break;
  3330. case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
  3331. case CrosshairCursor: cursorName = IDC_CROSS; break;
  3332. case LeftRightResizeCursor:
  3333. case LeftEdgeResizeCursor:
  3334. case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
  3335. case UpDownResizeCursor:
  3336. case TopEdgeResizeCursor:
  3337. case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
  3338. case TopLeftCornerResizeCursor:
  3339. case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
  3340. case TopRightCornerResizeCursor:
  3341. case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
  3342. case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
  3343. case DraggingHandCursor:
  3344. {
  3345. static void* dragHandCursor = nullptr;
  3346. if (dragHandCursor == nullptr)
  3347. {
  3348. static const unsigned char dragHandData[] =
  3349. { 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,
  3350. 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,
  3351. 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 };
  3352. dragHandCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), 8, 7).create();
  3353. }
  3354. return dragHandCursor;
  3355. }
  3356. case CopyingCursor:
  3357. {
  3358. static void* copyCursor = nullptr;
  3359. if (copyCursor == nullptr)
  3360. {
  3361. static unsigned char copyCursorData[] = { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,
  3362. 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,
  3363. 78,133,218,215,137,31,82,154,100,200,86,91,202,142,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,
  3364. 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 };
  3365. const int copyCursorSize = 119;
  3366. copyCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, copyCursorSize), 1, 3).create();
  3367. }
  3368. return copyCursor;
  3369. }
  3370. default:
  3371. jassertfalse; break;
  3372. }
  3373. HCURSOR cursorH = LoadCursor (0, cursorName);
  3374. if (cursorH == 0)
  3375. cursorH = LoadCursor (0, IDC_ARROW);
  3376. return cursorH;
  3377. }
  3378. //==============================================================================
  3379. void MouseCursor::showInWindow (ComponentPeer*) const
  3380. {
  3381. HCURSOR c = (HCURSOR) getHandle();
  3382. if (c == 0)
  3383. c = LoadCursor (0, IDC_ARROW);
  3384. else if (c == (HCURSOR) hiddenMouseCursorHandle)
  3385. c = 0;
  3386. SetCursor (c);
  3387. }
  3388. void MouseCursor::showInAllWindows() const
  3389. {
  3390. showInWindow (nullptr);
  3391. }