Skip to main content

native_windows_gui/win32/
mod.rs

1pub(crate) mod base_helper;
2pub(crate) mod window_helper;
3pub(crate) mod resources_helper;
4pub(crate) mod window;
5pub(crate) mod message_box;
6pub(crate) mod high_dpi;
7pub(crate) mod monitor;
8
9#[cfg(feature = "menu")]
10pub(crate) mod menu;
11
12#[cfg(feature = "cursor")]
13pub(crate) mod cursor;
14
15#[cfg(feature = "clipboard")]
16pub(crate) mod clipboard;
17
18#[cfg(feature = "tabs")]
19pub(crate) mod tabs;
20
21#[cfg(feature = "extern-canvas")]
22pub(crate) mod extern_canvas;
23
24#[cfg(feature = "image-decoder")]
25pub(crate) mod image_decoder;
26
27#[cfg(feature = "rich-textbox")]
28pub(crate) mod richedit;
29
30#[cfg(feature = "plotting")]
31pub(crate) mod plotters_d2d;
32
33use std::{fs, mem, ptr};
34use crate::errors::NwgError;
35
36
37use winapi::um::winuser::{IsDialogMessageW, GetAncestor, TranslateMessage, DispatchMessageW, GA_ROOT};
38
39/**
40    Dispatch system events in the current thread. This method will pause the thread until there are events to process.
41*/
42pub fn dispatch_thread_events() {
43    use winapi::um::winuser::MSG;
44    use winapi::um::winuser::GetMessageW;
45
46    unsafe {
47        let mut msg: MSG = mem::zeroed();
48        while GetMessageW(&mut msg, ptr::null_mut(), 0, 0) != 0 {
49            if IsDialogMessageW(GetAncestor(msg.hwnd, GA_ROOT), &mut msg) == 0 {
50                TranslateMessage(&msg); 
51                DispatchMessageW(&msg); 
52            }
53        }
54    }
55}
56
57
58/**
59    Dispatch system events in the current thread AND execute a callback after each peeking attempt.
60    Unlike `dispath_thread_events`, this method will not pause the thread while waiting for events.
61*/
62pub fn dispatch_thread_events_with_callback<F>(mut cb: F) 
63    where F: FnMut() -> () + 'static
64{
65    use winapi::um::winuser::MSG;
66    use winapi::um::winuser::{PeekMessageW, PM_REMOVE, WM_QUIT};
67
68    unsafe {
69        let mut msg: MSG = mem::zeroed();
70        while msg.message != WM_QUIT {
71            let has_message = PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, PM_REMOVE) != 0;
72            if has_message {
73                if IsDialogMessageW(GetAncestor(msg.hwnd, GA_ROOT), &mut msg) == 0 {
74                    TranslateMessage(&msg); 
75                    DispatchMessageW(&msg); 
76                }
77            }
78
79            cb();
80        }
81    }
82}
83
84/**
85    Break the events loop running on the current thread
86*/
87pub fn stop_thread_dispatch() {
88  use winapi::um::winuser::PostMessageW;
89  use winapi::um::winuser::WM_QUIT;
90
91  unsafe { PostMessageW(ptr::null_mut(), WM_QUIT, 0, 0) };
92}
93
94
95/**
96  Enable the Windows visual style in the application without having to use a manifest
97*/
98pub fn enable_visual_styles() {
99    use winapi::shared::minwindef::{ULONG, DWORD, MAX_PATH};
100    use winapi::shared::basetsd::ULONG_PTR;
101    use winapi::um::fileapi::{GetTempFileNameW, GetTempPathW};
102    use winapi::um::winbase::{ACTCTXW, CreateActCtxW, ActivateActCtx};
103
104    const MANIFEST_CONTENT: &str = r#"
105<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
106<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
107    <description>native-windows-gui comctl32 manifest</description> 
108    <dependency>
109        <dependentAssembly>
110            <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" /> 
111        </dependentAssembly>
112    </dependency>
113</assembly>
114"#;
115    const ACTCTX_FLAG_SET_PROCESS_DEFAULT: DWORD = 0x010;
116
117    let mut tmp_dir = [0u16; MAX_PATH + 1];
118    if unsafe { GetTempPathW(tmp_dir.len() as u32, tmp_dir.as_mut_ptr()) } == 0 {
119        return;
120    }
121    let mut tmp_path = [0u16; MAX_PATH]; // Smaller than above, but these are the respective maximums for each function.
122    let prefix = ['n' as u16, 'w' as u16, 'g' as u16];
123    if unsafe { GetTempFileNameW(tmp_dir.as_ptr(), prefix.as_ptr(), 0, tmp_path.as_mut_ptr())} == 0 {
124        return;
125    }
126
127    let manifest_path_raw = tmp_path;
128    let manifest_path = base_helper::from_utf16(&tmp_path);
129    let _ = fs::write(&manifest_path, MANIFEST_CONTENT);
130
131    let mut activation_cookie: ULONG_PTR = 0;
132    let mut act_ctx = ACTCTXW {
133        cbSize: mem::size_of::<ACTCTXW> as ULONG,
134        dwFlags: ACTCTX_FLAG_SET_PROCESS_DEFAULT,
135        lpSource: manifest_path_raw.as_ptr(),
136        wProcessorArchitecture: 0,
137        wLangId: 0,
138        lpAssemblyDirectory: ptr::null_mut(),
139        lpResourceName: ptr::null_mut(),
140        lpApplicationName: ptr::null_mut(),
141        hModule: ptr::null_mut()
142    };
143
144    unsafe {
145        let handle = CreateActCtxW(&mut act_ctx);
146        ActivateActCtx(handle, &mut activation_cookie);
147    }
148
149    let _ = fs::remove_file(&manifest_path);
150}
151
152/**
153    Ensure that the dll containing the winapi controls is loaded.
154    Also register the custom classes used by NWG
155*/
156pub fn init_common_controls() -> Result<(), NwgError> {
157    use winapi::um::objbase::CoInitialize;
158    use winapi::um::libloaderapi::LoadLibraryW;
159    use winapi::um::commctrl::{InitCommonControlsEx, INITCOMMONCONTROLSEX};
160    use winapi::um::commctrl::{ICC_BAR_CLASSES, ICC_STANDARD_CLASSES, ICC_DATE_CLASSES, ICC_PROGRESS_CLASS,
161     ICC_TAB_CLASSES, ICC_TREEVIEW_CLASSES, ICC_LISTVIEW_CLASSES};
162    use winapi::shared::winerror::{S_OK, S_FALSE};
163
164    unsafe {
165        let mut classes = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
166
167        if cfg!(feature = "datetime-picker") {
168            classes |= ICC_DATE_CLASSES;
169        }
170
171        if cfg!(feature = "progress-bar") {
172            classes |= ICC_PROGRESS_CLASS;
173        }
174
175        if cfg!(feature = "tabs") {
176            classes |= ICC_TAB_CLASSES;
177        }
178
179        if cfg!(feature = "tree-view") {
180            classes |= ICC_TREEVIEW_CLASSES;
181        }
182
183        if cfg!(feature = "list-view") {
184            classes |= ICC_LISTVIEW_CLASSES;
185        }
186
187        if cfg!(feature = "rich-textbox") {
188            let lib = base_helper::to_utf16("Msftedit.dll");
189            LoadLibraryW(lib.as_ptr());
190        }
191
192        let data = INITCOMMONCONTROLSEX {
193            dwSize: mem::size_of::<INITCOMMONCONTROLSEX>() as u32,
194            dwICC: classes
195        };
196
197        InitCommonControlsEx(&data);
198    }
199
200    window::init_window_class()?;
201    tabs_init()?;
202    extern_canvas_init()?;
203    frame_init()?;
204    
205    match unsafe { CoInitialize(ptr::null_mut()) } {
206        S_OK | S_FALSE => Ok(()),
207        _ => Err(NwgError::initialization("CoInitialize failed"))
208    }
209}
210
211#[cfg(feature = "tabs")]
212fn tabs_init() -> Result<(), NwgError> { tabs::create_tab_classes() }
213
214#[cfg(not(feature = "tabs"))]
215fn tabs_init() -> Result<(), NwgError> { Ok(()) }
216
217#[cfg(feature = "extern-canvas")]
218fn extern_canvas_init() -> Result<(), NwgError> { extern_canvas::create_extern_canvas_classes() }
219
220#[cfg(not(feature = "extern-canvas"))]
221fn extern_canvas_init() -> Result<(), NwgError> { Ok(()) }
222
223#[cfg(feature = "frame")]
224fn frame_init() -> Result<(), NwgError> { window::create_frame_classes() }
225
226#[cfg(not(feature = "frame"))]
227fn frame_init() -> Result<(), NwgError> { Ok(()) }
228