1use winapi::shared::minwindef::{BOOL, UINT, DWORD, HMODULE, WPARAM, LPARAM, LRESULT};
7use winapi::shared::windef::{HWND, HMENU, HBRUSH};
8use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
9use winapi::um::winuser::{WNDPROC, NMHDR, IDCANCEL, IDOK};
10use winapi::um::commctrl::{NMTTDISPINFOW, SUBCLASSPROC};
11use super::base_helper::{CUSTOM_ID_BEGIN, to_utf16};
12use super::window_helper::{NOTICE_MESSAGE, NWG_INIT, NWG_TRAY, NWG_TIMER_TICK, NWG_TIMER_STOP};
13use super::high_dpi;
14use crate::controls::ControlHandle;
15use crate::{Event, EventData, NwgError};
16use std::{ptr, mem};
17use std::rc::Rc;
18use std::ffi::OsString;
19use std::os::windows::prelude::OsStringExt;
20use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
21
22
23static TIMER_ID: AtomicU32 = AtomicU32::new(1);
24static NOTICE_ID: AtomicU32 = AtomicU32::new(1);
25static EVENT_HANDLER_ID: AtomicUsize = AtomicUsize::new(1);
26
27const NO_DATA: EventData = EventData::NoData;
28
29type RawCallback = dyn Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT>;
30type Callback = dyn Fn(Event, EventData, ControlHandle) -> ();
31
32pub struct EventHandler {
36 handles: Vec<HWND>,
37 id: SUBCLASSPROC,
38 subclass_id: UINT_PTR
39}
40
41pub struct RawEventHandler {
45 handle: HWND,
46 subclass_proc: SUBCLASSPROC,
47 handler_id: UINT_PTR
48}
49
50
51pub fn build_notice(parent: HWND) -> ControlHandle {
57 let id = NOTICE_ID.fetch_add(1, Ordering::SeqCst);
58 ControlHandle::Notice(parent, id)
59}
60
61pub unsafe fn build_timer(parent: HWND, interval: u32, stopped: bool) -> ControlHandle {
62 use winapi::um::winuser::SetTimer;
63
64 let id = TIMER_ID.fetch_add(1, Ordering::SeqCst);
65
66 if !stopped {
67 SetTimer(parent, id as UINT_PTR, interval as UINT, None);
68 }
69
70 ControlHandle::Timer(parent, id)
71}
72
73pub fn full_bind_event_handler<F>(handle: &ControlHandle, f: F) -> EventHandler
82 where F: Fn(Event, EventData, ControlHandle) -> () + 'static
83{
84 use winapi::um::winuser::EnumChildWindows;
85
86 struct SetSubclassParam {
87 callback_ptr: *mut *const Callback,
88 subclass_id: UINT_PTR,
89 }
90
91 unsafe extern "system" fn set_children_subclass(h: HWND, p: LPARAM) -> i32 {
95 let params_ptr = p as *mut SetSubclassParam;
96 let params = &*params_ptr;
97
98 let cb: Rc<Callback> = Rc::from_raw(*params.callback_ptr);
99
100 mem::forget(cb.clone());
103 SetWindowSubclass(h, Some(process_events), params.subclass_id, params.callback_ptr as UINT_PTR);
104
105 mem::forget(cb);
107
108 1
109 }
110
111 unsafe extern "system" fn handler_children(h: HWND, p: LPARAM) -> i32 {
115 let handles_ptr: *mut Vec<HWND> = p as *mut Vec<HWND>;
116 let handles = &mut *handles_ptr;
117 handles.push(h);
118 1
119 }
120
121 let hwnd = handle.hwnd().expect("Cannot bind control with an handle of type");
122
123 let callback: Rc<Callback> = Rc::new(f);
126 let callback_box: Box<*const Callback> = Box::new(Rc::into_raw(callback));
127 let callback_ptr: *mut *const Callback = Box::into_raw(callback_box);
128
129 let callback_fn: SUBCLASSPROC = Some(process_events);
130 let subclass_id = EVENT_HANDLER_ID.fetch_add(1, Ordering::SeqCst);
131 let mut handler = EventHandler {
132 handles: vec![hwnd],
133 id: callback_fn,
134 subclass_id,
135 };
136
137
138 let params = Box::new(SetSubclassParam { callback_ptr, subclass_id });
139 let params_ptr: *mut SetSubclassParam = Box::into_raw(params);
140
141 unsafe {
142 EnumChildWindows(hwnd, Some(handler_children), (&mut handler.handles as *mut Vec<HWND>) as LPARAM);
143 EnumChildWindows(hwnd, Some(set_children_subclass), params_ptr as LPARAM);
144 SetWindowSubclass(hwnd, callback_fn, subclass_id, callback_ptr as UINT_PTR);
145 Box::from_raw(params_ptr);
146 }
147
148 handler
149}
150
151
152pub fn bind_event_handler<F>(handle: &ControlHandle, parent_handle: &ControlHandle, f: F) -> EventHandler
165 where F: Fn(Event, EventData, ControlHandle) -> () + 'static
166{
167 let hwnd = handle.hwnd().expect("Cannot bind control with an handle of type");
168 let parent_hwnd = parent_handle.hwnd().expect("Cannot bind control with an handle of type");
169
170 let callback: Rc<Callback> = Rc::new(f);
171 let parent_callback = callback.clone();
172
173 let callback_box: Box<*const Callback> = Box::new(Rc::into_raw(callback));
174 let callback_box_parent: Box<*const Callback> = Box::new(Rc::into_raw(parent_callback));
175
176 let callback_ptr: *mut *const Callback = Box::into_raw(callback_box);
177 let callback_ptr_parent: *mut *const Callback = Box::into_raw(callback_box_parent);
178
179 let callback_fn: SUBCLASSPROC = Some(process_events);
180 let subclass_id = EVENT_HANDLER_ID.fetch_add(1, Ordering::SeqCst);
181 let handler = EventHandler {
182 handles: vec![hwnd, parent_hwnd],
183 id: callback_fn,
184 subclass_id,
185 };
186
187 unsafe {
188 SetWindowSubclass(hwnd, callback_fn, subclass_id, callback_ptr as UINT_PTR);
189 SetWindowSubclass(parent_hwnd, callback_fn, subclass_id, callback_ptr_parent as UINT_PTR);
190 }
191
192 handler
193}
194
195
196pub fn unbind_event_handler(handler: &EventHandler)
202{
203 let id = handler.id;
204 let subclass_id = handler.subclass_id;
205 let mut callback_ptr: *mut *const Callback = ptr::null_mut();
206
207 for &handle in handler.handles.iter() {
208 unsafe {
209 let mut callback_value: UINT_PTR = 0;
210 let result = GetWindowSubclass(handle, id, subclass_id, &mut callback_value);
211 if result == 0 {
212 panic!("Parent of hander was either freed or is already unbound");
213 }
214
215 callback_ptr = callback_value as *mut *const Callback;
216 let callback: Rc<Callback> = Rc::from_raw(*callback_ptr);
217
218 RemoveWindowSubclass(handle, id, subclass_id);
221
222 mem::drop(callback);
223 };
224 }
225
226 unsafe {
228 Box::from_raw(callback_ptr);
229 }
230}
231
232pub(crate) fn bind_raw_event_handler_inner<F>(handle: &ControlHandle, handler_id: UINT_PTR, f: F) -> Result<RawEventHandler, NwgError>
233 where F: Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT> + 'static
234{
235 let handler_id = handler_id;
236 let subclass_proc: SUBCLASSPROC = Some(process_raw_events);
237
238 let handle = match handle {
239 &ControlHandle::Hwnd(h) => unsafe {
240 let mut tmp_value = 0;
242 let result = GetWindowSubclass(h, subclass_proc, handler_id, &mut tmp_value);
243 if result != 0 {
244 return Err(NwgError::events_binding(format!("Events id {} is already present on this", handler_id)))
245 }
246
247 let boxed_proc: Box<RawCallback> = Box::new(f);
249 let boxed_proc_wrapper: Box<*mut RawCallback> = Box::new(Box::into_raw(boxed_proc));
250 let proc_data: *mut *mut RawCallback = Box::into_raw(boxed_proc_wrapper);
251 SetWindowSubclass(h, subclass_proc, handler_id, proc_data as UINT_PTR);
252
253 h
254 },
255 htype => panic!("Cannot bind control with an handle of type {:?}.", htype)
256 };
257
258 Ok(RawEventHandler {
259 handle,
260 subclass_proc,
261 handler_id
262 })
263}
264
265pub fn bind_raw_event_handler<F>(handle: &ControlHandle, handler_id: UINT_PTR, f: F) -> Result<RawEventHandler, NwgError>
299where F: Fn(HWND, UINT, WPARAM, LPARAM) -> Option<LRESULT> + 'static
300{
301 if handler_id <= 0xFFFF {
302 panic!("handler_id <= 0xFFFF are reserved by NWG");
303 }
304
305 bind_raw_event_handler_inner(handle, handler_id, f)
306}
307
308
309pub fn has_raw_handler(handle: &ControlHandle, handler_id: UINT_PTR) -> bool {
314 let handle = handle.hwnd().expect("This type of control cannot have a raw handler.");
315 let subclass_proc: SUBCLASSPROC = Some(process_raw_events);
316 let mut tmp_value = 0;
317 unsafe { GetWindowSubclass(handle, subclass_proc, handler_id, &mut tmp_value) != 0 }
318}
319
320pub fn unbind_raw_event_handler(handler: &RawEventHandler) -> Result<(), NwgError>
325{
326 let subclass_proc = handler.subclass_proc;
327 let handler_id = handler.handler_id;
328 let handle = handler.handle;
329
330 unsafe {
331 let mut callback_value: UINT_PTR = 0;
332 let result = GetWindowSubclass(handle, subclass_proc, handler_id, &mut callback_value);
333 if result == 0 {
334 let err = format!(concat!(
335 "Could not fetch raw event handler #{:?}.",
336 "This can happen if the control ({:?}) was freed or",
337 "if this raw event handler was already unbound"
338 ), handler_id, handle);
339 return Err(NwgError::EventsBinding(err));
340 }
341
342 let callback_wrapper_ptr = callback_value as *mut *mut RawCallback;
343 let callback_wrapper: Box<*mut RawCallback> = Box::from_raw(callback_wrapper_ptr);
344 let callback: Box<RawCallback> = Box::from_raw(*callback_wrapper);
345
346 RemoveWindowSubclass(handle, subclass_proc, handler_id);
349
350 mem::drop(callback);
351
352 Ok(())
353 }
354}
355
356pub(crate) unsafe fn build_hwnd_control<'a>(
360 class_name: &'a str,
361 window_title: Option<&'a str>,
362 size: Option<(i32, i32)>,
363 pos: Option<(i32, i32)>,
364 flags: Option<DWORD>,
365 ex_flags: Option<DWORD>,
366 forced_flags: DWORD,
367 parent: Option<HWND>
368) -> Result<ControlHandle, NwgError>
369{
370 use winapi::um::winuser::{WS_OVERLAPPEDWINDOW, WS_VISIBLE, WS_CLIPCHILDREN, };
371 use winapi::um::winuser::{CreateWindowExW, AdjustWindowRectEx};
372 use winapi::shared::windef::RECT;
373 use winapi::um::libloaderapi::GetModuleHandleW;
374
375 let hmod = GetModuleHandleW(ptr::null_mut());
376 if hmod.is_null() { return Err(NwgError::initialization("GetModuleHandleW failed")); }
377
378 let class_name = to_utf16(class_name);
379 let window_title = to_utf16(window_title.unwrap_or("New Window"));
380 let ex_flags = ex_flags.unwrap_or(0);
381 let flags = flags.unwrap_or(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE) | forced_flags;
382
383 let pos = pos.unwrap_or((0, 0));
384 let size = size.unwrap_or((500, 500));
385 let (px, py) = high_dpi::logical_to_physical(pos.0, pos.1);
386 let (mut sx, mut sy) = high_dpi::logical_to_physical(size.0, size.1);
387 let parent_handle = parent.unwrap_or(ptr::null_mut());
388 let menu = ptr::null_mut();
389 let lp_params = ptr::null_mut();
390
391 if parent.is_none() {
392 let mut rect = RECT {left: 0, top: 0, right: sx, bottom: sy};
393 AdjustWindowRectEx(&mut rect, flags, 0, ex_flags);
394
395 sx = rect.right - rect.left;
396 sy = rect.bottom - rect.top;
397 }
398
399 let handle = CreateWindowExW (
400 ex_flags,
401 class_name.as_ptr(), window_title.as_ptr(),
402 flags,
403 px, py,
404 sx, sy,
405 parent_handle,
406 menu,
407 hmod,
408 lp_params
409 );
410
411
412 if handle.is_null() {
413 Err(NwgError::initialization("Window creation failed"))
414 } else {
415 Ok(ControlHandle::Hwnd(handle))
416 }
417}
418
419pub(crate) unsafe fn build_sysclass<'a>(
420 hmod: HMODULE,
421 class_name: &'a str,
422 clsproc: WNDPROC,
423 background: Option<HBRUSH>,
424 style: Option<UINT>
425) -> Result<(), NwgError>
426{
427 use winapi::um::winuser::{LoadCursorW, RegisterClassExW};
428 use winapi::um::winuser::{CS_HREDRAW, CS_VREDRAW, COLOR_WINDOW, IDC_ARROW, WNDCLASSEXW};
429 use winapi::um::errhandlingapi::GetLastError;
430 use winapi::shared::winerror::ERROR_CLASS_ALREADY_EXISTS;
431
432 let class_name = to_utf16(class_name);
433 let background: HBRUSH = background.unwrap_or(COLOR_WINDOW as usize as HBRUSH);
434 let style: UINT = style.unwrap_or(CS_HREDRAW | CS_VREDRAW);
435
436 let class =
437 WNDCLASSEXW {
438 cbSize: mem::size_of::<WNDCLASSEXW>() as UINT,
439 style,
440 lpfnWndProc: clsproc,
441 cbClsExtra: 0,
442 cbWndExtra: 0,
443 hInstance: hmod,
444 hIcon: ptr::null_mut(),
445 hCursor: LoadCursorW(ptr::null_mut(), IDC_ARROW),
446 hbrBackground: background,
447 lpszMenuName: ptr::null(),
448 lpszClassName: class_name.as_ptr(),
449 hIconSm: ptr::null_mut()
450 };
451
452 let class_token = RegisterClassExW(&class);
453 if class_token == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS {
454 Err(NwgError::initialization("System class creation failed"))
455 } else {
456 Ok(())
457 }
458}
459
460pub(crate) fn init_window_class() -> Result<(), NwgError> {
462 use winapi::um::libloaderapi::GetModuleHandleW;
463
464 unsafe {
465 let hmod = GetModuleHandleW(ptr::null_mut());
466 if hmod.is_null() { return Err(NwgError::initialization("GetModuleHandleW failed")); }
467
468 build_sysclass(hmod, "NativeWindowsGuiWindow", Some(blank_window_proc), None, None)?;
469 }
470
471 Ok(())
472}
473
474
475#[cfg(feature = "frame")]
476pub(crate) fn create_frame_classes() -> Result<(), NwgError> {
478 use winapi::um::libloaderapi::GetModuleHandleW;
479
480 unsafe {
481 let hmod = GetModuleHandleW(ptr::null_mut());
482 if hmod.is_null() { return Err(NwgError::initialization("GetModuleHandleW failed")); }
483
484 build_sysclass(hmod, "NWG_FRAME", Some(blank_window_proc), None, None)?;
485 }
486
487 Ok(())
488}
489
490#[cfg(feature = "message-window")]
491pub(crate) fn create_message_window() -> Result<ControlHandle, NwgError> {
493 use winapi::um::winuser::HWND_MESSAGE;
494 use winapi::um::winuser::CreateWindowExW;
495 use winapi::um::libloaderapi::GetModuleHandleW;
496
497
498 let class_name = to_utf16("NativeWindowsGuiWindow");
499 let window_title = vec![0];
500
501 unsafe {
502 let hmod = GetModuleHandleW(ptr::null_mut());
503 if hmod.is_null() { return Err(NwgError::initialization("GetModuleHandleW failed")); }
504
505 let handle = CreateWindowExW (
506 0,
507 class_name.as_ptr(),
508 window_title.as_ptr(),
509 0,
510 0, 0,
511 0, 0,
512 HWND_MESSAGE,
513 ptr::null_mut(),
514 hmod,
515 ptr::null_mut()
516 );
517
518 if handle.is_null() {
519 Err(NwgError::initialization("Message only window creation failed"))
520 } else {
521 Ok(ControlHandle::Hwnd(handle))
522 }
523 }
524}
525
526
527unsafe extern "system" fn blank_window_proc(hwnd: HWND, msg: UINT, w: WPARAM, l: LPARAM) -> LRESULT {
531 use winapi::um::winuser::{WM_CREATE, WM_CLOSE, SW_HIDE};
532 use winapi::um::winuser::{DefWindowProcW, PostMessageW, ShowWindow};
533
534 let handled = match msg {
535 WM_CREATE => {
536 PostMessageW(hwnd, NWG_INIT, 0, 0);
537 true
538 },
539 WM_CLOSE => {
540 ShowWindow(hwnd, SW_HIDE);
541 true
542 },
543 _ => false
544 };
545
546 if handled {
547 0
548 } else {
549 DefWindowProcW(hwnd, msg, w, l)
550 }
551}
552
553#[allow(unused_variables)]
557unsafe extern "system" fn process_events(hwnd: HWND, msg: UINT, w: WPARAM, l: LPARAM, id: UINT_PTR, data: DWORD_PTR) -> LRESULT {
558 use std::char;
559 use crate::events::*;
560
561 use winapi::um::commctrl::{DefSubclassProc, TTN_GETDISPINFOW};
562 use winapi::um::winuser::{GetClassNameW, GetMenuItemID, GetSubMenu};
563 use winapi::um::winuser::{WM_CLOSE, WM_COMMAND, WM_MENUCOMMAND, WM_TIMER, WM_NOTIFY, WM_HSCROLL, WM_VSCROLL, WM_LBUTTONDOWN, WM_LBUTTONUP,
564 WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_MOVE, WM_PAINT, WM_MOUSEMOVE, WM_CONTEXTMENU, WM_INITMENUPOPUP, WM_MENUSELECT, WM_EXITSIZEMOVE,
565 WM_ENTERSIZEMOVE, SIZE_MAXIMIZED, SIZE_MINIMIZED, WM_KEYDOWN, WM_KEYUP, WM_CHAR, WM_MOUSEWHEEL, WM_DROPFILES, GET_WHEEL_DELTA_WPARAM,
566 WM_GETMINMAXINFO, WM_ENTERMENULOOP, WM_EXITMENULOOP, WM_SYSKEYDOWN, WM_SYSKEYUP};
567 use winapi::um::shellapi::{NIN_BALLOONSHOW, NIN_BALLOONHIDE, NIN_BALLOONTIMEOUT, NIN_BALLOONUSERCLICK};
568 use winapi::um::winnt::WCHAR;
569 use winapi::shared::minwindef::{HIWORD, LOWORD};
570
571 let callback_ptr = data as *mut *const Callback;
572 Rc::increment_strong_count(*callback_ptr);
573 let callback = Rc::from_raw(*callback_ptr);
574 let callback = &*callback;
575
576 let base_handle = ControlHandle::Hwnd(hwnd);
577
578 match msg {
579 WM_KEYDOWN | WM_KEYUP | WM_SYSKEYDOWN | WM_SYSKEYUP => {
580 let evt = match msg {
581 WM_SYSKEYDOWN => Event::OnSysKeyPress,
582 WM_SYSKEYUP=> Event::OnSysKeyRelease,
583 WM_KEYDOWN => Event::OnKeyPress,
584 _ => Event::OnKeyRelease,
585 };
586
587 if w == 27 {
589 if is_textbox_control(hwnd) {
590 return 0;
591 }
592 }
593
594 let keycode = w as u32;
595 let data = EventData::OnKey(keycode);
596 callback(evt, data, base_handle);
597 },
598 WM_NOTIFY => {
599 let code = {
600 let notif_ptr: *mut NMHDR = mem::transmute(l);
601 (&*notif_ptr).code
602 };
603
604 match code {
605 TTN_GETDISPINFOW => handle_tooltip_callback(mem::transmute::<_, *mut NMTTDISPINFOW>(l), callback),
606 _ => handle_default_notify_callback(mem::transmute::<_, *const NMHDR>(l), callback)
607 }
608 },
609 WM_MENUCOMMAND => {
610 let parent_handle: HMENU = mem::transmute(l);
611 let item_id = GetMenuItemID(parent_handle, w as i32);
612 let handle = ControlHandle::MenuItem(parent_handle, item_id);
613 callback(Event::OnMenuItemSelected, NO_DATA, handle);
614 },
615 WM_INITMENUPOPUP => {
616 callback(Event::OnMenuOpen, NO_DATA, ControlHandle::Menu(ptr::null_mut(), w as HMENU));
617 },
618 WM_ENTERMENULOOP => {
619 callback(Event::OnMenuEnter, NO_DATA, ControlHandle::Menu(ptr::null_mut(), w as HMENU));
620 },
621 WM_EXITMENULOOP => {
622 callback(Event::OnMenuExit, NO_DATA, ControlHandle::Menu(ptr::null_mut(), w as HMENU));
623 },
624 WM_MOUSEWHEEL => {
625 callback(Event::OnMouseWheel, EventData::OnMouseWheel(GET_WHEEL_DELTA_WPARAM(w) as i32), base_handle);
626 },
627 WM_MENUSELECT => {
628 let index = LOWORD(w as u32) as u32;
629 let parent = l as HMENU;
630 if index < CUSTOM_ID_BEGIN {
631 callback(Event::OnMenuHover, NO_DATA, ControlHandle::Menu(parent, GetSubMenu(parent, index as i32)));
633 } else {
634 callback(Event::OnMenuHover, NO_DATA, ControlHandle::MenuItem(parent, index));
636 }
637 },
638 WM_COMMAND => {
639 let child_handle: HWND = l as HWND;
640 let message = HIWORD(w as u32) as u16;
641 let handle = ControlHandle::Hwnd(child_handle);
642
643 let mut class_name_raw: [WCHAR; 100] = [0; 100];
646 let count = GetClassNameW(child_handle, class_name_raw.as_mut_ptr(), 100) as usize;
647 let class_name = OsString::from_wide(&class_name_raw[..count]).into_string().unwrap_or("".to_string());
648
649 match &class_name as &str {
650 "Button" => callback(button_commands(message), NO_DATA, handle),
651 "Edit" => callback(edit_commands(message), NO_DATA, handle),
652 "ComboBox" => callback(combo_commands(message), NO_DATA, handle),
653 "Static" => callback(static_commands(child_handle, message), NO_DATA, handle),
654 "ListBox" => callback(listbox_commands(message), NO_DATA, handle),
655 _ => match w as i32 {
656 IDOK | IDCANCEL => callback(no_class_name_commands(w), NO_DATA, base_handle),
657 _ => {}
658 },
659 }
660 },
661 WM_CONTEXTMENU => {
662 let target_handle = w as HWND;
663 let handle = ControlHandle::Hwnd(target_handle);
664 callback(Event::OnContextMenu, NO_DATA, handle);
665 },
666 NWG_TRAY => {
667 let msg = LOWORD(l as u32) as u32;
668 let handle = ControlHandle::SystemTray(hwnd);
669
670 match msg {
671 NIN_BALLOONSHOW => callback(Event::OnTrayNotificationShow, NO_DATA, handle),
672 NIN_BALLOONHIDE => callback(Event::OnTrayNotificationHide, NO_DATA, handle),
673 NIN_BALLOONTIMEOUT => callback(Event::OnTrayNotificationTimeout, NO_DATA, handle),
674 NIN_BALLOONUSERCLICK => callback(Event::OnTrayNotificationUserClose, NO_DATA, handle),
675 WM_LBUTTONUP => callback(Event::OnMousePress(MousePressEvent::MousePressLeftUp), NO_DATA, handle),
676 WM_LBUTTONDOWN => callback(Event::OnMousePress(MousePressEvent::MousePressLeftDown), NO_DATA, handle),
677 WM_RBUTTONUP => {
678 callback(Event::OnMousePress(MousePressEvent::MousePressRightUp), NO_DATA, handle);
679 callback(Event::OnContextMenu, NO_DATA, handle);
680 },
681 WM_RBUTTONDOWN => callback(Event::OnMousePress(MousePressEvent::MousePressRightDown), NO_DATA, handle),
682 WM_MOUSEMOVE => callback(Event::OnMouseMove, NO_DATA, handle),
683 _ => {}
684 }
685 },
686 WM_SIZE => {
687 match w {
688 SIZE_MAXIMIZED => callback(Event::OnWindowMaximize, NO_DATA, base_handle),
689 SIZE_MINIMIZED => callback(Event::OnWindowMinimize, NO_DATA, base_handle),
690 _ => callback(Event::OnResize, NO_DATA, base_handle)
691 }
692 },
693 WM_PAINT => {
694 let data = EventData::OnPaint(PaintData { hwnd } );
695 callback(Event::OnPaint, data, base_handle)
696 },
697 WM_DROPFILES => {
698 let data = EventData::OnFileDrop(DropFiles { drop: w as _ });
699 callback(Event::OnFileDrop, data, base_handle)
700 },
701 WM_GETMINMAXINFO => {
702 let data = EventData::OnMinMaxInfo(MinMaxInfo { inner: l as _ });
703 callback(Event::OnMinMaxInfo, data, base_handle)
704 },
705 WM_CHAR => callback(Event::OnChar, EventData::OnChar(char::from_u32(w as u32).unwrap_or('?')), base_handle),
706 WM_EXITSIZEMOVE => callback(Event::OnResizeEnd, NO_DATA, base_handle),
707 WM_ENTERSIZEMOVE => callback(Event::OnResizeBegin, NO_DATA, base_handle),
708 WM_TIMER => callback(Event::OnTimerTick, NO_DATA, ControlHandle::Timer(hwnd, w as u32)),
709 WM_MOVE => callback(Event::OnMove, NO_DATA, base_handle),
710 WM_HSCROLL => callback(Event::OnHorizontalScroll, NO_DATA, ControlHandle::Hwnd(l as HWND)),
711 WM_VSCROLL => callback(Event::OnVerticalScroll, NO_DATA, ControlHandle::Hwnd(l as HWND)),
712 WM_MOUSEMOVE => callback(Event::OnMouseMove, NO_DATA, base_handle),
713 WM_LBUTTONUP => callback(Event::OnMousePress(MousePressEvent::MousePressLeftUp), NO_DATA, base_handle),
714 WM_LBUTTONDOWN => callback(Event::OnMousePress(MousePressEvent::MousePressLeftDown), NO_DATA, base_handle),
715 WM_RBUTTONUP => callback(Event::OnMousePress(MousePressEvent::MousePressRightUp), NO_DATA, base_handle),
716 WM_RBUTTONDOWN => callback(Event::OnMousePress(MousePressEvent::MousePressRightDown), NO_DATA, base_handle),
717 NOTICE_MESSAGE => callback(Event::OnNotice, NO_DATA, ControlHandle::Notice(hwnd, w as u32)),
718 NWG_TIMER_STOP => callback(Event::OnTimerStop, NO_DATA, ControlHandle::Timer(hwnd, w as u32)),
719 NWG_TIMER_TICK => callback(Event::OnTimerTick, NO_DATA, ControlHandle::Timer(hwnd, w as u32)),
720 NWG_INIT => callback(Event::OnInit, NO_DATA, base_handle),
721 WM_CLOSE => {
722 let mut should_exit = true;
723 let data = EventData::OnWindowClose(WindowCloseData { data: &mut should_exit as *mut bool });
724 callback(Event::OnWindowClose, data, base_handle);
725
726 if !should_exit {
727 return 0;
728 }
729 },
730 _ => {}
731 }
732
733 DefSubclassProc(hwnd, msg, w, l)
734}
735
736#[allow(unused_variables)]
740unsafe extern "system" fn process_raw_events(hwnd: HWND, msg: UINT, w: WPARAM, l: LPARAM, id: UINT_PTR, data: DWORD_PTR) -> LRESULT {
741 let callback_wrapper_ptr = data as *mut *mut RawCallback;
742 let callback: Box<RawCallback> = Box::from_raw(*callback_wrapper_ptr);
743
744 let result = callback(hwnd, msg, w, l);
745 Box::into_raw(callback);
746
747 match result {
748 Some(r) => r,
749 None => ::winapi::um::commctrl::DefSubclassProc(hwnd, msg, w, l)
750 }
751}
752
753fn button_commands(m: u16) -> Event {
754 use winapi::um::winuser::{BN_CLICKED, BN_DBLCLK};
755 match m {
756 BN_CLICKED => Event::OnButtonClick,
757 BN_DBLCLK => Event::OnButtonDoubleClick,
758 _ => Event::Unknown
759 }
760}
761
762fn edit_commands(m: u16) -> Event {
763 use winapi::um::winuser::{EN_CHANGE};
764
765 match m {
766 EN_CHANGE => Event::OnTextInput,
767 _ => Event::Unknown
768 }
769}
770
771fn combo_commands(m: u16) -> Event {
772 use winapi::um::winuser::{CBN_CLOSEUP, CBN_DROPDOWN, CBN_SELCHANGE};
773 match m {
774 CBN_CLOSEUP => Event::OnComboBoxClosed,
775 CBN_DROPDOWN => Event::OnComboBoxDropdown,
776 CBN_SELCHANGE => Event::OnComboxBoxSelection,
777 _ => Event::Unknown
778 }
779}
780
781fn datetimepick_commands(m: u32) -> Event {
782 use winapi::um::commctrl::{DTN_CLOSEUP, DTN_DROPDOWN, DTN_DATETIMECHANGE};
783 match m {
784 DTN_CLOSEUP => Event::OnDatePickerClosed,
785 DTN_DROPDOWN => Event::OnDatePickerDropdown,
786 DTN_DATETIMECHANGE => Event::OnDatePickerChanged,
787 _ => Event::Unknown
788 }
789}
790
791fn tabs_commands(m: u32) -> Event {
792 use winapi::um::commctrl::{TCN_SELCHANGE, TCN_SELCHANGING};
793 match m {
794 TCN_SELCHANGE => Event::TabsContainerChanged,
795 TCN_SELCHANGING => Event::TabsContainerChanging,
796 _ => Event::Unknown
797 }
798}
799
800fn track_commands(m: u32) -> Event {
801 use winapi::um::commctrl::NM_RELEASEDCAPTURE;
802
803 match m {
804 NM_RELEASEDCAPTURE => Event::TrackBarUpdated,
805 _ => Event::Unknown
806 }
807}
808
809fn tree_commands(m: u32) -> Event {
810 use winapi::um::commctrl::{
811 NM_CLICK, NM_DBLCLK, NM_KILLFOCUS, NM_RCLICK, NM_SETFOCUS, TVN_BEGINLABELEDITW,
812 TVN_DELETEITEMW, TVN_ENDLABELEDITW, TVN_ITEMCHANGEDW, TVN_ITEMEXPANDEDW, TVN_SELCHANGEDW,
813 };
814
815 match m {
816 NM_CLICK => Event::OnTreeViewClick,
817 NM_DBLCLK => Event::OnTreeViewDoubleClick,
818 NM_KILLFOCUS => Event::OnTreeFocusLost,
819 NM_SETFOCUS => Event::OnTreeFocus,
820 NM_RCLICK => Event::OnTreeViewRightClick,
821 TVN_DELETEITEMW => Event::OnTreeItemDelete,
822 TVN_ITEMEXPANDEDW => Event::OnTreeItemExpanded,
823 TVN_SELCHANGEDW => Event::OnTreeItemSelectionChanged,
824 TVN_ITEMCHANGEDW => Event::OnTreeItemChanged,
825 TVN_BEGINLABELEDITW => Event::OnTreeViewBeginItemEdit,
826 TVN_ENDLABELEDITW => Event::OnTreeViewEndItemEdit,
827 _ => Event::Unknown,
828 }
829}
830
831fn list_view_commands(m: u32) -> Event {
832 use winapi::um::commctrl::{NM_KILLFOCUS, NM_SETFOCUS, LVN_DELETEALLITEMS,
833 LVN_DELETEITEM, LVN_INSERTITEM, LVN_ITEMACTIVATE, LVN_ITEMCHANGED,
834 NM_CLICK, NM_DBLCLK, NM_RCLICK, LVN_COLUMNCLICK};
835
836 match m {
837 NM_CLICK => Event::OnListViewClick,
838 NM_DBLCLK => Event::OnListViewDoubleClick,
839 NM_RCLICK => Event::OnListViewRightClick,
840 LVN_COLUMNCLICK => Event::OnListViewColumnClick,
841 LVN_DELETEALLITEMS => Event::OnListViewClear,
842 LVN_DELETEITEM => Event::OnListViewItemRemoved,
843 LVN_INSERTITEM => Event::OnListViewItemInsert,
844 LVN_ITEMACTIVATE => Event::OnListViewItemActivated,
845 LVN_ITEMCHANGED => Event::OnListViewItemChanged,
846 NM_KILLFOCUS => Event::OnListViewFocusLost,
847 NM_SETFOCUS => Event::OnListViewFocus,
848 _ => Event::Unknown
849 }
850}
851
852fn no_class_name_commands(m: usize) -> Event {
853 match m as i32 {
854 IDOK => Event::OnKeyEnter,
855 IDCANCEL => Event::OnKeyEsc,
856 _ => Event::Unknown,
857 }
858}
859
860#[cfg(feature = "tree-view")]
861fn tree_data(m: u32, notif_raw: *const NMHDR) -> EventData {
862 use crate::{ExpandState, TreeItem, TreeItemAction, TreeItemState};
863 use winapi::um::commctrl::{
864 NMTREEVIEWW, NMTVDISPINFOW, NMTVITEMCHANGE, TVE_COLLAPSE, TVE_EXPAND, TVN_DELETEITEMW,
865 TVN_ENDLABELEDITW, TVN_ITEMCHANGEDW, TVN_ITEMEXPANDEDW, TVN_SELCHANGEDW,
866 };
867
868 match m {
869 TVN_DELETEITEMW => {
870 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
871 let item = TreeItem { handle: data.itemOld.hItem };
872 EventData::OnTreeItemDelete(item)
873 },
874 TVN_ITEMEXPANDEDW => {
875 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
876 let item = TreeItem { handle: data.itemNew.hItem };
877
878 let action = match data.action as usize {
879 TVE_COLLAPSE => TreeItemAction::Expand(ExpandState::Collapse),
880 TVE_EXPAND => TreeItemAction::Expand(ExpandState::Expand),
881 _ => TreeItemAction::Unknown };
883
884 EventData::OnTreeItemUpdate { item, action }
885 },
886 TVN_SELCHANGEDW => {
887 let data = unsafe { &*(notif_raw as *const NMTREEVIEWW) };
888 let new = TreeItem { handle: data.itemNew.hItem };
889 let old = TreeItem { handle: data.itemOld.hItem };
890 EventData::OnTreeItemSelectionChanged { old, new }
891 },
892 TVN_ITEMCHANGEDW => {
893 let data = unsafe { &*(notif_raw as *const NMTVITEMCHANGE) };
894 let item = TreeItem { handle: data.hItem };
895 let action = TreeItemAction::State {
896 new: TreeItemState::from_bits_truncate(data.uStateNew),
897 old: TreeItemState::from_bits_truncate(data.uStateOld)
898 };
899 EventData::OnTreeItemUpdate { item, action }
900 }
901 TVN_ENDLABELEDITW => {
902 let data = unsafe { &*(notif_raw as *const NMTVDISPINFOW) };
903 let new_psztext = data.item.pszText;
904 if !new_psztext.is_null() {
905 let new_text_osstr = unsafe { u16_ptr_to_string(new_psztext) };
906 if let Ok(new_text) = new_text_osstr.into_string() {
907 EventData::OnTreeViewEndItemEdit {
908 f_cancel: false,
909 new_text,
910 }
911 } else {
912 EventData::OnTreeViewEndItemEdit {
913 f_cancel: false,
914 new_text: String::from(""),
915 }
916 }
917 } else {
918 EventData::OnTreeViewEndItemEdit {
919 f_cancel: true,
920 new_text: String::from(""),
921 }
922 }
923 }
924 _ => NO_DATA,
925 }
926}
927
928unsafe fn u16_ptr_to_string(ptr: *const u16) -> OsString {
929 let len = (0..).take_while(|&i| *ptr.offset(i) != 0).count();
930 let slice = std::slice::from_raw_parts(ptr, len);
931
932 OsString::from_wide(slice)
933}
934
935#[cfg(not(feature="tree-view"))]
936fn tree_data(_m: u32, _notif_raw: *const NMHDR) -> EventData {
937 NO_DATA
939}
940
941#[cfg(feature="list-view")]
942fn list_view_data(m: u32, notif_raw: *const NMHDR) -> EventData {
943 use winapi::um::commctrl::{NMLISTVIEW, NMITEMACTIVATE, LVN_DELETEITEM, LVN_ITEMACTIVATE,
944 LVN_INSERTITEM, LVN_ITEMCHANGED, LVIS_SELECTED, LVN_COLUMNCLICK,
945 NM_CLICK, NM_RCLICK, NM_DBLCLK};
946
947 match m {
948 LVN_DELETEITEM | LVN_INSERTITEM | LVN_COLUMNCLICK => {
949 let data: &NMLISTVIEW = unsafe { &*(notif_raw as *const NMLISTVIEW) };
950 EventData::OnListViewItemIndex {
951 row_index: data.iItem as _,
952 column_index: data.iSubItem as _
953 }
954 },
955 LVN_ITEMACTIVATE | NM_CLICK | NM_DBLCLK | NM_RCLICK => {
956 let data: &NMITEMACTIVATE = unsafe { &*(notif_raw as *const NMITEMACTIVATE) };
957 EventData::OnListViewItemIndex {
958 row_index: data.iItem as _,
959 column_index: data.iSubItem as _
960 }
961 },
962 LVN_ITEMCHANGED => {
963 let data: &NMLISTVIEW = unsafe { &*(notif_raw as *const NMLISTVIEW) };
964 EventData::OnListViewItemChanged {
965 row_index: data.iItem as _,
966 column_index: data.iSubItem as _,
967 selected: data.uNewState & LVIS_SELECTED == LVIS_SELECTED
968 }
969 },
970 _ => NO_DATA
971 }
972}
973
974#[cfg(not(feature="list-view"))]
975fn list_view_data(_m: u32, _notif_raw: *const NMHDR) -> EventData {
976 NO_DATA
978}
979
980
981unsafe fn static_commands(handle: HWND, m: u16) -> Event {
982 use winapi::um::winuser::{STN_CLICKED, STN_DBLCLK, STM_GETIMAGE, IMAGE_BITMAP, IMAGE_ICON, IMAGE_CURSOR};
983 use winapi::um::winuser::SendMessageW;
984
985 let has_image = SendMessageW(handle, STM_GETIMAGE, IMAGE_BITMAP as usize, 0) != 0;
986 let has_icon = SendMessageW(handle, STM_GETIMAGE, IMAGE_ICON as usize, 0) != 0;
987 let has_cursor = SendMessageW(handle, STM_GETIMAGE, IMAGE_CURSOR as usize, 0) != 0;
988
989 if has_image | has_icon | has_cursor {
990 match m {
991 STN_CLICKED => Event::OnImageFrameClick,
992 STN_DBLCLK => Event::OnImageFrameDoubleClick,
993 _ => Event::Unknown
994 }
995 } else {
996 match m {
997 STN_CLICKED => Event::OnLabelClick,
998 STN_DBLCLK => Event::OnLabelDoubleClick,
999 _ => Event::Unknown
1000 }
1001 }
1002}
1003
1004unsafe fn listbox_commands(m: u16) -> Event {
1005 use winapi::um::winuser::{LBN_SELCHANGE, LBN_DBLCLK};
1006
1007 match m {
1008 LBN_SELCHANGE => Event::OnListBoxSelect,
1009 LBN_DBLCLK => Event::OnListBoxDoubleClick,
1010 _ => Event::Unknown
1011 }
1012}
1013
1014unsafe fn handle_tooltip_callback<'a>(notif: *mut NMTTDISPINFOW, callback: &Callback) {
1015 use crate::events::ToolTipTextData;
1016
1017 let notif = &mut *notif;
1018 let handle = ControlHandle::Hwnd(notif.hdr.idFrom as HWND);
1019 let data = EventData::OnTooltipText(ToolTipTextData { data: notif });
1020 callback(Event::OnTooltipText, data, handle);
1021}
1022
1023unsafe fn handle_default_notify_callback<'a>(notif_raw: *const NMHDR, callback: &Callback){
1024 use winapi::um::winnt::WCHAR;
1025 use winapi::um::winuser::GetClassNameW;
1026
1027 let notif = &*notif_raw;
1028 let handle = ControlHandle::Hwnd(notif.hwndFrom);
1029
1030 let mut class_name_raw: [WCHAR; 100] = mem::zeroed();
1031 let count = GetClassNameW(notif.hwndFrom, class_name_raw.as_mut_ptr(), 100) as usize;
1032 let class_name = OsString::from_wide(&class_name_raw[..count]).into_string().unwrap_or("".to_string());
1033
1034 let code = notif.code;
1035
1036 match &class_name as &str {
1037 "SysDateTimePick32" => callback(datetimepick_commands(code), NO_DATA, handle),
1038 "SysTabControl32" => callback(tabs_commands(code), NO_DATA, handle),
1039 "msctls_trackbar32" => callback(track_commands(code), NO_DATA, handle),
1040 winapi::um::commctrl::WC_TREEVIEW => callback(tree_commands(code), tree_data(code, notif_raw), handle),
1041 winapi::um::commctrl::WC_LISTVIEW => callback(list_view_commands(code), list_view_data(code, notif_raw), handle),
1042 _ => {}
1043 }
1044}
1045
1046unsafe fn is_textbox_control(hwnd: HWND) -> bool {
1047 use winapi::um::winnt::WCHAR;
1048 use winapi::um::winuser::GetClassNameW;
1049
1050 let mut class_name_raw: [WCHAR; 100] = [0; 100];
1051 let count = GetClassNameW(hwnd, class_name_raw.as_mut_ptr(), 100) as usize;
1052 let class_name = OsString::from_wide(&class_name_raw[..count]).into_string().unwrap_or("".to_string());
1053
1054 class_name == "Edit" || class_name == "RICHEDIT50W"
1055}
1056
1057#[cfg(target_env="gnu")] use std::{sync::Mutex, collections::HashMap};
1062
1063#[cfg(target_env="gnu")]
1064type SubclassId = (usize, usize, UINT_PTR);
1065
1066#[cfg(target_env="gnu")]
1067static mut SUBCLASS_COLLECTION: Option<Mutex<HashMap<SubclassId, DWORD_PTR>>> = None;
1068
1069
1070#[cfg(target_env="gnu")]
1071#[allow(non_snake_case)]
1072unsafe fn GetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: *mut DWORD_PTR) -> BOOL {
1073 if SUBCLASS_COLLECTION.is_none() {
1074 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1075 }
1076
1077 let id = (hwnd as usize, mem::transmute(proc), uid);
1078 match SUBCLASS_COLLECTION.as_ref() {
1079 Some(collection_mutex) => {
1080 let collection = collection_mutex.lock().unwrap();
1081 match collection.get(&id) {
1082 Some(v) => { *data = *v; 1 },
1083 None => { 0 }
1084 }
1085 },
1086 None => unreachable!()
1087 }
1088}
1089
1090#[cfg(target_env="gnu")]
1091#[allow(non_snake_case)]
1092unsafe fn SetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: DWORD_PTR) -> BOOL {
1093 use winapi::um::commctrl::SetWindowSubclass;
1094
1095 if SUBCLASS_COLLECTION.is_none() {
1096 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1097 }
1098
1099 let id = (hwnd as usize, mem::transmute(proc), uid);
1100 match SUBCLASS_COLLECTION.as_ref() {
1101 Some(collection_mutex) => {
1102 let mut collection = collection_mutex.lock().unwrap();
1103 collection.insert(id, data);
1104 },
1105 None => unreachable!()
1106 }
1107
1108
1109 SetWindowSubclass(hwnd, proc, uid, data)
1110}
1111
1112
1113#[cfg(target_env="gnu")]
1114#[allow(non_snake_case)]
1115unsafe fn RemoveWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR) -> BOOL {
1116 use winapi::um::commctrl::RemoveWindowSubclass;
1117
1118 if SUBCLASS_COLLECTION.is_none() {
1119 SUBCLASS_COLLECTION = Some(Mutex::new(HashMap::new()));
1120 }
1121
1122 let id = (hwnd as usize, mem::transmute(proc), uid);
1123 match SUBCLASS_COLLECTION.as_ref() {
1124 Some(collection_mutex) => {
1125 let mut collection = collection_mutex.lock().unwrap();
1126 collection.remove(&id);
1127 },
1128 None => unreachable!()
1129 }
1130
1131 RemoveWindowSubclass(hwnd, proc, uid)
1132}
1133
1134#[cfg(not(target_env="gnu"))]
1135#[allow(non_snake_case)]
1136unsafe fn GetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: *mut DWORD_PTR) -> BOOL {
1137 use winapi::um::commctrl::GetWindowSubclass;
1138 GetWindowSubclass(hwnd, proc, uid, data)
1139}
1140
1141#[cfg(not(target_env="gnu"))]
1142#[allow(non_snake_case)]
1143unsafe fn SetWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR, data: DWORD_PTR) -> BOOL {
1144 use winapi::um::commctrl::SetWindowSubclass;
1145 SetWindowSubclass(hwnd, proc, uid, data)
1146}
1147
1148#[cfg(not(target_env="gnu"))]
1149#[allow(non_snake_case)]
1150unsafe fn RemoveWindowSubclass(hwnd: HWND, proc: SUBCLASSPROC, uid: UINT_PTR) -> BOOL {
1151 use winapi::um::commctrl::RemoveWindowSubclass;
1152 RemoveWindowSubclass(hwnd, proc, uid)
1153}
1154