Skip to content

Commit

Permalink
Events loop backend (#269)
Browse files Browse the repository at this point in the history
* Don't use UNIX_BACKEND in Window2::new

* Move get_available_monitors and get_primary_monitor to EventsLoop

* Remove UNIX_BACKEND

* Restore choosing the Linux backend

* Return a XNotSupported for new_x11()

* Fix fullscreen example
  • Loading branch information
tomaka authored Sep 1, 2017
1 parent e65cacb commit 3d1c18d
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 213 deletions.
8 changes: 4 additions & 4 deletions examples/fullscreen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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))
Expand Down
10 changes: 10 additions & 0 deletions src/api_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ macro_rules! gen_api_transition {
}
}

#[inline]
pub fn get_available_monitors(&self) -> ::std::collections::VecDeque<MonitorId> {
get_available_monitors()
}

#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor()
}

pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(::Event)
{
Expand Down
32 changes: 31 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]
Expand Down
35 changes: 28 additions & 7 deletions src/os/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arc<XConnection>> {
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<Self, XNotSupported>
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<Self, XNotSupported> {
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
}
}
}
}

Expand Down
168 changes: 67 additions & 101 deletions src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,106 +32,36 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub screen_id: Option<i32>,
}

pub enum UnixBackend {
X(Arc<XConnection>),
Wayland(Arc<wayland::WaylandContext>),
Error(Option<XNotSupported>, Option<String>),
}

lazy_static!(
pub static ref UNIX_BACKEND: UnixBackend = {
#[inline]
fn x_backend() -> Result<UnixBackend, XNotSupported> {
match XConnection::new(Some(x_error_callback)) {
Ok(x) => Ok(UnixBackend::X(Arc::new(x))),
Err(e) => Err(e),
}
}
#[inline]
fn wayland_backend() -> Result<UnixBackend, ()> {
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<Arc<XConnection>, 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<MonitorId> {
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<String> {
Expand Down Expand Up @@ -167,24 +98,14 @@ impl Window2 {
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Self, CreationError>
{
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!()
}
}
}

Expand Down Expand Up @@ -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<u8> = 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();
Expand All @@ -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)
}

Expand All @@ -364,18 +283,65 @@ pub enum EventsLoopProxy {

impl EventsLoop {
pub fn new() -> EventsLoop {
match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => {
EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone()))
},
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),
}
}

UnixBackend::X(ref ctxt) => {
EventsLoop::X(x11::EventsLoop::new(ctxt.clone()))
},
if let Ok(el) = EventsLoop::new_wayland() {
return el;
}

UnixBackend::Error(..) => {
panic!("Attempted to create an EventsLoop while no backend was available.")
}
if let Ok(el) = EventsLoop::new_x11() {
return el;
}

panic!("No backend is available")
}

pub fn new_wayland() -> Result<EventsLoop, ()> {
wayland::WaylandContext::init()
.map(|ctx| EventsLoop::Wayland(wayland::EventsLoop::new(Arc::new(ctx))))
.ok_or(())
}

pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
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<MonitorId> {
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())),
}
}

Expand Down
Loading

0 comments on commit 3d1c18d

Please sign in to comment.