From a5ba9983c934b5469e4d95a825912a6c5e98ee7e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 9 Oct 2016 16:03:00 +0200 Subject: [PATCH] wayland: basic mouse & keyboard support --- Cargo.toml | 2 +- src/api/wayland/context.rs | 272 +++++++++++++++++++++++++++++++++++- src/api/wayland/keyboard.rs | 216 ++++++++++++++++++++++++++++ src/api/wayland/mod.rs | 1 + 4 files changed, 485 insertions(+), 6 deletions(-) create mode 100644 src/api/wayland/keyboard.rs diff --git a/Cargo.toml b/Cargo.toml index 7d6c68ac..40fae0fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,6 @@ dwmapi-sys = "0.1" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] wayland-client = { version = "0.7.3", features = ["dlopen"] } -wayland-kbd = "0.6" +wayland-kbd = "0.6.2" wayland-window = "0.4.2" x11-dl = "2.8" diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs index 73e4ac44..45a13529 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -1,13 +1,16 @@ -use Event; +use {Event, ElementState, MouseButton}; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use wayland_client::{EnvHandler, default_connect, EventQueue, EventQueueHandle, Init, Proxy}; use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor, - wl_display, wl_registry, wl_output, wl_surface}; + wl_display, wl_registry, wl_output, wl_surface, wl_pointer, + wl_keyboard}; use super::wayland_window; +use super::wayland_kbd::MappedKeyboard; +use super::keyboard::KbdHandler; /* * Registry and globals handling @@ -15,7 +18,6 @@ use super::wayland_window; wayland_env!(InnerEnv, compositor: wl_compositor::WlCompositor, - seat: wl_seat::WlSeat, shell: wl_shell::WlShell, shm: wl_shm::WlShm, subcompositor: wl_subcompositor::WlSubcompositor @@ -26,7 +28,13 @@ struct WaylandEnv { inner: EnvHandler, monitors: Vec, my_id: usize, - windows: Vec<(Arc,Arc>>)> + windows: Vec<(Arc,Arc>>)>, + seat: Option, + mouse: Option, + mouse_focus: Option>>>, + mouse_location: (i32, i32), + kbd: Option, + kbd_handler: MappedKeyboard } struct OutputInfo { @@ -56,7 +64,13 @@ impl WaylandEnv { inner: EnvHandler::new(), monitors: Vec::new(), my_id: 0, - windows: Vec::new() + windows: Vec::new(), + seat: None, + mouse: None, + mouse_focus: None, + mouse_location: (0,0), + kbd: None, + kbd_handler: MappedKeyboard::new(KbdHandler::new()).ok().expect("Missing libxkbcommon!") // TODO } } @@ -94,6 +108,14 @@ impl wl_registry::Handler for WaylandEnv { .expect("Registry cannot be dead"); evqh.register::<_, WaylandEnv>(&output, self.my_id); self.monitors.push(OutputInfo::new(output, name)); + } else if interface == "wl_seat" && self.seat.is_none() { + // Only grab the first seat + // TODO: Handle multi-seat-setup? + assert!(version >= 5, "Version 5 of seat interface is needed by glutin."); + let seat = self.registry.bind::(5, name) + .expect("Registry cannot be dead"); + evqh.register::<_, WaylandEnv>(&seat, self.my_id); + self.seat = Some(seat); } self.inner.global(evqh, registry, name, interface, version); } @@ -241,6 +263,10 @@ impl WaylandContext { } } +/* + * Monitors API + */ + pub fn get_primary_monitor(ctxt: &Arc) -> MonitorId { let mut guard = ctxt.evq.lock().unwrap(); let state = guard.state(); @@ -298,3 +324,239 @@ impl MonitorId { (0,0) } } + +/* + * Input Handling + */ + +impl wl_seat::Handler for WaylandEnv { + fn capabilities(&mut self, + evqh: &mut EventQueueHandle, + seat: &wl_seat::WlSeat, + capabilities: wl_seat::Capability) + { + // create pointer if applicable + if capabilities.contains(wl_seat::Pointer) && self.mouse.is_none() { + let pointer = seat.get_pointer().expect("Seat is not dead"); + evqh.register::<_, WaylandEnv>(&pointer, self.my_id); + self.mouse = Some(pointer); + } + // destroy pointer if applicable + if !capabilities.contains(wl_seat::Pointer) { + if let Some(pointer) = self.mouse.take() { + pointer.release(); + } + } + // create keyboard if applicable + if capabilities.contains(wl_seat::Keyboard) && self.kbd.is_none() { + let kbd = seat.get_keyboard().expect("Seat is not dead"); + evqh.register::<_, WaylandEnv>(&kbd, self.my_id); + self.kbd = Some(kbd); + } + // destroy keyboard if applicable + if !capabilities.contains(wl_seat::Keyboard) { + if let Some(kbd) = self.kbd.take() { + kbd.release(); + } + } + } +} + +declare_handler!(WaylandEnv, wl_seat::Handler, wl_seat::WlSeat); + +/* + * Pointer Handling + */ + +impl wl_pointer::Handler for WaylandEnv { + fn enter(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _serial: u32, + surface: &wl_surface::WlSurface, + surface_x: f64, + surface_y: f64) + { + self.mouse_location = (surface_x as i32, surface_y as i32); + for &(ref window, ref eviter) in &self.windows { + if window.equals(surface) { + self.mouse_focus = Some(eviter.clone()); + let (w, h) = self.mouse_location; + eviter.lock().unwrap().push_back( + Event::MouseMoved(w, h) + ); + break; + } + } + } + + fn leave(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _serial: u32, + _surface: &wl_surface::WlSurface) + { + self.mouse_focus = None + } + + fn motion(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _time: u32, + surface_x: f64, + surface_y: f64) + { + self.mouse_location = (surface_x as i32, surface_y as i32); + if let Some(ref eviter) = self.mouse_focus { + let (w,h) = self.mouse_location; + eviter.lock().unwrap().push_back( + Event::MouseMoved(w, h) + ); + } + } + + fn button(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _serial: u32, + _time: u32, + button: u32, + state: wl_pointer::ButtonState) + { + if let Some(ref eviter) = self.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 + }; + eviter.lock().unwrap().push_back( + Event::MouseInput(state, button) + ); + } + } + + // TODO: proper scroll handling + + fn axis(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _time: u32, + axis: wl_pointer::Axis, + value: f64) + { + } + + fn frame(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer) + { + } + + fn axis_source(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + axis_source: wl_pointer::AxisSource) + { + } + + fn axis_stop(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _time: u32, + axis: wl_pointer::Axis) + { + } + + fn axis_discrete(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + axis: wl_pointer::Axis, + discrete: i32) + { + } +} + +declare_handler!(WaylandEnv, wl_pointer::Handler, wl_pointer::WlPointer); + +/* + * Keyboard Handling + */ + +impl wl_keyboard::Handler for WaylandEnv { + // mostly pass-through + fn keymap(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + format: wl_keyboard::KeymapFormat, + fd: ::std::os::unix::io::RawFd, + size: u32) + { + self.kbd_handler.keymap(evqh, proxy, format, fd, size) + } + + fn enter(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + surface: &wl_surface::WlSurface, + keys: Vec) + { + for &(ref window, ref eviter) in &self.windows { + if window.equals(surface) { + self.kbd_handler.handler().target = Some(eviter.clone()); + break + } + } + self.kbd_handler.enter(evqh, proxy, serial, surface, keys) + } + + fn leave(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + surface: &wl_surface::WlSurface) + { + self.kbd_handler.handler().target = None; + self.kbd_handler.leave(evqh, proxy, serial, surface) + } + + fn key(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + time: u32, + key: u32, + state: wl_keyboard::KeyState) + { + self.kbd_handler.key(evqh, proxy, serial, time, key, state) + } + + fn modifiers(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32) + { + self.kbd_handler.modifiers(evqh, proxy, serial, mods_depressed, mods_latched, mods_locked, group) + } + + fn repeat_info(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + rate: i32, + delay: i32) + { + self.kbd_handler.repeat_info(evqh, proxy, rate, delay) + } +} + +declare_handler!(WaylandEnv, wl_keyboard::Handler, wl_keyboard::WlKeyboard); diff --git a/src/api/wayland/keyboard.rs b/src/api/wayland/keyboard.rs new file mode 100644 index 00000000..abc3069c --- /dev/null +++ b/src/api/wayland/keyboard.rs @@ -0,0 +1,216 @@ +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; + +use {VirtualKeyCode, ElementState, Event}; + +use super::wayland_kbd; +use wayland_client::EventQueueHandle; +use wayland_client::protocol::wl_keyboard; + +pub struct KbdHandler { + pub target: Option>>> +} + +impl KbdHandler { + pub fn new() -> KbdHandler { + KbdHandler { target: None } + } +} + +impl wayland_kbd::Handler for KbdHandler { + fn key(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_keyboard::WlKeyboard, + _serial: u32, + _time: u32, + rawkey: u32, + keysym: u32, + state: wl_keyboard::KeyState, + utf8: Option) + { + if let Some(ref eviter) = self.target { + let state = match state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + }; + let vkcode = key_to_vkey(rawkey, keysym); + let mut guard = eviter.lock().unwrap(); + guard.push_back(Event::KeyboardInput(state, rawkey as u8, vkcode)); + // send char event only on key press, not release + if let ElementState::Released = state { return } + if let Some(txt) = utf8 { + for chr in txt.chars() { + guard.push_back(Event::ReceivedCharacter(chr)); + } + } + } + } +} + +fn key_to_vkey(rawkey: u32, keysym: u32) -> Option { + match rawkey { + 1 => Some(VirtualKeyCode::Escape), + 2 => Some(VirtualKeyCode::Key1), + 3 => Some(VirtualKeyCode::Key2), + 4 => Some(VirtualKeyCode::Key3), + 5 => Some(VirtualKeyCode::Key4), + 6 => Some(VirtualKeyCode::Key5), + 7 => Some(VirtualKeyCode::Key6), + 8 => Some(VirtualKeyCode::Key7), + 9 => Some(VirtualKeyCode::Key8), + 10 => Some(VirtualKeyCode::Key9), + 11 => Some(VirtualKeyCode::Key0), + _ => keysym_to_vkey(keysym) + } +} + +fn keysym_to_vkey(keysym: u32) -> Option { + use super::wayland_kbd::keysyms; + match keysym { + // letters + keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A), + keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B), + keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C), + keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D), + keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E), + keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F), + keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G), + keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H), + keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I), + keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J), + keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K), + keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L), + keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M), + keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N), + keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O), + keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P), + keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q), + keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R), + keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S), + keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T), + keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U), + keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V), + keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W), + keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X), + keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y), + keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z), + // F-- + keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1), + keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2), + keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3), + keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4), + keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5), + keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6), + keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7), + keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8), + keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9), + keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10), + keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11), + keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12), + keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13), + keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14), + keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15), + // flow control + keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot), + keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll), + keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause), + keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert), + keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home), + keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete), + keysyms::XKB_KEY_End => Some(VirtualKeyCode::End), + keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown), + keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp), + // arrows + keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left), + keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up), + keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right), + keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down), + // + keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back), + keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return), + keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space), + // keypad + keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock), + keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0), + keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1), + keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2), + keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3), + keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4), + keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5), + keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6), + keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7), + keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8), + keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9), + // misc + // => Some(VirtualKeyCode::AbntC1), + // => Some(VirtualKeyCode::AbntC2), + keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Add), + keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe), + // => Some(VirtualKeyCode::Apps), + // => Some(VirtualKeyCode::At), + // => Some(VirtualKeyCode::Ax), + keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash), + // => Some(VirtualKeyCode::Calculator), + // => Some(VirtualKeyCode::Capital), + keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon), + keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma), + // => Some(VirtualKeyCode::Convert), + // => Some(VirtualKeyCode::Decimal), + // => Some(VirtualKeyCode::Divide), + keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals), + // => Some(VirtualKeyCode::Grave), + // => Some(VirtualKeyCode::Kana), + // => Some(VirtualKeyCode::Kanji), + keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt), + // => Some(VirtualKeyCode::LBracket), + keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl), + // => Some(VirtualKeyCode::LMenu), + keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift), + // => Some(VirtualKeyCode::LWin), + // => Some(VirtualKeyCode::Mail), + // => Some(VirtualKeyCode::MediaSelect), + // => Some(VirtualKeyCode::MediaStop), + keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus), + keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Multiply), + // => Some(VirtualKeyCode::Mute), + // => Some(VirtualKeyCode::MyComputer), + // => Some(VirtualKeyCode::NextTrack), + // => Some(VirtualKeyCode::NoConvert), + keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma), + keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter), + keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals), + // => Some(VirtualKeyCode::OEM102), + // => Some(VirtualKeyCode::Period), + // => Some(VirtualKeyCode::Playpause), + // => Some(VirtualKeyCode::Power), + // => Some(VirtualKeyCode::Prevtrack), + keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt), + // => Some(VirtualKeyCode::RBracket), + keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl), + // => Some(VirtualKeyCode::RMenu), + keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift), + // => Some(VirtualKeyCode::RWin), + keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon), + keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash), + // => Some(VirtualKeyCode::Sleep), + // => Some(VirtualKeyCode::Stop), + // => Some(VirtualKeyCode::Subtract), + // => Some(VirtualKeyCode::Sysrq), + keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab), + // => Some(VirtualKeyCode::Underline), + // => Some(VirtualKeyCode::Unlabeled), + keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown), + keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp), + // => Some(VirtualKeyCode::Wake), + // => Some(VirtualKeyCode::Webback), + // => Some(VirtualKeyCode::WebFavorites), + // => Some(VirtualKeyCode::WebForward), + // => Some(VirtualKeyCode::WebHome), + // => Some(VirtualKeyCode::WebRefresh), + // => Some(VirtualKeyCode::WebSearch), + // => Some(VirtualKeyCode::WebStop), + // => Some(VirtualKeyCode::Yen), + // fallback + _ => None + } +} diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs index bf6ba775..cb0c5b5b 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -8,4 +8,5 @@ extern crate wayland_kbd; extern crate wayland_window; mod context; +mod keyboard; mod window;