diff --git a/Cargo.toml b/Cargo.toml index b2af1c94..4252a1b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ kernel32-sys = "0.2" dwmapi-sys = "0.1" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] -wayland-client = { version = "0.5.4", features = ["dlopen"] } -wayland-kbd = "0.3.3" -wayland-window = "0.2.2" +wayland-client = { version = "0.7.4", features = ["dlopen"] } +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 58789202..f98b1e88 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -1,211 +1,662 @@ -use Event as GlutinEvent; +use {Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase}; -use std::collections::{HashMap, VecDeque, HashSet}; +use std::collections::VecDeque; use std::sync::{Arc, Mutex}; -use libc::c_void; - -use wayland_client::{EventIterator, Proxy, ProxyId}; -use wayland_client::wayland::get_display; -use wayland_client::wayland::compositor::{WlCompositor, WlSurface}; -use wayland_client::wayland::output::WlOutput; -use wayland_client::wayland::seat::{WlSeat, WlPointer}; -use wayland_client::wayland::shell::{WlShell, WlShellSurface}; -use wayland_client::wayland::shm::WlShm; -use wayland_client::wayland::subcompositor::WlSubcompositor; +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_pointer, + wl_keyboard}; +use super::wayland_window; use super::wayland_kbd::MappedKeyboard; -use super::wayland_window::DecoratedSurface; +use super::keyboard::KbdHandler; -lazy_static! { - pub static ref WAYLAND_CONTEXT: Option = { - WaylandContext::init() - }; -} +/* + * Registry and globals handling + */ wayland_env!(InnerEnv, - compositor: WlCompositor, - seat: WlSeat, - shell: WlShell, - shm: WlShm, - subcompositor: WlSubcompositor + compositor: wl_compositor::WlCompositor, + shell: wl_shell::WlShell, + shm: wl_shm::WlShm, + subcompositor: wl_subcompositor::WlSubcompositor ); -pub struct WaylandFocuses { - pub pointer: Option, - pub pointer_on: Option, - pub pointer_at: Option<(f64, f64)>, - pub keyboard: Option, - pub keyboard_on: Option +enum KbdType { + Mapped(MappedKeyboard), + Plain(Option>>>) } +struct WaylandEnv { + registry: wl_registry::WlRegistry, + inner: EnvHandler, + monitors: Vec, + my_id: usize, + windows: Vec<(Arc,Arc>>)>, + seat: Option, + mouse: Option, + mouse_focus: Option>>>, + mouse_location: (i32, i32), + axis_buffer: Option<(f32, f32)>, + axis_discrete_buffer: Option<(i32, i32)>, + axis_state: TouchPhase, + kbd: Option, + kbd_handler: KbdType +} + +struct OutputInfo { + output: wl_output::WlOutput, + id: u32, + scale: f32, + pix_size: (u32, u32), + name: String +} + +impl OutputInfo { + fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo { + OutputInfo { + output: output, + id: id, + scale: 1.0, + pix_size: (0, 0), + name: "".into() + } + } +} + +impl WaylandEnv { + fn new(registry: wl_registry::WlRegistry) -> WaylandEnv { + let kbd_handler = match MappedKeyboard::new(KbdHandler::new()) { + Ok(h) => KbdType::Mapped(h), + Err(_) => KbdType::Plain(None) + }; + WaylandEnv { + registry: registry, + inner: EnvHandler::new(), + monitors: Vec::new(), + my_id: 0, + windows: Vec::new(), + seat: None, + mouse: None, + mouse_focus: None, + mouse_location: (0,0), + axis_buffer: None, + axis_discrete_buffer: None, + axis_state: TouchPhase::Started, + kbd: None, + kbd_handler: kbd_handler + } + } + + fn get_seat(&self) -> Option { + for &(name, ref interface, version) in self.inner.globals() { + if interface == "wl_seat" { + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let seat = self.registry.bind::(5, name).expect("Seat cannot be destroyed"); + return Some(seat) + } + } + None + } +} + +impl Init for WaylandEnv { + fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) { + evqh.register::<_, WaylandEnv>(&self.registry, index); + self.my_id = index + } +} + +impl wl_registry::Handler for WaylandEnv { + fn global(&mut self, + evqh: &mut EventQueueHandle, + registry: &wl_registry::WlRegistry, + name: u32, + interface: String, + version: u32) + { + if interface == "wl_output" { + // intercept outputs + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let output = self.registry.bind::(1, name) + .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); + } + + fn global_remove(&mut self, + evqh: &mut EventQueueHandle, + registry: &wl_registry::WlRegistry, + name: u32) + { + // prune old monitors + self.monitors.retain(|m| m.id != name); + self.inner.global_remove(evqh, registry, name); + } +} + +declare_handler!(WaylandEnv, wl_registry::Handler, wl_registry::WlRegistry); + +impl wl_output::Handler for WaylandEnv { + fn geometry(&mut self, + _: &mut EventQueueHandle, + proxy: &wl_output::WlOutput, + _x: i32, _y: i32, + _physical_width: i32, _physical_height: i32, + _subpixel: wl_output::Subpixel, + make: String, model: String, + _transform: wl_output::Transform) + { + for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { + m.name = format!("{} ({})", model, make); + break; + } + } + fn mode(&mut self, + _: &mut EventQueueHandle, + proxy: &wl_output::WlOutput, + flags: wl_output::Mode, + width: i32, height: i32, + _refresh: i32) + { + if flags.contains(wl_output::Current) { + for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { + m.pix_size = (width as u32, height as u32); + break; + } + } + } + fn scale(&mut self, + _: &mut EventQueueHandle, + proxy: &wl_output::WlOutput, + factor: i32) + { + for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { + m.scale = factor as f32; + break; + } + } +} + +declare_handler!(WaylandEnv, wl_output::Handler, wl_output::WlOutput); + +/* + * Main context struct + */ + pub struct WaylandContext { - inner: InnerEnv, - iterator: Mutex, - monitors: Vec<(WlOutput, u32, u32, String)>, - queues: Mutex>>>>, - known_surfaces: Mutex>, - focuses: Mutex + pub display: wl_display::WlDisplay, + evq: Mutex, + env_id: usize, } impl WaylandContext { - fn init() -> Option { - let display = match get_display() { - Some(display) => display, - None => return None + pub fn init() -> Option { + // attempt to connect to the wayland server + // this handles both "no libwayland" and "no compositor" cases + let (display, mut event_queue) = match default_connect() { + Ok(ret) => ret, + Err(e) => return None }; - let (mut inner_env, iterator) = InnerEnv::init(display); - - let outputs_events = EventIterator::new(); - - let mut monitors = inner_env.globals.iter() - .flat_map(|&(id, _, _)| inner_env.rebind_id::(id)) - .map(|(mut monitor, _)| { - monitor.set_evt_iterator(&outputs_events); - (monitor, 0, 0, String::new()) - }).collect(); - - inner_env.display.sync_roundtrip().unwrap(); - - super::monitor::init_monitors(&mut monitors, outputs_events); + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let registry = display.get_registry().expect("Display cannot be already destroyed."); + let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry)); + // two syncs fully initialize + event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); + event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); Some(WaylandContext { - inner: inner_env, - iterator: Mutex::new(iterator), - monitors: monitors, - queues: Mutex::new(HashMap::new()), - known_surfaces: Mutex::new(HashSet::new()), - focuses: Mutex::new(WaylandFocuses { - pointer: None, - pointer_on: None, - pointer_at: None, - keyboard: None, - keyboard_on: None - }) + evq: Mutex::new(event_queue), + display: display, + env_id: env_id }) } - pub fn new_surface(&self) -> Option<(WlSurface, Arc>>)> { - self.inner.compositor.as_ref().map(|c| { - let s = c.0.create_surface(); - let id = s.id(); - let queue = { - let mut q = VecDeque::new(); - q.push_back(GlutinEvent::Refresh); - Arc::new(Mutex::new(q)) - }; - self.queues.lock().unwrap().insert(id, queue.clone()); - self.known_surfaces.lock().unwrap().insert(id); - (s, queue) - }) + pub fn dispatch_pending(&self) { + let mut guard = self.evq.lock().unwrap(); + guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); } - pub fn dropped_surface(&self, id: ProxyId) { - self.queues.lock().unwrap().remove(&id); - self.known_surfaces.lock().unwrap().remove(&id); + pub fn dispatch(&self) { + let mut guard = self.evq.lock().unwrap(); + guard.dispatch().expect("Wayland connection unexpectedly lost"); } - pub fn decorated_from(&self, surface: &WlSurface, width: i32, height: i32) -> Option { - let inner = &self.inner; - match (&inner.compositor, &inner.subcompositor, &inner.shm, &inner.shell) { - (&Some(ref compositor), &Some(ref subcompositor), &Some(ref shm), &Some(ref shell)) => { - DecoratedSurface::new( - surface, width, height, - &compositor.0, &subcompositor.0, &shm.0, &shell.0, - self.inner.rebind::().map(|(seat, _)| seat) - ).ok() - } - _ => None + pub fn flush(&self) { + self.display.flush(); + } + + pub fn with_output(&self, id: MonitorId, f: F) where F: FnOnce(&wl_output::WlOutput) { + let mut guard = self.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(self.env_id); + for m in env.monitors.iter().filter(|m| m.id == id.id) { + f(&m.output); + break } } - pub fn plain_from(&self, surface: &WlSurface, fullscreen: Option) -> Option { - use wayland_client::wayland::shell::WlShellSurfaceFullscreenMethod; - - let inner = &self.inner; - if let Some((ref shell, _)) = inner.shell { - let shell_surface = shell.get_shell_surface(surface); - if let Some(monitor_id) = fullscreen { - for m in &self.monitors { - if m.0.id() == monitor_id { - shell_surface.set_fullscreen( - WlShellSurfaceFullscreenMethod::Default, - 0, - Some(&m.0) - ); - return Some(shell_surface) - } - } - } - shell_surface.set_toplevel(); - Some(shell_surface) - } else { - None - } + pub fn create_window(&self) + -> (Arc, Arc>>, wayland_window::DecoratedSurface) + { + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let env = state.get_mut_handler::(self.env_id); + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let surface = Arc::new(env.inner.compositor.create_surface().expect("Compositor cannot be dead")); + let eventiter = Arc::new(Mutex::new(VecDeque::new())); + env.windows.push((surface.clone(), eventiter.clone())); + let decorated = wayland_window::DecoratedSurface::new( + &*surface, 800, 600, + &env.inner.compositor, + &env.inner.subcompositor, + &env.inner.shm, + &env.inner.shell, + env.get_seat(), + false + ).expect("Failed to create a tmpfile buffer."); + (surface, eventiter, decorated) } - pub fn display_ptr(&self) -> *const c_void { - self.inner.display.ptr() as *const _ - } - - pub fn dispatch_events(&self) { - self.inner.display.dispatch_pending().unwrap(); - let mut iterator = self.iterator.lock().unwrap(); - let mut focuses = self.focuses.lock().unwrap(); - let known_surfaces = self.known_surfaces.lock().unwrap(); - let queues = self.queues.lock().unwrap(); - // first, keyboard events - let kdb_evts = super::keyboard::translate_kbd_events(&mut *focuses, &known_surfaces); - for (evt, id) in kdb_evts { - if let Some(q) = queues.get(&id) { - q.lock().unwrap().push_back(evt); - } - } - // then, the rest - for evt in &mut *iterator { - if let Some((evt, id)) = super::events::translate_event( - evt, &mut *focuses, &known_surfaces, - self.inner.seat.as_ref().map(|s| &s.0)) - { - if let Some(q) = queues.get(&id) { - q.lock().unwrap().push_back(evt); - } - } - } - } - - pub fn flush_events(&self) -> ::std::io::Result { - self.inner.display.flush() - } - - pub fn read_events(&self) -> ::std::io::Result> { - let guard = match self.inner.display.prepare_read() { - Some(g) => g, - None => return Ok(None) - }; - return guard.read_events().map(|i| Some(i)); - } - - pub fn monitor_ids(&self) -> Vec { - self.monitors.iter().map(|o| o.0.id()).collect() - } - - pub fn monitor_name(&self, pid: ProxyId) -> Option { - for o in &self.monitors { - if o.0.id() == pid { - return Some(o.3.clone()) - } - } - None - } - - pub fn monitor_dimensions(&self, pid: ProxyId) -> Option<(u32, u32)> { - for o in &self.monitors { - if o.0.id() == pid { - return Some((o.1, o.2)) - } - } - None + pub fn prune_dead_windows(&self) { + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let env = state.get_mut_handler::(self.env_id); + env.windows.retain(|w| w.0.is_alive()); } } + +/* + * Monitors API + */ + +pub fn get_primary_monitor(ctxt: &Arc) -> MonitorId { + let mut guard = ctxt.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(ctxt.env_id); + if let Some(ref monitor) = env.monitors.iter().next() { + MonitorId { + id: monitor.id, + ctxt: ctxt.clone() + } + } else { + panic!("No monitor is available.") + } +} + +pub fn get_available_monitors(ctxt: &Arc) -> VecDeque { + let mut guard = ctxt.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(ctxt.env_id); + env.monitors.iter() + .map(|m| MonitorId { id: m.id, ctxt: ctxt.clone() }) + .collect() +} + +#[derive(Clone)] +pub struct MonitorId { + id: u32, + ctxt: Arc +} + +impl MonitorId { + pub fn get_name(&self) -> Option { + let mut guard = self.ctxt.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(self.ctxt.env_id); + for m in env.monitors.iter().filter(|m| m.id == self.id) { + return Some(m.name.clone()) + } + // if we reach here, this monitor does not exist any more + None + } + + #[inline] + pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId { + ::native_monitor::NativeMonitorId::Unavailable + } + + pub fn get_dimensions(&self) -> (u32, u32) { + let mut guard = self.ctxt.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(self.ctxt.env_id); + for m in env.monitors.iter().filter(|m| m.id == self.id) { + return m.pix_size + } + // if we reach here, this monitor does not exist any more + (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) + ); + } + } + + fn axis(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + _time: u32, + axis: wl_pointer::Axis, + value: f64) + { + let (mut x, mut y) = self.axis_buffer.unwrap_or((0.0, 0.0)); + match axis { + wl_pointer::Axis::VerticalScroll => y += value as f32, + wl_pointer::Axis::HorizontalScroll => x += value as f32 + } + self.axis_buffer = Some((x,y)); + self.axis_state = match self.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + } + + fn frame(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer) + { + let axis_buffer = self.axis_buffer.take(); + let axis_discrete_buffer = self.axis_discrete_buffer.take(); + if let Some(ref eviter) = self.mouse_focus { + if let Some((x, y)) = axis_discrete_buffer { + eviter.lock().unwrap().push_back( + Event::MouseWheel( + MouseScrollDelta::LineDelta(x as f32, y as f32), + self.axis_state + ) + ); + } else if let Some((x, y)) = axis_buffer { + eviter.lock().unwrap().push_back( + Event::MouseWheel( + MouseScrollDelta::PixelDelta(x as f32, y as f32), + self.axis_state + ) + ); + } + } + } + + 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) + { + self.axis_state = TouchPhase::Ended; + } + + fn axis_discrete(&mut self, + _evqh: &mut EventQueueHandle, + _proxy: &wl_pointer::WlPointer, + axis: wl_pointer::Axis, + discrete: i32) + { + let (mut x, mut y) = self.axis_discrete_buffer.unwrap_or((0,0)); + match axis { + wl_pointer::Axis::VerticalScroll => y += discrete, + wl_pointer::Axis::HorizontalScroll => x += discrete + } + self.axis_discrete_buffer = Some((x,y)); + self.axis_state = match self.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + } +} + +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) + { + match self.kbd_handler { + KbdType::Mapped(ref mut h) => h.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) + { + let mut opt_eviter = None; + for &(ref window, ref eviter) in &self.windows { + if window.equals(surface) { + opt_eviter = Some(eviter.clone()); + break; + } + } + if let Some(ref eviter) = opt_eviter { + // send focused event + let mut guard = eviter.lock().unwrap(); + guard.push_back(Event::Focused(true)); + } + match self.kbd_handler { + KbdType::Mapped(ref mut h) => { + h.handler().target = opt_eviter; + h.enter(evqh, proxy, serial, surface, keys); + }, + KbdType::Plain(ref mut opt) => { *opt = opt_eviter; } + } + } + + fn leave(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + surface: &wl_surface::WlSurface) + { + let opt_eviter = match self.kbd_handler { + KbdType::Mapped(ref mut h) => { + let eviter = h.handler().target.take(); + h.leave(evqh, proxy, serial, surface); + eviter + }, + KbdType::Plain(ref mut opt) => opt.take() + }; + if let Some(eviter) = opt_eviter { + let mut guard = eviter.lock().unwrap(); + guard.push_back(Event::Focused(false)); + } + } + + fn key(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + time: u32, + key: u32, + state: wl_keyboard::KeyState) + { + match self.kbd_handler { + KbdType::Mapped(ref mut h) => h.key(evqh, proxy, serial, time, key, state), + KbdType::Plain(Some(ref eviter)) => { + let state = match state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + }; + let mut guard = eviter.lock().unwrap(); + guard.push_back(Event::KeyboardInput( + state, + key as u8, + None + )); + }, + KbdType::Plain(None) => () + } + } + + fn modifiers(&mut self, + evqh: &mut EventQueueHandle, + proxy: &wl_keyboard::WlKeyboard, + serial: u32, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32) + { + match self.kbd_handler { + KbdType::Mapped(ref mut h) => h.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) + { + match self.kbd_handler { + KbdType::Mapped(ref mut h) => h.repeat_info(evqh, proxy, rate, delay), + _ => () + } + } +} + +declare_handler!(WaylandEnv, wl_keyboard::Handler, wl_keyboard::WlKeyboard); diff --git a/src/api/wayland/events.rs b/src/api/wayland/events.rs deleted file mode 100644 index 92a0b95f..00000000 --- a/src/api/wayland/events.rs +++ /dev/null @@ -1,112 +0,0 @@ -use std::collections::HashSet; - -use TouchPhase; -use Event as GlutinEvent; -use ElementState; -use MouseButton; -use MouseScrollDelta; - -use wayland_client::Event as WaylandEvent; -use wayland_client::ProxyId; -use wayland_client::wayland::WaylandProtocolEvent as WPE; -use wayland_client::wayland::seat::{WlSeat, WlSeatEvent, WlPointerEvent, - WlPointerButtonState, - WlPointerAxis, WlSeatCapability}; - -use super::wayland_kbd::MappedKeyboard; - -use super::context::WaylandFocuses; - -pub fn translate_event( - evt: WaylandEvent, - focuses: &mut WaylandFocuses, - known_surfaces: &HashSet, - seat: Option<&WlSeat>, - ) -> Option<(GlutinEvent, ProxyId)> -{ - let WaylandEvent::Wayland(wayland_evt) = evt; - match wayland_evt { - WPE::WlSeat(_, seat_evt) => match seat_evt { - WlSeatEvent::Capabilities(cap) => { - if cap.contains(WlSeatCapability::Pointer) && focuses.pointer.is_none() { - if let Some(seat) = seat { - focuses.pointer = Some(seat.get_pointer()); - } - } - if cap.contains(WlSeatCapability::Keyboard) && focuses.keyboard.is_none() { - if let Some(seat) = seat { - match MappedKeyboard::new(seat) { - Ok(mk) => { - focuses.keyboard = Some(mk) - }, - Err(_) => {} - } - } - } - None - }, - _ => None - }, - WPE::WlPointer(_, pointer_evt) => match pointer_evt { - WlPointerEvent::Enter(_, surface, x, y) => { - if known_surfaces.contains(&surface) { - focuses.pointer_on = Some(surface); - focuses.pointer_at = Some((x, y)); - Some((GlutinEvent::MouseMoved(x as i32, y as i32), surface)) - } else { - None - } - } - WlPointerEvent::Leave(_, _) => { - focuses.pointer_on = None; - focuses.pointer_at = None; - None - } - WlPointerEvent::Motion(_, x, y) => { - if let Some(surface) = focuses.pointer_on { - focuses.pointer_at = Some((x, y)); - Some((GlutinEvent::MouseMoved(x as i32, y as i32), surface)) - } else { - None - } - } - WlPointerEvent::Button(_, _, button, state) => { - if let Some(surface) = focuses.pointer_on { - Some((GlutinEvent::MouseInput( - match state { - WlPointerButtonState::Pressed => ElementState::Pressed, - WlPointerButtonState::Released => ElementState::Released - }, - match button { - 0x110 => MouseButton::Left, - 0x111 => MouseButton::Right, - 0x112 => MouseButton::Middle, - // TODO figure out the translation ? - _ => return None - } - ), surface)) - } else { - None - } - } - WlPointerEvent::Axis(_, axis, amplitude) => { - if let Some(surface) = focuses.pointer_on { - Some((GlutinEvent::MouseWheel( - match axis { - WlPointerAxis::VerticalScroll => { - MouseScrollDelta::PixelDelta(amplitude as f32, 0.0) - } - WlPointerAxis::HorizontalScroll => { - MouseScrollDelta::PixelDelta(0.0, amplitude as f32) - } - }, - TouchPhase::Moved - ), surface)) - } else { - None - } - } - }, - _ => None - } -} diff --git a/src/api/wayland/keyboard.rs b/src/api/wayland/keyboard.rs index 3190b870..abc3069c 100644 --- a/src/api/wayland/keyboard.rs +++ b/src/api/wayland/keyboard.rs @@ -1,83 +1,70 @@ -use std::collections::HashSet; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; -use Event as GlutinEvent; -use ElementState; -use VirtualKeyCode; +use {VirtualKeyCode, ElementState, Event}; -use wayland_client::ProxyId; -use wayland_client::wayland::seat::{WlKeyboardEvent,WlKeyboardKeyState}; +use super::wayland_kbd; +use wayland_client::EventQueueHandle; +use wayland_client::protocol::wl_keyboard; -use super::wayland_kbd::MappedKeyboardEvent; +pub struct KbdHandler { + pub target: Option>>> +} -use super::context::WaylandFocuses; +impl KbdHandler { + pub fn new() -> KbdHandler { + KbdHandler { target: None } + } +} -pub fn translate_kbd_events( - focuses: &mut WaylandFocuses, - known_surfaces: &HashSet, -) -> Vec<(GlutinEvent, ProxyId)> { - let mut out = Vec::new(); - if let Some(mkbd) = focuses.keyboard.as_mut() { - for evt in mkbd { - match evt { - MappedKeyboardEvent::KeyEvent(kevt) => { - if let Some(surface) = focuses.keyboard_on { - let vkcode = match kevt.keycode { - 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), - _ => kevt.as_symbol().and_then(keysym_to_vkey) - }; - let text = kevt.as_utf8(); - out.push(( - GlutinEvent::KeyboardInput( - match kevt.keystate { - WlKeyboardKeyState::Pressed => ElementState::Pressed, - WlKeyboardKeyState::Released =>ElementState::Released - }, - (kevt.keycode & 0xff) as u8, - vkcode - ), - surface - )); - if let Some(c) = text.and_then(|s| s.chars().next()) { - out.push(( - GlutinEvent::ReceivedCharacter(c), - surface - )); - } - } - - } - MappedKeyboardEvent::Other(oevt) => match oevt { - WlKeyboardEvent::Enter(_, surface, _) => { - if known_surfaces.contains(&surface) { - focuses.keyboard_on = Some(surface); - out.push((GlutinEvent::Focused(true), surface)); - } - }, - WlKeyboardEvent::Leave(_, surface) => { - if known_surfaces.contains(&surface) { - focuses.keyboard_on = None; - out.push((GlutinEvent::Focused(false), surface)); - } - } - _ => {} +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)); } } } } - out } -pub fn keysym_to_vkey(keysym: u32) -> Option { +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 @@ -226,4 +213,4 @@ pub fn keysym_to_vkey(keysym: u32) -> Option { // fallback _ => None } -} \ No newline at end of file +} diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs index 77f018e5..cb0c5b5b 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -1,18 +1,12 @@ #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] -pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; pub use self::window::{PollEventsIterator, WaitEventsIterator, Window, WindowProxy}; +pub use self::context::{WaylandContext, MonitorId, get_available_monitors, + get_primary_monitor}; extern crate wayland_kbd; extern crate wayland_window; mod context; -mod events; mod keyboard; -mod monitor; mod window; - -#[inline] -pub fn is_available() -> bool { - context::WAYLAND_CONTEXT.is_some() -} diff --git a/src/api/wayland/monitor.rs b/src/api/wayland/monitor.rs deleted file mode 100644 index d87d4b67..00000000 --- a/src/api/wayland/monitor.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::collections::VecDeque; - -use wayland_client::{ProxyId, EventIterator}; -use wayland_client::wayland::output::WlOutput; - -use super::context::WAYLAND_CONTEXT; - -#[derive(Clone)] -pub struct MonitorId(ProxyId); - -#[inline] -pub fn get_available_monitors() -> VecDeque { - WAYLAND_CONTEXT.as_ref().map(|ctxt| - ctxt.monitor_ids().into_iter().map(MonitorId).collect() - ).unwrap_or(VecDeque::new()) -} -#[inline] -pub fn get_primary_monitor() -> MonitorId { - WAYLAND_CONTEXT.as_ref().and_then(|ctxt| - ctxt.monitor_ids().into_iter().next().map(MonitorId) - ).expect("wayland: No monitor available.") -} - -impl MonitorId { - pub fn get_name(&self) -> Option { - WAYLAND_CONTEXT.as_ref().and_then(|ctxt| ctxt.monitor_name(self.0)) - } - - #[inline] - pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId { - ::native_monitor::NativeMonitorId::Unavailable - } - - pub fn get_dimensions(&self) -> (u32, u32) { - WAYLAND_CONTEXT.as_ref().and_then(|ctxt| ctxt.monitor_dimensions(self.0)).unwrap() - } -} - -pub fn proxid_from_monitorid(x: &MonitorId) -> ProxyId { - x.0 -} - -pub fn init_monitors(outputs: &mut Vec<(WlOutput, u32, u32, String)>, evts: EventIterator) { - use wayland_client::{Event, Proxy}; - use wayland_client::wayland::WaylandProtocolEvent; - use wayland_client::wayland::output::{WlOutputEvent, WlOutputMode}; - - for evt in evts { - match evt { - Event::Wayland(WaylandProtocolEvent::WlOutput(pid, oevt)) => match oevt { - WlOutputEvent::Geometry(_, _, _, _, _, maker, model, _) => { - for o in outputs.iter_mut() { - if o.0.id() == pid { - o.3 = format!("{} - {}", maker, model); - break - } - } - }, - WlOutputEvent::Mode(flags, width, height, _) => { - if flags.contains(WlOutputMode::Current) { - for o in outputs.iter_mut() { - if o.0.id() == pid { - o.1 = width as u32; - o.2 = height as u32; - break - } - } - } - }, - _ => {} - }, - _ => {} - } - } -} \ No newline at end of file diff --git a/src/api/wayland/window.rs b/src/api/wayland/window.rs index 38f89bfe..f08e17cd 100644 --- a/src/api/wayland/window.rs +++ b/src/api/wayland/window.rs @@ -1,83 +1,39 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; -use libc; +use wayland_client::{EventQueue, EventQueueHandle, Init}; +use wayland_client::protocol::{wl_display,wl_surface,wl_shell_surface}; -use {CreationError, CursorState, Event, MouseCursor, WindowAttributes}; +use {CreationError, MouseCursor, CursorState, Event, WindowAttributes}; use platform::MonitorId as PlatformMonitorId; -use wayland_client::EventIterator; -use wayland_client::wayland::compositor::WlSurface; -use wayland_client::wayland::shell::WlShellSurface; -use super::wayland_window::{DecoratedSurface, add_borders, substract_borders}; -use super::context::{WaylandContext, WAYLAND_CONTEXT}; +use super::WaylandContext; +use super::wayland_window; +use super::wayland_window::DecoratedSurface; #[derive(Clone)] -pub struct WindowProxy; +pub struct WindowProxy { + ctxt: Arc, + eviter: Arc>>, +} impl WindowProxy { #[inline] pub fn wakeup_event_loop(&self) { - unimplemented!() + // Send a sync event, so that any waiting "dispatch" will return + self.ctxt.display.sync(); + self.eviter.lock().unwrap().push_back(Event::Awakened); } } pub struct Window { - wayland_context: &'static WaylandContext, - surface: WlSurface, - shell_window: Mutex, - evt_queue: Arc>>, - inner_size: Mutex<(i32, i32)>, - resize_callback: Option, -} - -impl Window { - fn next_event(&self) -> Option { - use wayland_client::Event as WEvent; - use wayland_client::wayland::WaylandProtocolEvent; - use wayland_client::wayland::shell::WlShellSurfaceEvent; - - let mut newsize = None; - let mut evt_queue_guard = self.evt_queue.lock().unwrap(); - - let mut shell_window_guard = self.shell_window.lock().unwrap(); - match *shell_window_guard { - ShellWindow::Decorated(ref mut deco) => { - for (_, w, h) in deco { - newsize = Some((w, h)); - } - }, - ShellWindow::Plain(ref plain, ref mut evtiter) => { - for evt in evtiter { - if let WEvent::Wayland(WaylandProtocolEvent::WlShellSurface(_, ssevt)) = evt { - match ssevt { - WlShellSurfaceEvent::Ping(u) => { - plain.pong(u); - }, - WlShellSurfaceEvent::Configure(_, w, h) => { - newsize = Some((w, h)); - }, - _ => {} - } - } - } - } - } - - if let Some((w, h)) = newsize { - let (w, h) = substract_borders(w, h); - *self.inner_size.lock().unwrap() = (w, h); - if let ShellWindow::Decorated(ref mut deco) = *shell_window_guard { - deco.resize(w, h); - } - if let Some(f) = self.resize_callback { - f(w as u32, h as u32); - } - Some(Event::Resized(w as u32, h as u32)) - } else { - evt_queue_guard.pop_front() - } - } + ctxt: Arc, + evq: Mutex, + eviter: Arc>>, + surface: Arc, + size: Mutex<(u32, u32)>, + handler_id: usize, + decorated_id: usize } pub struct PollEventsIterator<'a> { @@ -88,13 +44,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - match self.window.next_event() { - Some(evt) => return Some(evt), - None => {} - } - // the queue was empty, try a dispatch and see the result - self.window.wayland_context.dispatch_events(); - return self.window.next_event(); + self.window.next_event(false) } } @@ -106,93 +56,130 @@ impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - loop { - match self.window.next_event() { - Some(evt) => return Some(evt), - None => {} - } - // the queue was empty, try a dispatch & read and see the result - self.window.wayland_context.flush_events().expect("Connexion with the wayland compositor lost."); - match self.window.wayland_context.read_events() { - Ok(_) => { - // events were read or dispatch is needed, in both cases, we dispatch - self.window.wayland_context.dispatch_events() - } - Err(_) => panic!("Connexion with the wayland compositor lost.") - } - } + self.window.next_event(true) } } -enum ShellWindow { - Plain(WlShellSurface, EventIterator), - Decorated(DecoratedSurface) -} - impl Window { - pub fn new(window: &WindowAttributes) -> Result + pub fn new(ctxt: Arc, attributes: &WindowAttributes) -> Result { - use wayland_client::Proxy; - // not implemented - assert!(window.min_dimensions.is_none()); - assert!(window.max_dimensions.is_none()); + let (width, height) = attributes.dimensions.unwrap_or((800,600)); - let wayland_context = match *WAYLAND_CONTEXT { - Some(ref c) => c, - None => return Err(CreationError::NotSupported), - }; + let mut evq = ctxt.display.create_event_queue(); - let (w, h) = window.dimensions.unwrap_or((800, 600)); + let (surface, eviter, decorated) = ctxt.create_window::(); - let (surface, evt_queue) = match wayland_context.new_surface() { - Some(t) => t, - None => return Err(CreationError::NotSupported) - }; + // init DecoratedSurface + let decorated_id = evq.add_handler_with_init(decorated); + { + let mut state = evq.state(); + let decorated = state.get_mut_handler::>(decorated_id); + *(decorated.handler()) = Some(DecoratedHandler::new()); - let shell_window = if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = window.monitor { - let pid = super::monitor::proxid_from_monitorid(monitor_id); - match wayland_context.plain_from(&surface, Some(pid)) { - Some(mut s) => { - let iter = EventIterator::new(); - s.set_evt_iterator(&iter); - ShellWindow::Plain(s, iter) - }, - None => return Err(CreationError::NotSupported) + if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = attributes.monitor { + ctxt.with_output(monitor_id.clone(), |output| { + decorated.set_fullscreen( + wl_shell_surface::FullscreenMethod::Default, + 0, + Some(output) + ) + }); + } else if attributes.decorations { + decorated.set_decorate(true); } - } else if window.decorations { - match wayland_context.decorated_from(&surface, w as i32, h as i32) { - Some(s) => ShellWindow::Decorated(s), - None => return Err(CreationError::NotSupported) - } - } else { - match wayland_context.plain_from(&surface, None) { - Some(mut s) => { - let iter = EventIterator::new(); - s.set_evt_iterator(&iter); - ShellWindow::Plain(s, iter) - }, - None => return Err(CreationError::NotSupported) - } - }; + } + + // init general handler + let handler = WindowHandler::new(); + let handler_id = evq.add_handler_with_init(handler); Ok(Window { - wayland_context: wayland_context, + ctxt: ctxt, + evq: Mutex::new(evq), + eviter: eviter, surface: surface, - shell_window: Mutex::new(shell_window), - evt_queue: evt_queue, - inner_size: Mutex::new((w as i32, h as i32)), - resize_callback: None, + size: Mutex::new((width, height)), + handler_id: handler_id, + decorated_id: decorated_id }) } - pub fn set_title(&self, title: &str) { - let guard = self.shell_window.lock().unwrap(); - match *guard { - ShellWindow::Plain(ref plain, _) => { plain.set_title(title.into()); }, - ShellWindow::Decorated(ref deco) => { deco.set_title(title.into()); } + fn process_resize(&self) { + use std::cmp::max; + let mut evq_guard = self.evq.lock().unwrap(); + let mut state = evq_guard.state(); + let newsize = { + let decorated = state.get_mut_handler::>(self.decorated_id); + let newsize = decorated.handler().as_mut().and_then(|h| h.take_newsize()); + if let Some((w, h)) = newsize { + decorated.resize(w as i32, h as i32); + *self.size.lock().unwrap() = (w, h); + } + newsize + }; + // callback_resize if any + if let Some((w, h)) = newsize { + let mut handler = state.get_mut_handler::(self.handler_id); + if let Some(ref callback) = handler.resize_callback { + callback(w, h); + } + self.eviter.lock().unwrap().push_back(Event::Resized(w,h)); } } + fn next_event(&self, block: bool) -> Option { + let mut evt = { + let mut guard = self.eviter.lock().unwrap(); + guard.pop_front() + }; + if evt.is_some() { return evt } + + // There is no event in the queue, we need to fetch more + + // flush the display + self.ctxt.flush(); + + // read some events if some are waiting & queue is empty + if let Some(guard) = self.evq.lock().unwrap().prepare_read() { + guard.read_events(); + } + + // try a pending dispatch + { + self.ctxt.dispatch_pending(); + self.evq.lock().unwrap().dispatch_pending(); + // some events were dispatched, need to process a potential resising + self.process_resize(); + } + + let mut evt = { + let mut guard = self.eviter.lock().unwrap(); + guard.pop_front() + }; + + while block && evt.is_none() { + // no event waiting, need to repopulate! + { + self.ctxt.flush(); + self.ctxt.dispatch(); + self.evq.lock().unwrap().dispatch_pending(); + // some events were dispatched, need to process a potential resising + self.process_resize(); + } + // try again + let mut guard = self.eviter.lock().unwrap(); + evt = guard.pop_front(); + } + evt + } + + pub fn set_title(&self, title: &str) { + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let mut decorated = state.get_mut_handler::>(self.decorated_id); + decorated.set_title(title.into()) + } + #[inline] pub fn show(&self) { // TODO @@ -215,29 +202,31 @@ impl Window { } pub fn get_inner_size(&self) -> Option<(u32, u32)> { - let (w, h) = *self.inner_size.lock().unwrap(); - Some((w as u32, h as u32)) + Some(self.size.lock().unwrap().clone()) } #[inline] pub fn get_outer_size(&self) -> Option<(u32, u32)> { - let (w, h) = *self.inner_size.lock().unwrap(); - let (w, h) = add_borders(w, h); + let (w, h) = self.size.lock().unwrap().clone(); + let (w, h) = super::wayland_window::add_borders(w as i32, h as i32); Some((w as u32, h as u32)) } #[inline] + // NOTE: This will only resize the borders, the contents must be updated by the user pub fn set_inner_size(&self, x: u32, y: u32) { - let mut guard = self.shell_window.lock().unwrap(); - match *guard { - ShellWindow::Decorated(ref mut deco) => { deco.resize(x as i32, y as i32); }, - _ => {} - } + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let mut decorated = state.get_mut_handler::>(self.decorated_id); + decorated.resize(x as i32, y as i32); } #[inline] pub fn create_window_proxy(&self) -> WindowProxy { - WindowProxy + WindowProxy { + ctxt: self.ctxt.clone(), + eviter: self.eviter.clone() + } } #[inline] @@ -256,7 +245,10 @@ impl Window { #[inline] pub fn set_window_resize_callback(&mut self, callback: Option) { - self.resize_callback = callback; + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let mut handler = state.get_mut_handler::(self.handler_id); + handler.resize_callback = callback; } #[inline] @@ -277,6 +269,7 @@ impl Window { #[inline] pub fn hidpi_factor(&self) -> f32 { + // TODO 1.0 } @@ -285,35 +278,61 @@ impl Window { // TODO: not yet possible on wayland Err(()) } - - #[inline] - pub fn get_wayland_display(&self) -> *mut libc::c_void { - WAYLAND_CONTEXT.as_ref().unwrap() // context exists if window was created - .display_ptr() as *mut libc::c_void + + pub fn get_display(&self) -> &wl_display::WlDisplay { + &self.ctxt.display } - - #[inline] - pub fn get_wayland_surface(&self) -> *mut libc::c_void { - use wayland_client::Proxy; - self.surface.ptr() as *mut libc::c_void - } - - #[inline] - pub fn platform_display(&self) -> *mut libc::c_void { - WAYLAND_CONTEXT.as_ref().unwrap() // context exists if window was created - .display_ptr() as *mut libc::c_void - } - - #[inline] - pub fn platform_window(&self) -> *mut libc::c_void { - use wayland_client::Proxy; - self.surface.ptr() as *mut libc::c_void + + pub fn get_surface(&self) -> &wl_surface::WlSurface { + &self.surface } } impl Drop for Window { fn drop(&mut self) { - use wayland_client::Proxy; - self.wayland_context.dropped_surface(self.surface.id()); + self.surface.destroy(); + self.ctxt.prune_dead_windows(); } -} \ No newline at end of file +} + +struct DecoratedHandler { + newsize: Option<(u32, u32)> +} + +impl DecoratedHandler { + fn new() -> DecoratedHandler { DecoratedHandler { newsize: None }} + fn take_newsize(&mut self) -> Option<(u32, u32)> { + self.newsize.take() + } +} + +impl wayland_window::Handler for DecoratedHandler { + fn configure(&mut self, + _: &mut EventQueueHandle, + _: wl_shell_surface::Resize, + width: i32, height: i32) + { + use std::cmp::max; + self.newsize = Some((max(width,1) as u32, max(height,1) as u32)); + } +} + +struct WindowHandler { + my_id: usize, + resize_callback: Option, +} + +impl WindowHandler { + fn new() -> WindowHandler { + WindowHandler { + my_id: 0, + resize_callback: None + } + } +} + +impl Init for WindowHandler { + fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) { + self.my_id = index; + } +} diff --git a/src/lib.rs b/src/lib.rs index e37b9bf4..1f1c626b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ extern crate core_graphics; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] extern crate x11_dl; #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] -#[macro_use(wayland_env)] +#[macro_use(wayland_env,declare_handler)] extern crate wayland_client; pub use events::*; diff --git a/src/os/unix.rs b/src/os/unix.rs index c4dfac90..e6229714 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -5,6 +5,9 @@ use Window; use platform::Window as LinuxWindow; use WindowBuilder; +use wayland_client::protocol::wl_display::WlDisplay; +use wayland_client::protocol::wl_surface::WlSurface; + /// Additional methods on `Window` that are specific to Unix. pub trait WindowExt { /// Returns a pointer to the `Window` object of xlib that is used by this window. @@ -42,6 +45,24 @@ pub trait WindowExt { /// /// The pointer will become invalid when the glutin `Window` is destroyed. fn get_wayland_display(&self) -> Option<*mut libc::c_void>; + + /// Returns a reference to the `WlSurface` object of wayland that is used by this window. + /// + /// For use with the `wayland-client` crate. + /// + /// **This function is not part of winit's public API.** + /// + /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). + fn get_wayland_client_surface(&self) -> Option<&WlSurface>; + + /// Returns a pointer to the `WlDisplay` object of wayland that is used by this window. + /// + /// For use with the `wayland-client` crate. + /// + /// **This function is not part of winit's public API.** + /// + /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). + fn get_wayland_client_display(&self) -> Option<&WlDisplay>; } impl WindowExt for Window { @@ -70,16 +91,29 @@ impl WindowExt for Window { #[inline] fn get_wayland_surface(&self) -> Option<*mut libc::c_void> { + use wayland_client::Proxy; + self.get_wayland_client_surface().map(|p| p.ptr() as *mut _) + } + + + #[inline] + fn get_wayland_display(&self) -> Option<*mut libc::c_void> { + use wayland_client::Proxy; + self.get_wayland_client_display().map(|p| p.ptr() as *mut _) + } + + #[inline] + fn get_wayland_client_surface(&self) -> Option<&WlSurface> { match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_wayland_surface()), + LinuxWindow::Wayland(ref w) => Some(w.get_surface()), _ => None } } #[inline] - fn get_wayland_display(&self) -> Option<*mut libc::c_void> { + fn get_wayland_client_display(&self) -> Option<&WlDisplay> { match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_wayland_display()), + LinuxWindow::Wayland(ref w) => Some(w.get_display()), _ => None } } diff --git a/src/platform/linux/api_dispatch.rs b/src/platform/linux/api_dispatch.rs index 975cadaf..1609a1aa 100644 --- a/src/platform/linux/api_dispatch.rs +++ b/src/platform/linux/api_dispatch.rs @@ -1,6 +1,3 @@ -/*pub use api::x11::{Window, WindowProxy, MonitorId, get_available_monitors, get_primary_monitor}; -pub use api::x11::{WaitEventsIterator, PollEventsIterator};*/ - use std::collections::VecDeque; use std::sync::Arc; @@ -22,15 +19,15 @@ pub struct PlatformSpecificWindowBuilderAttributes; enum Backend { X(Arc), - Wayland, + Wayland(Arc), Error(XNotSupported), } lazy_static!( static ref BACKEND: Backend = { // Wayland backend is not production-ready yet so we disable it - if wayland::is_available() { - Backend::Wayland + if let Some(ctxt) = wayland::WaylandContext::init() { + Backend::Wayland(Arc::new(ctxt)) } else { match XConnection::new(Some(x_error_callback)) { Ok(x) => Backend::X(Arc::new(x)), @@ -78,7 +75,7 @@ pub enum MonitorId { #[inline] pub fn get_available_monitors() -> VecDeque { match *BACKEND { - Backend::Wayland => wayland::get_available_monitors() + Backend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt) .into_iter() .map(MonitorId::Wayland) .collect(), @@ -93,7 +90,7 @@ pub fn get_available_monitors() -> VecDeque { #[inline] pub fn get_primary_monitor() -> MonitorId { match *BACKEND { - Backend::Wayland => MonitorId::Wayland(wayland::get_primary_monitor()), + Backend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)), Backend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)), Backend::Error(_) => MonitorId::None, } @@ -173,8 +170,8 @@ impl Window { -> Result { match *BACKEND { - Backend::Wayland => { - wayland::Window::new(window).map(Window::Wayland) + Backend::Wayland(ref ctxt) => { + wayland::Window::new(ctxt.clone(), window).map(Window::Wayland) }, Backend::X(ref connec) => { @@ -318,17 +315,19 @@ impl Window { #[inline] pub fn platform_display(&self) -> *mut libc::c_void { + use wayland_client::Proxy; match self { &Window::X(ref w) => w.platform_display(), - &Window::Wayland(ref w) => w.platform_display() + &Window::Wayland(ref w) => w.get_display().ptr() as *mut _ } } #[inline] pub fn platform_window(&self) -> *mut libc::c_void { + use wayland_client::Proxy; match self { &Window::X(ref w) => w.platform_window(), - &Window::Wayland(ref w) => w.platform_window() + &Window::Wayland(ref w) => w.get_surface().ptr() as *mut _ } } }