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.

3007 lines
102KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
  19. // these are in the windows SDK, but need to be repeated here for GCC..
  20. #ifndef GET_APPCOMMAND_LPARAM
  21. #define FAPPCOMMAND_MASK 0xF000
  22. #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
  23. #define APPCOMMAND_MEDIA_NEXTTRACK 11
  24. #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
  25. #define APPCOMMAND_MEDIA_STOP 13
  26. #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
  27. #endif
  28. extern void juce_repeatLastProcessPriority();
  29. extern void juce_CheckCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp
  30. extern bool juce_IsRunningInWine();
  31. typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
  32. extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
  33. static bool shouldDeactivateTitleBar = true;
  34. //==============================================================================
  35. typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
  36. static UpdateLayeredWinFunc updateLayeredWindow = 0;
  37. bool Desktop::canUseSemiTransparentWindows() noexcept
  38. {
  39. if (updateLayeredWindow == 0)
  40. {
  41. if (! juce_IsRunningInWine())
  42. {
  43. HMODULE user32Mod = GetModuleHandle (_T("user32.dll"));
  44. updateLayeredWindow = (UpdateLayeredWinFunc) GetProcAddress (user32Mod, "UpdateLayeredWindow");
  45. }
  46. }
  47. return updateLayeredWindow != 0;
  48. }
  49. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  50. {
  51. return upright;
  52. }
  53. int64 getMouseEventTime()
  54. {
  55. static int64 eventTimeOffset = 0;
  56. static LONG lastMessageTime = 0;
  57. const LONG thisMessageTime = GetMessageTime();
  58. if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
  59. {
  60. lastMessageTime = thisMessageTime;
  61. eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
  62. }
  63. return eventTimeOffset + thisMessageTime;
  64. }
  65. //==============================================================================
  66. const int extendedKeyModifier = 0x10000;
  67. const int KeyPress::spaceKey = VK_SPACE;
  68. const int KeyPress::returnKey = VK_RETURN;
  69. const int KeyPress::escapeKey = VK_ESCAPE;
  70. const int KeyPress::backspaceKey = VK_BACK;
  71. const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
  72. const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
  73. const int KeyPress::tabKey = VK_TAB;
  74. const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
  75. const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
  76. const int KeyPress::upKey = VK_UP | extendedKeyModifier;
  77. const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
  78. const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
  79. const int KeyPress::endKey = VK_END | extendedKeyModifier;
  80. const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
  81. const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
  82. const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
  83. const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
  84. const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
  85. const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
  86. const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
  87. const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
  88. const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
  89. const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
  90. const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
  91. const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
  92. const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
  93. const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
  94. const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
  95. const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
  96. const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
  97. const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
  98. const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
  99. const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
  100. const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
  101. const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
  102. const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
  103. const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
  104. const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
  105. const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
  106. const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
  107. const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
  108. const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
  109. const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
  110. const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
  111. const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
  112. const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
  113. const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
  114. const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
  115. const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
  116. const int KeyPress::playKey = 0x30000;
  117. const int KeyPress::stopKey = 0x30001;
  118. const int KeyPress::fastForwardKey = 0x30002;
  119. const int KeyPress::rewindKey = 0x30003;
  120. //==============================================================================
  121. class WindowsBitmapImage : public Image::SharedImage
  122. {
  123. public:
  124. //==============================================================================
  125. WindowsBitmapImage (const Image::PixelFormat format_,
  126. const int w, const int h, const bool clearImage)
  127. : Image::SharedImage (format_, w, h)
  128. {
  129. jassert (format_ == Image::RGB || format_ == Image::ARGB);
  130. pixelStride = (format_ == Image::RGB) ? 3 : 4;
  131. lineStride = -((w * pixelStride + 3) & ~3);
  132. zerostruct (bitmapInfo);
  133. bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
  134. bitmapInfo.bV4Width = w;
  135. bitmapInfo.bV4Height = h;
  136. bitmapInfo.bV4Planes = 1;
  137. bitmapInfo.bV4CSType = 1;
  138. bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
  139. if (format_ == Image::ARGB)
  140. {
  141. bitmapInfo.bV4AlphaMask = 0xff000000;
  142. bitmapInfo.bV4RedMask = 0xff0000;
  143. bitmapInfo.bV4GreenMask = 0xff00;
  144. bitmapInfo.bV4BlueMask = 0xff;
  145. bitmapInfo.bV4V4Compression = BI_BITFIELDS;
  146. }
  147. else
  148. {
  149. bitmapInfo.bV4V4Compression = BI_RGB;
  150. }
  151. HDC dc = GetDC (0);
  152. hdc = CreateCompatibleDC (dc);
  153. ReleaseDC (0, dc);
  154. SetMapMode (hdc, MM_TEXT);
  155. hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
  156. (void**) &bitmapData, 0, 0);
  157. previousBitmap = SelectObject (hdc, hBitmap);
  158. if (format_ == Image::ARGB && clearImage)
  159. zeromem (bitmapData, (size_t) std::abs (h * lineStride));
  160. imageData = bitmapData - (lineStride * (h - 1));
  161. }
  162. ~WindowsBitmapImage()
  163. {
  164. SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
  165. DeleteDC (hdc);
  166. DeleteObject (hBitmap);
  167. }
  168. Image::ImageType getType() const { return Image::NativeImage; }
  169. LowLevelGraphicsContext* createLowLevelContext()
  170. {
  171. return new LowLevelGraphicsSoftwareRenderer (Image (this));
  172. }
  173. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/)
  174. {
  175. bitmap.data = imageData + x * pixelStride + y * lineStride;
  176. bitmap.pixelFormat = format;
  177. bitmap.lineStride = lineStride;
  178. bitmap.pixelStride = pixelStride;
  179. }
  180. Image::SharedImage* clone()
  181. {
  182. WindowsBitmapImage* im = new WindowsBitmapImage (format, width, height, false);
  183. for (int i = 0; i < height; ++i)
  184. memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
  185. return im;
  186. }
  187. void blitToWindow (HWND hwnd, HDC dc, const bool transparent,
  188. const int x, const int y,
  189. const RectangleList& maskedRegion,
  190. const uint8 updateLayeredWindowAlpha) noexcept
  191. {
  192. static HDRAWDIB hdd = createDrawDIB();
  193. SetMapMode (dc, MM_TEXT);
  194. if (transparent)
  195. {
  196. if (! maskedRegion.isEmpty())
  197. {
  198. for (RectangleList::Iterator i (maskedRegion); i.next();)
  199. {
  200. const Rectangle<int>& r = *i.getRectangle();
  201. ExcludeClipRect (hdc, r.getX(), r.getY(), r.getRight(), r.getBottom());
  202. }
  203. }
  204. RECT windowBounds;
  205. GetWindowRect (hwnd, &windowBounds);
  206. POINT p = { -x, -y };
  207. POINT pos = { windowBounds.left, windowBounds.top };
  208. SIZE size = { windowBounds.right - windowBounds.left,
  209. windowBounds.bottom - windowBounds.top };
  210. BLENDFUNCTION bf;
  211. bf.AlphaFormat = 1 /*AC_SRC_ALPHA*/;
  212. bf.BlendFlags = 0;
  213. bf.BlendOp = AC_SRC_OVER;
  214. bf.SourceConstantAlpha = updateLayeredWindowAlpha;
  215. updateLayeredWindow (hwnd, 0, &pos, &size, hdc, &p, 0, &bf, 2 /*ULW_ALPHA*/);
  216. }
  217. else
  218. {
  219. int savedDC = 0;
  220. if (! maskedRegion.isEmpty())
  221. {
  222. savedDC = SaveDC (dc);
  223. for (RectangleList::Iterator i (maskedRegion); i.next();)
  224. {
  225. const Rectangle<int>& r = *i.getRectangle();
  226. ExcludeClipRect (dc, r.getX(), r.getY(), r.getRight(), r.getBottom());
  227. }
  228. }
  229. if (hdd == 0)
  230. {
  231. StretchDIBits (dc,
  232. x, y, width, height,
  233. 0, 0, width, height,
  234. bitmapData, (const BITMAPINFO*) &bitmapInfo,
  235. DIB_RGB_COLORS, SRCCOPY);
  236. }
  237. else
  238. {
  239. DrawDibDraw (hdd, dc, x, y, -1, -1,
  240. (BITMAPINFOHEADER*) &bitmapInfo, bitmapData,
  241. 0, 0, width, height, 0);
  242. }
  243. if (! maskedRegion.isEmpty())
  244. RestoreDC (dc, savedDC);
  245. }
  246. }
  247. HBITMAP hBitmap;
  248. HGDIOBJ previousBitmap;
  249. BITMAPV4HEADER bitmapInfo;
  250. HDC hdc;
  251. uint8* bitmapData;
  252. int pixelStride, lineStride;
  253. uint8* imageData;
  254. private:
  255. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage);
  256. static HDRAWDIB createDrawDIB() noexcept
  257. {
  258. HDC dc = GetDC (0);
  259. const int n = GetDeviceCaps (dc, BITSPIXEL);
  260. ReleaseDC (0, dc);
  261. // only open if we're not palettised
  262. return n > 8 ? DrawDibOpen() : 0;
  263. }
  264. };
  265. //==============================================================================
  266. namespace IconConverters
  267. {
  268. Image createImageFromHBITMAP (HBITMAP bitmap)
  269. {
  270. Image im;
  271. if (bitmap != 0)
  272. {
  273. BITMAP bm;
  274. if (GetObject (bitmap, sizeof (BITMAP), &bm)
  275. && bm.bmWidth > 0 && bm.bmHeight > 0)
  276. {
  277. HDC tempDC = GetDC (0);
  278. HDC dc = CreateCompatibleDC (tempDC);
  279. ReleaseDC (0, tempDC);
  280. SelectObject (dc, bitmap);
  281. im = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
  282. Image::BitmapData imageData (im, Image::BitmapData::writeOnly);
  283. for (int y = bm.bmHeight; --y >= 0;)
  284. {
  285. for (int x = bm.bmWidth; --x >= 0;)
  286. {
  287. COLORREF col = GetPixel (dc, x, y);
  288. imageData.setPixelColour (x, y, Colour ((uint8) GetRValue (col),
  289. (uint8) GetGValue (col),
  290. (uint8) GetBValue (col)));
  291. }
  292. }
  293. DeleteDC (dc);
  294. }
  295. }
  296. return im;
  297. }
  298. Image createImageFromHICON (HICON icon)
  299. {
  300. ICONINFO info;
  301. if (GetIconInfo (icon, &info))
  302. {
  303. Image mask (createImageFromHBITMAP (info.hbmMask));
  304. Image image (createImageFromHBITMAP (info.hbmColor));
  305. if (mask.isValid() && image.isValid())
  306. {
  307. for (int y = image.getHeight(); --y >= 0;)
  308. {
  309. for (int x = image.getWidth(); --x >= 0;)
  310. {
  311. const float brightness = mask.getPixelAt (x, y).getBrightness();
  312. if (brightness > 0.0f)
  313. image.multiplyAlphaAt (x, y, 1.0f - brightness);
  314. }
  315. }
  316. return image;
  317. }
  318. }
  319. return Image::null;
  320. }
  321. HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
  322. {
  323. WindowsBitmapImage* nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
  324. Image bitmap (nativeBitmap);
  325. {
  326. Graphics g (bitmap);
  327. g.drawImageAt (image, 0, 0);
  328. }
  329. HBITMAP mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, 0);
  330. ICONINFO info;
  331. info.fIcon = isIcon;
  332. info.xHotspot = (DWORD) hotspotX;
  333. info.yHotspot = (DWORD) hotspotY;
  334. info.hbmMask = mask;
  335. info.hbmColor = nativeBitmap->hBitmap;
  336. HICON hi = CreateIconIndirect (&info);
  337. DeleteObject (mask);
  338. return hi;
  339. }
  340. }
  341. //==============================================================================
  342. class HWNDComponentPeer : public ComponentPeer
  343. {
  344. public:
  345. enum RenderingEngineType
  346. {
  347. softwareRenderingEngine = 0,
  348. direct2DRenderingEngine
  349. };
  350. //==============================================================================
  351. HWNDComponentPeer (Component* const component,
  352. const int windowStyleFlags,
  353. HWND parentToAddTo_)
  354. : ComponentPeer (component, windowStyleFlags),
  355. dontRepaint (false),
  356. currentRenderingEngine (softwareRenderingEngine),
  357. fullScreen (false),
  358. isDragging (false),
  359. isMouseOver (false),
  360. hasCreatedCaret (false),
  361. constrainerIsResizing (false),
  362. currentWindowIcon (0),
  363. dropTarget (nullptr),
  364. parentToAddTo (parentToAddTo_),
  365. updateLayeredWindowAlpha (255)
  366. {
  367. callFunctionIfNotLocked (&createWindowCallback, this);
  368. setTitle (component->getName());
  369. if ((windowStyleFlags & windowHasDropShadow) != 0
  370. && Desktop::canUseSemiTransparentWindows())
  371. {
  372. shadower = component->getLookAndFeel().createDropShadowerForComponent (component);
  373. if (shadower != nullptr)
  374. shadower->setOwner (component);
  375. }
  376. }
  377. ~HWNDComponentPeer()
  378. {
  379. shadower = nullptr;
  380. // do this before the next bit to avoid messages arriving for this window
  381. // before it's destroyed
  382. JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
  383. callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
  384. if (currentWindowIcon != 0)
  385. DestroyIcon (currentWindowIcon);
  386. if (dropTarget != nullptr)
  387. {
  388. dropTarget->Release();
  389. dropTarget = nullptr;
  390. }
  391. #if JUCE_DIRECT2D
  392. direct2DContext = nullptr;
  393. #endif
  394. }
  395. //==============================================================================
  396. void* getNativeHandle() const { return hwnd; }
  397. void setVisible (bool shouldBeVisible)
  398. {
  399. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  400. if (shouldBeVisible)
  401. InvalidateRect (hwnd, 0, 0);
  402. else
  403. lastPaintTime = 0;
  404. }
  405. void setTitle (const String& title)
  406. {
  407. SetWindowText (hwnd, title.toWideCharPointer());
  408. }
  409. void setPosition (int x, int y)
  410. {
  411. offsetWithinParent (x, y);
  412. SetWindowPos (hwnd, 0,
  413. x - windowBorder.getLeft(),
  414. y - windowBorder.getTop(),
  415. 0, 0,
  416. SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  417. }
  418. void repaintNowIfTransparent()
  419. {
  420. if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
  421. handlePaintMessage();
  422. }
  423. void updateBorderSize()
  424. {
  425. WINDOWINFO info;
  426. info.cbSize = sizeof (info);
  427. if (GetWindowInfo (hwnd, &info))
  428. {
  429. windowBorder = BorderSize<int> (info.rcClient.top - info.rcWindow.top,
  430. info.rcClient.left - info.rcWindow.left,
  431. info.rcWindow.bottom - info.rcClient.bottom,
  432. info.rcWindow.right - info.rcClient.right);
  433. }
  434. #if JUCE_DIRECT2D
  435. if (direct2DContext != nullptr)
  436. direct2DContext->resized();
  437. #endif
  438. }
  439. void setSize (int w, int h)
  440. {
  441. SetWindowPos (hwnd, 0, 0, 0,
  442. w + windowBorder.getLeftAndRight(),
  443. h + windowBorder.getTopAndBottom(),
  444. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  445. updateBorderSize();
  446. repaintNowIfTransparent();
  447. }
  448. void setBounds (int x, int y, int w, int h, bool isNowFullScreen)
  449. {
  450. fullScreen = isNowFullScreen;
  451. offsetWithinParent (x, y);
  452. SetWindowPos (hwnd, 0,
  453. x - windowBorder.getLeft(),
  454. y - windowBorder.getTop(),
  455. w + windowBorder.getLeftAndRight(),
  456. h + windowBorder.getTopAndBottom(),
  457. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  458. updateBorderSize();
  459. repaintNowIfTransparent();
  460. }
  461. const Rectangle<int> getBounds() const
  462. {
  463. RECT r;
  464. GetWindowRect (hwnd, &r);
  465. Rectangle<int> bounds (r.left, r.top, r.right - r.left, r.bottom - r.top);
  466. HWND parentH = GetParent (hwnd);
  467. if (parentH != 0)
  468. {
  469. GetWindowRect (parentH, &r);
  470. bounds.translate (-r.left, -r.top);
  471. }
  472. return windowBorder.subtractedFrom (bounds);
  473. }
  474. const Point<int> getScreenPosition() const
  475. {
  476. RECT r;
  477. GetWindowRect (hwnd, &r);
  478. return Point<int> (r.left + windowBorder.getLeft(),
  479. r.top + windowBorder.getTop());
  480. }
  481. const Point<int> localToGlobal (const Point<int>& relativePosition)
  482. {
  483. return relativePosition + getScreenPosition();
  484. }
  485. const Point<int> globalToLocal (const Point<int>& screenPosition)
  486. {
  487. return screenPosition - getScreenPosition();
  488. }
  489. void setAlpha (float newAlpha)
  490. {
  491. const uint8 intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
  492. if (component->isOpaque())
  493. {
  494. if (newAlpha < 1.0f)
  495. {
  496. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  497. SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
  498. }
  499. else
  500. {
  501. SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
  502. RedrawWindow (hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
  503. }
  504. }
  505. else
  506. {
  507. updateLayeredWindowAlpha = intAlpha;
  508. component->repaint();
  509. }
  510. }
  511. void setMinimised (bool shouldBeMinimised)
  512. {
  513. if (shouldBeMinimised != isMinimised())
  514. ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL);
  515. }
  516. bool isMinimised() const
  517. {
  518. WINDOWPLACEMENT wp;
  519. wp.length = sizeof (WINDOWPLACEMENT);
  520. GetWindowPlacement (hwnd, &wp);
  521. return wp.showCmd == SW_SHOWMINIMIZED;
  522. }
  523. void setFullScreen (bool shouldBeFullScreen)
  524. {
  525. setMinimised (false);
  526. if (isFullScreen() != shouldBeFullScreen)
  527. {
  528. fullScreen = shouldBeFullScreen;
  529. const WeakReference<Component> deletionChecker (component);
  530. if (! fullScreen)
  531. {
  532. const Rectangle<int> boundsCopy (lastNonFullscreenBounds);
  533. if (hasTitleBar())
  534. ShowWindow (hwnd, SW_SHOWNORMAL);
  535. if (! boundsCopy.isEmpty())
  536. {
  537. setBounds (boundsCopy.getX(),
  538. boundsCopy.getY(),
  539. boundsCopy.getWidth(),
  540. boundsCopy.getHeight(),
  541. false);
  542. }
  543. }
  544. else
  545. {
  546. if (hasTitleBar())
  547. ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  548. else
  549. SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
  550. }
  551. if (deletionChecker != nullptr)
  552. handleMovedOrResized();
  553. }
  554. }
  555. bool isFullScreen() const
  556. {
  557. if (! hasTitleBar())
  558. return fullScreen;
  559. WINDOWPLACEMENT wp;
  560. wp.length = sizeof (wp);
  561. GetWindowPlacement (hwnd, &wp);
  562. return wp.showCmd == SW_SHOWMAXIMIZED;
  563. }
  564. bool contains (const Point<int>& position, bool trueIfInAChildWindow) const
  565. {
  566. if (! (isPositiveAndBelow (position.getX(), component->getWidth())
  567. && isPositiveAndBelow (position.getY(), component->getHeight())))
  568. return false;
  569. RECT r;
  570. GetWindowRect (hwnd, &r);
  571. POINT p = { position.getX() + r.left + windowBorder.getLeft(),
  572. position.getY() + r.top + windowBorder.getTop() };
  573. HWND w = WindowFromPoint (p);
  574. return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
  575. }
  576. const BorderSize<int> getFrameSize() const
  577. {
  578. return windowBorder;
  579. }
  580. bool setAlwaysOnTop (bool alwaysOnTop)
  581. {
  582. const bool oldDeactivate = shouldDeactivateTitleBar;
  583. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  584. SetWindowPos (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
  585. 0, 0, 0, 0,
  586. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  587. shouldDeactivateTitleBar = oldDeactivate;
  588. if (shadower != nullptr)
  589. shadower->componentBroughtToFront (*component);
  590. return true;
  591. }
  592. void toFront (bool makeActive)
  593. {
  594. setMinimised (false);
  595. const bool oldDeactivate = shouldDeactivateTitleBar;
  596. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  597. callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
  598. shouldDeactivateTitleBar = oldDeactivate;
  599. if (! makeActive)
  600. {
  601. // in this case a broughttofront call won't have occured, so do it now..
  602. handleBroughtToFront();
  603. }
  604. }
  605. void toBehind (ComponentPeer* other)
  606. {
  607. HWNDComponentPeer* const otherPeer = dynamic_cast <HWNDComponentPeer*> (other);
  608. jassert (otherPeer != nullptr); // wrong type of window?
  609. if (otherPeer != nullptr)
  610. {
  611. setMinimised (false);
  612. // Must be careful not to try to put a topmost window behind a normal one, or Windows
  613. // promotes the normal one to be topmost!
  614. if (getComponent()->isAlwaysOnTop() == otherPeer->getComponent()->isAlwaysOnTop())
  615. SetWindowPos (hwnd, otherPeer->hwnd, 0, 0, 0, 0,
  616. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  617. else if (otherPeer->getComponent()->isAlwaysOnTop())
  618. SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
  619. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  620. }
  621. }
  622. bool isFocused() const
  623. {
  624. return callFunctionIfNotLocked (&getFocusCallback, 0) == (void*) hwnd;
  625. }
  626. void grabFocus()
  627. {
  628. const bool oldDeactivate = shouldDeactivateTitleBar;
  629. shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
  630. callFunctionIfNotLocked (&setFocusCallback, hwnd);
  631. shouldDeactivateTitleBar = oldDeactivate;
  632. }
  633. void textInputRequired (const Point<int>&)
  634. {
  635. if (! hasCreatedCaret)
  636. {
  637. hasCreatedCaret = true;
  638. CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
  639. }
  640. ShowCaret (hwnd);
  641. SetCaretPos (0, 0);
  642. }
  643. void dismissPendingTextInput()
  644. {
  645. imeHandler.handleSetContext (hwnd, false);
  646. }
  647. void repaint (const Rectangle<int>& area)
  648. {
  649. const RECT r = { area.getX(), area.getY(), area.getRight(), area.getBottom() };
  650. InvalidateRect (hwnd, &r, FALSE);
  651. }
  652. void performAnyPendingRepaintsNow()
  653. {
  654. MSG m;
  655. if (component->isVisible()
  656. && (PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) || isUsingUpdateLayeredWindow()))
  657. handlePaintMessage();
  658. }
  659. //==============================================================================
  660. static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
  661. {
  662. if (h != 0 && JuceWindowIdentifier::isJUCEWindow (h))
  663. return (HWNDComponentPeer*) (pointer_sized_int) GetWindowLongPtr (h, 8);
  664. return nullptr;
  665. }
  666. //==============================================================================
  667. bool isInside (HWND h) const noexcept
  668. {
  669. return GetAncestor (hwnd, GA_ROOT) == h;
  670. }
  671. //==============================================================================
  672. static bool isKeyDown (const int key) noexcept { return (GetAsyncKeyState (key) & 0x8000) != 0; }
  673. static void updateKeyModifiers() noexcept
  674. {
  675. int keyMods = 0;
  676. if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
  677. if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
  678. if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
  679. if (isKeyDown (VK_RMENU)) keyMods &= ~(ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
  680. currentModifiers = currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
  681. }
  682. static void updateModifiersFromWParam (const WPARAM wParam)
  683. {
  684. int mouseMods = 0;
  685. if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
  686. if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
  687. if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
  688. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  689. updateKeyModifiers();
  690. }
  691. //==============================================================================
  692. bool dontRepaint;
  693. static ModifierKeys currentModifiers;
  694. static ModifierKeys modifiersAtLastCallback;
  695. private:
  696. HWND hwnd, parentToAddTo;
  697. ScopedPointer<DropShadower> shadower;
  698. RenderingEngineType currentRenderingEngine;
  699. #if JUCE_DIRECT2D
  700. ScopedPointer<Direct2DLowLevelGraphicsContext> direct2DContext;
  701. #endif
  702. bool fullScreen, isDragging, isMouseOver, hasCreatedCaret, constrainerIsResizing;
  703. BorderSize<int> windowBorder;
  704. HICON currentWindowIcon;
  705. IDropTarget* dropTarget;
  706. uint8 updateLayeredWindowAlpha;
  707. //==============================================================================
  708. class TemporaryImage : public Timer
  709. {
  710. public:
  711. TemporaryImage() {}
  712. const Image& getImage (const bool transparent, const int w, const int h)
  713. {
  714. const Image::PixelFormat format = transparent ? Image::ARGB : Image::RGB;
  715. if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
  716. image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
  717. startTimer (3000);
  718. return image;
  719. }
  720. void timerCallback()
  721. {
  722. stopTimer();
  723. image = Image::null;
  724. }
  725. private:
  726. Image image;
  727. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage);
  728. };
  729. TemporaryImage offscreenImageGenerator;
  730. //==============================================================================
  731. class WindowClassHolder : public DeletedAtShutdown
  732. {
  733. public:
  734. WindowClassHolder()
  735. {
  736. // this name has to be different for each app/dll instance because otherwise poor old Windows can
  737. // get a bit confused (even despite it not being a process-global window class).
  738. String windowClassName ("JUCE_");
  739. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  740. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  741. TCHAR moduleFile [1024] = { 0 };
  742. GetModuleFileName (moduleHandle, moduleFile, 1024);
  743. WORD iconNum = 0;
  744. WNDCLASSEX wcex = { 0 };
  745. wcex.cbSize = sizeof (wcex);
  746. wcex.style = CS_OWNDC;
  747. wcex.lpfnWndProc = (WNDPROC) windowProc;
  748. wcex.lpszClassName = windowClassName.toWideCharPointer();
  749. wcex.cbWndExtra = 32;
  750. wcex.hInstance = moduleHandle;
  751. wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  752. iconNum = 1;
  753. wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
  754. atom = RegisterClassEx (&wcex);
  755. jassert (atom != 0);
  756. isEventBlockedByModalComps = checkEventBlockedByModalComps;
  757. }
  758. ~WindowClassHolder()
  759. {
  760. if (ComponentPeer::getNumPeers() == 0)
  761. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  762. clearSingletonInstance();
  763. }
  764. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  765. juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder);
  766. private:
  767. ATOM atom;
  768. static bool isHWNDBlockedByModalComponents (HWND h)
  769. {
  770. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  771. {
  772. Component* const c = Desktop::getInstance().getComponent (i);
  773. if (c != nullptr
  774. && (! c->isCurrentlyBlockedByAnotherModalComponent())
  775. && IsChild ((HWND) c->getWindowHandle(), h))
  776. return false;
  777. }
  778. return true;
  779. }
  780. static bool checkEventBlockedByModalComps (const MSG& m)
  781. {
  782. if (Component::getNumCurrentlyModalComponents() == 0 || JuceWindowIdentifier::isJUCEWindow (m.hwnd))
  783. return false;
  784. switch (m.message)
  785. {
  786. case WM_MOUSEMOVE:
  787. case WM_NCMOUSEMOVE:
  788. case 0x020A: /* WM_MOUSEWHEEL */
  789. case 0x020E: /* WM_MOUSEHWHEEL */
  790. case WM_KEYUP:
  791. case WM_SYSKEYUP:
  792. case WM_CHAR:
  793. case WM_APPCOMMAND:
  794. case WM_LBUTTONUP:
  795. case WM_MBUTTONUP:
  796. case WM_RBUTTONUP:
  797. case WM_MOUSEACTIVATE:
  798. case WM_NCMOUSEHOVER:
  799. case WM_MOUSEHOVER:
  800. return isHWNDBlockedByModalComponents (m.hwnd);
  801. case WM_NCLBUTTONDOWN:
  802. case WM_NCLBUTTONDBLCLK:
  803. case WM_NCRBUTTONDOWN:
  804. case WM_NCRBUTTONDBLCLK:
  805. case WM_NCMBUTTONDOWN:
  806. case WM_NCMBUTTONDBLCLK:
  807. case WM_LBUTTONDOWN:
  808. case WM_LBUTTONDBLCLK:
  809. case WM_MBUTTONDOWN:
  810. case WM_MBUTTONDBLCLK:
  811. case WM_RBUTTONDOWN:
  812. case WM_RBUTTONDBLCLK:
  813. case WM_KEYDOWN:
  814. case WM_SYSKEYDOWN:
  815. if (isHWNDBlockedByModalComponents (m.hwnd))
  816. {
  817. Component* const modal = Component::getCurrentlyModalComponent (0);
  818. if (modal != nullptr)
  819. modal->inputAttemptWhenModal();
  820. return true;
  821. }
  822. break;
  823. default:
  824. break;
  825. }
  826. return false;
  827. }
  828. JUCE_DECLARE_NON_COPYABLE (WindowClassHolder);
  829. };
  830. //==============================================================================
  831. static void* createWindowCallback (void* userData)
  832. {
  833. static_cast <HWNDComponentPeer*> (userData)->createWindow();
  834. return nullptr;
  835. }
  836. void createWindow()
  837. {
  838. DWORD exstyle = WS_EX_ACCEPTFILES;
  839. DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  840. if (hasTitleBar())
  841. {
  842. type |= WS_OVERLAPPED;
  843. if ((styleFlags & windowHasCloseButton) != 0)
  844. {
  845. type |= WS_SYSMENU;
  846. }
  847. else
  848. {
  849. // annoyingly, windows won't let you have a min/max button without a close button
  850. jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
  851. }
  852. if ((styleFlags & windowIsResizable) != 0)
  853. type |= WS_THICKFRAME;
  854. }
  855. else if (parentToAddTo != 0)
  856. {
  857. type |= WS_CHILD;
  858. }
  859. else
  860. {
  861. type |= WS_POPUP | WS_SYSMENU;
  862. }
  863. if ((styleFlags & windowAppearsOnTaskbar) == 0)
  864. exstyle |= WS_EX_TOOLWINDOW;
  865. else
  866. exstyle |= WS_EX_APPWINDOW;
  867. if ((styleFlags & windowHasMinimiseButton) != 0) type |= WS_MINIMIZEBOX;
  868. if ((styleFlags & windowHasMaximiseButton) != 0) type |= WS_MAXIMIZEBOX;
  869. if ((styleFlags & windowIgnoresMouseClicks) != 0) exstyle |= WS_EX_TRANSPARENT;
  870. if ((styleFlags & windowIsSemiTransparent) != 0 && Desktop::canUseSemiTransparentWindows())
  871. exstyle |= WS_EX_LAYERED;
  872. hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
  873. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  874. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  875. #if JUCE_DIRECT2D
  876. setCurrentRenderingEngine (1);
  877. #endif
  878. if (hwnd != 0)
  879. {
  880. SetWindowLongPtr (hwnd, 0, 0);
  881. SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
  882. JuceWindowIdentifier::setAsJUCEWindow (hwnd, true);
  883. if (dropTarget == nullptr)
  884. dropTarget = new JuceDropTarget (*this);
  885. RegisterDragDrop (hwnd, dropTarget);
  886. updateBorderSize();
  887. // Calling this function here is (for some reason) necessary to make Windows
  888. // correctly enable the menu items that we specify in the wm_initmenu message.
  889. GetSystemMenu (hwnd, false);
  890. const float alpha = component->getAlpha();
  891. if (alpha < 1.0f)
  892. setAlpha (alpha);
  893. }
  894. else
  895. {
  896. jassertfalse;
  897. }
  898. }
  899. static void* destroyWindowCallback (void* handle)
  900. {
  901. RevokeDragDrop ((HWND) handle);
  902. DestroyWindow ((HWND) handle);
  903. return nullptr;
  904. }
  905. static void* toFrontCallback1 (void* h)
  906. {
  907. SetForegroundWindow ((HWND) h);
  908. return nullptr;
  909. }
  910. static void* toFrontCallback2 (void* h)
  911. {
  912. SetWindowPos ((HWND) h, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  913. return nullptr;
  914. }
  915. static void* setFocusCallback (void* h)
  916. {
  917. SetFocus ((HWND) h);
  918. return nullptr;
  919. }
  920. static void* getFocusCallback (void*)
  921. {
  922. return GetFocus();
  923. }
  924. void offsetWithinParent (int& x, int& y) const
  925. {
  926. if (isUsingUpdateLayeredWindow())
  927. {
  928. HWND parentHwnd = GetParent (hwnd);
  929. if (parentHwnd != 0)
  930. {
  931. RECT parentRect;
  932. GetWindowRect (parentHwnd, &parentRect);
  933. x += parentRect.left;
  934. y += parentRect.top;
  935. }
  936. }
  937. }
  938. bool isUsingUpdateLayeredWindow() const
  939. {
  940. return ! component->isOpaque();
  941. }
  942. inline bool hasTitleBar() const noexcept { return (styleFlags & windowHasTitleBar) != 0; }
  943. void setIcon (const Image& newIcon)
  944. {
  945. HICON hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0);
  946. if (hicon != 0)
  947. {
  948. SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
  949. SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
  950. if (currentWindowIcon != 0)
  951. DestroyIcon (currentWindowIcon);
  952. currentWindowIcon = hicon;
  953. }
  954. }
  955. //==============================================================================
  956. void handlePaintMessage()
  957. {
  958. #if JUCE_DIRECT2D
  959. if (direct2DContext != nullptr)
  960. {
  961. RECT r;
  962. if (GetUpdateRect (hwnd, &r, false))
  963. {
  964. direct2DContext->start();
  965. direct2DContext->clipToRectangle (Rectangle<int> (r.left, r.top, r.right - r.left, r.bottom - r.top));
  966. handlePaint (*direct2DContext);
  967. direct2DContext->end();
  968. }
  969. }
  970. else
  971. #endif
  972. {
  973. HRGN rgn = CreateRectRgn (0, 0, 0, 0);
  974. const int regionType = GetUpdateRgn (hwnd, rgn, false);
  975. PAINTSTRUCT paintStruct;
  976. HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
  977. // message and become re-entrant, but that's OK
  978. // if something in a paint handler calls, e.g. a message box, this can become reentrant and
  979. // corrupt the image it's using to paint into, so do a check here.
  980. static bool reentrant = false;
  981. if (reentrant)
  982. {
  983. DeleteObject (rgn);
  984. EndPaint (hwnd, &paintStruct);
  985. return;
  986. }
  987. const ScopedValueSetter<bool> setter (reentrant, true, false);
  988. // this is the rectangle to update..
  989. int x = paintStruct.rcPaint.left;
  990. int y = paintStruct.rcPaint.top;
  991. int w = paintStruct.rcPaint.right - x;
  992. int h = paintStruct.rcPaint.bottom - y;
  993. const bool transparent = isUsingUpdateLayeredWindow();
  994. if (transparent)
  995. {
  996. // it's not possible to have a transparent window with a title bar at the moment!
  997. jassert (! hasTitleBar());
  998. RECT r;
  999. GetWindowRect (hwnd, &r);
  1000. x = y = 0;
  1001. w = r.right - r.left;
  1002. h = r.bottom - r.top;
  1003. }
  1004. if (w > 0 && h > 0)
  1005. {
  1006. clearMaskedRegion();
  1007. Image offscreenImage (offscreenImageGenerator.getImage (transparent, w, h));
  1008. RectangleList contextClip;
  1009. const Rectangle<int> clipBounds (0, 0, w, h);
  1010. bool needToPaintAll = true;
  1011. if (regionType == COMPLEXREGION && ! transparent)
  1012. {
  1013. HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
  1014. CombineRgn (rgn, rgn, clipRgn, RGN_AND);
  1015. DeleteObject (clipRgn);
  1016. char rgnData [8192];
  1017. const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData);
  1018. if (res > 0 && res <= sizeof (rgnData))
  1019. {
  1020. const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh);
  1021. if (hdr->iType == RDH_RECTANGLES
  1022. && hdr->rcBound.right - hdr->rcBound.left >= w
  1023. && hdr->rcBound.bottom - hdr->rcBound.top >= h)
  1024. {
  1025. needToPaintAll = false;
  1026. const RECT* rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER));
  1027. for (int i = (int) ((RGNDATA*) rgnData)->rdh.nCount; --i >= 0;)
  1028. {
  1029. if (rects->right <= x + w && rects->bottom <= y + h)
  1030. {
  1031. const int cx = jmax (x, (int) rects->left);
  1032. contextClip.addWithoutMerging (Rectangle<int> (cx - x, rects->top - y, rects->right - cx, rects->bottom - rects->top)
  1033. .getIntersection (clipBounds));
  1034. }
  1035. else
  1036. {
  1037. needToPaintAll = true;
  1038. break;
  1039. }
  1040. ++rects;
  1041. }
  1042. }
  1043. }
  1044. }
  1045. if (needToPaintAll)
  1046. {
  1047. contextClip.clear();
  1048. contextClip.addWithoutMerging (Rectangle<int> (w, h));
  1049. }
  1050. if (transparent)
  1051. {
  1052. RectangleList::Iterator i (contextClip);
  1053. while (i.next())
  1054. offscreenImage.clear (*i.getRectangle());
  1055. }
  1056. // if the component's not opaque, this won't draw properly unless the platform can support this
  1057. jassert (Desktop::canUseSemiTransparentWindows() || component->isOpaque());
  1058. updateCurrentModifiers();
  1059. LowLevelGraphicsSoftwareRenderer context (offscreenImage, -x, -y, contextClip);
  1060. handlePaint (context);
  1061. if (! dontRepaint)
  1062. static_cast <WindowsBitmapImage*> (offscreenImage.getSharedImage())
  1063. ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha);
  1064. }
  1065. DeleteObject (rgn);
  1066. EndPaint (hwnd, &paintStruct);
  1067. }
  1068. #ifndef JUCE_GCC
  1069. _fpreset(); // because some graphics cards can unmask FP exceptions
  1070. #endif
  1071. lastPaintTime = Time::getMillisecondCounter();
  1072. }
  1073. //==============================================================================
  1074. void doMouseEvent (const Point<int>& position)
  1075. {
  1076. handleMouseEvent (0, position, currentModifiers, getMouseEventTime());
  1077. }
  1078. StringArray getAvailableRenderingEngines()
  1079. {
  1080. StringArray s (ComponentPeer::getAvailableRenderingEngines());
  1081. #if JUCE_DIRECT2D
  1082. if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
  1083. s.add ("Direct2D");
  1084. #endif
  1085. return s;
  1086. }
  1087. int getCurrentRenderingEngine() const { return currentRenderingEngine; }
  1088. #if JUCE_DIRECT2D
  1089. void updateDirect2DContext()
  1090. {
  1091. if (currentRenderingEngine != direct2DRenderingEngine)
  1092. direct2DContext = 0;
  1093. else if (direct2DContext == 0)
  1094. direct2DContext = new Direct2DLowLevelGraphicsContext (hwnd);
  1095. }
  1096. #endif
  1097. void setCurrentRenderingEngine (int index)
  1098. {
  1099. (void) index;
  1100. #if JUCE_DIRECT2D
  1101. if (getAvailableRenderingEngines().size() > 1)
  1102. {
  1103. currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
  1104. updateDirect2DContext();
  1105. repaint (component->getLocalBounds());
  1106. }
  1107. #endif
  1108. }
  1109. static int getMinTimeBetweenMouseMoves()
  1110. {
  1111. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  1112. return 0;
  1113. return 1000 / 60; // Throttling the incoming mouse-events seems to still be needed in XP..
  1114. }
  1115. void doMouseMove (const Point<int>& position)
  1116. {
  1117. if (! isMouseOver)
  1118. {
  1119. isMouseOver = true;
  1120. updateKeyModifiers();
  1121. TRACKMOUSEEVENT tme;
  1122. tme.cbSize = sizeof (tme);
  1123. tme.dwFlags = TME_LEAVE;
  1124. tme.hwndTrack = hwnd;
  1125. tme.dwHoverTime = 0;
  1126. if (! TrackMouseEvent (&tme))
  1127. jassertfalse;
  1128. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  1129. }
  1130. else if (! isDragging)
  1131. {
  1132. if (! contains (position, false))
  1133. return;
  1134. }
  1135. static uint32 lastMouseTime = 0;
  1136. static int minTimeBetweenMouses = getMinTimeBetweenMouseMoves();
  1137. const uint32 now = Time::getMillisecondCounter();
  1138. if (now >= lastMouseTime + minTimeBetweenMouses)
  1139. {
  1140. lastMouseTime = now;
  1141. doMouseEvent (position);
  1142. }
  1143. }
  1144. void doMouseDown (const Point<int>& position, const WPARAM wParam)
  1145. {
  1146. if (GetCapture() != hwnd)
  1147. SetCapture (hwnd);
  1148. doMouseMove (position);
  1149. updateModifiersFromWParam (wParam);
  1150. isDragging = true;
  1151. doMouseEvent (position);
  1152. }
  1153. void doMouseUp (const Point<int>& position, const WPARAM wParam)
  1154. {
  1155. updateModifiersFromWParam (wParam);
  1156. isDragging = false;
  1157. // release the mouse capture if the user has released all buttons
  1158. if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
  1159. ReleaseCapture();
  1160. doMouseEvent (position);
  1161. }
  1162. void doCaptureChanged()
  1163. {
  1164. if (constrainerIsResizing)
  1165. {
  1166. if (constrainer != nullptr)
  1167. constrainer->resizeEnd();
  1168. constrainerIsResizing = false;
  1169. }
  1170. if (isDragging)
  1171. doMouseUp (getCurrentMousePos(), (WPARAM) 0);
  1172. }
  1173. void doMouseExit()
  1174. {
  1175. isMouseOver = false;
  1176. doMouseEvent (getCurrentMousePos());
  1177. }
  1178. void doMouseWheel (const Point<int>& globalPos, const WPARAM wParam, const bool isVertical)
  1179. {
  1180. updateKeyModifiers();
  1181. const float amount = jlimit (-1000.0f, 1000.0f, 0.75f * (short) HIWORD (wParam));
  1182. // Because Windows stupidly sends all wheel events to the window with the keyboard
  1183. // focus, we have to redirect them here according to the mouse pos..
  1184. POINT p = { globalPos.getX(), globalPos.getY() };
  1185. HWNDComponentPeer* peer = getOwnerOfWindow (WindowFromPoint (p));
  1186. if (peer == nullptr)
  1187. peer = this;
  1188. peer->handleMouseWheel (0, peer->globalToLocal (globalPos), getMouseEventTime(),
  1189. isVertical ? 0.0f : -amount,
  1190. isVertical ? amount : 0.0f);
  1191. }
  1192. //==============================================================================
  1193. void sendModifierKeyChangeIfNeeded()
  1194. {
  1195. if (modifiersAtLastCallback != currentModifiers)
  1196. {
  1197. modifiersAtLastCallback = currentModifiers;
  1198. handleModifierKeysChange();
  1199. }
  1200. }
  1201. bool doKeyUp (const WPARAM key)
  1202. {
  1203. updateKeyModifiers();
  1204. switch (key)
  1205. {
  1206. case VK_SHIFT:
  1207. case VK_CONTROL:
  1208. case VK_MENU:
  1209. case VK_CAPITAL:
  1210. case VK_LWIN:
  1211. case VK_RWIN:
  1212. case VK_APPS:
  1213. case VK_NUMLOCK:
  1214. case VK_SCROLL:
  1215. case VK_LSHIFT:
  1216. case VK_RSHIFT:
  1217. case VK_LCONTROL:
  1218. case VK_LMENU:
  1219. case VK_RCONTROL:
  1220. case VK_RMENU:
  1221. sendModifierKeyChangeIfNeeded();
  1222. }
  1223. return handleKeyUpOrDown (false)
  1224. || Component::getCurrentlyModalComponent() != nullptr;
  1225. }
  1226. bool doKeyDown (const WPARAM key)
  1227. {
  1228. updateKeyModifiers();
  1229. bool used = false;
  1230. switch (key)
  1231. {
  1232. case VK_SHIFT:
  1233. case VK_LSHIFT:
  1234. case VK_RSHIFT:
  1235. case VK_CONTROL:
  1236. case VK_LCONTROL:
  1237. case VK_RCONTROL:
  1238. case VK_MENU:
  1239. case VK_LMENU:
  1240. case VK_RMENU:
  1241. case VK_LWIN:
  1242. case VK_RWIN:
  1243. case VK_CAPITAL:
  1244. case VK_NUMLOCK:
  1245. case VK_SCROLL:
  1246. case VK_APPS:
  1247. sendModifierKeyChangeIfNeeded();
  1248. break;
  1249. case VK_LEFT:
  1250. case VK_RIGHT:
  1251. case VK_UP:
  1252. case VK_DOWN:
  1253. case VK_PRIOR:
  1254. case VK_NEXT:
  1255. case VK_HOME:
  1256. case VK_END:
  1257. case VK_DELETE:
  1258. case VK_INSERT:
  1259. case VK_F1:
  1260. case VK_F2:
  1261. case VK_F3:
  1262. case VK_F4:
  1263. case VK_F5:
  1264. case VK_F6:
  1265. case VK_F7:
  1266. case VK_F8:
  1267. case VK_F9:
  1268. case VK_F10:
  1269. case VK_F11:
  1270. case VK_F12:
  1271. case VK_F13:
  1272. case VK_F14:
  1273. case VK_F15:
  1274. case VK_F16:
  1275. used = handleKeyUpOrDown (true);
  1276. used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
  1277. break;
  1278. case VK_ADD:
  1279. case VK_SUBTRACT:
  1280. case VK_MULTIPLY:
  1281. case VK_DIVIDE:
  1282. case VK_SEPARATOR:
  1283. case VK_DECIMAL:
  1284. used = handleKeyUpOrDown (true);
  1285. break;
  1286. default:
  1287. used = handleKeyUpOrDown (true);
  1288. {
  1289. MSG msg;
  1290. if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
  1291. {
  1292. // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
  1293. // manually generate the key-press event that matches this key-down.
  1294. const UINT keyChar = MapVirtualKey ((UINT) key, 2);
  1295. used = handleKeyPress ((int) LOWORD (keyChar), 0) || used;
  1296. }
  1297. }
  1298. break;
  1299. }
  1300. return used || (Component::getCurrentlyModalComponent() != nullptr);
  1301. }
  1302. bool doKeyChar (int key, const LPARAM flags)
  1303. {
  1304. updateKeyModifiers();
  1305. juce_wchar textChar = (juce_wchar) key;
  1306. const int virtualScanCode = (flags >> 16) & 0xff;
  1307. if (key >= '0' && key <= '9')
  1308. {
  1309. switch (virtualScanCode) // check for a numeric keypad scan-code
  1310. {
  1311. case 0x52:
  1312. case 0x4f:
  1313. case 0x50:
  1314. case 0x51:
  1315. case 0x4b:
  1316. case 0x4c:
  1317. case 0x4d:
  1318. case 0x47:
  1319. case 0x48:
  1320. case 0x49:
  1321. key = (key - '0') + KeyPress::numberPad0;
  1322. break;
  1323. default:
  1324. break;
  1325. }
  1326. }
  1327. else
  1328. {
  1329. // convert the scan code to an unmodified character code..
  1330. const UINT virtualKey = MapVirtualKey ((UINT) virtualScanCode, 1);
  1331. UINT keyChar = MapVirtualKey (virtualKey, 2);
  1332. keyChar = LOWORD (keyChar);
  1333. if (keyChar != 0)
  1334. key = (int) keyChar;
  1335. // avoid sending junk text characters for some control-key combinations
  1336. if (textChar < ' ' && currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
  1337. textChar = 0;
  1338. }
  1339. return handleKeyPress (key, textChar);
  1340. }
  1341. void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
  1342. {
  1343. HWND parentH = GetParent (hwnd);
  1344. if (parentH != 0)
  1345. PostMessage (parentH, message, wParam, lParam);
  1346. }
  1347. bool doAppCommand (const LPARAM lParam)
  1348. {
  1349. int key = 0;
  1350. switch (GET_APPCOMMAND_LPARAM (lParam))
  1351. {
  1352. case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
  1353. case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
  1354. case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
  1355. case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
  1356. default: break;
  1357. }
  1358. if (key != 0)
  1359. {
  1360. updateKeyModifiers();
  1361. if (hwnd == GetActiveWindow())
  1362. {
  1363. handleKeyPress (key, 0);
  1364. return true;
  1365. }
  1366. }
  1367. return false;
  1368. }
  1369. bool isConstrainedNativeWindow() const
  1370. {
  1371. return constrainer != nullptr
  1372. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable);
  1373. }
  1374. LRESULT handleSizeConstraining (RECT* const r, const WPARAM wParam)
  1375. {
  1376. if (isConstrainedNativeWindow())
  1377. {
  1378. Rectangle<int> pos (r->left, r->top, r->right - r->left, r->bottom - r->top);
  1379. constrainer->checkBounds (pos, windowBorder.addedTo (component->getBounds()),
  1380. Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
  1381. wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
  1382. wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
  1383. wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
  1384. wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
  1385. r->left = pos.getX();
  1386. r->top = pos.getY();
  1387. r->right = pos.getRight();
  1388. r->bottom = pos.getBottom();
  1389. }
  1390. return TRUE;
  1391. }
  1392. LRESULT handlePositionChanging (WINDOWPOS* const wp)
  1393. {
  1394. if (isConstrainedNativeWindow())
  1395. {
  1396. if ((wp->flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
  1397. && ! Component::isMouseButtonDownAnywhere())
  1398. {
  1399. Rectangle<int> pos (wp->x, wp->y, wp->cx, wp->cy);
  1400. const Rectangle<int> current (windowBorder.addedTo (component->getBounds()));
  1401. constrainer->checkBounds (pos, current,
  1402. Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
  1403. pos.getY() != current.getY() && pos.getBottom() == current.getBottom(),
  1404. pos.getX() != current.getX() && pos.getRight() == current.getRight(),
  1405. pos.getY() == current.getY() && pos.getBottom() != current.getBottom(),
  1406. pos.getX() == current.getX() && pos.getRight() != current.getRight());
  1407. wp->x = pos.getX();
  1408. wp->y = pos.getY();
  1409. wp->cx = pos.getWidth();
  1410. wp->cy = pos.getHeight();
  1411. }
  1412. }
  1413. return 0;
  1414. }
  1415. void handleAppActivation (const WPARAM wParam)
  1416. {
  1417. modifiersAtLastCallback = -1;
  1418. updateKeyModifiers();
  1419. if (isMinimised())
  1420. {
  1421. component->repaint();
  1422. handleMovedOrResized();
  1423. if (! ComponentPeer::isValidPeer (this))
  1424. return;
  1425. }
  1426. Component* underMouse = component->getComponentAt (component->getMouseXYRelative());
  1427. if (underMouse == nullptr)
  1428. underMouse = component;
  1429. if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
  1430. {
  1431. if (LOWORD (wParam) == WA_CLICKACTIVE)
  1432. Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
  1433. else
  1434. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  1435. }
  1436. else
  1437. {
  1438. handleBroughtToFront();
  1439. }
  1440. }
  1441. void handleLeftClickInNCArea (WPARAM wParam)
  1442. {
  1443. if (! sendInputAttemptWhenModalMessage())
  1444. {
  1445. switch (wParam)
  1446. {
  1447. case HTBOTTOM:
  1448. case HTBOTTOMLEFT:
  1449. case HTBOTTOMRIGHT:
  1450. case HTGROWBOX:
  1451. case HTLEFT:
  1452. case HTRIGHT:
  1453. case HTTOP:
  1454. case HTTOPLEFT:
  1455. case HTTOPRIGHT:
  1456. if (isConstrainedNativeWindow())
  1457. {
  1458. constrainerIsResizing = true;
  1459. constrainer->resizeStart();
  1460. }
  1461. break;
  1462. default:
  1463. break;
  1464. }
  1465. }
  1466. }
  1467. void initialiseSysMenu (HMENU menu) const
  1468. {
  1469. if (! hasTitleBar())
  1470. {
  1471. if (isFullScreen())
  1472. {
  1473. EnableMenuItem (menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
  1474. EnableMenuItem (menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  1475. }
  1476. else if (! isMinimised())
  1477. {
  1478. EnableMenuItem (menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
  1479. }
  1480. }
  1481. }
  1482. //==============================================================================
  1483. class JuceDropTarget : public ComBaseClassHelper <IDropTarget>
  1484. {
  1485. public:
  1486. JuceDropTarget (HWNDComponentPeer& owner_)
  1487. : owner (owner_)
  1488. {
  1489. }
  1490. JUCE_COMRESULT DragEnter (IDataObject* pDataObject, DWORD grfKeyState, POINTL mousePos, DWORD* pdwEffect)
  1491. {
  1492. updateFileList (pDataObject);
  1493. return DragOver (grfKeyState, mousePos, pdwEffect);
  1494. }
  1495. JUCE_COMRESULT DragLeave()
  1496. {
  1497. owner.handleFileDragExit (files);
  1498. return S_OK;
  1499. }
  1500. JUCE_COMRESULT DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
  1501. {
  1502. const bool wasWanted = owner.handleFileDragMove (files, getMousePos (mousePos));
  1503. *pdwEffect = wasWanted ? (DWORD) DROPEFFECT_COPY : (DWORD) DROPEFFECT_NONE;
  1504. return S_OK;
  1505. }
  1506. JUCE_COMRESULT Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
  1507. {
  1508. updateFileList (pDataObject);
  1509. const bool wasWanted = owner.handleFileDragDrop (files, getMousePos (mousePos));
  1510. *pdwEffect = wasWanted ? (DWORD) DROPEFFECT_COPY : (DWORD) DROPEFFECT_NONE;
  1511. return S_OK;
  1512. }
  1513. private:
  1514. HWNDComponentPeer& owner;
  1515. StringArray files;
  1516. Point<int> getMousePos (const POINTL& mousePos) const
  1517. {
  1518. return owner.globalToLocal (Point<int> (mousePos.x, mousePos.y));
  1519. }
  1520. template <typename CharType>
  1521. void parseFileList (const CharType* names, const SIZE_T totalLen)
  1522. {
  1523. unsigned int i = 0;
  1524. for (;;)
  1525. {
  1526. unsigned int len = 0;
  1527. while (i + len < totalLen && names [i + len] != 0)
  1528. ++len;
  1529. if (len == 0)
  1530. break;
  1531. files.add (String (names + i, len));
  1532. i += len + 1;
  1533. }
  1534. }
  1535. void updateFileList (IDataObject* const pDataObject)
  1536. {
  1537. files.clear();
  1538. FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  1539. STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
  1540. if (pDataObject->GetData (&format, &medium) == S_OK)
  1541. {
  1542. const SIZE_T totalLen = GlobalSize (medium.hGlobal);
  1543. const LPDROPFILES dropFiles = (const LPDROPFILES) GlobalLock (medium.hGlobal);
  1544. const void* const names = addBytesToPointer (dropFiles, sizeof (DROPFILES));
  1545. if (dropFiles->fWide)
  1546. parseFileList (static_cast <const WCHAR*> (names), totalLen);
  1547. else
  1548. parseFileList (static_cast <const char*> (names), totalLen);
  1549. GlobalUnlock (medium.hGlobal);
  1550. }
  1551. }
  1552. JUCE_DECLARE_NON_COPYABLE (JuceDropTarget);
  1553. };
  1554. void doSettingChange()
  1555. {
  1556. Desktop::getInstance().refreshMonitorSizes();
  1557. if (fullScreen && ! isMinimised())
  1558. {
  1559. const Rectangle<int> r (component->getParentMonitorArea());
  1560. SetWindowPos (hwnd, 0, r.getX(), r.getY(), r.getWidth(), r.getHeight(),
  1561. SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
  1562. }
  1563. }
  1564. //==============================================================================
  1565. public:
  1566. static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  1567. {
  1568. HWNDComponentPeer* const peer = getOwnerOfWindow (h);
  1569. if (peer != nullptr)
  1570. {
  1571. jassert (isValidPeer (peer));
  1572. return peer->peerWindowProc (h, message, wParam, lParam);
  1573. }
  1574. return DefWindowProcW (h, message, wParam, lParam);
  1575. }
  1576. private:
  1577. static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
  1578. {
  1579. if (MessageManager::getInstance()->currentThreadHasLockedMessageManager())
  1580. return callback (userData);
  1581. else
  1582. return MessageManager::getInstance()->callFunctionOnMessageThread (callback, userData);
  1583. }
  1584. static Point<int> getPointFromLParam (LPARAM lParam) noexcept
  1585. {
  1586. return Point<int> (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
  1587. }
  1588. static Point<int> getCurrentMousePosGlobal() noexcept
  1589. {
  1590. const DWORD mp = GetMessagePos();
  1591. return Point<int> (GET_X_LPARAM (mp),
  1592. GET_Y_LPARAM (mp));
  1593. }
  1594. Point<int> getCurrentMousePos() noexcept
  1595. {
  1596. return globalToLocal (getCurrentMousePosGlobal());
  1597. }
  1598. LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
  1599. {
  1600. switch (message)
  1601. {
  1602. //==============================================================================
  1603. case WM_NCHITTEST:
  1604. if ((styleFlags & windowIgnoresMouseClicks) != 0)
  1605. return HTTRANSPARENT;
  1606. else if (! hasTitleBar())
  1607. return HTCLIENT;
  1608. break;
  1609. //==============================================================================
  1610. case WM_PAINT:
  1611. handlePaintMessage();
  1612. return 0;
  1613. case WM_NCPAINT:
  1614. if (wParam != 1) // (1 = a repaint of the entire NC region)
  1615. handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
  1616. if (hasTitleBar())
  1617. break; // let the DefWindowProc handle drawing the frame.
  1618. return 0;
  1619. case WM_ERASEBKGND:
  1620. case WM_NCCALCSIZE:
  1621. if (hasTitleBar())
  1622. break;
  1623. return 1;
  1624. //==============================================================================
  1625. case WM_MOUSEMOVE: doMouseMove (getPointFromLParam (lParam)); return 0;
  1626. case WM_MOUSELEAVE: doMouseExit(); return 0;
  1627. case WM_LBUTTONDOWN:
  1628. case WM_MBUTTONDOWN:
  1629. case WM_RBUTTONDOWN: doMouseDown (getPointFromLParam (lParam), wParam); return 0;
  1630. case WM_LBUTTONUP:
  1631. case WM_MBUTTONUP:
  1632. case WM_RBUTTONUP: doMouseUp (getPointFromLParam (lParam), wParam); return 0;
  1633. case WM_CAPTURECHANGED: doCaptureChanged(); return 0;
  1634. case WM_NCMOUSEMOVE:
  1635. if (hasTitleBar())
  1636. break;
  1637. return 0;
  1638. case 0x020A: /* WM_MOUSEWHEEL */
  1639. case 0x020E: /* WM_MOUSEHWHEEL */
  1640. doMouseWheel (getCurrentMousePosGlobal(), wParam, message == 0x020A);
  1641. return 0;
  1642. //==============================================================================
  1643. case WM_SIZING: return handleSizeConstraining ((RECT*) lParam, wParam);
  1644. case WM_WINDOWPOSCHANGING: return handlePositionChanging ((WINDOWPOS*) lParam);
  1645. case WM_WINDOWPOSCHANGED:
  1646. {
  1647. const Point<int> pos (getCurrentMousePos());
  1648. if (contains (pos, false))
  1649. doMouseEvent (pos);
  1650. }
  1651. handleMovedOrResized();
  1652. if (dontRepaint)
  1653. break; // needed for non-accelerated openGL windows to draw themselves correctly..
  1654. return 0;
  1655. //==============================================================================
  1656. case WM_KEYDOWN:
  1657. case WM_SYSKEYDOWN:
  1658. if (doKeyDown (wParam))
  1659. return 0;
  1660. forwardMessageToParent (message, wParam, lParam);
  1661. break;
  1662. case WM_KEYUP:
  1663. case WM_SYSKEYUP:
  1664. if (doKeyUp (wParam))
  1665. return 0;
  1666. forwardMessageToParent (message, wParam, lParam);
  1667. break;
  1668. case WM_CHAR:
  1669. if (doKeyChar ((int) wParam, lParam))
  1670. return 0;
  1671. forwardMessageToParent (message, wParam, lParam);
  1672. break;
  1673. case WM_APPCOMMAND:
  1674. if (doAppCommand (lParam))
  1675. return TRUE;
  1676. break;
  1677. //==============================================================================
  1678. case WM_SETFOCUS:
  1679. updateKeyModifiers();
  1680. handleFocusGain();
  1681. break;
  1682. case WM_KILLFOCUS:
  1683. if (hasCreatedCaret)
  1684. {
  1685. hasCreatedCaret = false;
  1686. DestroyCaret();
  1687. }
  1688. handleFocusLoss();
  1689. break;
  1690. case WM_ACTIVATEAPP:
  1691. // Windows does weird things to process priority when you swap apps,
  1692. // so this forces an update when the app is brought to the front
  1693. if (wParam != FALSE)
  1694. juce_repeatLastProcessPriority();
  1695. else
  1696. Desktop::getInstance().setKioskModeComponent (nullptr); // turn kiosk mode off if we lose focus
  1697. juce_CheckCurrentlyFocusedTopLevelWindow();
  1698. modifiersAtLastCallback = -1;
  1699. return 0;
  1700. case WM_ACTIVATE:
  1701. if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
  1702. {
  1703. handleAppActivation (wParam);
  1704. return 0;
  1705. }
  1706. break;
  1707. case WM_NCACTIVATE:
  1708. // while a temporary window is being shown, prevent Windows from deactivating the
  1709. // title bars of our main windows.
  1710. if (wParam == 0 && ! shouldDeactivateTitleBar)
  1711. wParam = TRUE; // change this and let it get passed to the DefWindowProc.
  1712. break;
  1713. case WM_MOUSEACTIVATE:
  1714. if (! component->getMouseClickGrabsKeyboardFocus())
  1715. return MA_NOACTIVATE;
  1716. break;
  1717. case WM_SHOWWINDOW:
  1718. if (wParam != 0)
  1719. handleBroughtToFront();
  1720. break;
  1721. case WM_CLOSE:
  1722. if (! component->isCurrentlyBlockedByAnotherModalComponent())
  1723. handleUserClosingWindow();
  1724. return 0;
  1725. case WM_QUERYENDSESSION:
  1726. if (JUCEApplication::getInstance() != nullptr)
  1727. {
  1728. JUCEApplication::getInstance()->systemRequestedQuit();
  1729. return MessageManager::getInstance()->hasStopMessageBeenSent();
  1730. }
  1731. return TRUE;
  1732. case WM_SYNCPAINT:
  1733. return 0;
  1734. case WM_DISPLAYCHANGE:
  1735. InvalidateRect (h, 0, 0);
  1736. // intentional fall-through...
  1737. case WM_SETTINGCHANGE: // note the fall-through in the previous case!
  1738. doSettingChange();
  1739. break;
  1740. case WM_INITMENU:
  1741. initialiseSysMenu ((HMENU) wParam);
  1742. break;
  1743. case WM_SYSCOMMAND:
  1744. switch (wParam & 0xfff0)
  1745. {
  1746. case SC_CLOSE:
  1747. if (sendInputAttemptWhenModalMessage())
  1748. return 0;
  1749. if (hasTitleBar())
  1750. {
  1751. PostMessage (h, WM_CLOSE, 0, 0);
  1752. return 0;
  1753. }
  1754. break;
  1755. case SC_KEYMENU:
  1756. // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
  1757. // situations that can arise if a modal loop is started from an alt-key keypress).
  1758. if (hasTitleBar() && h == GetCapture())
  1759. ReleaseCapture();
  1760. break;
  1761. case SC_MAXIMIZE:
  1762. if (! sendInputAttemptWhenModalMessage())
  1763. setFullScreen (true);
  1764. return 0;
  1765. case SC_MINIMIZE:
  1766. if (sendInputAttemptWhenModalMessage())
  1767. return 0;
  1768. if (! hasTitleBar())
  1769. {
  1770. setMinimised (true);
  1771. return 0;
  1772. }
  1773. break;
  1774. case SC_RESTORE:
  1775. if (sendInputAttemptWhenModalMessage())
  1776. return 0;
  1777. if (hasTitleBar())
  1778. {
  1779. if (isFullScreen())
  1780. {
  1781. setFullScreen (false);
  1782. return 0;
  1783. }
  1784. }
  1785. else
  1786. {
  1787. if (isMinimised())
  1788. setMinimised (false);
  1789. else if (isFullScreen())
  1790. setFullScreen (false);
  1791. return 0;
  1792. }
  1793. break;
  1794. }
  1795. break;
  1796. case WM_NCLBUTTONDOWN:
  1797. handleLeftClickInNCArea (wParam);
  1798. break;
  1799. case WM_NCRBUTTONDOWN:
  1800. case WM_NCMBUTTONDOWN:
  1801. sendInputAttemptWhenModalMessage();
  1802. break;
  1803. case WM_IME_SETCONTEXT:
  1804. imeHandler.handleSetContext (h, wParam == TRUE);
  1805. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  1806. break;
  1807. case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
  1808. case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
  1809. case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
  1810. case WM_GETDLGCODE:
  1811. return DLGC_WANTALLKEYS;
  1812. default:
  1813. break;
  1814. }
  1815. return DefWindowProcW (h, message, wParam, lParam);
  1816. }
  1817. bool sendInputAttemptWhenModalMessage()
  1818. {
  1819. if (component->isCurrentlyBlockedByAnotherModalComponent())
  1820. {
  1821. Component* const current = Component::getCurrentlyModalComponent();
  1822. if (current != nullptr)
  1823. current->inputAttemptWhenModal();
  1824. return true;
  1825. }
  1826. return false;
  1827. }
  1828. //==============================================================================
  1829. class IMEHandler
  1830. {
  1831. public:
  1832. IMEHandler()
  1833. {
  1834. reset();
  1835. }
  1836. void handleSetContext (HWND hWnd, const bool windowIsActive)
  1837. {
  1838. if (compositionInProgress && ! windowIsActive)
  1839. {
  1840. compositionInProgress = false;
  1841. HIMC hImc = ImmGetContext (hWnd);
  1842. if (hImc != 0)
  1843. {
  1844. ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
  1845. ImmReleaseContext (hWnd, hImc);
  1846. }
  1847. }
  1848. }
  1849. void handleStartComposition (ComponentPeer& owner)
  1850. {
  1851. reset();
  1852. TextInputTarget* const target = owner.findCurrentTextInputTarget();
  1853. if (target != nullptr)
  1854. target->insertTextAtCaret (String::empty);
  1855. }
  1856. void handleEndComposition (ComponentPeer& owner, HWND hWnd)
  1857. {
  1858. if (compositionInProgress)
  1859. {
  1860. // If this occurs, the user has cancelled the composition, so clear their changes..
  1861. TextInputTarget* const target = owner.findCurrentTextInputTarget();
  1862. if (target != nullptr)
  1863. {
  1864. target->setHighlightedRegion (compositionRange);
  1865. target->insertTextAtCaret (String::empty);
  1866. compositionRange.setLength (0);
  1867. target->setHighlightedRegion (Range<int>::emptyRange (compositionRange.getEnd()));
  1868. target->setTemporaryUnderlining (Array<Range<int> >());
  1869. }
  1870. HIMC hImc = ImmGetContext (hWnd);
  1871. if (hImc != 0)
  1872. {
  1873. ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
  1874. ImmReleaseContext (hWnd, hImc);
  1875. }
  1876. }
  1877. reset();
  1878. }
  1879. void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
  1880. {
  1881. TextInputTarget* const target = owner.findCurrentTextInputTarget();
  1882. HIMC hImc = ImmGetContext (hWnd);
  1883. if (target == nullptr || hImc == 0)
  1884. return;
  1885. if (compositionRange.getStart() < 0)
  1886. compositionRange = Range<int>::emptyRange (target->getHighlightedRegion().getStart());
  1887. if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
  1888. {
  1889. replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
  1890. Range<int>::emptyRange (compositionRange.getEnd()));
  1891. target->setTemporaryUnderlining (Array<Range<int> >());
  1892. compositionInProgress = false;
  1893. }
  1894. else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
  1895. {
  1896. const String newContent (getCompositionString (hImc, GCS_COMPSTR));
  1897. const Range<int> selection (getCompositionSelection (hImc, lParam));
  1898. replaceCurrentSelection (target, newContent, selection);
  1899. target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
  1900. compositionInProgress = true;
  1901. }
  1902. moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
  1903. ImmReleaseContext (hWnd, hImc);
  1904. }
  1905. private:
  1906. //==============================================================================
  1907. Range<int> compositionRange; // The range being modified in the TextInputTarget
  1908. bool compositionInProgress;
  1909. //==============================================================================
  1910. void reset()
  1911. {
  1912. compositionRange = Range<int>::emptyRange (-1);
  1913. compositionInProgress = false;
  1914. }
  1915. String getCompositionString (HIMC hImc, const DWORD type) const
  1916. {
  1917. jassert (hImc != 0);
  1918. const int stringSizeBytes = ImmGetCompositionString (hImc, type, 0, 0);
  1919. if (stringSizeBytes > 0)
  1920. {
  1921. HeapBlock<TCHAR> buffer;
  1922. buffer.calloc (stringSizeBytes / sizeof (TCHAR) + 1);
  1923. ImmGetCompositionString (hImc, type, buffer, (DWORD) stringSizeBytes);
  1924. return String (buffer);
  1925. }
  1926. return String::empty;
  1927. }
  1928. int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
  1929. {
  1930. jassert (hImc != 0);
  1931. if ((lParam & CS_NOMOVECARET) != 0)
  1932. return compositionRange.getStart();
  1933. if ((lParam & GCS_CURSORPOS) != 0)
  1934. {
  1935. const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, 0, 0);
  1936. return compositionRange.getStart() + jmax (0, localCaretPos);
  1937. }
  1938. return compositionRange.getStart() + currentIMEString.length();
  1939. }
  1940. // Get selected/highlighted range while doing composition:
  1941. // returned range is relative to beginning of TextInputTarget, not composition string
  1942. Range<int> getCompositionSelection (HIMC hImc, LPARAM lParam) const
  1943. {
  1944. jassert (hImc != 0);
  1945. int selectionStart = 0;
  1946. int selectionEnd = 0;
  1947. if ((lParam & GCS_COMPATTR) != 0)
  1948. {
  1949. // Get size of attributes array:
  1950. const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, 0, 0);
  1951. if (attributeSizeBytes > 0)
  1952. {
  1953. // Get attributes (8 bit flag per character):
  1954. HeapBlock<char> attributes ((size_t) attributeSizeBytes);
  1955. ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, (DWORD) attributeSizeBytes);
  1956. selectionStart = 0;
  1957. for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
  1958. if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
  1959. break;
  1960. for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
  1961. if (attributes [selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
  1962. break;
  1963. }
  1964. }
  1965. return Range<int> (selectionStart, selectionEnd) + compositionRange.getStart();
  1966. }
  1967. void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, const Range<int>& newSelection)
  1968. {
  1969. target->setHighlightedRegion (compositionRange);
  1970. target->insertTextAtCaret (newContent);
  1971. compositionRange.setLength (newContent.length());
  1972. target->setHighlightedRegion (newSelection);
  1973. }
  1974. Array<Range<int> > getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
  1975. {
  1976. Array<Range<int> > result;
  1977. if (hImc != 0 && (lParam & GCS_COMPCLAUSE) != 0)
  1978. {
  1979. const int clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, 0, 0);
  1980. if (clauseDataSizeBytes > 0)
  1981. {
  1982. const size_t numItems = clauseDataSizeBytes / sizeof (uint32);
  1983. HeapBlock<uint32> clauseData (numItems);
  1984. if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, (DWORD) clauseDataSizeBytes) > 0)
  1985. for (size_t i = 0; i + 1 < numItems; ++i)
  1986. result.add (Range<int> ((int) clauseData [i], (int) clauseData [i + 1]) + compositionRange.getStart());
  1987. }
  1988. }
  1989. return result;
  1990. }
  1991. void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
  1992. {
  1993. Component* const targetComp = dynamic_cast <Component*> (target);
  1994. if (targetComp != nullptr)
  1995. {
  1996. const Rectangle<int> area (peer.getComponent()
  1997. ->getLocalArea (targetComp, target->getCaretRectangle()));
  1998. CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
  1999. ImmSetCandidateWindow (hImc, &pos);
  2000. }
  2001. }
  2002. JUCE_DECLARE_NON_COPYABLE (IMEHandler);
  2003. };
  2004. IMEHandler imeHandler;
  2005. //==============================================================================
  2006. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer);
  2007. };
  2008. ModifierKeys HWNDComponentPeer::currentModifiers;
  2009. ModifierKeys HWNDComponentPeer::modifiersAtLastCallback;
  2010. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  2011. {
  2012. return new HWNDComponentPeer (this, styleFlags, (HWND) nativeWindowToAttachTo);
  2013. }
  2014. ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component* component, void* parent)
  2015. {
  2016. HWNDComponentPeer* const p = new HWNDComponentPeer (component, ComponentPeer::windowIgnoresMouseClicks, (HWND) parent);
  2017. p->dontRepaint = true;
  2018. return p;
  2019. }
  2020. juce_ImplementSingleton_SingleThreaded (HWNDComponentPeer::WindowClassHolder);
  2021. //==============================================================================
  2022. void ModifierKeys::updateCurrentModifiers() noexcept
  2023. {
  2024. currentModifiers = HWNDComponentPeer::currentModifiers;
  2025. }
  2026. ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept
  2027. {
  2028. HWNDComponentPeer::updateKeyModifiers();
  2029. int mouseMods = 0;
  2030. if (HWNDComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
  2031. if (HWNDComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
  2032. if (HWNDComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
  2033. HWNDComponentPeer::currentModifiers
  2034. = HWNDComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  2035. return HWNDComponentPeer::currentModifiers;
  2036. }
  2037. //==============================================================================
  2038. bool KeyPress::isKeyCurrentlyDown (const int keyCode)
  2039. {
  2040. SHORT k = (SHORT) keyCode;
  2041. if ((keyCode & extendedKeyModifier) == 0
  2042. && (k >= (SHORT) 'a' && k <= (SHORT) 'z'))
  2043. k += (SHORT) 'A' - (SHORT) 'a';
  2044. const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
  2045. (SHORT) '+', VK_OEM_PLUS,
  2046. (SHORT) '-', VK_OEM_MINUS,
  2047. (SHORT) '.', VK_OEM_PERIOD,
  2048. (SHORT) ';', VK_OEM_1,
  2049. (SHORT) ':', VK_OEM_1,
  2050. (SHORT) '/', VK_OEM_2,
  2051. (SHORT) '?', VK_OEM_2,
  2052. (SHORT) '[', VK_OEM_4,
  2053. (SHORT) ']', VK_OEM_6 };
  2054. for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
  2055. if (k == translatedValues [i])
  2056. k = translatedValues [i + 1];
  2057. return HWNDComponentPeer::isKeyDown (k);
  2058. }
  2059. //==============================================================================
  2060. bool Process::isForegroundProcess()
  2061. {
  2062. HWND fg = GetForegroundWindow();
  2063. if (fg == 0)
  2064. return true;
  2065. // when running as a plugin in IE8, the browser UI runs in a different process to the plugin, so
  2066. // process ID isn't a reliable way to check if the foreground window belongs to us - instead, we
  2067. // have to see if any of our windows are children of the foreground window
  2068. fg = GetAncestor (fg, GA_ROOT);
  2069. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  2070. {
  2071. HWNDComponentPeer* const wp = dynamic_cast <HWNDComponentPeer*> (ComponentPeer::getPeer (i));
  2072. if (wp != nullptr && wp->isInside (fg))
  2073. return true;
  2074. }
  2075. return false;
  2076. }
  2077. //==============================================================================
  2078. class WindowsMessageBox : public AsyncUpdater
  2079. {
  2080. public:
  2081. WindowsMessageBox (AlertWindow::AlertIconType iconType,
  2082. const String& title_, const String& message_,
  2083. Component* associatedComponent,
  2084. UINT extraFlags,
  2085. ModalComponentManager::Callback* callback_,
  2086. const bool runAsync)
  2087. : flags (extraFlags | getMessageBoxFlags (iconType)),
  2088. owner (getWindowForMessageBox (associatedComponent)),
  2089. title (title_), message (message_), callback (callback_)
  2090. {
  2091. if (runAsync)
  2092. triggerAsyncUpdate();
  2093. }
  2094. int getResult() const
  2095. {
  2096. const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags);
  2097. return (r == IDYES || r == IDOK) ? 1 : (r == IDNO ? 2 : 0);
  2098. }
  2099. void handleAsyncUpdate()
  2100. {
  2101. const int result = getResult();
  2102. if (callback != nullptr)
  2103. callback->modalStateFinished (result);
  2104. delete this;
  2105. }
  2106. private:
  2107. UINT flags;
  2108. HWND owner;
  2109. String title, message;
  2110. ModalComponentManager::Callback* callback;
  2111. static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) noexcept
  2112. {
  2113. UINT flags = MB_TASKMODAL | MB_SETFOREGROUND;
  2114. switch (iconType)
  2115. {
  2116. case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break;
  2117. case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break;
  2118. case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break;
  2119. default: break;
  2120. }
  2121. return flags;
  2122. }
  2123. static HWND getWindowForMessageBox (Component* associatedComponent)
  2124. {
  2125. return associatedComponent != nullptr ? (HWND) associatedComponent->getWindowHandle() : 0;
  2126. }
  2127. };
  2128. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  2129. const String& title, const String& message,
  2130. Component* associatedComponent)
  2131. {
  2132. WindowsMessageBox box (iconType, title, message, associatedComponent, MB_OK, 0, false);
  2133. (void) box.getResult();
  2134. }
  2135. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  2136. const String& title, const String& message,
  2137. Component* associatedComponent)
  2138. {
  2139. new WindowsMessageBox (iconType, title, message, associatedComponent, MB_OK, 0, true);
  2140. }
  2141. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
  2142. const String& title, const String& message,
  2143. Component* associatedComponent,
  2144. ModalComponentManager::Callback* callback)
  2145. {
  2146. ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  2147. MB_OKCANCEL, callback, callback != nullptr));
  2148. if (callback == nullptr)
  2149. return mb->getResult() != 0;
  2150. mb.release();
  2151. return false;
  2152. }
  2153. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  2154. const String& title, const String& message,
  2155. Component* associatedComponent,
  2156. ModalComponentManager::Callback* callback)
  2157. {
  2158. ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
  2159. MB_YESNOCANCEL, callback, callback != nullptr));
  2160. if (callback == nullptr)
  2161. return mb->getResult();
  2162. mb.release();
  2163. return 0;
  2164. }
  2165. //==============================================================================
  2166. void Desktop::createMouseInputSources()
  2167. {
  2168. mouseSources.add (new MouseInputSource (0, true));
  2169. }
  2170. Point<int> MouseInputSource::getCurrentMousePosition()
  2171. {
  2172. POINT mousePos;
  2173. GetCursorPos (&mousePos);
  2174. return Point<int> (mousePos.x, mousePos.y);
  2175. }
  2176. void Desktop::setMousePosition (const Point<int>& newPosition)
  2177. {
  2178. SetCursorPos (newPosition.getX(), newPosition.getY());
  2179. }
  2180. //==============================================================================
  2181. Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage)
  2182. {
  2183. return createSoftwareImage (format, width, height, clearImage);
  2184. }
  2185. //==============================================================================
  2186. class ScreenSaverDefeater : public Timer
  2187. {
  2188. public:
  2189. ScreenSaverDefeater()
  2190. {
  2191. startTimer (10000);
  2192. timerCallback();
  2193. }
  2194. void timerCallback()
  2195. {
  2196. if (Process::isForegroundProcess())
  2197. {
  2198. // simulate a shift key getting pressed..
  2199. INPUT input[2];
  2200. input[0].type = INPUT_KEYBOARD;
  2201. input[0].ki.wVk = VK_SHIFT;
  2202. input[0].ki.dwFlags = 0;
  2203. input[0].ki.dwExtraInfo = 0;
  2204. input[1].type = INPUT_KEYBOARD;
  2205. input[1].ki.wVk = VK_SHIFT;
  2206. input[1].ki.dwFlags = KEYEVENTF_KEYUP;
  2207. input[1].ki.dwExtraInfo = 0;
  2208. SendInput (2, input, sizeof (INPUT));
  2209. }
  2210. }
  2211. };
  2212. static ScopedPointer<ScreenSaverDefeater> screenSaverDefeater;
  2213. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  2214. {
  2215. if (isEnabled)
  2216. screenSaverDefeater = nullptr;
  2217. else if (screenSaverDefeater == nullptr)
  2218. screenSaverDefeater = new ScreenSaverDefeater();
  2219. }
  2220. bool Desktop::isScreenSaverEnabled()
  2221. {
  2222. return screenSaverDefeater == nullptr;
  2223. }
  2224. /* (The code below is the "correct" way to disable the screen saver, but it
  2225. completely fails on winXP when the saver is password-protected...)
  2226. static bool juce_screenSaverEnabled = true;
  2227. void Desktop::setScreenSaverEnabled (const bool isEnabled) noexcept
  2228. {
  2229. juce_screenSaverEnabled = isEnabled;
  2230. SetThreadExecutionState (isEnabled ? ES_CONTINUOUS
  2231. : (ES_DISPLAY_REQUIRED | ES_CONTINUOUS));
  2232. }
  2233. bool Desktop::isScreenSaverEnabled() noexcept
  2234. {
  2235. return juce_screenSaverEnabled;
  2236. }
  2237. */
  2238. //==============================================================================
  2239. void LookAndFeel::playAlertSound()
  2240. {
  2241. MessageBeep (MB_OK);
  2242. }
  2243. //==============================================================================
  2244. void SystemClipboard::copyTextToClipboard (const String& text)
  2245. {
  2246. if (OpenClipboard (0) != 0)
  2247. {
  2248. if (EmptyClipboard() != 0)
  2249. {
  2250. const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4;
  2251. if (bytesNeeded > 0)
  2252. {
  2253. HGLOBAL bufH = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, bytesNeeded + sizeof (WCHAR));
  2254. if (bufH != 0)
  2255. {
  2256. WCHAR* const data = static_cast <WCHAR*> (GlobalLock (bufH));
  2257. if (data != nullptr)
  2258. {
  2259. text.copyToUTF16 (data, (int) bytesNeeded);
  2260. GlobalUnlock (bufH);
  2261. SetClipboardData (CF_UNICODETEXT, bufH);
  2262. }
  2263. }
  2264. }
  2265. }
  2266. CloseClipboard();
  2267. }
  2268. }
  2269. String SystemClipboard::getTextFromClipboard()
  2270. {
  2271. String result;
  2272. if (OpenClipboard (0) != 0)
  2273. {
  2274. HANDLE bufH = GetClipboardData (CF_UNICODETEXT);
  2275. if (bufH != 0)
  2276. {
  2277. const WCHAR* const data = (const WCHAR*) GlobalLock (bufH);
  2278. if (data != nullptr)
  2279. {
  2280. result = String (data, (size_t) (GlobalSize (bufH) / sizeof (WCHAR)));
  2281. GlobalUnlock (bufH);
  2282. }
  2283. }
  2284. CloseClipboard();
  2285. }
  2286. return result;
  2287. }
  2288. //==============================================================================
  2289. void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /*allowMenusAndBars*/)
  2290. {
  2291. if (enableOrDisable)
  2292. kioskModeComponent->setBounds (Desktop::getInstance().getMainMonitorArea (false));
  2293. }
  2294. //==============================================================================
  2295. static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo)
  2296. {
  2297. Array <Rectangle<int> >* const monitorCoords = (Array <Rectangle<int> >*) userInfo;
  2298. monitorCoords->add (Rectangle<int> (r->left, r->top, r->right - r->left, r->bottom - r->top));
  2299. return TRUE;
  2300. }
  2301. void Desktop::getCurrentMonitorPositions (Array <Rectangle<int> >& monitorCoords, const bool clipToWorkArea)
  2302. {
  2303. EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitorCoords);
  2304. // make sure the first in the list is the main monitor
  2305. for (int i = 1; i < monitorCoords.size(); ++i)
  2306. if (monitorCoords[i].getX() == 0 && monitorCoords[i].getY() == 0)
  2307. monitorCoords.swap (i, 0);
  2308. if (monitorCoords.size() == 0)
  2309. {
  2310. RECT r;
  2311. GetWindowRect (GetDesktopWindow(), &r);
  2312. monitorCoords.add (Rectangle<int> (r.left, r.top, r.right - r.left, r.bottom - r.top));
  2313. }
  2314. if (clipToWorkArea)
  2315. {
  2316. // clip the main monitor to the active non-taskbar area
  2317. RECT r;
  2318. SystemParametersInfo (SPI_GETWORKAREA, 0, &r, 0);
  2319. Rectangle<int>& screen = monitorCoords.getReference (0);
  2320. screen.setPosition (jmax (screen.getX(), (int) r.left),
  2321. jmax (screen.getY(), (int) r.top));
  2322. screen.setSize (jmin (screen.getRight(), (int) r.right) - screen.getX(),
  2323. jmin (screen.getBottom(), (int) r.bottom) - screen.getY());
  2324. }
  2325. }
  2326. //==============================================================================
  2327. static HICON extractFileHICON (const File& file)
  2328. {
  2329. WORD iconNum = 0;
  2330. WCHAR name [MAX_PATH * 2];
  2331. file.getFullPathName().copyToUTF16 (name, sizeof (name));
  2332. return ExtractAssociatedIcon ((HINSTANCE) Process::getCurrentModuleInstanceHandle(),
  2333. name, &iconNum);
  2334. }
  2335. Image juce_createIconForFile (const File& file)
  2336. {
  2337. Image image;
  2338. HICON icon = extractFileHICON (file);
  2339. if (icon != 0)
  2340. {
  2341. image = IconConverters::createImageFromHICON (icon);
  2342. DestroyIcon (icon);
  2343. }
  2344. return image;
  2345. }
  2346. //==============================================================================
  2347. void* MouseCursor::createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY)
  2348. {
  2349. const int maxW = GetSystemMetrics (SM_CXCURSOR);
  2350. const int maxH = GetSystemMetrics (SM_CYCURSOR);
  2351. Image im (image);
  2352. if (im.getWidth() > maxW || im.getHeight() > maxH)
  2353. {
  2354. im = im.rescaled (maxW, maxH);
  2355. hotspotX = (hotspotX * maxW) / image.getWidth();
  2356. hotspotY = (hotspotY * maxH) / image.getHeight();
  2357. }
  2358. return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY);
  2359. }
  2360. void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStandard)
  2361. {
  2362. if (cursorHandle != nullptr && ! isStandard)
  2363. DestroyCursor ((HCURSOR) cursorHandle);
  2364. }
  2365. enum
  2366. {
  2367. hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor)
  2368. };
  2369. void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type)
  2370. {
  2371. LPCTSTR cursorName = IDC_ARROW;
  2372. switch (type)
  2373. {
  2374. case NormalCursor: break;
  2375. case NoCursor: return (void*) hiddenMouseCursorHandle;
  2376. case WaitCursor: cursorName = IDC_WAIT; break;
  2377. case IBeamCursor: cursorName = IDC_IBEAM; break;
  2378. case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
  2379. case CrosshairCursor: cursorName = IDC_CROSS; break;
  2380. case CopyingCursor: break; // can't seem to find one of these in the system list..
  2381. case LeftRightResizeCursor:
  2382. case LeftEdgeResizeCursor:
  2383. case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
  2384. case UpDownResizeCursor:
  2385. case TopEdgeResizeCursor:
  2386. case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
  2387. case TopLeftCornerResizeCursor:
  2388. case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
  2389. case TopRightCornerResizeCursor:
  2390. case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
  2391. case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
  2392. case DraggingHandCursor:
  2393. {
  2394. static void* dragHandCursor = nullptr;
  2395. if (dragHandCursor == nullptr)
  2396. {
  2397. static const unsigned char dragHandData[] =
  2398. { 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,
  2399. 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,
  2400. 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 };
  2401. dragHandCursor = createMouseCursorFromImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), 8, 7);
  2402. }
  2403. return dragHandCursor;
  2404. }
  2405. default:
  2406. jassertfalse; break;
  2407. }
  2408. HCURSOR cursorH = LoadCursor (0, cursorName);
  2409. if (cursorH == 0)
  2410. cursorH = LoadCursor (0, IDC_ARROW);
  2411. return cursorH;
  2412. }
  2413. //==============================================================================
  2414. void MouseCursor::showInWindow (ComponentPeer*) const
  2415. {
  2416. HCURSOR c = (HCURSOR) getHandle();
  2417. if (c == 0)
  2418. c = LoadCursor (0, IDC_ARROW);
  2419. else if (c == (HCURSOR) hiddenMouseCursorHandle)
  2420. c = 0;
  2421. SetCursor (c);
  2422. }
  2423. void MouseCursor::showInAllWindows() const
  2424. {
  2425. showInWindow (nullptr);
  2426. }