1use super::base_helper::{CUSTOM_ID_BEGIN, cwstr_to_str, to_utf16};
7use super::high_dpi;
8use super::window_helper::{NOTICE_MESSAGE, NWG_INIT, NWG_TIMER_STOP, NWG_TIMER_TICK, NWG_TRAY};
9use crate::controls::ControlHandle;
10use crate::{Event, EventData, NwgError};
11use std::ffi::OsString;
12use std::os::windows::prelude::OsStringExt;
13use std::rc::Rc;
14use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
15use std::{mem, ptr};
16use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
17use winapi::shared::minwindef::{BOOL, DWORD, HMODULE, LPARAM, LRESULT, UINT, WPARAM};
18use winapi::shared::windef::{HBRUSH, HMENU, HWND};
19use winapi::um::commctrl::{NMTTDISPINFOW, SUBCLASSPROC};
20use winapi::um::winuser::{
21 HCF_HIGHCONTRASTON, HIGHCONTRASTW, IDCANCEL, IDOK, MAKEINTRESOURCEA, NMHDR,
22 SPI_GETHIGHCONTRAST, SystemParametersInfoW, WM_SETTINGCHANGE, WNDPROC,
23};
24
25static TIMER_ID: AtomicU32 = AtomicU32::new(1);
26static NOTICE_ID: AtomicU32 = AtomicU32::new(1);
27static EVENT_HANDLER_ID: AtomicUsize = AtomicUsize::new(1);
28
29const NO_DATA: EventData = EventData::NoData;
30
31type RawCallback = dyn Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT>;
32type Callback = dyn Fn(Event, EventData, ControlHandle) -> ();
33
34pub struct EventHandler {
38 handles: Vec<HWND>,
39 id: SUBCLASSPROC,
40 subclass_id: UINT_PTR,
41}
42
43pub struct RawEventHandler {
47 handle: HWND,
48 subclass_proc: SUBCLASSPROC,
49 handler_id: UINT_PTR,
50}
51
52pub fn build_notice(parent: HWND) -> ControlHandle {
58 let id = NOTICE_ID.fetch_add(1, Ordering::SeqCst);
59 ControlHandle::Notice(parent, id)
60}
61
62pub unsafe fn build_timer(parent: HWND, interval: u32, stopped: bool) -> ControlHandle {
63 use winapi::um::winuser::SetTimer;
64
65 let id = TIMER_ID.fetch_add(1, Ordering::SeqCst);
66
67 if !stopped {
68 unsafe { SetTimer(parent, id as UINT_PTR, interval as UINT, None) };
69 }
70
71 ControlHandle::Timer(parent, id)
72}
73
74pub fn full_bind_event_handler<F>(handle: &ControlHandle, f: F) -> EventHandler
83where
84 F: Fn(Event, EventData, ControlHandle) -> () + 'static,
85{
86 use winapi::um::winuser::EnumChildWindows;
87
88 struct SetSubclassParam {
89 callback_ptr: *mut *const Callback,
90 subclass_id: UINT_PTR,
91 }
92
93 unsafe extern "system" fn set_children_subclass(h: HWND, p: LPARAM) -> i32 {
97 let params_ptr = p as *mut SetSubclassParam;
98 let params = unsafe { &*params_ptr };
99
100 let cb: Rc<Callback> = unsafe { Rc::from_raw(*params.callback_ptr) };
101
102 mem::forget(cb.clone());
105 SetWindowSubclass(
106 h,
107 Some(process_events),
108 params.subclass_id,
109 params.callback_ptr as UINT_PTR,
110 );
111
112 mem::forget(cb);
114
115 1
116 }
117
118 unsafe extern "system" fn handler_children(h: HWND, p: LPARAM) -> i32 {
122 let handles_ptr: *mut Vec<HWND> = p as *mut Vec<HWND>;
123 let handles = unsafe { &mut *handles_ptr };
124 handles.push(h);
125 1
126 }
127
128 let hwnd = handle
129 .hwnd()
130 .expect("Cannot bind control with an handle of type");
131
132 let callback: Rc<Callback> = Rc::new(f);
135 let callback_box: Box<*const Callback> = Box::new(Rc::into_raw(callback));
136 let callback_ptr: *mut *const Callback = Box::into_raw(callback_box);
137
138 let callback_fn: SUBCLASSPROC = Some(process_events);
139 let subclass_id = EVENT_HANDLER_ID.fetch_add(1, Ordering::SeqCst);
140 let mut handler = EventHandler {
141 handles: vec![hwnd],
142 id: callback_fn,
143 subclass_id,
144 };
145
146 let params = Box::new(SetSubclassParam {
147 callback_ptr,
148 subclass_id,
149 });
150 let params_ptr: *mut SetSubclassParam = Box::into_raw(params);
151
152 unsafe {
153 EnumChildWindows(
154 hwnd,
155 Some(handler_children),
156 (&mut handler.handles as *mut Vec<HWND>) as LPARAM,
157 );
158 EnumChildWindows(hwnd, Some(set_children_subclass), params_ptr as LPARAM);
159 SetWindowSubclass(hwnd, callback_fn, subclass_id, callback_ptr as UINT_PTR);
160 let _ = Box::from_raw(params_ptr);
161 }
162
163 handler
164}
165
166pub fn bind_event_handler<F>(
179 handle: &ControlHandle,
180 parent_handle: &ControlHandle,
181 f: F,
182) -> EventHandler
183where
184 F: Fn(Event, EventData, ControlHandle) -> () + 'static,
185{
186 let hwnd = handle
187 .hwnd()
188 .expect("Cannot bind control with an handle of type");
189 let parent_hwnd = parent_handle
190 .hwnd()
191 .expect("Cannot bind control with an handle of type");
192
193 let callback: Rc<Callback> = Rc::new(f);
194 let parent_callback = callback.clone();
195
196 let callback_box: Box<*const Callback> = Box::new(Rc::into_raw(callback));
197 let callback_box_parent: Box<*const Callback> = Box::new(Rc::into_raw(parent_callback));
198
199 let callback_ptr: *mut *const Callback = Box::into_raw(callback_box);
200 let callback_ptr_parent: *mut *const Callback = Box::into_raw(callback_box_parent);
201
202 let callback_fn: SUBCLASSPROC = Some(process_events);
203 let subclass_id = EVENT_HANDLER_ID.fetch_add(1, Ordering::SeqCst);
204 let handler = EventHandler {
205 handles: vec![hwnd, parent_hwnd],
206 id: callback_fn,
207 subclass_id,
208 };
209
210 SetWindowSubclass(hwnd, callback_fn, subclass_id, callback_ptr as UINT_PTR);
211 SetWindowSubclass(
212 parent_hwnd,
213 callback_fn,
214 subclass_id,
215 callback_ptr_parent as UINT_PTR,
216 );
217
218 handler
219}
220
221pub fn unbind_event_handler(handler: &EventHandler) {
227 let id = handler.id;
228 let subclass_id = handler.subclass_id;
229 let mut callback_ptr: *mut *const Callback = ptr::null_mut();
230
231 for &handle in handler.handles.iter() {
232 unsafe {
233 let mut callback_value: UINT_PTR = 0;
234 let result = GetWindowSubclass(handle, id, subclass_id, &mut callback_value);
235 if result == 0 {
236 panic!("Parent of hander was either freed or is already unbound");
237 }
238
239 callback_ptr = callback_value as *mut *const Callback;
240 let callback: Rc<Callback> = Rc::from_raw(*Box::from_raw(callback_ptr));
241
242 RemoveWindowSubclass(handle, id, subclass_id);
245
246 mem::drop(callback);
247 };
248 }
249
250 unsafe {
252 let _ = Box::from_raw(callback_ptr);
253 }
254}
255
256pub(crate) fn bind_raw_event_handler_inner<F>(
257 handle: &ControlHandle,
258 handler_id: UINT_PTR,
259 f: F,
260) -> Result<RawEventHandler, NwgError>
261where
262 F: Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT> + 'static,
263{
264 let handler_id = handler_id;
265 let subclass_proc: SUBCLASSPROC = Some(process_raw_events);
266
267 let handle = match handle {
268 &ControlHandle::Hwnd(h) => {
269 let mut tmp_value = 0;
271 let result = GetWindowSubclass(h, subclass_proc, handler_id, &mut tmp_value);
272 if result != 0 {
273 return Err(NwgError::events_binding(format!(
274 "Events id {} is already present on this",
275 handler_id
276 )));
277 }
278
279 let boxed_proc: Box<RawCallback> = Box::new(f);
281 let boxed_proc_wrapper: Box<*mut RawCallback> = Box::new(Box::into_raw(boxed_proc));
282 let proc_data: *mut *mut RawCallback = Box::into_raw(boxed_proc_wrapper);
283 SetWindowSubclass(h, subclass_proc, handler_id, proc_data as UINT_PTR);
284
285 h
286 }
287 htype => panic!("Cannot bind control with an handle of type {:?}.", htype),
288 };
289
290 Ok(RawEventHandler {
291 handle,
292 subclass_proc,
293 handler_id,
294 })
295}
296
297pub fn bind_raw_event_handler<F>(
331 handle: &ControlHandle,
332 handler_id: UINT_PTR,
333 f: F,
334) -> Result<RawEventHandler, NwgError>
335where
336 F: Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT> + 'static,
337{
338 if handler_id <= 0xFFFF {
339 panic!("handler_id <= 0xFFFF are reserved by NWG");
340 }
341
342 bind_raw_event_handler_inner(handle, handler_id, f)
343}
344
345pub fn has_raw_handler(handle: &ControlHandle, handler_id: UINT_PTR) -> bool {
350 let handle = handle
351 .hwnd()
352 .expect("This type of control cannot have a raw handler.");
353 let subclass_proc: SUBCLASSPROC = Some(process_raw_events);
354 let mut tmp_value = 0;
355 GetWindowSubclass(handle, subclass_proc, handler_id, &mut tmp_value) != 0
356}
357
358pub fn unbind_raw_event_handler(handler: &RawEventHandler) -> Result<(), NwgError> {
363 let subclass_proc = handler.subclass_proc;
364 let handler_id = handler.handler_id;
365 let handle = handler.handle;
366
367 unsafe {
368 let mut callback_value: UINT_PTR = 0;
369 let result = GetWindowSubclass(handle, subclass_proc, handler_id, &mut callback_value);
370 if result == 0 {
371 let err = format!(
372 concat!(
373 "Could not fetch raw event handler #{:?}.",
374 "This can happen if the control ({:?}) was freed or",
375 "if this raw event handler was already unbound"
376 ),
377 handler_id, handle
378 );
379 return Err(NwgError::EventsBinding(err));
380 }
381
382 let callback_wrapper_ptr = callback_value as *mut *mut RawCallback;
383 let callback_wrapper: Box<*mut RawCallback> = Box::from_raw(callback_wrapper_ptr);
384 let callback: Box<RawCallback> = Box::from_raw(*callback_wrapper);
385
386 RemoveWindowSubclass(handle, subclass_proc, handler_id);
389
390 mem::drop(callback);
391
392 Ok(())
393 }
394}
395
396unsafe fn is_dark_mode_active() -> bool {
397 type FnShouldAppsUseDarkMode = extern "system" fn() -> bool;
399 let h_uxtheme = unsafe {
402 LoadLibraryExW(
403 to_utf16("uxtheme.dll").as_ptr(),
404 0 as HANDLE,
405 LOAD_LIBRARY_SEARCH_SYSTEM32,
406 )
407 };
408
409 if h_uxtheme.is_null() {
410 println!("Failed to load uxtheme.dll");
411 return false;
412 }
413
414 let should_apps_use_dark_mode: FnShouldAppsUseDarkMode =
415 unsafe { mem::transmute(GetProcAddress(h_uxtheme, MAKEINTRESOURCEA(132))) };
416
417 let mut is_high_contrast = false;
418 let mut high_contrast = HIGHCONTRASTW::default();
419 high_contrast.cbSize = size_of::<HIGHCONTRASTW>() as UINT;
420
421 if unsafe {
422 SystemParametersInfoW(
423 SPI_GETHIGHCONTRAST,
424 high_contrast.cbSize,
425 &mut high_contrast as *mut _ as *mut winapi::ctypes::c_void,
426 0,
427 )
428 } != 0
429 {
430 is_high_contrast = (high_contrast.dwFlags & HCF_HIGHCONTRASTON) != 0;
431 }
432
433 let result = should_apps_use_dark_mode() && !is_high_contrast;
434 unsafe { FreeLibrary(h_uxtheme) };
435 result
436}
437
438fn update_dark_mode_for_window(hwnd: HWND) -> Result<(), String> {
447 let value: BOOL = unsafe { is_dark_mode_active() as BOOL };
448 let result: HRESULT = unsafe {
449 winapi::um::dwmapi::DwmSetWindowAttribute(
450 hwnd,
451 20,
452 &value as *const BOOL as *const _,
453 size_of::<BOOL>() as _,
454 )
455 };
456
457 if result != 0 {
458 return Err("DwmSetWindowAttribute failed".to_string());
459 }
460
461 return Ok(());
462}
463
464pub(crate) fn build_hwnd_control<'a>(
468 class_name: &'a str,
469 window_title: Option<&'a str>,
470 size: Option<(i32, i32)>,
471 pos: Option<(i32, i32)>,
472 flags: Option<DWORD>,
473 ex_flags: Option<DWORD>,
474 forced_flags: DWORD,
475 parent: Option<HWND>,
476) -> Result<ControlHandle, NwgError> {
477 use winapi::shared::windef::RECT;
478 use winapi::um::libloaderapi::GetModuleHandleW;
479 use winapi::um::winuser::{AdjustWindowRectEx, CreateWindowExW};
480 use winapi::um::winuser::{
481 WS_CLIPCHILDREN, WS_OVERLAPPEDWINDOW, WS_VISIBLE,
483 };
484
485 let hmod = unsafe { GetModuleHandleW(ptr::null_mut()) };
486 if hmod.is_null() {
487 return Err(NwgError::initialization("GetModuleHandleW failed"));
488 }
489
490 let class_name = to_utf16(class_name);
491 let window_title = to_utf16(window_title.unwrap_or("New Window"));
492 let ex_flags = ex_flags.unwrap_or(0);
493 let flags = flags.unwrap_or(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE) | forced_flags;
494
495 let pos = pos.unwrap_or((0, 0));
496 let size = size.unwrap_or((500, 500));
497 let (px, py) = high_dpi::logical_to_physical(pos.0, pos.1);
498 let (mut sx, mut sy) = high_dpi::logical_to_physical(size.0, size.1);
499 let parent_handle = parent.unwrap_or(ptr::null_mut());
500 let menu = ptr::null_mut();
501 let lp_params = ptr::null_mut();
502
503 if parent.is_none() {
504 let mut rect = RECT {
505 left: 0,
506 top: 0,
507 right: sx,
508 bottom: sy,
509 };
510 unsafe { AdjustWindowRectEx(&mut rect, flags, 0, ex_flags) };
511
512 sx = rect.right - rect.left;
513 sy = rect.bottom - rect.top;
514 }
515
516 let handle = unsafe {
517 CreateWindowExW(
518 ex_flags,
519 class_name.as_ptr(),
520 window_title.as_ptr(),
521 flags,
522 px,
523 py,
524 sx,
525 sy,
526 parent_handle,
527 menu,
528 hmod,
529 lp_params,
530 )
531 };
532
533 if handle.is_null() {
534 Err(NwgError::initialization("Window creation failed"))
535 } else {
536 Ok(ControlHandle::Hwnd(handle))
537 }
538}
539
540pub(crate) fn build_sysclass<'a>(
541 hmod: HMODULE,
542 class_name: &'a str,
543 clsproc: WNDPROC,
544 background: Option<HBRUSH>,
545 style: Option<UINT>,
546) -> Result<(), NwgError> {
547 use winapi::shared::winerror::ERROR_CLASS_ALREADY_EXISTS;
548 use winapi::um::errhandlingapi::GetLastError;
549 use winapi::um::winuser::{COLOR_WINDOW, CS_HREDRAW, CS_VREDRAW, IDC_ARROW, WNDCLASSEXW};
550 use winapi::um::winuser::{LoadCursorW, RegisterClassExW};
551
552 let class_name = to_utf16(class_name);
553 let background: HBRUSH = background.unwrap_or(COLOR_WINDOW as usize as HBRUSH);
554 let style: UINT = style.unwrap_or(CS_HREDRAW | CS_VREDRAW);
555
556 let class = unsafe {
557 WNDCLASSEXW {
558 cbSize: mem::size_of::<WNDCLASSEXW>() as UINT,
559 style,
560 lpfnWndProc: clsproc,
561 cbClsExtra: 0,
562 cbWndExtra: 0,
563 hInstance: hmod,
564 hIcon: ptr::null_mut(),
565 hCursor: LoadCursorW(ptr::null_mut(), IDC_ARROW),
566 hbrBackground: background,
567 lpszMenuName: ptr::null(),
568 lpszClassName: class_name.as_ptr(),
569 hIconSm: ptr::null_mut(),
570 }
571 };
572
573 let class_token = unsafe { RegisterClassExW(&class) };
574 if class_token == 0 && unsafe { GetLastError() } != ERROR_CLASS_ALREADY_EXISTS {
575 Err(NwgError::initialization("System class creation failed"))
576 } else {
577 Ok(())
578 }
579}
580
581pub(crate) fn init_window_class() -> Result<(), NwgError> {
583 use winapi::um::libloaderapi::GetModuleHandleW;
584
585 unsafe {
586 let hmod = GetModuleHandleW(ptr::null_mut());
587 if hmod.is_null() {
588 return Err(NwgError::initialization("GetModuleHandleW failed"));
589 }
590
591 build_sysclass(
592 hmod,
593 "NativeWindowsGuiWindow",
594 Some(blank_window_proc),
595 None,
596 None,
597 )?;
598 }
599
600 Ok(())
601}
602
603#[cfg(feature = "frame")]
604pub(crate) fn create_frame_classes() -> Result<(), NwgError> {
606 use winapi::um::libloaderapi::GetModuleHandleW;
607
608 unsafe {
609 let hmod = GetModuleHandleW(ptr::null_mut());
610 if hmod.is_null() {
611 return Err(NwgError::initialization("GetModuleHandleW failed"));
612 }
613
614 build_sysclass(hmod, "NWG_FRAME", Some(blank_window_proc), None, None)?;
615 }
616
617 Ok(())
618}
619
620#[cfg(feature = "message-window")]
621pub(crate) fn create_message_window() -> Result<ControlHandle, NwgError> {
623 use winapi::um::libloaderapi::GetModuleHandleW;
624 use winapi::um::winuser::CreateWindowExW;
625 use winapi::um::winuser::HWND_MESSAGE;
626
627 let class_name = to_utf16("NativeWindowsGuiWindow");
628 let window_title = vec![0];
629
630 unsafe {
631 let hmod = GetModuleHandleW(ptr::null_mut());
632 if hmod.is_null() {
633 return Err(NwgError::initialization("GetModuleHandleW failed"));
634 }
635
636 let handle = CreateWindowExW(
637 0,
638 class_name.as_ptr(),
639 window_title.as_ptr(),
640 0,
641 0,
642 0,
643 0,
644 0,
645 HWND_MESSAGE,
646 ptr::null_mut(),
647 hmod,
648 ptr::null_mut(),
649 );
650
651 if handle.is_null() {
652 Err(NwgError::initialization(
653 "Message only window creation failed",
654 ))
655 } else {
656 Ok(ControlHandle::Hwnd(handle))
657 }
658 }
659}
660
661extern "system" fn blank_window_proc(hwnd: HWND, msg: UINT, w: WPARAM, l: LPARAM) -> LRESULT {
665 use winapi::um::winuser::{DefWindowProcW, PostMessageW, ShowWindow};
666 use winapi::um::winuser::{SW_HIDE, WM_CLOSE, WM_CREATE};
667
668 let handled = match msg {
669 WM_CREATE => {
670 unsafe { PostMessageW(hwnd, NWG_INIT, 0, 0) };
671 true
672 }
673 WM_CLOSE => {
674 unsafe { ShowWindow(hwnd, SW_HIDE) };
675 true
676 }
677 _ => false,
678 };
679
680 if handled {
681 0
682 } else {
683 unsafe { DefWindowProcW(hwnd, msg, w, l) }
684 }
685}
686
687#[allow(unused_variables)]
691extern "system" fn process_events(
692 hwnd: HWND,
693 msg: UINT,
694 w: WPARAM,
695 l: LPARAM,
696 id: UINT_PTR,
697 data: DWORD_PTR,
698) -> LRESULT {
699 use crate::events::*;
700 use std::char;
701
702 use winapi::shared::minwindef::{HIWORD, LOWORD};
703 use winapi::um::commctrl::{DefSubclassProc, TTN_GETDISPINFOW};
704 use winapi::um::shellapi::{
705 NIN_BALLOONHIDE, NIN_BALLOONSHOW, NIN_BALLOONTIMEOUT, NIN_BALLOONUSERCLICK,
706 };
707 use winapi::um::winnt::WCHAR;
708 use winapi::um::winuser::{
709 GET_WHEEL_DELTA_WPARAM, SIZE_MAXIMIZED, SIZE_MINIMIZED, WM_CHAR, WM_CLOSE, WM_COMMAND,
710 WM_CONTEXTMENU, WM_DROPFILES, WM_ENTERMENULOOP, WM_ENTERSIZEMOVE, WM_EXITMENULOOP,
711 WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_HSCROLL, WM_INITMENUPOPUP, WM_KEYDOWN, WM_KEYUP,
712 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MENUCOMMAND, WM_MENUSELECT, WM_MOUSEMOVE, WM_MOUSEWHEEL,
713 WM_MOVE, WM_NOTIFY, WM_PAINT, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_SYSKEYDOWN,
714 WM_SYSKEYUP, WM_TIMER, WM_VSCROLL,
715 };
716 use winapi::um::winuser::{GetClassNameW, GetMenuItemID, GetSubMenu};
717
718 let callback_ptr = data as *mut *const Callback;
719 unsafe { Rc::increment_strong_count(*callback_ptr) };
720 let callback = unsafe { Rc::from_raw(*callback_ptr) };
721 let callback = &*callback;
722
723 let base_handle = ControlHandle::Hwnd(hwnd);
724
725 match msg {
726 WM_KEYDOWN | WM_KEYUP | WM_SYSKEYDOWN | WM_SYSKEYUP => {
727 let evt = match msg {
728 WM_SYSKEYDOWN => Event::OnSysKeyPress,
729 WM_SYSKEYUP=> Event::OnSysKeyRelease,
730 WM_KEYDOWN => Event::OnKeyPress,
731 _ => Event::OnKeyRelease,
732 };
733
734 if w == 27 {
736 if is_textbox_control(hwnd) {
737 return 0;
738 }
739 }
740
741 let keycode = w as u32;
742 let data = EventData::OnKey(keycode);
743 callback(evt, data, base_handle);
744 }
745 WM_NOTIFY => {
746 let code = {
747 let notif_ptr = l as *mut NMHDR;
748 unsafe { (&*notif_ptr).code }
749 };
750
751 match code {
752 TTN_GETDISPINFOW => handle_tooltip_callback(l as *mut NMTTDISPINFOW, callback),
753 _ => handle_default_notify_callback(l as *const NMHDR, callback),
754 }
755 }
756 WM_MENUCOMMAND => {
757 let parent_handle = l as HMENU;
758 let item_id = unsafe { GetMenuItemID(parent_handle, w as i32) };
759 let handle = ControlHandle::MenuItem(parent_handle, item_id);
760 callback(Event::OnMenuItemSelected, NO_DATA, handle);
761 }
762 WM_INITMENUPOPUP => {
763 callback(
764 Event::OnMenuOpen,
765 NO_DATA,
766 ControlHandle::Menu(ptr::null_mut(), w as HMENU),
767 );
768 }
769 WM_ENTERMENULOOP => {
770 callback(
771 Event::OnMenuEnter,
772 NO_DATA,
773 ControlHandle::Menu(ptr::null_mut(), w as HMENU),
774 );
775 }
776 WM_EXITMENULOOP => {
777 callback(
778 Event::OnMenuExit,
779 NO_DATA,
780 ControlHandle::Menu(ptr::null_mut(), w as HMENU),
781 );
782 }
783 WM_MOUSEWHEEL => {
784 callback(
785 Event::OnMouseWheel,
786 EventData::OnMouseWheel(GET_WHEEL_DELTA_WPARAM(w) as i32),
787 base_handle,
788 );
789 }
790 WM_MENUSELECT => {
791 let index = LOWORD(w as u32) as u32;
792 let parent = l as HMENU;
793 if index < CUSTOM_ID_BEGIN {
794 callback(
796 Event::OnMenuHover,
797 NO_DATA,
798 ControlHandle::Menu(parent, unsafe { GetSubMenu(parent, index as i32) }),
799 );
800 } else {
801 callback(
803 Event::OnMenuHover,
804 NO_DATA,
805 ControlHandle::MenuItem(parent, index),
806 );
807 }
808 }
809 WM_COMMAND => {
810 let child_handle: HWND = l as HWND;
811 let message = HIWORD(w as u32) as u16;
812 let handle = ControlHandle::Hwnd(child_handle);
813
814 let mut class_name_raw: [WCHAR; 100] = [0; 100];
817 let count =
818 unsafe { GetClassNameW(child_handle, class_name_raw.as_mut_ptr(), 100) as usize };
819 let class_name = OsString::from_wide(&class_name_raw[..count])
820 .into_string()
821 .unwrap_or("".to_string());
822
823 match &class_name as &str {
824 "Button" => callback(button_commands(message), NO_DATA, handle),
825 "Edit" => callback(edit_commands(message), NO_DATA, handle),
826 "ComboBox" => callback(combo_commands(message), NO_DATA, handle),
827 "Static" => callback(static_commands(child_handle, message), NO_DATA, handle),
828 "ListBox" => callback(listbox_commands(message), NO_DATA, handle),
829 _ => match w as i32 {
830 IDOK | IDCANCEL => callback(no_class_name_commands(w), NO_DATA, base_handle),
831 _ => {}
832 },
833 }
834 }
835 WM_CONTEXTMENU => {
836 let target_handle = w as HWND;
837 let handle = ControlHandle::Hwnd(target_handle);
838 callback(Event::OnContextMenu, NO_DATA, handle);
839 }
840 NWG_TRAY => {
841 let msg = LOWORD(l as u32) as u32;
842 let handle = ControlHandle::SystemTray(hwnd);
843
844 match msg {
845 NIN_BALLOONSHOW => callback(Event::OnTrayNotificationShow, NO_DATA, handle),
846 NIN_BALLOONHIDE => callback(Event::OnTrayNotificationHide, NO_DATA, handle),
847 NIN_BALLOONTIMEOUT => callback(Event::OnTrayNotificationTimeout, NO_DATA, handle),
848 NIN_BALLOONUSERCLICK => {
849 callback(Event::OnTrayNotificationUserClose, NO_DATA, handle)
850 }
851 WM_LBUTTONUP => callback(
852 Event::OnMousePress(MousePressEvent::MousePressLeftUp),
853 NO_DATA,
854 handle,
855 ),
856 WM_LBUTTONDOWN => callback(
857 Event::OnMousePress(MousePressEvent::MousePressLeftDown),
858 NO_DATA,
859 handle,
860 ),
861 WM_RBUTTONUP => {
862 callback(
863 Event::OnMousePress(MousePressEvent::MousePressRightUp),
864 NO_DATA,
865 handle,
866 );
867 callback(Event::OnContextMenu, NO_DATA, handle);
868 }
869 WM_RBUTTONDOWN => callback(
870 Event::OnMousePress(MousePressEvent::MousePressRightDown),
871 NO_DATA,
872 handle,
873 ),
874 WM_MOUSEMOVE => callback(Event::OnMouseMove, NO_DATA, handle),
875 _ => {}
876 }
877 }
878 WM_SIZE => match w {
879 SIZE_MAXIMIZED => callback(Event::OnWindowMaximize, NO_DATA, base_handle),
880 SIZE_MINIMIZED => callback(Event::OnWindowMinimize, NO_DATA, base_handle),
881 _ => callback(Event::OnResize, NO_DATA, base_handle),
882 },
883 WM_PAINT => {
884 let data = EventData::OnPaint(PaintData { hwnd });
885 callback(Event::OnPaint, data, base_handle)
886 }
887 WM_DROPFILES => {
888 let data = EventData::OnFileDrop(DropFiles { drop: w as _ });
889 callback(Event::OnFileDrop, data, base_handle)
890 }
891 WM_GETMINMAXINFO => {
892 let data = EventData::OnMinMaxInfo(MinMaxInfo { inner: l as _ });
893 callback(Event::OnMinMaxInfo, data, base_handle)
894 }
895 WM_CHAR => callback(
896 Event::OnChar,
897 EventData::OnChar(char::from_u32(w as u32).unwrap_or('?')),
898 base_handle,
899 ),
900 WM_EXITSIZEMOVE => callback(Event::OnResizeEnd, NO_DATA, base_handle),
901 WM_ENTERSIZEMOVE => callback(Event::OnResizeBegin, NO_DATA, base_handle),
902 WM_TIMER => callback(
903 Event::OnTimerTick,
904 NO_DATA,
905 ControlHandle::Timer(hwnd, w as u32),
906 ),
907 WM_MOVE => callback(Event::OnMove, NO_DATA, base_handle),
908 WM_HSCROLL => callback(
909 Event::OnHorizontalScroll,
910 NO_DATA,
911 ControlHandle::Hwnd(l as HWND),
912 ),
913 WM_VSCROLL => callback(
914 Event::OnVerticalScroll,
915 NO_DATA,
916 ControlHandle::Hwnd(l as HWND),
917 ),
918 WM_MOUSEMOVE => callback(Event::OnMouseMove, NO_DATA, base_handle),
919 WM_LBUTTONUP => callback(
920 Event::OnMousePress(MousePressEvent::MousePressLeftUp),
921 NO_DATA,
922 base_handle,
923 ),
924 WM_LBUTTONDOWN => callback(
925 Event::OnMousePress(MousePressEvent::MousePressLeftDown),
926 NO_DATA,
927 base_handle,
928 ),
929 WM_RBUTTONUP => callback(
930 Event::OnMousePress(MousePressEvent::MousePressRightUp),
931 NO_DATA,
932 base_handle,
933 ),
934 WM_RBUTTONDOWN => callback(
935 Event::OnMousePress(MousePressEvent::MousePressRightDown),
936 NO_DATA,
937 base_handle,
938 ),
939 WM_SETTINGCHANGE => {
940 let str = cwstr_to_str(l);
941 match str.as_str() {
942 "ImmersiveColorSet" => {
943 if update_dark_mode_for_window(hwnd).is_err() {
944 println!(
945 "enable_dark_mode_for_window failed, is the Windows version too old?"
946 );
947 }
948 }
949 _ => {}
950 }
951 }
952 NOTICE_MESSAGE => callback(
953 Event::OnNotice,
954 NO_DATA,
955 ControlHandle::Notice(hwnd, w as u32),
956 ),
957 NWG_TIMER_STOP => callback(
958 Event::OnTimerStop,
959 NO_DATA,
960 ControlHandle::Timer(hwnd, w as u32),
961 ),
962 NWG_TIMER_TICK => callback(
963 Event::OnTimerTick,
964 NO_DATA,
965 ControlHandle::Timer(hwnd, w as u32),
966 ),
967 NWG_INIT => {
968 if update_dark_mode_for_window(hwnd).is_err() {
969 println!("enable_dark_mode_for_window failed, is the Windows version too old?");
970 }
971 callback(Event::OnInit, NO_DATA, base_handle)
972 }
973 WM_CLOSE => {
974 let mut should_exit = true;
975 let data = EventData::OnWindowClose(WindowCloseData {
976 data: &mut should_exit as *mut bool,
977 });
978 callback(Event::OnWindowClose, data, base_handle);
979
980 if !should_exit {
981 return 0;
982 }
983 }
984 _ => {}
985 }
986
987 unsafe { DefSubclassProc(hwnd, msg, w, l) }
988}
989
990#[allow(unused_variables)]
994extern "system" fn process_raw_events(
995 hwnd: HWND,
996 msg: UINT,
997 w: WPARAM,
998 l: LPARAM,
999 id: UINT_PTR,
1000 data: DWORD_PTR,
1001) -> LRESULT {
1002 let callback_wrapper_ptr = data as *mut *mut RawCallback;
1003 let callback: Box<RawCallback> = unsafe { Box::from_raw(*callback_wrapper_ptr) };
1004
1005 let result = callback(hwnd, msg, w, l);
1006 let _ = Box::into_raw(callback);
1007
1008 match result {
1009 Some(r) => r,
1010 None => unsafe { ::winapi::um::commctrl::DefSubclassProc(hwnd, msg, w, l) },
1011 }
1012}
1013
1014fn button_commands(m: u16) -> Event {
1015 use winapi::um::winuser::{BN_CLICKED, BN_DBLCLK};
1016 match m {
1017 BN_CLICKED => Event::OnButtonClick,
1018 BN_DBLCLK => Event::OnButtonDoubleClick,
1019 _ => Event::Unknown,
1020 }
1021}
1022
1023fn edit_commands(m: u16) -> Event {
1024 use winapi::um::winuser::EN_CHANGE;
1025
1026 match m {
1027 EN_CHANGE => Event::OnTextInput,
1028 _ => Event::Unknown,
1029 }
1030}
1031
1032fn combo_commands(m: u16) -> Event {
1033 use winapi::um::winuser::{CBN_CLOSEUP, CBN_DROPDOWN, CBN_SELCHANGE};
1034 match m {
1035 CBN_CLOSEUP => Event::OnComboBoxClosed,
1036 CBN_DROPDOWN => Event::OnComboBoxDropdown,
1037 CBN_SELCHANGE => Event::OnComboxBoxSelection,
1038 _ => Event::Unknown,
1039 }
1040}
1041
1042fn datetimepick_commands(m: u32) -> Event {
1043 use winapi::um::commctrl::{DTN_CLOSEUP, DTN_DATETIMECHANGE, DTN_DROPDOWN};
1044 match m {
1045 DTN_CLOSEUP => Event::OnDatePickerClosed,
1046 DTN_DROPDOWN => Event::OnDatePickerDropdown,
1047 DTN_DATETIMECHANGE => Event::OnDatePickerChanged,
1048 _ => Event::Unknown,
1049 }
1050}
1051
1052fn tabs_commands(m: u32) -> Event {
1053 use winapi::um::commctrl::{TCN_SELCHANGE, TCN_SELCHANGING};
1054 match m {
1055 TCN_SELCHANGE => Event::TabsContainerChanged,
1056 TCN_SELCHANGING => Event::TabsContainerChanging,
1057 _ => Event::Unknown,
1058 }
1059}
1060
1061fn track_commands(m: u32) -> Event {
1062 use winapi::um::commctrl::NM_RELEASEDCAPTURE;
1063
1064 match m {
1065 NM_RELEASEDCAPTURE => Event::TrackBarUpdated,
1066 _ => Event::Unknown,
1067 }
1068}
1069
1070fn tree_commands(m: u32) -> Event {
1071 use winapi::um::commctrl::{
1072 NM_CLICK, NM_DBLCLK, NM_KILLFOCUS, NM_RCLICK, NM_SETFOCUS, TVN_BEGINLABELEDITW,
1073 TVN_DELETEITEMW, TVN_ENDLABELEDITW, TVN_ITEMCHANGEDW, TVN_ITEMEXPANDEDW, TVN_SELCHANGEDW,
1074 };
1075
1076 match m {
1077 NM_CLICK => Event::OnTreeViewClick,
1078 NM_DBLCLK => Event::OnTreeViewDoubleClick,
1079 NM_KILLFOCUS => Event::OnTreeFocusLost,
1080 NM_SETFOCUS => Event::OnTreeFocus,
1081 NM_RCLICK => Event::OnTreeViewRightClick,
1082 TVN_DELETEITEMW => Event::OnTreeItemDelete,
1083 TVN_ITEMEXPANDEDW => Event::OnTreeItemExpanded,
1084 TVN_SELCHANGEDW => Event::OnTreeItemSelectionChanged,
1085 TVN_ITEMCHANGEDW => Event::OnTreeItemChanged,
1086 TVN_BEGINLABELEDITW => Event::OnTreeViewBeginItemEdit,
1087 TVN_ENDLABELEDITW => Event::OnTreeViewEndItemEdit,
1088 _ => Event::Unknown,
1089 }
1090}
1091
1092fn list_view_commands(m: u32) -> Event {
1093 use winapi::um::commctrl::{
1094 LVN_COLUMNCLICK, LVN_DELETEALLITEMS, LVN_DELETEITEM, LVN_INSERTITEM, LVN_ITEMACTIVATE,
1095 LVN_ITEMCHANGED, NM_CLICK, NM_DBLCLK, NM_KILLFOCUS, NM_RCLICK, NM_SETFOCUS,
1096 };
1097
1098 match m {
1099 NM_CLICK => Event::OnListViewClick,
1100 NM_DBLCLK => Event::OnListViewDoubleClick,
1101 NM_RCLICK => Event::OnListViewRightClick,
1102 LVN_COLUMNCLICK => Event::OnListViewColumnClick,
1103 LVN_DELETEALLITEMS => Event::OnListViewClear,
1104 LVN_DELETEITEM => Event::OnListViewItemRemoved,
1105 LVN_INSERTITEM => Event::OnListViewItemInsert,
1106 LVN_ITEMACTIVATE => Event::OnListViewItemActivated,
1107 LVN_ITEMCHANGED => Event::OnListViewItemChanged,
1108 NM_KILLFOCUS => Event::OnListViewFocusLost,
1109 NM_SETFOCUS => Event::OnListViewFocus,
1110 _ => Event::Unknown,
1111 }
1112}
1113
1114fn no_class_name_commands(m: usize) -> Event {
1115 match m as i32 {
1116 IDOK => Event::OnKeyEnter,
1117 IDCANCEL => Event::OnKeyEsc,
1118 _ => Event::Unknown,
1119 }
1120}
1121
1122#[cfg(feature = "tree-view")]
1123fn tree_data(m: u32, notif_raw: *const NMHDR) -> EventData {
1124 use crate::{ExpandState, TreeItem, TreeItemAction, TreeItemState};
1125 use winapi::um::commctrl::{
1126 NMTREEVIEWW, NMTVDISPINFOW, NMTVITEMCHANGE, TVE_COLLAPSE, TVE_EXPAND, TVN_DELETEITEMW,
1127 TVN_ENDLABELEDITW, TVN_ITEMCHANGEDW, TVN_ITEMEXPANDEDW, TVN_SELCHANGEDW,
1128 };
1129
1130 match m {
1131 TVN_DELETEITEMW => {
1132 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
1133 let item = TreeItem {
1134 handle: data.itemOld.hItem,
1135 };
1136 EventData::OnTreeItemDelete(item)
1137 }
1138 TVN_ITEMEXPANDEDW => {
1139 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
1140 let item = TreeItem {
1141 handle: data.itemNew.hItem,
1142 };
1143
1144 let action = match data.action as usize {
1145 TVE_COLLAPSE => TreeItemAction::Expand(ExpandState::Collapse),
1146 TVE_EXPAND => TreeItemAction::Expand(ExpandState::Expand),
1147 _ => TreeItemAction::Unknown, };
1149
1150 EventData::OnTreeItemUpdate { item, action }
1151 }
1152 TVN_SELCHANGEDW => {
1153 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
1154 let new = TreeItem {
1155 handle: data.itemNew.hItem,
1156 };
1157 let old = TreeItem {
1158 handle: data.itemOld.hItem,
1159 };
1160 EventData::OnTreeItemSelectionChanged { old, new }
1161 }
1162 TVN_ITEMCHANGEDW => {
1163 let data = unsafe { &*(notif_raw as *const NMTVITEMCHANGE) };
1164 let item = TreeItem { handle: data.hItem };
1165 let action = TreeItemAction::State {
1166 new: TreeItemState::from_bits_truncate(data.uStateNew),
1167 old: TreeItemState::from_bits_truncate(data.uStateOld),
1168 };
1169 EventData::OnTreeItemUpdate { item, action }
1170 }
1171 TVN_ENDLABELEDITW => {
1172 let data = unsafe { &*(notif_raw as *const NMTVDISPINFOW) };
1173 let new_psztext = data.item.pszText;
1174 if !new_psztext.is_null() {
1175 let new_text_osstr = u16_ptr_to_string(new_psztext);
1176 if let Ok(new_text) = new_text_osstr.into_string() {
1177 EventData::OnTreeViewEndItemEdit {
1178 f_cancel: false,
1179 new_text,
1180 }
1181 } else {
1182 EventData::OnTreeViewEndItemEdit {
1183 f_cancel: false,
1184 new_text: String::from(""),
1185 }
1186 }
1187 } else {
1188 EventData::OnTreeViewEndItemEdit {
1189 f_cancel: true,
1190 new_text: String::from(""),
1191 }
1192 }
1193 }
1194 _ => NO_DATA,
1195 }
1196}
1197
1198fn u16_ptr_to_string(ptr: *const u16) -> OsString {
1199 let len = (0..)
1200 .take_while(|&i| unsafe { *ptr.offset(i) } != 0)
1201 .count();
1202 let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
1203
1204 OsString::from_wide(slice)
1205}
1206
1207#[cfg(not(feature = "tree-view"))]
1208fn tree_data(_m: u32, _notif_raw: *const NMHDR) -> EventData {
1209 NO_DATA
1211}
1212
1213#[cfg(feature = "list-view")]
1214fn list_view_data(m: u32, notif_raw: *const NMHDR) -> EventData {
1215 use winapi::um::commctrl::{
1216 LVIS_SELECTED, LVN_COLUMNCLICK, LVN_DELETEITEM, LVN_INSERTITEM, LVN_ITEMACTIVATE,
1217 LVN_ITEMCHANGED, NM_CLICK, NM_DBLCLK, NM_RCLICK, NMITEMACTIVATE, NMLISTVIEW,
1218 };
1219
1220 match m {
1221 LVN_DELETEITEM | LVN_INSERTITEM | LVN_COLUMNCLICK => {
1222 let data: &NMLISTVIEW = unsafe { &*(notif_raw as *const NMLISTVIEW) };
1223 EventData::OnListViewItemIndex {
1224 row_index: data.iItem as _,
1225 column_index: data.iSubItem as _,
1226 }
1227 }
1228 LVN_ITEMACTIVATE | NM_CLICK | NM_DBLCLK | NM_RCLICK => {
1229 let data: &NMITEMACTIVATE = unsafe { &*(notif_raw as *const NMITEMACTIVATE) };
1230 EventData::OnListViewItemIndex {
1231 row_index: data.iItem as _,
1232 column_index: data.iSubItem as _,
1233 }
1234 }
1235 LVN_ITEMCHANGED => {
1236 let data: &NMLISTVIEW = unsafe { &*(notif_raw as *const NMLISTVIEW) };
1237 EventData::OnListViewItemChanged {
1238 row_index: data.iItem as _,
1239 column_index: data.iSubItem as _,
1240 selected: data.uNewState & LVIS_SELECTED == LVIS_SELECTED,
1241 }
1242 }
1243 _ => NO_DATA,
1244 }
1245}
1246
1247#[cfg(not(feature = "list-view"))]
1248fn list_view_data(_m: u32, _notif_raw: *const NMHDR) -> EventData {
1249 NO_DATA
1251}
1252
1253fn static_commands(handle: HWND, m: u16) -> Event {
1254 use winapi::um::winuser::SendMessageW;
1255 use winapi::um::winuser::{
1256 IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ICON, STM_GETIMAGE, STN_CLICKED, STN_DBLCLK,
1257 };
1258
1259 let has_image = unsafe { SendMessageW(handle, STM_GETIMAGE, IMAGE_BITMAP as usize, 0) } != 0;
1260 let has_icon = unsafe { SendMessageW(handle, STM_GETIMAGE, IMAGE_ICON as usize, 0) } != 0;
1261 let has_cursor = unsafe { SendMessageW(handle, STM_GETIMAGE, IMAGE_CURSOR as usize, 0) } != 0;
1262
1263 if has_image | has_icon | has_cursor {
1264 match m {
1265 STN_CLICKED => Event::OnImageFrameClick,
1266 STN_DBLCLK => Event::OnImageFrameDoubleClick,
1267 _ => Event::Unknown,
1268 }
1269 } else {
1270 match m {
1271 STN_CLICKED => Event::OnLabelClick,
1272 STN_DBLCLK => Event::OnLabelDoubleClick,
1273 _ => Event::Unknown,
1274 }
1275 }
1276}
1277
1278fn listbox_commands(m: u16) -> Event {
1279 use winapi::um::winuser::{LBN_DBLCLK, LBN_SELCHANGE};
1280
1281 match m {
1282 LBN_SELCHANGE => Event::OnListBoxSelect,
1283 LBN_DBLCLK => Event::OnListBoxDoubleClick,
1284 _ => Event::Unknown,
1285 }
1286}
1287
1288fn handle_tooltip_callback<'a>(notif: *mut NMTTDISPINFOW, callback: &Callback) {
1289 use crate::events::ToolTipTextData;
1290
1291 let notif = unsafe { &mut *notif };
1292 let handle = ControlHandle::Hwnd(notif.hdr.idFrom as HWND);
1293 let data = EventData::OnTooltipText(ToolTipTextData { data: notif });
1294 callback(Event::OnTooltipText, data, handle);
1295}
1296
1297fn handle_default_notify_callback<'a>(notif_raw: *const NMHDR, callback: &Callback) {
1298 use winapi::um::winnt::WCHAR;
1299 use winapi::um::winuser::GetClassNameW;
1300
1301 let notif = unsafe { &*notif_raw };
1302 let handle = ControlHandle::Hwnd(notif.hwndFrom);
1303
1304 let mut class_name_raw: [WCHAR; 100] = unsafe { mem::zeroed() };
1305 let count = unsafe { GetClassNameW(notif.hwndFrom, class_name_raw.as_mut_ptr(), 100) as usize };
1306 let class_name = OsString::from_wide(&class_name_raw[..count])
1307 .into_string()
1308 .unwrap_or("".to_string());
1309
1310 let code = notif.code;
1311
1312 match &class_name as &str {
1313 "SysDateTimePick32" => callback(datetimepick_commands(code), NO_DATA, handle),
1314 "SysTabControl32" => callback(tabs_commands(code), NO_DATA, handle),
1315 "msctls_trackbar32" => callback(track_commands(code), NO_DATA, handle),
1316 winapi::um::commctrl::WC_TREEVIEW => {
1317 callback(tree_commands(code), tree_data(code, notif_raw), handle)
1318 }
1319 winapi::um::commctrl::WC_LISTVIEW => callback(
1320 list_view_commands(code),
1321 list_view_data(code, notif_raw),
1322 handle,
1323 ),
1324 _ => {}
1325 }
1326}
1327
1328fn is_textbox_control(hwnd: HWND) -> bool {
1329 use winapi::um::winnt::WCHAR;
1330 use winapi::um::winuser::GetClassNameW;
1331
1332 let mut class_name_raw: [WCHAR; 100] = [0; 100];
1333 let count = unsafe { GetClassNameW(hwnd, class_name_raw.as_mut_ptr(), 100) as usize };
1334 let class_name = OsString::from_wide(&class_name_raw[..count])
1335 .into_string()
1336 .unwrap_or("".to_string());
1337
1338 class_name == "Edit" || class_name == "RICHEDIT50W"
1339}
1340
1341#[cfg(target_env = "gnu")]
1346use std::{collections::HashMap, sync::Mutex};
1347use winapi::shared::winerror::HRESULT;
1348use winapi::um::libloaderapi::{
1349 FreeLibrary, GetProcAddress, LOAD_LIBRARY_SEARCH_SYSTEM32, LoadLibraryExW,
1350};
1351use winapi::um::winnt::HANDLE;
1352
1353#[cfg(target_env = "gnu")]
1354type SubclassId = (usize, usize, UINT_PTR);
1355
1356#[cfg(target_env = "gnu")]
1357static mut SUBCLASS_COLLECTION: Option<Mutex<HashMap<SubclassId, DWORD_PTR>>> = None;
1358
1359#[cfg(target_env = "gnu")]
1360#[allow(non_snake_case)]
1361unsafe fn GetWindowSubclass(
1362 hwnd: HWND,
1363 proc: SUBCLASSPROC,
1364 uid: UINT_PTR,
1365 data: *mut DWORD_PTR,
1366) -> BOOL {
1367 if SUBCLASS_COLLECTION.is_none() {
1368 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1369 }
1370
1371 let proc_id = match proc {
1372 Some(p) => p as usize,
1373 None => 0,
1374 };
1375
1376 let id = (hwnd as usize, proc_id, uid);
1377 match SUBCLASS_COLLECTION.as_ref() {
1378 Some(collection_mutex) => {
1379 let collection = collection_mutex.lock().unwrap();
1380 match collection.get(&id) {
1381 Some(v) => {
1382 *data = *v;
1383 1
1384 }
1385 None => 0,
1386 }
1387 }
1388 None => unreachable!(),
1389 }
1390}
1391
1392#[cfg(target_env = "gnu")]
1393#[allow(non_snake_case)]
1394unsafe fn SetWindowSubclass(
1395 hwnd: HWND,
1396 proc: SUBCLASSPROC,
1397 uid: UINT_PTR,
1398 data: DWORD_PTR,
1399) -> BOOL {
1400 use winapi::um::commctrl::SetWindowSubclass;
1401
1402 if SUBCLASS_COLLECTION.is_none() {
1403 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1404 }
1405
1406 let proc_id = match proc {
1407 Some(p) => p as usize,
1408 None => 0,
1409 };
1410
1411 let id = (hwnd as usize, proc_id, uid);
1412 match SUBCLASS_COLLECTION.as_ref() {
1413 Some(collection_mutex) => {
1414 let mut collection = collection_mutex.lock().unwrap();
1415 collection.insert(id, data);
1416 }
1417 None => unreachable!(),
1418 }
1419
1420 SetWindowSubclass(hwnd, proc, uid, data)
1421}
1422
1423#[cfg(target_env = "gnu")]
1424#[allow(non_snake_case)]
1425unsafe fn RemoveWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR) -> BOOL {
1426 use winapi::um::commctrl::RemoveWindowSubclass;
1427
1428 if SUBCLASS_COLLECTION.is_none() {
1429 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1430 }
1431
1432 let proc_id = match proc {
1433 Some(p) => p as usize,
1434 None => 0,
1435 };
1436
1437 let id = (hwnd as usize, proc_id, uid);
1438 match SUBCLASS_COLLECTION.as_ref() {
1439 Some(collection_mutex) => {
1440 let mut collection = collection_mutex.lock().unwrap();
1441 collection.remove(&id);
1442 }
1443 None => unreachable!(),
1444 }
1445
1446 RemoveWindowSubclass(hwnd, proc, uid)
1447}
1448
1449#[cfg(not(target_env = "gnu"))]
1450#[allow(non_snake_case)]
1451fn GetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: *mut DWORD_PTR) -> BOOL {
1452 unsafe {
1453 use winapi::um::commctrl::GetWindowSubclass;
1454
1455 GetWindowSubclass(hwnd, proc, uid, data)
1456 }
1457}
1458
1459#[cfg(not(target_env = "gnu"))]
1460#[allow(non_snake_case)]
1461fn SetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: DWORD_PTR) -> BOOL {
1462 unsafe {
1463 use winapi::um::commctrl::SetWindowSubclass;
1464 SetWindowSubclass(hwnd, proc, uid, data)
1465 }
1466}
1467
1468#[cfg(not(target_env = "gnu"))]
1469#[allow(non_snake_case)]
1470fn RemoveWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR) -> BOOL {
1471 unsafe {
1472 use winapi::um::commctrl::RemoveWindowSubclass;
1473 RemoveWindowSubclass(hwnd, proc, uid)
1474 }
1475}