Skip to main content

winit/platform_impl/windows/
dpi.rs

1#![allow(non_snake_case, unused_unsafe)]
2
3use std::sync::Once;
4
5use windows_sys::Win32::Foundation::{HWND, S_OK};
6use windows_sys::Win32::Graphics::Gdi::{
7    GetDC, GetDeviceCaps, MonitorFromWindow, HMONITOR, LOGPIXELSX, MONITOR_DEFAULTTONEAREST,
8};
9use windows_sys::Win32::UI::HiDpi::{
10    DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
11    MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE,
12};
13use windows_sys::Win32::UI::WindowsAndMessaging::IsProcessDPIAware;
14
15use crate::platform_impl::platform::util::{
16    ENABLE_NON_CLIENT_DPI_SCALING, GET_DPI_FOR_MONITOR, GET_DPI_FOR_WINDOW, SET_PROCESS_DPI_AWARE,
17    SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT,
18};
19
20pub fn become_dpi_aware() {
21    static ENABLE_DPI_AWARENESS: Once = Once::new();
22    ENABLE_DPI_AWARENESS.call_once(|| {
23        unsafe {
24            if let Some(SetProcessDpiAwarenessContext) = *SET_PROCESS_DPI_AWARENESS_CONTEXT {
25                // We are on Windows 10 Anniversary Update (1607) or later.
26                if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
27                    == false.into()
28                {
29                    // V2 only works with Windows 10 Creators Update (1703). Try using the older
30                    // V1 if we can't set V2.
31                    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
32                }
33            } else if let Some(SetProcessDpiAwareness) = *SET_PROCESS_DPI_AWARENESS {
34                // We are on Windows 8.1 or later.
35                SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
36            } else if let Some(SetProcessDPIAware) = *SET_PROCESS_DPI_AWARE {
37                // We are on Vista or later.
38                SetProcessDPIAware();
39            }
40        }
41    });
42}
43
44pub fn enable_non_client_dpi_scaling(hwnd: HWND) {
45    unsafe {
46        if let Some(EnableNonClientDpiScaling) = *ENABLE_NON_CLIENT_DPI_SCALING {
47            EnableNonClientDpiScaling(hwnd);
48        }
49    }
50}
51
52pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
53    unsafe {
54        if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
55            // We are on Windows 8.1 or later.
56            let mut dpi_x = 0;
57            let mut dpi_y = 0;
58            if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
59                // MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
60                // record one of the values to determine the DPI and respond appropriately".
61                // https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
62                return Some(dpi_x);
63            }
64        }
65    }
66    None
67}
68
69pub const BASE_DPI: u32 = 96;
70pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
71    dpi as f64 / BASE_DPI as f64
72}
73
74pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
75    let hdc = unsafe { GetDC(hwnd) };
76    if hdc == 0 {
77        panic!("[winit] `GetDC` returned null!");
78    }
79    if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
80        // We are on Windows 10 Anniversary Update (1607) or later.
81        match unsafe { GetDpiForWindow(hwnd) } {
82            0 => BASE_DPI, // 0 is returned if hwnd is invalid
83            dpi => dpi,
84        }
85    } else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
86        // We are on Windows 8.1 or later.
87        let monitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };
88        if monitor == 0 {
89            return BASE_DPI;
90        }
91
92        let mut dpi_x = 0;
93        let mut dpi_y = 0;
94        if unsafe { GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) } == S_OK {
95            dpi_x
96        } else {
97            BASE_DPI
98        }
99    } else {
100        // We are on Vista or later.
101        if unsafe { IsProcessDPIAware() } != false.into() {
102            // If the process is DPI aware, then scaling must be handled by the application using
103            // this DPI value.
104            unsafe { GetDeviceCaps(hdc, LOGPIXELSX as i32) as u32 }
105        } else {
106            // If the process is DPI unaware, then scaling is performed by the OS; we thus return
107            // 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
108            // application and the WM.
109            BASE_DPI
110        }
111    }
112}