Audio plugin host https://kx.studio/carla
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.

239 lines
8.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. extern void* getUser32Function (const char*);
  20. namespace IconConverters
  21. {
  22. extern HICON createHICONFromImage (const Image&, BOOL isIcon, int hotspotX, int hotspotY);
  23. }
  24. //==============================================================================
  25. class SystemTrayIconComponent::Pimpl
  26. {
  27. public:
  28. Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd)
  29. : owner (owner_),
  30. originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)),
  31. taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated")))
  32. {
  33. SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc);
  34. zerostruct (iconData);
  35. iconData.cbSize = sizeof (iconData);
  36. iconData.hWnd = hwnd;
  37. iconData.uID = (UINT) (pointer_sized_int) hwnd;
  38. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  39. iconData.uCallbackMessage = WM_TRAYNOTIFY;
  40. iconData.hIcon = hicon;
  41. notify (NIM_ADD);
  42. // In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out.
  43. // (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later)
  44. typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD);
  45. if (ChangeWindowMessageFilterType changeWindowMessageFilter
  46. = (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"))
  47. changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */);
  48. }
  49. ~Pimpl()
  50. {
  51. SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
  52. iconData.uFlags = 0;
  53. notify (NIM_DELETE);
  54. DestroyIcon (iconData.hIcon);
  55. }
  56. void updateIcon (HICON hicon)
  57. {
  58. HICON oldIcon = iconData.hIcon;
  59. iconData.hIcon = hicon;
  60. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  61. notify (NIM_MODIFY);
  62. DestroyIcon (oldIcon);
  63. }
  64. void setToolTip (const String& toolTip)
  65. {
  66. iconData.uFlags = NIF_TIP;
  67. toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1);
  68. notify (NIM_MODIFY);
  69. }
  70. void handleTaskBarEvent (const LPARAM lParam)
  71. {
  72. if (owner.isCurrentlyBlockedByAnotherModalComponent())
  73. {
  74. if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
  75. || lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
  76. {
  77. if (auto* current = Component::getCurrentlyModalComponent())
  78. current->inputAttemptWhenModal();
  79. }
  80. }
  81. else
  82. {
  83. ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
  84. if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
  85. eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
  86. else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
  87. eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
  88. else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
  89. eventMods = eventMods.withoutMouseButtons();
  90. const Time eventTime (getMouseEventTime());
  91. const MouseEvent e (Desktop::getInstance().getMainMouseSource(), {}, eventMods,
  92. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation,
  93. MouseInputSource::invalidRotation, MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
  94. &owner, &owner, eventTime, {}, eventTime, 1, false);
  95. if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
  96. {
  97. SetFocus (iconData.hWnd);
  98. SetForegroundWindow (iconData.hWnd);
  99. owner.mouseDown (e);
  100. }
  101. else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
  102. {
  103. owner.mouseUp (e);
  104. }
  105. else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
  106. {
  107. owner.mouseDoubleClick (e);
  108. }
  109. else if (lParam == WM_MOUSEMOVE)
  110. {
  111. owner.mouseMove (e);
  112. }
  113. }
  114. }
  115. static Pimpl* getPimpl (HWND hwnd)
  116. {
  117. if (JuceWindowIdentifier::isJUCEWindow (hwnd))
  118. if (ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8))
  119. if (SystemTrayIconComponent* const iconComp = dynamic_cast<SystemTrayIconComponent*> (&(peer->getComponent())))
  120. return iconComp->pimpl;
  121. return nullptr;
  122. }
  123. static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  124. {
  125. if (Pimpl* const p = getPimpl (hwnd))
  126. return p->windowProc (hwnd, message, wParam, lParam);
  127. return DefWindowProcW (hwnd, message, wParam, lParam);
  128. }
  129. LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  130. {
  131. if (message == WM_TRAYNOTIFY)
  132. {
  133. handleTaskBarEvent (lParam);
  134. }
  135. else if (message == taskbarCreatedMessage)
  136. {
  137. iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  138. notify (NIM_ADD);
  139. }
  140. return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam);
  141. }
  142. void showBubble (const String& title, const String& content)
  143. {
  144. iconData.uFlags = 0x10 /*NIF_INFO*/;
  145. title.copyToUTF16 (iconData.szInfoTitle, sizeof (iconData.szInfoTitle) - 1);
  146. content.copyToUTF16 (iconData.szInfo, sizeof (iconData.szInfo) - 1);
  147. notify (NIM_MODIFY);
  148. }
  149. SystemTrayIconComponent& owner;
  150. NOTIFYICONDATA iconData;
  151. private:
  152. WNDPROC originalWndProc;
  153. const DWORD taskbarCreatedMessage;
  154. enum { WM_TRAYNOTIFY = WM_USER + 100 };
  155. void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); }
  156. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  157. };
  158. //==============================================================================
  159. void SystemTrayIconComponent::setIconImage (const Image& newImage)
  160. {
  161. if (newImage.isValid())
  162. {
  163. HICON hicon = IconConverters::createHICONFromImage (newImage, TRUE, 0, 0);
  164. if (pimpl == nullptr)
  165. pimpl = new Pimpl (*this, hicon, (HWND) getWindowHandle());
  166. else
  167. pimpl->updateIcon (hicon);
  168. }
  169. else
  170. {
  171. pimpl = nullptr;
  172. }
  173. }
  174. void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
  175. {
  176. if (pimpl != nullptr)
  177. pimpl->setToolTip (tooltip);
  178. }
  179. void SystemTrayIconComponent::setHighlighted (bool)
  180. {
  181. // N/A on Windows.
  182. }
  183. void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content)
  184. {
  185. if (pimpl != nullptr)
  186. pimpl->showBubble (title, content);
  187. }
  188. void SystemTrayIconComponent::hideInfoBubble()
  189. {
  190. showInfoBubble (String(), String());
  191. }
  192. void* SystemTrayIconComponent::getNativeHandle() const
  193. {
  194. return pimpl != nullptr ? &(pimpl->iconData) : nullptr;
  195. }