From e5ab8e55cf2636da55c9ed03863e02a9a3fe3b91 Mon Sep 17 00:00:00 2001 From: kosyak Date: Fri, 17 May 2019 21:16:23 +0000 Subject: [PATCH] Hacky Gbm backend --- .gitignore | 2 + Cargo.toml | 2 + src/lib.rs | 4 + src/os/unix.rs | 60 +++- src/platform/linux/gbm/card.rs | 80 +++++ src/platform/linux/gbm/event_loop.rs | 348 ++++++++++++++++++++ src/platform/linux/gbm/mod.rs | 22 ++ src/platform/linux/gbm/window.rs | 453 +++++++++++++++++++++++++++ src/platform/linux/mod.rs | 97 +++++- 9 files changed, 1059 insertions(+), 9 deletions(-) create mode 100644 src/platform/linux/gbm/card.rs create mode 100644 src/platform/linux/gbm/event_loop.rs create mode 100644 src/platform/linux/gbm/mod.rs create mode 100644 src/platform/linux/gbm/window.rs diff --git a/.gitignore b/.gitignore index bf5cea56b6a..b1896b1dd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ rls/ .vscode/ *~ #*# +._.DS_Store +._DS_Store diff --git a/Cargo.toml b/Cargo.toml index 35fde6d689e..2efc1adde3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,3 +68,5 @@ smithay-client-toolkit = "0.4.3" x11-dl = "2.18.3" parking_lot = "0.7" percent-encoding = "1.0" +gbm = { git = "https://github.com/kosyak/gbm.rs", default-features = false, features = ["drm-support", "import-egl", "gen"] } +drm = { git = "https://github.com/kosyak/drm-rs", branch = "master" } diff --git a/src/lib.rs b/src/lib.rs index d4b61578110..420d26586be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,10 @@ extern crate parking_lot; extern crate percent_encoding; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate smithay_client_toolkit as sctk; +#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +extern crate drm; +#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +extern crate gbm; pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase. pub use events::*; diff --git a/src/os/unix.rs b/src/os/unix.rs index f5e2f61c95b..da4c8a3730b 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -111,6 +111,9 @@ pub trait EventsLoopExt { /// True if the `EventsLoop` uses X11. fn is_x11(&self) -> bool; + /// True if the `EventsLoop` uses Gbm. + fn is_gbm(&self) -> bool; + #[doc(hidden)] fn get_xlib_xconnection(&self) -> Option>; @@ -120,6 +123,8 @@ pub trait EventsLoopExt { /// /// The pointer will become invalid when the glutin `EventsLoop` is destroyed. fn get_wayland_display(&self) -> Option<*mut raw::c_void>; + + fn get_gbm_display(&self) -> Option<*mut raw::c_void>; } impl EventsLoopExt for EventsLoop { @@ -151,7 +156,12 @@ impl EventsLoopExt for EventsLoop { #[inline] fn is_x11(&self) -> bool { - !self.events_loop.is_wayland() + !self.events_loop.is_wayland() && !self.events_loop.is_gbm() + } + + #[inline] + fn is_gbm(&self) -> bool { + self.events_loop.is_gbm() } #[inline] @@ -167,6 +177,15 @@ impl EventsLoopExt for EventsLoop { _ => None } } + + #[inline] + fn get_gbm_display(&self) -> Option<*mut raw::c_void> { + use gbm::AsRaw; + match self.events_loop { + LinuxEventsLoop::Gbm(ref e) => Some(e.get_display().as_raw() as *mut _), + _ => None + } + } } /// Additional methods on `Window` that are specific to Unix. @@ -205,6 +224,8 @@ pub trait WindowExt { /// The pointer will become invalid when the glutin `Window` is destroyed. fn get_wayland_surface(&self) -> Option<*mut raw::c_void>; + fn get_gbm_surface(&self) -> Option<*mut raw::c_void>; + /// Returns a pointer to the `wl_display` object of wayland that is used by this window. /// /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). @@ -212,6 +233,11 @@ pub trait WindowExt { /// The pointer will become invalid when the glutin `Window` is destroyed. fn get_wayland_display(&self) -> Option<*mut raw::c_void>; + fn get_gbm_display(&self) -> Option<*mut raw::c_void>; + + fn gbm_set_crtc(&self); + fn gbm_page_flip(&self); + /// Sets the color theme of the client side window decorations on wayland fn set_wayland_theme(&self, theme: WaylandTheme); @@ -282,6 +308,15 @@ impl WindowExt for Window { } } + #[inline] + fn get_gbm_surface(&self) -> Option<*mut raw::c_void> { + use gbm::AsRaw; + match self.window { + LinuxWindow::Gbm(ref w) => Some(w.get_surface().as_raw() as *mut _), + _ => None + } + } + #[inline] fn get_wayland_display(&self) -> Option<*mut raw::c_void> { match self.window { @@ -290,6 +325,29 @@ impl WindowExt for Window { } } + #[inline] + fn get_gbm_display(&self) -> Option<*mut raw::c_void> { + use gbm::AsRaw; + match self.window { + LinuxWindow::Gbm(ref w) => Some(w.get_display().as_raw() as *mut _), + _ => None + } + } + + fn gbm_set_crtc(&self) { + match self.window { + LinuxWindow::Gbm(ref w) => w.set_crtc(), + _ => {} + } + } + + fn gbm_page_flip(&self) { + match self.window { + LinuxWindow::Gbm(ref w) => w.page_flip(), + _ => {} + } + } + #[inline] fn set_wayland_theme(&self, theme: WaylandTheme) { match self.window { diff --git a/src/platform/linux/gbm/card.rs b/src/platform/linux/gbm/card.rs new file mode 100644 index 00000000000..2947dfbf5c1 --- /dev/null +++ b/src/platform/linux/gbm/card.rs @@ -0,0 +1,80 @@ +use drm::control::{Device as ControlDevice, connector::Info as ConnectorInfo, Mode, ResourceInfo, + encoder::Info as EncoderInfo, crtc::Info as CrtcInfo}; + +use std::fs::{File, OpenOptions}; +use std::os::unix::io::{AsRawFd, RawFd}; + +#[derive(Debug)] +// This is our customized struct that implements the traits in drm. +pub struct Card(File); + +// Need to implement AsRawFd before we can implement drm::Device +impl AsRawFd for Card { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl drm::Device for Card {} +impl ControlDevice for Card {} + +impl Card { + pub fn open(path: &str) -> Self { + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Card(options.open(path).unwrap()) + } + + pub fn open_global() -> Self { + Self::open("/dev/dri/card0") + } + + // fn open_control() -> Self { + // Self::open("/dev/dri/controlD64") + // } + + pub fn try_clone(&self) -> std::io::Result { + let file = self.0.try_clone()?; + Ok(Card(file)) + } + + pub fn get_resources(&self) -> (ConnectorInfo, Mode, EncoderInfo, CrtcInfo) { + let resources = self.resource_handles().unwrap(); + + let connector = resources.connectors().iter().find_map(|&c| { + if let Ok(c) = ConnectorInfo::load_from_device(self, c) { + if c.connection_state() == drm::control::connector::State::Connected && + c.size().0 > 0 && c.size().1 > 0 + { + return Some(c); + } + } + + None + }).unwrap(); + + let connector_clone = connector.clone(); + let modes = connector_clone.modes(); + let modes = &mut modes.to_owned(); + modes.sort_by(|a, b| { + /*if a.is_preferred() != b.is_preferred() { + a.is_preferred().cmp(&b.is_preferred()).reverse() + } else*/ if a.size().0 as u32 * a.size().1 as u32 != b.size().0 as u32 * b.size().1 as u32 { + (a.size().0 as u32 * a.size().1 as u32).cmp(&(b.size().0 as u32 * b.size().1 as u32)).reverse() + } else { + a.vrefresh().cmp(&b.vrefresh()).reverse() + } + }); + + let mode = modes.iter().next().unwrap(); + + let encoder = + EncoderInfo::load_from_device(self, connector.current_encoder().unwrap()).unwrap(); + + let ctrc_handle = encoder.current_crtc().unwrap(); + let crtc = CrtcInfo::load_from_device(self, ctrc_handle).unwrap(); + + (connector, *mode, encoder, crtc) + } +} diff --git a/src/platform/linux/gbm/event_loop.rs b/src/platform/linux/gbm/event_loop.rs new file mode 100644 index 00000000000..f3441681bd1 --- /dev/null +++ b/src/platform/linux/gbm/event_loop.rs @@ -0,0 +1,348 @@ +use std::collections::VecDeque; +use std::fmt; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, Weak}; +use std::io::{Error as IoError}; + +use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize}; + +use super::window::WindowStore; +use super::WindowId; + +use super::card::Card; + +use drm::control::{Device, ResourceInfo}; +use gbm::{Device as GbmDevice}; + +pub struct EventsLoopSink { + buffer: VecDeque<::Event>, +} + +impl EventsLoopSink { + pub fn new() -> EventsLoopSink { + EventsLoopSink { + buffer: VecDeque::new(), + } + } + + pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) { + let evt = ::Event::WindowEvent { + event: evt, + window_id: ::WindowId(::platform::WindowId::Gbm(wid)), + }; + self.buffer.push_back(evt); + } + + pub fn send_raw_event(&mut self, evt: ::Event) { + self.buffer.push_back(evt); + } + + fn empty_with(&mut self, callback: &mut F) + where + F: FnMut(::Event), + { + for evt in self.buffer.drain(..) { + callback(evt) + } + } +} + +pub struct EventsLoop { + // The Event Queue + // pub evq: RefCell, + // our sink, shared with some handlers, buffering the events + sink: Arc>, + // Whether or not there is a pending `Awakened` event to be emitted. + pending_wakeup: Arc, + // The window store + pub store: Arc>, + // the env + // pub env: Environment, + // a cleanup switch to prune dead windows + pub cleanup_needed: Arc>, + // The wayland display + // pub display: Arc, + pub display: Arc>, + // The list of seats + // pub seats: Arc)>>>, +} + +// A handle that can be sent across threads and used to wake up the `EventsLoop`. +// +// We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs. +#[derive(Clone)] +pub struct EventsLoopProxy { + // display: Weak, + pending_wakeup: Weak, +} + +impl EventsLoopProxy { + // Causes the `EventsLoop` to stop blocking on `run_forever` and emit an `Awakened` event. + // + // Returns `Err` if the associated `EventsLoop` no longer exists. + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + unimplemented!() + // let display = self.display.upgrade(); + // let wakeup = self.pending_wakeup.upgrade(); + // match (display, wakeup) { + // (Some(display), Some(wakeup)) => { + // // Update the `EventsLoop`'s `pending_wakeup` flag. + // wakeup.store(true, Ordering::Relaxed); + // // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. + // let _ = display.sync(|callback| callback.implement(|_, _| {}, ())); + // display.flush().map_err(|_| EventsLoopClosed)?; + // Ok(()) + // } + // _ => Err(EventsLoopClosed), + // } + } +} + +impl EventsLoop { + pub fn new() -> Result { + let display = GbmDevice::new(Card::open_global()).unwrap(); + + let pending_wakeup = Arc::new(AtomicBool::new(false)); + let sink = Arc::new(Mutex::new(EventsLoopSink::new())); + let store = Arc::new(Mutex::new(WindowStore::new())); + + Ok(EventsLoop { + sink, + pending_wakeup, + store, + cleanup_needed: Arc::new(Mutex::new(false)), + display: Arc::new(display), + }) + } + + pub fn create_proxy(&self) -> EventsLoopProxy { + EventsLoopProxy { + // display: Arc::downgrade(&self.display), + pending_wakeup: Arc::downgrade(&self.pending_wakeup), + } + } + + pub fn poll_events(&mut self, mut callback: F) + where + F: FnMut(::Event), + { + // // send pending events to the server + // self.display.flush().expect("Wayland connection lost."); + + // dispatch any pre-buffered events + self.sink.lock().unwrap().empty_with(&mut callback); + + // // try to read pending events + // if let Some(h) = self.evq.get_mut().prepare_read() { + // h.read_events().expect("Wayland connection lost."); + // } + // // dispatch wayland events + // self.evq + // .get_mut() + // .dispatch_pending() + // .expect("Wayland connection lost."); + self.post_dispatch_triggers(); + + // dispatch buffered events to client + self.sink.lock().unwrap().empty_with(&mut callback); + } + + pub fn run_forever(&mut self, mut callback: F) + where + F: FnMut(::Event) -> ControlFlow, + { + // send pending events to the server +// self.display.flush().expect("Wayland connection lost."); + + // Check for control flow by wrapping the callback. + let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); + let mut callback = |event| { + if let ControlFlow::Break = callback(event) { + control_flow.set(ControlFlow::Break); + } + }; + + // dispatch any pre-buffered events + self.post_dispatch_triggers(); + self.sink.lock().unwrap().empty_with(&mut callback); + + loop { + // dispatch events blocking if needed +// self.evq +// .get_mut() +// .dispatch() +// .expect("Wayland connection lost."); + self.post_dispatch_triggers(); + + // empty buffer of events + self.sink.lock().unwrap().empty_with(&mut callback); + + if let ControlFlow::Break = control_flow.get() { + println!("break!"); + break; + } + } + } + + pub fn get_primary_monitor(&self) -> MonitorId { + get_available_monitors(&self.display).iter().next().unwrap().clone() + } + + pub fn get_available_monitors(&self) -> VecDeque { + get_available_monitors(&self.display) + } + + pub fn get_display(&self) -> &GbmDevice { + &*self.display + } +} + +/* + * Private EventsLoop Internals + */ + +impl EventsLoop { + fn post_dispatch_triggers(&mut self) { + let mut sink = self.sink.lock().unwrap(); + // process a possible pending wakeup call + if self.pending_wakeup.load(Ordering::Relaxed) { + sink.send_raw_event(::Event::Awakened); + self.pending_wakeup.store(false, Ordering::Relaxed); + } + // prune possible dead windows + { + let mut cleanup_needed = self.cleanup_needed.lock().unwrap(); + if *cleanup_needed { + let pruned = self.store.lock().unwrap().cleanup(); + *cleanup_needed = false; + for wid in pruned { + sink.send_event(::WindowEvent::Destroyed, wid); + } + } + } + // process pending resize/refresh + self.store.lock().unwrap().for_each( + |_newsize, _size, new_dpi, refresh, _frame_refresh, closed, wid| { /*, frame*/ +// if let Some(frame) = frame { +// if let Some((w, h)) = newsize { +// frame.resize(w, h); +// frame.refresh(); +// let logical_size = ::LogicalSize::new(w as f64, h as f64); +// sink.send_event(::WindowEvent::Resized(logical_size), wid); +// *size = (w, h); +// } else if frame_refresh { +// frame.refresh(); +// if !refresh { +// frame.surface().commit() +// } +// } +// } + if let Some(dpi) = new_dpi { + sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid); + } + if refresh { + sink.send_event(::WindowEvent::Refresh, wid); + } + if closed { + sink.send_event(::WindowEvent::CloseRequested, wid); + } + }, + ) + } +} + +/* + * Monitor stuff + */ + +pub struct MonitorId { + pub(crate) handle: drm::control::connector::Handle, + pub(crate) card: Card, +} + +impl Clone for MonitorId { + fn clone(&self) -> MonitorId { + MonitorId { + handle: self.handle.clone(), + card: self.card.try_clone().unwrap(), + } + } +} + +impl fmt::Debug for MonitorId { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + // #[derive(Debug)] + // struct MonitorId { + // name: Option, + // native_identifier: u32, + // dimensions: PhysicalSize, + // position: PhysicalPosition, + // hidpi_factor: i32, + // } + + // let monitor_id_proxy = MonitorId { + // name: self.get_name(), + // native_identifier: self.get_native_identifier(), + // dimensions: self.get_dimensions(), + // position: self.get_position(), + // hidpi_factor: self.get_hidpi_factor(), + // }; + + // monitor_id_proxy.fmt(f) + } +} + +impl MonitorId { + pub fn get_name(&self) -> Option { + let info = drm::control::connector::Info::load_from_device(&self.card, self.handle).unwrap(); + Some(format!("{} ({:?}, {:?})", "DRM", info.connector_type(), info.connection_state())) + } + + #[inline] + pub fn get_native_identifier(&self) -> u32 { + unimplemented!() + // self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0) + } + + pub fn get_dimensions(&self) -> PhysicalSize { + let (_connector, mode, _encoder, _crtc) = self.card.get_resources(); + (mode.size().0 as u32, mode.size().1 as u32).into() + } + + pub fn get_position(&self) -> PhysicalPosition { + unimplemented!() + // self.mgr + // .with_info(&self.proxy, |_, info| info.location) + // .unwrap_or((0, 0)) + // .into() + } + + #[inline] + pub fn get_hidpi_factor(&self) -> i32 { + 1 + } +} + +pub fn get_primary_monitor(/*outputs: &OutputMgr*/) -> MonitorId { + unimplemented!() +// outputs.with_all(|list| { +// if let Some(&(_, ref proxy, _)) = list.first() { +// MonitorId { +// proxy: proxy.clone(), +// mgr: outputs.clone(), +// } +// } else { +// panic!("No monitor is available.") +// } +// }) +} + +pub fn get_available_monitors(display: &GbmDevice) -> VecDeque { + display.resource_handles() + .unwrap() + .connectors() + .iter() + .map(|&h| MonitorId { handle: h, card: display.try_clone().unwrap() }) + .collect() +} diff --git a/src/platform/linux/gbm/mod.rs b/src/platform/linux/gbm/mod.rs new file mode 100644 index 00000000000..b9bdd638eb9 --- /dev/null +++ b/src/platform/linux/gbm/mod.rs @@ -0,0 +1,22 @@ +#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", + target_os = "netbsd", target_os = "openbsd"))] + +pub use self::window::Window; +pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId}; + +use gbm::{Surface, AsRaw}; + +mod event_loop; +mod window; +mod card; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeviceId; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId(usize); + +#[inline] +fn make_wid(s: &Surface) -> WindowId { + WindowId(s.as_raw() as usize) +} diff --git a/src/platform/linux/gbm/window.rs b/src/platform/linux/gbm/window.rs new file mode 100644 index 00000000000..3c1d549aa59 --- /dev/null +++ b/src/platform/linux/gbm/window.rs @@ -0,0 +1,453 @@ +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; + +use {CreationError, MouseCursor, WindowAttributes}; +use dpi::{LogicalPosition, LogicalSize}; +use platform::{PlatformSpecificWindowBuilderAttributes as PlAttributes}; +use window::MonitorId as RootMonitorId; + +use super::{make_wid, EventsLoop, MonitorId, WindowId, card::Card}; +use platform::platform::gbm::event_loop::{get_available_monitors, get_primary_monitor}; + +use gbm::{Surface, Format, BufferObjectFlags, Device as GbmDevice}; +use drm::control::{crtc, framebuffer, connector::Info as ConnectorInfo, Mode, ResourceInfo}; + +pub struct Window { + surface: Arc>, + crtc: Arc, + connector: Arc, + mode: Arc, + // frame: Arc>>, + // outputs: OutputMgr, // Access to info for all monitors + display: Arc>, + size: Arc>, + kill_switch: (Arc>, Arc>), + // display: Arc, + need_frame_refresh: Arc>, +} + +impl Window { + pub fn new(evlp: &EventsLoop, attributes: WindowAttributes, _pl_attribs: PlAttributes) + -> Result + { + let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600)); + // Create the window + let size = Arc::new(Mutex::new((width, height))); + + let _window_store = evlp.store.clone(); + let gbm = evlp.display.clone(); + + let (connector, mode, _encoder, crtc) = gbm.get_resources(); + + let surface = gbm.create_surface::( + width, + height, + Format::XRGB8888, + BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING + ).unwrap(); + + // let window_store = evlp.store.clone(); + // let my_surface = surface.clone(); + // let mut frame = SWindow::::init_from_env( + // &evlp.env, + // surface.clone(), + // (width, height), + // move |event| match event { + // WEvent::Configure { new_size, .. } => { + // let mut store = window_store.lock().unwrap(); + // for window in &mut store.windows { + // if window.surface.equals(&my_surface) { + // window.newsize = new_size; + // window.need_refresh = true; + // *(window.need_frame_refresh.lock().unwrap()) = true; + // return; + // } + // } + // } + // WEvent::Refresh => { + // let store = window_store.lock().unwrap(); + // for window in &store.windows { + // if window.surface.equals(&my_surface) { + // *(window.need_frame_refresh.lock().unwrap()) = true; + // return; + // } + // } + // } + // WEvent::Close => { + // let mut store = window_store.lock().unwrap(); + // for window in &mut store.windows { + // if window.surface.equals(&my_surface) { + // window.closed = true; + // return; + // } + // } + // } + // }, + // ).unwrap(); + + // if let Some(app_id) = pl_attribs.app_id { + // frame.set_app_id(app_id); + // } + + // frame.set_title(attributes.title); + + // for &(_, ref seat) in evlp.seats.lock().unwrap().iter() { + // frame.new_seat(seat); + // } + + // Check for fullscreen requirements + // if let Some(RootMonitorId { + // inner: PlatformMonitorId::Wayland(ref monitor_id), + // }) = attributes.fullscreen + // { + // frame.set_fullscreen(Some(&monitor_id.proxy)); + // } else if attributes.maximized { + // frame.set_maximized(); + // } + + // frame.set_resizable(attributes.resizable); + + // // set decorations + // frame.set_decorate(attributes.decorations); + + // // min-max dimensions + // frame.set_min_size(attributes.min_dimensions.map(Into::into)); + // frame.set_max_size(attributes.max_dimensions.map(Into::into)); + + let kill_switch = Arc::new(Mutex::new(false)); + let need_frame_refresh = Arc::new(Mutex::new(true)); + // let frame = Arc::new(Mutex::new(frame)); + let surface = Arc::new(surface); + + evlp.store.lock().unwrap().windows.push(InternalWindow { + closed: false, + newsize: None, + size: size.clone(), + need_refresh: false, + need_frame_refresh: need_frame_refresh.clone(), + surface: surface.clone(), + kill_switch: kill_switch.clone(), +// frame: Arc::downgrade(&frame), + current_dpi: 1, + new_dpi: None, + }); +// evlp.evq.borrow_mut().sync_roundtrip().unwrap(); + + Ok(Window { + surface, + crtc: Arc::new(crtc), + connector: Arc::new(connector), + mode: Arc::new(mode), + display: gbm, + size, + kill_switch: (kill_switch, evlp.cleanup_needed.clone()), + need_frame_refresh + }) + } + + #[inline] + pub fn id(&self) -> WindowId { + make_wid(&self.surface) + } + + pub fn set_title(&self, _title: &str) { + // self.frame.lock().unwrap().set_title(title.into()); + } + + #[inline] + pub fn show(&self) { + // TODO + } + + #[inline] + pub fn hide(&self) { + // TODO + } + + #[inline] + pub fn get_position(&self) -> Option { + // Not possible with wayland + None + } + + #[inline] + pub fn get_inner_position(&self) -> Option { + // Not possible with wayland + None + } + + #[inline] + pub fn set_position(&self, _pos: LogicalPosition) { + // Not possible with wayland + } + + pub fn get_inner_size(&self) -> Option { + Some(self.size.lock().unwrap().clone().into()) + } + + #[inline] + pub fn get_outer_size(&self) -> Option { + let (w, h) = self.size.lock().unwrap().clone(); + // let (w, h) = super::wayland_window::add_borders(w as i32, h as i32); + Some((w, h).into()) + } + + #[inline] + // NOTE: This will only resize the borders, the contents must be updated by the user + pub fn set_inner_size(&self, size: LogicalSize) { + let (w, h) = size.into(); + // self.frame.lock().unwrap().resize(w, h); + *(self.size.lock().unwrap()) = (w, h); + } + + #[inline] + pub fn set_min_dimensions(&self, _dimensions: Option) { + // self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into)); + } + + #[inline] + pub fn set_max_dimensions(&self, _dimensions: Option) { + // self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into)); + } + + #[inline] + pub fn set_resizable(&self, _resizable: bool) { + // self.frame.lock().unwrap().set_resizable(resizable); + } + + #[inline] + pub fn hidpi_factor(&self) -> i32 { + 1 + } + + pub fn set_decorations(&self, _decorate: bool) { + // self.frame.lock().unwrap().set_decorate(decorate); + *(self.need_frame_refresh.lock().unwrap()) = true; + } + + pub fn set_maximized(&self, maximized: bool) { + if maximized { + // self.frame.lock().unwrap().set_maximized(); + } else { + // self.frame.lock().unwrap().unset_maximized(); + } + } + + pub fn set_fullscreen(&self, _monitor: Option) { + unimplemented!() + // if let Some(RootMonitorId { + // inner: PlatformMonitorId::Wayland(ref monitor_id), + // }) = monitor + // { + // self.frame + // .lock() + // .unwrap() + // .set_fullscreen(Some(&monitor_id.proxy)); + // } else { + // // self.frame.lock().unwrap().unset_fullscreen(); + // } + } + + + // pub fn set_theme(&self, theme: T) { + // self.frame.lock().unwrap().set_theme(theme) + // } + + #[inline] + pub fn set_cursor(&self, _cursor: MouseCursor) { + // TODO + } + + #[inline] + pub fn hide_cursor(&self, _hide: bool) { + // TODO: This isn't possible on Wayland yet + } + + #[inline] + pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> { + Err("Cursor grabbing is not yet possible on Wayland.".to_owned()) + } + + #[inline] + pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), String> { + Err("Setting the cursor position is not yet possible on Wayland.".to_owned()) + } + + pub fn get_display(&self) -> &GbmDevice { + &*self.display + } + + pub fn get_surface(&self) -> &Surface { + &*self.surface + } + + pub fn get_current_monitor(&self) -> MonitorId { + unimplemented!() + // let output = get_outputs(&self.surface).last().unwrap().clone(); + // MonitorId { + // // proxy: output, + // // mgr: self.outputs.clone(), + // } + } + + pub fn get_available_monitors(&self) -> VecDeque { + get_available_monitors(&self.display) + } + + pub fn get_primary_monitor(&self) -> MonitorId { + get_primary_monitor(/*&self.outputs*/) + } + + pub fn set_crtc(&self) { + let display = &*self.display; + let mut bo = unsafe { self.surface.lock_front_buffer() }.unwrap(); + let fb_info = framebuffer::create(display, &*bo).unwrap(); + + let _ = crtc::set( + display, + self.crtc.handle(), + fb_info.handle(), + &[self.connector.handle()], + (0, 0), + Some(*self.mode) + ).unwrap(); + } + + pub fn page_flip(&self) { + let display = &*self.display; + let mut next_bo = unsafe { self.surface.lock_front_buffer() }.unwrap(); + let fb_info = framebuffer::create(display, &*next_bo).unwrap(); + + // * Here you could also update drm plane layers if you want + // * hw composition + + crtc::page_flip( + &*self.display, + self.crtc.handle(), + fb_info.handle(), + &[crtc::PageFlipFlags::PageFlipEvent], + ).expect("Failed to queue Page Flip"); + + let mut events: crtc::Events; + let mut waiting_for_flip = true; + while waiting_for_flip { + events = crtc::receive_events(display).unwrap(); + for event in events { + match event { + crtc::Event::Vblank(_s) => {}, //println!("VblankEvent:{}", s.frame), + crtc::Event::PageFlip(_s) => { + // println!("PageFlipEvent:{}", s.frame); + waiting_for_flip = false; + } + crtc::Event::Unknown(_s) => {}, //println!("unkonw event:{:?}", s), + } + } + } + +// bo = next_bo; +// next_bo.drop(); + } +} + +impl Drop for Window { + fn drop(&mut self) { + *(self.kill_switch.0.lock().unwrap()) = true; + *(self.kill_switch.1.lock().unwrap()) = true; + } +} + +/* + * Internal store for windows + */ + +struct InternalWindow { + surface: Arc>, + newsize: Option<(u32, u32)>, + size: Arc>, + need_refresh: bool, + need_frame_refresh: Arc>, + closed: bool, + kill_switch: Arc>, + // frame: Weak>>, + current_dpi: i32, + new_dpi: Option, +} + +pub struct WindowStore { + windows: Vec, +} + +impl WindowStore { + pub fn new() -> WindowStore { + WindowStore { + windows: Vec::new(), + } + } + + // pub fn find_wid(&self, surface: &Proxy) -> Option { + // for window in &self.windows { + // if surface.equals(&window.surface) { + // return Some(make_wid(/*surface*/)); + // } + // } + // None + // } + + pub fn cleanup(&mut self) -> Vec { + let mut pruned = Vec::new(); + self.windows.retain(|w| { + if *w.kill_switch.lock().unwrap() { + // window is dead, cleanup + pruned.push(make_wid(&w.surface)); + // w.surface.destroy(); + false + } else { + true + } + }); + pruned + } + + // pub fn new_seat(&self, seat: &Proxy) { + // for window in &self.windows { + // if let Some(w) = window.frame.upgrade() { + // w.lock().unwrap().new_seat(seat); + // } + // } + // } + + // fn dpi_change(&mut self, surface: &Proxy, new: i32) { + // for window in &mut self.windows { + // if surface.equals(&window.surface) { + // window.new_dpi = Some(new); + // } + // } + // } + + pub fn for_each(&mut self, mut f: F) + where + F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option, + bool, bool, bool, WindowId /*, Option<&mut SWindow>*/), + { + for window in &mut self.windows { +// let opt_arc = window.frame.upgrade(); +// let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap()); + f( + window.newsize.take(), + &mut *(window.size.lock().unwrap()), + window.new_dpi, + window.need_refresh, + ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), + window.closed, + make_wid(&window.surface), +// opt_mutex_lock.as_mut().map(|m| &mut **m), + ); + if let Some(dpi) = window.new_dpi.take() { + window.current_dpi = dpi; + } + window.need_refresh = false; + // avoid re-spamming the event + window.closed = false; + } + } +} diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 1f88cdfed33..6c4dd4330da 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -26,10 +26,11 @@ pub use self::x11::XNotSupported; mod dlopen; pub mod wayland; pub mod x11; +pub mod gbm; /// Environment variable specifying which backend should be used on unix platform. /// -/// Legal values are x11 and wayland. If this variable is set only the named backend +/// Legal values are x11, wayland and gbm. If this variable is set only the named backend /// will be tried by winit. If it is not set, winit will try to connect to a wayland connection, /// and if it fails will fallback on x11. /// @@ -58,12 +59,14 @@ lazy_static!( pub enum Window { X(x11::Window), Wayland(wayland::Window), + Gbm(gbm::Window), } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum WindowId { X(x11::WindowId), Wayland(wayland::WindowId), + Gbm(gbm::WindowId), } impl WindowId { @@ -76,6 +79,7 @@ impl WindowId { pub enum DeviceId { X(x11::DeviceId), Wayland(wayland::DeviceId), + Gbm(gbm::DeviceId), } impl DeviceId { @@ -88,6 +92,7 @@ impl DeviceId { pub enum MonitorId { X(x11::MonitorId), Wayland(wayland::MonitorId), + Gbm(gbm::MonitorId), } impl MonitorId { @@ -96,6 +101,7 @@ impl MonitorId { match self { &MonitorId::X(ref m) => m.get_name(), &MonitorId::Wayland(ref m) => m.get_name(), + &MonitorId::Gbm(ref m) => m.get_name(), } } @@ -104,6 +110,7 @@ impl MonitorId { match self { &MonitorId::X(ref m) => m.get_native_identifier(), &MonitorId::Wayland(ref m) => m.get_native_identifier(), + &MonitorId::Gbm(ref m) => m.get_native_identifier(), } } @@ -112,6 +119,7 @@ impl MonitorId { match self { &MonitorId::X(ref m) => m.get_dimensions(), &MonitorId::Wayland(ref m) => m.get_dimensions(), + &MonitorId::Gbm(ref m) => m.get_dimensions(), } } @@ -120,6 +128,7 @@ impl MonitorId { match self { &MonitorId::X(ref m) => m.get_position(), &MonitorId::Wayland(ref m) => m.get_position(), + &MonitorId::Gbm(ref m) => m.get_position(), } } @@ -128,6 +137,7 @@ impl MonitorId { match self { &MonitorId::X(ref m) => m.get_hidpi_factor(), &MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64, + &MonitorId::Gbm(ref m) => m.get_hidpi_factor() as f64, } } } @@ -146,6 +156,9 @@ impl Window { EventsLoop::X(ref events_loop) => { x11::Window::new(events_loop, attribs, pl_attribs).map(Window::X) }, + EventsLoop::Gbm(ref events_loop) => { + gbm::Window::new(events_loop, attribs, pl_attribs).map(Window::Gbm) + }, } } @@ -154,6 +167,7 @@ impl Window { match self { &Window::X(ref w) => WindowId::X(w.id()), &Window::Wayland(ref w) => WindowId::Wayland(w.id()), + &Window::Gbm(ref w) => WindowId::Gbm(w.id()), } } @@ -162,6 +176,7 @@ impl Window { match self { &Window::X(ref w) => w.set_title(title), &Window::Wayland(ref w) => w.set_title(title), + &Window::Gbm(ref w) => w.set_title(title), } } @@ -170,6 +185,7 @@ impl Window { match self { &Window::X(ref w) => w.show(), &Window::Wayland(ref w) => w.show(), + &Window::Gbm(ref w) => w.show(), } } @@ -178,6 +194,7 @@ impl Window { match self { &Window::X(ref w) => w.hide(), &Window::Wayland(ref w) => w.hide(), + &Window::Gbm(ref w) => w.hide(), } } @@ -186,6 +203,7 @@ impl Window { match self { &Window::X(ref w) => w.get_position(), &Window::Wayland(ref w) => w.get_position(), + &Window::Gbm(ref w) => w.get_position(), } } @@ -194,6 +212,7 @@ impl Window { match self { &Window::X(ref m) => m.get_inner_position(), &Window::Wayland(ref m) => m.get_inner_position(), + &Window::Gbm(ref m) => m.get_inner_position(), } } @@ -202,6 +221,7 @@ impl Window { match self { &Window::X(ref w) => w.set_position(position), &Window::Wayland(ref w) => w.set_position(position), + &Window::Gbm(ref w) => w.set_position(position), } } @@ -210,6 +230,7 @@ impl Window { match self { &Window::X(ref w) => w.get_inner_size(), &Window::Wayland(ref w) => w.get_inner_size(), + &Window::Gbm(ref w) => w.get_inner_size(), } } @@ -218,6 +239,7 @@ impl Window { match self { &Window::X(ref w) => w.get_outer_size(), &Window::Wayland(ref w) => w.get_outer_size(), + &Window::Gbm(ref w) => w.get_outer_size(), } } @@ -226,6 +248,7 @@ impl Window { match self { &Window::X(ref w) => w.set_inner_size(size), &Window::Wayland(ref w) => w.set_inner_size(size), + &Window::Gbm(ref w) => w.set_inner_size(size), } } @@ -234,6 +257,7 @@ impl Window { match self { &Window::X(ref w) => w.set_min_dimensions(dimensions), &Window::Wayland(ref w) => w.set_min_dimensions(dimensions), + &Window::Gbm(ref w) => w.set_min_dimensions(dimensions), } } @@ -242,6 +266,7 @@ impl Window { match self { &Window::X(ref w) => w.set_max_dimensions(dimensions), &Window::Wayland(ref w) => w.set_max_dimensions(dimensions), + &Window::Gbm(ref w) => w.set_max_dimensions(dimensions), } } @@ -250,6 +275,7 @@ impl Window { match self { &Window::X(ref w) => w.set_resizable(resizable), &Window::Wayland(ref w) => w.set_resizable(resizable), + &Window::Gbm(ref w) => w.set_resizable(resizable), } } @@ -257,7 +283,8 @@ impl Window { pub fn set_cursor(&self, cursor: MouseCursor) { match self { &Window::X(ref w) => w.set_cursor(cursor), - &Window::Wayland(ref w) => w.set_cursor(cursor) + &Window::Wayland(ref w) => w.set_cursor(cursor), + &Window::Gbm(ref w) => w.set_cursor(cursor), } } @@ -266,6 +293,7 @@ impl Window { match self { &Window::X(ref window) => window.grab_cursor(grab), &Window::Wayland(ref window) => window.grab_cursor(grab), + &Window::Gbm(ref window) => window.grab_cursor(grab), } } @@ -274,6 +302,7 @@ impl Window { match self { &Window::X(ref window) => window.hide_cursor(hide), &Window::Wayland(ref window) => window.hide_cursor(hide), + &Window::Gbm(ref window) => window.hide_cursor(hide), } } @@ -282,6 +311,7 @@ impl Window { match self { &Window::X(ref w) => w.get_hidpi_factor(), &Window::Wayland(ref w) => w.hidpi_factor() as f64, + &Window::Gbm(ref w) => w.hidpi_factor() as f64, } } @@ -290,6 +320,7 @@ impl Window { match self { &Window::X(ref w) => w.set_cursor_position(position), &Window::Wayland(ref w) => w.set_cursor_position(position), + &Window::Gbm(ref w) => w.set_cursor_position(position), } } @@ -298,6 +329,7 @@ impl Window { match self { &Window::X(ref w) => w.set_maximized(maximized), &Window::Wayland(ref w) => w.set_maximized(maximized), + &Window::Gbm(ref w) => w.set_maximized(maximized), } } @@ -305,7 +337,8 @@ impl Window { pub fn set_fullscreen(&self, monitor: Option) { match self { &Window::X(ref w) => w.set_fullscreen(monitor), - &Window::Wayland(ref w) => w.set_fullscreen(monitor) + &Window::Wayland(ref w) => w.set_fullscreen(monitor), + &Window::Gbm(ref w) => w.set_fullscreen(monitor), } } @@ -313,7 +346,8 @@ impl Window { pub fn set_decorations(&self, decorations: bool) { match self { &Window::X(ref w) => w.set_decorations(decorations), - &Window::Wayland(ref w) => w.set_decorations(decorations) + &Window::Wayland(ref w) => w.set_decorations(decorations), + &Window::Gbm(ref w) => w.set_decorations(decorations), } } @@ -322,6 +356,7 @@ impl Window { match self { &Window::X(ref w) => w.set_always_on_top(always_on_top), &Window::Wayland(_) => (), + &Window::Gbm(_) => (), } } @@ -330,6 +365,7 @@ impl Window { match self { &Window::X(ref w) => w.set_window_icon(window_icon), &Window::Wayland(_) => (), + &Window::Gbm(_) => (), } } @@ -338,6 +374,7 @@ impl Window { match self { &Window::X(ref w) => w.set_ime_spot(position), &Window::Wayland(_) => (), + &Window::Gbm(_) => (), } } @@ -346,6 +383,7 @@ impl Window { match self { &Window::X(ref window) => RootMonitorId { inner: MonitorId::X(window.get_current_monitor()) }, &Window::Wayland(ref window) => RootMonitorId { inner: MonitorId::Wayland(window.get_current_monitor()) }, + &Window::Gbm(ref window) => RootMonitorId { inner: MonitorId::Gbm(window.get_current_monitor()) }, } } @@ -360,6 +398,10 @@ impl Window { .into_iter() .map(MonitorId::Wayland) .collect(), + &Window::Gbm(ref window) => window.get_available_monitors() + .into_iter() + .map(MonitorId::Gbm) + .collect(), } } @@ -368,6 +410,7 @@ impl Window { match self { &Window::X(ref window) => MonitorId::X(window.get_primary_monitor()), &Window::Wayland(ref window) => MonitorId::Wayland(window.get_primary_monitor()), + &Window::Gbm(ref window) => MonitorId::Gbm(window.get_primary_monitor()), } } } @@ -404,13 +447,15 @@ unsafe extern "C" fn x_error_callback( pub enum EventsLoop { Wayland(wayland::EventsLoop), - X(x11::EventsLoop) + X(x11::EventsLoop), + Gbm(gbm::EventsLoop), } #[derive(Clone)] pub enum EventsLoopProxy { X(x11::EventsLoopProxy), Wayland(wayland::EventsLoopProxy), + Gbm(gbm::EventsLoopProxy), } impl EventsLoop { @@ -425,6 +470,10 @@ impl EventsLoop { return EventsLoop::new_wayland() .expect("Failed to initialize Wayland backend"); }, + "gbm" => { + return EventsLoop::new_gbm() + .expect("Failed to initialize GLES2 backend"); + }, _ => panic!( "Unknown environment variable value for {}, try one of `x11`,`wayland`", BACKEND_PREFERENCE_ENV_VAR, @@ -442,10 +491,16 @@ impl EventsLoop { Err(err) => err, }; + let gbm_err = match EventsLoop::new_gbm() { + Ok(event_loop) => return event_loop, + Err(err) => err, + }; + let err_string = format!( - "Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}", + "Failed to initialize any backend! Wayland status: {:?} X11 status: {:?} Gbm status: {:?}", wayland_err, x11_err, + gbm_err, ); panic!(err_string); } @@ -455,6 +510,11 @@ impl EventsLoop { .map(EventsLoop::Wayland) } + pub fn new_gbm() -> Result { + gbm::EventsLoop::new() + .map(EventsLoop::Gbm) + } + pub fn new_x11() -> Result { X11_BACKEND .lock() @@ -479,6 +539,11 @@ impl EventsLoop { .into_iter() .map(MonitorId::X) .collect(), + EventsLoop::Gbm(ref evlp) => evlp + .get_available_monitors() + .into_iter() + .map(MonitorId::Gbm) + .collect(), } } @@ -487,6 +552,7 @@ impl EventsLoop { match *self { EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()), EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()), + EventsLoop::Gbm(ref evlp) => MonitorId::Gbm(evlp.get_primary_monitor()), } } @@ -494,6 +560,7 @@ impl EventsLoop { match *self { EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()), EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()), + EventsLoop::Gbm(ref evlp) => EventsLoopProxy::Gbm(evlp.create_proxy()), } } @@ -502,7 +569,8 @@ impl EventsLoop { { match *self { EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), - EventsLoop::X(ref mut evlp) => evlp.poll_events(callback) + EventsLoop::X(ref mut evlp) => evlp.poll_events(callback), + EventsLoop::Gbm(ref mut evlp) => evlp.poll_events(callback), } } @@ -511,7 +579,8 @@ impl EventsLoop { { match *self { EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), - EventsLoop::X(ref mut evlp) => evlp.run_forever(callback) + EventsLoop::X(ref mut evlp) => evlp.run_forever(callback), + EventsLoop::Gbm(ref mut evlp) => evlp.run_forever(callback), } } @@ -520,6 +589,16 @@ impl EventsLoop { match *self { EventsLoop::Wayland(_) => true, EventsLoop::X(_) => false, + EventsLoop::Gbm(_) => false, + } + } + + #[inline] + pub fn is_gbm(&self) -> bool { + match *self { + EventsLoop::Wayland(_) => false, + EventsLoop::X(_) => false, + EventsLoop::Gbm(_) => true, } } @@ -528,6 +607,7 @@ impl EventsLoop { match *self { EventsLoop::Wayland(_) => None, EventsLoop::X(ref ev) => Some(ev.x_connection()), + EventsLoop::Gbm(_) => None, } } } @@ -537,6 +617,7 @@ impl EventsLoopProxy { match *self { EventsLoopProxy::Wayland(ref proxy) => proxy.wakeup(), EventsLoopProxy::X(ref proxy) => proxy.wakeup(), + EventsLoopProxy::Gbm(ref proxy) => proxy.wakeup(), } } }