Rewrite of wayland backend to new wayland-client API (#325)

* wayland: clean state for rewrite to new wayland-client API

* wayland: context init

* wayland: Monitors logic

* wayland: Basic event loop logic

* wayland: Keyboard handling

* wayland: pointer handling

* wayland: refactor to remove WaylandContext

* wayland: window logic

* wayland: event dispatching logic

* wayland: update changelog
This commit is contained in:
Victor Berger 2017-10-20 09:46:42 +02:00 committed by tomaka
parent 4e4db1749d
commit d10312c6b1
8 changed files with 953 additions and 1158 deletions

View file

@ -1,97 +1,86 @@
use std::sync::{Arc, Mutex};
use std::sync::atomic::{Ordering, AtomicBool};
use std::cmp;
use std::sync::{Arc, Mutex, Weak};
use wayland_client::{EventQueueHandle, Proxy};
use wayland_client::protocol::{wl_display,wl_surface};
use wayland_client::{Proxy, StateToken};
use {CreationError, MouseCursor, CursorState, WindowAttributes};
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
use platform::wayland::MonitorId as WaylandMonitorId;
use platform::wayland::context::get_available_monitors;
use super::{WaylandContext, EventsLoop};
use super::wayland_window;
use super::wayland_window::DecoratedSurface;
use super::{EventsLoop, WindowId, make_wid, MonitorId};
use super::wayland_window::{DecoratedSurface, DecoratedSurfaceImplementation};
use super::event_loop::StateContext;
pub struct Window {
// the global wayland context
ctxt: Arc<WaylandContext>,
// signal to advertize the EventsLoop when we are destroyed
cleanup_signal: Arc<AtomicBool>,
// our wayland surface
surface: Arc<wl_surface::WlSurface>,
// our current inner dimensions
size: Mutex<(u32, u32)>,
// the id of our DecoratedHandler in the EventQueue
decorated_id: usize
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
#[inline]
pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
WindowId(s.ptr() as usize)
display: Arc<wl_display::WlDisplay>,
surface: wl_surface::WlSurface,
decorated: Arc<Mutex<DecoratedSurface>>,
monitors: Arc<Mutex<MonitorList>>,
ready: Arc<Mutex<bool>>,
size: Arc<Mutex<(u32, u32)>>,
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
}
impl Window {
pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result<Window, CreationError>
{
let ctxt = evlp.context().clone();
let (width, height) = attributes.dimensions.unwrap_or((800,600));
let (surface, decorated, xdg) = ctxt.create_window::<DecoratedHandler>(width, height, attributes.decorations);
// init DecoratedSurface
let cleanup_signal = evlp.get_window_init();
let mut fullscreen_monitor = None;
// Create the decorated surface
let ready = Arc::new(Mutex::new(false));
let size = Arc::new(Mutex::new((width, height)));
let store_token = evlp.store.clone();
let (surface, mut decorated, xdg) = evlp.create_window(
width, height, attributes.decorations, decorated_impl(),
|surface| DecoratedIData {
ready: ready.clone(),
surface: surface.clone().unwrap(),
store_token: store_token.clone()
}
);
// If we are using xdg, we are not ready yet
{ *ready.lock().unwrap() = !xdg; }
// Check for fullscreen requirements
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen {
ctxt.with_output(monitor_id.clone(), |output| {
fullscreen_monitor = output.clone();
let info = monitor_id.info.lock().unwrap();
decorated.set_fullscreen(Some(&info.output));
}
// setup the monitor tracking
let monitor_list = Arc::new(Mutex::new(MonitorList::default()));
{
let mut evq = evlp.evq.borrow_mut();
let idata = (evlp.ctxt_token.clone(), monitor_list.clone());
evq.register(&surface, surface_impl(), idata);
}
// a surface commit with no buffer so that the compositor don't
// forget to configure us
surface.commit();
let kill_switch = Arc::new(Mutex::new(false));
let decorated = Arc::new(Mutex::new(decorated));
{
let mut evq = evlp.evq.borrow_mut();
evq.state().get_mut(&store_token).windows.push(InternalWindow {
closed: false,
newsize: None,
need_refresh: false,
surface: surface.clone().unwrap(),
kill_switch: kill_switch.clone(),
decorated: Arc::downgrade(&decorated)
});
evq.sync_roundtrip().unwrap();
}
let decorated_id = {
let mut evq_guard = ctxt.evq.lock().unwrap();
// store the DecoratedSurface handler
let decorated_id = evq_guard.add_handler_with_init(decorated);
{
let mut state = evq_guard.state();
// initialize the DecoratedHandler
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
*(decorated.handler()) = Some(DecoratedHandler::new(!xdg));
// set fullscreen if necessary
if let Some(output) = fullscreen_monitor {
decorated.set_fullscreen(Some(&output));
}
// Finally, set the decorations size
decorated.resize(width as i32, height as i32);
}
evq_guard.sync_roundtrip().unwrap();
decorated_id
};
// send our configuration to the compositor
// if we're in xdg mode, no buffer is attached yet, so this
// is fine (and more or less required actually)
surface.commit();
let me = Window {
ctxt: ctxt,
cleanup_signal: cleanup_signal,
Ok(Window {
display: evlp.display.clone(),
surface: surface,
size: Mutex::new((width, height)),
decorated_id: decorated_id
};
// register ourselves to the EventsLoop
evlp.register_window(me.decorated_id, me.surface.clone());
Ok(me)
decorated: decorated,
monitors: monitor_list,
ready: ready,
size: size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone())
})
}
#[inline]
@ -100,10 +89,7 @@ impl Window {
}
pub fn set_title(&self, title: &str) {
let mut guard = self.ctxt.evq.lock().unwrap();
let mut state = guard.state();
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.set_title(title.into())
self.decorated.lock().unwrap().set_title(title.into());
}
#[inline]
@ -141,10 +127,7 @@ impl Window {
#[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.ctxt.evq.lock().unwrap();
let mut state = guard.state();
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.resize(x as i32, y as i32);
self.decorated.lock().unwrap().resize(x as i32, y as i32);
*(self.size.lock().unwrap()) = (x, y);
}
@ -166,8 +149,13 @@ impl Window {
#[inline]
pub fn hidpi_factor(&self) -> f32 {
// TODO
1.0
let mut factor = 1.0;
let guard = self.monitors.lock().unwrap();
for monitor_id in &guard.monitors {
let info = monitor_id.info.lock().unwrap();
if info.scale > factor { factor = info.scale; }
}
factor
}
#[inline]
@ -177,109 +165,148 @@ impl Window {
}
pub fn get_display(&self) -> &wl_display::WlDisplay {
&self.ctxt.display
&*self.display
}
pub fn get_surface(&self) -> &wl_surface::WlSurface {
&self.surface
}
pub fn get_current_monitor(&self) -> WaylandMonitorId {
let monitors = get_available_monitors(&self.ctxt);
let default = monitors[0].clone();
let (wx,wy) = match self.get_position() {
Some(val) => (cmp::max(0,val.0) as u32, cmp::max(0,val.1) as u32),
None=> return default,
};
let (ww,wh) = match self.get_outer_size() {
Some(val) => val,
None=> return default,
};
// Opposite corner coordinates
let (wxo, wyo) = (wx+ww-1, wy+wh-1);
// Find the monitor with the biggest overlap with the window
let mut overlap = 0;
let mut find = default;
for monitor in monitors {
let (mx, my) = monitor.get_position();
let mx = mx as u32;
let my = my as u32;
let (mw, mh) = monitor.get_dimensions();
let (mxo, myo) = (mx+mw-1, my+mh-1);
let (ox, oy) = (cmp::max(wx, mx), cmp::max(wy, my));
let (oxo, oyo) = (cmp::min(wxo, mxo), cmp::min(wyo, myo));
let osize = if ox <= oxo || oy <= oyo { 0 } else { (oxo-ox)*(oyo-oy) };
if osize > overlap {
overlap = osize;
find = monitor;
}
}
find
pub fn get_current_monitor(&self) -> MonitorId {
// we don't know how much each monitor sees us so...
// just return the most recent one ?
let guard = self.monitors.lock().unwrap();
guard.monitors.last().unwrap().clone()
}
pub fn is_ready(&self) -> bool {
let mut guard = self.ctxt.evq.lock().unwrap();
let mut state = guard.state();
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.handler().as_ref().unwrap().configured
*self.ready.lock().unwrap()
}
}
impl Drop for Window {
fn drop(&mut self) {
self.surface.destroy();
self.cleanup_signal.store(true, Ordering::Relaxed);
*(self.kill_switch.0.lock().unwrap()) = true;
*(self.kill_switch.1.lock().unwrap()) = true;
}
}
pub struct DecoratedHandler {
newsize: Option<(u32, u32)>,
refresh: bool,
/*
* Internal store for windows
*/
struct InternalWindow {
surface: wl_surface::WlSurface,
newsize: Option<(i32, i32)>,
need_refresh: bool,
closed: bool,
configured: bool
kill_switch: Arc<Mutex<bool>>,
decorated: Weak<Mutex<DecoratedSurface>>
}
impl DecoratedHandler {
fn new(configured: bool) -> DecoratedHandler {
DecoratedHandler {
newsize: None,
refresh: false,
closed: false,
configured: configured
pub struct WindowStore {
windows: Vec<InternalWindow>
}
impl WindowStore {
pub fn new() -> WindowStore {
WindowStore { windows: Vec::new() }
}
pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option<WindowId> {
for window in &self.windows {
if surface.equals(&window.surface) {
return Some(make_wid(surface));
}
}
None
}
pub fn cleanup(&mut self) {
self.windows.retain(|w| {
if *w.kill_switch.lock().unwrap() {
// window is dead, cleanup
w.surface.destroy();
false
} else {
true
}
});
}
pub fn for_each<F>(&mut self, mut f: F)
where F: FnMut(Option<(i32, i32)>, bool, bool, WindowId, Option<&mut DecoratedSurface>)
{
for window in &mut self.windows {
let opt_arc = window.decorated.upgrade();
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
f(
window.newsize.take(),
window.need_refresh,
window.closed,
make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m)
);
window.need_refresh = false;
// avoid re-spamming the event
window.closed = false;
}
}
pub fn take_newsize(&mut self) -> Option<(u32, u32)> {
self.newsize.take()
}
pub fn take_refresh(&mut self) -> bool {
let refresh = self.refresh;
self.refresh = false;
refresh
}
pub fn is_closed(&self) -> bool { self.closed }
}
impl wayland_window::Handler for DecoratedHandler {
fn configure(&mut self,
_: &mut EventQueueHandle,
_cfg: wayland_window::Configure,
newsize: Option<(i32, i32)>)
{
self.newsize = newsize.map(|(w, h)| (w as u32, h as u32));
self.refresh = true;
self.configured = true;
}
/*
* Protocol implementation
*/
fn close(&mut self, _: &mut EventQueueHandle) {
self.closed = true;
struct DecoratedIData {
ready: Arc<Mutex<bool>>,
store_token: StateToken<WindowStore>,
surface: wl_surface::WlSurface
}
fn decorated_impl() -> DecoratedSurfaceImplementation<DecoratedIData> {
DecoratedSurfaceImplementation {
configure: |evqh, idata, _, newsize| {
*idata.ready.lock().unwrap() = true;
let store = evqh.state().get_mut(&idata.store_token);
for window in &mut store.windows {
if window.surface.equals(&idata.surface) {
window.newsize = newsize;
window.need_refresh = true;
return;
}
}
},
close: |evqh, idata| {
let store = evqh.state().get_mut(&idata.store_token);
for window in &mut store.windows {
if window.surface.equals(&idata.surface) {
window.closed = true;
return;
}
}
}
}
}
#[derive(Default)]
struct MonitorList {
monitors: Vec<MonitorId>
}
fn surface_impl() -> wl_surface::Implementation<(StateToken<StateContext>, Arc<Mutex<MonitorList>>)> {
wl_surface::Implementation {
enter: |evqh, &mut (ref token, ref list), _, output| {
let mut guard = list.lock().unwrap();
let ctxt = evqh.state().get(token);
let monitor = ctxt.monitor_id_for(output);
guard.monitors.push(monitor);
},
leave: |evqh, &mut (ref token, ref list), _, output| {
let mut guard = list.lock().unwrap();
let ctxt = evqh.state().get(token);
let monitor = ctxt.monitor_id_for(output);
guard.monitors.retain(|m| !Arc::ptr_eq(&m.info, &monitor.info));
}
}
}