From c61f9b75f8e3d425edb6090ebd545884f1de3470 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 11 Nov 2017 10:03:42 +0100 Subject: [PATCH] Wayland: implement touch events (#342) * wayland: move pointer to its own file * wayland: touch events support * update changelog --- CHANGELOG.md | 1 + src/platform/linux/wayland/event_loop.rs | 197 +++-------------------- src/platform/linux/wayland/mod.rs | 2 + src/platform/linux/wayland/pointer.rs | 181 +++++++++++++++++++++ src/platform/linux/wayland/touch.rs | 106 ++++++++++++ 5 files changed, 313 insertions(+), 174 deletions(-) create mode 100644 src/platform/linux/wayland/pointer.rs create mode 100644 src/platform/linux/wayland/touch.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a4f731e5..8412a83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - On Windows added `MonitorIdExt::hmonitor` method - Impl `Clone` for `EventsLoopProxy` - `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found +- Support for touch event on wayland # Version 0.8.3 (2017-10-11) diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 4da5c64a..67e0be62 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -3,16 +3,16 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, EventsLoopClosed, ControlFlow}; +use {EventsLoopClosed, ControlFlow}; -use super::{WindowId, DeviceId}; +use super::WindowId; use super::window::WindowStore; use super::keyboard::init_keyboard; use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken}; use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor, wl_display, wl_registry, wl_output, wl_surface, - wl_pointer, wl_keyboard}; + wl_pointer, wl_keyboard, wl_touch}; use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation}; use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6; @@ -132,6 +132,7 @@ impl EventsLoop { sink: sink.clone(), keyboard: None, pointer: None, + touch: None, windows_token: store.clone() }; @@ -439,6 +440,7 @@ struct SeatIData { sink: Arc>, pointer: Option, keyboard: Option, + touch: Option, windows_token: StateToken } @@ -449,8 +451,11 @@ fn seat_implementation() -> wl_seat::Implementation { // create pointer if applicable if capabilities.contains(wl_seat::Capability::Pointer) && idata.pointer.is_none() { let pointer = seat.get_pointer().expect("Seat is not dead"); - let p_idata = PointerIData::new(&idata.sink, idata.windows_token.clone()); - evqh.register(&pointer, pointer_implementation(), p_idata); + let p_idata = super::pointer::PointerIData::new( + &idata.sink, + idata.windows_token.clone() + ); + evqh.register(&pointer, super::pointer::pointer_implementation(), p_idata); idata.pointer = Some(pointer); } // destroy pointer if applicable @@ -471,179 +476,23 @@ fn seat_implementation() -> wl_seat::Implementation { kbd.release(); } } - // TODO: Handle touch - } - } -} - -struct PointerIData { - sink: Arc>, - windows_token: StateToken, - mouse_focus: Option, - axis_buffer: Option<(f32, f32)>, - axis_discrete_buffer: Option<(i32, i32)>, - axis_state: TouchPhase, -} - -impl PointerIData { - fn new(sink: &Arc>, token: StateToken) - -> PointerIData - { - PointerIData { - sink: sink.clone(), - windows_token: token, - mouse_focus: None, - axis_buffer: None, - axis_discrete_buffer: None, - axis_state: TouchPhase::Cancelled - } - } -} - -fn pointer_implementation() -> wl_pointer::Implementation { - wl_pointer::Implementation { - enter: |evqh, idata, _, _, surface, x, y| { - let wid = evqh.state().get(&idata.windows_token).find_wid(surface); - if let Some(wid) = wid { - idata.mouse_focus = Some(wid); - let mut guard = idata.sink.lock().unwrap(); - guard.send_event( - Event::MouseEntered { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - }, - wid, - ); - guard.send_event( - Event::MouseMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (x, y), - }, - wid, + // create touch if applicable + if capabilities.contains(wl_seat::Capability::Touch) && idata.touch.is_none() { + let touch = seat.get_touch().expect("Seat is not dead"); + let t_idata = super::touch::TouchIData::new( + &idata.sink, + idata.windows_token.clone() ); + evqh.register(&touch, super::touch::touch_implementation(), t_idata); + idata.touch = Some(touch); } - }, - leave: |evqh, idata, _, _, surface| { - idata.mouse_focus = None; - let wid = evqh.state().get(&idata.windows_token).find_wid(surface); - if let Some(wid) = wid { - let mut guard = idata.sink.lock().unwrap(); - guard.send_event( - Event::MouseLeft { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - }, - wid, - ); - } - }, - motion: |_, idata, _, _, x, y| { - if let Some(wid) = idata.mouse_focus { - idata.sink.lock().unwrap().send_event( - Event::MouseMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (x, y) - }, - wid - ); - } - }, - button: |_, idata, _, _, _, button, state| { - if let Some(wid) = idata.mouse_focus { - let state = match state { - wl_pointer::ButtonState::Pressed => ElementState::Pressed, - wl_pointer::ButtonState::Released => ElementState::Released - }; - let button = match button { - 0x110 => MouseButton::Left, - 0x111 => MouseButton::Right, - 0x112 => MouseButton::Middle, - // TODO figure out the translation ? - _ => return - }; - idata.sink.lock().unwrap().send_event( - Event::MouseInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - state: state, - button: button, - }, - wid - ); - } - }, - axis: |_, idata, pointer, _, axis, value| { - if let Some(wid) = idata.mouse_focus { - if pointer.version() < 5 { - let (mut x, mut y) = (0.0, 0.0); - // old seat compatibility - match axis { - // wayland vertical sign convention is the inverse of winit - wl_pointer::Axis::VerticalScroll => y -= value as f32, - wl_pointer::Axis::HorizontalScroll => x += value as f32 - } - idata.sink.lock().unwrap().send_event( - Event::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), - phase: TouchPhase::Moved, - }, - wid - ); - } else { - let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0)); - match axis { - // wayland vertical sign convention is the inverse of winit - wl_pointer::Axis::VerticalScroll => y -= value as f32, - wl_pointer::Axis::HorizontalScroll => x += value as f32 - } - idata.axis_buffer = Some((x,y)); - idata.axis_state = match idata.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started - } + // destroy touch if applicable + if !capabilities.contains(wl_seat::Capability::Touch) { + if let Some(touch) = idata.touch.take() { + touch.release(); } } - }, - frame: |_, idata, _| { - let axis_buffer = idata.axis_buffer.take(); - let axis_discrete_buffer = idata.axis_discrete_buffer.take(); - if let Some(wid) = idata.mouse_focus { - if let Some((x, y)) = axis_discrete_buffer { - idata.sink.lock().unwrap().send_event( - Event::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - delta: MouseScrollDelta::LineDelta(x as f32, y as f32), - phase: idata.axis_state, - }, - wid - ); - } else if let Some((x, y)) = axis_buffer { - idata.sink.lock().unwrap().send_event( - Event::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), - phase: idata.axis_state, - }, - wid - ); - } - } - }, - axis_source: |_, _, _, _| {}, - axis_stop: |_, idata, _, _, _| { - idata.axis_state = TouchPhase::Ended; - }, - axis_discrete: |_, idata, _, axis, discrete| { - let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0)); - match axis { - // wayland vertical sign convention is the inverse of winit - wl_pointer::Axis::VerticalScroll => y -= discrete, - wl_pointer::Axis::HorizontalScroll => x += discrete - } - idata.axis_discrete_buffer = Some((x,y)); - idata.axis_state = match idata.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started - } - }, + } } } diff --git a/src/platform/linux/wayland/mod.rs b/src/platform/linux/wayland/mod.rs index 732bfc3e..df3e3cb8 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform/linux/wayland/mod.rs @@ -11,6 +11,8 @@ use wayland_client::protocol::wl_surface; use wayland_client::Proxy; mod event_loop; +mod pointer; +mod touch; mod keyboard; mod window; diff --git a/src/platform/linux/wayland/pointer.rs b/src/platform/linux/wayland/pointer.rs new file mode 100644 index 00000000..e7abcf34 --- /dev/null +++ b/src/platform/linux/wayland/pointer.rs @@ -0,0 +1,181 @@ +use std::sync::{Arc, Mutex}; + +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase}; + +use super::{WindowId, DeviceId}; +use super::event_loop::EventsLoopSink; +use super::window::WindowStore; + +use wayland_client::{Proxy, StateToken}; +use wayland_client::protocol::wl_pointer; + +pub struct PointerIData { + sink: Arc>, + windows_token: StateToken, + mouse_focus: Option, + axis_buffer: Option<(f32, f32)>, + axis_discrete_buffer: Option<(i32, i32)>, + axis_state: TouchPhase, +} + +impl PointerIData { + pub fn new(sink: &Arc>, token: StateToken) + -> PointerIData + { + PointerIData { + sink: sink.clone(), + windows_token: token, + mouse_focus: None, + axis_buffer: None, + axis_discrete_buffer: None, + axis_state: TouchPhase::Cancelled + } + } +} + +pub fn pointer_implementation() -> wl_pointer::Implementation { + wl_pointer::Implementation { + enter: |evqh, idata, _, _, surface, x, y| { + let wid = evqh.state().get(&idata.windows_token).find_wid(surface); + if let Some(wid) = wid { + idata.mouse_focus = Some(wid); + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::MouseEntered { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + wid, + ); + guard.send_event( + Event::MouseMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (x, y), + }, + wid, + ); + } + }, + leave: |evqh, idata, _, _, surface| { + idata.mouse_focus = None; + let wid = evqh.state().get(&idata.windows_token).find_wid(surface); + if let Some(wid) = wid { + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::MouseLeft { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + wid, + ); + } + }, + motion: |_, idata, _, _, x, y| { + if let Some(wid) = idata.mouse_focus { + idata.sink.lock().unwrap().send_event( + Event::MouseMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (x, y) + }, + wid + ); + } + }, + button: |_, idata, _, _, _, button, state| { + if let Some(wid) = idata.mouse_focus { + let state = match state { + wl_pointer::ButtonState::Pressed => ElementState::Pressed, + wl_pointer::ButtonState::Released => ElementState::Released + }; + let button = match button { + 0x110 => MouseButton::Left, + 0x111 => MouseButton::Right, + 0x112 => MouseButton::Middle, + // TODO figure out the translation ? + _ => return + }; + idata.sink.lock().unwrap().send_event( + Event::MouseInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + state: state, + button: button, + }, + wid + ); + } + }, + axis: |_, idata, pointer, _, axis, value| { + if let Some(wid) = idata.mouse_focus { + if pointer.version() < 5 { + let (mut x, mut y) = (0.0, 0.0); + // old seat compatibility + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x += value as f32 + } + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: TouchPhase::Moved, + }, + wid + ); + } else { + let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0)); + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x += value as f32 + } + idata.axis_buffer = Some((x,y)); + idata.axis_state = match idata.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + } + } + }, + frame: |_, idata, _| { + let axis_buffer = idata.axis_buffer.take(); + let axis_discrete_buffer = idata.axis_discrete_buffer.take(); + if let Some(wid) = idata.mouse_focus { + if let Some((x, y)) = axis_discrete_buffer { + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::LineDelta(x as f32, y as f32), + phase: idata.axis_state, + }, + wid + ); + } else if let Some((x, y)) = axis_buffer { + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: idata.axis_state, + }, + wid + ); + } + } + }, + axis_source: |_, _, _, _| {}, + axis_stop: |_, idata, _, _, _| { + idata.axis_state = TouchPhase::Ended; + }, + axis_discrete: |_, idata, _, axis, discrete| { + let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0)); + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= discrete, + wl_pointer::Axis::HorizontalScroll => x += discrete + } + idata.axis_discrete_buffer = Some((x,y)); + idata.axis_state = match idata.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + }, + } +} \ No newline at end of file diff --git a/src/platform/linux/wayland/touch.rs b/src/platform/linux/wayland/touch.rs new file mode 100644 index 00000000..9aa9b4b4 --- /dev/null +++ b/src/platform/linux/wayland/touch.rs @@ -0,0 +1,106 @@ +use std::sync::{Arc, Mutex}; + +use {WindowEvent as Event, TouchPhase}; + +use super::{WindowId, DeviceId}; +use super::event_loop::EventsLoopSink; +use super::window::WindowStore; + +use wayland_client::StateToken; +use wayland_client::protocol::wl_touch; + +pub struct TouchIData { + sink: Arc>, + windows_token: StateToken, + pending_ids: Vec, +} + +struct TouchPoint { + wid: WindowId, + location: (f64, f64), + id: i32 +} + +impl TouchIData { + pub fn new(sink: &Arc>, token: StateToken) + -> TouchIData + { + TouchIData { + sink: sink.clone(), + windows_token: token, + pending_ids: Vec::new(), + } + } +} + +pub fn touch_implementation() -> wl_touch::Implementation { + wl_touch::Implementation { + down: |evqh, idata, _, _serial, _time, surface, touch_id, x, y| { + let wid = evqh.state().get(&idata.windows_token).find_wid(surface); + if let Some(wid) = wid { + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Started, + location: (x, y), + id: touch_id as u64 + }), + wid, + ); + idata.pending_ids.push(TouchPoint { + wid: wid, + location: (x, y), + id: touch_id + }); + } + }, + up: |_, idata, _, _serial, _time, touch_id| { + let idx = idata.pending_ids.iter().position(|p| p.id == touch_id); + if let Some(idx) = idx { + let pt = idata.pending_ids.remove(idx); + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Ended, + location: pt.location, + id: touch_id as u64 + }), + pt.wid, + ); + } + }, + motion: |_, idata, _, _time, touch_id, x, y| { + let pt = idata.pending_ids.iter_mut().find(|p| p.id == touch_id); + if let Some(pt) = pt { + let mut guard = idata.sink.lock().unwrap(); + pt.location = (x, y); + guard.send_event( + Event::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Moved, + location: (x, y), + id: touch_id as u64 + }), + pt.wid, + ); + } + }, + frame: |_, _, _| {}, + cancel: |_, idata, _| { + let mut guard = idata.sink.lock().unwrap(); + for pt in idata.pending_ids.drain(..) { + guard.send_event( + Event::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Cancelled, + location: pt.location, + id: pt.id as u64 + }), + pt.wid, + ); + } + } + } +} \ No newline at end of file