Hi,
I’ve ran into issues with Juce wrt it not picking up the DPI awareness of a DPI aware host application (Premiere Pro) on Windows. The result was teeny unscaled dialogs. On Mac the Juce framework correctly scales under the same host application. Moreover, standalone Juce apps work fine on Windows as well.
I’ve delved a bit in the Juce code, and in the WIndows version of Desktop::getDefaultMasterScale() the DPI scaling is only looked at when we’re a JUCEApplicationBase::isStandaloneApp, which I guess (my) plugins aren’t. Just removing that test and always doing the DPI calculation fixes my plugins. And, yes, I know, that’s not the correct solution to the problem
I’ve modified my copy of Juce 5.3.2 to fix this more elegantly. When running as a standalone Juce app, the code invokes the DPI awareness setting functions under Windows as it did before. However, if we’re not a standalone app, we can request the current state of our host app’s DPI awareness via similar functions. Using these I added code to the else branch of the JUCEApplicationBase::isStandaloneApp logic, to look if we should scale or not. And in my rudimentary test cases it works out nicely; my windows are now always DPI scaled correctly, no matter if I’m under Premiere or in a standalone app.
I basically:
- added DLL imports for the IsProcessDPIAware and GetProcessDPIAwareness functions,
- modfified setDPIAwareness() into initializeDPIAwareness(), which now not only sets it for standalone apps but also checks it for the system-DPI aware setting in plugins, and caches the result,
- added isDPIAware() which checks the cached state above, and if it’s per-monitor aware it re-evaluates the state on each call (per-monitor can only be derived at when the host used SetProcessDPIAwareness(), and in that case I think the host is free to change it later on? def. don’t know for sure though)
I also spotted the check on getDPIForMonitor() being available in setDPIAwareness(), but not being used in that function. Probably because it is used in enumMonitorsProc() which depends on setDPIAwareness() to load the API function. There’s however room for improvement there too I think. As well as for per-monitor DPI awareness; I already saw commented-out code to set it in setDPIAwareness(), but it needs extra handling by catching the “DPI changed” messages when users drag the window onto other monitors etc. I already added commented-out accompanying code for the cached state though.
Also note that I mentioned “rudimentary tests”. I’m sure I’ve failed to think of lots of edge cases here, and I only tested on Windows 10 under Premiere. So extra testing is definitely needed.
I hope anyone finds it of use, and I hope this could get the Juce team onto the right track here as well. Any intermediate feedback is also very welcome; I intent to use my own patch in my own product, so if anyone spots opportunities for improvement that would be great!
Anyway, as for the patch (again, for Juce 5.3.2); all changes are made in modules\juce_gui_basics\native\juce_win32_Windowing.cpp
=================================
--- juce_win32_Windowing.cpp Thu Nov 01 13:33:07 2018
+++ juce_win32_Windowing.cpp Tue Jun 04 17:25:21 2019
@@ -253,7 +253,9 @@
typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, TOUCHINPUT*, int);
typedef BOOL (WINAPI* CloseTouchInputHandleFunc) (HTOUCHINPUT);
typedef BOOL (WINAPI* GetGestureInfoFunc) (HGESTUREINFO, GESTUREINFO*);
+typedef BOOL (WINAPI* IsProcessDPIAwareFunc)();
typedef BOOL (WINAPI* SetProcessDPIAwareFunc)();
+typedef BOOL (WINAPI* GetProcessDPIAwarenessFunc) (HANDLE, Process_DPI_Awareness*);
typedef BOOL (WINAPI* SetProcessDPIAwarenessFunc) (Process_DPI_Awareness);
typedef HRESULT (WINAPI* GetDPIForMonitorFunc) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*);
@@ -261,7 +263,9 @@
static GetTouchInputInfoFunc getTouchInputInfo = nullptr;
static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr;
static GetGestureInfoFunc getGestureInfo = nullptr;
+static IsProcessDPIAwareFunc isProcessDPIAware = nullptr;
static SetProcessDPIAwareFunc setProcessDPIAware = nullptr;
+static GetProcessDPIAwarenessFunc getProcessDPIAwareness = nullptr;
static SetProcessDPIAwarenessFunc setProcessDPIAwareness = nullptr;
static GetDPIForMonitorFunc getDPIForMonitor = nullptr;
@@ -329,41 +333,88 @@
}
//==============================================================================
+enum DPI_Awareness_State
+{
+ DPI_Awareness_State_Unset,
+ DPI_Awareness_State_Unaware,
+ DPI_Awareness_State_Aware,
+ DPI_Awareness_State_PerMonitor
+};
+static DPI_Awareness_State dpiAwarenessState = DPI_Awareness_State_Unset;
+static void initializeDPIAwareness()
+{
+ if (dpiAwarenessState == DPI_Awareness_State_Unset) {
+ dpiAwarenessState = DPI_Awareness_State_Unaware;
+ HMODULE shcoreModule = GetModuleHandleA("SHCore.dll");
+ if (shcoreModule != 0)
+ {
+ getDPIForMonitor = (GetDPIForMonitorFunc)GetProcAddress(shcoreModule, "GetDpiForMonitor");
+ setProcessDPIAwareness = (SetProcessDPIAwarenessFunc)GetProcAddress(shcoreModule, "SetProcessDpiAwareness");
+ setProcessDPIAware = (SetProcessDPIAwareFunc)getUser32Function("SetProcessDPIAware");
+ isProcessDPIAware = (IsProcessDPIAwareFunc)getUser32Function("IsProcessDPIAware");
+ getProcessDPIAwareness = (GetProcessDPIAwarenessFunc)GetProcAddress(shcoreModule, "GetProcessDpiAwareness");
+ if (JUCEApplicationBase::isStandaloneApp())
+ {
+ if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr
+// && SUCCEEDED(setProcessDPIAwareness(Process_Per_Monitor_DPI_Aware)))
+ && SUCCEEDED(setProcessDPIAwareness(Process_System_DPI_Aware))) // (keep using this mode temporarily..)
+// dpiAwarenessState = DPI_Awareness_State_PerMonitor;
+ dpiAwarenessState = DPI_Awareness_State_Aware;
+ else if (setProcessDPIAware != nullptr
+ && setProcessDPIAware())
+ dpiAwarenessState = DPI_Awareness_State_Aware;
+ else
+ dpiAwarenessState = DPI_Awareness_State_Unaware;
+ }
+ else
+ {
+ Process_DPI_Awareness processDPIAwareness;
+ if (getProcessDPIAwareness != nullptr
+ && SUCCEEDED(getProcessDPIAwareness(NULL, &processDPIAwareness)))
+ {
+ if (processDPIAwareness == Process_Per_Monitor_DPI_Aware)
+ dpiAwarenessState = DPI_Awareness_State_PerMonitor;
+ else if (processDPIAwareness == Process_System_DPI_Aware)
+ dpiAwarenessState = DPI_Awareness_State_Aware;
+ else
+ dpiAwarenessState = DPI_Awareness_State_Unaware;
+ }
+ else if (isProcessDPIAware != nullptr)
+ if (isProcessDPIAware())
+ dpiAwarenessState = DPI_Awareness_State_Aware;
+ else
+ dpiAwarenessState = DPI_Awareness_State_Unaware;
+ }
+ }
+ }
+}
+
+static bool isDPIAware()
+{
+ bool isAware = false;
+#if ! JUCE_DISABLE_WIN32_DPI_AWARENESS
+ initializeDPIAwareness();
+ if (dpiAwarenessState == DPI_Awareness_State_Aware)
+ isAware = true;
+ else if (dpiAwarenessState == DPI_Awareness_State_PerMonitor)
+ {
+ if (JUCEApplicationBase::isStandaloneApp())
+ isAware = false;
+ else
+ {
+ Process_DPI_Awareness processDPIAwareness;
+ if (getProcessDPIAwareness != nullptr
+ && SUCCEEDED(getProcessDPIAwareness(NULL, &processDPIAwareness)))
+ isAware = processDPIAwareness != Process_DPI_Unaware;
+ }
+ }
+#endif
+ return isAware;
+}
-static void setDPIAwareness()
-{
- #if ! JUCE_DISABLE_WIN32_DPI_AWARENESS
- if (JUCEApplicationBase::isStandaloneApp())
- {
- if (setProcessDPIAwareness == nullptr)
- {
- HMODULE shcoreModule = GetModuleHandleA ("SHCore.dll");
-
- if (shcoreModule != 0)
- {
- setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness");
- getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor");
-
- if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr
-// && SUCCEEDED (setProcessDPIAwareness (Process_Per_Monitor_DPI_Aware)))
- && SUCCEEDED (setProcessDPIAwareness (Process_System_DPI_Aware))) // (keep using this mode temporarily..)
- return;
- }
-
- if (setProcessDPIAware == nullptr)
- {
- setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware");
-
- if (setProcessDPIAware != nullptr)
- setProcessDPIAware();
- }
- }
- }
- #endif
-}
static double getGlobalDPI()
{
- setDPIAwareness();
+ initializeDPIAwareness();
HDC dc = GetDC (0);
const double dpi = (GetDeviceCaps (dc, LOGPIXELSX)
@@ -374,8 +425,8 @@
double Desktop::getDefaultMasterScale()
{
- return JUCEApplicationBase::isStandaloneApp() ? getGlobalDPI() / 96.0
- : 1.0;
+ return isDPIAware() ? getGlobalDPI() / 96.0
+ : 1.0;
}
bool Desktop::canUseSemiTransparentWindows() noexcept { return true; }
@@ -1826,7 +1877,7 @@
if (canUseMultiTouch())
registerTouchWindow (hwnd, 0);
- setDPIAwareness();
+ initializeDPIAwareness();
setMessageFilter();
updateBorderSize();
checkForPointerAPI();
@@ -4034,7 +4085,7 @@
void Desktop::Displays::findDisplays (float masterScale)
{
- setDPIAwareness();
+ initializeDPIAwareness();
Array<MonitorInfo> monitors;
EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors);