makepad_platform/os/windows/
win32_app.rs

1use {
2    std::{
3        ffi::OsStr,
4        os::windows::ffi::OsStrExt,
5        mem,
6        cell::{
7            Cell,
8            RefCell,
9        },
10    },
11    crate::{
12        log,
13        error,
14        windows::{
15            core::HRESULT,
16            core::PCWSTR,
17            core::PCSTR,
18            //core::IntoParam,
19            Win32::{
20                UI::{
21                    WindowsAndMessaging::{
22                        WNDCLASSEXW,
23                        PM_REMOVE,
24                        LoadIconW,
25                        RegisterClassExW,
26                        IsGUIThread,
27                        GetMessageW,
28                        TranslateMessage,
29                        DispatchMessageW,
30                        PeekMessageW,
31                        SetTimer,
32                        KillTimer,
33                        ShowCursor,
34                        SetCursor,
35                        LoadCursorW,
36                        IsProcessDPIAware,
37                        IDC_ARROW,
38                        IDC_CROSS,
39                        IDC_HAND,
40                        IDC_SIZEALL,
41                        IDC_IBEAM,
42                        IDC_HELP,
43                        IDC_NO,
44                        IDC_SIZEWE,
45                        IDC_SIZENS,
46                        IDC_SIZENESW,
47                        IDC_SIZENWSE,
48                        WM_QUIT,
49                        CS_HREDRAW,
50                        CS_VREDRAW,
51                        CS_OWNDC,
52                        IDI_WINLOGO,
53                    },
54                    HiDpi::{
55                        PROCESS_DPI_AWARENESS,
56                        DPI_AWARENESS_CONTEXT,
57                        MONITOR_DPI_TYPE,
58                        DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
59                        DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
60                        PROCESS_PER_MONITOR_DPI_AWARE,
61                        MDT_EFFECTIVE_DPI
62                    },
63                },
64                Graphics::Gdi::{
65                    CreateSolidBrush,
66                    HMONITOR,
67                    GetDC,
68                    MonitorFromWindow,
69                    GetDeviceCaps,
70                    MONITOR_DEFAULTTONEAREST,
71                    LOGPIXELSX
72                },
73                Foundation::{
74                    COLORREF,
75                    S_OK,
76                    HWND,
77                    BOOL,
78                    FARPROC,
79                    DRAGDROP_S_DROP,
80                    DRAGDROP_S_CANCEL,
81                },
82                System::{
83                    Threading::ExitProcess,
84                    LibraryLoader::{
85                        GetModuleHandleW,
86                        LoadLibraryA,
87                        GetProcAddress,
88                    },
89                    Performance::{
90                        QueryPerformanceCounter,
91                        QueryPerformanceFrequency,
92                    },
93                    Com::IDataObject,
94                    Ole::{
95                        OleInitialize,
96                        DoDragDrop,
97                        IDropSource,
98                        DROPEFFECT,
99                        DROPEFFECT_COPY,
100                        DROPEFFECT_MOVE,
101                    },
102                },
103            },
104        },
105        event::*,
106        cursor::MouseCursor,
107        os::{
108            cx_native::EventFlow,
109            windows::{
110                dropsource::*,
111                dataobject::DragItemWindows,
112                win32_event::Win32Event,
113                win32_window::Win32Window,
114            },
115        },
116        window::WindowId,
117    },
118};
119pub const FALSE: BOOL = BOOL(0);
120pub const TRUE: BOOL = BOOL(1);
121
122
123thread_local! {
124    pub static WIN32_APP: RefCell<Option<Win32App>> = RefCell::new(None);
125}
126
127pub fn with_win32_app<R>(f: impl FnOnce(&mut Win32App) -> R) -> R {
128    WIN32_APP.with_borrow_mut(|app| {
129        f(app.as_mut().unwrap())
130    })
131}
132
133pub fn init_win32_app_global(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) {
134    WIN32_APP.with(|app| {
135        *app.borrow_mut() = Some(Win32App::new(event_callback));
136    });
137}
138/*
139// copied from Microsoft so it refers to the right IDataObject
140#[allow(non_snake_case)]
141pub unsafe fn DoDragDrop<P0, P1>(pdataobj: P0, pdropsource: P1, dwokeffects: DROPEFFECT, pdweffect: *mut DROPEFFECT) -> HRESULT
142where
143P0: IntoParam<IDataObject>,
144P1: IntoParam<IDropSource>,
145{
146    ::windows_targets::link!("ole32.dll" "system" fn DoDragDrop(pdataobj: *mut::core::ffi::c_void, pdropsource: *mut::core::ffi::c_void, dwokeffects: DROPEFFECT, pdweffect: *mut DROPEFFECT) -> HRESULT);
147    DoDragDrop(pdataobj.into_param().abi(), pdropsource.into_param().abi(), dwokeffects, pdweffect)
148}*/
149
150pub struct Win32App {
151    event_callback: Option<Box<dyn FnMut(Win32Event) -> EventFlow >>,
152    pub window_class_name: Vec<u16>,
153    pub all_windows: Vec<HWND>,
154    pub time: Win32Time,
155    pub timers: Vec<Win32Timer>,
156    pub was_signal_poll: bool,
157    pub event_flow: EventFlow,
158    pub dpi_functions: DpiFunctions,
159    pub current_cursor: Option<MouseCursor>,
160    pub currently_clicked_window_id: Option<WindowId >,
161    pub start_dragging_items: Option<Vec<DragItem >>,
162    pub is_dragging_internal: Cell<bool>,
163}
164
165#[derive(Clone)]
166pub enum Win32Timer {
167    Free,
168    Timer {win32_id: usize, timer_id: u64, interval: f64, repeats: bool},
169    Resize {win32_id: usize},
170    DragDrop {win32_id: usize},
171    SignalPoll {win32_id: usize},
172}
173
174pub struct Win32Time{
175    pub time_start: i64,
176    pub time_freq: i64,
177}
178
179impl Win32Time{
180    pub fn new()->Self{
181        let mut time_start = 0i64;
182        unsafe {QueryPerformanceCounter(&mut time_start).unwrap()};
183                
184        let mut time_freq = 0i64;
185        unsafe {QueryPerformanceFrequency(&mut time_freq).unwrap()};
186        Self{
187            time_start,
188            time_freq,
189        }
190    }
191    
192    pub fn time_now(&self)->f64{
193        unsafe {
194            let mut time_now = 0i64;
195            QueryPerformanceCounter(&mut time_now).unwrap();
196            (time_now - self.time_start) as f64 / self.time_freq as f64
197        }
198    }
199}
200
201impl Win32App {
202    pub fn new(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) -> Win32App {
203        
204        let window_class_name = encode_wide("MakepadWindow\0");
205        let class = WNDCLASSEXW {
206            cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
207            style: CS_HREDRAW
208                | CS_VREDRAW
209                | CS_OWNDC,
210            lpfnWndProc: Some(Win32Window::window_class_proc),
211            hInstance: unsafe {GetModuleHandleW(None).unwrap().into()},
212            hIcon: unsafe {LoadIconW(None, IDI_WINLOGO).unwrap()}, //h_icon,
213            lpszClassName: PCWSTR(window_class_name.as_ptr()),
214            hbrBackground: unsafe {CreateSolidBrush(COLORREF(0x3f3f3f3f))},
215            ..Default::default()
216            /*            
217            cbClsExtra: 0,
218            cbWndExtra: 0,
219            hCursor: Default::default(), //unsafe {winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_ARROW)}, // must be null in order for cursor state to work properly
220            hbrBackground: Default::default(),
221            lpszMenuName: PCWSTR::null(),
222            hIconSm: Default::default(),*/
223        };
224        
225        unsafe {
226            RegisterClassExW(&class);
227            let _ = IsGUIThread(TRUE);
228            
229            // initialize COM using OleInitialize to allow Drag&Drop and other shell features
230            OleInitialize(None).unwrap();
231        }
232        
233        
234        let win32_app = Win32App {
235            start_dragging_items: None,
236            window_class_name,
237            was_signal_poll: false,
238            time: Win32Time::new(),
239            event_callback: Some(event_callback),
240            event_flow: EventFlow::Poll,
241            all_windows: Vec::new(),
242            timers: Vec::new(),
243            dpi_functions: DpiFunctions::new(),
244            current_cursor: None,
245            currently_clicked_window_id: None,
246            is_dragging_internal: Cell::new(false),
247        };
248        win32_app.dpi_functions.become_dpi_aware();
249        
250        win32_app
251    }
252    
253    pub fn event_loop() {
254        unsafe {
255            loop {
256                let event_flow = with_win32_app(|app| app.event_flow.clone());
257                match event_flow {
258                    EventFlow::Wait => {
259                        let mut msg = std::mem::MaybeUninit::uninit();
260                        let ret = GetMessageW(msg.as_mut_ptr(), None, 0, 0);
261                        let msg = msg.assume_init();
262                        if ret == FALSE {
263                            // Only happens if the message is `WM_QUIT`.
264                            debug_assert_eq!(msg.message, WM_QUIT);
265                            with_win32_app(|app| app.event_flow = EventFlow::Exit);
266                        }
267                        else {
268                            let _ = TranslateMessage(&msg);
269                            DispatchMessageW(&msg);
270                            if !with_win32_app(|app| app.was_signal_poll()) {
271                                Win32App::do_callback(Win32Event::Paint);
272                            }
273                        }
274                    }
275                    EventFlow::Poll => {
276                        let mut msg = std::mem::MaybeUninit::uninit();
277                        let ret = PeekMessageW(msg.as_mut_ptr(), None, 0, 0, PM_REMOVE);
278                        let msg = msg.assume_init();
279                        if ret == FALSE {
280                            Win32App::do_callback(Win32Event::Paint)
281                        }
282                        else {
283                            let _ = TranslateMessage(&msg);
284                            DispatchMessageW(&msg);
285                        }
286                    }
287                    EventFlow::Exit => panic!()
288                }
289                Win32App::poll_start_drag_drop();
290            }
291        }
292    }
293    
294    pub fn do_callback(event: Win32Event) {
295        let cb = with_win32_app(|app| app.event_callback.take());
296        if let Some(mut callback) = cb {
297            let event_flow = callback(event);
298            with_win32_app(|app| app.event_flow = event_flow);
299            if let EventFlow::Exit = event_flow {
300                unsafe {ExitProcess(0);}
301            }
302            with_win32_app(|app| app.event_callback = Some(callback));
303        }
304    }
305    
306    pub unsafe extern "system" fn timer_proc(_hwnd: HWND, _arg1: u32, in_win32_id: usize, _arg2: u32) {
307        let hit_timer = {
308            with_win32_app(|app|{
309                let mut hit_timer = None;
310                for slot in 0..app.timers.len() {
311                    match app.timers[slot] {
312                        Win32Timer::Timer {win32_id, repeats, ..} => if win32_id == in_win32_id {
313                            hit_timer = Some(app.timers[slot].clone());
314                            if !repeats {
315                                KillTimer(None, in_win32_id).unwrap();
316                                app.timers[slot] = Win32Timer::Free;
317                            }
318                            break;
319                        },
320                        Win32Timer::DragDrop {win32_id, ..} => if win32_id == in_win32_id {
321                            hit_timer = Some(app.timers[slot].clone());
322                            break;
323                        },
324                        Win32Timer::Resize {win32_id, ..} => if win32_id == in_win32_id {
325                            hit_timer = Some(app.timers[slot].clone());
326                            break;
327                        },
328                        Win32Timer::SignalPoll {win32_id, ..} => if win32_id == in_win32_id {
329                            hit_timer = Some(app.timers[slot].clone());
330                            break;
331                        }
332                        _ => ()
333                    }
334                };
335                hit_timer
336            })
337        };
338        // call the dependencies
339        let time =  with_win32_app(|app| app.time_now());
340        if let Some(hit_timer) = hit_timer {
341            match hit_timer {
342                Win32Timer::Timer {timer_id, ..} => {
343                    Win32App::do_callback(Win32Event::Timer(TimerEvent {
344                        time: Some(time),
345                        timer_id: timer_id
346                    }));
347                },
348                Win32Timer::Resize {..} => {
349                    Win32App::do_callback(Win32Event::Paint);
350                },
351                Win32Timer::DragDrop {..} => {
352                    Win32App::do_callback(Win32Event::Paint);
353                },
354                Win32Timer::SignalPoll {..} => {
355                    Win32App::do_callback(
356                        Win32Event::Signal
357                    );
358                    with_win32_app(|app| app.was_signal_poll = true);
359                }
360                _ => ()
361            }
362        }
363    }
364    
365    pub fn was_signal_poll(&mut self) -> bool {
366        if self.was_signal_poll {
367            self.was_signal_poll = false;
368            true
369        }
370        else {
371            false
372        }
373    }
374    
375    pub fn get_free_timer_slot(&mut self) -> usize {
376        //let win32_app = get_win32_app_global();
377        for slot in 0..self.timers.len() {
378            if let Win32Timer::Free = self.timers[slot] {
379                return slot
380            }
381        }
382        let slot = self.timers.len();
383        self.timers.push(Win32Timer::Free);
384        slot
385    }
386    
387    pub fn start_timer(&mut self, timer_id: u64, interval: f64, repeats: bool) {
388        let slot = self.get_free_timer_slot();
389        let win32_id = unsafe {SetTimer(None, 0, (interval * 1000.0) as u32, Some(Self::timer_proc))};
390        if timer_id == 0{
391            self.timers[slot] = Win32Timer::SignalPoll {
392                win32_id: win32_id,
393            };
394        }
395        else{
396            self.timers[slot] = Win32Timer::Timer {
397                timer_id: timer_id,
398                win32_id: win32_id,
399                interval: interval,
400                repeats: repeats
401            };
402        }
403    }
404    
405    pub fn stop_timer(&mut self, which_timer_id: u64) {
406        for slot in 0..self.timers.len() {
407            if let Win32Timer::Timer {win32_id, timer_id, ..} = self.timers[slot] {
408                if timer_id == which_timer_id {
409                    self.timers[slot] = Win32Timer::Free;
410                    unsafe {KillTimer(None, win32_id).unwrap();}
411                }
412            }
413        }
414    }
415    
416    pub fn start_resize(&mut self) {
417        let slot = self.get_free_timer_slot();
418        let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
419        self.timers[slot] = Win32Timer::Resize {win32_id: win32_id};
420    }
421    
422    pub fn poll_start_drag_drop() {
423        let items = with_win32_app(|app| app.start_dragging_items.take());
424        if let Some(items) = items {
425            with_win32_app(|app| {
426                let slot = app.get_free_timer_slot();
427                let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
428                app.timers[slot] = Win32Timer::DragDrop {win32_id: win32_id};
429            });
430            
431            if items.len() > 1 {
432                error!("multi-item drag/drop operation not supported");
433            }
434            match &items[0] {
435                DragItem::FilePath {path, internal_id,} => {
436                    
437                    //log!("win32: about to drag path \"{}\" with internal ID {:?}", path, internal_id);
438                    
439                    // only drag if something is there
440                    if (path.len() > 0) || internal_id.is_some() {
441                        
442                        // create COM IDataObject that hosts the drag item
443                        let data_object: IDataObject = DragItemWindows(DragItem::FilePath {path: path.clone(), internal_id: internal_id.clone(),}).into();
444                        
445                        // create COM IDropSource to indicate when to stop dragging
446                        let drop_source: IDropSource = DropSource {}.into();
447                        
448                        with_win32_app(|app| app.is_dragging_internal.replace(true));
449                        let mut effect = DROPEFFECT(0);
450                        match unsafe {DoDragDrop(&data_object, &drop_source, DROPEFFECT_COPY | DROPEFFECT_MOVE, &mut effect)} {
451                            DRAGDROP_S_DROP => {/*log!("DoDragDrop: succesful")*/},
452                            DRAGDROP_S_CANCEL => {/*log!("DoDragDrop: canceled")*/},
453                            _ => {log!("DoDragDrop: failed for some reason")},
454                        }
455                        with_win32_app(|app| app.is_dragging_internal.replace(false));
456                    }
457                },
458                _ => {
459                    error!("Only DragItem::FilePath supported");
460                }
461            }
462            with_win32_app(|app|{
463                for slot in 0..app.timers.len() {
464                    if let Win32Timer::DragDrop {win32_id} = app.timers[slot] {
465                        app.timers[slot] = Win32Timer::Free;
466                        unsafe {KillTimer(None, win32_id).unwrap();}
467                    }
468                }
469            })
470        }
471    }
472    
473    pub fn start_signal_poll(&mut self) {
474        let slot = self.get_free_timer_slot();
475        let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
476        self.timers[slot] = Win32Timer::SignalPoll {win32_id: win32_id};
477    }
478    
479    pub fn stop_resize(&mut self) {
480        for slot in 0..self.timers.len() {
481            if let Win32Timer::Resize {win32_id} = self.timers[slot] {
482                self.timers[slot] = Win32Timer::Free;
483                unsafe {KillTimer(None, win32_id).unwrap();}
484            }
485        }
486    }
487    
488    pub fn start_dragging(&mut self, items: Vec<DragItem>) {
489        self.start_dragging_items = Some(items);
490    }
491    
492    pub fn time_now(&self) -> f64 {
493        self.time.time_now()
494    }
495    
496    pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
497        if self.current_cursor.is_none() || self.current_cursor.unwrap() != cursor {
498            let win32_cursor = match cursor {
499                MouseCursor::Hidden => {
500                    PCWSTR::null()
501                },
502                MouseCursor::Default => IDC_ARROW,
503                MouseCursor::Crosshair => IDC_CROSS,
504                MouseCursor::Hand => IDC_HAND,
505                // Default to Hand for non-supported cursors, until we include our own custom cursor files.
506                MouseCursor::Grab | MouseCursor::Grabbing => IDC_HAND,
507                MouseCursor::Arrow => IDC_ARROW,
508                MouseCursor::Move => IDC_SIZEALL,
509                MouseCursor::Text => IDC_IBEAM,
510                MouseCursor::Wait => IDC_ARROW,
511                MouseCursor::Help => IDC_HELP,
512                MouseCursor::NotAllowed => IDC_NO,
513                
514                MouseCursor::EResize => IDC_SIZEWE,
515                MouseCursor::NResize => IDC_SIZENS,
516                MouseCursor::NeResize => IDC_SIZENESW,
517                MouseCursor::NwResize => IDC_SIZENWSE,
518                MouseCursor::SResize => IDC_SIZENS,
519                MouseCursor::SeResize => IDC_SIZENWSE,
520                MouseCursor::SwResize => IDC_SIZENESW,
521                MouseCursor::WResize => IDC_SIZEWE,
522                
523                
524                MouseCursor::NsResize => IDC_SIZENS,
525                MouseCursor::NeswResize => IDC_SIZENESW,
526                MouseCursor::EwResize => IDC_SIZEWE,
527                MouseCursor::NwseResize => IDC_SIZENWSE,
528                
529                MouseCursor::ColResize => IDC_SIZEWE,
530                MouseCursor::RowResize => IDC_SIZENS,
531            };
532            self.current_cursor = Some(cursor);
533            unsafe {
534                if win32_cursor == PCWSTR::null() {
535                    ShowCursor(FALSE);
536                }
537                else {
538                    SetCursor(LoadCursorW(None, win32_cursor).unwrap());
539                    ShowCursor(TRUE);
540                }
541            }
542            //TODO
543        }
544    }
545}
546
547
548
549// reworked from winit windows platform https://github.com/rust-windowing/winit/blob/eventloop-2.0/src/platform_impl/windows/dpi.rs
550
551type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
552type SetProcessDpiAwareness = unsafe extern "system" fn (value: PROCESS_DPI_AWARENESS,) -> HRESULT;
553type SetProcessDpiAwarenessContext = unsafe extern "system" fn (value: DPI_AWARENESS_CONTEXT,) -> BOOL;
554type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> u32;
555type GetDpiForMonitor = unsafe extern "system" fn (hmonitor: HMONITOR, dpi_type: MONITOR_DPI_TYPE, dpi_x: *mut u32, dpi_y: *mut u32) -> HRESULT;
556type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
557
558// Helper function to dynamically load function pointer.
559// `library` and `function` must be zero-terminated.
560fn get_function_impl(library: &str, function: &str) -> FARPROC {
561    // Library names we will use are ASCII so we can use the A version to avoid string conversion.
562    
563    let module = unsafe {LoadLibraryA(PCSTR::from_raw(library.as_ptr()))};
564    if module.is_err() {
565        return None;
566    }
567    
568    let function_ptr = unsafe {GetProcAddress(module.unwrap(), PCSTR::from_raw(function.as_ptr()))};
569    if function_ptr.is_none() {
570        return None;
571    }
572    
573    function_ptr
574}
575
576macro_rules!get_function {
577    ( $ lib: expr, $ func: ident) => {
578        get_function_impl(concat!( $ lib, '\0'), concat!(stringify!( $ func), '\0'))
579            .map( | f | unsafe {mem::transmute::<_, $ func>(f)})
580    }
581}
582
583pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
584    string.as_ref().encode_wide().chain(std::iter::once(0)).collect()
585}
586
587/*
588pub fn post_signal_to_hwnd(hwnd:HWND, signal:Signal){
589    unsafe{PostMessageW(
590        hwnd,
591        WM_USER,
592        WPARAM(((signal.0.0)&0xffff_ffff) as usize),
593        LPARAM(((signal.0.0>>32)&0xffff_ffff) as isize),
594    )};
595}
596*/
597pub struct DpiFunctions {
598    get_dpi_for_window: Option<GetDpiForWindow>,
599    get_dpi_for_monitor: Option<GetDpiForMonitor>,
600    enable_nonclient_dpi_scaling: Option<EnableNonClientDpiScaling>,
601    set_process_dpi_awareness_context: Option<SetProcessDpiAwarenessContext>,
602    set_process_dpi_awareness: Option<SetProcessDpiAwareness>,
603    set_process_dpi_aware: Option<SetProcessDPIAware>
604}
605
606const BASE_DPI: u32 = 96;
607
608impl DpiFunctions {
609    fn new() -> DpiFunctions {
610        DpiFunctions {
611            get_dpi_for_window: get_function!("user32.dll", GetDpiForWindow),
612            get_dpi_for_monitor: get_function!("shcore.dll", GetDpiForMonitor),
613            enable_nonclient_dpi_scaling: get_function!("user32.dll", EnableNonClientDpiScaling),
614            set_process_dpi_awareness_context: get_function!("user32.dll", SetProcessDpiAwarenessContext),
615            set_process_dpi_awareness: get_function!("shcore.dll", SetProcessDpiAwareness),
616            set_process_dpi_aware: get_function!("user32.dll", SetProcessDPIAware)
617        }
618    }
619    
620    fn become_dpi_aware(&self) {
621        unsafe {
622            if let Some(set_process_dpi_awareness_context) = self.set_process_dpi_awareness_context {
623                // We are on Windows 10 Anniversary Update (1607) or later.
624                if set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == FALSE {
625                    // V2 only works with Windows 10 Creators Update (1703). Try using the older
626                    // V1 if we can't set V2.
627                    let _ = set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
628                }
629            }
630            else if let Some(set_process_dpi_awareness) = self.set_process_dpi_awareness {
631                // We are on Windows 8.1 or later.
632                set_process_dpi_awareness(PROCESS_PER_MONITOR_DPI_AWARE).unwrap();
633            }
634            else if let Some(set_process_dpi_aware) = self.set_process_dpi_aware {
635                // We are on Vista or later.
636                set_process_dpi_aware().unwrap();
637            }
638        }
639    }
640    
641    pub fn enable_non_client_dpi_scaling(&self, hwnd: HWND) {
642        unsafe {
643            if let Some(enable_nonclient_dpi_scaling) = self.enable_nonclient_dpi_scaling {
644                let _ = enable_nonclient_dpi_scaling(hwnd);
645            }
646        }
647    }
648    /*
649    pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
650        unsafe {
651            if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
652                // We are on Windows 8.1 or later.
653                let mut dpi_x = 0;
654                let mut dpi_y = 0;
655                if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
656                    // MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
657                    // record one of the values to determine the DPI and respond appropriately".
658                    // https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
659                    return Some(dpi_x as u32)
660                }
661            }
662        }
663        None
664    }*/
665    
666    pub fn hwnd_dpi_factor(&self, hwnd: HWND) -> f32 {
667        unsafe {
668            let hdc = GetDC(hwnd);
669            if hdc.is_invalid() {
670                panic!("`GetDC` returned null!");
671            }
672            let dpi = if let Some(get_dpi_for_window) = self.get_dpi_for_window {
673                // We are on Windows 10 Anniversary Update (1607) or later.
674                match get_dpi_for_window(hwnd) {
675                    0 => BASE_DPI, // 0 is returned if hwnd is invalid
676                    dpi => dpi as u32,
677                }
678            }
679            else if let Some(get_dpi_for_monitor) = self.get_dpi_for_monitor {
680                // We are on Windows 8.1 or later.
681                let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
682                if monitor.is_invalid() {
683                    BASE_DPI
684                }
685                else {
686                    let mut dpi_x = 0;
687                    let mut dpi_y = 0;
688                    if get_dpi_for_monitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
689                        dpi_x as u32
690                    } else {
691                        BASE_DPI
692                    }
693                }
694            }
695            else {
696                // We are on Vista or later.
697                if IsProcessDPIAware() == TRUE {
698                    // If the process is DPI aware, then scaling must be handled by the application using
699                    // this DPI value.
700                    GetDeviceCaps(hdc, LOGPIXELSX) as u32
701                } else {
702                    // If the process is DPI unaware, then scaling is performed by the OS; we thus return
703                    // 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
704                    // application and the WM.
705                    BASE_DPI
706                }
707            };
708            dpi as f32 / BASE_DPI as f32
709        }
710    }
711    
712}