Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events loop backend #269

Merged
merged 6 commits into from
Sep 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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