From 1ee106d73092475baadc189c2db24bb45f454442 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 7 Jun 2021 20:02:12 +0100 Subject: [PATCH] PopupMenu: Avoid dismissing PopupMenus in bridged plugin editors When bridging 32-bit/64-bit plugins on Windows, the plugin is hosted in an auxilliary process, and the plugin's editor is embedded into an HWND owned by a different process (the plugin host). Previously, the `isForegroundProcess` check would fail for bridged plugins, because the foreground window may belong to the DAW, rather than to the auxilliary hosting process. This patch adds an additional check, to find whether the same process owns both the foreground window, and the window which embeds the PopupMenu's target component. In this case, we avoid immediately dismissing the PopupMenu. --- .../juce_ApplicationCommandManager.cpp | 10 +++--- .../filebrowser/juce_FileBrowserComponent.cpp | 2 +- modules/juce_gui_basics/juce_gui_basics.cpp | 16 ++++++++- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 2 +- .../native/juce_win32_Windowing.cpp | 33 +++++++++++++++---- .../windows/juce_CallOutBox.cpp | 2 +- .../windows/juce_TooltipWindow.cpp | 2 +- 7 files changed, 52 insertions(+), 15 deletions(-) diff --git a/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp b/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp index 4940e52a8d..0ca18aa5e2 100644 --- a/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp +++ b/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp @@ -263,15 +263,17 @@ ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget( } } - if (c == nullptr && Process::isForegroundProcess()) + if (c == nullptr) { auto& desktop = Desktop::getInstance(); // getting a bit desperate now: try all desktop comps.. for (int i = desktop.getNumComponents(); --i >= 0;) - if (auto* peer = desktop.getComponent(i)->getPeer()) - if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent())) - return target; + if (auto* component = desktop.getComponent (i)) + if (isForegroundOrEmbeddedProcess (component)) + if (auto* peer = component->getPeer()) + if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent())) + return target; } if (c != nullptr) diff --git a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index 96ff84f721..a36a3fc6fb 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -605,7 +605,7 @@ void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPa void FileBrowserComponent::timerCallback() { - const bool isProcessActive = Process::isForegroundProcess(); + const auto isProcessActive = isForegroundOrEmbeddedProcess (this); if (wasProcessActive != isProcessActive) { diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index 438c2c49db..fcd4cbfbf3 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -105,7 +105,21 @@ namespace juce { - extern bool juce_areThereAnyAlwaysOnTopWindows(); + bool juce_areThereAnyAlwaysOnTopWindows(); + + bool isEmbeddedInForegroundProcess (Component* c); + + #if ! JUCE_WINDOWS + bool isEmbeddedInForegroundProcess (Component*) { return false; } + #endif + + /* Returns true if this process is in the foreground, or if the viewComponent + is embedded into a window owned by the foreground process. + */ + bool isForegroundOrEmbeddedProcess (Component* viewComponent) + { + return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent); + } } #include "accessibility/juce_AccessibilityHandler.cpp" diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 9af574885e..262c10e3af 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -746,7 +746,7 @@ struct MenuWindow : public Component bool doesAnyJuceCompHaveFocus() { - if (! Process::isForegroundProcess()) + if (! isForegroundOrEmbeddedProcess (componentAttachedTo)) return false; if (Component::getCurrentlyFocusedComponent() != nullptr) diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 709408cf10..0103297b3c 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -4333,15 +4333,36 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); } //============================================================================== +static DWORD getProcess (HWND hwnd) +{ + DWORD result = 0; + GetWindowThreadProcessId (hwnd, &result); + return result; +} + +/* Returns true if the viewComponent is embedded into a window + owned by the foreground process. +*/ +bool isEmbeddedInForegroundProcess (Component* c) +{ + if (c == nullptr) + return false; + + auto* peer = c->getPeer(); + auto* hwnd = peer != nullptr ? static_cast (peer->getNativeHandle()) : nullptr; + + if (hwnd == nullptr) + return true; + + const auto fgProcess = getProcess (GetForegroundWindow()); + const auto ownerProcess = getProcess (GetAncestor (hwnd, GA_ROOTOWNER)); + return fgProcess == ownerProcess; +} + bool JUCE_CALLTYPE Process::isForegroundProcess() { if (auto fg = GetForegroundWindow()) - { - DWORD processID = 0; - GetWindowThreadProcessId (fg, &processID); - - return processID == GetCurrentProcessId(); - } + return getProcess (fg) == GetCurrentProcessId(); return true; } diff --git a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index aa70a4af00..a95d92cdcd 100644 --- a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -67,7 +67,7 @@ public: void timerCallback() override { - if (! Process::isForegroundProcess()) + if (! isForegroundOrEmbeddedProcess (&callout)) callout.dismiss(); } diff --git a/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp b/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp index aa8b12f85e..d2ee788e6b 100644 --- a/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp @@ -130,7 +130,7 @@ void TooltipWindow::displayTip (Point screenPos, const String& tip) String TooltipWindow::getTipFor (Component& c) { - if (Process::isForegroundProcess() + if (isForegroundOrEmbeddedProcess (&c) && ! ModifierKeys::currentModifiers.isAnyMouseButtonDown()) { if (auto* ttc = dynamic_cast (&c))