winit: rework around `WinitEvent` vs `InputEvent<Special = WinitEvent>`

This commit is contained in:
i509VCB 2021-10-17 17:47:13 -05:00 committed by Victor Berger
parent d5f4094cc7
commit 77c970e197
5 changed files with 486 additions and 449 deletions

View File

@ -11,8 +11,13 @@
- `PopupSurface::send_configure` now checks the protocol version and returns an `Result` - `PopupSurface::send_configure` now checks the protocol version and returns an `Result`
- `KeyboardHandle::input` filter closure now receives a `KeysymHandle` instead of a `Keysym` and returns a `FilterResult`. - `KeyboardHandle::input` filter closure now receives a `KeysymHandle` instead of a `Keysym` and returns a `FilterResult`.
#### Backend #### Backends
- Rename `WinitInputBacked` to `WinitEventLoop`.
- Rename `WinitInputError` to `WinitError`;
- `WinitInputBackend` no longer implements `InputBackend`. Input events are now received from the `WinitEvent::Input` variant.
- All winit backend internal event types now use `WinitInput` as the backend type.
- `WinitEventLoop::dispatch_new_events` is now used to receive some `WinitEvent`s.
- Added `TabletToolType::Unknown` as an option for tablet events - Added `TabletToolType::Unknown` as an option for tablet events
### Additions ### Additions

View File

@ -22,7 +22,7 @@ use smithay::{
}; };
#[cfg(any(feature = "winit", feature = "x11"))] #[cfg(any(feature = "winit", feature = "x11"))]
use smithay::{backend::input::PointerMotionAbsoluteEvent, wayland::output::Mode}; use smithay::backend::input::PointerMotionAbsoluteEvent;
#[cfg(feature = "udev")] #[cfg(feature = "udev")]
use smithay::{ use smithay::{
@ -150,12 +150,7 @@ impl<Backend> AnvilState<Backend> {
#[cfg(feature = "winit")] #[cfg(feature = "winit")]
impl AnvilState<WinitData> { impl AnvilState<WinitData> {
pub fn process_input_event<B>(&mut self, event: InputEvent<B>) pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
where
B: InputBackend<SpecialEvent = smithay::backend::winit::WinitEvent>,
{
use smithay::backend::winit::WinitEvent;
match event { match event {
InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) { InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) {
KeyAction::None => {} KeyAction::None => {}
@ -206,20 +201,6 @@ impl AnvilState<WinitData> {
InputEvent::PointerMotionAbsolute { event, .. } => self.on_pointer_move_absolute::<B>(event), InputEvent::PointerMotionAbsolute { event, .. } => self.on_pointer_move_absolute::<B>(event),
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event), InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event),
InputEvent::Special(WinitEvent::Resized { size, .. }) => {
self.output_map.borrow_mut().update_mode_by_name(
Mode {
size,
refresh: 60_000,
},
crate::winit::OUTPUT_NAME,
);
let output_mut = self.output_map.borrow();
let output = output_mut.find_by_name(crate::winit::OUTPUT_NAME).unwrap();
self.window_map.borrow_mut().layers.arange_layers(output);
}
_ => { _ => {
// other events are not handled in anvil (yet) // other events are not handled in anvil (yet)
} }

View File

@ -8,7 +8,10 @@ use smithay::{
wayland::dmabuf::init_dmabuf_global, wayland::dmabuf::init_dmabuf_global,
}; };
use smithay::{ use smithay::{
backend::{input::InputBackend, winit, SwapBuffersError}, backend::{
winit::{self, WinitEvent},
SwapBuffersError,
},
reexports::{ reexports::{
calloop::EventLoop, calloop::EventLoop,
wayland_server::{protocol::wl_output, Display}, wayland_server::{protocol::wl_output, Display},
@ -43,7 +46,7 @@ pub fn run_winit(log: Logger) {
let mut event_loop = EventLoop::try_new().unwrap(); let mut event_loop = EventLoop::try_new().unwrap();
let display = Rc::new(RefCell::new(Display::new())); let display = Rc::new(RefCell::new(Display::new()));
let (renderer, mut input) = match winit::init(log.clone()) { let (renderer, mut winit) = match winit::init(log.clone()) {
Ok(ret) => ret, Ok(ret) => ret,
Err(err) => { Err(err) => {
slog::crit!(log, "Failed to initialize Winit backend: {}", err); slog::crit!(log, "Failed to initialize Winit backend: {}", err);
@ -121,8 +124,27 @@ pub fn run_winit(log: Logger) {
info!(log, "Initialization completed, starting the main loop."); info!(log, "Initialization completed, starting the main loop.");
while state.running.load(Ordering::SeqCst) { while state.running.load(Ordering::SeqCst) {
if input if winit
.dispatch_new_events(|event| state.process_input_event(event)) .dispatch_new_events(|event| match event {
WinitEvent::Resized { size, .. } => {
state.output_map.borrow_mut().update_mode_by_name(
Mode {
size,
refresh: 60_000,
},
crate::winit::OUTPUT_NAME,
);
let output_mut = state.output_map.borrow();
let output = output_mut.find_by_name(crate::winit::OUTPUT_NAME).unwrap();
state.window_map.borrow_mut().layers.arange_layers(output);
}
WinitEvent::Input(event) => state.process_input_event(event),
_ => (),
})
.is_err() .is_err()
{ {
state.running.store(false, Ordering::SeqCst); state.running.store(false, Ordering::SeqCst);

391
src/backend/winit/input.rs Normal file
View File

@ -0,0 +1,391 @@
use std::{cell::RefCell, path::PathBuf, rc::Rc};
use winit::{
dpi::LogicalPosition,
event::{ElementState, MouseButton as WinitMouseButton, MouseScrollDelta},
};
use crate::backend::input::{
Axis, AxisSource, ButtonState, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState,
KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent,
};
use super::{WindowSize, WinitError};
/// Marker used to define the `InputBackend` types for the winit backend.
#[derive(Debug)]
pub struct WinitInput;
/// Virtual input device used by the backend to associate input events
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct WinitVirtualDevice;
impl Device for WinitVirtualDevice {
fn id(&self) -> String {
String::from("winit")
}
fn name(&self) -> String {
String::from("winit virtual input")
}
fn has_capability(&self, capability: DeviceCapability) -> bool {
matches!(
capability,
DeviceCapability::Keyboard | DeviceCapability::Pointer | DeviceCapability::Touch
)
}
fn usb_id(&self) -> Option<(u32, u32)> {
None
}
fn syspath(&self) -> Option<PathBuf> {
None
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`KeyboardKeyEvent`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitKeyboardInputEvent {
pub(crate) time: u32,
pub(crate) key: u32,
pub(crate) count: u32,
pub(crate) state: ElementState,
}
impl Event<WinitInput> for WinitKeyboardInputEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl KeyboardKeyEvent<WinitInput> for WinitKeyboardInputEvent {
fn key_code(&self) -> u32 {
self.key
}
fn state(&self) -> KeyState {
self.state.into()
}
fn count(&self) -> u32 {
self.count
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerMotionAbsoluteEvent`]
#[derive(Debug, Clone)]
pub struct WinitMouseMovedEvent {
pub(crate) size: Rc<RefCell<WindowSize>>,
pub(crate) time: u32,
pub(crate) logical_position: LogicalPosition<f64>,
}
impl Event<WinitInput> for WinitMouseMovedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerMotionAbsoluteEvent<WinitInput> for WinitMouseMovedEvent {
// TODO: maybe use {Logical, Physical}Position from winit?
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.logical_position.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.logical_position.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.logical_position.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.logical_position.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerAxisEvent`]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WinitMouseWheelEvent {
pub(crate) time: u32,
pub(crate) delta: MouseScrollDelta,
}
impl Event<WinitInput> for WinitMouseWheelEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerAxisEvent<WinitInput> for WinitMouseWheelEvent {
fn source(&self) -> AxisSource {
match self.delta {
MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel,
MouseScrollDelta::PixelDelta(_) => AxisSource::Continuous,
}
}
fn amount(&self, axis: Axis) -> Option<f64> {
match (axis, self.delta) {
(Axis::Horizontal, MouseScrollDelta::PixelDelta(delta)) => Some(delta.x),
(Axis::Vertical, MouseScrollDelta::PixelDelta(delta)) => Some(delta.y),
(_, MouseScrollDelta::LineDelta(_, _)) => None,
}
}
fn amount_discrete(&self, axis: Axis) -> Option<f64> {
match (axis, self.delta) {
(Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) => Some(x as f64),
(Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) => Some(y as f64),
(_, MouseScrollDelta::PixelDelta(_)) => None,
}
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerButtonEvent`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitMouseInputEvent {
pub(crate) time: u32,
pub(crate) button: WinitMouseButton,
pub(crate) state: ElementState,
}
impl Event<WinitInput> for WinitMouseInputEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerButtonEvent<WinitInput> for WinitMouseInputEvent {
fn button(&self) -> MouseButton {
self.button.into()
}
fn state(&self) -> ButtonState {
self.state.into()
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchDownEvent`]
#[derive(Debug, Clone)]
pub struct WinitTouchStartedEvent {
pub(crate) size: Rc<RefCell<WindowSize>>,
pub(crate) time: u32,
pub(crate) location: LogicalPosition<f64>,
pub(crate) id: u64,
}
impl Event<WinitInput> for WinitTouchStartedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchDownEvent<WinitInput> for WinitTouchStartedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.location.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.location.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.location.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.location.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchMotionEvent`]
#[derive(Debug, Clone)]
pub struct WinitTouchMovedEvent {
pub(crate) size: Rc<RefCell<WindowSize>>,
pub(crate) time: u32,
pub(crate) location: LogicalPosition<f64>,
pub(crate) id: u64,
}
impl Event<WinitInput> for WinitTouchMovedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchMotionEvent<WinitInput> for WinitTouchMovedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.location.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.location.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.location.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.location.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a `TouchUpEvent`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitTouchEndedEvent {
pub(crate) time: u32,
pub(crate) id: u64,
}
impl Event<WinitInput> for WinitTouchEndedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchUpEvent<WinitInput> for WinitTouchEndedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchCancelEvent`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitTouchCancelledEvent {
pub(crate) time: u32,
pub(crate) id: u64,
}
impl Event<WinitInput> for WinitTouchCancelledEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchCancelEvent<WinitInput> for WinitTouchCancelledEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
}
impl From<WinitMouseButton> for MouseButton {
fn from(button: WinitMouseButton) -> MouseButton {
match button {
WinitMouseButton::Left => MouseButton::Left,
WinitMouseButton::Right => MouseButton::Right,
WinitMouseButton::Middle => MouseButton::Middle,
WinitMouseButton::Other(num) => MouseButton::Other(num as u8),
}
}
}
impl From<ElementState> for KeyState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => KeyState::Pressed,
ElementState::Released => KeyState::Released,
}
}
}
impl From<ElementState> for ButtonState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => ButtonState::Pressed,
ElementState::Released => ButtonState::Released,
}
}
}
impl InputBackend for WinitInput {
type EventError = WinitError;
type Device = WinitVirtualDevice;
type KeyboardKeyEvent = WinitKeyboardInputEvent;
type PointerAxisEvent = WinitMouseWheelEvent;
type PointerButtonEvent = WinitMouseInputEvent;
type PointerMotionEvent = UnusedEvent;
type PointerMotionAbsoluteEvent = WinitMouseMovedEvent;
type TouchDownEvent = WinitTouchStartedEvent;
type TouchUpEvent = WinitTouchEndedEvent;
type TouchMotionEvent = WinitTouchMovedEvent;
type TouchCancelEvent = WinitTouchCancelledEvent;
type TouchFrameEvent = UnusedEvent;
type TabletToolAxisEvent = UnusedEvent;
type TabletToolProximityEvent = UnusedEvent;
type TabletToolTipEvent = UnusedEvent;
type TabletToolButtonEvent = UnusedEvent;
type SpecialEvent = UnusedEvent;
fn dispatch_new_events<F>(&mut self, _callback: F) -> Result<(), Self::EventError>
where
F: FnMut(InputEvent<Self>),
{
unreachable!()
}
}

View File

@ -13,23 +13,19 @@
//! - a [`WinitGraphicsBackend`], which can give you an implementation of a [`Renderer`] //! - a [`WinitGraphicsBackend`], which can give you an implementation of a [`Renderer`]
//! (or even [`Gles2Renderer`]) through its `renderer` method in addition to further //! (or even [`Gles2Renderer`]) through its `renderer` method in addition to further
//! functionality to access and manage the created winit-window. //! functionality to access and manage the created winit-window.
//! - a [`WinitInputBackend`], which is an implementation of the [`InputBackend`] trait //! - a [`WinitEventLoop`], which dispatches some [`WinitEvent`] from the host graphics server.
//! using the input events forwarded from the host graphics server.
//! //!
//! The other types in this module are the instances of the associated types of these //! The other types in this module are the instances of the associated types of these
//! two traits for the winit backend. //! two traits for the winit backend.
mod input;
use crate::{ use crate::{
backend::{ backend::{
egl::{ egl::{
context::GlAttributes, display::EGLDisplay, native, EGLContext, EGLSurface, Error as EGLError, context::GlAttributes, display::EGLDisplay, native, EGLContext, EGLSurface, Error as EGLError,
}, },
input::{ input::InputEvent,
Axis, AxisSource, ButtonState, Device, DeviceCapability, Event as BackendEvent, InputBackend,
InputEvent, KeyState, KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent,
PointerMotionAbsoluteEvent, TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot,
TouchUpEvent, UnusedEvent,
},
renderer::{ renderer::{
gles2::{Gles2Error, Gles2Frame, Gles2Renderer}, gles2::{Gles2Error, Gles2Frame, Gles2Renderer},
Bind, Renderer, Transform, Unbind, Bind, Renderer, Transform, Unbind,
@ -37,14 +33,11 @@ use crate::{
}, },
utils::{Logical, Physical, Size}, utils::{Logical, Physical, Size},
}; };
use std::{cell::RefCell, path::PathBuf, rc::Rc, time::Instant}; use std::{cell::RefCell, rc::Rc, time::Instant};
use wayland_egl as wegl; use wayland_egl as wegl;
use winit::{ use winit::{
dpi::{LogicalPosition, LogicalSize}, dpi::LogicalSize,
event::{ event::{ElementState, Event, KeyboardInput, Touch, TouchPhase, WindowEvent},
ElementState, Event, KeyboardInput, MouseButton as WinitMouseButton, MouseScrollDelta, Touch,
TouchPhase, WindowEvent,
},
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
platform::run_return::EventLoopExtRunReturn, platform::run_return::EventLoopExtRunReturn,
platform::unix::WindowExtUnix, platform::unix::WindowExtUnix,
@ -54,6 +47,8 @@ use winit::{
use slog::{debug, error, info, o, trace, warn}; use slog::{debug, error, info, o, trace, warn};
use std::cell::Cell; use std::cell::Cell;
pub use self::input::*;
/// Errors thrown by the `winit` backends /// Errors thrown by the `winit` backends
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
@ -97,12 +92,12 @@ pub struct WinitGraphicsBackend {
resize_notification: Rc<Cell<Option<Size<i32, Physical>>>>, resize_notification: Rc<Cell<Option<Size<i32, Physical>>>>,
} }
/// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait /// Abstracted event loop of a [`WinitWindow`].
/// ///
/// You need to call [`dispatch_new_events`](InputBackend::dispatch_new_events) /// You need to call [`dispatch_new_events`](WinitEventLoop::dispatch_new_events)
/// periodically to receive any events. /// periodically to receive any events.
#[derive(Debug)] #[derive(Debug)]
pub struct WinitInputBackend { pub struct WinitEventLoop {
window: Rc<WinitWindow>, window: Rc<WinitWindow>,
events_loop: EventLoop<()>, events_loop: EventLoop<()>,
time: Instant, time: Instant,
@ -113,9 +108,9 @@ pub struct WinitInputBackend {
resize_notification: Rc<Cell<Option<Size<i32, Physical>>>>, resize_notification: Rc<Cell<Option<Size<i32, Physical>>>>,
} }
/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait and a corresponding [`WinitInputBackend`], /// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait and a corresponding
/// which implements the [`InputBackend`] trait /// [`WinitEventLoop`].
pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend), Error> pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -129,11 +124,11 @@ where
} }
/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`] /// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`]
/// struct and a corresponding [`WinitInputBackend`], which implements the [`InputBackend`] trait /// struct and a corresponding [`WinitEventLoop`].
pub fn init_from_builder<L>( pub fn init_from_builder<L>(
builder: WindowBuilder, builder: WindowBuilder,
logger: L, logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend), Error> ) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -151,12 +146,12 @@ where
/// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`] /// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait, from a given [`WindowBuilder`]
/// struct, as well as given [`GlAttributes`] for further customization of the rendering pipeline and a /// struct, as well as given [`GlAttributes`] for further customization of the rendering pipeline and a
/// corresponding [`WinitInputBackend`], which implements the [`InputBackend`] trait. /// corresponding [`WinitEventLoop`].
pub fn init_from_builder_with_gl_attr<L>( pub fn init_from_builder_with_gl_attr<L>(
builder: WindowBuilder, builder: WindowBuilder,
attributes: GlAttributes, attributes: GlAttributes,
logger: L, logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend), Error> ) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -226,14 +221,14 @@ where
size: size.clone(), size: size.clone(),
resize_notification: resize_notification.clone(), resize_notification: resize_notification.clone(),
}, },
WinitInputBackend { WinitEventLoop {
resize_notification, resize_notification,
events_loop, events_loop,
window, window,
time: Instant::now(), time: Instant::now(),
key_counter: 0, key_counter: 0,
initialized: false, initialized: false,
logger: log.new(o!("smithay_winit_component" => "input")), logger: log.new(o!("smithay_winit_component" => "event_loop")),
size, size,
}, },
)) ))
@ -249,8 +244,13 @@ pub enum WinitEvent {
/// The new scale factor /// The new scale factor
scale_factor: f64, scale_factor: f64,
}, },
/// The focus state of the window changed /// The focus state of the window changed
Focus(bool), Focus(bool),
/// An input event occurred.
Input(InputEvent<WinitInput>),
/// A redraw was requested /// A redraw was requested
Refresh, Refresh,
} }
@ -295,38 +295,9 @@ impl WinitGraphicsBackend {
} }
} }
/// Virtual input device used by the backend to associate input events /// Errors that may happen when driving a [`WinitEventLoop`]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct WinitVirtualDevice;
impl Device for WinitVirtualDevice {
fn id(&self) -> String {
String::from("winit")
}
fn name(&self) -> String {
String::from("winit virtual input")
}
fn has_capability(&self, capability: DeviceCapability) -> bool {
matches!(
capability,
DeviceCapability::Keyboard | DeviceCapability::Pointer | DeviceCapability::Touch
)
}
fn usb_id(&self) -> Option<(u32, u32)> {
None
}
fn syspath(&self) -> Option<PathBuf> {
None
}
}
/// Errors that may happen when driving the event loop of [`WinitInputBackend`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
pub enum WinitInputError { pub enum WinitError {
/// The underlying [`WinitWindow`] was closed. No further events can be processed. /// The underlying [`WinitWindow`] was closed. No further events can be processed.
/// ///
/// See `dispatch_new_events`. /// See `dispatch_new_events`.
@ -334,313 +305,7 @@ pub enum WinitInputError {
WindowClosed, WindowClosed,
} }
/// Winit-Backend internal event wrapping `winit`'s types into a [`KeyboardKeyEvent`] impl WinitEventLoop {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitKeyboardInputEvent {
time: u32,
key: u32,
count: u32,
state: ElementState,
}
impl BackendEvent<WinitInputBackend> for WinitKeyboardInputEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl KeyboardKeyEvent<WinitInputBackend> for WinitKeyboardInputEvent {
fn key_code(&self) -> u32 {
self.key
}
fn state(&self) -> KeyState {
self.state.into()
}
fn count(&self) -> u32 {
self.count
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerMotionAbsoluteEvent`]
#[derive(Debug, Clone)]
pub struct WinitMouseMovedEvent {
size: Rc<RefCell<WindowSize>>,
time: u32,
logical_position: LogicalPosition<f64>,
}
impl BackendEvent<WinitInputBackend> for WinitMouseMovedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerMotionAbsoluteEvent<WinitInputBackend> for WinitMouseMovedEvent {
// TODO: maybe use {Logical, Physical}Position from winit?
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.logical_position.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.logical_position.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.logical_position.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.logical_position.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerAxisEvent`]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WinitMouseWheelEvent {
time: u32,
delta: MouseScrollDelta,
}
impl BackendEvent<WinitInputBackend> for WinitMouseWheelEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerAxisEvent<WinitInputBackend> for WinitMouseWheelEvent {
fn source(&self) -> AxisSource {
match self.delta {
MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel,
MouseScrollDelta::PixelDelta(_) => AxisSource::Continuous,
}
}
fn amount(&self, axis: Axis) -> Option<f64> {
match (axis, self.delta) {
(Axis::Horizontal, MouseScrollDelta::PixelDelta(delta)) => Some(delta.x),
(Axis::Vertical, MouseScrollDelta::PixelDelta(delta)) => Some(delta.y),
(_, MouseScrollDelta::LineDelta(_, _)) => None,
}
}
fn amount_discrete(&self, axis: Axis) -> Option<f64> {
match (axis, self.delta) {
(Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) => Some(x as f64),
(Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) => Some(y as f64),
(_, MouseScrollDelta::PixelDelta(_)) => None,
}
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`PointerButtonEvent`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitMouseInputEvent {
time: u32,
button: WinitMouseButton,
state: ElementState,
}
impl BackendEvent<WinitInputBackend> for WinitMouseInputEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl PointerButtonEvent<WinitInputBackend> for WinitMouseInputEvent {
fn button(&self) -> MouseButton {
self.button.into()
}
fn state(&self) -> ButtonState {
self.state.into()
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchDownEvent`]
#[derive(Debug, Clone)]
pub struct WinitTouchStartedEvent {
size: Rc<RefCell<WindowSize>>,
time: u32,
location: LogicalPosition<f64>,
id: u64,
}
impl BackendEvent<WinitInputBackend> for WinitTouchStartedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchDownEvent<WinitInputBackend> for WinitTouchStartedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.location.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.location.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.location.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.location.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchMotionEvent`]
#[derive(Debug, Clone)]
pub struct WinitTouchMovedEvent {
size: Rc<RefCell<WindowSize>>,
time: u32,
location: LogicalPosition<f64>,
id: u64,
}
impl BackendEvent<WinitInputBackend> for WinitTouchMovedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchMotionEvent<WinitInputBackend> for WinitTouchMovedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
fn x(&self) -> f64 {
let wsize = self.size.borrow();
self.location.x * wsize.scale_factor
}
fn y(&self) -> f64 {
let wsize = self.size.borrow();
self.location.y * wsize.scale_factor
}
fn x_transformed(&self, width: i32) -> f64 {
let wsize = self.size.borrow();
let w_width = wsize.logical_size().w;
f64::max(self.location.x * width as f64 / w_width, 0.0)
}
fn y_transformed(&self, height: i32) -> f64 {
let wsize = self.size.borrow();
let w_height = wsize.logical_size().h;
f64::max(self.location.y * height as f64 / w_height, 0.0)
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a `TouchUpEvent`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitTouchEndedEvent {
time: u32,
id: u64,
}
impl BackendEvent<WinitInputBackend> for WinitTouchEndedEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchUpEvent<WinitInputBackend> for WinitTouchEndedEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
}
/// Winit-Backend internal event wrapping `winit`'s types into a [`TouchCancelEvent`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WinitTouchCancelledEvent {
time: u32,
id: u64,
}
impl BackendEvent<WinitInputBackend> for WinitTouchCancelledEvent {
fn time(&self) -> u32 {
self.time
}
fn device(&self) -> WinitVirtualDevice {
WinitVirtualDevice
}
}
impl TouchCancelEvent<WinitInputBackend> for WinitTouchCancelledEvent {
fn slot(&self) -> Option<TouchSlot> {
Some(TouchSlot::new(self.id))
}
}
impl InputBackend for WinitInputBackend {
type EventError = WinitInputError;
type Device = WinitVirtualDevice;
type KeyboardKeyEvent = WinitKeyboardInputEvent;
type PointerAxisEvent = WinitMouseWheelEvent;
type PointerButtonEvent = WinitMouseInputEvent;
type PointerMotionEvent = UnusedEvent;
type PointerMotionAbsoluteEvent = WinitMouseMovedEvent;
type TouchDownEvent = WinitTouchStartedEvent;
type TouchUpEvent = WinitTouchEndedEvent;
type TouchMotionEvent = WinitTouchMovedEvent;
type TouchCancelEvent = WinitTouchCancelledEvent;
type TouchFrameEvent = UnusedEvent;
type TabletToolAxisEvent = UnusedEvent;
type TabletToolProximityEvent = UnusedEvent;
type TabletToolTipEvent = UnusedEvent;
type TabletToolButtonEvent = UnusedEvent;
type SpecialEvent = WinitEvent;
/// Processes new events of the underlying event loop and calls the provided callback. /// Processes new events of the underlying event loop and calls the provided callback.
/// ///
/// You need to periodically call this function to keep the underlying event loop and /// You need to periodically call this function to keep the underlying event loop and
@ -652,10 +317,12 @@ impl InputBackend for WinitInputBackend {
/// ///
/// The linked [`WinitGraphicsBackend`] will error with a lost context and should /// The linked [`WinitGraphicsBackend`] will error with a lost context and should
/// not be used anymore as well. /// not be used anymore as well.
fn dispatch_new_events<F>(&mut self, mut callback: F) -> ::std::result::Result<(), WinitInputError> pub fn dispatch_new_events<F>(&mut self, mut callback: F) -> Result<(), WinitError>
where where
F: FnMut(InputEvent<Self>), F: FnMut(WinitEvent),
{ {
use self::WinitEvent::*;
let mut closed = false; let mut closed = false;
{ {
@ -673,9 +340,9 @@ impl InputBackend for WinitInputBackend {
let window_size = &self.size; let window_size = &self.size;
if !self.initialized { if !self.initialized {
callback(InputEvent::DeviceAdded { callback(Input(InputEvent::DeviceAdded {
device: WinitVirtualDevice, device: WinitVirtualDevice,
}); }));
self.initialized = true; self.initialized = true;
} }
@ -685,7 +352,7 @@ impl InputBackend for WinitInputBackend {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
Event::RedrawRequested(_id) => { Event::RedrawRequested(_id) => {
callback(InputEvent::Special(WinitEvent::Refresh)); callback(WinitEvent::Refresh);
} }
Event::WindowEvent { event, .. } => { Event::WindowEvent { event, .. } => {
let duration = Instant::now().duration_since(*time); let duration = Instant::now().duration_since(*time);
@ -702,13 +369,13 @@ impl InputBackend for WinitInputBackend {
resize_notification.set(Some(wsize.physical_size)); resize_notification.set(Some(wsize.physical_size));
callback(InputEvent::Special(WinitEvent::Resized { callback(WinitEvent::Resized {
size: wsize.physical_size, size: wsize.physical_size,
scale_factor, scale_factor,
})); });
} }
WindowEvent::Focused(focus) => { WindowEvent::Focused(focus) => {
callback(InputEvent::Special(WinitEvent::Focus(focus))); callback(WinitEvent::Focus(focus));
} }
WindowEvent::ScaleFactorChanged { WindowEvent::ScaleFactorChanged {
@ -721,10 +388,10 @@ impl InputBackend for WinitInputBackend {
let (pw, ph): (u32, u32) = (*new_psize).into(); let (pw, ph): (u32, u32) = (*new_psize).into();
resize_notification.set(Some((pw as i32, ph as i32).into())); resize_notification.set(Some((pw as i32, ph as i32).into()));
callback(InputEvent::Special(WinitEvent::Resized { callback(WinitEvent::Resized {
size: (pw as i32, ph as i32).into(), size: (pw as i32, ph as i32).into(),
scale_factor: wsize.scale_factor, scale_factor: wsize.scale_factor,
})); });
} }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { scancode, state, .. }, input: KeyboardInput { scancode, state, .. },
@ -736,33 +403,33 @@ impl InputBackend for WinitInputBackend {
*key_counter = key_counter.checked_sub(1).unwrap_or(0) *key_counter = key_counter.checked_sub(1).unwrap_or(0)
} }
}; };
callback(InputEvent::Keyboard { callback(Input(InputEvent::Keyboard {
event: WinitKeyboardInputEvent { event: WinitKeyboardInputEvent {
time, time,
key: scancode, key: scancode,
count: *key_counter, count: *key_counter,
state, state,
}, },
}); }));
} }
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
let lpos = position.to_logical(window_size.borrow().scale_factor); let lpos = position.to_logical(window_size.borrow().scale_factor);
callback(InputEvent::PointerMotionAbsolute { callback(Input(InputEvent::PointerMotionAbsolute {
event: WinitMouseMovedEvent { event: WinitMouseMovedEvent {
size: window_size.clone(), size: window_size.clone(),
time, time,
logical_position: lpos, logical_position: lpos,
}, },
}); }));
} }
WindowEvent::MouseWheel { delta, .. } => { WindowEvent::MouseWheel { delta, .. } => {
let event = WinitMouseWheelEvent { time, delta }; let event = WinitMouseWheelEvent { time, delta };
callback(InputEvent::PointerAxis { event }); callback(Input(InputEvent::PointerAxis { event }));
} }
WindowEvent::MouseInput { state, button, .. } => { WindowEvent::MouseInput { state, button, .. } => {
callback(InputEvent::PointerButton { callback(Input(InputEvent::PointerButton {
event: WinitMouseInputEvent { time, button, state }, event: WinitMouseInputEvent { time, button, state },
}); }));
} }
WindowEvent::Touch(Touch { WindowEvent::Touch(Touch {
@ -772,14 +439,14 @@ impl InputBackend for WinitInputBackend {
.. ..
}) => { }) => {
let location = location.to_logical(window_size.borrow().scale_factor); let location = location.to_logical(window_size.borrow().scale_factor);
callback(InputEvent::TouchDown { callback(Input(InputEvent::TouchDown {
event: WinitTouchStartedEvent { event: WinitTouchStartedEvent {
size: window_size.clone(), size: window_size.clone(),
time, time,
location, location,
id, id,
}, },
}); }));
} }
WindowEvent::Touch(Touch { WindowEvent::Touch(Touch {
phase: TouchPhase::Moved, phase: TouchPhase::Moved,
@ -788,14 +455,14 @@ impl InputBackend for WinitInputBackend {
.. ..
}) => { }) => {
let location = location.to_logical(window_size.borrow().scale_factor); let location = location.to_logical(window_size.borrow().scale_factor);
callback(InputEvent::TouchMotion { callback(Input(InputEvent::TouchMotion {
event: WinitTouchMovedEvent { event: WinitTouchMovedEvent {
size: window_size.clone(), size: window_size.clone(),
time, time,
location, location,
id, id,
}, },
}); }));
} }
WindowEvent::Touch(Touch { WindowEvent::Touch(Touch {
@ -805,17 +472,17 @@ impl InputBackend for WinitInputBackend {
.. ..
}) => { }) => {
let location = location.to_logical(window_size.borrow().scale_factor); let location = location.to_logical(window_size.borrow().scale_factor);
callback(InputEvent::TouchMotion { callback(Input(InputEvent::TouchMotion {
event: WinitTouchMovedEvent { event: WinitTouchMovedEvent {
size: window_size.clone(), size: window_size.clone(),
time, time,
location, location,
id, id,
}, },
}); }));
callback(InputEvent::TouchUp { callback(Input(InputEvent::TouchUp {
event: WinitTouchEndedEvent { time, id }, event: WinitTouchEndedEvent { time, id },
}) }))
} }
WindowEvent::Touch(Touch { WindowEvent::Touch(Touch {
@ -823,14 +490,14 @@ impl InputBackend for WinitInputBackend {
id, id,
.. ..
}) => { }) => {
callback(InputEvent::TouchCancel { callback(Input(InputEvent::TouchCancel {
event: WinitTouchCancelledEvent { time, id }, event: WinitTouchCancelledEvent { time, id },
}); }));
} }
WindowEvent::CloseRequested | WindowEvent::Destroyed => { WindowEvent::CloseRequested | WindowEvent::Destroyed => {
callback(InputEvent::DeviceRemoved { callback(Input(InputEvent::DeviceRemoved {
device: WinitVirtualDevice, device: WinitVirtualDevice,
}); }));
warn!(logger, "Window closed"); warn!(logger, "Window closed");
*closed_ptr = true; *closed_ptr = true;
} }
@ -842,38 +509,9 @@ impl InputBackend for WinitInputBackend {
} }
if closed { if closed {
Err(WinitInputError::WindowClosed) Err(WinitError::WindowClosed)
} else { } else {
Ok(()) Ok(())
} }
} }
} }
impl From<WinitMouseButton> for MouseButton {
fn from(button: WinitMouseButton) -> MouseButton {
match button {
WinitMouseButton::Left => MouseButton::Left,
WinitMouseButton::Right => MouseButton::Right,
WinitMouseButton::Middle => MouseButton::Middle,
WinitMouseButton::Other(num) => MouseButton::Other(num as u8),
}
}
}
impl From<ElementState> for KeyState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => KeyState::Pressed,
ElementState::Released => KeyState::Released,
}
}
}
impl From<ElementState> for ButtonState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => ButtonState::Pressed,
ElementState::Released => ButtonState::Released,
}
}
}