diff --git a/src/platform/linux/x11/window.rs b/src/platform/linux/x11/window.rs index b6a8ca37..50131588 100644 --- a/src/platform/linux/x11/window.rs +++ b/src/platform/linux/x11/window.rs @@ -3,7 +3,7 @@ use CreationError; use CreationError::OsError; use libc; use std::borrow::Borrow; -use std::{mem, cmp}; +use std::{mem, cmp, ptr}; use std::sync::{Arc, Mutex}; use std::os::raw::{c_int, c_long, c_uchar, c_ulong, c_void}; use std::thread; @@ -53,6 +53,42 @@ pub struct XWindow { screen_id: i32, } +impl XWindow { + /// Get parent window of `child` + /// + /// This method can return None if underlying xlib call fails. + /// + /// # Unsafety + /// + /// `child` must be a valid `Window`. + unsafe fn get_parent_window(&self, child: ffi::Window) -> Option { + let mut root: ffi::Window = mem::uninitialized(); + let mut parent: ffi::Window = mem::uninitialized(); + let mut children: *mut ffi::Window = ptr::null_mut(); + let mut nchildren: libc::c_uint = mem::uninitialized(); + + let res = (self.display.xlib.XQueryTree)( + self.display.display, + child, + &mut root, + &mut parent, + &mut children, + &mut nchildren + ); + + if res == 0 { + return None; + } + + // The list of children isn't used + if children != ptr::null_mut() { + (self.display.xlib.XFree)(children as *mut _); + } + + Some(parent) + } +} + unsafe impl Send for XWindow {} unsafe impl Sync for XWindow {} @@ -497,7 +533,29 @@ impl Window2 { let mut border: libc::c_uint = mem::uninitialized(); let mut depth: libc::c_uint = mem::uninitialized(); - if (self.x.display.xlib.XGetGeometry)(self.x.display.display, self.x.window, + + // Some window managers like i3wm will actually nest application + // windows (like those opened by winit) within other windows to, for + // example, add decorations. Initially when debugging this method on + // i3, the x and y positions were always returned as "2". + // + // The solution that other xlib abstractions use is to climb up the + // window hierarchy until just below the root window, and that + // window must be used to determine the appropriate position. + let window = { + let root = self.x.root; + let mut window = self.x.window; + loop { + let candidate = self.x.get_parent_window(window).unwrap(); + if candidate == root { + break window; + } + + window = candidate; + } + }; + + if (self.x.display.xlib.XGetGeometry)(self.x.display.display, window, &mut root, &mut x, &mut y, &mut width, &mut height, &mut border, &mut depth) == 0 {