1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

AUv3: Fix an issue in detecting the available screen user area

On at least iOS 26 using a temporary window frame is unreliable. This
change tries to use an existing window for any non-standalone app. It
also updates the details on any changes, such as when the device
orientation changes.
This commit is contained in:
Anthony Nicholls 2025-10-06 10:56:14 +01:00 committed by Anthony Nicholls
parent 32b4423ca8
commit 88af872d4d
8 changed files with 81 additions and 37 deletions

View file

@ -1896,6 +1896,8 @@ public:
{ {
JUCE_ASSERT_MESSAGE_THREAD JUCE_ASSERT_MESSAGE_THREAD
refreshDisplays();
if (auto p = createPluginFilterOfType (AudioProcessor::wrapperType_AudioUnitv3)) if (auto p = createPluginFilterOfType (AudioProcessor::wrapperType_AudioUnitv3))
{ {
processorHolder = new AudioProcessorHolder (std::move (p)); processorHolder = new AudioProcessorHolder (std::move (p));
@ -1932,6 +1934,8 @@ public:
void viewDidLayoutSubviews() void viewDidLayoutSubviews()
{ {
refreshDisplays();
if (auto holder = processorHolder.get()) if (auto holder = processorHolder.get())
{ {
if ([myself view] != nullptr) if ([myself view] != nullptr)
@ -2002,6 +2006,7 @@ public:
} }
private: private:
static void refreshDisplays() { const_cast<Displays&> (Desktop::getInstance().getDisplays()).refresh(); }
// There's a chance that createAudioUnit will be called from a background // There's a chance that createAudioUnit will be called from a background
// thread while the processorHolder is being updated on the main thread. // thread while the processorHolder is being updated on the main thread.

View file

@ -35,14 +35,14 @@
namespace juce namespace juce
{ {
Displays::Displays (Desktop& desktop) Displays::Displays (const Desktop& desktop)
{ {
init (desktop); init (desktop);
} }
void Displays::init (Desktop& desktop) void Displays::init (const Desktop& desktop)
{ {
findDisplays (desktop.getGlobalScaleFactor()); findDisplays (desktop);
} }
const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept

View file

@ -44,7 +44,7 @@ namespace juce
class JUCE_API Displays class JUCE_API Displays
{ {
private: private:
Displays (Desktop&); Displays (const Desktop&);
public: public:
//============================================================================== //==============================================================================
@ -232,8 +232,8 @@ public:
private: private:
friend class Desktop; friend class Desktop;
void init (Desktop&); void init (const Desktop&);
void findDisplays (float masterScale); void findDisplays (const Desktop& desktop);
void updateToLogical(); void updateToLogical();

View file

@ -2849,7 +2849,7 @@ DECLARE_JNI_CLASS (AndroidDisplayMetrics, "android/util/DisplayMetrics")
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
//============================================================================== //==============================================================================
void Displays::findDisplays (float masterScale) void Displays::findDisplays (const Desktop& desktop)
{ {
auto* env = getEnv(); auto* env = getEnv();
@ -2865,7 +2865,7 @@ void Displays::findDisplays (float masterScale)
d.scale = env->GetFloatField (displayMetrics, AndroidDisplayMetrics.density); d.scale = env->GetFloatField (displayMetrics, AndroidDisplayMetrics.density);
d.dpi = (d.scale * 160.f); d.dpi = (d.scale * 160.f);
d.scale *= masterScale; d.scale *= desktop.getGlobalScaleFactor();
d.totalArea = Rectangle<int> (env->GetIntField (displayMetrics, AndroidDisplayMetrics.widthPixels), d.totalArea = Rectangle<int> (env->GetIntField (displayMetrics, AndroidDisplayMetrics.widthPixels),
env->GetIntField (displayMetrics, AndroidDisplayMetrics.heightPixels)) / d.scale; env->GetIntField (displayMetrics, AndroidDisplayMetrics.heightPixels)) / d.scale;

View file

@ -651,42 +651,78 @@ Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
return Orientations::convertToJuce (orientation); return Orientations::convertToJuce (orientation);
} }
// The most straightforward way of retrieving the screen area available to an iOS app struct WindowInfo
// seems to be to create a new window (which will take up all available space) and to
// query its frame.
struct TemporaryWindow
{ {
UIWindow* window = std::invoke ([&] explicit WindowInfo (const UIWindow* window)
: bounds (convertToRectInt (window.frame)),
safeInsets (window.safeAreaInsets.top,
window.safeAreaInsets.left,
window.safeAreaInsets.bottom,
window.safeAreaInsets.right)
{}
Rectangle<int> bounds;
BorderSize<double> safeInsets;
};
static const UIWindow* findWindow (const UIView* view)
{
if (view == nullptr)
return nullptr;
if (view.window != nullptr)
return view.window;
return findWindow (view.superview);
}
static const UIWindow* findWindow (const Desktop& desktop)
{
if (auto* c = desktop.getComponent (0))
if (auto* p = static_cast<UIViewComponentPeer*> (c->getPeer()))
if (auto* w = findWindow (p->view))
return w;
return {};
}
static WindowInfo getWindowInfo (const Desktop& desktop)
{
if (! JUCEApplication::isStandaloneApp())
if (const auto* window = findWindow (desktop))
return WindowInfo { window };
const auto createTemporaryWindow = []()
{ {
if (@available (iOS 13, *)) if (@available (iOS 13, *))
{ {
SharedResourcePointer<WindowSceneTracker> windowSceneTracker; SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
if (auto* scene = windowSceneTracker->getWindowScene()) if (auto* scene = windowSceneTracker->getWindowScene())
return [[UIWindow alloc] initWithWindowScene: scene]; return NSUniquePtr<UIWindow> { [[UIWindow alloc] initWithWindowScene: scene] };
} }
return [[UIWindow alloc] init]; return NSUniquePtr<UIWindow> { [[UIWindow alloc] init] };
}); };
~TemporaryWindow() noexcept { [window release]; }
};
static Rectangle<int> getRecommendedWindowBounds() auto window (createTemporaryWindow());
{ return WindowInfo { window.get() };
return convertToRectInt (TemporaryWindow().window.frame);
} }
static BorderSize<int> getSafeAreaInsets (float masterScale) static Rectangle<int> getRecommendedWindowBounds (const Desktop& desktop)
{ {
UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; return getWindowInfo (desktop).bounds;
return detail::WindowingHelpers::roundToInt (BorderSize<double> { safeInsets.top, }
safeInsets.left,
safeInsets.bottom, static BorderSize<int> getSafeAreaInsets (const Desktop& desktop)
safeInsets.right }.multipliedBy (1.0 / (double) masterScale)); {
const auto masterScale = (double) desktop.getGlobalScaleFactor();
const auto safeInsets = getWindowInfo (desktop).safeInsets;
return detail::WindowingHelpers::roundToInt (safeInsets.multipliedBy (1.0 / masterScale));
} }
//============================================================================== //==============================================================================
void Displays::findDisplays (float masterScale) void Displays::findDisplays (const Desktop& desktop)
{ {
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
static const auto keyboardShownSelector = @selector (juceKeyboardShown:); static const auto keyboardShownSelector = @selector (juceKeyboardShown:);
@ -716,7 +752,7 @@ void Displays::findDisplays (float masterScale)
addMethod (keyboardShownSelector, [] (id self, SEL, NSNotification* notification) addMethod (keyboardShownSelector, [] (id self, SEL, NSNotification* notification)
{ {
setKeyboardScreenBounds (self, [&]() -> BorderSize<double> setKeyboardScreenBounds (self, std::invoke ([&]() -> BorderSize<double>
{ {
auto* info = [notification userInfo]; auto* info = [notification userInfo];
@ -744,7 +780,7 @@ void Displays::findDisplays (float masterScale)
result.setBottom (rect.getHeight()); result.setBottom (rect.getHeight());
return result; return result;
}()); }));
}); });
addMethod (keyboardHiddenSelector, [] (id self, SEL, NSNotification*) addMethod (keyboardHiddenSelector, [] (id self, SEL, NSNotification*)
@ -777,9 +813,10 @@ void Displays::findDisplays (float masterScale)
UIScreen* s = [UIScreen mainScreen]; UIScreen* s = [UIScreen mainScreen];
Display d; Display d;
const auto masterScale = desktop.getGlobalScaleFactor();
d.totalArea = convertToRectInt ([s bounds]) / masterScale; d.totalArea = convertToRectInt ([s bounds]) / masterScale;
d.userArea = getRecommendedWindowBounds() / masterScale; d.userArea = getRecommendedWindowBounds (desktop) / masterScale;
d.safeAreaInsets = getSafeAreaInsets (masterScale); d.safeAreaInsets = getSafeAreaInsets (desktop);
const auto scaledInsets = keyboardChangeDetector.getInsets().multipliedBy (1.0 / (double) masterScale); const auto scaledInsets = keyboardChangeDetector.getInsets().multipliedBy (1.0 / (double) masterScale);
d.keyboardInsets = detail::WindowingHelpers::roundToInt (scaledInsets); d.keyboardInsets = detail::WindowingHelpers::roundToInt (scaledInsets);
d.isMain = true; d.isMain = true;

View file

@ -631,11 +631,11 @@ void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea); comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
} }
void Displays::findDisplays (float masterScale) void Displays::findDisplays (const Desktop& desktop)
{ {
if (XWindowSystem::getInstance()->getDisplay() != nullptr) if (XWindowSystem::getInstance()->getDisplay() != nullptr)
{ {
displays = XWindowSystem::getInstance()->findDisplays (masterScale); displays = XWindowSystem::getInstance()->findDisplays (desktop.getGlobalScaleFactor());
if (! displays.isEmpty()) if (! displays.isEmpty())
updateToLogical(); updateToLogical();

View file

@ -473,7 +473,7 @@ static Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenB
return d; return d;
} }
void Displays::findDisplays (const float masterScale) void Displays::findDisplays (const Desktop& desktop)
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
@ -483,7 +483,7 @@ void Displays::findDisplays (const float masterScale)
CGFloat mainScreenBottom = 0; CGFloat mainScreenBottom = 0;
for (NSScreen* s in [NSScreen screens]) for (NSScreen* s in [NSScreen screens])
displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale)); displays.add (getDisplayFromScreen (s, mainScreenBottom, desktop.getGlobalScaleFactor()));
} }
} }

View file

@ -6038,7 +6038,7 @@ static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT, LPARAM userInfo
return TRUE; return TRUE;
} }
void Displays::findDisplays (float masterScale) void Displays::findDisplays (const Desktop& desktop)
{ {
setDPIAwareness(); setDPIAwareness();
@ -6058,6 +6058,8 @@ void Displays::findDisplays (float masterScale)
if (monitors.getReference (i).isMain) if (monitors.getReference (i).isMain)
monitors.swap (i, 0); monitors.swap (i, 0);
const auto masterScale = desktop.getGlobalScaleFactor();
for (auto& monitor : monitors) for (auto& monitor : monitors)
{ {
Display d; Display d;