Skip to main content

native_windows_gui/win32/
window.rs

1/*!
2Native Windows GUI windowing base. Includes events dispatching and window creation.
3
4Warning. Not for the faint of heart.
5*/
6use 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
32/**
33    An opaque structure that represent a window subclass hook. 
34*/
35pub struct EventHandler {
36    handles: Vec<HWND>,
37    id: SUBCLASSPROC,
38    subclass_id: UINT_PTR
39}
40
41/**
42    An opaque structure that represent a window subclass hook. 
43*/
44pub struct RawEventHandler {
45    handle: HWND,
46    subclass_proc: SUBCLASSPROC,
47    handler_id: UINT_PTR
48}
49
50
51/**
52    Note. While there might be a race condition here, it does not matter because
53    All controls are thread local and the true id is (HANDLE + NOTICE_ID)
54    The same apply to timers
55*/
56pub 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
73/**
74    Hook the window subclass with the default event dispatcher.
75    The hook is applied to the window and all it's children (recursively).
76
77    Returns a `EventHandler` that can be passed to `unbind_event_handler` to remove the callbacks.
78
79    This function will panic if `handle` is not a window handle.
80*/
81pub 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    /**
92        Function that iters over a top level window and bind the events dispatch callback
93    */
94    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        // Simply increase the rc count because the callback
101        // will also be stored into the current children window. 
102        mem::forget(cb.clone());
103        SetWindowSubclass(h, Some(process_events), params.subclass_id, params.callback_ptr as UINT_PTR);
104
105        // Do not decrease the refcount
106        mem::forget(cb);
107
108        1
109    }
110
111    /**
112        Push the children window handle into the EventHandler
113    */
114    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    // The callback function must be passed to each children of the control
124    // To do so, we must RC the callback
125    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
152/**
153Hook the window subclass with the default event dispatcher.
154The hook is applied to the control and its parent. All common controls send their events to their parent.
155
156Arguments:
157    - handle: Handle to the main control to hook
158    - parent_handle: Parent to the main control.
159    - f: User event callback
160
161Returns a `EventHandler` that can be passed to `unbind_event_handler` to remove the callbacks.
162
163*/
164pub 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
196/**
197    Free all associated callbacks with the event handler.
198
199    This function will panic if the handler was already freed.
200*/
201pub 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            // Remove the window subclass before dropping the callback to prevent the
219            // subclass window procedure from being called during the drop.
220            RemoveWindowSubclass(handle, id, subclass_id);
221
222            mem::drop(callback);
223        };
224    }
225
226    // Finally free the pointer to the pointer to the callback
227    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            // Check if the handler is already bound to the control
241            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            // Bind the callback
248            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
265/**
266
267Set a window subclass the uses the `process_raw_events` function of NWG.
268The subclass is only applied to the control itself and NOT the children.
269
270When assigning multiple callback to the same control, a different `id` must be specified for each call
271or otherwise, the old callback will be replaced by the new one. See `Label::hook_background_color` for example.
272
273Error:
274- If the event handler with the same ID is already bound, this function will return an Error. The `has_raw_handler` method can be used to check this.
275
276Panic:
277- If the `handle` parameter is not a window-like control
278- If the `handler_id` parameter is <= 0xFFFF
279
280
281```rust
282use native_windows_gui as nwg;
283
284fn bind_raw_handler(window: &nwg::Window) -> nwg::RawEventHandler {
285    const WM_MOVE: u32 = 3287542; // Not the actual value, but who cares?
286    let handler_id = 0x10000;     // handler ids equal or smaller than 0xFFFF are reserved by NWG
287
288    nwg::bind_raw_event_handler(&window.handle, handler_id, move |_hwnd, msg, _w, _l| {
289        if msg == WM_MOVE {
290            println!("MOVING!");
291        }
292        None
293    }).unwrap()
294}
295
296```
297*/
298pub 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
309/** 
310    Check if a raw handler with the specified handler_id is currently bound on the control.
311    This function will panic if the handle parameter is not a window control.
312*/
313pub 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
320/**
321    Remove the raw event handler from the associated window.
322    Calling unbind twice or trying to unbind an handler after destroying its parent will cause the function to panic.
323*/
324pub 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        // Remove the window subclass before dropping the callback to prevent the
347        // subclass window procedure from being called during the drop.
348        RemoveWindowSubclass(handle, subclass_proc, handler_id);
349
350        mem::drop(callback);
351
352        Ok(())
353    }
354}
355
356/**
357    High level function that handle the creation of custom window control or built in window control
358*/
359pub(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, /*WS_EX_LAYERED*/};
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
460/// Create the window class for the base nwg window
461pub(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")]
476/// Create the window class for the frame control
477pub(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")]
491/// Create a message only window. Used with the `MessageWindow` control
492pub(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
527/**
528    A blank system procedure used when creating new window class. Actual system event handling is done in the subclass procedure `process_events`.
529*/
530unsafe 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/**
554    A window subclass procedure that dispatch the windows control events to the associated application control
555*/
556#[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                _ /* WM_KEYUP */ => Event::OnKeyRelease,
585            };
586
587            // Block the textbox ESC key from closing the whole application
588            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                // Item is a sub menu
632                callback(Event::OnMenuHover, NO_DATA, ControlHandle::Menu(parent, GetSubMenu(parent, index as i32)));
633            } else {
634                // Item is a menu item
635                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            // Converting the class name into rust string might not be the most efficient way to do this
644            // It might be a good idea to just compare the class_name_raw
645            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/**
737    A window subclass procedure that dispatch the windows control events to the associated application control
738*/
739#[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 // Other values shoudn't be raised by this event
882            };
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    // If tree-view is not enabled, the data type won't be available so we return NO_DATA
938    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    // If list-view is not enabled, the data type won't be available so we return NO_DATA
977    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//
1058// Hack to make `GetWindowSubclass` work on GNU
1059//
1060
1061#[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