aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/api/x11/window.rs232
-rw-r--r--src/os/unix.rs15
-rw-r--r--src/platform/linux/api_dispatch.rs14
3 files changed, 255 insertions, 6 deletions
diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs
index dc7fa62..247e4e4 100644
--- a/src/api/x11/window.rs
+++ b/src/api/x11/window.rs
@@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex};
use std::os::raw::c_long;
use std::thread;
use std::time::Duration;
+use std::os::raw::c_ulong;
use Api;
use ContextError;
@@ -704,6 +705,237 @@ impl Window {
Ok(window)
}
+ /// Instead of actually creating an X11 window and returning a handle to
+ /// it, this function will use Xlib to find a pre-existing window (eg, one
+ /// created by another process) and return a wrapper for that.
+ ///
+ /// `window_attrs` and `pf_reqs` will mostly be ignored, while the `opengl`
+ /// attributes will be honored.
+ ///
+ /// `window_id` is the X window id of the existing window. This can be
+ /// found with, eg, the `xwininfo -tree` command. If the window can't be
+ /// found, an error will be returned.
+ ///
+ /// You almost certainly want to use `new()` instead, unless you are doing
+ /// something like drawing into an embedded sub-window of an existing
+ /// application.
+ pub fn from_existing_window(display: &Arc<XConnection>, window_attrs: &WindowAttributes,
+ pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes<&Window>,
+ window_id: c_ulong)
+ -> Result<Window, CreationError>
+ {
+
+ // check early that window_id actually exists, and grab attributes for it
+ let window = window_id;
+ let (screen_id, root) = unsafe {
+ let mut existing_attr: ffi::XWindowAttributes = mem::uninitialized();
+ (display.xlib.XGetWindowAttributes)(display.display, window_id, &mut existing_attr);
+ display.check_errors().expect("window_id did not map to an accessible existing window");
+ // Seems like there would be a cleaner way to find the screen_number, but there doesn't seem to be.
+ let max_screen_id = (display.xlib.XScreenCount)(display.display);
+ let mut screen_id = 0;
+ for id in 0..max_screen_id {
+ if existing_attr.screen == (display.xlib.XScreenOfDisplay)(display.display, id) {
+ screen_id = id;
+ break;
+ }
+ display.check_errors().expect("Problem fetching screen info");
+ if id == max_screen_id {
+ return Err(CreationError::OsError(format!("Couldn't find screen_number for window's screen")));
+ }
+ }
+ // get the root window handle
+ let root = existing_attr.root;
+ (screen_id, root)
+ };
+
+ // We aren't handling mode changes, so pretend we're not full-screen
+ // (even if existing window is)
+ let is_fullscreen = false;
+ let xf86_desk_mode = None;
+
+ // start the (OpenGL) context building process (same as new())
+ enum Prototype<'a> {
+ Glx(::api::glx::ContextPrototype<'a>),
+ Egl(::api::egl::ContextPrototype<'a>),
+ }
+ let builder_clone_opengl_glx = opengl.clone().map_sharing(|_| unimplemented!()); // FIXME:
+ let builder_clone_opengl_egl = opengl.clone().map_sharing(|_| unimplemented!()); // FIXME:
+ let context = match opengl.version {
+ GlRequest::Latest | GlRequest::Specific(Api::OpenGl, _) | GlRequest::GlThenGles { .. } => {
+ // GLX should be preferred over EGL, otherwise crashes may occur
+ // on X11 – issue #314
+ if let Some(ref glx) = display.glx {
+ Prototype::Glx(try!(GlxContext::new(glx.clone(), &display.xlib, pf_reqs, &builder_clone_opengl_glx, display.display, screen_id)))
+ } else if let Some(ref egl) = display.egl {
+ Prototype::Egl(try!(EglContext::new(egl.clone(), pf_reqs, &builder_clone_opengl_egl, egl::NativeDisplay::X11(Some(display.display as *const _)))))
+ } else {
+ return Err(CreationError::NotSupported);
+ }
+ },
+ GlRequest::Specific(Api::OpenGlEs, _) => {
+ if let Some(ref egl) = display.egl {
+ Prototype::Egl(try!(EglContext::new(egl.clone(), pf_reqs, &builder_clone_opengl_egl, egl::NativeDisplay::X11(Some(display.display as *const _)))))
+ } else {
+ return Err(CreationError::NotSupported);
+ }
+ },
+ GlRequest::Specific(_, _) => {
+ return Err(CreationError::NotSupported);
+ },
+ };
+
+ // getting the `visual_infos` (a struct that contains information about the visual to use) (same as new() )
+ let visual_infos = match context {
+ Prototype::Glx(ref p) => p.get_visual_infos().clone(),
+ Prototype::Egl(ref p) => {
+ unsafe {
+ let mut template: ffi::XVisualInfo = mem::zeroed();
+ template.visualid = p.get_native_visual_id() as ffi::VisualID;
+
+ let mut num_visuals = 0;
+ let vi = (display.xlib.XGetVisualInfo)(display.display, ffi::VisualIDMask,
+ &mut template, &mut num_visuals);
+ display.check_errors().expect("Failed to call XGetVisualInfo");
+ assert!(!vi.is_null());
+ assert!(num_visuals == 1);
+
+ let vi_copy = ptr::read(vi as *const _);
+ (display.xlib.XFree)(vi as *mut _);
+ vi_copy
+ }
+ },
+ };
+
+ // creating the color map (same as new() )
+ let cmap = unsafe {
+ let cmap = (display.xlib.XCreateColormap)(display.display, root,
+ visual_infos.visual as *mut _,
+ ffi::AllocNone);
+ display.check_errors().expect("Failed to call XCreateColormap");
+ cmap
+ };
+
+ // subscribe to particular events (*not* the same as new() )
+ let mut set_win_attr = {
+ let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
+ swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask |
+ ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
+ ffi::KeyReleaseMask | ffi::ButtonPressMask |
+ ffi::ButtonReleaseMask | ffi::KeymapStateMask;
+ swa.override_redirect = 0;
+ swa
+ };
+
+ let window_attributes = ffi::CWEventMask;
+
+ /* XXX: This seems important, but results in failure
+ unsafe {
+ (display.xlib.XChangeWindowAttributes)(display.display, window_id, window_attributes, &mut set_win_attr);
+ }
+ display.check_errors().expect("Failed to (re)configure existing X window");
+ */
+
+ // creating window, step 2
+ let wm_delete_window = unsafe {
+ let mut wm_delete_window = with_c_str("WM_DELETE_WINDOW", |delete_window|
+ (display.xlib.XInternAtom)(display.display, delete_window, 0)
+ );
+ display.check_errors().expect("Failed to call XInternAtom");
+ (display.xlib.XSetWMProtocols)(display.display, window, &mut wm_delete_window, 1);
+ display.check_errors().expect("Failed to call XSetWMProtocols");
+ (display.xlib.XFlush)(display.display);
+ display.check_errors().expect("Failed to call XFlush");
+
+ wm_delete_window
+ };
+
+ // creating IM
+ let im = unsafe {
+ let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
+
+ let im = (display.xlib.XOpenIM)(display.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
+ if im.is_null() {
+ return Err(OsError(format!("XOpenIM failed")));
+ }
+ im
+ };
+
+ // creating input context
+ let ic = unsafe {
+ let ic = with_c_str("inputStyle", |input_style|
+ with_c_str("clientWindow", |client_window|
+ (display.xlib.XCreateIC)(
+ im, input_style,
+ ffi::XIMPreeditNothing | ffi::XIMStatusNothing, client_window,
+ window, ptr::null::<()>()
+ )
+ )
+ );
+ if ic.is_null() {
+ return Err(OsError(format!("XCreateIC failed")));
+ }
+ (display.xlib.XSetICFocus)(ic);
+ display.check_errors().expect("Failed to call XSetICFocus");
+ ic
+ };
+
+ // Attempt to make keyboard input repeat detectable
+ unsafe {
+ let mut supported_ptr = ffi::False;
+ (display.xlib.XkbSetDetectableAutoRepeat)(display.display, ffi::True, &mut supported_ptr);
+ if supported_ptr == ffi::False {
+ return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
+ }
+ }
+
+ // finish creating the OpenGL context
+ let context = match context {
+ Prototype::Glx(ctxt) => {
+ Context::Glx(try!(ctxt.finish(window)))
+ },
+ Prototype::Egl(ctxt) => {
+ Context::Egl(try!(ctxt.finish(window as *const libc::c_void)))
+ },
+ };
+
+ // creating the OpenGL can produce errors, but since everything is checked we ignore
+ display.ignore_error();
+
+ // creating the window object
+ let window_proxy_data = WindowProxyData {
+ display: display.clone(),
+ window: window,
+ };
+ let window_proxy_data = Arc::new(Mutex::new(Some(window_proxy_data)));
+
+ let window = Window {
+ x: Arc::new(XWindow {
+ display: display.clone(),
+ window: window,
+ im: im,
+ ic: ic,
+ context: context,
+ screen_id: screen_id,
+ is_fullscreen: is_fullscreen,
+ xf86_desk_mode: xf86_desk_mode,
+ colormap: cmap,
+ window_proxy_data: window_proxy_data,
+ }),
+ is_closed: AtomicBool::new(false),
+ wm_delete_window: wm_delete_window,
+ current_size: Cell::new((0, 0)),
+ pending_events: Mutex::new(VecDeque::new()),
+ cursor_state: Mutex::new(CursorState::Normal),
+ input_handler: Mutex::new(XInputEventHandler::new(display, window, ic, window_attrs))
+ };
+
+ // Don't set title or "make visible"
+
+ // returning
+ Ok(window)
+ }
+
pub fn set_title(&self, title: &str) {
let wm_name = unsafe {
(self.x.display.xlib.XInternAtom)(self.x.display.display, b"_NET_WM_NAME\0".as_ptr() as *const _, 0)
diff --git a/src/os/unix.rs b/src/os/unix.rs
index 61c89bc..0d89571 100644
--- a/src/os/unix.rs
+++ b/src/os/unix.rs
@@ -1,5 +1,7 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
+use std::os::raw::c_ulong;
+
use libc;
use Window;
use platform::Window as LinuxWindow;
@@ -41,9 +43,16 @@ impl WindowExt for Window {
}
/// Additional methods on `WindowBuilder` that are specific to Unix.
-pub trait WindowBuilderExt {
-
+pub trait WindowBuilderExt<'a> {
+ fn from_existing_window(mut self, window_id: c_ulong) -> WindowBuilder<'a>;
}
-impl<'a> WindowBuilderExt for WindowBuilder<'a> {
+impl<'a> WindowBuilderExt<'a> for WindowBuilder<'a> {
+
+ /// Tells this (UNIX/X11) WindowBuilder to use an existing X window (eg,
+ /// one created by another application) instead of creating a new window.
+ fn from_existing_window(mut self, window_id: c_ulong) -> WindowBuilder<'a> {
+ self.platform_specific.existing_x11_window_id = Some(window_id);
+ self
+ }
}
diff --git a/src/platform/linux/api_dispatch.rs b/src/platform/linux/api_dispatch.rs
index 54e0186..3a2a1e8 100644
--- a/src/platform/linux/api_dispatch.rs
+++ b/src/platform/linux/api_dispatch.rs
@@ -21,9 +21,14 @@ use api::x11;
use api::x11::XConnection;
use api::x11::XError;
use api::x11::XNotSupported;
+use std::os::raw::c_ulong;
#[derive(Clone, Default)]
-pub struct PlatformSpecificWindowBuilderAttributes;
+pub struct PlatformSpecificWindowBuilderAttributes {
+ /// Optionally tells the WindowBuilder to re-use an existing X window with
+ /// the given Window id number
+ pub existing_x11_window_id: Option<c_ulong>,
+}
enum Backend {
X(Arc<XConnection>),
@@ -175,7 +180,7 @@ impl<'a> Iterator for WaitEventsIterator<'a> {
impl Window {
#[inline]
pub fn new(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
- opengl: &GlAttributes<&Window>, _: &PlatformSpecificWindowBuilderAttributes)
+ opengl: &GlAttributes<&Window>, platform: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
match *BACKEND {
@@ -194,7 +199,10 @@ impl Window {
_ => panic!() // TODO: return an error
});
- x11::Window::new(connec, window, pf_reqs, &opengl).map(Window::X)
+ match platform.existing_x11_window_id {
+ None => x11::Window::new(connec, window, pf_reqs, &opengl).map(Window::X),
+ Some(window_id) => x11::Window::from_existing_window(connec, window, pf_reqs, &opengl, window_id).map(Window::X),
+ }
},
Backend::Error(ref error) => Err(CreationError::NoBackendAvailable(Box::new(error.clone())))