diff options
Diffstat (limited to 'src/api/win32/callback.rs')
-rw-r--r-- | src/api/win32/callback.rs | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/api/win32/callback.rs b/src/api/win32/callback.rs new file mode 100644 index 0000000..e852eeb --- /dev/null +++ b/src/api/win32/callback.rs @@ -0,0 +1,253 @@ +use std::mem; +use std::ptr; +use std::cell::RefCell; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; + +use CursorState; +use Event; +use super::event; + +use user32; +use winapi; + +/// There's no parameters passed to the callback function, so it needs to get +/// its context (the HWND, the Sender for events, etc.) stashed in +/// a thread-local variable. +thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None)); + +pub struct ThreadLocalData { + pub win: winapi::HWND, + pub sender: Sender<Event>, + pub cursor_state: Arc<Mutex<CursorState>> +} + +/// Checks that the window is the good one, and if so send the event to it. +fn send_event(input_window: winapi::HWND, event: Event) { + CONTEXT_STASH.with(|context_stash| { + let context_stash = context_stash.borrow(); + let stored = match *context_stash { + None => return, + Some(ref v) => v + }; + + let &ThreadLocalData { ref win, ref sender, .. } = stored; + + if win != &input_window { + return; + } + + sender.send(event).ok(); // ignoring if closed + }); +} + +/// This is the callback that is called by `DispatchMessage` in the events loop. +/// +/// Returning 0 tells the Win32 API that the message has been processed. +pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, + wparam: winapi::WPARAM, lparam: winapi::LPARAM) + -> winapi::LRESULT +{ + match msg { + winapi::WM_DESTROY => { + use events::Event::Closed; + + CONTEXT_STASH.with(|context_stash| { + let context_stash = context_stash.borrow(); + let stored = match *context_stash { + None => return, + Some(ref v) => v + }; + + let &ThreadLocalData { ref win, .. } = stored; + + if win == &window { + user32::PostQuitMessage(0); + } + }); + + send_event(window, Closed); + 0 + }, + + winapi::WM_ERASEBKGND => { + 1 + }, + + winapi::WM_SIZE => { + use events::Event::Resized; + let w = winapi::LOWORD(lparam as winapi::DWORD) as u32; + let h = winapi::HIWORD(lparam as winapi::DWORD) as u32; + send_event(window, Resized(w, h)); + 0 + }, + + winapi::WM_MOVE => { + use events::Event::Moved; + let x = winapi::LOWORD(lparam as winapi::DWORD) as i32; + let y = winapi::HIWORD(lparam as winapi::DWORD) as i32; + send_event(window, Moved(x, y)); + 0 + }, + + winapi::WM_CHAR => { + use std::mem; + use events::Event::ReceivedCharacter; + let chr: char = mem::transmute(wparam as u32); + send_event(window, ReceivedCharacter(chr)); + 0 + }, + + winapi::WM_MOUSEMOVE => { + use events::Event::MouseMoved; + + let x = winapi::GET_X_LPARAM(lparam) as i32; + let y = winapi::GET_Y_LPARAM(lparam) as i32; + + send_event(window, MouseMoved((x, y))); + + 0 + }, + + winapi::WM_MOUSEWHEEL => { + use events::Event::MouseWheel; + + let value = (wparam >> 16) as i16; + let value = value as i32; + + send_event(window, MouseWheel(value)); + + 0 + }, + + winapi::WM_KEYDOWN => { + use events::Event::KeyboardInput; + use events::ElementState::Pressed; + let scancode = ((lparam >> 16) & 0xff) as u8; + let vkey = event::vkeycode_to_element(wparam); + send_event(window, KeyboardInput(Pressed, scancode, vkey)); + 0 + }, + + winapi::WM_KEYUP => { + use events::Event::KeyboardInput; + use events::ElementState::Released; + let scancode = ((lparam >> 16) & 0xff) as u8; + let vkey = event::vkeycode_to_element(wparam); + send_event(window, KeyboardInput(Released, scancode, vkey)); + 0 + }, + + winapi::WM_LBUTTONDOWN => { + use events::Event::MouseInput; + use events::MouseButton::Left; + use events::ElementState::Pressed; + send_event(window, MouseInput(Pressed, Left)); + 0 + }, + + winapi::WM_LBUTTONUP => { + use events::Event::MouseInput; + use events::MouseButton::Left; + use events::ElementState::Released; + send_event(window, MouseInput(Released, Left)); + 0 + }, + + winapi::WM_RBUTTONDOWN => { + use events::Event::MouseInput; + use events::MouseButton::Right; + use events::ElementState::Pressed; + send_event(window, MouseInput(Pressed, Right)); + 0 + }, + + winapi::WM_RBUTTONUP => { + use events::Event::MouseInput; + use events::MouseButton::Right; + use events::ElementState::Released; + send_event(window, MouseInput(Released, Right)); + 0 + }, + + winapi::WM_MBUTTONDOWN => { + use events::Event::MouseInput; + use events::MouseButton::Middle; + use events::ElementState::Pressed; + send_event(window, MouseInput(Pressed, Middle)); + 0 + }, + + winapi::WM_MBUTTONUP => { + use events::Event::MouseInput; + use events::MouseButton::Middle; + use events::ElementState::Released; + send_event(window, MouseInput(Released, Middle)); + 0 + }, + + winapi::WM_INPUT => { + let mut data: winapi::RAWINPUT = mem::uninitialized(); + let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT; + user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT, + mem::transmute(&mut data), &mut data_size, + mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT); + + if data.header.dwType == winapi::RIM_TYPEMOUSE { + let _x = data.mouse.lLastX; // FIXME: this is not always the relative movement + let _y = data.mouse.lLastY; + // TODO: + //send_event(window, Event::MouseRawMovement { x: x, y: y }); + + 0 + + } else { + user32::DefWindowProcW(window, msg, wparam, lparam) + } + }, + + winapi::WM_SETFOCUS => { + use events::Event::Focused; + send_event(window, Focused(true)); + 0 + }, + + winapi::WM_KILLFOCUS => { + use events::Event::Focused; + send_event(window, Focused(false)); + 0 + }, + + winapi::WM_SETCURSOR => { + CONTEXT_STASH.with(|context_stash| { + let cstash = context_stash.borrow(); + let cstash = cstash.as_ref(); + // there's a very bizarre borrow checker bug + // possibly related to rust-lang/rust/#23338 + let cursor_state = if let Some(cstash) = cstash { + if let Ok(cursor_state) = cstash.cursor_state.lock() { + match *cursor_state { + CursorState::Normal => { + user32::SetCursor(user32::LoadCursorW( + ptr::null_mut(), + winapi::IDC_ARROW)); + }, + CursorState::Grab | CursorState::Hide => { + user32::SetCursor(ptr::null_mut()); + } + } + } + } else { + return + }; + +// let &ThreadLocalData { ref cursor_state, .. } = stored; + }); + 0 + }, + + _ => { + user32::DefWindowProcW(window, msg, wparam, lparam) + } + } +} |