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