diff options
-rw-r--r-- | src/api/x11/window.rs | 232 | ||||
-rw-r--r-- | src/os/unix.rs | 15 | ||||
-rw-r--r-- | src/platform/linux/api_dispatch.rs | 14 |
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()))) |