Skip to main content

native_windows_gui2/win32/
mod.rs

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