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

(WIP) Wayland backend #410

Merged
merged 1 commit into from
May 7, 2015
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ kernel32-sys = "0.1"

[target.i686-unknown-linux-gnu.dependencies]
osmesa-sys = "0.0.5"
wayland-client = "*"
x11 = "*"

[target.x86_64-unknown-linux-gnu.dependencies]
osmesa-sys = "0.0.5"
wayland-client = "*"
x11 = "*"

[target.arm-unknown-linux-gnueabihf.dependencies]
Expand Down
1 change: 1 addition & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ pub mod egl;
pub mod emscripten;
pub mod glx;
pub mod osmesa;
pub mod wayland;
pub mod win32;
pub mod x11;
378 changes: 378 additions & 0 deletions src/api/wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
#![cfg(target_os = "linux")]

use self::wayland::egl::{EGLSurface, is_egl_available};
use self::wayland::core::{Display, Registry, Compositor, Shell, ShellSurface,
Seat, Pointer, default_display, WSurface, SurfaceId,
Surface};

use libc;
use api::dlopen;
use api::egl::Context as EglContext;

use BuilderAttribs;
use CreationError;
use Event;
use PixelFormat;
use CursorState;
use MouseCursor;
use GlContext;

use std::collections::{VecDeque, HashMap};
use std::sync::{Arc, Mutex};
use std::ffi::CString;

extern crate wayland_client as wayland;

struct WaylandContext {
pub display: Display,
pub registry: Registry,
pub compositor: Compositor,
pub shell: Shell,
pub seat: Seat,
pub pointer: Option<Pointer<WSurface>>,
windows_event_queues: Arc<Mutex<HashMap<SurfaceId, Arc<Mutex<VecDeque<Event>>>>>>,
current_pointer_surface: Arc<Mutex<Option<SurfaceId>>>
}

impl WaylandContext {
pub fn new() -> Option<WaylandContext> {
let display = match default_display() {
Some(d) => d,
None => return None,
};
let registry = display.get_registry();
// let the registry get its events
display.sync_roundtrip();
let compositor = match registry.get_compositor() {
Some(c) => c,
None => return None,
};
let shell = match registry.get_shell() {
Some(s) => s,
None => return None,
};
let seat = match registry.get_seats().into_iter().next() {
Some(s) => s,
None => return None,
};
// let the other globals get their events
display.sync_roundtrip();

let current_pointer_surface = Arc::new(Mutex::new(None));

// rustc has trouble finding the correct type here, so we explicit it.
let windows_event_queues = Arc::new(Mutex::new(
HashMap::<SurfaceId, Arc<Mutex<VecDeque<Event>>>>::new()
));

// handle inputs
let mut pointer = seat.get_pointer();
if let Some(ref mut p) = pointer {
// set the enter/leave callbacks
let current_surface = current_pointer_surface.clone();
p.set_enter_action(move |_, sid, x, y| {
*current_surface.lock().unwrap() = Some(sid);
});
let current_surface = current_pointer_surface.clone();
p.set_leave_action(move |_, sid| {
*current_surface.lock().unwrap() = None;
});
// set the events callbacks
let current_surface = current_pointer_surface.clone();
let event_queues = windows_event_queues.clone();
p.set_motion_action(move |_, _, x, y| {
// dispatch to the appropriate queue
let sid = *current_surface.lock().unwrap();
if let Some(sid) = sid {
let map = event_queues.lock().unwrap();
if let Some(queue) = map.get(&sid) {
queue.lock().unwrap().push_back(Event::Moved(x as i32,y as i32))
}
}
});
let current_surface = current_pointer_surface.clone();
let event_queues = windows_event_queues.clone();
p.set_button_action(move |_, sid, b, s| {
use self::wayland::core::ButtonState;
use MouseButton;
use ElementState;
let button = match b {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
_ => return
};
let state = match s {
ButtonState::WL_POINTER_BUTTON_STATE_RELEASED => ElementState::Released,
ButtonState::WL_POINTER_BUTTON_STATE_PRESSED => ElementState::Pressed
};
// dispatch to the appropriate queue
let sid = *current_surface.lock().unwrap();
if let Some(sid) = sid {
let map = event_queues.lock().unwrap();
if let Some(queue) = map.get(&sid) {
queue.lock().unwrap().push_back(Event::MouseInput(state, button))
}
}
});
}
Some(WaylandContext {
display: display,
registry: registry,
compositor: compositor,
shell: shell,
seat: seat,
pointer: pointer,
windows_event_queues: windows_event_queues,
current_pointer_surface: current_pointer_surface
})
}

fn register_surface(&self, sid: SurfaceId, queue: Arc<Mutex<VecDeque<Event>>>) {
self.windows_event_queues.lock().unwrap().insert(sid, queue);
}

fn deregister_surface(&self, sid: SurfaceId) {
self.windows_event_queues.lock().unwrap().remove(&sid);
}
}

lazy_static! {
static ref WAYLAND_CONTEXT: Option<WaylandContext> = {
WaylandContext::new()
};
}

pub struct Window {
shell_surface: ShellSurface<EGLSurface>,
pending_events: Arc<Mutex<VecDeque<Event>>>,
pub context: EglContext,
}

// It is okay, as the window is completely self-owned: it has its
// own wayland connexion.
unsafe impl Send for Window {}

#[derive(Clone)]
pub struct WindowProxy;

impl WindowProxy {
pub fn wakeup_event_loop(&self) {
unimplemented!()
}
}

pub struct MonitorID;

pub fn get_available_monitors() -> VecDeque<MonitorID> {
VecDeque::new()
}
pub fn get_primary_monitor() -> MonitorID {
MonitorID
}

impl MonitorID {
pub fn get_name(&self) -> Option<String> {
unimplemented!();
}

pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
::native_monitor::NativeMonitorId::Unavailable
}

pub fn get_dimensions(&self) -> (u32, u32) {
unimplemented!();
}
}


pub struct PollEventsIterator<'a> {
window: &'a Window,
}

impl<'a> Iterator for PollEventsIterator<'a> {
type Item = Event;

fn next(&mut self) -> Option<Event> {
if let Some(ref ctxt) = *WAYLAND_CONTEXT {
ctxt.display.dispatch_pending();
}
self.window.pending_events.lock().unwrap().pop_front()
}
}

pub struct WaitEventsIterator<'a> {
window: &'a Window,
}

impl<'a> Iterator for WaitEventsIterator<'a> {
type Item = Event;

fn next(&mut self) -> Option<Event> {
if let Some(ref ctxt) = *WAYLAND_CONTEXT {
ctxt.display.dispatch();
}
self.window.pending_events.lock().unwrap().pop_front()
}
}

impl Window {
pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
use self::wayland::internals::FFI;

let wayland_context = match *WAYLAND_CONTEXT {
Some(ref c) => c,
None => return Err(CreationError::NotSupported),
};

if !is_egl_available() { return Err(CreationError::NotSupported) }

let (w, h) = builder.dimensions.unwrap_or((800, 600));

let surface = EGLSurface::new(
wayland_context.compositor.create_surface(),
w as i32,
h as i32
);

let context = {
let libegl = unsafe { dlopen::dlopen(b"libEGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) };
if libegl.is_null() {
return Err(CreationError::NotSupported);
}
let egl = ::api::egl::ffi::egl::Egl::load_with(|sym| {
let sym = CString::new(sym).unwrap();
unsafe { dlopen::dlsym(libegl, sym.as_ptr()) }
});
try!(EglContext::new(
egl,
builder,
Some(wayland_context.display.ptr() as *const _),
surface.ptr() as *const _
))
};

let shell_surface = wayland_context.shell.get_shell_surface(surface);
shell_surface.set_toplevel();
let events = Arc::new(Mutex::new(VecDeque::new()));

wayland_context.register_surface(shell_surface.get_wsurface().get_id(), events.clone());

wayland_context.display.flush();

Ok(Window {
shell_surface: shell_surface,
pending_events: events,
context: context
})
}

pub fn is_closed(&self) -> bool {
false
}

pub fn set_title(&self, title: &str) {
}

pub fn show(&self) {
}

pub fn hide(&self) {
}

pub fn get_position(&self) -> Option<(i32, i32)> {
unimplemented!()
}

pub fn set_position(&self, x: i32, y: i32) {
}

pub fn get_inner_size(&self) -> Option<(u32, u32)> {
unimplemented!()
}

pub fn get_outer_size(&self) -> Option<(u32, u32)> {
unimplemented!()
}

pub fn set_inner_size(&self, _x: u32, _y: u32) {
unimplemented!()
}

pub fn create_window_proxy(&self) -> WindowProxy {
unimplemented!()
}

pub fn poll_events(&self) -> PollEventsIterator {
PollEventsIterator {
window: self
}
}

pub fn wait_events(&self) -> WaitEventsIterator {
WaitEventsIterator {
window: self
}
}

pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
}

pub fn set_cursor(&self, cursor: MouseCursor) {
}

pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
Ok(())
}

pub fn hidpi_factor(&self) -> f32 {
1.0
}

pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
Ok(())
}

pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!()
}

pub fn platform_window(&self) -> *mut libc::c_void {
unimplemented!()
}
}

impl GlContext for Window {

unsafe fn make_current(&self) {
self.context.make_current()
}

fn is_current(&self) -> bool {
self.context.is_current()
}

fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
self.context.get_proc_address(addr)
}

fn swap_buffers(&self) {
self.context.swap_buffers()
}

fn get_api(&self) -> ::Api {
self.context.get_api()
}

fn get_pixel_format(&self) -> PixelFormat {
self.context.get_pixel_format().clone()
}
}

impl Drop for Window {
fn drop(&mut self) {
if let Some(ref ctxt) = *WAYLAND_CONTEXT {
ctxt.deregister_surface(self.shell_surface.get_wsurface().get_id())
}
}
}