diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 2da143d1..e39003f9 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -4,9 +4,11 @@ use std::io::{self, Write}; use winit::{ControlFlow, Event, WindowEvent, FullScreenState}; fn main() { + let mut events_loop = winit::EventsLoop::new(); + // enumerating monitors let monitor = { - for (num, monitor) in winit::get_available_monitors().enumerate() { + for (num, monitor) in events_loop.get_available_monitors().enumerate() { println!("Monitor #{}: {:?}", num, monitor.get_name()); } @@ -16,15 +18,13 @@ fn main() { let mut num = String::new(); io::stdin().read_line(&mut num).unwrap(); let num = num.trim().parse().ok().expect("Please enter a number"); - let monitor = winit::get_available_monitors().nth(num).expect("Please enter a valid ID"); + let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID"); println!("Using {:?}", monitor.get_name()); monitor }; - let mut events_loop = winit::EventsLoop::new(); - let _window = winit::WindowBuilder::new() .with_title("Hello world!") .with_fullscreen(FullScreenState::Exclusive(monitor)) diff --git a/src/api_transition.rs b/src/api_transition.rs index 158c6d44..de7414c0 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -24,6 +24,16 @@ macro_rules! gen_api_transition { } } + #[inline] + pub fn get_available_monitors(&self) -> ::std::collections::VecDeque { + get_available_monitors() + } + + #[inline] + pub fn get_primary_monitor(&self) -> MonitorId { + get_primary_monitor() + } + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(::Event) { diff --git a/src/lib.rs b/src/lib.rs index c25ad860..ae8e5185 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,7 +116,7 @@ extern crate x11_dl; extern crate wayland_client; pub use events::*; -pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor}; +pub use window::{AvailableMonitorsIter, MonitorId}; #[macro_use] mod api_transition; @@ -201,6 +201,36 @@ impl EventsLoop { } } + /// Returns the list of all available monitors. + /// + /// Usage will result in display backend initialisation, this can be controlled on linux + /// using an environment variable `WINIT_UNIX_BACKEND`. + /// > Legal values are `x11` and `wayland`. 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. + /// > + /// > If this variable is set with any other value, winit will panic. + // Note: should be replaced with `-> impl Iterator` once stable. + #[inline] + pub fn get_available_monitors(&self) -> AvailableMonitorsIter { + let data = self.events_loop.get_available_monitors(); + AvailableMonitorsIter{ data: data.into_iter() } + } + + /// Returns the primary monitor of the system. + /// + /// Usage will result in display backend initialisation, this can be controlled on linux + /// using an environment variable `WINIT_UNIX_BACKEND`. + /// > Legal values are `x11` and `wayland`. 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. + /// > + /// > If this variable is set with any other value, winit will panic. + #[inline] + pub fn get_primary_monitor(&self) -> MonitorId { + MonitorId { inner: self.events_loop.get_primary_monitor() } + } + /// Fetches all the events that are pending, calls the callback function for each of them, /// and returns. #[inline] diff --git a/src/os/unix.rs b/src/os/unix.rs index 4d360fe0..c2c73fe5 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -3,21 +3,42 @@ use std::sync::Arc; use std::ptr; use libc; +use EventsLoop; use MonitorId; use Window; +use platform::EventsLoop as LinuxEventsLoop; use platform::Window2 as LinuxWindow; -use platform::{UnixBackend, UNIX_BACKEND}; use WindowBuilder; use platform::x11::XConnection; use platform::x11::ffi::XVisualInfo; -pub use platform::x11; +pub use platform::XNotSupported; -// TODO: do not expose XConnection -pub fn get_x11_xconnection() -> Option> { - match *UNIX_BACKEND { - UnixBackend::X(ref connec) => Some(connec.clone()), - _ => None, +/// Additional methods on `EventsLoop` that are specific to Linux. +pub trait EventsLoopExt { + /// Builds a new `EventsLoop` that is forced to use X11. + fn new_x11() -> Result + where Self: Sized; + + /// Builds a new `EventsLoop` that is forced to use Wayland. + fn new_wayland() -> Self + where Self: Sized; +} + +impl EventsLoopExt for EventsLoop { + #[inline] + fn new_x11() -> Result { + LinuxEventsLoop::new_x11().map(|ev| EventsLoop { events_loop: ev }) + } + + #[inline] + fn new_wayland() -> Self { + EventsLoop { + events_loop: match LinuxEventsLoop::new_wayland() { + Ok(e) => e, + Err(_) => panic!() // TODO: propagate + } + } } } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 9c86a412..110b03f3 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -9,9 +9,10 @@ use libc; use self::x11::XConnection; use self::x11::XError; -use self::x11::XNotSupported; use self::x11::ffi::XVisualInfo; +pub use self::x11::XNotSupported; + mod dlopen; pub mod wayland; pub mod x11; @@ -31,106 +32,36 @@ pub struct PlatformSpecificWindowBuilderAttributes { pub screen_id: Option, } -pub enum UnixBackend { - X(Arc), - Wayland(Arc), - Error(Option, Option), -} - lazy_static!( - pub static ref UNIX_BACKEND: UnixBackend = { - #[inline] - fn x_backend() -> Result { - match XConnection::new(Some(x_error_callback)) { - Ok(x) => Ok(UnixBackend::X(Arc::new(x))), - Err(e) => Err(e), - } - } - #[inline] - fn wayland_backend() -> Result { - wayland::WaylandContext::init() - .map(|ctx| UnixBackend::Wayland(Arc::new(ctx))) - .ok_or(()) - } - match env::var(BACKEND_PREFERENCE_ENV_VAR) { - Ok(s) => match s.as_str() { - "x11" => x_backend().unwrap_or_else(|e| UnixBackend::Error(Some(e), None)), - "wayland" => wayland_backend().unwrap_or_else(|_| { - UnixBackend::Error(None, Some("Wayland not available".into())) - }), - _ => panic!("Unknown environment variable value for {}, try one of `x11`,`wayland`", - BACKEND_PREFERENCE_ENV_VAR), - }, - Err(_) => { - // Try wayland, fallback to X11 - wayland_backend().unwrap_or_else(|_| { - x_backend().unwrap_or_else(|x_err| { - UnixBackend::Error(Some(x_err), Some("Wayland not available".into())) - }) - }) - }, - } + pub static ref X11_BACKEND: Result, XNotSupported> = { + XConnection::new(Some(x_error_callback)).map(Arc::new) }; ); - pub enum Window2 { - #[doc(hidden)] X(x11::Window2), - #[doc(hidden)] Wayland(wayland::Window) } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum WindowId { - #[doc(hidden)] X(x11::WindowId), - #[doc(hidden)] Wayland(wayland::WindowId) } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DeviceId { - #[doc(hidden)] X(x11::DeviceId), - #[doc(hidden)] Wayland(wayland::DeviceId) } #[derive(Clone)] pub enum MonitorId { - #[doc(hidden)] X(x11::MonitorId), - #[doc(hidden)] Wayland(wayland::MonitorId), - #[doc(hidden)] None, } -#[inline] -pub fn get_available_monitors() -> VecDeque { - match *UNIX_BACKEND { - UnixBackend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt) - .into_iter() - .map(MonitorId::Wayland) - .collect(), - UnixBackend::X(ref connec) => x11::get_available_monitors(connec) - .into_iter() - .map(MonitorId::X) - .collect(), - UnixBackend::Error(..) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d}, - } -} - -#[inline] -pub fn get_primary_monitor() -> MonitorId { - match *UNIX_BACKEND { - UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)), - UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)), - UnixBackend::Error(..) => MonitorId::None, - } -} - impl MonitorId { #[inline] pub fn get_name(&self) -> Option { @@ -167,24 +98,14 @@ impl Window2 { pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { - match *UNIX_BACKEND { - UnixBackend::Wayland(ref ctxt) => { - if let EventsLoop::Wayland(ref evlp) = *events_loop { - wayland::Window::new(evlp, ctxt.clone(), window).map(Window2::Wayland) - } else { - // It is not possible to instanciate an EventsLoop not matching its backend - unreachable!() - } + match *events_loop { + EventsLoop::Wayland(ref evlp) => { + wayland::Window::new(evlp, window).map(Window2::Wayland) }, - UnixBackend::X(_) => { - x11::Window2::new(events_loop, window, pl_attribs).map(Window2::X) + EventsLoop::X(ref el) => { + x11::Window2::new(el, window, pl_attribs).map(Window2::X) }, - UnixBackend::Error(..) => { - // If the Backend is Error(), it is not possible to instanciate an EventsLoop at all, - // thus this function cannot be called! - unreachable!() - } } } @@ -332,7 +253,7 @@ unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x { use std::ffi::CStr; - if let UnixBackend::X(ref x) = *UNIX_BACKEND { + if let Ok(ref x) = *X11_BACKEND { let mut buff: Vec = Vec::with_capacity(1024); (x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32); let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy(); @@ -351,9 +272,7 @@ unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x } pub enum EventsLoop { - #[doc(hidden)] Wayland(wayland::EventsLoop), - #[doc(hidden)] X(x11::EventsLoop) } @@ -364,19 +283,66 @@ pub enum EventsLoopProxy { impl EventsLoop { pub fn new() -> EventsLoop { - match *UNIX_BACKEND { - UnixBackend::Wayland(ref ctxt) => { - EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone())) - }, - - UnixBackend::X(ref ctxt) => { - EventsLoop::X(x11::EventsLoop::new(ctxt.clone())) - }, - - UnixBackend::Error(..) => { - panic!("Attempted to create an EventsLoop while no backend was available.") + if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { + match env_var.as_str() { + "x11" => { + return EventsLoop::new_x11().unwrap(); // TODO: propagate + }, + "wayland" => { + match EventsLoop::new_wayland() { + Ok(e) => return e, + Err(_) => panic!() // TODO: propagate + } + }, + _ => panic!("Unknown environment variable value for {}, try one of `x11`,`wayland`", + BACKEND_PREFERENCE_ENV_VAR), } } + + if let Ok(el) = EventsLoop::new_wayland() { + return el; + } + + if let Ok(el) = EventsLoop::new_x11() { + return el; + } + + panic!("No backend is available") + } + + pub fn new_wayland() -> Result { + wayland::WaylandContext::init() + .map(|ctx| EventsLoop::Wayland(wayland::EventsLoop::new(Arc::new(ctx)))) + .ok_or(()) + } + + pub fn new_x11() -> Result { + match *X11_BACKEND { + Ok(ref x) => Ok(EventsLoop::X(x11::EventsLoop::new(x.clone()))), + Err(ref err) => Err(err.clone()), + } + } + + #[inline] + pub fn get_available_monitors(&self) -> VecDeque { + match *self { + EventsLoop::Wayland(ref evlp) => wayland::get_available_monitors(evlp.context()) + .into_iter() + .map(MonitorId::Wayland) + .collect(), + EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection()) + .into_iter() + .map(MonitorId::X) + .collect(), + } + } + + #[inline] + pub fn get_primary_monitor(&self) -> MonitorId { + match *self { + EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(wayland::get_primary_monitor(evlp.context())), + EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())), + } } pub fn create_proxy(&self) -> EventsLoopProxy { diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 8a7a8387..4bea66e1 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -131,6 +131,11 @@ impl EventsLoop { } } + #[inline] + pub fn context(&self) -> &Arc { + &self.ctxt + } + pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { ctxt: Arc::downgrade(&self.ctxt), diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 489bee1e..b8c734be 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -37,8 +37,9 @@ pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId { } impl Window { - pub fn new(evlp: &EventsLoop, ctxt: Arc, attributes: &WindowAttributes) -> Result + pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result { + let ctxt = evlp.context().clone(); let (width, height) = attributes.dimensions.unwrap_or((800,600)); let (surface, decorated) = ctxt.create_window::(width, height); diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index f655ce48..f3dd609d 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -120,6 +120,12 @@ impl EventsLoop { result } + /// Returns the `XConnection` of this events loop. + #[inline] + pub fn x_connection(&self) -> &Arc { + &self.display + } + pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { pending_wakeup: Arc::downgrade(&self.pending_wakeup), @@ -675,12 +681,11 @@ lazy_static! { // TODO: use a static mutex when that's possible, and put me } impl Window2 { - pub fn new(events_loop: &::platform::EventsLoop, + pub fn new(x_events_loop: &EventsLoop, window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { - let x_events_loop = if let ::platform::EventsLoop::X(ref e) = *events_loop { e } else { unreachable!() }; let win = ::std::sync::Arc::new(try!(Window::new(&x_events_loop, window, pl_attribs))); // creating IM diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index dbde5ee9..6c1ad39a 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,7 +1,7 @@ #![cfg(target_os = "macos")] pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy}; -pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; +pub use self::monitor::MonitorId; pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window}; use std::sync::Arc; diff --git a/src/platform/macos/monitor.rs b/src/platform/macos/monitor.rs index 10a7dac1..1d70d66f 100644 --- a/src/platform/macos/monitor.rs +++ b/src/platform/macos/monitor.rs @@ -1,27 +1,30 @@ use core_graphics::display; use std::collections::VecDeque; +use super::EventsLoop; #[derive(Clone)] pub struct MonitorId(u32); -pub fn get_available_monitors() -> VecDeque { - let mut monitors = VecDeque::new(); - unsafe { - let max_displays = 10u32; - let mut active_displays = [0u32; 10]; - let mut display_count = 0; - display::CGGetActiveDisplayList(max_displays, &mut active_displays[0], &mut display_count); - for i in 0..display_count as usize { - monitors.push_back(MonitorId(active_displays[i])); +impl EventsLoop { + pub fn get_available_monitors(&self) -> VecDeque { + let mut monitors = VecDeque::new(); + unsafe { + let max_displays = 10u32; + let mut active_displays = [0u32; 10]; + let mut display_count = 0; + display::CGGetActiveDisplayList(max_displays, &mut active_displays[0], &mut display_count); + for i in 0..display_count as usize { + monitors.push_back(MonitorId(active_displays[i])); + } } + monitors } - monitors -} -#[inline] -pub fn get_primary_monitor() -> MonitorId { - let id = unsafe { MonitorId(display::CGMainDisplayID()) }; - id + #[inline] + pub fn get_primary_monitor(&self) -> MonitorId { + let id = unsafe { MonitorId(display::CGMainDisplayID()) }; + id + } } impl MonitorId { diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 52e77525..7c086cc5 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -3,7 +3,7 @@ use winapi; pub use self::events_loop::{EventsLoop, EventsLoopProxy}; -pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; +pub use self::monitor::MonitorId; pub use self::window::Window; #[derive(Clone, Default)] diff --git a/src/platform/windows/monitor.rs b/src/platform/windows/monitor.rs index 6d9d3942..f51aea9b 100644 --- a/src/platform/windows/monitor.rs +++ b/src/platform/windows/monitor.rs @@ -4,6 +4,8 @@ use user32; use std::collections::VecDeque; use std::mem; +use super::EventsLoop; + /// Win32 implementation of the main `MonitorId` object. #[derive(Clone)] pub struct MonitorId { @@ -90,61 +92,61 @@ fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { .to_string() } -/// Win32 implementation of the main `get_available_monitors` function. -pub fn get_available_monitors() -> VecDeque { - // return value - let mut result = VecDeque::new(); +impl EventsLoop { + pub fn get_available_monitors(&self) -> VecDeque { + // return value + let mut result = VecDeque::new(); - for adapter in DeviceEnumerator::adapters() { - // getting the position - let (position, dimensions) = unsafe { - let mut dev: winapi::DEVMODEW = mem::zeroed(); - dev.dmSize = mem::size_of::() as winapi::WORD; + for adapter in DeviceEnumerator::adapters() { + // getting the position + let (position, dimensions) = unsafe { + let mut dev: winapi::DEVMODEW = mem::zeroed(); + dev.dmSize = mem::size_of::() as winapi::WORD; - if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(), - winapi::ENUM_CURRENT_SETTINGS, - &mut dev, 0) == 0 - { - continue; + if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(), + winapi::ENUM_CURRENT_SETTINGS, + &mut dev, 0) == 0 + { + continue; + } + + let point: &winapi::POINTL = mem::transmute(&dev.union1); + let position = (point.x as u32, point.y as u32); + + let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32); + + (position, dimensions) + }; + + for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() { + // adding to the resulting list + result.push_back(MonitorId { + adapter_name: adapter.DeviceName, + monitor_name: wchar_as_string(&monitor.DeviceName), + readable_name: wchar_as_string(&monitor.DeviceString), + flags: monitor.StateFlags, + primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 && + num == 0, + position: position, + dimensions: dimensions, + }); } - - let point: &winapi::POINTL = mem::transmute(&dev.union1); - let position = (point.x as u32, point.y as u32); - - let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32); - - (position, dimensions) - }; - - for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() { - // adding to the resulting list - result.push_back(MonitorId { - adapter_name: adapter.DeviceName, - monitor_name: wchar_as_string(&monitor.DeviceName), - readable_name: wchar_as_string(&monitor.DeviceString), - flags: monitor.StateFlags, - primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 && - num == 0, - position: position, - dimensions: dimensions, - }); - } - } - result -} - -/// Win32 implementation of the main `get_primary_monitor` function. -pub fn get_primary_monitor() -> MonitorId { - // we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag - // TODO: it is possible to query the win32 API for the primary monitor, this should be done - // instead - for monitor in get_available_monitors().into_iter() { - if monitor.primary { - return monitor; } + result } - panic!("Failed to find the primary monitor") + pub fn get_primary_monitor(&self) -> MonitorId { + // we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag + // TODO: it is possible to query the win32 API for the primary monitor, this should be done + // instead + for monitor in self.get_available_monitors().into_iter() { + if monitor.primary { + return monitor; + } + } + + panic!("Failed to find the primary monitor") + } } impl MonitorId { diff --git a/src/window.rs b/src/window.rs index 79459f11..65865be7 100644 --- a/src/window.rs +++ b/src/window.rs @@ -322,7 +322,7 @@ impl Window { // Implementation note: we retreive the list once, then serve each element by one by one. // This may change in the future. pub struct AvailableMonitorsIter { - data: VecDequeIter, + pub(crate) data: VecDequeIter, } impl Iterator for AvailableMonitorsIter { @@ -339,36 +339,6 @@ impl Iterator for AvailableMonitorsIter { } } -/// Returns the list of all available monitors. -/// -/// Usage will result in display backend initialisation, this can be controlled on linux -/// using an environment variable `WINIT_UNIX_BACKEND`. -/// > Legal values are `x11` and `wayland`. 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. -/// > -/// > If this variable is set with any other value, winit will panic. -// Note: should be replaced with `-> impl Iterator` once stable. -#[inline] -pub fn get_available_monitors() -> AvailableMonitorsIter { - let data = platform::get_available_monitors(); - AvailableMonitorsIter{ data: data.into_iter() } -} - -/// Returns the primary monitor of the system. -/// -/// Usage will result in display backend initialisation, this can be controlled on linux -/// using an environment variable `WINIT_UNIX_BACKEND`. -/// > Legal values are `x11` and `wayland`. 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. -/// > -/// > If this variable is set with any other value, winit will panic. -#[inline] -pub fn get_primary_monitor() -> MonitorId { - MonitorId { inner: platform::get_primary_monitor() } -} - /// Identifier for a monitor. #[derive(Clone)] pub struct MonitorId {