diff --git a/Cargo.toml b/Cargo.toml index 2f4903d9f2..9a8219bfaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,10 @@ bitflags = "1" [dev-dependencies] image = "0.23" simple_logger = "1" +ron = {version = "0.6"} +serde_json = {version = "1"} +bincode = {version = "1"} +assert_approx_eq = "1" [target.'cfg(target_os = "android")'.dependencies] ndk = "0.1.0" diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 6d71541216..d145943355 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -1,7 +1,7 @@ use std::{thread, time}; use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{Event, LogicalKey, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -37,7 +37,6 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{ElementState, StartCause, VirtualKeyCode}; println!("{:?}", event); match event { Event::NewEvents(start_cause) => { @@ -46,36 +45,28 @@ fn main() { _ => false, } } - Event::WindowEvent { event, .. } => match event { + Event::WindowEvent(_, event) => match event { WindowEvent::CloseRequested => { close_requested = true; } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: ElementState::Pressed, - .. - }, - .. - } => match virtual_code { - VirtualKeyCode::Key1 => { + WindowEvent::Key(e) if e.is_down() => match e.logical_key() { + Some(LogicalKey::Key1) => { mode = Mode::Wait; println!("\nmode: {:?}\n", mode); } - VirtualKeyCode::Key2 => { + Some(LogicalKey::Key2) => { mode = Mode::WaitUntil; println!("\nmode: {:?}\n", mode); } - VirtualKeyCode::Key3 => { + Some(LogicalKey::Key3) => { mode = Mode::Poll; println!("\nmode: {:?}\n", mode); } - VirtualKeyCode::R => { + Some(LogicalKey::R) => { request_redraw = !request_redraw; println!("\nrequest_redraw: {}\n", request_redraw); } - VirtualKeyCode::Escape => { + Some(LogicalKey::Escape) => { close_requested = true; } _ => (), diff --git a/examples/cursor.rs b/examples/cursor.rs index de45adae33..498e93b1d1 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,5 +1,5 @@ use winit::{ - event::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; @@ -17,18 +17,7 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { - event: - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Pressed, - .. - }, - .. - }, - .. - } => { + Event::WindowEvent(_, WindowEvent::Key(e)) if e.is_down() => { println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]); window.set_cursor_icon(CURSORS[cursor_idx]); if cursor_idx < CURSORS.len() - 1 { @@ -37,10 +26,7 @@ fn main() { cursor_idx = 0; } } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { + Event::WindowEvent(_, WindowEvent::CloseRequested) => { *control_flow = ControlFlow::Exit; return; } diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 317577e3c5..a0f4fecaba 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,5 +1,5 @@ use winit::{ - event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, + event::{Event, LogicalKey, ModifiersState, RawPointerEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -19,34 +19,28 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => match event { + Event::WindowEvent(_, event) => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), - .. - }, - .. - } => { - use winit::event::VirtualKeyCode::*; - match key { - Escape => *control_flow = ControlFlow::Exit, - G => window.set_cursor_grab(!modifiers.shift()).unwrap(), - H => window.set_cursor_visible(modifiers.shift()), - _ => (), - } - } + WindowEvent::Key(e) if e.is_down() => match e.logical_key() { + Some(LogicalKey::Escape) => *control_flow = ControlFlow::Exit, + Some(LogicalKey::G) => window.set_cursor_grab(!modifiers.shift()).unwrap(), + Some(LogicalKey::H) => window.set_cursor_visible(modifiers.shift()), + _ => (), + }, WindowEvent::ModifiersChanged(m) => modifiers = m, _ => (), }, - Event::DeviceEvent { event, .. } => match event { - DeviceEvent::MouseMotion { delta } => println!("mouse moved: {:?}", delta), - DeviceEvent::Button { button, state } => match state { - ElementState::Pressed => println!("mouse button {} pressed", button), - ElementState::Released => println!("mouse button {} released", button), - }, + Event::RawPointerEvent(_, event) => match event { + RawPointerEvent::MovedRelative(delta) => println!("pointer moved: {:?}", delta), + RawPointerEvent::MovedAbsolute(position) => { + println!("pointer moved to: {:?}", position) + } + RawPointerEvent::Button(e) if e.is_down() => { + println!("pointer button {:?} pressed", e.button()) + } + RawPointerEvent::Button(e) if e.is_up() => { + println!("pointer button {:?} released", e.button()) + } _ => (), }, _ => (), diff --git a/examples/custom_events.rs b/examples/custom_events.rs index c59299cab4..72787c0190 100644 --- a/examples/custom_events.rs +++ b/examples/custom_events.rs @@ -37,10 +37,7 @@ fn main() { match event { Event::UserEvent(event) => println!("user event: {:?}", event), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, _ => (), } }); diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 4cdb7b63ed..ef9afe076f 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,5 +1,5 @@ use std::io::{stdin, stdout, Write}; -use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; +use winit::event::{Event, LogicalKey, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; @@ -34,33 +34,25 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => match event { + Event::WindowEvent(_, event) => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state, - .. - }, - .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, - (VirtualKeyCode::F, ElementState::Pressed) => { + WindowEvent::Key(e) if e.is_down() => match e.logical_key() { + Some(LogicalKey::Escape) => *control_flow = ControlFlow::Exit, + Some(LogicalKey::F) => { if window.fullscreen().is_some() { window.set_fullscreen(None); } else { window.set_fullscreen(fullscreen.clone()); } } - (VirtualKeyCode::S, ElementState::Pressed) => { + Some(LogicalKey::S) => { println!("window.fullscreen {:?}", window.fullscreen()); } - (VirtualKeyCode::M, ElementState::Pressed) => { + Some(LogicalKey::M) => { is_maximized = !is_maximized; window.set_maximized(is_maximized); } - (VirtualKeyCode::D, ElementState::Pressed) => { + Some(LogicalKey::D) => { decorations = !decorations; window.set_decorations(decorations); } diff --git a/examples/handling_close.rs b/examples/handling_close.rs index cbd5705369..a6446e4a74 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,5 +1,5 @@ use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{Event, LogicalKey, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -16,67 +16,51 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{ - ElementState::Released, - VirtualKeyCode::{N, Y}, - }; *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CloseRequested => { - // `CloseRequested` is sent when the close button on the window is pressed (or - // through whatever other mechanisms the window manager provides for closing a - // window). If you don't handle this event, the close button won't actually do - // anything. + Event::WindowEvent(_, event) => match event { + WindowEvent::CloseRequested => { + // `CloseRequested` is sent when the close button on the window is pressed (or + // through whatever other mechanisms the window manager provides for closing a + // window). If you don't handle this event, the close button won't actually do + // anything. - // A common thing to do here is prompt the user if they have unsaved work. - // Creating a proper dialog box for that is far beyond the scope of this - // example, so here we'll just respond to the Y and N keys. - println!("Are you ready to bid your window farewell? [Y/N]"); - close_requested = true; + // A common thing to do here is prompt the user if they have unsaved work. + // Creating a proper dialog box for that is far beyond the scope of this + // example, so here we'll just respond to the Y and N keys. + println!("Are you ready to bid your window farewell? [Y/N]"); + close_requested = true; - // In applications where you can safely close the window without further - // action from the user, this is generally where you'd handle cleanup before - // closing the window. How to close the window is detailed in the handler for - // the Y key. - } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: Released, - .. - }, - .. - } => { - match virtual_code { - Y => { - if close_requested { - // This is where you'll want to do any cleanup you need. - println!("Buh-bye!"); + // In applications where you can safely close the window without further + // action from the user, this is generally where you'd handle cleanup before + // closing the window. How to close the window is detailed in the handler for + // the Y key. + } + WindowEvent::Key(e) if e.is_up() => match e.logical_key() { + Some(LogicalKey::Y) => { + if close_requested { + // This is where you'll want to do any cleanup you need. + println!("Buh-bye!"); - // For a single-window application like this, you'd normally just - // break out of the event loop here. If you wanted to keep running the - // event loop (i.e. if it's a multi-window application), you need to - // drop the window. That closes it, and results in `Destroyed` being - // sent. - *control_flow = ControlFlow::Exit; - } - } - N => { - if close_requested { - println!("Your window will continue to stay by your side."); - close_requested = false; - } - } - _ => (), + // For a single-window application like this, you'd normally just + // break out of the event loop here. If you wanted to keep running the + // event loop (i.e. if it's a multi-window application), you need to + // drop the window. That closes it, and results in `Destroyed` being + // sent. + *control_flow = ControlFlow::Exit; + } + } + Some(LogicalKey::N) => { + if close_requested { + println!("Your window will continue to stay by your side."); + close_requested = false; } } _ => (), - } - } + }, + _ => (), + }, _ => (), } }); diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index f4fd00c1bb..77e9d26301 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -19,10 +19,7 @@ fn main() { println!("{:?}", event); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, _ => (), } }); diff --git a/examples/minimize.rs b/examples/minimize.rs index 871cfe2edb..4808930998 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -1,6 +1,6 @@ extern crate winit; -use winit::event::{Event, VirtualKeyCode, WindowEvent}; +use winit::event::{Event, LogicalKey, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -17,22 +17,13 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, // Keyboard input event to handle minimize via a hotkey - Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, - window_id, - } => { - if window_id == window.id() { - // Pressing the 'M' key will minimize the window - if input.virtual_keycode == Some(VirtualKeyCode::M) { - window.set_minimized(true); - } - } + Event::WindowEvent(window_id, WindowEvent::Key(e)) + if e.is_down() && e.logical_key_is(LogicalKey::M) && window_id == window.id() => + { + window.set_minimized(true) } _ => (), } diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 60f9d802de..8402bbedc7 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -4,7 +4,7 @@ fn main() { use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; @@ -23,6 +23,7 @@ fn main() { let mut video_modes: Vec<_> = window.current_monitor().video_modes().collect(); let mut video_mode_id = 0usize; + let mut modifiers = ModifiersState::empty(); let (tx, rx) = mpsc::channel(); window_senders.insert(window.id(), tx); @@ -48,97 +49,90 @@ fn main() { ); } } - #[allow(deprecated)] - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), - modifiers, - .. - }, - .. - } => { - window.set_title(&format!("{:?}", key)); - let state = !modifiers.shift(); - use VirtualKeyCode::*; - match key { - A => window.set_always_on_top(state), - C => window.set_cursor_icon(match state { - true => CursorIcon::Progress, - false => CursorIcon::Default, - }), - D => window.set_decorations(!state), - // Cycle through video modes - Right | Left => { - video_mode_id = match key { - Left => video_mode_id.saturating_sub(1), - Right => (video_modes.len() - 1).min(video_mode_id + 1), - _ => unreachable!(), - }; - println!( - "Picking video mode: {}", - video_modes.iter().nth(video_mode_id).unwrap() - ); - } - F => window.set_fullscreen(match (state, modifiers.alt()) { - (true, false) => { - Some(Fullscreen::Borderless(window.current_monitor())) + WindowEvent::ModifiersChanged(mods) => modifiers = mods, + WindowEvent::Key(e) if e.is_up() => { + if let Some(key) = e.logical_key() { + window.set_title(&format!("{:?}", key)); + let state = !modifiers.shift(); + use winit::event::LogicalKey::*; + match key { + A => window.set_always_on_top(state), + C => window.set_cursor_icon(match state { + true => CursorIcon::Progress, + false => CursorIcon::Default, + }), + D => window.set_decorations(!state), + // Cycle through video modes + Right | Left => { + video_mode_id = match key { + Left => video_mode_id.saturating_sub(1), + Right => (video_modes.len() - 1).min(video_mode_id + 1), + _ => unreachable!(), + }; + println!( + "Picking video mode: {}", + video_modes.iter().nth(video_mode_id).unwrap() + ); } - (true, true) => Some(Fullscreen::Exclusive( - video_modes.iter().nth(video_mode_id).unwrap().clone(), - )), - (false, _) => None, - }), - G => window.set_cursor_grab(state).unwrap(), - H => window.set_cursor_visible(!state), - I => { - println!("Info:"); - println!("-> outer_position : {:?}", window.outer_position()); - println!("-> inner_position : {:?}", window.inner_position()); - println!("-> outer_size : {:?}", window.outer_size()); - println!("-> inner_size : {:?}", window.inner_size()); - println!("-> fullscreen : {:?}", window.fullscreen()); - } - L => window.set_min_inner_size(match state { - true => Some(WINDOW_SIZE), - false => None, - }), - M => window.set_maximized(state), - P => window.set_outer_position({ - let mut position = window.outer_position().unwrap(); - let sign = if state { 1 } else { -1 }; - position.x += 10 * sign; - position.y += 10 * sign; - position - }), - Q => window.request_redraw(), - R => window.set_resizable(state), - S => window.set_inner_size(match state { - true => PhysicalSize::new( - WINDOW_SIZE.width + 100, - WINDOW_SIZE.height + 100, - ), - false => WINDOW_SIZE, - }), - W => { - if let Size::Physical(size) = WINDOW_SIZE.into() { - window - .set_cursor_position(Position::Physical( - PhysicalPosition::new( - size.width as i32 / 2, - size.height as i32 / 2, - ), - )) - .unwrap() + F => window.set_fullscreen(match (state, modifiers.alt()) { + (true, false) => { + Some(Fullscreen::Borderless(window.current_monitor())) + } + (true, true) => Some(Fullscreen::Exclusive( + video_modes.iter().nth(video_mode_id).unwrap().clone(), + )), + (false, _) => None, + }), + G => window.set_cursor_grab(state).unwrap(), + H => window.set_cursor_visible(!state), + I => { + println!("Info:"); + println!("-> outer_position : {:?}", window.outer_position()); + println!("-> inner_position : {:?}", window.inner_position()); + println!("-> outer_size : {:?}", window.outer_size()); + println!("-> inner_size : {:?}", window.inner_size()); + println!("-> fullscreen : {:?}", window.fullscreen()); } + L => window.set_min_inner_size(match state { + true => Some(WINDOW_SIZE), + false => None, + }), + M => window.set_maximized(state), + P => window.set_outer_position({ + let mut position = window.outer_position().unwrap(); + let sign = if state { 1 } else { -1 }; + position.x += 10 * sign; + position.y += 10 * sign; + position + }), + Q => window.request_redraw(), + R => window.set_resizable(state), + S => window.set_inner_size(match state { + true => PhysicalSize::new( + WINDOW_SIZE.width + 100, + WINDOW_SIZE.height + 100, + ), + false => WINDOW_SIZE, + }), + W => { + if let Size::Physical(size) = WINDOW_SIZE.into() { + window + .set_cursor_position(Position::Physical( + PhysicalPosition::new( + size.width as i32 / 2, + size.height as i32 / 2, + ), + )) + .unwrap() + } + } + Z => { + window.set_visible(false); + thread::sleep(Duration::from_secs(1)); + window.set_visible(true); + } + _ => (), } - Z => { - window.set_visible(false); - thread::sleep(Duration::from_secs(1)); - window.set_visible(true); - } - _ => (), } } _ => (), @@ -152,23 +146,13 @@ fn main() { false => ControlFlow::Exit, }; match event { - Event::WindowEvent { event, window_id } => match event { - WindowEvent::CloseRequested - | WindowEvent::Destroyed - | WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - }, - .. - } => { + Event::WindowEvent(window_id, event) => match event { + WindowEvent::CloseRequested | WindowEvent::Destroyed => { window_senders.remove(&window_id); } _ => { if let Some(tx) = window_senders.get(&window_id) { - if let Some(event) = event.to_static() { + if let Ok(event) = event.to_static() { tx.send(event).unwrap(); } } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 01c91bb646..9e570b8de0 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use winit::{ - event::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; @@ -19,7 +19,7 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, window_id } => { + Event::WindowEvent(window_id, event) => { match event { WindowEvent::CloseRequested => { println!("Window {:?} has received the signal to close", window_id); @@ -31,14 +31,7 @@ fn main() { *control_flow = ControlFlow::Exit; } } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Pressed, - .. - }, - .. - } => { + WindowEvent::Key(e) if e.is_down() => { let window = Window::new(&event_loop).unwrap(); windows.insert(window.id(), window); } diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs index 5d761ae479..8036a15153 100644 --- a/examples/request_redraw.rs +++ b/examples/request_redraw.rs @@ -1,5 +1,5 @@ use winit::{ - event::{ElementState, Event, WindowEvent}, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -19,14 +19,9 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => match event { + Event::WindowEvent(_, event) => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::MouseInput { - state: ElementState::Released, - .. - } => { - window.request_redraw(); - } + WindowEvent::PointerButton(_, e) if e.is_up() => window.request_redraw(), _ => (), }, Event::RedrawRequested(_) => { diff --git a/examples/request_redraw_threaded.rs b/examples/request_redraw_threaded.rs index 341612ca64..237788ce8d 100644 --- a/examples/request_redraw_threaded.rs +++ b/examples/request_redraw_threaded.rs @@ -26,10 +26,7 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => (), - }, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, Event::RedrawRequested(_) => { println!("\nredrawing!\n"); } diff --git a/examples/resizable.rs b/examples/resizable.rs index 6a90391937..0269291ef1 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,6 +1,6 @@ use winit::{ dpi::LogicalSize, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, LogicalKey, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -22,17 +22,9 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::WindowEvent { event, .. } => match event { + Event::WindowEvent(_, event) => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Space), - state: ElementState::Released, - .. - }, - .. - } => { + WindowEvent::Key(e) if e.is_up() && e.logical_key_is(LogicalKey::Space) => { resizable = !resizable; println!("Resizable: {}", resizable); window.set_resizable(resizable); diff --git a/examples/timer.rs b/examples/timer.rs index 52a1444ec2..4e61751aba 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -28,10 +28,7 @@ fn main() { *control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length); println!("\nTimer\n"); } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, _ => (), } }); diff --git a/examples/transparent.rs b/examples/transparent.rs index 78e3c4da33..093c7f612f 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -21,10 +21,7 @@ fn main() { println!("{:?}", event); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent(_, WindowEvent::CloseRequested) => *control_flow = ControlFlow::Exit, _ => (), } }); diff --git a/examples/web.rs b/examples/web.rs index 46b024ccf2..5d425d0653 100644 --- a/examples/web.rs +++ b/examples/web.rs @@ -49,10 +49,11 @@ pub fn main() { std_web::console!(log, "%s", format!("{:?}", event)); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id, - } if window_id == window.id() => *control_flow = ControlFlow::Exit, + Event::WindowEvent(window_id, WindowEvent::CloseRequested) + if window_id == window.id() => + { + *control_flow = ControlFlow::Exit + } Event::MainEventsCleared => { window.request_redraw(); } diff --git a/examples/window.rs b/examples/window.rs index c028a50f3f..c8cf4a8859 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -16,16 +16,34 @@ fn main() { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - println!("{:?}", event); + // println!("{:?}", event); match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id, - } if window_id == window.id() => *control_flow = ControlFlow::Exit, - Event::MainEventsCleared => { - window.request_redraw(); + Event::WindowEvent(window_id, WindowEvent::CloseRequested) + if window_id == window.id() => + { + *control_flow = ControlFlow::Exit } + Event::WindowEvent(_, e) => match e { + WindowEvent::PointerCreated(..) | + WindowEvent::PointerForce(..) | + WindowEvent::PointerTilt(..) | + WindowEvent::PointerTwist(..) | + WindowEvent::PointerContactArea(..) | + WindowEvent::PointerMoved(..) | + WindowEvent::PointerButton(..) | + WindowEvent::PointerEntered(..) | + WindowEvent::PointerLeft(..) | + WindowEvent::ScrollStarted | + WindowEvent::ScrollLines(..) | + WindowEvent::ScrollPixels(..) | + WindowEvent::ScrollEnded | + WindowEvent::PointerDestroyed(..) => println!("{:?}", e), + _ => () + }, + Event::MainEventsCleared => { + window.request_redraw() + }, _ => (), } }); diff --git a/examples/window_debug.rs b/examples/window_debug.rs index f6e960a794..49ce011927 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -2,7 +2,7 @@ use winit::{ dpi::{LogicalSize, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, LogicalKey, RawKeyboardEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Fullscreen, WindowBuilder}, }; @@ -33,84 +33,69 @@ fn main() { *control_flow = ControlFlow::Wait; match event { - Event::DeviceEvent { - event: - DeviceEvent::Key(KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, - .. - }), - .. - } => match key { - VirtualKeyCode::M => { - if minimized { - minimized = !minimized; - window.set_minimized(minimized); - } - } - VirtualKeyCode::V => { - if !visible { - visible = !visible; - window.set_visible(visible); - } - } - _ => (), - }, - Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, - .. - } => match input { - KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, - .. - } => match key { - VirtualKeyCode::E => { - fn area(size: PhysicalSize) -> u32 { - size.width * size.height - } - - let monitor = window.current_monitor(); - if let Some(mode) = monitor - .video_modes() - .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) - { - window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); - } else { - eprintln!("no video modes available"); + Event::RawKeyboardEvent(_, RawKeyboardEvent::Key(e)) if e.is_down() => { + match e.logical_key() { + Some(LogicalKey::M) => { + if minimized { + minimized = !minimized; + window.set_minimized(minimized); } } - VirtualKeyCode::F => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - let monitor = window.current_monitor(); - window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); + Some(LogicalKey::V) => { + if !visible { + visible = !visible; + window.set_visible(visible); } } - VirtualKeyCode::M => { - minimized = !minimized; - window.set_minimized(minimized); - } - VirtualKeyCode::Q => { - *control_flow = ControlFlow::Exit; + _ => (), + } + } + Event::WindowEvent(_, WindowEvent::Key(e)) if e.is_down() => match e.logical_key() { + Some(LogicalKey::E) => { + fn area(size: PhysicalSize) -> u32 { + size.width * size.height } - VirtualKeyCode::V => { - visible = !visible; - window.set_visible(visible); + + let monitor = window.current_monitor(); + if let Some(mode) = monitor + .video_modes() + .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) + { + window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); + } else { + eprintln!("no video modes available"); } - VirtualKeyCode::X => { - maximized = !maximized; - window.set_maximized(maximized); + } + Some(LogicalKey::F) => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + let monitor = window.current_monitor(); + window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); } - _ => (), - }, + } + Some(LogicalKey::M) => { + minimized = !minimized; + window.set_minimized(minimized); + } + Some(LogicalKey::Q) => { + *control_flow = ControlFlow::Exit; + } + Some(LogicalKey::V) => { + visible = !visible; + window.set_visible(visible); + } + Some(LogicalKey::X) => { + maximized = !maximized; + window.set_maximized(maximized); + } _ => (), }, - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id, - } if window_id == window.id() => *control_flow = ControlFlow::Exit, + Event::WindowEvent(window_id, WindowEvent::CloseRequested) + if window_id == window.id() => + { + *control_flow = ControlFlow::Exit + } _ => (), } }); diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 734debf4d4..07e1545c9c 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -1,7 +1,7 @@ extern crate image; use std::path::Path; use winit::{ - event::Event, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Icon, WindowBuilder}, }; @@ -30,15 +30,13 @@ fn main() { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - if let Event::WindowEvent { event, .. } = event { - use winit::event::WindowEvent::*; - match event { - CloseRequested => *control_flow = ControlFlow::Exit, - DroppedFile(path) => { - window.set_window_icon(Some(load_icon(&path))); - } + match event { + Event::WindowEvent(_, event) => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::FileDropped(path) => window.set_window_icon(Some(load_icon(&path))), _ => (), - } + }, + _ => (), } }); } diff --git a/examples/window_run_return.rs b/examples/window_run_return.rs index 0112eb3e5b..6c17112ff5 100644 --- a/examples/window_run_return.rs +++ b/examples/window_run_return.rs @@ -30,16 +30,13 @@ fn main() { event_loop.run_return(|event, _, control_flow| { *control_flow = ControlFlow::Wait; - if let Event::WindowEvent { event, .. } = &event { + if let Event::WindowEvent(_, event) = &event { // Print only Window events to reduce noise println!("{:?}", event); } match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { + Event::WindowEvent(_, WindowEvent::CloseRequested) => { quit = true; } Event::MainEventsCleared => { diff --git a/src/dpi.rs b/src/dpi.rs index 8a56ae6e06..2d3bbf9ee5 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -155,347 +155,238 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool { scale_factor.is_sign_positive() && scale_factor.is_normal() } -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalPosition

{ - pub x: P, - pub y: P, -} - -impl

LogicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - LogicalPosition { x, y } - } -} - -impl LogicalPosition

{ - #[inline] - pub fn from_physical>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() * scale_factor; - let y = self.y.into() * scale_factor; - PhysicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalPosition { - LogicalPosition { - x: self.x.cast(), - y: self.y.cast(), +macro_rules! dpi_type { + ( + let h = $h:ident; + let v = $v:ident; + + $(#[$logical_meta:meta])* + pub struct $LogicalType:ident; + $(#[$physical_meta:meta])* + pub struct $PhysicalType:ident; + $(#[$unified_meta:meta])* + pub enum $UnifiedType:ident; + ) => { + $(#[$logical_meta])* + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $LogicalType

{ + pub $h: P, + pub $v: P, } - } -} - -impl From<(X, X)> for LogicalPosition

{ - fn from((x, y): (X, X)) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for LogicalPosition

{ - fn into(self: Self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} - -impl From<[X; 2]> for LogicalPosition

{ - fn from([x, y]: [X; 2]) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for LogicalPosition

{ - fn into(self: Self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A position represented in physical pixels. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalPosition

{ - pub x: P, - pub y: P, -} - -impl

PhysicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - PhysicalPosition { x, y } - } -} - -impl PhysicalPosition

{ - #[inline] - pub fn from_logical>, X: Pixel>( - logical: T, - scale_factor: f64, - ) -> Self { - logical.into().to_physical(scale_factor) - } - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() / scale_factor; - let y = self.y.into() / scale_factor; - LogicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalPosition { - PhysicalPosition { - x: self.x.cast(), - y: self.y.cast(), + impl

$LogicalType

{ + #[inline] + pub const fn new($h: P, $v: P) -> Self { + $LogicalType { $h, $v } + } } - } -} - -impl From<(X, X)> for PhysicalPosition

{ - fn from((x, y): (X, X)) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for PhysicalPosition

{ - fn into(self: Self) -> (X, X) { - (self.x.cast(), self.y.cast()) - } -} - -impl From<[X; 2]> for PhysicalPosition

{ - fn from([x, y]: [X; 2]) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl Into<[X; 2]> for PhysicalPosition

{ - fn into(self: Self) -> [X; 2] { - [self.x.cast(), self.y.cast()] - } -} - -/// A size represented in logical pixels. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalSize

{ - pub width: P, - pub height: P, -} - -impl

LogicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - LogicalSize { width, height } - } -} - -impl LogicalSize

{ - #[inline] - pub fn from_physical>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() * scale_factor; - let height = self.height.into() * scale_factor; - PhysicalSize::new(width, height).cast() - } - #[inline] - pub fn cast(&self) -> LogicalSize { - LogicalSize { - width: self.width.cast(), - height: self.height.cast(), + impl $LogicalType

{ + #[inline] + pub fn from_physical>, X: Pixel>( + physical: T, + scale_factor: f64, + ) -> Self { + physical.into().to_logical(scale_factor) + } + + #[inline] + pub fn to_physical(&self, scale_factor: f64) -> $PhysicalType { + assert!(validate_scale_factor(scale_factor)); + let $h = self.$h.into() * scale_factor; + let $v = self.$v.into() * scale_factor; + $PhysicalType::new($h, $v).cast() + } + + #[inline] + pub fn cast(&self) -> $LogicalType { + $LogicalType { + $h: self.$h.cast(), + $v: self.$v.cast(), + } + } } - } -} -impl From<(X, X)> for LogicalSize

{ - fn from((x, y): (X, X)) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} - -impl Into<(X, X)> for LogicalSize

{ - fn into(self: LogicalSize

) -> (X, X) { - (self.width.cast(), self.height.cast()) - } -} - -impl From<[X; 2]> for LogicalSize

{ - fn from([x, y]: [X; 2]) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} + impl From<(X, X)> for $LogicalType

{ + fn from(($h, $v): (X, X)) -> $LogicalType

{ + $LogicalType::new($h.cast(), $v.cast()) + } + } -impl Into<[X; 2]> for LogicalSize

{ - fn into(self: Self) -> [X; 2] { - [self.width.cast(), self.height.cast()] - } -} + impl Into<(X, X)> for $LogicalType

{ + fn into(self: Self) -> (X, X) { + (self.$h.cast(), self.$v.cast()) + } + } -/// A size represented in physical pixels. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalSize

{ - pub width: P, - pub height: P, -} + impl From<[X; 2]> for $LogicalType

{ + fn from([$h, $v]: [X; 2]) -> $LogicalType

{ + $LogicalType::new($h.cast(), $v.cast()) + } + } -impl

PhysicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - PhysicalSize { width, height } - } -} + impl Into<[X; 2]> for $LogicalType

{ + fn into(self: Self) -> [X; 2] { + [self.$h.cast(), self.$v.cast()] + } + } -impl PhysicalSize

{ - #[inline] - pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } + $(#[$physical_meta])* + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $PhysicalType

{ + pub $h: P, + pub $v: P, + } - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() / scale_factor; - let height = self.height.into() / scale_factor; - LogicalSize::new(width, height).cast() - } + impl

$PhysicalType

{ + #[inline] + pub const fn new($h: P, $v: P) -> Self { + $PhysicalType { $h, $v } + } + } - #[inline] - pub fn cast(&self) -> PhysicalSize { - PhysicalSize { - width: self.width.cast(), - height: self.height.cast(), + impl $PhysicalType

{ + #[inline] + pub fn from_logical>, X: Pixel>( + logical: T, + scale_factor: f64, + ) -> Self { + logical.into().to_physical(scale_factor) + } + + #[inline] + pub fn to_logical(&self, scale_factor: f64) -> $LogicalType { + assert!(validate_scale_factor(scale_factor)); + let $h = self.$h.into() / scale_factor; + let $v = self.$v.into() / scale_factor; + $LogicalType::new($h, $v).cast() + } + + #[inline] + pub fn cast(&self) -> $PhysicalType { + $PhysicalType { + $h: self.$h.cast(), + $v: self.$v.cast(), + } + } } - } -} -impl From<(X, X)> for PhysicalSize

{ - fn from((x, y): (X, X)) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} + impl From<(X, X)> for $PhysicalType

{ + fn from(($h, $v): (X, X)) -> $PhysicalType

{ + $PhysicalType::new($h.cast(), $v.cast()) + } + } -impl Into<(X, X)> for PhysicalSize

{ - fn into(self: Self) -> (X, X) { - (self.width.cast(), self.height.cast()) - } -} + impl Into<(X, X)> for $PhysicalType

{ + fn into(self: Self) -> (X, X) { + (self.$h.cast(), self.$v.cast()) + } + } -impl From<[X; 2]> for PhysicalSize

{ - fn from([x, y]: [X; 2]) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} + impl From<[X; 2]> for $PhysicalType

{ + fn from([$h, $v]: [X; 2]) -> $PhysicalType

{ + $PhysicalType::new($h.cast(), $v.cast()) + } + } -impl Into<[X; 2]> for PhysicalSize

{ - fn into(self: Self) -> [X; 2] { - [self.width.cast(), self.height.cast()] - } -} + impl Into<[X; 2]> for $PhysicalType

{ + fn into(self: Self) -> [X; 2] { + [self.$h.cast(), self.$v.cast()] + } + } -/// A size that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Size { - Physical(PhysicalSize), - Logical(LogicalSize), -} + $(#[$unified_meta])* + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum $UnifiedType { + Physical($PhysicalType), + Logical($LogicalType), + } -impl Size { - pub fn new>(size: S) -> Size { - size.into() - } + impl $UnifiedType { + pub fn new>(val: S) -> $UnifiedType { + val.into() + } + + pub fn to_logical(&self, scale_factor: f64) -> $LogicalType

{ + match *self { + $UnifiedType::Physical(val) => val.to_logical(scale_factor), + $UnifiedType::Logical(val) => val.cast(), + } + } + + pub fn to_physical(&self, scale_factor: f64) -> $PhysicalType

{ + match *self { + $UnifiedType::Physical(val) => val.cast(), + $UnifiedType::Logical(val) => val.to_physical(scale_factor), + } + } + } - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize

{ - match *self { - Size::Physical(size) => size.to_logical(scale_factor), - Size::Logical(size) => size.cast(), + impl From<$PhysicalType

> for $UnifiedType { + #[inline] + fn from(val: $PhysicalType

) -> $UnifiedType { + $UnifiedType::Physical(val.cast()) + } } - } - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize

{ - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(scale_factor), + impl From<$LogicalType

> for $UnifiedType { + #[inline] + fn from(val: $LogicalType

) -> $UnifiedType { + $UnifiedType::Logical(val.cast()) + } } - } + }; } -impl From> for Size { - #[inline] - fn from(size: PhysicalSize

) -> Size { - Size::Physical(size.cast()) - } -} +dpi_type! { + let h = x; + let v = y; -impl From> for Size { - #[inline] - fn from(size: LogicalSize

) -> Size { - Size::Logical(size.cast()) - } + /// A position represented in logical pixels. + pub struct LogicalPosition; + /// A position represented in physical pixels. + pub struct PhysicalPosition; + /// A position that's either physical or logical. + pub enum Position; } -/// A position that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Position { - Physical(PhysicalPosition), - Logical(LogicalPosition), -} +dpi_type! { + let h = width; + let v = height; -impl Position { - pub fn new>(position: S) -> Position { - position.into() - } + /// A size represented in logical pixels. + pub struct LogicalSize; + /// A size represented in physical pixels. + pub struct PhysicalSize; + /// A size that's either physical or logical. + pub enum Size; +} - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ - match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), - } - } +dpi_type! { + let h = x; + let v = y; - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), - } - } + /// A delta represented in logical pixels. + pub struct LogicalDelta; + /// A delta represented in physical pixels. + pub struct PhysicalDelta; + /// A delta that's either physical or logical. + pub enum Delta; } -impl From> for Position { - #[inline] - fn from(position: PhysicalPosition

) -> Position { - Position::Physical(position.cast()) - } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct UnitlessDelta { + pub x: T, + pub y: T, } -impl From> for Position { +impl UnitlessDelta { #[inline] - fn from(position: LogicalPosition

) -> Position { - Position::Logical(position.cast()) + pub const fn new(x: T, y: T) -> Self { + UnitlessDelta { x, y } } } diff --git a/src/event.rs b/src/event.rs index 47119fa807..f9bfa7202a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -34,10 +34,10 @@ //! //! [event_loop_run]: crate::event_loop::EventLoop::run use instant::Instant; -use std::path::PathBuf; +use std::{fmt, path::PathBuf}; use crate::{ - dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}, + dpi::{PhysicalDelta, PhysicalPosition, PhysicalSize, UnitlessDelta}, platform_impl, window::{Theme, WindowId}, }; @@ -55,27 +55,17 @@ pub enum Event<'a, T: 'static> { /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. NewEvents(StartCause), + AppEvent(AppEvent), + /// Emitted when the OS sends an event to a winit window. - WindowEvent { - window_id: WindowId, - event: WindowEvent<'a>, - }, + WindowEvent(WindowId, WindowEvent<'a>), - /// Emitted when the OS sends an event to a device. - DeviceEvent { - device_id: DeviceId, - event: DeviceEvent, - }, + RawPointerEvent(PointerDeviceId, RawPointerEvent), + RawKeyboardEvent(KeyboardDeviceId, RawKeyboardEvent), /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) UserEvent(T), - /// Emitted when the application has been suspended. - Suspended, - - /// Emitted when the application has been resumed. - Resumed, - /// Emitted when all of the event loop's input events have been processed and redraw processing /// is about to begin. /// @@ -118,28 +108,141 @@ pub enum Event<'a, T: 'static> { LoopDestroyed, } -impl Clone for Event<'static, T> { - fn clone(&self) -> Self { - use self::Event::*; - match self { - WindowEvent { window_id, event } => WindowEvent { - window_id: *window_id, - event: event.clone(), - }, - UserEvent(event) => UserEvent(event.clone()), - DeviceEvent { device_id, event } => DeviceEvent { - device_id: *device_id, - event: event.clone(), - }, - NewEvents(cause) => NewEvents(cause.clone()), - MainEventsCleared => MainEventsCleared, - RedrawRequested(wid) => RedrawRequested(*wid), - RedrawEventsCleared => RedrawEventsCleared, - LoopDestroyed => LoopDestroyed, - Suspended => Suspended, - Resumed => Resumed, - } - } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AppEvent { + /// Emitted when the application has been suspended. + Suspended, + + /// Emitted when the application has been resumed. + Resumed, +} + +/// Describes an event from a `Window`. +#[derive(Debug, PartialEq)] +pub enum WindowEvent<'a> { + /// The size of the window has changed. Contains the client area's new dimensions. + Resized(PhysicalSize), + + /// The position of the window has changed. Contains the window's new position. + Moved(PhysicalPosition), + + /// The window has been requested to close. + CloseRequested, + + /// The window has been destroyed. + Destroyed, + + /// A file is being hovered over the window. + /// + /// When the user hovers multiple files at once, this event will be emitted for each file + /// separately. + FileHovered(PathBuf), + + /// A file has been dropped into the window. + /// + /// When the user drops multiple files at once, this event will be emitted for each file + /// separately. + FileDropped(PathBuf), + + /// A file was hovered, but has exited the window. + /// + /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were + /// hovered. + FileHoverCancelled, + + /// The window gained focus. + FocusGained, + + /// The window lost focus. + FocusLost, + + Key(KeyEvent), + + /// The window received a unicode character. + CharReceived(char), + + /// The keyboard modifiers have changed. + /// + /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from + /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere. + /// + /// Platform-specific behavior: + /// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an + /// issue, and it should get fixed - but it's the current state of the API. + ModifiersChanged(ModifiersState), + + PointerCreated(PointerId), + PointerEntered(PointerId), + PointerForce(PointerId, Force), + PointerTilt(PointerId, PointerTiltEvent), + PointerTwist(PointerId, f64), + PointerContactArea(PointerId, PhysicalSize), + PointerMoved(PointerId, PhysicalPosition), + // TODO: IMPLEMENT. IS THIS REASONABLE TO IMPLEMENT ON NON-WINDOWS PLATFORMS? + // PointerHovered(PointerId), + PointerButton(PointerId, PointerButtonEvent), + PointerDestroyed(PointerId), + PointerLeft(PointerId), + + ScrollStarted, + ScrollLines(UnitlessDelta), + ScrollPixels(PhysicalDelta), + ScrollEnded, + + /// The system window theme has changed. + /// + /// Applications might wish to react to this to change the theme of the content of the window + /// when the system changes the window theme. + /// + /// At the moment this is only supported on Windows. + ThemeChanged(Theme), + + /// The window's scale factor has changed. + /// + /// The following user actions can cause DPI changes: + /// + /// * Changing the display's resolution. + /// * Changing the display's scale factor (e.g. in Control Panel on Windows). + /// * Moving the window to a display with a different scale factor. + /// + /// After this event callback has been processed, the window will be resized to whatever value + /// is pointed to by the `PhysicalSize` reference. By default, this will contain the size suggested + /// by the OS, but it can be changed to any value. + /// + /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. + ScaleFactorChanged(f64, &'a mut PhysicalSize), +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum RawPointerEvent { + /// A device has been added. + Added, + /// A device has been removed. + Removed, + Button(RawPointerButtonEvent), + /// Relative change in physical position of a pointing device. + /// + /// This represents raw, unfiltered physical motion, NOT the position of the mouse. Accordingly, + /// the values provided here are the change in position of the mouse since the previous + /// `MovedRelative` event. + MovedRelative(PhysicalDelta), + /// Change in absolute position of a pointing device. + /// + /// The `PhysicalPosition` value is the new position of the cursor relative to the desktop. This + /// generally doesn't get output by standard mouse devices, but can get output from tablet devices. + MovedAbsolute(PhysicalPosition), + /// Change in rotation of mouse wheel. + Wheel(UnitlessDelta), +} + +/// Raw keyboard events. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum RawKeyboardEvent { + /// A keyboard device has been added. + Added, + /// A keyboard device has been removed. + Removed, + Key(RawKeyEvent), } impl<'a, T> Event<'a, T> { @@ -147,35 +250,110 @@ impl<'a, T> Event<'a, T> { use self::Event::*; match self { UserEvent(_) => Err(self), - WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }), - DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }), + WindowEvent(window_id, event) => Ok(WindowEvent(window_id, event)), + RawPointerEvent(pointer_id, event) => Ok(RawPointerEvent(pointer_id, event)), + RawKeyboardEvent(keyboard_id, event) => Ok(RawKeyboardEvent(keyboard_id, event)), + AppEvent(app_event) => Ok(AppEvent(app_event)), NewEvents(cause) => Ok(NewEvents(cause)), MainEventsCleared => Ok(MainEventsCleared), RedrawRequested(wid) => Ok(RedrawRequested(wid)), RedrawEventsCleared => Ok(RedrawEventsCleared), LoopDestroyed => Ok(LoopDestroyed), - Suspended => Ok(Suspended), - Resumed => Ok(Resumed), } } /// If the event doesn't contain a reference, turn it into an event with a `'static` lifetime. /// Otherwise, return `None`. - pub fn to_static(self) -> Option> { + pub fn to_static(self) -> Result, Event<'a, T>> { use self::Event::*; match self { - WindowEvent { window_id, event } => event + NewEvents(cause) => Ok(NewEvents(cause)), + WindowEvent(window_id, event) => event .to_static() - .map(|event| WindowEvent { window_id, event }), - UserEvent(event) => Some(UserEvent(event)), - DeviceEvent { device_id, event } => Some(DeviceEvent { device_id, event }), - NewEvents(cause) => Some(NewEvents(cause)), - MainEventsCleared => Some(MainEventsCleared), - RedrawRequested(wid) => Some(RedrawRequested(wid)), - RedrawEventsCleared => Some(RedrawEventsCleared), - LoopDestroyed => Some(LoopDestroyed), - Suspended => Some(Suspended), - Resumed => Some(Resumed), + .map(|e| -> Event<'static, T> { WindowEvent(window_id, e) }) + .map_err(|e| -> Event<'a, T> { WindowEvent(window_id, e) }), + RawPointerEvent(pointer_id, event) => Ok(RawPointerEvent(pointer_id, event)), + RawKeyboardEvent(keyboard_id, event) => Ok(RawKeyboardEvent(keyboard_id, event)), + AppEvent(app_event) => Ok(AppEvent(app_event)), + UserEvent(e) => Ok(UserEvent(e)), + MainEventsCleared => Ok(MainEventsCleared), + RedrawRequested(wid) => Ok(RedrawRequested(wid)), + RedrawEventsCleared => Ok(RedrawEventsCleared), + LoopDestroyed => Ok(LoopDestroyed), + } + } +} + +impl Clone for WindowEvent<'static> { + fn clone(&self) -> Self { + use self::WindowEvent::*; + match *self { + Resized(size) => Resized(size), + Moved(position) => Moved(position), + CloseRequested => CloseRequested, + Destroyed => Destroyed, + FileDropped(ref path) => FileDropped(path.clone()), + FileHovered(ref path) => FileHovered(path.clone()), + FileHoverCancelled => FileHoverCancelled, + FocusGained => FocusGained, + FocusLost => FocusLost, + CharReceived(char) => CharReceived(char), + Key(key_press) => Key(key_press), + ModifiersChanged(state) => ModifiersChanged(state), + PointerCreated(id) => PointerCreated(id), + PointerTilt(id, tilt) => PointerTilt(id, tilt), + PointerTwist(id, twist) => PointerTwist(id, twist), + PointerForce(id, force) => PointerForce(id, force), + PointerContactArea(id, contact_area) => PointerContactArea(id, contact_area), + PointerMoved(id, position) => PointerMoved(id, position), + PointerButton(id, pointer_button) => PointerButton(id, pointer_button), + PointerEntered(id) => PointerEntered(id), + PointerLeft(id) => PointerLeft(id), + PointerDestroyed(id) => PointerDestroyed(id), + ScrollStarted => ScrollStarted, + ScrollLines(delta) => ScrollLines(delta), + ScrollPixels(delta) => ScrollPixels(delta), + ScrollEnded => ScrollEnded, + ThemeChanged(theme) => ThemeChanged(theme), + ScaleFactorChanged(..) => { + unreachable!("Static event can't be about scale factor changing") + } + } + } +} + +impl<'a> WindowEvent<'a> { + pub fn to_static(self) -> Result, WindowEvent<'a>> { + use self::WindowEvent::*; + match self { + Resized(size) => Ok(Resized(size)), + Moved(position) => Ok(Moved(position)), + CloseRequested => Ok(CloseRequested), + Destroyed => Ok(Destroyed), + FileDropped(path) => Ok(FileDropped(path)), + FileHovered(path) => Ok(FileHovered(path)), + FileHoverCancelled => Ok(FileHoverCancelled), + FocusGained => Ok(FocusGained), + FocusLost => Ok(FocusLost), + CharReceived(char) => Ok(CharReceived(char)), + Key(key_press) => Ok(Key(key_press)), + ModifiersChanged(state) => Ok(ModifiersChanged(state)), + PointerCreated(id) => Ok(PointerCreated(id)), + PointerTilt(id, tilt) => Ok(PointerTilt(id, tilt)), + PointerTwist(id, twist) => Ok(PointerTwist(id, twist)), + PointerForce(id, force) => Ok(PointerForce(id, force)), + PointerContactArea(id, contact_area) => Ok(PointerContactArea(id, contact_area)), + PointerMoved(id, position) => Ok(PointerMoved(id, position)), + PointerButton(id, pointer_button) => Ok(PointerButton(id, pointer_button)), + PointerEntered(id) => Ok(PointerEntered(id)), + PointerLeft(id) => Ok(PointerLeft(id)), + PointerDestroyed(id) => Ok(PointerDestroyed(id)), + ScrollStarted => Ok(ScrollStarted), + ScrollLines(delta) => Ok(ScrollLines(delta)), + ScrollPixels(delta) => Ok(ScrollPixels(delta)), + ScrollEnded => Ok(ScrollEnded), + ThemeChanged(theme) => Ok(ThemeChanged(theme)), + ScaleFactorChanged(..) => Err(self), } } } @@ -206,463 +384,453 @@ pub enum StartCause { Init, } -/// Describes an event from a `Window`. -#[derive(Debug, PartialEq)] -pub enum WindowEvent<'a> { - /// The size of the window has changed. Contains the client area's new dimensions. - Resized(PhysicalSize), +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct KeyEvent { + pub(crate) logical_key: Option, + pub(crate) scan_code: u32, + pub(crate) is_down: bool, + pub(crate) is_repeat: bool, + pub(crate) is_synthetic: bool, +} - /// The position of the window has changed. Contains the window's new position. - Moved(PhysicalPosition), +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct RawKeyEvent { + pub(crate) logical_key: Option, + pub(crate) scan_code: u32, + pub(crate) is_down: bool, +} - /// The window has been requested to close. - CloseRequested, +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct PointerButtonEvent { + pub(crate) button: PointerButton, + pub(crate) is_down: bool, + pub(crate) multi_click_count: u32, +} - /// The window has been destroyed. - Destroyed, +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct RawPointerButtonEvent { + pub(crate) button: PointerButton, + pub(crate) is_down: bool, +} - /// A file has been dropped into the window. - /// - /// When the user drops multiple files at once, this event will be emitted for each file - /// separately. - DroppedFile(PathBuf), +impl KeyEvent { + pub fn logical_key(&self) -> Option { + self.logical_key + } - /// A file is being hovered over the window. - /// - /// When the user hovers multiple files at once, this event will be emitted for each file - /// separately. - HoveredFile(PathBuf), + pub fn logical_key_is(&self, key: LogicalKey) -> bool { + self.logical_key == Some(key) + } - /// A file was hovered, but has exited the window. + pub fn scan_code(&self) -> u32 { + self.scan_code + } + pub fn is_down(&self) -> bool { + self.is_down + } + pub fn is_up(&self) -> bool { + !self.is_down + } + /// Is `true` if the user has held down the key long enough to send duplicate events. /// - /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were - /// hovered. - HoveredFileCancelled, + /// Is always `false` if `is_down` is `false`. + pub fn is_repeat(&self) -> bool { + self.is_repeat + } + /// If set, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed + /// when a window gains focus. Likewise, synthetic key release events + /// are generated for all keys pressed when a window goes out of focus. + /// ***Currently, this is only functional on X11 and Windows*** + /// + /// Otherwise, this value is always `false`. + pub fn is_synthetic(&self) -> bool { + self.is_synthetic + } +} - /// The window received a unicode character. - ReceivedCharacter(char), +impl KeyEvent { + pub fn new() -> Self { + Self::default() + } - /// The window gained or lost focus. - /// - /// The parameter is true if the window has gained focus, and false if it has lost focus. - Focused(bool), - - /// An event from the keyboard has been received. - KeyboardInput { - device_id: DeviceId, - input: KeyboardInput, - /// If `true`, the event was generated synthetically by winit - /// in one of the following circumstances: - /// - /// * Synthetic key press events are generated for all keys pressed - /// when a window gains focus. Likewise, synthetic key release events - /// are generated for all keys pressed when a window goes out of focus. - /// ***Currently, this is only functional on X11 and Windows*** - /// - /// Otherwise, this value is always `false`. - is_synthetic: bool, - }, + pub fn set_logical_key(&mut self, logical_key: Option) -> &mut Self { + self.logical_key = logical_key; + self + } + pub fn set_scan_code(&mut self, scan_code: u32) -> &mut Self { + self.scan_code = scan_code; + self + } + pub fn set_is_down(&mut self, is_down: bool) -> &mut Self { + self.is_down = is_down; + self + } + pub fn set_is_repeat(&mut self, is_repeat: bool) -> &mut Self { + self.is_repeat = is_repeat; + self + } + pub fn set_is_synthetic(&mut self, is_synthetic: bool) -> &mut Self { + self.is_synthetic = is_synthetic; + self + } +} - /// The keyboard modifiers have changed. - /// - /// Platform-specific behavior: - /// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an - /// issue, and it should get fixed - but it's the current state of the API. - ModifiersChanged(ModifiersState), +impl RawKeyEvent { + pub fn logical_key(&self) -> Option { + self.logical_key + } + pub fn scan_code(&self) -> u32 { + self.scan_code + } + pub fn is_down(&self) -> bool { + self.is_down + } + pub fn is_up(&self) -> bool { + !self.is_down + } +} - /// The cursor has moved on the window. - CursorMoved { - device_id: DeviceId, +impl RawKeyEvent { + pub fn new() -> Self { + Self::default() + } - /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is - /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor - /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. - position: PhysicalPosition, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, + pub fn set_logical_key(&mut self, logical_key: Option) -> &mut Self { + self.logical_key = logical_key; + self + } + pub fn set_scan_code(&mut self, scan_code: u32) -> &mut Self { + self.scan_code = scan_code; + self + } + pub fn set_is_down(&mut self, is_down: bool) -> &mut Self { + self.is_down = is_down; + self + } +} - /// The cursor has entered the window. - CursorEntered { device_id: DeviceId }, +impl PointerButtonEvent { + pub fn button(&self) -> PointerButton { + self.button + } + pub fn is_down(&self) -> bool { + self.is_down + } + pub fn is_up(&self) -> bool { + !self.is_down + } + /// The number of clicks the user has made in the same spot within the system's double-click + /// interval. `1` is emitted on the first click, `2` is emitted on the second click, etc. + pub fn multi_click_count(&self) -> u32 { + self.multi_click_count + } +} - /// The cursor has left the window. - CursorLeft { device_id: DeviceId }, +impl PointerButtonEvent { + pub fn new() -> Self { + Self::default() + } - /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { - device_id: DeviceId, - delta: MouseScrollDelta, - phase: TouchPhase, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, + pub fn set_button(&mut self, button: PointerButton) -> &mut Self { + self.button = button; + self + } + pub fn set_is_down(&mut self, is_down: bool) -> &mut Self { + self.is_down = is_down; + self + } + pub fn set_multi_click_count(&mut self, multi_click_count: u32) -> &mut Self { + self.multi_click_count = multi_click_count; + self + } +} - /// An mouse button press has been received. - MouseInput { - device_id: DeviceId, - state: ElementState, - button: MouseButton, - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - modifiers: ModifiersState, - }, +impl RawPointerButtonEvent { + pub fn button(&self) -> PointerButton { + self.button + } + pub fn is_down(&self) -> bool { + self.is_down + } + pub fn is_up(&self) -> bool { + !self.is_down + } +} - /// Touchpad pressure event. - /// - /// At the moment, only supported on Apple forcetouch-capable macbooks. - /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad - /// is being pressed) and stage (integer representing the click level). - TouchpadPressure { - device_id: DeviceId, - pressure: f32, - stage: i64, - }, +impl RawPointerButtonEvent { + pub fn new() -> Self { + Self::default() + } - /// Motion on some analog axis. May report data redundant to other, more specific events. - AxisMotion { - device_id: DeviceId, - axis: AxisId, - value: f64, - }, + pub fn set_button(&mut self, button: PointerButton) -> &mut Self { + self.button = button; + self + } + pub fn set_is_down(&mut self, is_down: bool) -> &mut Self { + self.is_down = is_down; + self + } +} - /// Touch event has been received - Touch(Touch), +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(transparent)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct PointerButton(pub(crate) PointerButtonInner); - /// The window's scale factor has changed. +/// We use an internal enum rather than exposing the variants directly so that the `BUTTON_n` +/// constants are formatted and exposed in a similar style to the `{MOUSE|TOUCH|PEN}_n` constants. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename = "PointerButton"))] +#[cfg_attr(feature = "serde", serde(try_from = "pointer_button_serde::PointerButtonSerialize"))] +#[cfg_attr(feature = "serde", serde(into = "pointer_button_serde::PointerButtonSerialize"))] +#[allow(non_camel_case_types)] +pub(crate) enum PointerButtonInner { + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, + BUTTON_5, + BUTTON_6, +} + +impl Default for PointerButtonInner { + #[inline(always)] + fn default() -> Self { + PointerButtonInner::BUTTON_1 + } +} + +impl PointerButton { + pub const MOUSE_LEFT: Self = Self::BUTTON_1; + pub const MOUSE_RIGHT: Self = Self::BUTTON_2; + pub const MOUSE_MIDDLE: Self = Self::BUTTON_3; + pub const MOUSE_X1: Self = Self::BUTTON_4; + pub const MOUSE_X2: Self = Self::BUTTON_5; + + pub const TOUCH_CONTACT: Self = Self::BUTTON_1; + + pub const PEN_CONTACT: Self = Self::BUTTON_1; + pub const PEN_BARREL: Self = Self::BUTTON_2; + pub const PEN_ERASER: Self = Self::BUTTON_6; + + pub const BUTTON_1: Self = Self(PointerButtonInner::BUTTON_1); + pub const BUTTON_2: Self = Self(PointerButtonInner::BUTTON_2); + pub const BUTTON_3: Self = Self(PointerButtonInner::BUTTON_3); + pub const BUTTON_4: Self = Self(PointerButtonInner::BUTTON_4); + pub const BUTTON_5: Self = Self(PointerButtonInner::BUTTON_5); + pub const BUTTON_6: Self = Self(PointerButtonInner::BUTTON_6); + + pub fn as_u8(&self) -> u8 { + self.0 as u8 + } + pub fn is_mouse_left(&self) -> bool { + *self == Self::MOUSE_LEFT + } + pub fn is_mouse_right(&self) -> bool { + *self == Self::MOUSE_RIGHT + } + pub fn is_mouse_middle(&self) -> bool { + *self == Self::MOUSE_MIDDLE + } + pub fn is_mouse_x1(&self) -> bool { + *self == Self::MOUSE_X1 + } + pub fn is_mouse_x2(&self) -> bool { + *self == Self::MOUSE_X2 + } + pub fn is_touch_contact(&self) -> bool { + *self == Self::TOUCH_CONTACT + } + pub fn is_pen_contact(&self) -> bool { *self == Self::PEN_CONTACT } + pub fn is_pen_barrel(&self) -> bool { *self == Self::PEN_BARREL } + pub fn is_pen_eraser(&self) -> bool { *self == Self::PEN_ERASER } + pub fn is_button_1(&self) -> bool { + *self == Self::BUTTON_1 + } + pub fn is_button_2(&self) -> bool { + *self == Self::BUTTON_2 + } + pub fn is_button_3(&self) -> bool { + *self == Self::BUTTON_3 + } + pub fn is_button_4(&self) -> bool { + *self == Self::BUTTON_4 + } + pub fn is_button_5(&self) -> bool { + *self == Self::BUTTON_5 + } + pub fn is_button_6(&self) -> bool { *self == Self::BUTTON_6 } + + /// Serializes the `PointerButton` as the `BUTTON_*` constants. This is the default + /// serialization style, since it's pointer-type agnostic. /// - /// The following user actions can cause DPI changes: + /// For use with `#[serde(serialize_with = "path")]` + #[cfg(feature = "serde")] + pub fn serialize_agnostic(&self, serializer: S) -> Result + where S: serde::Serializer + { + serde::Serialize::serialize(&self.0.as_serialize_agnostic(), serializer) + } + + /// Tries to serialize the `PointerButton` as the `MOUSE_*` constants, falling back to + /// the `BUTTON_{NUM}` constants if the value doesn't map onto a mouse constant. /// - /// * Changing the display's resolution. - /// * Changing the display's scale factor (e.g. in Control Panel on Windows). - /// * Moving the window to a display with a different scale factor. + /// For use with `#[serde(serialize_with = "path")]` + #[cfg(feature = "serde")] + pub fn serialize_mouse(&self, serializer: S) -> Result + where S: serde::Serializer + { + serde::Serialize::serialize(&self.0.as_serialize_mouse(), serializer) + } + + /// Tries to serialize the `PointerButton` as the `TOUCH_*` constants, falling back to + /// the `BUTTON_{NUM}` constants if the value doesn't map onto a touch constant. /// - /// After this event callback has been processed, the window will be resized to whatever value - /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested - /// by the OS, but it can be changed to any value. + /// For use with `#[serde(serialize_with = "path")]` + #[cfg(feature = "serde")] + pub fn serialize_touch(&self, serializer: S) -> Result + where S: serde::Serializer + { + serde::Serialize::serialize(&self.0.as_serialize_touch(), serializer) + } + + /// Tries to serialize the `PointerButton` as the `PEN_*` constants, falling back to + /// the `BUTTON_{NUM}` constants if the value doesn't map onto a pen constant. /// - /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. - ScaleFactorChanged { - scale_factor: f64, - new_inner_size: &'a mut PhysicalSize, - }, + /// For use with `#[serde(serialize_with = "path")]` + #[cfg(feature = "serde")] + pub fn serialize_pen(&self, serializer: S) -> Result + where S: serde::Serializer + { + serde::Serialize::serialize(&self.0.as_serialize_pen(), serializer) + } - /// The system window theme has changed. + /// Serializes the `PointerButton` as a legacy Winit [`MouseButton`]. This is provided for + /// backwards-compatibility purposes. /// - /// Applications might wish to react to this to change the theme of the content of the window - /// when the system changes the window theme. + /// For use with `#[serde(serialize_with = "path")]` /// - /// At the moment this is only supported on Windows. - ThemeChanged(Theme), + /// [`MouseButton`]: https://docs.rs/winit/0.22.2/winit/event/enum.MouseButton.html + #[cfg(feature = "serde")] + pub fn serialize_legacy(&self, serializer: S) -> Result + where S: serde::Serializer + { + serde::Serialize::serialize(&self.0.as_serialize_legacy(), serializer) + } } -impl Clone for WindowEvent<'static> { - fn clone(&self) -> Self { - use self::WindowEvent::*; - return match self { - Resized(size) => Resized(size.clone()), - Moved(pos) => Moved(pos.clone()), - CloseRequested => CloseRequested, - Destroyed => Destroyed, - DroppedFile(file) => DroppedFile(file.clone()), - HoveredFile(file) => HoveredFile(file.clone()), - HoveredFileCancelled => HoveredFileCancelled, - ReceivedCharacter(c) => ReceivedCharacter(*c), - Focused(f) => Focused(*f), - KeyboardInput { - device_id, - input, - is_synthetic, - } => KeyboardInput { - device_id: *device_id, - input: *input, - is_synthetic: *is_synthetic, - }, - - ModifiersChanged(modifiers) => ModifiersChanged(modifiers.clone()), - #[allow(deprecated)] - CursorMoved { - device_id, - position, - modifiers, - } => CursorMoved { - device_id: *device_id, - position: *position, - modifiers: *modifiers, - }, - CursorEntered { device_id } => CursorEntered { - device_id: *device_id, - }, - CursorLeft { device_id } => CursorLeft { - device_id: *device_id, - }, - #[allow(deprecated)] - MouseWheel { - device_id, - delta, - phase, - modifiers, - } => MouseWheel { - device_id: *device_id, - delta: *delta, - phase: *phase, - modifiers: *modifiers, - }, - #[allow(deprecated)] - MouseInput { - device_id, - state, - button, - modifiers, - } => MouseInput { - device_id: *device_id, - state: *state, - button: *button, - modifiers: *modifiers, - }, - TouchpadPressure { - device_id, - pressure, - stage, - } => TouchpadPressure { - device_id: *device_id, - pressure: *pressure, - stage: *stage, - }, - AxisMotion { - device_id, - axis, - value, - } => AxisMotion { - device_id: *device_id, - axis: *axis, - value: *value, - }, - Touch(touch) => Touch(*touch), - ThemeChanged(theme) => ThemeChanged(theme.clone()), - ScaleFactorChanged { .. } => { - unreachable!("Static event can't be about scale factor changing") - } - }; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum PointerId { + MouseId(MouseId), + TouchId(TouchId), + PenId(PenId), +} + +/// A typed identifier for a pointer device. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MouseId(pub(crate) platform_impl::MouseId); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TouchId(pub(crate) platform_impl::TouchId); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PenId(pub(crate) platform_impl::PenId); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PointerDeviceId(pub(crate) platform_impl::PointerDeviceId); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct KeyboardDeviceId(pub(crate) platform_impl::KeyboardDeviceId); + +impl PointerId { + pub fn is_mouse_id(&self) -> bool { + match *self { + PointerId::MouseId(_) => true, + _ => false, + } } -} -impl<'a> WindowEvent<'a> { - pub fn to_static(self) -> Option> { - use self::WindowEvent::*; - match self { - Resized(size) => Some(Resized(size)), - Moved(position) => Some(Moved(position)), - CloseRequested => Some(CloseRequested), - Destroyed => Some(Destroyed), - DroppedFile(file) => Some(DroppedFile(file)), - HoveredFile(file) => Some(HoveredFile(file)), - HoveredFileCancelled => Some(HoveredFileCancelled), - ReceivedCharacter(c) => Some(ReceivedCharacter(c)), - Focused(focused) => Some(Focused(focused)), - KeyboardInput { - device_id, - input, - is_synthetic, - } => Some(KeyboardInput { - device_id, - input, - is_synthetic, - }), - ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), - #[allow(deprecated)] - CursorMoved { - device_id, - position, - modifiers, - } => Some(CursorMoved { - device_id, - position, - modifiers, - }), - CursorEntered { device_id } => Some(CursorEntered { device_id }), - CursorLeft { device_id } => Some(CursorLeft { device_id }), - #[allow(deprecated)] - MouseWheel { - device_id, - delta, - phase, - modifiers, - } => Some(MouseWheel { - device_id, - delta, - phase, - modifiers, - }), - #[allow(deprecated)] - MouseInput { - device_id, - state, - button, - modifiers, - } => Some(MouseInput { - device_id, - state, - button, - modifiers, - }), - TouchpadPressure { - device_id, - pressure, - stage, - } => Some(TouchpadPressure { - device_id, - pressure, - stage, - }), - AxisMotion { - device_id, - axis, - value, - } => Some(AxisMotion { - device_id, - axis, - value, - }), - Touch(touch) => Some(Touch(touch)), - ThemeChanged(theme) => Some(ThemeChanged(theme)), - ScaleFactorChanged { .. } => None, + pub fn is_touch_id(&self) -> bool { + match *self { + PointerId::TouchId(_) => true, + _ => false, + } + } + + pub fn is_pen_id(&self) -> bool { + match *self { + PointerId::PenId(_) => true, + _ => false, } } } -/// Identifier of an input device. -/// -/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which -/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or -/// physical. Virtual devices typically aggregate inputs from multiple physical devices. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(pub(crate) platform_impl::DeviceId); - -impl DeviceId { - /// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return +impl MouseId { + /// Returns a dummy `MouseId`, useful for unit testing. The only guarantee made about the return /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `DeviceId`. + /// by this function. No other guarantees are made. This may be equal to a real `MouseId`. /// /// **Passing this into a winit function will result in undefined behavior.** pub unsafe fn dummy() -> Self { - DeviceId(platform_impl::DeviceId::dummy()) + MouseId(platform_impl::MouseId::dummy()) } } -/// Represents raw hardware events that are not associated with any particular window. -/// -/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person -/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because -/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs -/// may not match. -/// -/// Note that these events are delivered regardless of input focus. -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - Added, - Removed, - - /// Change in physical position of a pointing device. +impl PointerDeviceId { + /// Returns a dummy `PointerDeviceId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `PointerDeviceId`. /// - /// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`. - MouseMotion { - /// (x, y) change in position in unspecified units. - /// - /// Different devices may use different units. - delta: (f64, f64), - }, - - /// Physical scroll event - MouseWheel { - delta: MouseScrollDelta, - }, - - /// Motion on some analog axis. This event will be reported for all arbitrary input devices - /// that winit supports on this platform, including mouse devices. If the device is a mouse - /// device then this will be reported alongside the MouseMotion event. - Motion { - axis: AxisId, - value: f64, - }, - - Button { - button: ButtonId, - state: ElementState, - }, - - Key(KeyboardInput), - - Text { - codepoint: char, - }, + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + PointerDeviceId(platform_impl::PointerDeviceId::dummy()) + } } -/// Describes a keyboard input event. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct KeyboardInput { - /// Identifies the physical key pressed +impl KeyboardDeviceId { + /// Returns a dummy `KeyboardDeviceId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `KeyboardDeviceId`. /// - /// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the - /// key is more important than the key's host GUI semantics, such as for movement controls in a first-person - /// game. - pub scancode: ScanCode, + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + KeyboardDeviceId(platform_impl::KeyboardDeviceId::dummy()) + } +} - pub state: ElementState, +impl fmt::Debug for MouseId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} - /// Identifies the semantic meaning of the key - /// - /// Use when the semantics of the key are more important than the physical location of the key, such as when - /// implementing appropriate behavior for "page up." - pub virtual_keycode: Option, +impl fmt::Debug for PointerDeviceId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} - /// Modifier keys active at the time of this input. - /// - /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from - /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere. - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - pub modifiers: ModifiersState, +impl fmt::Debug for KeyboardDeviceId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } } -/// Describes touch-screen input state. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum TouchPhase { - Started, - Moved, - Ended, - Cancelled, +impl fmt::Debug for TouchId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } } -/// Represents a touch event -/// -/// Every time the user touches the screen, a new `Start` event with an unique -/// identifier for the finger is generated. When the finger is lifted, an `End` -/// event is generated with the same finger id. -/// -/// After a `Start` event has been emitted, there may be zero or more `Move` -/// events when the finger is moved or the touch pressure changes. -/// -/// The finger id may be reused by the system after an `End` event. The user -/// should assume that a new `Start` event received with the same id has nothing -/// to do with the old finger and is a new finger. -/// -/// A `Cancelled` event is emitted when the system has canceled tracking this -/// touch, such as when the window loses focus, or on iOS if the user moves the -/// device against their face. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Touch { - pub device_id: DeviceId, - pub phase: TouchPhase, - pub location: PhysicalPosition, - /// Describes how hard the screen was pressed. May be `None` if the platform - /// does not support pressure sensitivity. - /// - /// ## Platform-specific - /// - /// - Only available on **iOS** 9.0+ and **Windows** 8+. - pub force: Option, - /// Unique identifier of a finger. - pub id: u64, +impl fmt::Debug for PenId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } } /// Describes the force of a touch event @@ -721,57 +889,97 @@ impl Force { } } -/// Hardware-dependent keyboard scan code. -pub type ScanCode = u32; +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PointerTiltEvent { + tilt_angle_x: f64, + tilt_angle_y: f64, +} -/// Identifier for a specific analog axis on some device. -pub type AxisId = u32; +impl PointerTiltEvent { + pub fn new_tilt_angle(tilt_angle_x: f64, tilt_angle_y: f64) -> PointerTiltEvent { + PointerTiltEvent { + tilt_angle_x, + tilt_angle_y, + } + } -/// Identifier for a specific button on some device. -pub type ButtonId = u32; + #[inline(always)] + pub fn tilt_angle_x(&self) -> f64 { + self.tilt_angle_x + } -/// Describes the input state of a key. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ElementState { - Pressed, - Released, -} + #[inline(always)] + pub fn tilt_angle_y(&self) -> f64 { + self.tilt_angle_y + } -/// Describes a button of a mouse controller. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseButton { - Left, - Right, - Middle, - Other(u8), -} + #[inline(always)] + pub fn tilt_angle(&self) -> f64 { + // As shown by `tilt_vector_(x|y)`, the length of the tilt vector on the XY plane is + // trivially relatable to the tilt angle. This computes the length of the XY vector and uses + // that to get the axis-independent tilt angle. + let tilt_xy_distance_from_center = f64::sqrt(self.tilt_vector_x().powi(2) + self.tilt_vector_y().powi(2)); + let tilt = f64::asin(tilt_xy_distance_from_center); + if tilt.is_nan() { + 0.0 + } else { + tilt + } + } -/// Describes a difference in the mouse scroll wheel state. -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseScrollDelta { - /// Amount in lines or rows to scroll in the horizontal - /// and vertical directions. - /// - /// Positive values indicate movement forward - /// (away from the user) or rightwards. - LineDelta(f32, f32), - /// Amount in pixels to scroll in the horizontal and - /// vertical direction. - /// - /// Scroll events are expressed as a PixelDelta if - /// supported by the device (eg. a touchpad) and - /// platform. - PixelDelta(LogicalPosition), + #[inline(always)] + pub fn altitude_angle(&self) -> f64 { + std::f64::consts::PI/2.0 - self.tilt_angle() + } + + #[inline(always)] + pub fn tilt_vector_x(&self) -> f64 { + self.tilt_angle_x.sin() + } + + #[inline(always)] + pub fn tilt_vector_y(&self) -> f64 { + self.tilt_angle_y.sin() + } + + #[inline(always)] + pub fn tilt_vector_z(&self) -> f64 { + // The tilt vector is a normalized three-component vector. Since we know the X and Y + // components of that vector, we can use a transformed version of the pythagorean theorem + // to get the Z component. + let z = f64::sqrt(1.0 - self.tilt_vector_x().powi(2) - self.tilt_vector_y().powi(2)); + if z.is_nan() { + 0.0 + } else { + z + } + } + + #[inline(always)] + pub fn azimuth_angle(&self) -> Option { + if self.tilt_angle_x == 0.0 && self.tilt_angle_y == 0.0 { + None + } else { + Some(f64::atan2(self.tilt_angle_x, self.tilt_angle_y)) + } + } + + #[inline(always)] + pub fn azimuth_vector_x(&self) -> f64 { + self.azimuth_angle().map(|a| a.sin()).unwrap_or(0.0) + } + + #[inline(always)] + pub fn azimuth_vector_y(&self) -> f64 { + self.azimuth_angle().map(|a| a.cos()).unwrap_or(0.0) + } } /// Symbolic name for a keyboard key. #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] #[repr(u32)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum VirtualKeyCode { +pub enum LogicalKey { /// The '1' key over the letters. Key1, /// The '2' key over the letters. @@ -968,25 +1176,6 @@ pub enum VirtualKeyCode { Cut, } -impl ModifiersState { - /// Returns `true` if the shift key is pressed. - pub fn shift(&self) -> bool { - self.intersects(Self::SHIFT) - } - /// Returns `true` if the control key is pressed. - pub fn ctrl(&self) -> bool { - self.intersects(Self::CTRL) - } - /// Returns `true` if the alt key is pressed. - pub fn alt(&self) -> bool { - self.intersects(Self::ALT) - } - /// Returns `true` if the logo key is pressed. - pub fn logo(&self) -> bool { - self.intersects(Self::LOGO) - } -} - bitflags! { /// Represents the current state of the keyboard modifiers /// @@ -1014,6 +1203,25 @@ bitflags! { } } +impl ModifiersState { + /// Returns `true` if the shift key is pressed. + pub fn shift(&self) -> bool { + self.intersects(Self::SHIFT) + } + /// Returns `true` if the control key is pressed. + pub fn ctrl(&self) -> bool { + self.intersects(Self::CTRL) + } + /// Returns `true` if the alt key is pressed. + pub fn alt(&self) -> bool { + self.intersects(Self::ALT) + } + /// Returns `true` if the logo key is pressed. + pub fn logo(&self) -> bool { + self.intersects(Self::LOGO) + } +} + #[cfg(feature = "serde")] mod modifiers_serde { use super::ModifiersState; @@ -1064,3 +1272,575 @@ mod modifiers_serde { } } } + +#[cfg(feature = "serde")] +mod pointer_button_serde { + use super::PointerButtonInner; + use std::{ + convert::TryFrom, + fmt, + }; + use serde::{Serialize, Deserialize}; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] + #[derive(Serialize, Deserialize)] + #[serde(rename = "PointerButton")] + #[allow(non_camel_case_types)] + pub enum PointerButtonSerialize { + // legacy style + Left, + Right, + Middle, + Other(u8), + + // input-agnostic style + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, + BUTTON_5, + BUTTON_6, + + // mouse style + MOUSE_LEFT, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_X1, + MOUSE_X2, + + // touch style + TOUCH_CONTACT, + + // pen style + PEN_CONTACT, + PEN_BARREL, + PEN_ERASER, + } + + + pub struct OtherConvertError(u8); + + impl TryFrom for PointerButtonInner { + type Error = OtherConvertError; + fn try_from(serialize: PointerButtonSerialize) -> Result { + match serialize { + PointerButtonSerialize::TOUCH_CONTACT | + PointerButtonSerialize::PEN_CONTACT | + PointerButtonSerialize::BUTTON_1 | + PointerButtonSerialize::Left | + PointerButtonSerialize::MOUSE_LEFT => Ok(PointerButtonInner::BUTTON_1), + + PointerButtonSerialize::BUTTON_2 | + PointerButtonSerialize::PEN_BARREL | + PointerButtonSerialize::Right | + PointerButtonSerialize::MOUSE_RIGHT => Ok(PointerButtonInner::BUTTON_2), + + PointerButtonSerialize::BUTTON_3 | + PointerButtonSerialize::Middle | + PointerButtonSerialize::MOUSE_MIDDLE => Ok(PointerButtonInner::BUTTON_3), + + PointerButtonSerialize::BUTTON_4 | + PointerButtonSerialize::Other(0) | + PointerButtonSerialize::MOUSE_X1 => Ok(PointerButtonInner::BUTTON_4), + + PointerButtonSerialize::BUTTON_5 | + PointerButtonSerialize::Other(1) | + PointerButtonSerialize::MOUSE_X2 => Ok(PointerButtonInner::BUTTON_5), + + PointerButtonSerialize::BUTTON_6 | + PointerButtonSerialize::Other(2) | + PointerButtonSerialize::PEN_ERASER => Ok(PointerButtonInner::BUTTON_6), + + PointerButtonSerialize::Other(i) => Err(OtherConvertError(i)), + } + } + } + + impl From for PointerButtonSerialize { + fn from(inner: PointerButtonInner) -> PointerButtonSerialize { + inner.as_serialize_agnostic() + } + } + + impl PointerButtonInner { + pub fn as_serialize_agnostic(&self) -> PointerButtonSerialize { + match self { + PointerButtonInner::BUTTON_1 => PointerButtonSerialize::BUTTON_1, + PointerButtonInner::BUTTON_2 => PointerButtonSerialize::BUTTON_2, + PointerButtonInner::BUTTON_3 => PointerButtonSerialize::BUTTON_3, + PointerButtonInner::BUTTON_4 => PointerButtonSerialize::BUTTON_4, + PointerButtonInner::BUTTON_5 => PointerButtonSerialize::BUTTON_5, + PointerButtonInner::BUTTON_6 => PointerButtonSerialize::BUTTON_6, + } + } + + pub fn as_serialize_legacy(&self) -> PointerButtonSerialize { + match self { + PointerButtonInner::BUTTON_1 => PointerButtonSerialize::Left, + PointerButtonInner::BUTTON_2 => PointerButtonSerialize::Right, + PointerButtonInner::BUTTON_3 => PointerButtonSerialize::Middle, + PointerButtonInner::BUTTON_4 => PointerButtonSerialize::Other(0), + PointerButtonInner::BUTTON_5 => PointerButtonSerialize::Other(1), + PointerButtonInner::BUTTON_6 => PointerButtonSerialize::BUTTON_6, + } + } + + pub fn as_serialize_mouse(&self) -> PointerButtonSerialize { + match self { + PointerButtonInner::BUTTON_1 => PointerButtonSerialize::MOUSE_LEFT, + PointerButtonInner::BUTTON_2 => PointerButtonSerialize::MOUSE_RIGHT, + PointerButtonInner::BUTTON_3 => PointerButtonSerialize::MOUSE_MIDDLE, + PointerButtonInner::BUTTON_4 => PointerButtonSerialize::MOUSE_X1, + PointerButtonInner::BUTTON_5 => PointerButtonSerialize::MOUSE_X2, + PointerButtonInner::BUTTON_6 => PointerButtonSerialize::BUTTON_6, + } + } + + pub fn as_serialize_touch(&self) -> PointerButtonSerialize { + match self { + PointerButtonInner::BUTTON_1 => PointerButtonSerialize::TOUCH_CONTACT, + PointerButtonInner::BUTTON_2 => PointerButtonSerialize::BUTTON_2, + PointerButtonInner::BUTTON_3 => PointerButtonSerialize::BUTTON_3, + PointerButtonInner::BUTTON_4 => PointerButtonSerialize::BUTTON_4, + PointerButtonInner::BUTTON_5 => PointerButtonSerialize::BUTTON_5, + PointerButtonInner::BUTTON_6 => PointerButtonSerialize::BUTTON_6, + } + } + + pub fn as_serialize_pen(&self) -> PointerButtonSerialize { + match self { + PointerButtonInner::BUTTON_1 => PointerButtonSerialize::PEN_CONTACT, + PointerButtonInner::BUTTON_2 => PointerButtonSerialize::PEN_BARREL, + PointerButtonInner::BUTTON_3 => PointerButtonSerialize::BUTTON_3, + PointerButtonInner::BUTTON_4 => PointerButtonSerialize::BUTTON_4, + PointerButtonInner::BUTTON_5 => PointerButtonSerialize::BUTTON_5, + PointerButtonInner::BUTTON_6 => PointerButtonSerialize::PEN_ERASER, + } + } + } + + impl fmt::Display for OtherConvertError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "could not deserialize Other({})", self.0) + } + } + + #[cfg(test)] + mod tests { + use crate::event::PointerButton; + use serde::{Serialize, Deserialize}; + + /// legacy mouse button struct + #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] + #[derive(Serialize, Deserialize)] + enum MouseButton { + Left, + Right, + Middle, + Other(u8), + } + + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] + #[serde(rename = "Serde")] + struct LegacySerde(MouseButton); + + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] + #[serde(rename = "Serde")] + struct NewSerde(#[serde(serialize_with = "PointerButton::serialize_legacy")] PointerButton); + + trait Serde { + type Error: std::fmt::Debug; + fn to_bytes(value: &T) -> Result, Self::Error> + where + T: Serialize; + fn from_bytes<'a, T>(s: &'a [u8]) -> Result + where + T: Deserialize<'a>; + } + + struct Ron; + impl Serde for Ron { + type Error = ron::Error; + fn to_bytes(value: &T) -> Result, Self::Error> + where + T: Serialize + { + ron::ser::to_string(value).map(|s| s.into_bytes()) + } + fn from_bytes<'a, T>(s: &'a [u8]) -> Result + where + T: Deserialize<'a> + { + ron::de::from_bytes(s) + } + } + + struct Bincode; + impl Serde for Bincode { + type Error = bincode::Error; + fn to_bytes(value: &T) -> Result, Self::Error> + where + T: Serialize + { + bincode::serialize(value) + } + fn from_bytes<'a, T>(s: &'a [u8]) -> Result + where + T: Deserialize<'a> + { + bincode::deserialize(s) + } + } + + struct Json; + impl Serde for Json { + type Error = serde_json::Error; + fn to_bytes(value: &T) -> Result, Self::Error> + where + T: Serialize + { + serde_json::to_vec(value) + } + fn from_bytes<'a, T>(s: &'a [u8]) -> Result + where + T: Deserialize<'a> + { + serde_json::from_slice(s) + } + } + + fn serde() { + let legacy = [ + LegacySerde(MouseButton::Left), + LegacySerde(MouseButton::Right), + LegacySerde(MouseButton::Middle), + LegacySerde(MouseButton::Other(0)), + LegacySerde(MouseButton::Other(1)), + ]; + let new = [ + NewSerde(PointerButton::MOUSE_LEFT), + NewSerde(PointerButton::MOUSE_RIGHT), + NewSerde(PointerButton::MOUSE_MIDDLE), + NewSerde(PointerButton::MOUSE_X1), + NewSerde(PointerButton::MOUSE_X2), + ]; + + let try_to_utf8 = |b: &[u8]| format!("\n\tstr: {}\n\tbytes: {:?}", std::str::from_utf8(b).unwrap_or_else(|_| ""), b); + for (l, n) in legacy.iter().cloned().zip(new.iter().cloned()) { + println!("legacy: {:?}, new: {:?}", l, n); + let legacy_serialized: Vec = S::to_bytes(&l).unwrap(); + let new_serialized: Vec = S::to_bytes(&n).unwrap(); + println!("legacy serialized: {}", try_to_utf8(&legacy_serialized)); + println!("new serialized: {}", try_to_utf8(&new_serialized)); + + let legacy_deserialized: LegacySerde = S::from_bytes(&new_serialized).unwrap(); + let new_deserialized: NewSerde = S::from_bytes(&legacy_serialized).unwrap(); + + assert_eq!(&legacy_serialized, &new_serialized); + assert_eq!(legacy_deserialized, l); + assert_eq!(new_deserialized, n); + } + } + + #[test] + fn serde_pointer_button_backwards_compatibility_ron() { + serde::(); + } + + #[test] + fn serde_pointer_button_backwards_compatibility_bincode() { + serde::(); + } + + #[test] + fn serde_pointer_button_backwards_compatibility_json() { + serde::(); + } + } +} + +#[cfg(test)] +mod tests { + use super::PointerTiltEvent; + use std::f64::consts::PI; + use assert_approx_eq::assert_approx_eq; + + #[test] + fn pointer_tilt_azimuth() { + let half_angle = 0.707107; + + println!("up"); + let up = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: 0.0, + }; + assert_eq!(None, up.azimuth_angle()); + + println!("north"); + let north = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: PI / 2.0, + }; + assert_approx_eq!(0.0, north.azimuth_angle().unwrap()); + assert_approx_eq!(0.0, north.azimuth_vector_x()); + assert_approx_eq!(1.0, north.azimuth_vector_y()); + + println!("north_east"); + let north_east = PointerTiltEvent { + tilt_angle_x: PI / 4.0, + tilt_angle_y: PI / 4.0, + }; + assert_approx_eq!(PI / 4.0, north_east.azimuth_angle().unwrap()); + assert_approx_eq!(half_angle, north_east.azimuth_vector_x()); + assert_approx_eq!(half_angle, north_east.azimuth_vector_y()); + + println!("east"); + let east = PointerTiltEvent { + tilt_angle_x: PI / 2.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq!(1.0 * PI / 2.0, east.azimuth_angle().unwrap()); + assert_approx_eq!(1.0, east.azimuth_vector_x()); + assert_approx_eq!(0.0, east.azimuth_vector_y()); + + println!("south_east"); + let south_east = PointerTiltEvent { + tilt_angle_x: PI / 4.0, + tilt_angle_y: -PI / 4.0, + }; + assert_approx_eq!(3.0 * PI / 4.0, south_east.azimuth_angle().unwrap()); + assert_approx_eq!(half_angle, south_east.azimuth_vector_x()); + assert_approx_eq!(-half_angle, south_east.azimuth_vector_y()); + + println!("south"); + let south = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: -PI / 2.0, + }; + assert_approx_eq!(2.0 * PI / 2.0, south.azimuth_angle().unwrap()); + assert_approx_eq!(0.0, south.azimuth_vector_x()); + assert_approx_eq!(-1.0, south.azimuth_vector_y()); + + println!("south_west"); + let south_west = PointerTiltEvent { + tilt_angle_x: -PI / 4.0, + tilt_angle_y: -PI / 4.0, + }; + assert_approx_eq!(-3.0 * PI / 4.0, south_west.azimuth_angle().unwrap()); + assert_approx_eq!(-half_angle, south_west.azimuth_vector_x()); + assert_approx_eq!(-half_angle, south_west.azimuth_vector_y()); + + println!("west"); + let west = PointerTiltEvent { + tilt_angle_x: -PI / 2.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq!(-1.0 * PI / 2.0, west.azimuth_angle().unwrap()); + assert_approx_eq!(-1.0, west.azimuth_vector_x()); + assert_approx_eq!(0.0, west.azimuth_vector_y()); + + println!("north_west"); + let north_west = PointerTiltEvent { + tilt_angle_x: -PI / 4.0, + tilt_angle_y: PI / 4.0, + }; + assert_approx_eq!(-PI / 4.0, north_west.azimuth_angle().unwrap()); + assert_approx_eq!(-half_angle, north_west.azimuth_vector_x()); + assert_approx_eq!(half_angle, north_west.azimuth_vector_y()); + } + + #[test] + fn pointer_tilt_vector() { + let tilt_vector = |te: PointerTiltEvent| [te.tilt_vector_x(), te.tilt_vector_y(), te.tilt_vector_z()]; + + let eps = 1.0e-6; + let assert_normalized = |v: [f64; 3]| { + let length = (v[0].powi(2) + v[1].powi(2) + v[2].powi(2)).sqrt(); + if (length - 1.0).abs() > eps { + assert_eq!(length, 1.0, "vector {:?} is not normalized", v); + } + }; + let assert_approx_eq = |left: [f64; 3], right: [f64; 3]| { + println!("testing left normalized"); + assert_normalized(left); + println!("testing right normalized"); + assert_normalized(right); + + let mut equals = true; + for i in 0..3 { + equals &= (left[i] - right[i]).abs() <= eps; + } + if !equals { + assert_eq!(left, right); + } + }; + + println!("up"); + let up = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq([0.0, 0.0, 1.0], tilt_vector(up)); + + println!("east"); + let east = PointerTiltEvent { + tilt_angle_x: PI / 2.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq([1.0, 0.0, 0.0], tilt_vector(east)); + + println!("west"); + let west = PointerTiltEvent { + tilt_angle_x: -PI / 2.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq([-1.0, 0.0, 0.0], tilt_vector(west)); + + println!("north"); + let north = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: PI / 2.0, + }; + assert_approx_eq([0.0, 1.0, 0.0], tilt_vector(north)); + + println!("south"); + let south = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: -PI / 2.0, + }; + assert_approx_eq([0.0, -1.0, 0.0], tilt_vector(south)); + + let half_angle = 0.707107; + let circle_corner = PI/4.0; + println!("north_east"); + let north_east = PointerTiltEvent { + tilt_angle_x: circle_corner, + tilt_angle_y: circle_corner, + }; + assert_approx_eq([half_angle, half_angle, 0.0], tilt_vector(north_east)); + + println!("half_east"); + let half_east = PointerTiltEvent { + tilt_angle_x: PI / 4.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq([half_angle, 0.0, half_angle], tilt_vector(half_east)); + + println!("half_north"); + let half_north = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: PI / 4.0, + }; + assert_approx_eq([0.0, half_angle, half_angle], tilt_vector(half_north)); + + println!("half_north_east"); + let half_north_east = PointerTiltEvent { + tilt_angle_x: PI / 6.0, + tilt_angle_y: PI / 6.0, + }; + assert_approx_eq([0.5, 0.5, half_angle], tilt_vector(half_north_east)); + } + + #[test] + fn pointer_tilt_angle() { + let angle_slight = PI / 6.0; + let angle_lots = PI / 3.0; + let angle_full = PI / 2.0; + let diagonal_angle = |a: f64| (a.sin().powi(2)/2.0).sqrt().asin(); + let diagonal_slight = diagonal_angle(angle_slight); + let diagonal_lots = diagonal_angle(angle_lots); + let diagonal_full = diagonal_angle(angle_full); + + println!("up"); + let up = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: 0.0, + }; + assert_approx_eq!(0.0, up.tilt_angle()); + + println!("north_slight"); + let north_slight = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: angle_slight, + }; + assert_approx_eq!(angle_slight, north_slight.tilt_angle()); + + println!("north_lots"); + let north_lots = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: angle_lots, + }; + assert_approx_eq!(angle_lots, north_lots.tilt_angle()); + + println!("north_full"); + let north_full = PointerTiltEvent { + tilt_angle_x: 0.0, + tilt_angle_y: angle_full, + }; + assert_approx_eq!(angle_full, north_full.tilt_angle()); + + println!("north_east_slight"); + let north_east_slight = PointerTiltEvent { + tilt_angle_x: diagonal_slight, + tilt_angle_y: diagonal_slight, + }; + assert_approx_eq!(angle_slight, north_east_slight.tilt_angle()); + + println!("north_east_lots"); + let north_east_lots = PointerTiltEvent { + tilt_angle_x: -diagonal_lots, + tilt_angle_y: -diagonal_lots, + }; + assert_approx_eq!(angle_lots, north_east_lots.tilt_angle()); + + println!("south_east_full"); + let south_east_full = PointerTiltEvent { + tilt_angle_x: -diagonal_full, + tilt_angle_y: -diagonal_full, + }; + assert_approx_eq!(angle_full, south_east_full.tilt_angle()); + + + println!("south_slight"); + let south_slight = PointerTiltEvent { + tilt_angle_x: -0.0, + tilt_angle_y: -angle_slight, + }; + assert_approx_eq!(angle_slight, south_slight.tilt_angle()); + + println!("south_lots"); + let south_lots = PointerTiltEvent { + tilt_angle_x: -0.0, + tilt_angle_y: -angle_lots, + }; + assert_approx_eq!(angle_lots, south_lots.tilt_angle()); + + println!("south_full"); + let south_full = PointerTiltEvent { + tilt_angle_x: -0.0, + tilt_angle_y: -angle_full, + }; + assert_approx_eq!(angle_full, south_full.tilt_angle()); + + println!("south_west_slight"); + let south_west_slight = PointerTiltEvent { + tilt_angle_x: -diagonal_slight, + tilt_angle_y: -diagonal_slight, + }; + assert_approx_eq!(angle_slight, south_west_slight.tilt_angle()); + + println!("south_west_lots"); + let south_west_lots = PointerTiltEvent { + tilt_angle_x: -diagonal_lots, + tilt_angle_y: -diagonal_lots, + }; + assert_approx_eq!(angle_lots, south_west_lots.tilt_angle()); + + println!("south_west_full"); + let south_west_full = PointerTiltEvent { + tilt_angle_x: -diagonal_full, + tilt_angle_y: -diagonal_full, + }; + assert_approx_eq!(angle_full, south_west_full.tilt_angle()); + } +} diff --git a/src/lib.rs b/src/lib.rs index ce66665616..2037e6bc77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,9 +24,10 @@ //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can //! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the //! window or a key getting pressed while the window is focused. Devices can generate -//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. -//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a -//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired. +//! [`RawPointerEvent`]s and [`RawKeyboardEvent`]s, which contain unfiltered event data that isn't +//! specific to a certain window. Some user activity, like mouse movement, can generate both +//! [`WindowEvent`]s *and* raw events. You can also create and handle your own custom +//! [`UserEvent`]s, if desired. //! //! You can retreive events by calling [`EventLoop::run`][event_loop_run]. This function will //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and @@ -62,10 +63,7 @@ //! *control_flow = ControlFlow::Wait; //! //! match event { -//! Event::WindowEvent { -//! event: WindowEvent::CloseRequested, -//! .. -//! } => { +//! Event::WindowEvent(_, WindowEvent::CloseRequested) => { //! println!("The close button was pressed; stopping"); //! *control_flow = ControlFlow::Exit //! }, @@ -123,7 +121,8 @@ //! [window_id_fn]: window::Window::id //! [`Event`]: event::Event //! [`WindowEvent`]: event::WindowEvent -//! [`DeviceEvent`]: event::DeviceEvent +//! [`RawKeyboardEvent`]: event::RawKeyboardEvent +//! [`RawPointerEvent`]: event::RawPointerEvent //! [`UserEvent`]: event::Event::UserEvent //! [`LoopDestroyed`]: event::Event::LoopDestroyed //! [`platform`]: platform diff --git a/src/platform/windows.rs b/src/platform/windows.rs index a356f1e1c0..80c5d6df3a 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -9,7 +9,7 @@ use winapi::shared::windef::HWND; use crate::{ dpi::PhysicalSize, - event::DeviceId, + event::{KeyboardDeviceId, PointerDeviceId}, event_loop::EventLoop, monitor::MonitorHandle, platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, @@ -160,19 +160,39 @@ impl MonitorHandleExtWindows for MonitorHandle { } } -/// Additional methods on `DeviceId` that are specific to Windows. +/// Additional methods on device IDs that are specific to Windows. pub trait DeviceIdExtWindows { /// Returns an identifier that persistently refers to this specific device. /// /// Will return `None` if the device is no longer available. fn persistent_identifier(&self) -> Option; + + /// Returns the handle of the device - `HANDLE`. + fn handle(&self) -> *mut c_void; } -impl DeviceIdExtWindows for DeviceId { +impl DeviceIdExtWindows for PointerDeviceId { #[inline] fn persistent_identifier(&self) -> Option { self.0.persistent_identifier() } + + #[inline] + fn handle(&self) -> *mut c_void { + self.0.handle() as _ + } +} + +impl DeviceIdExtWindows for KeyboardDeviceId { + #[inline] + fn persistent_identifier(&self) -> Option { + self.0.persistent_identifier() + } + + #[inline] + fn handle(&self) -> *mut c_void { + self.0.handle() as _ + } } /// Additional methods on `Icon` that are specific to Windows. diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index f6c7a044d4..902e8bfeea 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -24,7 +24,7 @@ use winapi::{ use crate::platform_impl::platform::WindowId; -use crate::{event::Event, window::WindowId as SuperWindowId}; +use crate::event::{Event, WindowEvent}; #[repr(C)] pub struct FileDropHandlerData { @@ -92,13 +92,12 @@ impl FileDropHandler { _pt: *const POINTL, pdwEffect: *mut DWORD, ) -> HRESULT { - use crate::event::WindowEvent::HoveredFile; let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), - event: HoveredFile(filename), - }); + drop_handler.send_event(Event::WindowEvent( + WindowId(drop_handler.window).into(), + WindowEvent::FileHovered(filename), + )); }); drop_handler.hovered_is_valid = hdrop.is_some(); drop_handler.cursor_effect = if drop_handler.hovered_is_valid { @@ -124,13 +123,12 @@ impl FileDropHandler { } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use crate::event::WindowEvent::HoveredFileCancelled; let drop_handler = Self::from_interface(this); if drop_handler.hovered_is_valid { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), - event: HoveredFileCancelled, - }); + drop_handler.send_event(Event::WindowEvent( + WindowId(drop_handler.window).into(), + WindowEvent::FileHoverCancelled, + )); } S_OK @@ -143,13 +141,12 @@ impl FileDropHandler { _pt: *const POINTL, _pdwEffect: *mut DWORD, ) -> HRESULT { - use crate::event::WindowEvent::DroppedFile; let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), - event: DroppedFile(filename), - }); + drop_handler.send_event(Event::WindowEvent( + WindowId(drop_handler.window).into(), + WindowEvent::FileDropped(filename), + )); }); if let Some(hdrop) = hdrop { shellapi::DragFinish(hdrop); diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 49501f1a3c..b4402269be 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use std::{ char, os::raw::c_int, @@ -5,11 +7,18 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::event::{ModifiersState, ScanCode, VirtualKeyCode}; +use crate::{ + dpi::{PhysicalSize, PhysicalPosition}, + event::{LogicalKey, ModifiersState, Force, PointerTiltEvent, PointerButton, WindowEvent, PointerId, PointerButtonEvent}, +}; +use std::collections::hash_map::{HashMap, Entry}; use winapi::{ - shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, - um::winuser, + shared::{ + windef::HWND, + minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM, DWORD, BOOL}, + }, + um::{winuser, winreg}, }; fn key_pressed(vkey: c_int) -> bool { @@ -160,159 +169,159 @@ fn layout_uses_altgr() -> bool { } } -pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { +pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { // VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx match vkey { - //winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), - //winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), - //winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel), - //winuser::VK_MBUTTON => Some(VirtualKeyCode::Mbutton), - //winuser::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1), - //winuser::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2), - winuser::VK_BACK => Some(VirtualKeyCode::Back), - winuser::VK_TAB => Some(VirtualKeyCode::Tab), - //winuser::VK_CLEAR => Some(VirtualKeyCode::Clear), - winuser::VK_RETURN => Some(VirtualKeyCode::Return), - winuser::VK_LSHIFT => Some(VirtualKeyCode::LShift), - winuser::VK_RSHIFT => Some(VirtualKeyCode::RShift), - winuser::VK_LCONTROL => Some(VirtualKeyCode::LControl), - winuser::VK_RCONTROL => Some(VirtualKeyCode::RControl), - winuser::VK_LMENU => Some(VirtualKeyCode::LAlt), - winuser::VK_RMENU => Some(VirtualKeyCode::RAlt), - winuser::VK_PAUSE => Some(VirtualKeyCode::Pause), - winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital), - winuser::VK_KANA => Some(VirtualKeyCode::Kana), - //winuser::VK_HANGUEL => Some(VirtualKeyCode::Hanguel), - //winuser::VK_HANGUL => Some(VirtualKeyCode::Hangul), - //winuser::VK_JUNJA => Some(VirtualKeyCode::Junja), - //winuser::VK_FINAL => Some(VirtualKeyCode::Final), - //winuser::VK_HANJA => Some(VirtualKeyCode::Hanja), - winuser::VK_KANJI => Some(VirtualKeyCode::Kanji), - winuser::VK_ESCAPE => Some(VirtualKeyCode::Escape), - winuser::VK_CONVERT => Some(VirtualKeyCode::Convert), - winuser::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert), - //winuser::VK_ACCEPT => Some(VirtualKeyCode::Accept), - //winuser::VK_MODECHANGE => Some(VirtualKeyCode::Modechange), - winuser::VK_SPACE => Some(VirtualKeyCode::Space), - winuser::VK_PRIOR => Some(VirtualKeyCode::PageUp), - winuser::VK_NEXT => Some(VirtualKeyCode::PageDown), - winuser::VK_END => Some(VirtualKeyCode::End), - winuser::VK_HOME => Some(VirtualKeyCode::Home), - winuser::VK_LEFT => Some(VirtualKeyCode::Left), - winuser::VK_UP => Some(VirtualKeyCode::Up), - winuser::VK_RIGHT => Some(VirtualKeyCode::Right), - winuser::VK_DOWN => Some(VirtualKeyCode::Down), - //winuser::VK_SELECT => Some(VirtualKeyCode::Select), - //winuser::VK_PRINT => Some(VirtualKeyCode::Print), - //winuser::VK_EXECUTE => Some(VirtualKeyCode::Execute), - winuser::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot), - winuser::VK_INSERT => Some(VirtualKeyCode::Insert), - winuser::VK_DELETE => Some(VirtualKeyCode::Delete), - //winuser::VK_HELP => Some(VirtualKeyCode::Help), - 0x30 => Some(VirtualKeyCode::Key0), - 0x31 => Some(VirtualKeyCode::Key1), - 0x32 => Some(VirtualKeyCode::Key2), - 0x33 => Some(VirtualKeyCode::Key3), - 0x34 => Some(VirtualKeyCode::Key4), - 0x35 => Some(VirtualKeyCode::Key5), - 0x36 => Some(VirtualKeyCode::Key6), - 0x37 => Some(VirtualKeyCode::Key7), - 0x38 => Some(VirtualKeyCode::Key8), - 0x39 => Some(VirtualKeyCode::Key9), - 0x41 => Some(VirtualKeyCode::A), - 0x42 => Some(VirtualKeyCode::B), - 0x43 => Some(VirtualKeyCode::C), - 0x44 => Some(VirtualKeyCode::D), - 0x45 => Some(VirtualKeyCode::E), - 0x46 => Some(VirtualKeyCode::F), - 0x47 => Some(VirtualKeyCode::G), - 0x48 => Some(VirtualKeyCode::H), - 0x49 => Some(VirtualKeyCode::I), - 0x4A => Some(VirtualKeyCode::J), - 0x4B => Some(VirtualKeyCode::K), - 0x4C => Some(VirtualKeyCode::L), - 0x4D => Some(VirtualKeyCode::M), - 0x4E => Some(VirtualKeyCode::N), - 0x4F => Some(VirtualKeyCode::O), - 0x50 => Some(VirtualKeyCode::P), - 0x51 => Some(VirtualKeyCode::Q), - 0x52 => Some(VirtualKeyCode::R), - 0x53 => Some(VirtualKeyCode::S), - 0x54 => Some(VirtualKeyCode::T), - 0x55 => Some(VirtualKeyCode::U), - 0x56 => Some(VirtualKeyCode::V), - 0x57 => Some(VirtualKeyCode::W), - 0x58 => Some(VirtualKeyCode::X), - 0x59 => Some(VirtualKeyCode::Y), - 0x5A => Some(VirtualKeyCode::Z), - winuser::VK_LWIN => Some(VirtualKeyCode::LWin), - winuser::VK_RWIN => Some(VirtualKeyCode::RWin), - winuser::VK_APPS => Some(VirtualKeyCode::Apps), - winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep), - winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), - winuser::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1), - winuser::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2), - winuser::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3), - winuser::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4), - winuser::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5), - winuser::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6), - winuser::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7), - winuser::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8), - winuser::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9), - winuser::VK_MULTIPLY => Some(VirtualKeyCode::Multiply), - winuser::VK_ADD => Some(VirtualKeyCode::Add), - //winuser::VK_SEPARATOR => Some(VirtualKeyCode::Separator), - winuser::VK_SUBTRACT => Some(VirtualKeyCode::Subtract), - winuser::VK_DECIMAL => Some(VirtualKeyCode::Decimal), - winuser::VK_DIVIDE => Some(VirtualKeyCode::Divide), - winuser::VK_F1 => Some(VirtualKeyCode::F1), - winuser::VK_F2 => Some(VirtualKeyCode::F2), - winuser::VK_F3 => Some(VirtualKeyCode::F3), - winuser::VK_F4 => Some(VirtualKeyCode::F4), - winuser::VK_F5 => Some(VirtualKeyCode::F5), - winuser::VK_F6 => Some(VirtualKeyCode::F6), - winuser::VK_F7 => Some(VirtualKeyCode::F7), - winuser::VK_F8 => Some(VirtualKeyCode::F8), - winuser::VK_F9 => Some(VirtualKeyCode::F9), - winuser::VK_F10 => Some(VirtualKeyCode::F10), - winuser::VK_F11 => Some(VirtualKeyCode::F11), - winuser::VK_F12 => Some(VirtualKeyCode::F12), - winuser::VK_F13 => Some(VirtualKeyCode::F13), - winuser::VK_F14 => Some(VirtualKeyCode::F14), - winuser::VK_F15 => Some(VirtualKeyCode::F15), - winuser::VK_F16 => Some(VirtualKeyCode::F16), - winuser::VK_F17 => Some(VirtualKeyCode::F17), - winuser::VK_F18 => Some(VirtualKeyCode::F18), - winuser::VK_F19 => Some(VirtualKeyCode::F19), - winuser::VK_F20 => Some(VirtualKeyCode::F20), - winuser::VK_F21 => Some(VirtualKeyCode::F21), - winuser::VK_F22 => Some(VirtualKeyCode::F22), - winuser::VK_F23 => Some(VirtualKeyCode::F23), - winuser::VK_F24 => Some(VirtualKeyCode::F24), - winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock), - winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll), - winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward), - winuser::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward), - winuser::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh), - winuser::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop), - winuser::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch), - winuser::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites), - winuser::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome), - winuser::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute), - winuser::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown), - winuser::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp), - winuser::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack), - winuser::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack), - winuser::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop), - winuser::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause), - winuser::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail), - winuser::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect), - /*winuser::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1), - winuser::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/ - winuser::VK_OEM_PLUS => Some(VirtualKeyCode::Equals), - winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma), - winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus), - winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period), + //winuser::VK_LBUTTON => Some(LogicalKey::Lbutton), + //winuser::VK_RBUTTON => Some(LogicalKey::Rbutton), + //winuser::VK_CANCEL => Some(LogicalKey::Cancel), + //winuser::VK_MBUTTON => Some(LogicalKey::Mbutton), + //winuser::VK_XBUTTON1 => Some(LogicalKey::Xbutton1), + //winuser::VK_XBUTTON2 => Some(LogicalKey::Xbutton2), + winuser::VK_BACK => Some(LogicalKey::Back), + winuser::VK_TAB => Some(LogicalKey::Tab), + //winuser::VK_CLEAR => Some(LogicalKey::Clear), + winuser::VK_RETURN => Some(LogicalKey::Return), + winuser::VK_LSHIFT => Some(LogicalKey::LShift), + winuser::VK_RSHIFT => Some(LogicalKey::RShift), + winuser::VK_LCONTROL => Some(LogicalKey::LControl), + winuser::VK_RCONTROL => Some(LogicalKey::RControl), + winuser::VK_LMENU => Some(LogicalKey::LAlt), + winuser::VK_RMENU => Some(LogicalKey::RAlt), + winuser::VK_PAUSE => Some(LogicalKey::Pause), + winuser::VK_CAPITAL => Some(LogicalKey::Capital), + winuser::VK_KANA => Some(LogicalKey::Kana), + //winuser::VK_HANGUEL => Some(LogicalKey::Hanguel), + //winuser::VK_HANGUL => Some(LogicalKey::Hangul), + //winuser::VK_JUNJA => Some(LogicalKey::Junja), + //winuser::VK_FINAL => Some(LogicalKey::Final), + //winuser::VK_HANJA => Some(LogicalKey::Hanja), + winuser::VK_KANJI => Some(LogicalKey::Kanji), + winuser::VK_ESCAPE => Some(LogicalKey::Escape), + winuser::VK_CONVERT => Some(LogicalKey::Convert), + winuser::VK_NONCONVERT => Some(LogicalKey::NoConvert), + //winuser::VK_ACCEPT => Some(LogicalKey::Accept), + //winuser::VK_MODECHANGE => Some(LogicalKey::Modechange), + winuser::VK_SPACE => Some(LogicalKey::Space), + winuser::VK_PRIOR => Some(LogicalKey::PageUp), + winuser::VK_NEXT => Some(LogicalKey::PageDown), + winuser::VK_END => Some(LogicalKey::End), + winuser::VK_HOME => Some(LogicalKey::Home), + winuser::VK_LEFT => Some(LogicalKey::Left), + winuser::VK_UP => Some(LogicalKey::Up), + winuser::VK_RIGHT => Some(LogicalKey::Right), + winuser::VK_DOWN => Some(LogicalKey::Down), + //winuser::VK_SELECT => Some(LogicalKey::Select), + //winuser::VK_PRINT => Some(LogicalKey::Print), + //winuser::VK_EXECUTE => Some(LogicalKey::Execute), + winuser::VK_SNAPSHOT => Some(LogicalKey::Snapshot), + winuser::VK_INSERT => Some(LogicalKey::Insert), + winuser::VK_DELETE => Some(LogicalKey::Delete), + //winuser::VK_HELP => Some(LogicalKey::Help), + 0x30 => Some(LogicalKey::Key0), + 0x31 => Some(LogicalKey::Key1), + 0x32 => Some(LogicalKey::Key2), + 0x33 => Some(LogicalKey::Key3), + 0x34 => Some(LogicalKey::Key4), + 0x35 => Some(LogicalKey::Key5), + 0x36 => Some(LogicalKey::Key6), + 0x37 => Some(LogicalKey::Key7), + 0x38 => Some(LogicalKey::Key8), + 0x39 => Some(LogicalKey::Key9), + 0x41 => Some(LogicalKey::A), + 0x42 => Some(LogicalKey::B), + 0x43 => Some(LogicalKey::C), + 0x44 => Some(LogicalKey::D), + 0x45 => Some(LogicalKey::E), + 0x46 => Some(LogicalKey::F), + 0x47 => Some(LogicalKey::G), + 0x48 => Some(LogicalKey::H), + 0x49 => Some(LogicalKey::I), + 0x4A => Some(LogicalKey::J), + 0x4B => Some(LogicalKey::K), + 0x4C => Some(LogicalKey::L), + 0x4D => Some(LogicalKey::M), + 0x4E => Some(LogicalKey::N), + 0x4F => Some(LogicalKey::O), + 0x50 => Some(LogicalKey::P), + 0x51 => Some(LogicalKey::Q), + 0x52 => Some(LogicalKey::R), + 0x53 => Some(LogicalKey::S), + 0x54 => Some(LogicalKey::T), + 0x55 => Some(LogicalKey::U), + 0x56 => Some(LogicalKey::V), + 0x57 => Some(LogicalKey::W), + 0x58 => Some(LogicalKey::X), + 0x59 => Some(LogicalKey::Y), + 0x5A => Some(LogicalKey::Z), + winuser::VK_LWIN => Some(LogicalKey::LWin), + winuser::VK_RWIN => Some(LogicalKey::RWin), + winuser::VK_APPS => Some(LogicalKey::Apps), + winuser::VK_SLEEP => Some(LogicalKey::Sleep), + winuser::VK_NUMPAD0 => Some(LogicalKey::Numpad0), + winuser::VK_NUMPAD1 => Some(LogicalKey::Numpad1), + winuser::VK_NUMPAD2 => Some(LogicalKey::Numpad2), + winuser::VK_NUMPAD3 => Some(LogicalKey::Numpad3), + winuser::VK_NUMPAD4 => Some(LogicalKey::Numpad4), + winuser::VK_NUMPAD5 => Some(LogicalKey::Numpad5), + winuser::VK_NUMPAD6 => Some(LogicalKey::Numpad6), + winuser::VK_NUMPAD7 => Some(LogicalKey::Numpad7), + winuser::VK_NUMPAD8 => Some(LogicalKey::Numpad8), + winuser::VK_NUMPAD9 => Some(LogicalKey::Numpad9), + winuser::VK_MULTIPLY => Some(LogicalKey::Multiply), + winuser::VK_ADD => Some(LogicalKey::Add), + //winuser::VK_SEPARATOR => Some(LogicalKey::Separator), + winuser::VK_SUBTRACT => Some(LogicalKey::Subtract), + winuser::VK_DECIMAL => Some(LogicalKey::Decimal), + winuser::VK_DIVIDE => Some(LogicalKey::Divide), + winuser::VK_F1 => Some(LogicalKey::F1), + winuser::VK_F2 => Some(LogicalKey::F2), + winuser::VK_F3 => Some(LogicalKey::F3), + winuser::VK_F4 => Some(LogicalKey::F4), + winuser::VK_F5 => Some(LogicalKey::F5), + winuser::VK_F6 => Some(LogicalKey::F6), + winuser::VK_F7 => Some(LogicalKey::F7), + winuser::VK_F8 => Some(LogicalKey::F8), + winuser::VK_F9 => Some(LogicalKey::F9), + winuser::VK_F10 => Some(LogicalKey::F10), + winuser::VK_F11 => Some(LogicalKey::F11), + winuser::VK_F12 => Some(LogicalKey::F12), + winuser::VK_F13 => Some(LogicalKey::F13), + winuser::VK_F14 => Some(LogicalKey::F14), + winuser::VK_F15 => Some(LogicalKey::F15), + winuser::VK_F16 => Some(LogicalKey::F16), + winuser::VK_F17 => Some(LogicalKey::F17), + winuser::VK_F18 => Some(LogicalKey::F18), + winuser::VK_F19 => Some(LogicalKey::F19), + winuser::VK_F20 => Some(LogicalKey::F20), + winuser::VK_F21 => Some(LogicalKey::F21), + winuser::VK_F22 => Some(LogicalKey::F22), + winuser::VK_F23 => Some(LogicalKey::F23), + winuser::VK_F24 => Some(LogicalKey::F24), + winuser::VK_NUMLOCK => Some(LogicalKey::Numlock), + winuser::VK_SCROLL => Some(LogicalKey::Scroll), + winuser::VK_BROWSER_BACK => Some(LogicalKey::NavigateBackward), + winuser::VK_BROWSER_FORWARD => Some(LogicalKey::NavigateForward), + winuser::VK_BROWSER_REFRESH => Some(LogicalKey::WebRefresh), + winuser::VK_BROWSER_STOP => Some(LogicalKey::WebStop), + winuser::VK_BROWSER_SEARCH => Some(LogicalKey::WebSearch), + winuser::VK_BROWSER_FAVORITES => Some(LogicalKey::WebFavorites), + winuser::VK_BROWSER_HOME => Some(LogicalKey::WebHome), + winuser::VK_VOLUME_MUTE => Some(LogicalKey::Mute), + winuser::VK_VOLUME_DOWN => Some(LogicalKey::VolumeDown), + winuser::VK_VOLUME_UP => Some(LogicalKey::VolumeUp), + winuser::VK_MEDIA_NEXT_TRACK => Some(LogicalKey::NextTrack), + winuser::VK_MEDIA_PREV_TRACK => Some(LogicalKey::PrevTrack), + winuser::VK_MEDIA_STOP => Some(LogicalKey::MediaStop), + winuser::VK_MEDIA_PLAY_PAUSE => Some(LogicalKey::PlayPause), + winuser::VK_LAUNCH_MAIL => Some(LogicalKey::Mail), + winuser::VK_LAUNCH_MEDIA_SELECT => Some(LogicalKey::MediaSelect), + /*winuser::VK_LAUNCH_APP1 => Some(LogicalKey::Launch_app1), + winuser::VK_LAUNCH_APP2 => Some(LogicalKey::Launch_app2),*/ + winuser::VK_OEM_PLUS => Some(LogicalKey::Equals), + winuser::VK_OEM_COMMA => Some(LogicalKey::Comma), + winuser::VK_OEM_MINUS => Some(LogicalKey::Minus), + winuser::VK_OEM_PERIOD => Some(LogicalKey::Period), winuser::VK_OEM_1 => map_text_keys(vkey), winuser::VK_OEM_2 => map_text_keys(vkey), winuser::VK_OEM_3 => map_text_keys(vkey), @@ -320,19 +329,19 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { winuser::VK_OEM_5 => map_text_keys(vkey), winuser::VK_OEM_6 => map_text_keys(vkey), winuser::VK_OEM_7 => map_text_keys(vkey), - /* winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */ - winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102), - /*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), - winuser::VK_PACKET => Some(VirtualKeyCode::Packet), - winuser::VK_ATTN => Some(VirtualKeyCode::Attn), - winuser::VK_CRSEL => Some(VirtualKeyCode::Crsel), - winuser::VK_EXSEL => Some(VirtualKeyCode::Exsel), - winuser::VK_EREOF => Some(VirtualKeyCode::Ereof), - winuser::VK_PLAY => Some(VirtualKeyCode::Play), - winuser::VK_ZOOM => Some(VirtualKeyCode::Zoom), - winuser::VK_NONAME => Some(VirtualKeyCode::Noname), - winuser::VK_PA1 => Some(VirtualKeyCode::Pa1), - winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ + /* winuser::VK_OEM_8 => Some(LogicalKey::Oem_8), */ + winuser::VK_OEM_102 => Some(LogicalKey::OEM102), + /*winuser::VK_PROCESSKEY => Some(LogicalKey::Processkey), + winuser::VK_PACKET => Some(LogicalKey::Packet), + winuser::VK_ATTN => Some(LogicalKey::Attn), + winuser::VK_CRSEL => Some(LogicalKey::Crsel), + winuser::VK_EXSEL => Some(LogicalKey::Exsel), + winuser::VK_EREOF => Some(LogicalKey::Ereof), + winuser::VK_PLAY => Some(LogicalKey::Play), + winuser::VK_ZOOM => Some(LogicalKey::Zoom), + winuser::VK_NONAME => Some(LogicalKey::Noname), + winuser::VK_PA1 => Some(LogicalKey::Pa1), + winuser::VK_OEM_CLEAR => Some(LogicalKey::Oem_clear),*/ _ => None, } } @@ -387,27 +396,310 @@ pub fn handle_extended_keys( pub fn process_key_params( wparam: WPARAM, lparam: LPARAM, -) -> Option<(ScanCode, Option)> { +) -> Option<(u32, Option, bool)> { let scancode = ((lparam >> 16) & 0xff) as UINT; let extended = (lparam & 0x01000000) != 0; + let is_repeat = (lparam & 0x7fff) != 0; handle_extended_keys(wparam as _, scancode, extended) - .map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey))) + .map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey), is_repeat)) } // This is needed as windows doesn't properly distinguish // some virtual key codes for different keyboard layouts -fn map_text_keys(win_virtual_key: i32) -> Option { +fn map_text_keys(win_virtual_key: i32) -> Option { let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } & 0x7FFF; match char::from_u32(char_key) { - Some(';') => Some(VirtualKeyCode::Semicolon), - Some('/') => Some(VirtualKeyCode::Slash), - Some('`') => Some(VirtualKeyCode::Grave), - Some('[') => Some(VirtualKeyCode::LBracket), - Some(']') => Some(VirtualKeyCode::RBracket), - Some('\'') => Some(VirtualKeyCode::Apostrophe), - Some('\\') => Some(VirtualKeyCode::Backslash), + Some(';') => Some(LogicalKey::Semicolon), + Some('/') => Some(LogicalKey::Slash), + Some('`') => Some(LogicalKey::Grave), + Some('[') => Some(LogicalKey::LBracket), + Some(']') => Some(LogicalKey::RBracket), + Some('\'') => Some(LogicalKey::Apostrophe), + Some('\\') => Some(LogicalKey::Backslash), _ => None, } } + +bitflags!{ + pub struct ButtonsDown: u8 { + const BUTTON_1 = 1 << 1; + const BUTTON_2 = 1 << 2; + const BUTTON_3 = 1 << 3; + const BUTTON_4 = 1 << 4; + const BUTTON_5 = 1 << 5; + const BUTTON_6 = 1 << 6; + } +} + +impl PointerButton { + pub(crate) fn into_flags(&self) -> ButtonsDown { + use crate::event::PointerButtonInner::*; + match self.0 { + BUTTON_1 => ButtonsDown::BUTTON_1, + BUTTON_2 => ButtonsDown::BUTTON_2, + BUTTON_3 => ButtonsDown::BUTTON_3, + BUTTON_4 => ButtonsDown::BUTTON_4, + BUTTON_5 => ButtonsDown::BUTTON_5, + BUTTON_6 => ButtonsDown::BUTTON_6, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PointerState { + pub in_window: bool, + pub force: Force, + pub tilt: PointerTiltEvent, + pub twist: f64, + pub contact_area: PhysicalSize, + pub position: PhysicalPosition, + pub buttons_down: ButtonsDown, +} + +#[derive(Default, Debug, Clone, Copy)] +struct FullPointerState { + state: PointerState, + last_button_down: Option, +} + +#[derive(Debug, Clone, Copy)] +struct LastButtonDown { + button: PointerButton, + time: DWORD, + position: PhysicalPosition, + multi_click_count: u32, +} + +impl Default for PointerState { + fn default() -> PointerState { + PointerState { + in_window: false, + force: Force::Normalized(0.0), + tilt: PointerTiltEvent::new_tilt_angle(0.0, 0.0), + twist: 0.0, + contact_area: PhysicalSize::new(1.0, 1.0), + position: PhysicalPosition::new(f64::NEG_INFINITY, f64::NEG_INFINITY), + buttons_down: ButtonsDown::empty(), + } + } +} + +type EnableMouseInPointer = + unsafe extern "system" fn(fEnable: BOOL) -> BOOL; + +lazy_static! { + static ref ENABLE_MOUSE_IN_POINTER: Option = + get_function!("user32.dll", EnableMouseInPointer); +} + +#[derive(Clone)] +pub struct PointerTracker { + pointer_info: HashMap, + call_legacy_capture_fns: bool, +} + +impl PointerTracker { + pub fn new() -> PointerTracker { + PointerTracker { + pointer_info: HashMap::new(), + call_legacy_capture_fns: match *ENABLE_MOUSE_IN_POINTER { + Some(EnableMouseInPointer) => unsafe{ EnableMouseInPointer(1) == 0 }, + None => true, + }, + } + } + + pub fn update_pointer_state( + &mut self, + pointer_id: PointerId, + message_time: i32, + window: HWND, + new_state: impl FnOnce(PointerState) -> PointerState, + mut send_event: impl FnMut(WindowEvent<'static>) + ) { + let entry = self.pointer_info.entry(pointer_id); + if let Entry::Vacant(_) = entry { + send_event(WindowEvent::PointerCreated(pointer_id)); + } + let message_time = message_time as DWORD; + + let full_pointer_state = entry.or_default(); + let new_state = new_state(full_pointer_state.state); + + let &mut FullPointerState { + state: PointerState { + ref mut in_window, + ref mut force, + ref mut tilt, + ref mut twist, + ref mut contact_area, + ref mut position, + ref mut buttons_down, + }, + ref mut last_button_down, + } = full_pointer_state; + + if *in_window != new_state.in_window && new_state.in_window { + *in_window = new_state.in_window; + send_event(WindowEvent::PointerEntered(pointer_id)); + + if self.call_legacy_capture_fns { + unsafe { + // Calling TrackMouseEvent in order to receive mouse leave events. + winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT { + cbSize: std::mem::size_of::() as DWORD, + dwFlags: winuser::TME_LEAVE, + hwndTrack: window, + dwHoverTime: winuser::HOVER_DEFAULT, + }); + } + } + } + if *force != new_state.force { + *force = new_state.force; + send_event(WindowEvent::PointerForce(pointer_id, *force)) + } + if *tilt != new_state.tilt { + *tilt = new_state.tilt; + send_event(WindowEvent::PointerTilt(pointer_id, *tilt)) + } + if *twist != new_state.twist { + *twist = new_state.twist; + send_event(WindowEvent::PointerTwist(pointer_id, *twist)) + } + if *contact_area != new_state.contact_area { + *contact_area = new_state.contact_area; + send_event(WindowEvent::PointerContactArea(pointer_id, *contact_area)) + } + if *position != new_state.position { + *position = new_state.position; + send_event(WindowEvent::PointerMoved(pointer_id, *position)) + } + if *buttons_down != new_state.buttons_down { + let diff = *buttons_down ^ new_state.buttons_down; + if diff != ButtonsDown::empty() { + *buttons_down = new_state.buttons_down; + + if self.call_legacy_capture_fns && pointer_id == PointerId::MOUSE_ID { + unsafe { + match *buttons_down == ButtonsDown::empty() { + // Capture mouse input, allowing `window` to receive mouse events when + // the cursor is outside of the window. + false => {winuser::SetCapture(window);} + // Release mouse input, stopping windows on this thread from receiving + // mouse input when the cursor is outside the window. + true => {winuser::ReleaseCapture();} + } + } + } + + let buttons = [ + (PointerButton::BUTTON_1, ButtonsDown::BUTTON_1), + (PointerButton::BUTTON_2, ButtonsDown::BUTTON_2), + (PointerButton::BUTTON_3, ButtonsDown::BUTTON_3), + (PointerButton::BUTTON_4, ButtonsDown::BUTTON_4), + (PointerButton::BUTTON_5, ButtonsDown::BUTTON_5), + (PointerButton::BUTTON_6, ButtonsDown::BUTTON_6), + ]; + + let (double_click_time, cx_double_click, cy_double_click): (DWORD, f64, f64); + match pointer_id { + PointerId::TouchId(_) | + PointerId::PenId(_) => unsafe { + let sub_key = "Software\\Microsoft\\Wisp\\Pen\\SysEventParameters\0"; + let dist_value = "DblDist\0"; + let time_value = "DblTime\0"; + + let mut time: DWORD = 0; + let result = winreg::RegGetValueA( + winreg::HKEY_CURRENT_USER, + sub_key.as_ptr() as *const _, + time_value.as_ptr() as *const _, + winreg::RRF_RT_REG_DWORD, + ptr::null_mut(), + &mut time as *mut DWORD as *mut _, + &mut (std::mem::size_of::() as DWORD), + ); + double_click_time = match result { + 0 => time, + _ => winuser::GetDoubleClickTime(), + }; + + let mut dist: DWORD = 0; + let result = winreg::RegGetValueA( + winreg::HKEY_CURRENT_USER, + sub_key.as_ptr() as *const _, + dist_value.as_ptr() as *const _, + winreg::RRF_RT_REG_DWORD, + ptr::null_mut(), + &mut dist as *mut DWORD as *mut _, + &mut (std::mem::size_of::() as DWORD), + ); + cx_double_click = match result { + 0 => dist as f64, + _ => winuser::GetSystemMetrics(winuser::SM_CXDOUBLECLK) as f64, + }; + cy_double_click = cx_double_click; + }, + PointerId::MouseId(_) => unsafe { + double_click_time = winuser::GetDoubleClickTime(); + cx_double_click = winuser::GetSystemMetrics(winuser::SM_CXDOUBLECLK) as f64; + cy_double_click = winuser::GetSystemMetrics(winuser::SM_CYDOUBLECLK) as f64; + } + } + let scale_factor = unsafe{ winuser::GetDpiForWindow(window) as f64 / 96.0 }; + let cx_double_click = cx_double_click * scale_factor; + let cy_double_click = cy_double_click * scale_factor; + + let mut last_click_set = false; + for (pointer_button, button_flag) in buttons.iter().cloned() { + if diff.contains(button_flag) { + let multi_click_count = match last_button_down { + Some(lbd) if + lbd.button == pointer_button && + (message_time.saturating_sub(lbd.time)) < double_click_time && + (lbd.position.x - position.x).abs() < cx_double_click && + (lbd.position.y - position.y).abs() < cy_double_click + => { + lbd.multi_click_count + }, + _ => 0, + }; + let is_down = buttons_down.contains(button_flag); + + send_event(WindowEvent::PointerButton( + pointer_id, + PointerButtonEvent { + button: pointer_button, + is_down, + multi_click_count, + }, + )); + + if !last_click_set && !is_down { + last_click_set = true; + *last_button_down = Some(LastButtonDown { + button: pointer_button, + time: message_time, + position: *position, + multi_click_count: multi_click_count + 1, + }); + } + } + } + } + } + if *in_window != new_state.in_window && !new_state.in_window { + *in_window = new_state.in_window; + send_event(WindowEvent::PointerLeft(pointer_id)); + } + } + + pub fn destroy_pointer(&mut self, pointer_id: PointerId, mut send_event: impl FnMut(WindowEvent<'static>)) { + if self.pointer_info.remove(&pointer_id).is_some() { + send_event(WindowEvent::PointerDestroyed(pointer_id)); + } + } +} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a7deac50c5..3702d35271 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -24,25 +24,29 @@ use winapi::{ }, um::{ commctrl, libloaderapi, ole2, processthreadsapi, winbase, - winnt::{HANDLE, LONG, LPCSTR, SHORT}, + winnt::{HANDLE, LONG, LPCSTR}, winuser, }, }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, + dpi::{PhysicalDelta, PhysicalPosition, PhysicalSize, UnitlessDelta}, + event::{ + Event, Force, KeyEvent, LogicalKey, ModifiersState, PointerButton, + PointerId, RawKeyEvent, RawKeyboardEvent, RawPointerButtonEvent, RawPointerEvent, + WindowEvent, PointerTiltEvent, + }, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, platform_impl::platform::{ dark_mode::try_dark_mode, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, - event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, + event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, PointerState, ButtonsDown}, monitor, raw_input, util, - window_state::{CursorFlags, WindowFlags, WindowState}, - wrap_device_id, WindowId, DEVICE_ID, + window_state::{WindowFlags, WindowState}, + KeyboardDeviceId, PointerDeviceId, TouchId, WindowId, PenId, }, - window::{Fullscreen, WindowId as RootWindowId}, + window::{Fullscreen, Theme}, }; use runner::{EventLoopRunner, EventLoopRunnerShared}; @@ -66,6 +70,9 @@ type GetPointerTouchInfo = type GetPointerPenInfo = unsafe extern "system" fn(pointId: UINT, penInfo: *mut winuser::POINTER_PEN_INFO) -> BOOL; +type GetPointerInfo = + unsafe extern "system" fn(pointId: UINT, penInfo: *mut winuser::POINTER_INFO) -> BOOL; + lazy_static! { static ref GET_POINTER_FRAME_INFO_HISTORY: Option = get_function!("user32.dll", GetPointerFrameInfoHistory); @@ -77,6 +84,8 @@ lazy_static! { get_function!("user32.dll", GetPointerTouchInfo); static ref GET_POINTER_PEN_INFO: Option = get_function!("user32.dll", GetPointerPenInfo); + static ref GET_POINTER_INFO: Option = + get_function!("user32.dll", GetPointerInfo); } pub(crate) struct SubclassInput { @@ -89,6 +98,59 @@ impl SubclassInput { unsafe fn send_event(&self, event: Event<'_, T>) { self.event_loop_runner.send_event(event); } + + unsafe fn lock_and_update_pointer_state( + &self, + pointer_id: PointerId, + message_time: i32, + window: HWND, + new_state: impl FnOnce(PointerState) -> PointerState, + ) { + let mut event_buffer = Vec::new(); + + { + let mut w = self.window_state.lock(); + w.mouse.pointer_tracker.update_pointer_state( + pointer_id, + message_time, + window, + new_state, + |event| event_buffer.push(event), + ); + w.mouse.cursor_flags().refresh_os_cursor(window).ok(); + } + + for event in event_buffer { + self.send_event(Event::WindowEvent( + WindowId(window).into(), + event, + )) + } + } + + unsafe fn lock_and_destroy_pointer( + &self, + pointer_id: PointerId, + window: HWND, + ) { + let mut event_opt = None; + + { + let mut w = self.window_state.lock(); + w.mouse.pointer_tracker.destroy_pointer( + pointer_id, + |event| assert!(event_opt.replace(event).is_none()), + ); + w.mouse.cursor_flags().refresh_os_cursor(window).ok(); + } + + for event in event_opt { + self.send_event(Event::WindowEvent( + WindowId(window).into(), + event, + )) + } + } } struct ThreadMsgTargetSubclassInput { @@ -603,22 +665,6 @@ fn subclass_event_target_window( } } -/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of -/// the window. -unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) { - window_state.mouse.buttons_down += 1; - winuser::SetCapture(window); -} - -/// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor -/// is outside the window. -unsafe fn release_mouse(window_state: &mut WindowState) { - window_state.mouse.buttons_down = window_state.mouse.buttons_down.saturating_sub(1); - if window_state.mouse.buttons_down == 0 { - winuser::ReleaseCapture(); - } -} - const WINDOW_SUBCLASS_ID: UINT_PTR = 0; const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1; pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) { @@ -637,7 +683,7 @@ pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) fn normalize_pointer_pressure(pressure: u32) -> Option { match pressure { - 1..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)), + 0..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)), _ => None, } } @@ -706,8 +752,6 @@ unsafe fn process_control_flow(runner: &EventLoopRunner) { /// Emit a `ModifiersChanged` event whenever modifiers have changed. fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { - use crate::event::WindowEvent::ModifiersChanged; - let modifiers = event::get_key_mods(); let mut window_state = subclass_input.window_state.lock(); if window_state.modifiers_state != modifiers { @@ -717,10 +761,10 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { drop(window_state); unsafe { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ModifiersChanged(modifiers), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ModifiersChanged(modifiers), + )); } } } @@ -774,28 +818,26 @@ unsafe extern "system" fn public_window_callback( commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_NCLBUTTONDOWN => { - if wparam == winuser::HTCAPTION as _ { + if wparam == winuser::HTCAPTION as WPARAM { winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); } commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_CLOSE => { - use crate::event::WindowEvent::CloseRequested; - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: CloseRequested, - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::CloseRequested, + )); 0 } winuser::WM_DESTROY => { - use crate::event::WindowEvent::Destroyed; ole2::RevokeDragDrop(window); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Destroyed, - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Destroyed, + )); subclass_input.event_loop_runner.remove_window(window); drop(subclass_input); @@ -816,7 +858,7 @@ unsafe extern "system" fn public_window_callback( } else { let managing_redraw = flush_paint_messages(Some(window), &subclass_input.event_loop_runner); - subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window)))); + subclass_input.send_event(Event::RedrawRequested(WindowId(window).into())); if managing_redraw { subclass_input.event_loop_runner.redraw_events_cleared(); process_control_flow(&subclass_input.event_loop_runner); @@ -873,16 +915,14 @@ unsafe extern "system" fn public_window_callback( // WM_MOVE supplies client area positions, so we send Moved here instead. winuser::WM_WINDOWPOSCHANGED => { - use crate::event::WindowEvent::Moved; - let windowpos = lparam as *const winuser::WINDOWPOS; if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE { let physical_position = PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Moved(physical_position), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Moved(physical_position), + )); } // This is necessary for us to still get sent WM_SIZE. @@ -890,15 +930,12 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_SIZE => { - use crate::event::WindowEvent::Resized; let w = LOWORD(lparam as DWORD) as u32; let h = HIWORD(lparam as DWORD) as u32; let physical_size = PhysicalSize::new(w, h); - let event = Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Resized(physical_size), - }; + let event = + Event::WindowEvent(WindowId(window).into(), WindowEvent::Resized(physical_size)); { let mut w = subclass_input.window_state.lock(); @@ -917,7 +954,6 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_CHAR | winuser::WM_SYSCHAR => { - use crate::event::WindowEvent::ReceivedCharacter; use std::char; let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -930,20 +966,20 @@ unsafe extern "system" fn public_window_callback( if let Some(high_surrogate) = high_surrogate { let pair = [high_surrogate, wparam as u16]; if let Some(Ok(chr)) = char::decode_utf16(pair.iter().copied()).next() { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ReceivedCharacter(chr), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::CharReceived(chr), + )); } } } else { subclass_input.window_state.lock().high_surrogate = None; if let Some(chr) = char::from_u32(wparam as u32) { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ReceivedCharacter(chr), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::CharReceived(chr), + )); } } 0 @@ -972,154 +1008,111 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_MOUSEMOVE => { - use crate::event::WindowEvent::{CursorEntered, CursorMoved}; - let mouse_was_outside_window = { - let mut w = subclass_input.window_state.lock(); - - let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW); - w.mouse - .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true)) - .ok(); - was_outside_window - }; - - if mouse_was_outside_window { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: CursorEntered { - device_id: DEVICE_ID, - }, - }); - - // Calling TrackMouseEvent in order to receive mouse leave events. - winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT { - cbSize: mem::size_of::() as DWORD, - dwFlags: winuser::TME_LEAVE, - hwndTrack: window, - dwHoverTime: winuser::HOVER_DEFAULT, - }); - } - let x = windowsx::GET_X_LPARAM(lparam) as f64; let y = windowsx::GET_Y_LPARAM(lparam) as f64; let position = PhysicalPosition::new(x, y); - let cursor_moved; - { - // handle spurious WM_MOUSEMOVE messages - // see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343 - // and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html - let mut w = subclass_input.window_state.lock(); - cursor_moved = w.mouse.last_position != Some(position); - w.mouse.last_position = Some(position); - } - if cursor_moved { - update_modifiers(window, subclass_input); + update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: CursorMoved { - device_id: DEVICE_ID, - position, - modifiers: event::get_key_mods(), - }, - }); - } + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + position, + in_window: true, + ..pointer_state + }, + ); 0 } winuser::WM_MOUSELEAVE => { - use crate::event::WindowEvent::CursorLeft; - { - let mut w = subclass_input.window_state.lock(); - w.mouse - .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)) - .ok(); - } - - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: CursorLeft { - device_id: DEVICE_ID, + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + in_window: false, + ..pointer_state }, - }); + ); 0 } winuser::WM_MOUSEWHEEL => { - use crate::event::MouseScrollDelta::LineDelta; - let value = (wparam >> 16) as i16; let value = value as i32; - let value = value as f32 / winuser::WHEEL_DELTA as f32; + let value = value as f64 / winuser::WHEEL_DELTA as f64; update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::MouseWheel { - device_id: DEVICE_ID, - delta: LineDelta(0.0, value), - phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), - }, - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollStarted, + )); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollLines(UnitlessDelta::new(0.0, value)), + )); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollEnded, + )); 0 } winuser::WM_MOUSEHWHEEL => { - use crate::event::MouseScrollDelta::LineDelta; - let value = (wparam >> 16) as i16; let value = value as i32; - let value = value as f32 / winuser::WHEEL_DELTA as f32; + let value = value as f64 / winuser::WHEEL_DELTA as f64; update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::MouseWheel { - device_id: DEVICE_ID, - delta: LineDelta(value, 0.0), - phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), - }, - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollStarted, + )); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollLines(UnitlessDelta::new(value, 0.0)), + )); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScrollEnded, + )); 0 } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - use crate::event::{ElementState::Pressed, VirtualKeyCode}; if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + if let Some((scan_code, logical_key, is_repeat)) = + process_key_params(wparam, lparam) + { update_modifiers(window, subclass_input); - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Pressed, - scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Key(KeyEvent { + logical_key, + scan_code, + is_down: true, + is_repeat, is_synthetic: false, - }, - }); + }), + )); // Windows doesn't emit a delete character by default, but in order to make it // consistent with the other platforms we'll emit a delete character here. - if vkey == Some(VirtualKeyCode::Delete) { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::ReceivedCharacter('\u{7F}'), - }); + if logical_key == Some(LogicalKey::Delete) { + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::CharReceived('\u{7F}'), + )); } } 0 @@ -1127,199 +1120,164 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - use crate::event::ElementState::Released; - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + if let Some((scan_code, logical_key, _)) = process_key_params(wparam, lparam) { update_modifiers(window, subclass_input); - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Released, - scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Key(KeyEvent { + logical_key, + scan_code, + is_down: false, + is_repeat: false, is_synthetic: false, - }, - }); + }), + )); } 0 } winuser::WM_LBUTTONDOWN => { - use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput}; - - capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Pressed, - button: Left, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down | PointerButton::MOUSE_LEFT.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_LBUTTONUP => { - use crate::event::{ - ElementState::Released, MouseButton::Left, WindowEvent::MouseInput, - }; - - release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Released, - button: Left, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down & !PointerButton::MOUSE_LEFT.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_RBUTTONDOWN => { - use crate::event::{ - ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput, - }; - - capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Pressed, - button: Right, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down | PointerButton::MOUSE_RIGHT.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_RBUTTONUP => { - use crate::event::{ - ElementState::Released, MouseButton::Right, WindowEvent::MouseInput, - }; - - release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Released, - button: Right, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down & !PointerButton::MOUSE_RIGHT.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_MBUTTONDOWN => { - use crate::event::{ - ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput, - }; - - capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Pressed, - button: Middle, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down | PointerButton::MOUSE_MIDDLE.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_MBUTTONUP => { - use crate::event::{ - ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput, - }; - - release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Released, - button: Middle, - modifiers: event::get_key_mods(), + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down & !PointerButton::MOUSE_MIDDLE.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_XBUTTONDOWN => { - use crate::event::{ - ElementState::Pressed, MouseButton::Other, WindowEvent::MouseInput, - }; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - - capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Pressed, - button: Other(xbutton as u8), - modifiers: event::get_key_mods(), + let button = match winuser::GET_XBUTTON_WPARAM(wparam) { + 1 => PointerButton::MOUSE_X1, + 2 => PointerButton::MOUSE_X2, + _ => { + warn!("invalid x-button passed"); + return 0; + } + }; + + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down | button.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_XBUTTONUP => { - use crate::event::{ - ElementState::Released, MouseButton::Other, WindowEvent::MouseInput, - }; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - - release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: MouseInput { - device_id: DEVICE_ID, - state: Released, - button: Other(xbutton as u8), - modifiers: event::get_key_mods(), + let button = match winuser::GET_XBUTTON_WPARAM(wparam) { + 1 => PointerButton::MOUSE_X1, + 2 => PointerButton::MOUSE_X2, + _ => { + warn!("invalid x-button passed"); + return 0; + } + }; + + subclass_input.lock_and_update_pointer_state( + PointerId::MOUSE_ID, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + buttons_down: pointer_state.buttons_down & !button.into_flags(), + ..pointer_state }, - }); + ); 0 } winuser::WM_TOUCH => { let pcount = LOWORD(wparam as DWORD) as usize; - let mut inputs = Vec::with_capacity(pcount); + let mut inputs = Vec::::with_capacity(pcount); inputs.set_len(pcount); let htouch = lparam as winuser::HTOUCHINPUT; if winuser::GetTouchInputInfo( @@ -1330,42 +1288,94 @@ unsafe extern "system" fn public_window_callback( ) > 0 { for input in &inputs { - let mut location = POINT { + let mut position = POINT { x: input.x / 100, y: input.y / 100, }; - if winuser::ScreenToClient(window, &mut location as *mut _) == 0 { + if winuser::ScreenToClient(window, &mut position as *mut _) == 0 { continue; } - let x = location.x as f64 + (input.x % 100) as f64 / 100f64; - let y = location.y as f64 + (input.y % 100) as f64 / 100f64; - let location = PhysicalPosition::new(x, y); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::Touch(Touch { - phase: if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 { - TouchPhase::Started - } else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 { - TouchPhase::Ended - } else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 { - TouchPhase::Moved - } else { - continue; - }, - location, - force: None, // WM_TOUCH doesn't support pressure information - id: input.dwID as u64, - device_id: DEVICE_ID, - }), - }); + let x = position.x as f64 + (input.x % 100) as f64 / 100f64; + let y = position.y as f64 + (input.y % 100) as f64 / 100f64; + let position = PhysicalPosition::new(x, y); + let pointer_id = PointerId::TouchId(TouchId(input.dwID).into()); + + if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 { + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::PointerCreated(pointer_id), + )); + } + + let mut button_and_mask = ButtonsDown::empty(); + button_and_mask.set( + !PointerButton::TOUCH_CONTACT.into_flags(), + input.dwFlags & winuser::TOUCHEVENTF_UP != 0 + ); + let mut button_or_mask = ButtonsDown::empty(); + button_or_mask.set( + PointerButton::TOUCH_CONTACT.into_flags(), + input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 + ); + let contact_area = Some(PhysicalSize::new(input.cxContact as f64, input.cyContact as f64)) + .filter(|_| input.dwMask & winuser::TOUCHINPUTMASKF_CONTACTAREA != 0); + + subclass_input.lock_and_update_pointer_state( + pointer_id, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + in_window: true, + position, + buttons_down: pointer_state.buttons_down & button_and_mask | button_or_mask, + contact_area: contact_area.unwrap_or(pointer_state.contact_area), + ..pointer_state + } + ); + + if input.dwFlags & winuser::TOUCHEVENTF_INRANGE == 0 { + subclass_input.lock_and_destroy_pointer(pointer_id, window); + } } } winuser::CloseTouchInputHandle(htouch); 0 } + // WM_POINTERENTER and WM_POINTERLEAVE send events when the pointer enters/leaves the entire + // window area, **NOT** just the client area. We process `WM_NCPOINTERUPDATE` to handle the + // case where the pointer is in the window area but not the client area. + winuser::WM_POINTERENTER | winuser::WM_POINTERLEAVE | winuser::WM_NCPOINTERUPDATE => { + if let Some(GetPointerInfo) = *GET_POINTER_INFO { + let pointer_id = LOWORD(wparam as DWORD) as u32; + let mut pointer_info: winuser::POINTER_INFO = mem::zeroed(); + if GetPointerInfo(pointer_id, &mut pointer_info) == 0 { + return 0; + } + if pointer_info.pointerFlags & winuser::POINTER_FLAG_INRANGE != 0 { + let pointer_id = match pointer_info.pointerType { + winuser::PT_TOUCH => PointerId::TouchId(TouchId(pointer_id).into()), + winuser::PT_PEN => PointerId::PenId(PenId(pointer_id).into()), + _ => PointerId::MOUSE_ID, + }; + + subclass_input.lock_and_update_pointer_state( + pointer_id, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + in_window: util::point_in_window_client(window, pointer_info.ptPixelLocation).unwrap_or(false), + ..pointer_state + } + ); + } + } + + 0 + } + winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => { if let ( Some(GetPointerFrameInfoHistory), @@ -1390,7 +1400,8 @@ unsafe extern "system" fn public_window_callback( } let pointer_info_count = (entries_count * pointers_count) as usize; - let mut pointer_infos = Vec::with_capacity(pointer_info_count); + let mut pointer_infos = + Vec::::with_capacity(pointer_info_count); pointer_infos.set_len(pointer_info_count); if GetPointerFrameInfoHistory( pointer_id, @@ -1436,69 +1447,153 @@ unsafe extern "system" fn public_window_callback( let y = display_rect.top as f64 + pointer_info.ptHimetricLocation.y as f64 * himetric_to_pixel_ratio_y; - let mut location = POINT { + let mut position = POINT { x: x.floor() as i32, y: y.floor() as i32, }; - if winuser::ScreenToClient(window, &mut location as *mut _) == 0 { + if winuser::ScreenToClient(window, &mut position as *mut _) == 0 { continue; } - let force = match pointer_info.pointerType { + + + let x = position.x as f64 + x.fract(); + let y = position.y as f64 + y.fract(); + let mut position = PhysicalPosition::new(x, y); + + let pi = std::f64::consts::PI; + let deg_to_twist = |deg: u32| (deg as f64 / 360.0) * pi; + let mut force = None; + let mut tilt = None; + let mut twist = None; + let mut contact_area = None; + let pointer_id: PointerId; + + let mut buttons_down = ButtonsDown::empty(); + + match pointer_info.pointerType { winuser::PT_TOUCH => { + pointer_id = PointerId::TouchId(TouchId(pointer_info.pointerId).into()); + + buttons_down.set( + PointerButton::TOUCH_CONTACT.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_INCONTACT != 0 + ); + let mut touch_info = mem::MaybeUninit::uninit(); - GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| { - match GetPointerTouchInfo( + if let Some(GetPointerTouchInfo) = *GET_POINTER_TOUCH_INFO { + let get_touch_info_result = GetPointerTouchInfo( pointer_info.pointerId, - touch_info.as_mut_ptr(), - ) { - 0 => None, - _ => normalize_pointer_pressure( - touch_info.assume_init().pressure, - ), + touch_info.as_mut_ptr() + ); + if 0 != get_touch_info_result { + let touch_info = touch_info.assume_init(); + if touch_info.touchMask & winuser::TOUCH_MASK_PRESSURE != 0 { + force = normalize_pointer_pressure(touch_info.pressure); + } + if touch_info.touchMask & winuser::TOUCH_MASK_ORIENTATION != 0 { + twist = Some(deg_to_twist(touch_info.orientation)); + } + if touch_info.touchMask & winuser::TOUCH_MASK_CONTACTAREA != 0 { + let rect = touch_info.rcContactRaw; + contact_area = Some(PhysicalSize::new(rect.right - rect.left, rect.bottom - rect.top).cast()); + } } - }) + } } winuser::PT_PEN => { + pointer_id = PointerId::PenId(PenId(pointer_info.pointerId).into()); + + buttons_down.set( + PointerButton::PEN_CONTACT.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_INCONTACT != 0 + ); + let mut pen_info = mem::MaybeUninit::uninit(); - GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| { - match GetPointerPenInfo( + if let Some(GetPointerPenInfo) = *GET_POINTER_PEN_INFO { + let get_pen_info_result = GetPointerPenInfo( pointer_info.pointerId, pen_info.as_mut_ptr(), - ) { - 0 => None, - _ => { - normalize_pointer_pressure(pen_info.assume_init().pressure) + ); + if 0 != get_pen_info_result { + let pen_info = pen_info.assume_init(); + if pen_info.penMask & winuser::PEN_MASK_PRESSURE != 0 { + force = normalize_pointer_pressure(pen_info.pressure); + } + if pen_info.penMask & winuser::PEN_MASK_ROTATION != 0 { + twist = Some(deg_to_twist(pen_info.rotation)); } + if pen_info.penMask & (winuser::PEN_MASK_TILT_X | winuser::PEN_MASK_TILT_Y) != 0 { + tilt = Some(PointerTiltEvent::new_tilt_angle( + pen_info.tiltX as f64 / 90.0 * (pi / 2.0), + pen_info.tiltY as f64 / 90.0 * (pi / 2.0), + )); + } + buttons_down.set( + PointerButton::PEN_BARREL.into_flags(), + pen_info.penFlags & winuser::PEN_FLAG_BARREL != 0 + ); + buttons_down.set( + PointerButton::PEN_ERASER.into_flags(), + pen_info.penFlags & winuser::PEN_FLAG_ERASER != 0 + ); } - }) + } } - _ => None, - }; + winuser::PT_MOUSE | + winuser::PT_TOUCHPAD => { + pointer_id = PointerId::MOUSE_ID; + + // There can be some rounding errors when doing the mouse hiMetric math, + // and it doesn't make a whole lot of sense for a traditional mouse + // pointer to be on a non-integer point, so round off any fractional + // values that might've cropped up. + position.x = position.x.round(); + position.y = position.y.round(); + + buttons_down.set( + PointerButton::MOUSE_LEFT.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_FIRSTBUTTON != 0, + ); + buttons_down.set( + PointerButton::MOUSE_RIGHT.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_SECONDBUTTON != 0, + ); + buttons_down.set( + PointerButton::MOUSE_MIDDLE.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_THIRDBUTTON != 0, + ); + buttons_down.set( + PointerButton::MOUSE_X1.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_FOURTHBUTTON != 0, + ); + buttons_down.set( + PointerButton::MOUSE_X2.into_flags(), + pointer_info.pointerFlags & winuser::POINTER_FLAG_FIFTHBUTTON != 0, + ); + } + _ => return 0, + } - let x = location.x as f64 + x.fract(); - let y = location.y as f64 + y.fract(); - let location = PhysicalPosition::new(x, y); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::Touch(Touch { - phase: if pointer_info.pointerFlags & winuser::POINTER_FLAG_DOWN != 0 { - TouchPhase::Started - } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UP != 0 { - TouchPhase::Ended - } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UPDATE != 0 - { - TouchPhase::Moved - } else { - continue; - }, - location, - force, - id: pointer_info.pointerId as u64, - device_id: DEVICE_ID, - }), - }); + subclass_input.lock_and_update_pointer_state( + pointer_id, + winuser::GetMessageTime(), + window, + |pointer_state| PointerState { + in_window: true, + force: force.unwrap_or(pointer_state.force), + tilt: tilt.unwrap_or(pointer_state.tilt), + twist: twist.unwrap_or(pointer_state.twist), + contact_area: contact_area.unwrap_or(pointer_state.contact_area), + position, + buttons_down, + } + ); + + if pointer_info.pointerFlags & winuser::POINTER_FLAG_INRANGE == 0 { + subclass_input.lock_and_destroy_pointer(pointer_id, window); + } } SkipPointerFrameMessages(pointer_id); @@ -1507,86 +1602,70 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_SETFOCUS => { - use crate::event::{ElementState::Released, WindowEvent::Focused}; for windows_keycode in event::get_pressed_keys() { - let scancode = + let scan_code = winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); - let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + let logical_key = event::vkey_to_winit_vkey(windows_keycode); update_modifiers(window, subclass_input); - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Key(KeyEvent { + logical_key, + scan_code, + is_down: true, + is_repeat: false, is_synthetic: true, - }, - }) + }), + )); } - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Focused(true), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::FocusGained, + )); 0 } winuser::WM_KILLFOCUS => { - use crate::event::{ - ElementState::Released, - ModifiersState, - WindowEvent::{Focused, ModifiersChanged}, - }; for windows_keycode in event::get_pressed_keys() { - let scancode = + let scan_code = winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); - let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); - - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, + let logical_key = event::vkey_to_winit_vkey(windows_keycode); + + update_modifiers(window, subclass_input); + + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::Key(KeyEvent { + logical_key, + scan_code, + is_down: false, + is_repeat: false, is_synthetic: true, - }, - }) + }), + )); } subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ModifiersChanged(ModifiersState::empty()), - }); - - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Focused(false), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ModifiersChanged(ModifiersState::empty()), + )); + + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::FocusLost, + )); 0 } winuser::WM_SETCURSOR => { let set_cursor_to = { - let window_state = subclass_input.window_state.lock(); - if window_state - .mouse - .cursor_flags() - .contains(CursorFlags::IN_WINDOW) - { + if util::cursor_in_window_client(window).unwrap_or(false) { + let window_state = subclass_input.window_state.lock(); Some(window_state.mouse.cursor) } else { None @@ -1638,8 +1717,6 @@ unsafe extern "system" fn public_window_callback( // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change // DPI, therefore all applications are closed while DPI is changing. winuser::WM_DPICHANGED => { - use crate::event::WindowEvent::ScaleFactorChanged; - // This message actually provides two DPI values - x and y. However MSDN says that // "you only need to use either the X-axis or the Y-axis value when scaling your // application since they are the same". @@ -1714,13 +1791,10 @@ unsafe extern "system" fn public_window_callback( false => old_physical_inner_size, }; - let _ = subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ScaleFactorChanged { - scale_factor: new_scale_factor, - new_inner_size: &mut new_physical_inner_size, - }, - }); + let _ = subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ScaleFactorChanged(new_scale_factor, &mut new_physical_inner_size), + )); let dragging_window: bool; @@ -1853,22 +1927,23 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_SETTINGCHANGE => { - use crate::event::WindowEvent::ThemeChanged; - let is_dark_mode = try_dark_mode(window); let mut window_state = subclass_input.window_state.lock(); let changed = window_state.is_dark_mode != is_dark_mode; if changed { - use crate::window::Theme::*; - let theme = if is_dark_mode { Dark } else { Light }; + let theme = if is_dark_mode { + Theme::Dark + } else { + Theme::Light + }; window_state.is_dark_mode = is_dark_mode; mem::drop(window_state); - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ThemeChanged(theme), - }); + subclass_input.send_event(Event::WindowEvent( + WindowId(window).into(), + WindowEvent::ThemeChanged(theme), + )); } commctrl::DefSubclassProc(window, msg, wparam, lparam) @@ -1947,84 +2022,86 @@ unsafe extern "system" fn thread_event_target_callback( } winuser::WM_INPUT_DEVICE_CHANGE => { - let event = match wparam as _ { - winuser::GIDC_ARRIVAL => DeviceEvent::Added, - winuser::GIDC_REMOVAL => DeviceEvent::Removed, - _ => unreachable!(), - }; + let mut device_info: winuser::RID_DEVICE_INFO = mem::zeroed(); + device_info.cbSize = mem::size_of::() as _; - subclass_input.send_event(Event::DeviceEvent { - device_id: wrap_device_id(lparam as _), - event, - }); + let result = winuser::GetRawInputDeviceInfoW( + lparam as _, + winuser::RIDI_DEVICEINFO, + &mut device_info as *mut _ as *mut _, + ptr::null_mut(), + ); + assert!(result > 0); + + match device_info.dwType { + winuser::RIM_TYPEMOUSE => subclass_input.send_event(Event::RawPointerEvent( + PointerDeviceId(lparam as _).into(), + match wparam as _ { + winuser::GIDC_ARRIVAL => RawPointerEvent::Added, + winuser::GIDC_REMOVAL => RawPointerEvent::Removed, + _ => unreachable!(), + }, + )), + winuser::RIM_TYPEKEYBOARD => subclass_input.send_event(Event::RawKeyboardEvent( + KeyboardDeviceId(lparam as _).into(), + match wparam as _ { + winuser::GIDC_ARRIVAL => RawKeyboardEvent::Added, + winuser::GIDC_REMOVAL => RawKeyboardEvent::Removed, + _ => unreachable!(), + }, + )), + _ => (), + } 0 } winuser::WM_INPUT => { - use crate::event::{ - DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, - ElementState::{Pressed, Released}, - MouseScrollDelta::LineDelta, - }; - if let Some(data) = raw_input::get_raw_input_data(lparam as _) { - let device_id = wrap_device_id(data.header.hDevice as _); - if data.header.dwType == winuser::RIM_TYPEMOUSE { + let pointer_id = PointerDeviceId(data.header.hDevice).into(); let mouse = data.data.mouse(); if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { let x = mouse.lLastX as f64; let y = mouse.lLastY as f64; - if x != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 0, value: x }, - }); - } - - if y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 1, value: y }, - }); - } - if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseMotion { delta: (x, y) }, - }); + subclass_input.send_event(Event::RawPointerEvent( + pointer_id, + RawPointerEvent::MovedRelative(PhysicalDelta::new(x, y)), + )); } } if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { - delta: LineDelta(0.0, delta as f32), - }, - }); + let delta = mouse.usButtonData as f64 / winuser::WHEEL_DELTA as f64; + subclass_input.send_event(Event::RawPointerEvent( + pointer_id, + RawPointerEvent::Wheel(UnitlessDelta::new(0.0, delta)), + )); + } + if util::has_flag( + mouse.usButtonFlags, + 0x0800, /*winuser::RI_MOUSE_HWHEEL*/ + ) { + let delta = mouse.usButtonData as f64 / winuser::WHEEL_DELTA as f64; + subclass_input.send_event(Event::RawPointerEvent( + pointer_id, + RawPointerEvent::Wheel(UnitlessDelta::new(delta, 0.0)), + )); } let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags); // Left, middle, and right, respectively. - for (index, state) in button_state.iter().enumerate() { - if let Some(state) = *state { - // This gives us consistency with X11, since there doesn't - // seem to be anything else reasonable to do for a mouse - // button ID. - let button = (index + 1) as _; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Button { button, state }, - }); - } + for (button, is_down) in button_state.iter().cloned().flatten() { + subclass_input.send_event(Event::RawPointerEvent( + pointer_id, + RawPointerEvent::Button(RawPointerButtonEvent { button, is_down }), + )); } } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard_id = KeyboardDeviceId(data.header.hDevice).into(); let keyboard = data.data.keyboard(); let pressed = keyboard.Message == winuser::WM_KEYDOWN @@ -2033,27 +2110,25 @@ unsafe extern "system" fn thread_event_target_callback( || keyboard.Message == winuser::WM_SYSKEYUP; if pressed || released { - let state = if pressed { Pressed } else { Released }; + let is_down = pressed; - let scancode = keyboard.MakeCode as _; + let scan_code = keyboard.MakeCode as _; let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); - if let Some((vkey, scancode)) = - handle_extended_keys(keyboard.VKey as _, scancode, extended) + if let Some((vkey, scan_code)) = + handle_extended_keys(keyboard.VKey as _, scan_code, extended) { - let virtual_keycode = vkey_to_winit_vkey(vkey); - - #[allow(deprecated)] - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Key(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), + let logical_key = vkey_to_winit_vkey(vkey); + + subclass_input.send_event(Event::RawKeyboardEvent( + keyboard_id, + RawKeyboardEvent::Key(RawKeyEvent { + logical_key, + scan_code, + is_down, }), - }); + )); } } } diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 258a40c08c..09f766b766 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -376,15 +376,11 @@ impl EventLoopRunner { impl BufferedEvent { pub fn from_event(event: Event<'_, T>) -> BufferedEvent { match event { - Event::WindowEvent { - event: - WindowEvent::ScaleFactorChanged { - scale_factor, - new_inner_size, - }, + Event::WindowEvent( window_id, - } => BufferedEvent::ScaleFactorChanged(window_id, scale_factor, *new_inner_size), - event => BufferedEvent::Event(event.to_static().unwrap()), + WindowEvent::ScaleFactorChanged(scale_factor, new_inner_size), + ) => BufferedEvent::ScaleFactorChanged(window_id, scale_factor, *new_inner_size), + event => BufferedEvent::Event(event.to_static().ok().unwrap()), } } @@ -392,13 +388,10 @@ impl BufferedEvent { match self { Self::Event(event) => dispatch(event), Self::ScaleFactorChanged(window_id, scale_factor, mut new_inner_size) => { - dispatch(Event::WindowEvent { + dispatch(Event::WindowEvent( window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); + WindowEvent::ScaleFactorChanged(scale_factor, &mut new_inner_size), + )); util::set_inner_size_physical( (window_id.0).0, new_inner_size.width as _, diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 021c903b8d..61cc451461 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -1,6 +1,6 @@ #![cfg(target_os = "windows")] -use winapi::{self, shared::windef::HWND}; +use winapi::{self, shared::windef::HWND, um::winnt::HANDLE}; pub use self::{ event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, @@ -11,7 +11,6 @@ pub use self::{ pub use self::icon::WinIcon as PlatformIcon; -use crate::event::DeviceId as RootDeviceId; use crate::icon::Icon; #[derive(Clone, Default)] @@ -30,30 +29,81 @@ pub struct Cursor(pub *const winapi::ctypes::wchar_t); unsafe impl Send for Cursor {} unsafe impl Sync for Cursor {} -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(u32); +macro_rules! device_id { + ($name:ident) => { + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub(crate) struct $name(HANDLE); + + unsafe impl Send for $name {} + unsafe impl Sync for $name {} + + impl $name { + pub unsafe fn dummy() -> Self { + Self(std::ptr::null_mut()) + } + + pub fn persistent_identifier(&self) -> Option { + raw_input::get_raw_input_device_name(self.0) + } -impl DeviceId { + #[inline(always)] + pub fn handle(&self) -> HANDLE { + self.0 + } + } + + impl From<$name> for crate::event::$name { + fn from(platform_id: $name) -> Self { + Self(platform_id) + } + } + }; +} + +device_id!(PointerDeviceId); +device_id!(KeyboardDeviceId); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MouseId; +impl MouseId { pub unsafe fn dummy() -> Self { - DeviceId(0) + MouseId } } - -impl DeviceId { - pub fn persistent_identifier(&self) -> Option { - if self.0 != 0 { - raw_input::get_raw_input_device_name(self.0 as _) - } else { - None - } +impl From for crate::event::MouseId { + fn from(platform_id: MouseId) -> Self { + Self(platform_id) } } -// Constant device ID, to be removed when this backend is updated to report real device IDs. -const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0)); +impl crate::event::PointerId { + const MOUSE_ID: Self = Self::MouseId(crate::event::MouseId(MouseId)); +} -fn wrap_device_id(id: u32) -> RootDeviceId { - RootDeviceId(DeviceId(id)) +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TouchId(u32); +impl TouchId { + pub unsafe fn dummy() -> Self { + TouchId(!0) + } +} +impl From for crate::event::TouchId { + fn from(platform_id: TouchId) -> Self { + Self(platform_id) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PenId(u32); +impl PenId { + pub unsafe fn dummy() -> Self { + PenId(!0) + } +} +impl From for crate::event::PenId { + fn from(platform_id: PenId) -> Self { + Self(platform_id) + } } pub type OsError = std::io::Error; @@ -71,6 +121,12 @@ impl WindowId { } } +impl From for crate::window::WindowId { + fn from(platform_id: WindowId) -> Self { + Self(platform_id) + } +} + #[macro_use] mod util; mod dark_mode; diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 73b136a82f..ea7de2cee3 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -21,7 +21,7 @@ use winapi::{ }, }; -use crate::{event::ElementState, platform_impl::platform::util}; +use crate::{event::PointerButton, platform_impl::platform::util}; #[allow(dead_code)] pub fn get_raw_input_device_list() -> Option> { @@ -189,33 +189,48 @@ fn button_flags_to_element_state( button_flags: USHORT, down_flag: USHORT, up_flag: USHORT, -) -> Option { +) -> Option { // We assume the same button won't be simultaneously pressed and released. if util::has_flag(button_flags, down_flag) { - Some(ElementState::Pressed) + Some(true) } else if util::has_flag(button_flags, up_flag) { - Some(ElementState::Released) + Some(false) } else { None } } -pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option; 3] { +pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option<(PointerButton, bool)>; 5] { [ button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_LEFT_BUTTON_DOWN, - winuser::RI_MOUSE_LEFT_BUTTON_UP, - ), + winuser::RI_MOUSE_BUTTON_1_DOWN, + winuser::RI_MOUSE_BUTTON_1_UP, + ) + .map(|b| (PointerButton::BUTTON_1, b)), button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_MIDDLE_BUTTON_DOWN, - winuser::RI_MOUSE_MIDDLE_BUTTON_UP, - ), + winuser::RI_MOUSE_BUTTON_2_DOWN, + winuser::RI_MOUSE_BUTTON_2_UP, + ) + .map(|b| (PointerButton::BUTTON_2, b)), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_BUTTON_3_DOWN, + winuser::RI_MOUSE_BUTTON_3_UP, + ) + .map(|b| (PointerButton::BUTTON_3, b)), button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_RIGHT_BUTTON_DOWN, - winuser::RI_MOUSE_RIGHT_BUTTON_UP, - ), + winuser::RI_MOUSE_BUTTON_4_DOWN, + winuser::RI_MOUSE_BUTTON_4_UP, + ) + .map(|b| (PointerButton::BUTTON_4, b)), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_BUTTON_5_DOWN, + winuser::RI_MOUSE_BUTTON_5_UP, + ) + .map(|b| (PointerButton::BUTTON_5, b)), ] } diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 5faaae3e68..53a27a3be9 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -11,7 +11,7 @@ use winapi::{ ctypes::wchar_t, shared::{ minwindef::{BOOL, DWORD, UINT}, - windef::{DPI_AWARENESS_CONTEXT, HMONITOR, HWND, LPRECT, RECT}, + windef::{DPI_AWARENESS_CONTEXT, HMONITOR, HWND, LPRECT, RECT, POINT}, }, um::{ libloaderapi::{GetProcAddress, LoadLibraryA}, @@ -159,6 +159,27 @@ pub fn set_cursor_hidden(hidden: bool) { } } +pub fn get_cursor_position() -> Result { + unsafe { + let mut point: POINT = mem::zeroed(); + win_to_err(|| winuser::GetCursorPos(&mut point)).map(|_| point) + } +} + +pub fn point_in_rect(rect: RECT, point: POINT) -> bool { + unsafe { + winuser::PtInRect(&rect, point) != 0 + } +} + +pub fn point_in_window_client(window: HWND, point: POINT) -> Result { + Ok(point_in_rect(get_client_rect(window)?, point)) +} + +pub fn cursor_in_window_client(window: HWND) -> Result { + point_in_window_client(window, get_cursor_position()?) +} + pub fn get_cursor_clip() -> Result { unsafe { let mut rect: RECT = mem::zeroed(); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 124090813c..bc92d10f46 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,8 +1,8 @@ use crate::{ - dpi::{PhysicalPosition, Size}, + dpi::Size, event::ModifiersState, icon::Icon, - platform_impl::platform::{event_loop, util}, + platform_impl::platform::{event_loop, util, event::PointerTracker}, window::{CursorIcon, Fullscreen, WindowAttributes}, }; use parking_lot::MutexGuard; @@ -46,15 +46,14 @@ pub struct SavedWindow { pub struct MouseProperties { pub cursor: CursorIcon, pub buttons_down: u32, + pub pointer_tracker: PointerTracker, cursor_flags: CursorFlags, - pub last_position: Option>, } bitflags! { pub struct CursorFlags: u8 { const GRABBED = 1 << 0; const HIDDEN = 1 << 1; - const IN_WINDOW = 1 << 2; } } bitflags! { @@ -107,7 +106,7 @@ impl WindowState { cursor: CursorIcon::default(), buttons_down: 0, cursor_flags: CursorFlags::empty(), - last_position: None, + pointer_tracker: PointerTracker::new(), }, min_size: attributes.min_inner_size, @@ -332,7 +331,7 @@ impl WindowFlags { } impl CursorFlags { - fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> { + pub fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> { let client_rect = util::get_client_rect(window)?; if util::is_focused(window) { @@ -358,7 +357,7 @@ impl CursorFlags { } } - let cursor_in_client = self.contains(CursorFlags::IN_WINDOW); + let cursor_in_client = util::point_in_rect(client_rect, util::get_cursor_position()?); if cursor_in_client { util::set_cursor_hidden(self.contains(CursorFlags::HIDDEN)); } else { diff --git a/src/window.rs b/src/window.rs index 113e7c042b..531e3fad14 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,52 @@ -//! The `Window` struct and associated types. +//! The [`Window`] struct and associated types. +//! +//! # Window creation +//! +//! [`Window`]s can be created either by calling the [`Window::new`] function or by creating a +//! [`WindowBuilder`], populating it with data, and calling the [`WindowBuilder::build`] method: +//! +//! ```no_run +//! # use winit::{event_loop::EventLoop, window::Window}; +//! let mut event_loop = EventLoop::new(); +//! let window = Window::new(&event_loop).unwrap(); +//! ``` +//! +//! ```no_run +//! # use winit::{event_loop::EventLoop, window::WindowBuilder}; +//! let mut event_loop = EventLoop::new(); +//! let window = WindowBuilder::new() +//! .with_title("A fantastic window!") +//! .build(&event_loop) +//! .unwrap(); +//! ``` +//! +//! # Rendering +//! +//! Winit does not provide any tools to directly render onto windows. Instead, you should use +//! either the platform-specific handle retrieval methods provided in the `platform` module +//! +//! Applications should listen for `WindowEvent::CloseRequested` to handle the user attempting to +//! close the window. `CloseRequested` does not force the window to be closed immediately - the +//! `Window` will only close either when it is explicitly dropped or the application quits. +//! +//! ```no_run +//! # use winit::{ +//! # event_loop::{ControlFlow, EventLoop}, +//! # event::{Event, WindowEvent}, +//! # window::WindowBuilder +//! # }; +//! # let event_loop = EventLoop::new(); +//! event_loop.run(move |event, _, control_flow| { +//! *control_flow = ControlFlow::Wait; +//! +//! match event { +//! Event::WindowEvent(_, WindowEvent::CloseRequested) +//! => *control_flow = ControlFlow::Exit, +//! _ => (), +//! } +//! }); +//! ``` + use std::fmt; use crate::{ @@ -13,30 +61,7 @@ pub use crate::icon::{BadIcon, Icon}; /// Represents a window. /// -/// # Example -/// -/// ```no_run -/// use winit::{ -/// event::{Event, WindowEvent}, -/// event_loop::{ControlFlow, EventLoop}, -/// window::Window, -/// }; -/// -/// let mut event_loop = EventLoop::new(); -/// let window = Window::new(&event_loop).unwrap(); -/// -/// event_loop.run(move |event, _, control_flow| { -/// *control_flow = ControlFlow::Wait; -/// -/// match event { -/// Event::WindowEvent { -/// event: WindowEvent::CloseRequested, -/// .. -/// } => *control_flow = ControlFlow::Exit, -/// _ => (), -/// } -/// }); -/// ``` +/// See the module-level docs for more info. pub struct Window { pub(crate) window: platform_impl::Window, } @@ -80,6 +105,8 @@ impl WindowId { } /// Object that allows you to build windows. +/// +/// See the module-level docs for more info. #[derive(Clone, Default)] pub struct WindowBuilder { /// The attributes to use to create the window. @@ -863,7 +890,7 @@ pub enum Fullscreen { Borderless(MonitorHandle), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Theme { Light, Dark, diff --git a/tests/send_objects.rs b/tests/send_objects.rs index 252b271a4b..742decadfe 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -22,6 +22,7 @@ fn window_send() { fn ids_send() { // ensures that the various `..Id` types implement `Send` needs_send::(); - needs_send::(); + needs_send::(); + needs_send::(); needs_send::(); } diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index ad729dcd1b..dcee06e882 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -2,11 +2,11 @@ use serde::{Deserialize, Serialize}; use winit::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, - event::{ - ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, + dpi::{ + LogicalDelta, LogicalPosition, LogicalSize, PhysicalDelta, PhysicalPosition, PhysicalSize, + Pixel, UnitlessDelta, }, + event::{LogicalKey, ModifiersState, PointerButton}, window::CursorIcon, }; @@ -20,20 +20,17 @@ fn window_serde() { #[test] fn events_serde() { - needs_serde::(); - needs_serde::(); - needs_serde::(); - needs_serde::(); - needs_serde::(); - needs_serde::(); needs_serde::(); + needs_serde::(); + needs_serde::(); } -#[test] -fn dpi_serde() { - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); - needs_serde::>(); +pub fn dpi_serde Deserialize<'a>>() { + needs_serde::>(); + needs_serde::>(); + needs_serde::>(); + needs_serde::>(); + needs_serde::>(); + needs_serde::>(); + needs_serde::>(); }