From 9dc5689eef87cace5e6ac6b5438928a1c99688c4 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 17 Dec 2014 14:49:11 +1000 Subject: [PATCH 1/4] Introduce a WindowProxy for accessing a subset of functionality from other threads. This currently provides a way for other threads to wakeup a blocked event loop on X11. Other platforms have stub functions that need to be implemented. This is similar to the functionality of glfwPostEmptyEvent. --- src/android/mod.rs | 14 +++++ src/events.rs | 3 + src/lib.rs | 31 +++++++++ src/osx/mod.rs | 14 +++++ src/win32/mod.rs | 14 +++++ src/x11/ffi.rs | 2 + src/x11/mod.rs | 2 +- src/x11/window/mod.rs | 143 +++++++++++++++++++++++++++--------------- 8 files changed, 171 insertions(+), 52 deletions(-) diff --git a/src/android/mod.rs b/src/android/mod.rs index 8093d509..fd0a8574 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -207,6 +207,10 @@ impl Window { pub fn set_inner_size(&self, _x: uint, _y: uint) { } + pub fn create_window_proxy(&self) -> WindowProxy { + WindowProxy + } + pub fn poll_events(&self) -> Vec { use std::time::Duration; 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) { + // TODO + } +} + #[unsafe_destructor] impl Drop for Window { fn drop(&mut self) { diff --git a/src/events.rs b/src/events.rs index 76fda817..3cb81ff6 100644 --- a/src/events.rs +++ b/src/events.rs @@ -31,6 +31,9 @@ pub enum Event { /// An event from the mouse has been received. MouseInput(ElementState, MouseButton), + + /// The window should be redrawn. + Refresh, } pub type ScanCode = u8; diff --git a/src/lib.rs b/src/lib.rs index 9ba3e13f..029b5769 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -473,6 +473,15 @@ impl Window { pub fn get_api(&self) -> 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")] @@ -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. #[cfg(feature = "headless")] pub struct HeadlessContext { diff --git a/src/osx/mod.rs b/src/osx/mod.rs index c69486a7..3f9da82b 100644 --- a/src/osx/mod.rs +++ b/src/osx/mod.rs @@ -74,6 +74,16 @@ impl Window { } } +#[cfg(feature = "window")] +#[deriving(Clone)] +pub struct WindowProxy; + +impl WindowProxy { + pub fn wakeup_event_loop(&self) { + // TODO + } +} + extern fn window_should_close(this: id, _: id) -> id { unsafe { let mut stored_value = ptr::null_mut(); @@ -278,6 +288,10 @@ impl Window { unimplemented!() } + pub fn create_window_proxy(&self) -> WindowProxy { + WindowProxy + } + pub fn poll_events(&self) -> Vec { let mut events = Vec::new(); diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 5ba5a26c..099c1262 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -83,6 +83,16 @@ impl Window { } } +#[cfg(feature = "window")] +#[deriving(Clone)] +pub struct WindowProxy; + +impl WindowProxy { + pub fn wakeup_event_loop(&self) { + // TODO + } +} + impl Window { /// See the docs in the crate root file. 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. // TODO: return iterator pub fn poll_events(&self) -> Vec { diff --git a/src/x11/ffi.rs b/src/x11/ffi.rs index 3fe64993..1f8b10f5 100644 --- a/src/x11/ffi.rs +++ b/src/x11/ffi.rs @@ -1420,6 +1420,8 @@ extern "C" { x_return: *mut libc::c_int, y_return: *mut libc::c_int, 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; + 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, only_if_exists: Bool) -> Atom; pub fn XKeycodeToKeysym(display: *mut Display, keycode: KeyCode, diff --git a/src/x11/mod.rs b/src/x11/mod.rs index 9d9d25c0..9ce789f7 100644 --- a/src/x11/mod.rs +++ b/src/x11/mod.rs @@ -2,7 +2,7 @@ pub use self::headless::HeadlessContext; #[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; diff --git a/src/x11/window/mod.rs b/src/x11/window/mod.rs index f22d1f89..6ea14c5f 100644 --- a/src/x11/window/mod.rs +++ b/src/x11/window/mod.rs @@ -6,7 +6,7 @@ use std::{mem, ptr}; use std::cell::Cell; use std::sync::atomic::AtomicBool; 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}; @@ -23,17 +23,65 @@ fn ensure_thread_init() { }); } -pub struct Window { +struct XWindow { display: *mut ffi::Display, window: ffi::Window, - im: ffi::XIM, - ic: ffi::XIC, 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, +} + +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, is_closed: AtomicBool, 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)>, } @@ -257,7 +305,7 @@ impl Window { }); let share = if let Some(win) = builder.sharing { - win.window.context + win.window.x.context } else { ptr::null() }; @@ -278,16 +326,18 @@ impl Window { // creating the window object let window = Window { - display: display, - window: window, - im: im, - ic: ic, - context: context, + x: Arc::new(XWindow { + display: display, + window: window, + im: im, + 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), 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)), }; @@ -303,22 +353,22 @@ impl Window { pub fn set_title(&self, title: &str) { let c_title = title.to_c_str(); unsafe { - ffi::XStoreName(self.display, self.window, c_title.as_ptr()); - ffi::XFlush(self.display); + ffi::XStoreName(self.x.display, self.x.window, c_title.as_ptr()); + ffi::XFlush(self.x.display); } } pub fn show(&self) { unsafe { - ffi::XMapRaised(self.display, self.window); - ffi::XFlush(self.display); + ffi::XMapRaised(self.x.display, self.x.window); + ffi::XFlush(self.x.display); } } pub fn hide(&self) { unsafe { - ffi::XUnmapWindow(self.display, self.window); - ffi::XFlush(self.display); + ffi::XUnmapWindow(self.x.display, self.x.window); + ffi::XFlush(self.x.display); } } @@ -334,7 +384,7 @@ impl Window { let mut border: 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 border, &mut depth) == 0 { @@ -350,7 +400,7 @@ impl Window { } 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)> { @@ -365,6 +415,12 @@ impl Window { unimplemented!() } + pub fn create_window_proxy(&self) -> WindowProxy { + WindowProxy { + x: self.x.clone() + } + } + pub fn poll_events(&self) -> Vec { use std::mem; @@ -374,10 +430,10 @@ impl Window { use std::num::Int; 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 { - 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 { break @@ -390,7 +446,7 @@ impl Window { }, ffi::ClientMessage => { - use events::Event::Closed; + use events::Event::{Closed, Refresh}; use std::sync::atomic::Relaxed; 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 { self.is_closed.store(true, Relaxed); events.push(Closed); + } else { + events.push(Refresh); } }, @@ -424,7 +482,7 @@ impl Window { if event.type_ == ffi::KeyPress { 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 }; @@ -434,7 +492,7 @@ impl Window { let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16]; 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()), buffer.len() as libc::c_int, ptr::null_mut(), ptr::null_mut()); @@ -447,7 +505,7 @@ impl Window { } 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); @@ -500,7 +558,7 @@ impl Window { // this will block until an event arrives, but doesn't remove // it from the queue let mut xev = unsafe { mem::uninitialized() }; - unsafe { ffi::XPeekEvent(self.display, &mut xev) }; + unsafe { ffi::XPeekEvent(self.x.display, &mut xev) }; // calling poll_events() let ev = self.poll_events(); @@ -511,7 +569,7 @@ impl Window { } 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 { panic!("glx::MakeCurrent failed"); } @@ -529,11 +587,11 @@ impl Window { } 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 { - self.display as *mut libc::c_void + self.x.display as *mut libc::c_void } /// See the docs in the crate root file. @@ -541,20 +599,3 @@ impl Window { ::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); } - } -} From 435c64f4732dbf6fa4e92d04a962e42e34f21f1a Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 18 Dec 2014 10:57:12 +1000 Subject: [PATCH 2/4] Fix event name --- src/events.rs | 4 ++-- src/x11/window/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/events.rs b/src/events.rs index 3cb81ff6..beafbecb 100644 --- a/src/events.rs +++ b/src/events.rs @@ -32,8 +32,8 @@ pub enum Event { /// An event from the mouse has been received. MouseInput(ElementState, MouseButton), - /// The window should be redrawn. - Refresh, + /// The event loop was woken up by another thread. + Awakened, } pub type ScanCode = u8; diff --git a/src/x11/window/mod.rs b/src/x11/window/mod.rs index 6ea14c5f..57ae22d9 100644 --- a/src/x11/window/mod.rs +++ b/src/x11/window/mod.rs @@ -446,7 +446,7 @@ impl Window { }, ffi::ClientMessage => { - use events::Event::{Closed, Refresh}; + use events::Event::{Closed, Awakened}; use std::sync::atomic::Relaxed; let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) }; @@ -455,7 +455,7 @@ impl Window { self.is_closed.store(true, Relaxed); events.push(Closed); } else { - events.push(Refresh); + events.push(Awakened); } }, From 2c150143d8da8bded7d9441ef08f67eab51db7c4 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 18 Dec 2014 14:22:58 +1000 Subject: [PATCH 3/4] Add mac implementation of wakeup_event_loop. --- src/osx/mod.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/osx/mod.rs b/src/osx/mod.rs index 3f9da82b..22ae432a 100644 --- a/src/osx/mod.rs +++ b/src/osx/mod.rs @@ -80,7 +80,23 @@ pub struct WindowProxy; impl WindowProxy { pub fn wakeup_event_loop(&self) { - // TODO + 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(); + } } } From 95f82927b37e27f1880e9941b316227f6c6baebc Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Fri, 19 Dec 2014 05:00:43 +1000 Subject: [PATCH 4/4] Change todo to unimplemented --- src/android/mod.rs | 2 +- src/win32/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/mod.rs b/src/android/mod.rs index fd0a8574..8bc82ceb 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -281,7 +281,7 @@ pub struct WindowProxy; impl WindowProxy { pub fn wakeup_event_loop(&self) { - // TODO + unimplemented!() } } diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 099c1262..978e3b5d 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -89,7 +89,7 @@ pub struct WindowProxy; impl WindowProxy { pub fn wakeup_event_loop(&self) { - // TODO + unimplemented!() } }