Automatic DPI for all platforms, per monitor DPI for Windows.
authorWouter <waterflames>
Sat, 11 Mar 2017 02:27:08 +0000 (03:27 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 25 Mar 2017 10:22:16 +0000 (11:22 +0100)
For Windows 8.1 and X11 (Linux, BSD) now use the DPI specified by the operating
system, which previously only worked on macOS. For Windows this is handled per
monitor, for X11 this is based on Xft.dpi or xrandr --dpi. This should result
in appropriate font and button sizes by default in most cases.

The UI has been simplified to a single UI Scale factor relative to the automatic
DPI, instead of two DPI and Virtual Pixel Size settings. There is forward and
backwards compatibility for existing user preferences.

Reviewed By: brecht, LazyDodo

Differential Revision: https://developer.blender.org/D2539

18 files changed:
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_IWindow.h
intern/ghost/GHOST_Types.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_Window.h
intern/ghost/intern/GHOST_WindowSDL.cpp
intern/ghost/intern/GHOST_WindowSDL.h
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.h
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/intern/GHOST_WindowX11.h
release/scripts/startup/bl_ui/space_userpref.py
source/blender/blenloader/intern/versioning_defaults.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm_window.h

index ff1922af4f3b2208197b877c92fb3350a66d8644..6887063eae9697dd59ef3b6df76d7b254b444a97 100644 (file)
@@ -907,6 +907,11 @@ extern int GHOST_UseNativePixels(void);
  */
 extern float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle);
 
+/**
+ * Returns the suggested DPI for this window.
+ */
+extern GHOST_TUns16 GHOST_GetDPIHint(GHOST_WindowHandle windowhandle);
+
 /**
  * Enable IME attached to the given window, i.e. allows user-input
  * events to be dispatched to the IME.
index 688ebecf55731b942ecb5b852c143ad2063a1cc8..4a4d6be5ceded8427f6c2a5c4161027a576ee6ca 100644 (file)
@@ -332,6 +332,12 @@ public:
 
        virtual float getNativePixelSize(void) = 0;
 
+       /**
+        * Returns the recommended DPI for this window.
+        * \return The recommended DPI for this window.
+        */
+       virtual GHOST_TUns16 getDPIHint() = 0;
+
 #ifdef WITH_INPUT_IME
        /**
         * Enable IME attached to the given window, i.e. allows user-input
index 9ee4599a4a6dc33b4d6277dedf4841650a1bd40f..02b5063e515072dfc47857ca0fe59a4c937c0676 100644 (file)
@@ -189,6 +189,7 @@ typedef enum {
        GHOST_kEventWindowUpdate,
        GHOST_kEventWindowSize,
        GHOST_kEventWindowMove,
+       GHOST_kEventWindowDPIHintChanged,
        
        GHOST_kEventDraggingEntered,
        GHOST_kEventDraggingUpdated,
index 41bc735e1e23785bdb903350450cced963f9ca6c..ce653188760cbfdc104c05a6eaa41ada93ab5203 100644 (file)
@@ -914,6 +914,12 @@ float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
        return 1.0f;
 }
 
+GHOST_TUns16 GHOST_GetDPIHint(GHOST_WindowHandle windowhandle)
+{
+       GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+       return window->getDPIHint();
+}
+
 #ifdef WITH_INPUT_IME
 
 void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
index 7d55a973f9151c6294774692e2a26e0c8a24e364..240d7ccd2fe63187b280e44647687eae72d97611 100644 (file)
 #define VK_MEDIA_PLAY_PAUSE 0xB3
 #endif // VK_MEDIA_PLAY_PAUSE
 
+// Window message newer than Windows 7
+#ifndef WM_DPICHANGED
+#define WM_DPICHANGED 0x02E0
+#endif // WM_DPICHANGED
+
 /* Workaround for some laptop touchpads, some of which seems to
  * have driver issues which makes it so window function receives
  * the message, but PeekMessage doesn't pick those messages for
@@ -152,6 +157,27 @@ static void initRawInput()
 #undef DEVICE_COUNT
 }
 
+#ifndef DPI_ENUMS_DECLARED
+typedef enum PROCESS_DPI_AWARENESS {
+       PROCESS_DPI_UNAWARE = 0,
+       PROCESS_SYSTEM_DPI_AWARE = 1,
+       PROCESS_PER_MONITOR_DPI_AWARE = 2
+} PROCESS_DPI_AWARENESS;
+
+typedef enum MONITOR_DPI_TYPE {
+       MDT_EFFECTIVE_DPI = 0,
+       MDT_ANGULAR_DPI = 1,
+       MDT_RAW_DPI = 2,
+       MDT_DEFAULT = MDT_EFFECTIVE_DPI
+} MONITOR_DPI_TYPE;
+
+#define USER_DEFAULT_SCREEN_DPI 96
+
+#define DPI_ENUMS_DECLARED
+#endif
+typedef HRESULT(API * GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
+typedef BOOL(API * GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
+
 GHOST_SystemWin32::GHOST_SystemWin32()
        : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
 {
@@ -161,6 +187,18 @@ GHOST_SystemWin32::GHOST_SystemWin32()
 
        m_consoleStatus = 1;
 
+       // Tell Windows we are per monitor DPI aware. This disables the default
+       // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
+       HMODULE m_shcore = ::LoadLibrary("Shcore.dll");
+       if (m_shcore) {
+               GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness =
+                       (GHOST_WIN32_SetProcessDpiAwareness) ::GetProcAddress(m_shcore, "SetProcessDpiAwareness");
+
+               if (fpSetProcessDpiAwareness) {
+                       fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
+               }
+       }
+
        // Check if current keyboard layout uses AltGr and save keylayout ID for
        // specialized handling if keys like VK_OEM_*. I.e. french keylayout
        // generates VK_OEM_8 for their exclamation key (key left of right shift)
@@ -922,6 +960,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
        GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
 
        if (hwnd) {
+               if(msg == WM_NCCREATE) {
+                       // Tell Windows to automatically handle scaling of non-client areas
+                       // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
+                       HMODULE m_user32 = ::LoadLibrary("User32.dll");
+                       if (m_user32) {
+                               GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
+                                       (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling");
+
+                               if (fpEnableNonClientDpiScaling) {
+                                       fpEnableNonClientDpiScaling(hwnd);
+                               }
+                       }
+               }
+
                GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
                if (window) {
                        switch (msg) {
@@ -1293,6 +1345,32 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                                event = processWindowEvent(GHOST_kEventWindowMove, window);
                                        }
 
+                                       break;
+                               case WM_DPICHANGED:
+                                       /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed.
+                                       * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to
+                                       * change such as when the window is moved to a monitor with a different DPI.
+                                       */
+                                       {
+                                               WORD newYAxisDPI = HIWORD(wParam);
+                                               WORD newXAxisDPI = LOWORD(wParam);
+                                               // The suggested new size and position of the window.
+                                               RECT* const suggestedWindowRect = (RECT*)lParam;
+
+                                               // Push DPI change event first
+                                               system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
+                                               system->dispatchEvents();
+                                               eventHandled = true;
+
+                                               // Then move and resize window
+                                               SetWindowPos(hwnd,
+                                                       NULL,
+                                                       suggestedWindowRect->left,
+                                                       suggestedWindowRect->top,
+                                                       suggestedWindowRect->right - suggestedWindowRect->left,
+                                                       suggestedWindowRect->bottom - suggestedWindowRect->top,
+                                                       SWP_NOZORDER | SWP_NOACTIVATE);
+                                       }
                                        break;
                                ////////////////////////////////////////////////////////////////////////
                                // Window events, ignored
index d778628ea37cd08366d9850f01934d108054070b..2798bdf72f3cda3231bf6e948166e661863a7d9a 100644 (file)
@@ -295,6 +295,15 @@ public:
                return 1.0f;
        }
 
+       /**
+       * Returns the recommended DPI for this window.
+       * \return The recommended DPI for this window.
+       */
+       virtual inline GHOST_TUns16 getDPIHint()
+       {
+               return 96;
+       }
+
 #ifdef WITH_INPUT_IME
        virtual void beginIME(GHOST_TInt32 x,
                              GHOST_TInt32 y,
index 1335c38d9770e20ae230ffdbf9ad759faad454e0..aeb6188daef24511f0db18da5c5a28a6c2522fe4 100644 (file)
@@ -563,3 +563,19 @@ GHOST_WindowSDL::setWindowCursorVisibility(bool visible)
        SDL_ShowCursor(visible);
        return GHOST_kSuccess;
 }
+
+GHOST_TUns16
+GHOST_WindowSDL::getDPIHint()
+{
+       int displayIndex = SDL_GetWindowDisplayIndex(m_sdl_win);
+       if (displayIndex < 0) {
+               return 96;
+       }
+
+       float ddpi;
+       if (SDL_GetDisplayDPI(displayIndex, &ddpi, NULL, NULL) != 0) {
+               return 96;
+       }
+
+       return (int)ddpi;
+}
index 5f658e8ad01375e8498ff0557bf802f1be424d4b..96104ec28b462226da09313a960b59be85a559aa 100644 (file)
@@ -168,6 +168,8 @@ protected:
        GHOST_TSuccess beginFullScreen() const { return GHOST_kFailure; }
 
        GHOST_TSuccess endFullScreen() const { return GHOST_kFailure; }
+
+       GHOST_TUns16 getDPIHint();
 };
 
 
index 7d80aa43a401fdf1038940a3b8d6a116c4280126..fc46164c1357a071dd6c79bf7f3846a84b6cee68 100644 (file)
@@ -92,6 +92,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
       m_tablet(0),
       m_maxPressure(0),
       m_normal_state(GHOST_kWindowStateNormal),
+         m_user32(NULL),
       m_parentWindowHwnd(parentwindowhwnd),
       m_debug_context(is_debug)
 {
@@ -965,6 +966,23 @@ void GHOST_WindowWin32::bringTabletContextToFront()
        }
 }
 
+GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
+{
+       if (!m_user32) {
+               m_user32 = ::LoadLibrary("user32.dll");
+       }
+
+       if (m_user32) {
+               GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow");
+
+               if (fpGetDpiForWindow) {
+                       return fpGetDpiForWindow(this->m_hWnd);
+               }
+       }
+
+       return USER_DEFAULT_SCREEN_DPI;
+}
+
 /** Reverse the bits in a GHOST_TUns8 */
 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
 {
index a1cf58c9cebf66357556b2e7b12b039185124af7..75a33951ff475eba17c1efcd9137b4715de55938 100644 (file)
@@ -58,6 +58,12 @@ typedef BOOL (API * GHOST_WIN32_WTClose)(HCTX);
 typedef BOOL (API * GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID);
 typedef BOOL (API * GHOST_WIN32_WTOverlap)(HCTX, BOOL);
 
+// typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions
+typedef UINT(API * GHOST_WIN32_GetDpiForWindow)(HWND);
+#ifndef USER_DEFAULT_SCREEN_DPI
+#define USER_DEFAULT_SCREEN_DPI 96
+#endif // USER_DEFAULT_SCREEN_DPI
+
 /**
  * GHOST window on M$ Windows OSs.
  * \author     Maarten Gribnau
@@ -251,6 +257,8 @@ public:
 
        GHOST_TSuccess endFullScreen() const {return GHOST_kFailure;}
 
+       GHOST_TUns16 getDPIHint() override;
+
        /** if the window currently resizing */
        bool m_inLiveResize;
 
@@ -351,6 +359,9 @@ private:
 
        GHOST_TWindowState m_normal_state;
 
+       /** user32 dll handle*/
+       HMODULE m_user32;
+
        /** Hwnd to parent window */
        GHOST_TEmbedderWindowID m_parentWindowHwnd;
 
index 47fbe1256b1d363a3e3c1ae07cd54476ccaeb936..429c9af0e3ef7fafd8a058f40f57049117c7933a 100644 (file)
@@ -56,6 +56,9 @@
 #  include <X11/extensions/XInput2.h>
 #endif
 
+//For DPI value
+#include <X11/Xresource.h>
+
 #if defined(__sun__) || defined(__sun) || defined(__sparc) || defined(__sparc__) || defined(_AIX)
 #  include <strings.h>
 #endif
@@ -68,6 +71,7 @@
 
 #include <algorithm>
 #include <string>
+#include <math.h>
 
 /* For obscure full screen mode stuff
  * lifted verbatim from blut. */
@@ -1672,3 +1676,42 @@ endFullScreen() const
 
        return GHOST_kSuccess;
 }
+
+GHOST_TUns16
+GHOST_WindowX11::
+getDPIHint()
+{
+       /* Try to read DPI setting set using xrdb */
+       char* resMan = XResourceManagerString(m_display);
+       XrmDatabase xrdb = XrmGetStringDatabase(resMan);
+       if (xrdb) {
+               char* type = NULL;
+               XrmValue val;
+
+               int success = XrmGetResource(xrdb, "Xft.dpi", "Xft.Dpi", &type, &val);
+               if (success && type) {
+                       if (strcmp(type, "String") == 0) {
+                               return atoi((char*)val.addr);
+                       }
+               }
+       }
+
+       /* Fallback to calculating DPI using X reported DPI, set using xrandr --dpi */
+       XWindowAttributes attr;
+       if (!XGetWindowAttributes(m_display, m_window, &attr)) {
+               /* Failed to get window attributes, return X11 default DPI */
+               return 96;
+       }
+
+       Screen* screen = attr.screen;
+       int pixelWidth = WidthOfScreen(screen);
+       int pixelHeight = HeightOfScreen(screen);
+       int mmWidth = WidthMMOfScreen(screen);
+       int mmHeight = HeightMMOfScreen(screen);
+
+       double pixelDiagonal = sqrt((pixelWidth * pixelWidth) + (pixelHeight * pixelHeight));
+       double mmDiagonal = sqrt((mmWidth * mmWidth) + (mmHeight * mmHeight));
+       float inchDiagonal = mmDiagonal * 0.039f;
+       int dpi = pixelDiagonal / inchDiagonal;
+       return dpi;
+}
index 9380aa9d6310de594b80e481bc469ab1e7774511..5c54c1e8162bb3746ca20f12a90234db2c43c6a3 100644 (file)
@@ -235,6 +235,8 @@ public:
 
        GHOST_TSuccess endFullScreen() const;
 
+       GHOST_TUns16 getDPIHint();
+
 protected:
        /**
         * \param type  The type of rendering context create.
index f4e2cf006b207907edc2211ebe25733553c6cb2c..e50beba50d8ad8da03ba628d07191bcfd0141e23 100644 (file)
@@ -217,6 +217,7 @@ class USERPREF_PT_interface(Panel):
 
         col = row.column()
         col.label(text="Display:")
+        col.prop(view, "ui_scale", text="Scale")
         col.prop(view, "show_tooltips")
         col.prop(view, "show_tooltips_python")
         col.prop(view, "show_object_info", text="Object Info")
@@ -467,11 +468,6 @@ class USERPREF_PT_system(Panel):
 
         col = colsplit.column()
         col.label(text="General:")
-        col.prop(system, "dpi")
-        col.label("Virtual Pixel Mode:")
-        col.prop(system, "virtual_pixel_mode", text="")
-
-        col.separator()
 
         col.prop(system, "frame_server_port")
         col.prop(system, "scrollback", text="Console Scrollback")
index 99d9e140481f2a101ad051748cd16ed588d644fd..6145b90b406923c7e90bbc14e67638e4cceb0bcf 100644 (file)
@@ -68,6 +68,10 @@ void BLO_update_defaults_userpref_blend(void)
         * but take care since some hardware has driver bugs here (T46962).
         * Further hardware workarounds should be made in gpu_extensions.c */
        U.glalphaclip = (1.0f / 255);
+
+       /* default so DPI is detected automatically */
+       U.dpi = 0;
+       U.ui_scale = 1.0f;
 }
 
 /**
index d76452edb83183c10b00acce4dd1f480d4c7e6d8..5e7e7366e351936387d39ffab0fc5911a294b1fe 100644 (file)
@@ -467,9 +467,11 @@ typedef struct UserDef {
        int audioformat;
        int audiochannels;
 
-       int scrollback; /* console scrollback limit */
-       int dpi;                /* range 48-128? */
-       char node_margin; /* node insert offset (aka auto-offset) margin, but might be useful for later stuff as well */
+       int scrollback;     /* console scrollback limit */
+       int dpi;            /* range 48-128? */
+       float ui_scale;     /* interface scale */
+       int pad1;
+       char node_margin;   /* node insert offset (aka auto-offset) margin, but might be useful for later stuff as well */
        char pad2;
        short transopts;
        short menuthreshold1, menuthreshold2;
index 7b6eb5fef475e6abbad1ea625a91e861388aafa4..4d70b87843a58180f6f0f06c5b40df9830a5ec5d 100644 (file)
@@ -138,23 +138,11 @@ static void rna_userdef_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe
 }
 
 /* also used by buffer swap switching */
-static void rna_userdef_dpi_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+static void rna_userdef_dpi_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
 {
        /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
        BLF_cache_clear();
 
-       BKE_blender_userdef_refresh();
-       WM_main_add_notifier(NC_WINDOW, NULL);      /* full redraw */
-       WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);    /* refresh region sizes */
-}
-
-static void rna_userdef_virtual_pixel_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
-{
-       /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
-       BLF_cache_clear();
-       
-       BKE_blender_userdef_refresh();
-
        /* force setting drawable again */
        wmWindowManager *wm = bmain->wm.first;
        if (wm) {
@@ -3325,6 +3313,12 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_struct_ui_text(srna, "View & Controls", "Preferences related to viewing data");
 
        /* View  */
+       prop = RNA_def_property(srna, "ui_scale", PROP_FLOAT, PROP_FACTOR);
+       RNA_def_property_ui_text(prop, "UI Scale", "Changes the size of the fonts and buttons in the interface");
+       RNA_def_property_range(prop, 0.25f, 4.0f);
+       RNA_def_property_ui_range(prop, 0.5f, 2.0f, 1, 1);
+       RNA_def_property_float_default(prop, 1.0f);
+       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
 
        /* display */
        prop = RNA_def_property(srna, "show_tooltips", PROP_BOOLEAN, PROP_NONE);
@@ -3916,12 +3910,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
            {0, NULL, 0, NULL, NULL}
        };
 
-       static EnumPropertyItem virtual_pixel_mode_items[] = {
-               {VIRTUAL_PIXEL_NATIVE, "NATIVE", 0, "Native", "Use native pixel size of the display"},
-               {VIRTUAL_PIXEL_DOUBLE, "DOUBLE", 0, "Double", "Use double the native pixel size of the display"},
-               {0, NULL, 0, NULL, NULL}
-       };
-
        srna = RNA_def_struct(brna, "UserPreferencesSystem", NULL);
        RNA_def_struct_sdna(srna, "UserDef");
        RNA_def_struct_nested(brna, srna, "UserPreferences");
@@ -3936,16 +3924,8 @@ static void rna_def_userdef_system(BlenderRNA *brna)
        RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
 
        prop = RNA_def_property(srna, "dpi", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "dpi");
-       RNA_def_property_range(prop, 48, 144);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_ui_text(prop, "DPI", "Font size and resolution for display");
-       RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
-       prop = RNA_def_property(srna, "virtual_pixel_mode", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_sdna(prop, NULL, "virtual_pixel");
-       RNA_def_property_enum_items(prop, virtual_pixel_mode_items);
-       RNA_def_property_ui_text(prop, "Virtual Pixel Mode", "Modify the pixel size for hi-res devices");
-       RNA_def_property_update(prop, 0, "rna_userdef_virtual_pixel_update");
 
        prop = RNA_def_property(srna, "pixel_size", PROP_FLOAT, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
index 2d43c47679dad310598a7d36dc160b4bc363ee3c..40baab8809c366bc5f5e7e0064411608887f0e96 100644 (file)
@@ -77,6 +77,7 @@
 #include "GPU_extensions.h"
 #include "GPU_init_exit.h"
 #include "GPU_glew.h"
+#include "BLF_api.h"
 
 /* for assert */
 #ifndef NDEBUG
@@ -374,14 +375,39 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win)
        }
 }
 
-static float wm_window_get_virtual_pixelsize(void)
+static void wm_window_set_dpi(wmWindow *win)
 {
-       return ((U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1.0f : 2.0f);
-}
+       int auto_dpi = GHOST_GetDPIHint(win->ghostwin);
 
-float wm_window_pixelsize(wmWindow *win)
-{
-       return (GHOST_GetNativePixelSize(win->ghostwin) * wm_window_get_virtual_pixelsize());
+       /* Lazily init UI scale size, preserving backwards compatibility by
+        * computing UI scale from ratio of previous DPI and auto DPI */
+       if (U.ui_scale == 0) {
+               int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2;
+
+               if (U.dpi == 0) {
+                       U.ui_scale = virtual_pixel;
+               }
+               else {
+                       U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f);
+               }
+
+               CLAMP(U.ui_scale, 0.25f, 4.0f);
+       }
+
+       /* Blender's UI drawing assumes DPI 72 as a good default following macOS
+        * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we
+        * remap the DPI to Blender's convention. */
+       int dpi = auto_dpi * U.ui_scale * (72.0/96.0f);
+
+       /* Automatically set larger pixel size for high DPI. */
+       int pixelsize = MAX2(1, dpi / 54);
+
+       /* Set user preferences globals for drawing, and for forward compatibility. */
+       U.pixelsize = GHOST_GetNativePixelSize(win->ghostwin) * pixelsize;
+       U.dpi = dpi / pixelsize;
+       U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
+
+       BKE_blender_userdef_refresh();
 }
 
 /* belongs to below */
@@ -456,10 +482,8 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
                        glClear(GL_COLOR_BUFFER_BIT);
                }
                
-               /* displays with larger native pixels, like Macbook. Used to scale dpi with */
                /* needed here, because it's used before it reads userdef */
-               U.pixelsize = wm_window_pixelsize(win);
-               BKE_blender_userdef_refresh();
+               wm_window_set_dpi(win);
                
                wm_window_swap_buffers(win);
                
@@ -626,7 +650,6 @@ wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type)
        Scene *scene = CTX_data_scene(C);
        const char *title;
        rcti rect = *rect_init;
-       const short px_virtual = (short)wm_window_get_virtual_pixelsize();
 
        /* changes rect to fit within desktop */
        wm_window_check_position(&rect);
@@ -644,9 +667,8 @@ wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type)
                win->posy = rect.ymin;
        }
 
-       /* multiply with virtual pixelsize, ghost handles native one (e.g. for retina) */
-       win->sizex = BLI_rcti_size_x(&rect) * px_virtual;
-       win->sizey = BLI_rcti_size_y(&rect) * px_virtual;
+       win->sizex = BLI_rcti_size_x(&rect);
+       win->sizey = BLI_rcti_size_y(&rect);
 
        if (win->ghostwin) {
                wm_window_set_size(win, win->sizex, win->sizey);
@@ -835,8 +857,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
                GHOST_ActivateWindowDrawingContext(win->ghostwin);
                
                /* this can change per window */
-               U.pixelsize = wm_window_pixelsize(win);
-               BKE_blender_userdef_refresh();
+               wm_window_set_dpi(win);
        }
 }
 
@@ -1035,6 +1056,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                                if (type == GHOST_kEventWindowSize) {
                                        WM_jobs_stop(wm, win->screen, NULL);
                                }
+
+                               wm_window_set_dpi(win);
                                
                                /* win32: gives undefined window size when minimized */
                                if (state != GHOST_kWindowStateMinimized) {
@@ -1118,7 +1141,19 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                                }
                                break;
                        }
-                               
+
+                       case GHOST_kEventWindowDPIHintChanged:
+                       {
+                               wm_window_set_dpi(win);
+                               /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
+                               BLF_cache_clear();
+
+                               BKE_blender_userdef_refresh();
+                               WM_main_add_notifier(NC_WINDOW, NULL);      /* full redraw */
+                               WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);    /* refresh region sizes */
+                               break;
+                       }
+
                        case GHOST_kEventOpenMainFile:
                        {
                                PointerRNA props_ptr;
@@ -1199,11 +1234,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                        {
                                // only update if the actual pixel size changes
                                float prev_pixelsize = U.pixelsize;
-                               U.pixelsize = wm_window_pixelsize(win);
+                               wm_window_set_dpi(win);
 
                                if (U.pixelsize != prev_pixelsize) {
-                                       BKE_blender_userdef_refresh();
-
                                        // close all popups since they are positioned with the pixel
                                        // size baked in and it's difficult to correct them
                                        wmWindow *oldWindow = CTX_wm_window(C);
index c106f9d785172db8398c56ebedcd15083c4cbf20..f70ec6b47f65fda4a0884088b0fe107b45e1dff1 100644 (file)
@@ -63,8 +63,6 @@ void          wm_window_swap_buffers  (wmWindow *win);
 void           wm_window_set_swap_interval(wmWindow *win, int interval);
 bool           wm_window_get_swap_interval(wmWindow *win, int *intervalOut);
 
-float          wm_window_pixelsize(wmWindow *win);
-
 void           wm_get_cursor_position                  (wmWindow *win, int *x, int *y);
 void           wm_cursor_position_from_ghost   (wmWindow *win, int *x, int *y);
 void           wm_cursor_position_to_ghost             (wmWindow *win, int *x, int *y);