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.

juce_win32_SystemTrayIcon.cpp 8.1KB

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