Merge pull request #161 from glennw/thread-proxy

Introduce a WindowProxy for accessing a subset of functionality
This commit is contained in:
tomaka 2014-12-18 20:36:37 +01:00
commit 0164449955
8 changed files with 187 additions and 52 deletions

View file

@ -207,6 +207,10 @@ impl Window {
pub fn set_inner_size(&self, _x: uint, _y: uint) { pub fn set_inner_size(&self, _x: uint, _y: uint) {
} }
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
}
pub fn poll_events(&self) -> Vec<Event> { pub fn poll_events(&self) -> Vec<Event> {
use std::time::Duration; use std::time::Duration;
use std::io::timer; use std::io::timer;
@ -271,6 +275,16 @@ impl Window {
} }
} }
#[cfg(feature = "window")]
#[deriving(Clone)]
pub struct WindowProxy;
impl WindowProxy {
pub fn wakeup_event_loop(&self) {
unimplemented!()
}
}
#[unsafe_destructor] #[unsafe_destructor]
impl Drop for Window { impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {

View file

@ -31,6 +31,9 @@ pub enum Event {
/// An event from the mouse has been received. /// An event from the mouse has been received.
MouseInput(ElementState, MouseButton), MouseInput(ElementState, MouseButton),
/// The event loop was woken up by another thread.
Awakened,
} }
pub type ScanCode = u8; pub type ScanCode = u8;

View file

@ -473,6 +473,15 @@ impl Window {
pub fn get_api(&self) -> Api { pub fn get_api(&self) -> Api {
self.window.get_api() self.window.get_api()
} }
/// Create a window proxy for this window, that can be freely
/// passed to different threads.
#[inline]
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy {
proxy: self.window.create_window_proxy()
}
}
} }
#[cfg(feature = "window")] #[cfg(feature = "window")]
@ -482,6 +491,28 @@ impl gl_common::GlFunctionsSource for Window {
} }
} }
/// Represents a thread safe subset of operations that can be called
/// on a window. This structure can be safely cloned and sent between
/// threads.
///
#[cfg(feature = "window")]
#[deriving(Clone)]
pub struct WindowProxy {
proxy: winimpl::WindowProxy,
}
#[cfg(feature = "window")]
impl WindowProxy {
/// Triggers a blocked event loop to wake up. This is
/// typically called when another thread wants to wake
/// up the blocked rendering thread to cause a refresh.
#[inline]
pub fn wakeup_event_loop(&self) {
self.proxy.wakeup_event_loop();
}
}
/// Represents a headless OpenGL context. /// Represents a headless OpenGL context.
#[cfg(feature = "headless")] #[cfg(feature = "headless")]
pub struct HeadlessContext { pub struct HeadlessContext {

View file

@ -74,6 +74,32 @@ impl Window {
} }
} }
#[cfg(feature = "window")]
#[deriving(Clone)]
pub struct WindowProxy;
impl WindowProxy {
pub fn wakeup_event_loop(&self) {
unsafe {
let pool = NSAutoreleasePool::new(nil);
let event =
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
nil,
NSApplicationDefined,
NSPoint::new(0.0, 0.0),
0,
0.0,
0,
ptr::null_mut(),
0,
0,
0);
NSApp().postEvent_atStart_(event, true);
pool.drain();
}
}
}
extern fn window_should_close(this: id, _: id) -> id { extern fn window_should_close(this: id, _: id) -> id {
unsafe { unsafe {
let mut stored_value = ptr::null_mut(); let mut stored_value = ptr::null_mut();
@ -278,6 +304,10 @@ impl Window {
unimplemented!() unimplemented!()
} }
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
}
pub fn poll_events(&self) -> Vec<Event> { pub fn poll_events(&self) -> Vec<Event> {
let mut events = Vec::new(); let mut events = Vec::new();

View file

@ -83,6 +83,16 @@ impl Window {
} }
} }
#[cfg(feature = "window")]
#[deriving(Clone)]
pub struct WindowProxy;
impl WindowProxy {
pub fn wakeup_event_loop(&self) {
unimplemented!()
}
}
impl Window { impl Window {
/// See the docs in the crate root file. /// See the docs in the crate root file.
pub fn is_closed(&self) -> bool { pub fn is_closed(&self) -> bool {
@ -180,6 +190,10 @@ impl Window {
} }
} }
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy
}
/// See the docs in the crate root file. /// See the docs in the crate root file.
// TODO: return iterator // TODO: return iterator
pub fn poll_events(&self) -> Vec<Event> { pub fn poll_events(&self) -> Vec<Event> {

View file

@ -1420,6 +1420,8 @@ extern "C" {
x_return: *mut libc::c_int, y_return: *mut libc::c_int, x_return: *mut libc::c_int, y_return: *mut libc::c_int,
width_return: *mut libc::c_uint, height_return: *mut libc::c_uint, width_return: *mut libc::c_uint, height_return: *mut libc::c_uint,
border_width_return: *mut libc::c_uint, depth_return: *mut libc::c_uint) -> Status; border_width_return: *mut libc::c_uint, depth_return: *mut libc::c_uint) -> Status;
pub fn XSendEvent(display: *mut Display, window: Window, propagate: Bool,
event_mask: libc::c_long, event_send: *mut XEvent) -> Status;
pub fn XInternAtom(display: *mut Display, atom_name: *const libc::c_char, pub fn XInternAtom(display: *mut Display, atom_name: *const libc::c_char,
only_if_exists: Bool) -> Atom; only_if_exists: Bool) -> Atom;
pub fn XKeycodeToKeysym(display: *mut Display, keycode: KeyCode, pub fn XKeycodeToKeysym(display: *mut Display, keycode: KeyCode,

View file

@ -2,7 +2,7 @@
pub use self::headless::HeadlessContext; pub use self::headless::HeadlessContext;
#[cfg(feature = "window")] #[cfg(feature = "window")]
pub use self::window::{Window, MonitorID, get_available_monitors, get_primary_monitor}; pub use self::window::{Window, WindowProxy, MonitorID, get_available_monitors, get_primary_monitor};
mod ffi; mod ffi;

View file

@ -6,7 +6,7 @@ use std::{mem, ptr};
use std::cell::Cell; use std::cell::Cell;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use super::ffi; use super::ffi;
use std::sync::{Once, ONCE_INIT}; use std::sync::{Arc, Once, ONCE_INIT};
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
@ -23,17 +23,65 @@ fn ensure_thread_init() {
}); });
} }
pub struct Window { struct XWindow {
display: *mut ffi::Display, display: *mut ffi::Display,
window: ffi::Window, window: ffi::Window,
im: ffi::XIM,
ic: ffi::XIC,
context: ffi::GLXContext, context: ffi::GLXContext,
is_fullscreen: bool,
screen_id: libc::c_int,
xf86_desk_mode: *mut ffi::XF86VidModeModeInfo,
ic: ffi::XIC,
im: ffi::XIM,
}
impl Drop for XWindow {
fn drop(&mut self) {
unsafe {
ffi::glx::MakeCurrent(self.display, 0, ptr::null());
ffi::glx::DestroyContext(self.display, self.context);
if self.is_fullscreen {
ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode);
ffi::XF86VidModeSetViewPort(self.display, self.screen_id, 0, 0);
}
ffi::XDestroyIC(self.ic);
ffi::XCloseIM(self.im);
ffi::XDestroyWindow(self.display, self.window);
ffi::XCloseDisplay(self.display);
}
}
}
#[deriving(Clone)]
pub struct WindowProxy {
x: Arc<XWindow>,
}
impl WindowProxy {
pub fn wakeup_event_loop(&self) {
let mut xev = ffi::XClientMessageEvent {
type_: ffi::ClientMessage,
window: self.x.window,
format: 32,
message_type: 0,
serial: 0,
send_event: 0,
display: self.x.display,
l: [0, 0, 0, 0, 0],
};
unsafe {
ffi::XSendEvent(self.x.display, self.x.window, 0, 0, mem::transmute(&mut xev));
ffi::XFlush(self.x.display);
}
}
}
pub struct Window {
x: Arc<XWindow>,
is_closed: AtomicBool, is_closed: AtomicBool,
wm_delete_window: ffi::Atom, wm_delete_window: ffi::Atom,
xf86_desk_mode: *mut ffi::XF86VidModeModeInfo,
screen_id: libc::c_int,
is_fullscreen: bool,
current_size: Cell<(libc::c_int, libc::c_int)>, current_size: Cell<(libc::c_int, libc::c_int)>,
} }
@ -257,7 +305,7 @@ impl Window {
}); });
let share = if let Some(win) = builder.sharing { let share = if let Some(win) = builder.sharing {
win.window.context win.window.x.context
} else { } else {
ptr::null() ptr::null()
}; };
@ -278,16 +326,18 @@ impl Window {
// creating the window object // creating the window object
let window = Window { let window = Window {
display: display, x: Arc::new(XWindow {
window: window, display: display,
im: im, window: window,
ic: ic, im: im,
context: context, ic: ic,
context: context,
screen_id: screen_id,
is_fullscreen: builder.monitor.is_some(),
xf86_desk_mode: xf86_desk_mode,
}),
is_closed: AtomicBool::new(false), is_closed: AtomicBool::new(false),
wm_delete_window: wm_delete_window, wm_delete_window: wm_delete_window,
xf86_desk_mode: xf86_desk_mode,
screen_id: screen_id,
is_fullscreen: builder.monitor.is_some(),
current_size: Cell::new((0, 0)), current_size: Cell::new((0, 0)),
}; };
@ -303,22 +353,22 @@ impl Window {
pub fn set_title(&self, title: &str) { pub fn set_title(&self, title: &str) {
let c_title = title.to_c_str(); let c_title = title.to_c_str();
unsafe { unsafe {
ffi::XStoreName(self.display, self.window, c_title.as_ptr()); ffi::XStoreName(self.x.display, self.x.window, c_title.as_ptr());
ffi::XFlush(self.display); ffi::XFlush(self.x.display);
} }
} }
pub fn show(&self) { pub fn show(&self) {
unsafe { unsafe {
ffi::XMapRaised(self.display, self.window); ffi::XMapRaised(self.x.display, self.x.window);
ffi::XFlush(self.display); ffi::XFlush(self.x.display);
} }
} }
pub fn hide(&self) { pub fn hide(&self) {
unsafe { unsafe {
ffi::XUnmapWindow(self.display, self.window); ffi::XUnmapWindow(self.x.display, self.x.window);
ffi::XFlush(self.display); ffi::XFlush(self.x.display);
} }
} }
@ -334,7 +384,7 @@ impl Window {
let mut border: libc::c_uint = mem::uninitialized(); let mut border: libc::c_uint = mem::uninitialized();
let mut depth: libc::c_uint = mem::uninitialized(); let mut depth: libc::c_uint = mem::uninitialized();
if ffi::XGetGeometry(self.display, self.window, if ffi::XGetGeometry(self.x.display, self.x.window,
&mut root, &mut x, &mut y, &mut width, &mut height, &mut root, &mut x, &mut y, &mut width, &mut height,
&mut border, &mut depth) == 0 &mut border, &mut depth) == 0
{ {
@ -350,7 +400,7 @@ impl Window {
} }
pub fn set_position(&self, x: int, y: int) { pub fn set_position(&self, x: int, y: int) {
unsafe { ffi::XMoveWindow(self.display, self.window, x as libc::c_int, y as libc::c_int) } unsafe { ffi::XMoveWindow(self.x.display, self.x.window, x as libc::c_int, y as libc::c_int) }
} }
pub fn get_inner_size(&self) -> Option<(uint, uint)> { pub fn get_inner_size(&self) -> Option<(uint, uint)> {
@ -365,6 +415,12 @@ impl Window {
unimplemented!() unimplemented!()
} }
pub fn create_window_proxy(&self) -> WindowProxy {
WindowProxy {
x: self.x.clone()
}
}
pub fn poll_events(&self) -> Vec<Event> { pub fn poll_events(&self) -> Vec<Event> {
use std::mem; use std::mem;
@ -374,10 +430,10 @@ impl Window {
use std::num::Int; use std::num::Int;
let mut xev = unsafe { mem::uninitialized() }; let mut xev = unsafe { mem::uninitialized() };
let res = unsafe { ffi::XCheckMaskEvent(self.display, Int::max_value(), &mut xev) }; let res = unsafe { ffi::XCheckMaskEvent(self.x.display, Int::max_value(), &mut xev) };
if res == 0 { if res == 0 {
let res = unsafe { ffi::XCheckTypedEvent(self.display, ffi::ClientMessage, &mut xev) }; let res = unsafe { ffi::XCheckTypedEvent(self.x.display, ffi::ClientMessage, &mut xev) };
if res == 0 { if res == 0 {
break break
@ -390,7 +446,7 @@ impl Window {
}, },
ffi::ClientMessage => { ffi::ClientMessage => {
use events::Event::Closed; use events::Event::{Closed, Awakened};
use std::sync::atomic::Relaxed; use std::sync::atomic::Relaxed;
let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) }; let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };
@ -398,6 +454,8 @@ impl Window {
if client_msg.l[0] == self.wm_delete_window as libc::c_long { if client_msg.l[0] == self.wm_delete_window as libc::c_long {
self.is_closed.store(true, Relaxed); self.is_closed.store(true, Relaxed);
events.push(Closed); events.push(Closed);
} else {
events.push(Awakened);
} }
}, },
@ -424,7 +482,7 @@ impl Window {
if event.type_ == ffi::KeyPress { if event.type_ == ffi::KeyPress {
let raw_ev: *mut ffi::XKeyEvent = event; let raw_ev: *mut ffi::XKeyEvent = event;
unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window) }; unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.x.window) };
} }
let state = if xev.type_ == ffi::KeyPress { Pressed } else { Released }; let state = if xev.type_ == ffi::KeyPress { Pressed } else { Released };
@ -434,7 +492,7 @@ impl Window {
let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16]; let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16];
let raw_ev: *mut ffi::XKeyEvent = event; let raw_ev: *mut ffi::XKeyEvent = event;
let count = ffi::Xutf8LookupString(self.ic, mem::transmute(raw_ev), let count = ffi::Xutf8LookupString(self.x.ic, mem::transmute(raw_ev),
mem::transmute(buffer.as_mut_ptr()), mem::transmute(buffer.as_mut_ptr()),
buffer.len() as libc::c_int, ptr::null_mut(), ptr::null_mut()); buffer.len() as libc::c_int, ptr::null_mut(), ptr::null_mut());
@ -447,7 +505,7 @@ impl Window {
} }
let keysym = unsafe { let keysym = unsafe {
ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) ffi::XKeycodeToKeysym(self.x.display, event.keycode as ffi::KeyCode, 0)
}; };
let vkey = events::keycode_to_element(keysym as libc::c_uint); let vkey = events::keycode_to_element(keysym as libc::c_uint);
@ -500,7 +558,7 @@ impl Window {
// this will block until an event arrives, but doesn't remove // this will block until an event arrives, but doesn't remove
// it from the queue // it from the queue
let mut xev = unsafe { mem::uninitialized() }; let mut xev = unsafe { mem::uninitialized() };
unsafe { ffi::XPeekEvent(self.display, &mut xev) }; unsafe { ffi::XPeekEvent(self.x.display, &mut xev) };
// calling poll_events() // calling poll_events()
let ev = self.poll_events(); let ev = self.poll_events();
@ -511,7 +569,7 @@ impl Window {
} }
pub unsafe fn make_current(&self) { pub unsafe fn make_current(&self) {
let res = ffi::glx::MakeCurrent(self.display, self.window, self.context); let res = ffi::glx::MakeCurrent(self.x.display, self.x.window, self.x.context);
if res == 0 { if res == 0 {
panic!("glx::MakeCurrent failed"); panic!("glx::MakeCurrent failed");
} }
@ -529,11 +587,11 @@ impl Window {
} }
pub fn swap_buffers(&self) { pub fn swap_buffers(&self) {
unsafe { ffi::glx::SwapBuffers(self.display, self.window) } unsafe { ffi::glx::SwapBuffers(self.x.display, self.x.window) }
} }
pub fn platform_display(&self) -> *mut libc::c_void { pub fn platform_display(&self) -> *mut libc::c_void {
self.display as *mut libc::c_void self.x.display as *mut libc::c_void
} }
/// See the docs in the crate root file. /// See the docs in the crate root file.
@ -541,20 +599,3 @@ impl Window {
::Api::OpenGl ::Api::OpenGl
} }
} }
impl Drop for Window {
fn drop(&mut self) {
unsafe { ffi::glx::MakeCurrent(self.display, 0, ptr::null()); }
unsafe { ffi::glx::DestroyContext(self.display, self.context); }
if self.is_fullscreen {
unsafe { ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode); }
unsafe { ffi::XF86VidModeSetViewPort(self.display, self.screen_id, 0, 0); }
}
unsafe { ffi::XDestroyIC(self.ic); }
unsafe { ffi::XCloseIM(self.im); }
unsafe { ffi::XDestroyWindow(self.display, self.window); }
unsafe { ffi::XCloseDisplay(self.display); }
}
}