Bug 1254026 - When Win10 dpi-awareness-context APIs are available, run the Open File and Save As dialogs as system-dpi-aware windows, so that they are auto-scaled by the system appropriately for any secondary display's resolution. r=emk a=gchang
authorJonathan Kew <jkew@mozilla.com>
Fri, 01 Jul 2016 23:42:32 +0100
changeset 340036 59e0323f3db6d891daa2389dd8716d9bfb0c83a0
parent 340035 ebd239b4cbfb8eff833560f72b163275ac4026d3
child 340037 70ca7c42db312a2a07aad0560aff0bd1fc48de02
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk, gchang
bugs1254026
milestone49.0a2
Bug 1254026 - When Win10 dpi-awareness-context APIs are available, run the Open File and Save As dialogs as system-dpi-aware windows, so that they are auto-scaled by the system appropriately for any secondary display's resolution. r=emk a=gchang
widget/windows/WinUtils.cpp
widget/windows/WinUtils.h
widget/windows/nsFilePicker.cpp
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -444,16 +444,18 @@ const size_t kNTPrefixLen = ArrayLength(
 
 struct CoTaskMemFreePolicy
 {
   void operator()(void* aPtr) {
     ::CoTaskMemFree(aPtr);
   }
 };
 
+SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
+EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
 
 /* static */
 void
 WinUtils::Initialize()
 {
   if (!gWindowsLog) {
     gWindowsLog = PR_NewLogModule("Widget");
   }
@@ -468,16 +470,26 @@ WinUtils::Initialize()
       dwmGetWindowAttributePtr = (DwmGetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmGetWindowAttribute");
       dwmSetWindowAttributePtr = (DwmSetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmSetWindowAttribute");
       dwmInvalidateIconicBitmapsPtr = (DwmInvalidateIconicBitmapsProc)::GetProcAddress(sDwmDll, "DwmInvalidateIconicBitmaps");
       dwmDwmDefWindowProcPtr = (DwmDefWindowProcProc)::GetProcAddress(sDwmDll, "DwmDefWindowProc");
       dwmGetCompositionTimingInfoPtr = (DwmGetCompositionTimingInfoProc)::GetProcAddress(sDwmDll, "DwmGetCompositionTimingInfo");
       dwmFlushProcPtr = (DwmFlushProc)::GetProcAddress(sDwmDll, "DwmFlush");
     }
   }
+
+  if (IsWin10OrLater()) {
+    HMODULE user32Dll = ::GetModuleHandleW(L"user32");
+    if (user32Dll) {
+      sEnableNonClientDpiScaling = (EnableNonClientDpiScalingProc)
+        ::GetProcAddress(user32Dll, "EnableNonClientDpiScaling");
+      sSetThreadDpiAwarenessContext = (SetThreadDpiAwarenessContextProc)
+        ::GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext");
+    }
+  }
 }
 
 // static
 void
 WinUtils::LogW(const wchar_t *fmt, ...)
 {
   va_list args = nullptr;
   if(!lstrlenW(fmt)) {
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -73,16 +73,37 @@ protected:                              
   nsAutoRefCnt mRefCnt;                                                       \
   NS_DECL_OWNINGTHREAD                                                        \
 public:
 
 class nsWindow;
 class nsWindowBase;
 struct KeyPair;
 
+#ifndef DPI_AWARENESS_CONTEXT_DECLARED
+
+DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
+
+typedef enum DPI_AWARENESS {
+  DPI_AWARENESS_INVALID = -1,
+  DPI_AWARENESS_UNAWARE = 0,
+  DPI_AWARENESS_SYSTEM_AWARE = 1,
+  DPI_AWARENESS_PER_MONITOR_AWARE = 2
+} DPI_AWARENESS;
+
+#define DPI_AWARENESS_CONTEXT_UNAWARE           ((DPI_AWARENESS_CONTEXT)-1)
+#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE      ((DPI_AWARENESS_CONTEXT)-2)
+#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)
+
+#define DPI_AWARENESS_CONTEXT_DECLARED
+#endif // (DPI_AWARENESS_CONTEXT_DECLARED)
+
+typedef DPI_AWARENESS_CONTEXT(WINAPI * SetThreadDpiAwarenessContextProc)(DPI_AWARENESS_CONTEXT);
+typedef BOOL(WINAPI * EnableNonClientDpiScalingProc)(HWND);
+
 namespace mozilla {
 namespace widget {
 
 // Windows message debugging data
 typedef struct {
   const char * mStr;
   UINT         mId;
 } EventMsgInfo;
@@ -121,17 +142,43 @@ class myDownloadObserver final : public 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOWNLOADOBSERVER
 };
 #endif
 
 class WinUtils
 {
+  // Function pointers for APIs that may not be available depending on
+  // the Win10 update version -- will be set up in Initialize().
+  static SetThreadDpiAwarenessContextProc sSetThreadDpiAwarenessContext;
+  static EnableNonClientDpiScalingProc sEnableNonClientDpiScaling;
+
 public:
+  class AutoSystemDpiAware
+  {
+  public:
+    AutoSystemDpiAware()
+    {
+      if (sSetThreadDpiAwarenessContext) {
+        mPrevContext = sSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
+      }
+    }
+
+    ~AutoSystemDpiAware()
+    {
+      if (sSetThreadDpiAwarenessContext) {
+        sSetThreadDpiAwarenessContext(mPrevContext);
+      }
+    }
+
+  private:
+    DPI_AWARENESS_CONTEXT mPrevContext;
+  };
+
   /**
    * Get the system's default logical-to-physical DPI scaling factor,
    * which is based on the primary display. Note however that unlike
    * LogToPhysFactor(GetPrimaryMonitor()), this will not change during
    * a session even if the displays are reconfigured. This scale factor
    * is used by Windows theme metrics etc, which do not fully support
    * dynamic resolution changes but are only updated on logout.
    */
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -1044,16 +1044,20 @@ nsFilePicker::ShowW(int16_t *aReturnVal)
     // Allocate copy of last used dir.
     initialDir = mLastUsedUnicodeDirectory;
   }
 
   // Clear previous file selections
   mUnicodeFile.Truncate();
   mFiles.Clear();
 
+  // On Win10, the picker doesn't support per-monitor DPI, so we open it
+  // with our context set temporarily to system-dpi-aware
+  WinUtils::AutoSystemDpiAware dpiAwareness;
+
   // Launch the XP file/folder picker on XP and as a fallback on Vista+. 
   // The CoCreateInstance call to CLSID_FileOpenDialog fails with "(0x80040111)
   // ClassFactory cannot supply requested class" when the checkbox for
   // Disable Visual Themes is on in the compatability tab within the shortcut
   // properties.
   bool result = false, wasInitError = true;
   if (mMode == modeGetFolder) {
     if (IsVistaOrLater())