2017-04-23 06:52:35 +10:00
|
|
|
use MouseCursor;
|
2015-05-17 19:19:06 +10:00
|
|
|
use CreationError;
|
|
|
|
use CreationError::OsError;
|
|
|
|
use libc;
|
2015-06-23 07:49:48 +10:00
|
|
|
use std::borrow::Borrow;
|
2016-03-26 05:52:57 +11:00
|
|
|
use std::{mem, ptr, cmp};
|
2015-05-17 19:19:06 +10:00
|
|
|
use std::sync::{Arc, Mutex};
|
2017-04-23 06:52:35 +10:00
|
|
|
use std::os::raw::{c_int, c_long, c_uchar};
|
2016-01-17 04:11:06 +11:00
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
2015-05-17 19:19:06 +10:00
|
|
|
|
|
|
|
use CursorState;
|
2015-09-21 19:52:21 +10:00
|
|
|
use WindowAttributes;
|
2017-08-28 09:22:26 +10:00
|
|
|
use FullScreenState;
|
2017-01-07 07:59:35 +11:00
|
|
|
use platform::PlatformSpecificWindowBuilderAttributes;
|
2015-05-17 19:19:06 +10:00
|
|
|
|
2015-09-24 17:11:59 +10:00
|
|
|
use platform::MonitorId as PlatformMonitorId;
|
2015-05-17 19:19:06 +10:00
|
|
|
|
2015-06-29 07:09:26 +10:00
|
|
|
use super::{ffi};
|
2017-04-23 06:52:35 +10:00
|
|
|
use super::{MonitorId, XConnection, WindowId, EventsLoop};
|
2015-05-17 19:19:06 +10:00
|
|
|
|
|
|
|
// TODO: remove me
|
|
|
|
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
|
|
|
|
use std::ffi::CString;
|
|
|
|
let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
|
|
|
|
f(c_str.as_ptr())
|
|
|
|
}
|
|
|
|
|
2015-07-17 08:07:46 +10:00
|
|
|
struct WindowProxyData {
|
|
|
|
display: Arc<XConnection>,
|
|
|
|
window: ffi::Window,
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for WindowProxyData {}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
pub struct XWindow {
|
|
|
|
display: Arc<XConnection>,
|
|
|
|
window: ffi::Window,
|
2017-08-28 09:19:26 +10:00
|
|
|
root: ffi::Window,
|
2015-05-17 19:19:06 +10:00
|
|
|
is_fullscreen: bool,
|
|
|
|
screen_id: libc::c_int,
|
2016-01-19 16:39:08 +11:00
|
|
|
xf86_desk_mode: Option<ffi::XF86VidModeModeInfo>,
|
2015-07-17 08:07:46 +10:00
|
|
|
window_proxy_data: Arc<Mutex<Option<WindowProxyData>>>,
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for XWindow {}
|
|
|
|
unsafe impl Sync for XWindow {}
|
|
|
|
|
|
|
|
unsafe impl Send for Window {}
|
|
|
|
unsafe impl Sync for Window {}
|
|
|
|
|
|
|
|
impl Drop for XWindow {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2015-07-17 08:07:46 +10:00
|
|
|
// Clear out the window proxy data arc, so that any window proxy objects
|
|
|
|
// are no longer able to send messages to this window.
|
|
|
|
*self.window_proxy_data.lock().unwrap() = None;
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
if self.is_fullscreen {
|
2016-01-19 16:39:08 +11:00
|
|
|
if let Some(mut xf86_desk_mode) = self.xf86_desk_mode {
|
|
|
|
(self.display.xf86vmode.XF86VidModeSwitchToMode)(self.display.display, self.screen_id, &mut xf86_desk_mode);
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
(self.display.xf86vmode.XF86VidModeSetViewPort)(self.display.display, self.screen_id, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
(self.display.xlib.XDestroyWindow)(self.display.display, self.window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct WindowProxy {
|
2015-07-17 08:07:46 +10:00
|
|
|
data: Arc<Mutex<Option<WindowProxyData>>>,
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl WindowProxy {
|
|
|
|
pub fn wakeup_event_loop(&self) {
|
2015-07-17 08:07:46 +10:00
|
|
|
let window_proxy_data = self.data.lock().unwrap();
|
|
|
|
|
|
|
|
if let Some(ref data) = *window_proxy_data {
|
|
|
|
let mut xev = ffi::XClientMessageEvent {
|
|
|
|
type_: ffi::ClientMessage,
|
|
|
|
window: data.window,
|
|
|
|
format: 32,
|
|
|
|
message_type: 0,
|
|
|
|
serial: 0,
|
|
|
|
send_event: 0,
|
|
|
|
display: data.display.display,
|
|
|
|
data: unsafe { mem::zeroed() },
|
|
|
|
};
|
2015-05-17 19:19:06 +10:00
|
|
|
|
2015-07-17 08:07:46 +10:00
|
|
|
unsafe {
|
|
|
|
(data.display.xlib.XSendEvent)(data.display.display, data.window, 0, 0, mem::transmute(&mut xev));
|
|
|
|
(data.display.xlib.XFlush)(data.display.display);
|
2015-12-24 20:57:08 +11:00
|
|
|
data.display.check_errors().expect("Failed to call XSendEvent after wakeup");
|
2015-07-17 08:07:46 +10:00
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Window {
|
|
|
|
pub x: Arc<XWindow>,
|
|
|
|
cursor_state: Mutex<CursorState>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Window {
|
2017-04-23 06:52:35 +10:00
|
|
|
pub fn new(ctx: &EventsLoop, window_attrs: &WindowAttributes,
|
2017-01-07 07:59:35 +11:00
|
|
|
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
2015-09-21 19:52:21 +10:00
|
|
|
-> Result<Window, CreationError>
|
|
|
|
{
|
2017-04-23 06:52:35 +10:00
|
|
|
let display = &ctx.display;
|
2016-03-26 05:52:57 +11:00
|
|
|
let dimensions = {
|
|
|
|
|
|
|
|
// x11 only applies constraints when the window is actively resized
|
|
|
|
// by the user, so we have to manually apply the initial constraints
|
|
|
|
let mut dimensions = window_attrs.dimensions.unwrap_or((800, 600));
|
|
|
|
if let Some(max) = window_attrs.max_dimensions {
|
|
|
|
dimensions.0 = cmp::min(dimensions.0, max.0);
|
|
|
|
dimensions.1 = cmp::min(dimensions.1, max.1);
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
|
2016-03-26 05:52:57 +11:00
|
|
|
if let Some(min) = window_attrs.min_dimensions {
|
|
|
|
dimensions.0 = cmp::max(dimensions.0, min.0);
|
|
|
|
dimensions.1 = cmp::max(dimensions.1, min.1);
|
|
|
|
}
|
|
|
|
dimensions
|
|
|
|
|
|
|
|
};
|
2015-11-09 22:18:48 +11:00
|
|
|
|
2017-01-07 07:59:35 +11:00
|
|
|
let screen_id = match pl_attribs.screen_id {
|
|
|
|
Some(id) => id,
|
2017-08-28 09:22:26 +10:00
|
|
|
None => match window_attrs.fullscreen {
|
|
|
|
FullScreenState::Exclusive(PlatformMonitorId::X(MonitorId(_, monitor))) => monitor as i32,
|
2017-01-07 07:59:35 +11:00
|
|
|
_ => unsafe { (display.xlib.XDefaultScreen)(display.display) },
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
};
|
|
|
|
|
2017-08-28 09:22:26 +10:00
|
|
|
let is_fullscreen = window_attrs.fullscreen.get_monitor().is_some();
|
|
|
|
|
2015-06-27 00:47:39 +10:00
|
|
|
// finding the mode to switch to if necessary
|
|
|
|
let (mode_to_switch_to, xf86_desk_mode) = unsafe {
|
2015-05-17 19:19:06 +10:00
|
|
|
let mut mode_num: libc::c_int = mem::uninitialized();
|
|
|
|
let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized();
|
|
|
|
if (display.xf86vmode.XF86VidModeGetAllModeLines)(display.display, screen_id, &mut mode_num, &mut modes) == 0 {
|
2016-01-19 16:39:08 +11:00
|
|
|
(None, None)
|
|
|
|
} else {
|
|
|
|
let xf86_desk_mode: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(0));
|
2017-08-28 09:22:26 +10:00
|
|
|
let mode_to_switch_to = if is_fullscreen {
|
2016-01-19 16:39:08 +11:00
|
|
|
let matching_mode = (0 .. mode_num).map(|i| {
|
2015-06-27 00:47:39 +10:00
|
|
|
let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m
|
2016-01-19 16:39:08 +11:00
|
|
|
}).find(|m| m.hdisplay == dimensions.0 as u16 && m.vdisplay == dimensions.1 as u16);
|
|
|
|
if let Some(matching_mode) = matching_mode {
|
|
|
|
Some(matching_mode)
|
|
|
|
} else {
|
|
|
|
let m = (0 .. mode_num).map(|i| {
|
|
|
|
let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m
|
|
|
|
}).find(|m| m.hdisplay >= dimensions.0 as u16 && m.vdisplay >= dimensions.1 as u16);
|
2016-03-22 05:42:54 +11:00
|
|
|
|
2016-01-19 16:39:08 +11:00
|
|
|
match m {
|
|
|
|
Some(m) => Some(m),
|
|
|
|
None => return Err(OsError(format!("Could not find a suitable graphics mode")))
|
|
|
|
}
|
2015-06-27 00:47:39 +10:00
|
|
|
}
|
2016-01-19 16:39:08 +11:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
(display.xlib.XFree)(modes as *mut _);
|
|
|
|
(mode_to_switch_to, Some(xf86_desk_mode))
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
// getting the root window
|
2017-06-17 22:59:56 +10:00
|
|
|
let root = ctx.root;
|
2015-05-17 19:19:06 +10:00
|
|
|
|
|
|
|
// creating
|
|
|
|
let mut set_win_attr = {
|
|
|
|
let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
|
2017-01-07 07:59:35 +11:00
|
|
|
swa.colormap = if let Some(vi) = pl_attribs.visual_infos {
|
|
|
|
unsafe {
|
|
|
|
let visual = vi.visual;
|
|
|
|
(display.xlib.XCreateColormap)(display.display, root, visual, ffi::AllocNone)
|
|
|
|
}
|
|
|
|
} else { 0 };
|
2015-05-17 19:19:06 +10:00
|
|
|
swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask |
|
|
|
|
ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
|
|
|
|
ffi::KeyReleaseMask | ffi::ButtonPressMask |
|
|
|
|
ffi::ButtonReleaseMask | ffi::KeymapStateMask;
|
|
|
|
swa.border_pixel = 0;
|
2015-09-21 19:52:21 +10:00
|
|
|
if window_attrs.transparent {
|
2015-06-15 07:20:32 +10:00
|
|
|
swa.background_pixel = 0;
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
swa.override_redirect = 0;
|
|
|
|
swa
|
|
|
|
};
|
|
|
|
|
2015-06-06 00:03:07 +10:00
|
|
|
let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask;
|
2015-06-15 07:20:32 +10:00
|
|
|
|
2015-09-21 19:52:21 +10:00
|
|
|
if window_attrs.transparent {
|
2015-06-15 07:20:32 +10:00
|
|
|
window_attributes |= ffi::CWBackPixel;
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
// finally creating the window
|
|
|
|
let window = unsafe {
|
|
|
|
let win = (display.xlib.XCreateWindow)(display.display, root, 0, 0, dimensions.0 as libc::c_uint,
|
2017-01-07 07:59:35 +11:00
|
|
|
dimensions.1 as libc::c_uint, 0,
|
|
|
|
match pl_attribs.visual_infos {
|
|
|
|
Some(vi) => vi.depth,
|
|
|
|
None => ffi::CopyFromParent
|
|
|
|
},
|
|
|
|
ffi::InputOutput as libc::c_uint,
|
|
|
|
match pl_attribs.visual_infos {
|
|
|
|
Some(vi) => vi.visual,
|
|
|
|
None => ffi::CopyFromParent as *mut _
|
|
|
|
},
|
|
|
|
window_attributes,
|
2015-05-17 19:19:06 +10:00
|
|
|
&mut set_win_attr);
|
2015-12-24 20:57:08 +11:00
|
|
|
display.check_errors().expect("Failed to call XCreateWindow");
|
2015-05-17 19:19:06 +10:00
|
|
|
win
|
|
|
|
};
|
|
|
|
|
2017-05-05 07:34:45 +10:00
|
|
|
// Set ICCCM WM_CLASS property based on initial window title
|
|
|
|
// Must be done *before* mapping the window by ICCCM 4.1.2.5
|
|
|
|
unsafe {
|
|
|
|
with_c_str(&*window_attrs.title, |c_name| {
|
|
|
|
let hint = (display.xlib.XAllocClassHint)();
|
|
|
|
(*hint).res_name = c_name as *mut libc::c_char;
|
|
|
|
(*hint).res_class = c_name as *mut libc::c_char;
|
|
|
|
(display.xlib.XSetClassHint)(display.display, window, hint);
|
|
|
|
display.check_errors().expect("Failed to call XSetClassHint");
|
|
|
|
(display.xlib.XFree)(hint as *mut _);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
// set visibility
|
2015-09-21 19:52:21 +10:00
|
|
|
if window_attrs.visible {
|
2015-05-17 19:19:06 +10:00
|
|
|
unsafe {
|
|
|
|
(display.xlib.XMapRaised)(display.display, window);
|
|
|
|
(display.xlib.XFlush)(display.display);
|
|
|
|
}
|
2015-12-24 20:57:08 +11:00
|
|
|
|
|
|
|
display.check_errors().expect("Failed to set window visibility");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
2017-04-23 06:52:35 +10:00
|
|
|
// Opt into handling window close
|
|
|
|
unsafe {
|
|
|
|
(display.xlib.XSetWMProtocols)(display.display, window, &ctx.wm_delete_window as *const _ as *mut _, 1);
|
2015-12-24 20:57:08 +11:00
|
|
|
display.check_errors().expect("Failed to call XSetWMProtocols");
|
2015-05-17 19:19:06 +10:00
|
|
|
(display.xlib.XFlush)(display.display);
|
2015-12-24 20:57:08 +11:00
|
|
|
display.check_errors().expect("Failed to call XFlush");
|
2017-04-23 06:52:35 +10:00
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
|
|
|
|
// 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")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 18:40:37 +11:00
|
|
|
if is_fullscreen {
|
2017-08-28 09:19:26 +10:00
|
|
|
Window::set_netwm(display, window, root, "_NET_WM_STATE_FULLSCREEN", true);
|
2015-10-27 18:07:37 +11:00
|
|
|
|
|
|
|
if let Some(mut mode_to_switch_to) = mode_to_switch_to {
|
|
|
|
unsafe {
|
|
|
|
(display.xf86vmode.XF86VidModeSwitchToMode)(
|
|
|
|
display.display,
|
|
|
|
screen_id,
|
|
|
|
&mut mode_to_switch_to
|
|
|
|
);
|
2015-12-24 20:57:08 +11:00
|
|
|
display.check_errors().expect("Failed to call XF86VidModeSwitchToMode");
|
2015-10-27 18:07:37 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
println!("[glutin] Unexpected state: `mode` is None creating fullscreen window");
|
|
|
|
}
|
|
|
|
unsafe {
|
|
|
|
(display.xf86vmode.XF86VidModeSetViewPort)(display.display, screen_id, 0, 0);
|
2015-12-24 20:57:08 +11:00
|
|
|
display.check_errors().expect("Failed to call XF86VidModeSetViewPort");
|
2015-10-27 18:07:37 +11:00
|
|
|
}
|
2016-03-07 06:47:10 +11:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// set size hints
|
|
|
|
let mut size_hints: ffi::XSizeHints = unsafe { mem::zeroed() };
|
|
|
|
size_hints.flags = ffi::PSize;
|
|
|
|
size_hints.width = dimensions.0 as i32;
|
|
|
|
size_hints.height = dimensions.1 as i32;
|
|
|
|
|
|
|
|
if let Some(dimensions) = window_attrs.min_dimensions {
|
|
|
|
size_hints.flags |= ffi::PMinSize;
|
|
|
|
size_hints.min_width = dimensions.0 as i32;
|
|
|
|
size_hints.min_height = dimensions.1 as i32;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(dimensions) = window_attrs.max_dimensions {
|
|
|
|
size_hints.flags |= ffi::PMaxSize;
|
|
|
|
size_hints.max_width = dimensions.0 as i32;
|
|
|
|
size_hints.max_height = dimensions.1 as i32;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
(display.xlib.XSetNormalHints)(display.display, window, &mut size_hints);
|
|
|
|
display.check_errors().expect("Failed to call XSetNormalHints");
|
|
|
|
}
|
|
|
|
|
2015-10-26 18:40:37 +11:00
|
|
|
}
|
|
|
|
|
2017-04-23 06:52:35 +10:00
|
|
|
// Select XInput2 events
|
|
|
|
{
|
|
|
|
let mask = ffi::XI_MotionMask
|
|
|
|
| ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask
|
|
|
|
// | ffi::XI_KeyPressMask | ffi::XI_KeyReleaseMask
|
|
|
|
| ffi::XI_EnterMask | ffi::XI_LeaveMask
|
|
|
|
| ffi::XI_FocusInMask | ffi::XI_FocusOutMask
|
|
|
|
| if window_attrs.multitouch { ffi::XI_TouchBeginMask | ffi::XI_TouchUpdateMask | ffi::XI_TouchEndMask } else { 0 };
|
|
|
|
unsafe {
|
|
|
|
let mut event_mask = ffi::XIEventMask{
|
|
|
|
deviceid: ffi::XIAllMasterDevices,
|
|
|
|
mask: mem::transmute::<*const i32, *mut c_uchar>(&mask as *const i32),
|
|
|
|
mask_len: mem::size_of_val(&mask) as c_int,
|
|
|
|
};
|
|
|
|
(display.xinput2.XISelectEvents)(display.display, window,
|
|
|
|
&mut event_mask as *mut ffi::XIEventMask, 1);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
// creating the window object
|
2015-07-17 08:07:46 +10:00
|
|
|
let window_proxy_data = WindowProxyData {
|
|
|
|
display: display.clone(),
|
|
|
|
window: window,
|
|
|
|
};
|
|
|
|
let window_proxy_data = Arc::new(Mutex::new(Some(window_proxy_data)));
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
let window = Window {
|
|
|
|
x: Arc::new(XWindow {
|
|
|
|
display: display.clone(),
|
|
|
|
window: window,
|
2017-08-28 09:19:26 +10:00
|
|
|
root: root,
|
2015-05-17 19:19:06 +10:00
|
|
|
screen_id: screen_id,
|
|
|
|
is_fullscreen: is_fullscreen,
|
|
|
|
xf86_desk_mode: xf86_desk_mode,
|
2015-07-17 08:07:46 +10:00
|
|
|
window_proxy_data: window_proxy_data,
|
2015-05-17 19:19:06 +10:00
|
|
|
}),
|
|
|
|
cursor_state: Mutex::new(CursorState::Normal),
|
|
|
|
};
|
|
|
|
|
2016-01-18 04:40:31 +11:00
|
|
|
window.set_title(&window_attrs.title);
|
2017-03-06 04:27:03 +11:00
|
|
|
window.set_decorations(window_attrs.decorations);
|
2017-08-28 10:21:31 +10:00
|
|
|
window.set_maximized(window_attrs.maximized);
|
|
|
|
window.set_fullscreen_windowed(window_attrs.fullscreen == FullScreenState::Windowed);
|
2016-01-18 04:40:31 +11:00
|
|
|
|
2015-12-24 21:58:50 +11:00
|
|
|
if window_attrs.visible {
|
|
|
|
unsafe {
|
|
|
|
let ref x_window: &XWindow = window.x.borrow();
|
|
|
|
|
|
|
|
// XSetInputFocus generates an error if the window is not visible,
|
2016-01-17 04:11:06 +11:00
|
|
|
// therefore we wait until it's the case.
|
|
|
|
loop {
|
|
|
|
let mut window_attributes = mem::uninitialized();
|
|
|
|
(display.xlib.XGetWindowAttributes)(display.display, x_window.window, &mut window_attributes);
|
|
|
|
display.check_errors().expect("Failed to call XGetWindowAttributes");
|
|
|
|
|
|
|
|
if window_attributes.map_state == ffi::IsViewable {
|
|
|
|
(display.xlib.XSetInputFocus)(
|
|
|
|
display.display,
|
|
|
|
x_window.window,
|
|
|
|
ffi::RevertToParent,
|
|
|
|
ffi::CurrentTime
|
|
|
|
);
|
|
|
|
display.check_errors().expect("Failed to call XSetInputFocus");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait about a frame to avoid too-busy waiting
|
|
|
|
thread::sleep(Duration::from_millis(16));
|
|
|
|
}
|
2015-12-24 21:58:50 +11:00
|
|
|
}
|
2015-10-26 12:56:49 +11:00
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
// returning
|
|
|
|
Ok(window)
|
|
|
|
}
|
|
|
|
|
2017-08-28 09:19:26 +10:00
|
|
|
fn set_netwm(display: &Arc<XConnection>, window: u64, root: u64, property: &str, val: bool) {
|
|
|
|
let state_atom = unsafe {
|
|
|
|
with_c_str("_NET_WM_STATE", |state|
|
|
|
|
(display.xlib.XInternAtom)(display.display, state, 0)
|
|
|
|
)
|
|
|
|
};
|
|
|
|
display.check_errors().expect("Failed to call XInternAtom");
|
|
|
|
let atom = unsafe {
|
|
|
|
with_c_str(property, |state|
|
|
|
|
(display.xlib.XInternAtom)(display.display, state, 0)
|
|
|
|
)
|
|
|
|
};
|
|
|
|
display.check_errors().expect("Failed to call XInternAtom");
|
|
|
|
|
|
|
|
let client_message_event = ffi::XClientMessageEvent {
|
|
|
|
type_: ffi::ClientMessage,
|
|
|
|
serial: 0,
|
|
|
|
send_event: 1, // true because we are sending this through `XSendEvent`
|
|
|
|
display: display.display,
|
|
|
|
window: window,
|
|
|
|
message_type: state_atom, // the _NET_WM_STATE atom is sent to change the state of a window
|
|
|
|
format: 32, // view `data` as `c_long`s
|
|
|
|
data: {
|
|
|
|
let mut data = ffi::ClientMessageData::new();
|
|
|
|
// This first `long` is the action; `1` means add/set following property.
|
|
|
|
data.set_long(0, val as i64);
|
|
|
|
// This second `long` is the property to set (fullscreen)
|
|
|
|
data.set_long(1, atom as c_long);
|
|
|
|
data
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut x_event = ffi::XEvent::from(client_message_event);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
(display.xlib.XSendEvent)(
|
|
|
|
display.display,
|
|
|
|
root,
|
|
|
|
0,
|
|
|
|
ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask,
|
|
|
|
&mut x_event as *mut _
|
|
|
|
);
|
|
|
|
display.check_errors().expect("Failed to call XSendEvent");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_fullscreen_windowed(&self, fullscreen: bool) {
|
|
|
|
Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_FULLSCREEN", fullscreen);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_maximized(&self, maximized: bool) {
|
|
|
|
Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_MAXIMIZED_HORZ", maximized);
|
|
|
|
Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_MAXIMIZED_VERT", maximized);
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn set_title(&self, title: &str) {
|
2016-01-18 04:02:40 +11:00
|
|
|
let wm_name = unsafe {
|
|
|
|
(self.x.display.xlib.XInternAtom)(self.x.display.display, b"_NET_WM_NAME\0".as_ptr() as *const _, 0)
|
|
|
|
};
|
|
|
|
self.x.display.check_errors().expect("Failed to call XInternAtom");
|
|
|
|
|
|
|
|
let wm_utf8_string = unsafe {
|
|
|
|
(self.x.display.xlib.XInternAtom)(self.x.display.display, b"UTF8_STRING\0".as_ptr() as *const _, 0)
|
|
|
|
};
|
|
|
|
self.x.display.check_errors().expect("Failed to call XInternAtom");
|
|
|
|
|
|
|
|
with_c_str(title, |c_title| unsafe {
|
|
|
|
(self.x.display.xlib.XStoreName)(self.x.display.display, self.x.window, c_title);
|
|
|
|
|
|
|
|
let len = title.as_bytes().len();
|
|
|
|
(self.x.display.xlib.XChangeProperty)(self.x.display.display, self.x.window,
|
|
|
|
wm_name, wm_utf8_string, 8, ffi::PropModeReplace,
|
|
|
|
c_title as *const u8, len as libc::c_int);
|
2015-05-17 19:19:06 +10:00
|
|
|
(self.x.display.xlib.XFlush)(self.x.display.display);
|
2015-12-24 20:57:08 +11:00
|
|
|
});
|
2016-01-18 04:02:40 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to set window title");
|
2015-12-24 20:57:08 +11:00
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
2017-03-06 04:27:03 +11:00
|
|
|
pub fn set_decorations(&self, decorations: bool) {
|
|
|
|
#[repr(C)]
|
|
|
|
struct MotifWindowHints {
|
|
|
|
flags: u32,
|
|
|
|
functions: u32,
|
|
|
|
decorations: u32,
|
|
|
|
input_mode: i32,
|
|
|
|
status: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let wm_hints = unsafe {
|
|
|
|
(self.x.display.xlib.XInternAtom)(self.x.display.display, b"_MOTIF_WM_HINTS\0".as_ptr() as *const _, 0)
|
|
|
|
};
|
|
|
|
self.x.display.check_errors().expect("Failed to call XInternAtom");
|
|
|
|
|
|
|
|
if !decorations {
|
|
|
|
let hints = MotifWindowHints {
|
|
|
|
flags: 2, // MWM_HINTS_DECORATIONS
|
|
|
|
functions: 0,
|
|
|
|
decorations: 0,
|
|
|
|
input_mode: 0,
|
|
|
|
status: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XChangeProperty)(
|
|
|
|
self.x.display.display, self.x.window,
|
|
|
|
wm_hints, wm_hints, 32 /* Size of elements in struct */,
|
|
|
|
ffi::PropModeReplace, &hints as *const MotifWindowHints as *const u8,
|
|
|
|
5 /* Number of elements in struct */);
|
|
|
|
(self.x.display.xlib.XFlush)(self.x.display.display);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XDeleteProperty)(self.x.display.display, self.x.window, wm_hints);
|
|
|
|
(self.x.display.xlib.XFlush)(self.x.display.display);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.x.display.check_errors().expect("Failed to set decorations");
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn show(&self) {
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XMapRaised)(self.x.display.display, self.x.window);
|
|
|
|
(self.x.display.xlib.XFlush)(self.x.display.display);
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to call XMapRaised");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn hide(&self) {
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XUnmapWindow)(self.x.display.display, self.x.window);
|
|
|
|
(self.x.display.xlib.XFlush)(self.x.display.display);
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to call XUnmapWindow");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_geometry(&self) -> Option<(i32, i32, u32, u32, u32)> {
|
|
|
|
unsafe {
|
|
|
|
use std::mem;
|
|
|
|
|
|
|
|
let mut root: ffi::Window = mem::uninitialized();
|
|
|
|
let mut x: libc::c_int = mem::uninitialized();
|
|
|
|
let mut y: libc::c_int = mem::uninitialized();
|
|
|
|
let mut width: libc::c_uint = mem::uninitialized();
|
|
|
|
let mut height: libc::c_uint = mem::uninitialized();
|
|
|
|
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,
|
|
|
|
&mut root, &mut x, &mut y, &mut width, &mut height,
|
|
|
|
&mut border, &mut depth) == 0
|
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some((x as i32, y as i32, width as u32, height as u32, border as u32))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn get_position(&self) -> Option<(i32, i32)> {
|
|
|
|
self.get_geometry().map(|(x, y, _, _, _)| (x, y))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_position(&self, x: i32, y: i32) {
|
|
|
|
unsafe { (self.x.display.xlib.XMoveWindow)(self.x.display.display, self.x.window, x as libc::c_int, y as libc::c_int); }
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to call XMoveWindow");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
2017-07-27 15:04:04 +10:00
|
|
|
self.get_geometry().map(|(_, _, w, h, _)| ((w as f32 / self.hidpi_factor()) as u32, (h as f32 / self.hidpi_factor()) as u32))
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
|
|
|
self.get_geometry().map(|(_, _, w, h, b)| (w + b, h + b)) // TODO: is this really outside?
|
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-07-24 08:50:25 +10:00
|
|
|
pub fn set_inner_size(&self, x: u32, y: u32) {
|
|
|
|
unsafe { (self.x.display.xlib.XResizeWindow)(self.x.display.display, self.x.window, x as libc::c_uint, y as libc::c_uint); }
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to call XResizeWindow");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn create_window_proxy(&self) -> WindowProxy {
|
|
|
|
WindowProxy {
|
2015-07-17 08:07:46 +10:00
|
|
|
data: self.x.window_proxy_data.clone()
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-26 02:04:55 +10:00
|
|
|
#[inline]
|
|
|
|
pub fn get_xlib_display(&self) -> *mut libc::c_void {
|
|
|
|
self.x.display.display as *mut libc::c_void
|
|
|
|
}
|
|
|
|
|
2016-10-20 03:11:02 +11:00
|
|
|
#[inline]
|
|
|
|
pub fn get_xlib_screen_id(&self) -> *mut libc::c_void {
|
|
|
|
self.x.screen_id as *mut libc::c_void
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get_xlib_xconnection(&self) -> Arc<XConnection> {
|
|
|
|
self.x.display.clone()
|
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn platform_display(&self) -> *mut libc::c_void {
|
|
|
|
self.x.display.display as *mut libc::c_void
|
|
|
|
}
|
|
|
|
|
2015-09-26 02:04:55 +10:00
|
|
|
#[inline]
|
|
|
|
pub fn get_xlib_window(&self) -> *mut libc::c_void {
|
|
|
|
self.x.window as *mut libc::c_void
|
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn platform_window(&self) -> *mut libc::c_void {
|
|
|
|
self.x.window as *mut libc::c_void
|
|
|
|
}
|
|
|
|
|
2016-07-31 07:58:28 +10:00
|
|
|
pub fn get_xcb_connection(&self) -> *mut libc::c_void {
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib_xcb.XGetXCBConnection)(self.get_xlib_display() as *mut _) as *mut _
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-21 22:42:05 +10:00
|
|
|
#[inline]
|
2015-05-17 19:19:06 +10:00
|
|
|
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
|
|
|
unsafe {
|
2016-04-18 02:14:53 +10:00
|
|
|
let load = |name: &str| {
|
|
|
|
self.load_cursor(name)
|
2016-04-17 04:37:53 +10:00
|
|
|
};
|
2016-04-18 02:14:53 +10:00
|
|
|
|
|
|
|
let loadn = |names: &[&str]| {
|
|
|
|
self.load_first_existing_cursor(names)
|
2016-04-17 04:37:53 +10:00
|
|
|
};
|
2016-04-18 02:14:53 +10:00
|
|
|
|
2016-04-17 04:37:53 +10:00
|
|
|
// Try multiple names in some cases where the name
|
|
|
|
// differs on the desktop environments or themes.
|
|
|
|
//
|
|
|
|
// Try the better looking (or more suiting) names first.
|
2017-03-04 21:14:08 +11:00
|
|
|
let xcursor = match cursor {
|
2016-04-17 04:37:53 +10:00
|
|
|
MouseCursor::Alias => load("link"),
|
|
|
|
MouseCursor::Arrow => load("arrow"),
|
|
|
|
MouseCursor::Cell => load("plus"),
|
|
|
|
MouseCursor::Copy => load("copy"),
|
|
|
|
MouseCursor::Crosshair => load("crosshair"),
|
|
|
|
MouseCursor::Default => load("left_ptr"),
|
2017-01-25 01:35:38 +11:00
|
|
|
MouseCursor::Hand => loadn(&["hand2", "hand1"]),
|
2016-04-17 04:37:53 +10:00
|
|
|
MouseCursor::Help => load("question_arrow"),
|
|
|
|
MouseCursor::Move => load("move"),
|
|
|
|
MouseCursor::Grab => loadn(&["openhand", "grab"]),
|
|
|
|
MouseCursor::Grabbing => loadn(&["closedhand", "grabbing"]),
|
|
|
|
MouseCursor::Progress => load("left_ptr_watch"),
|
|
|
|
MouseCursor::AllScroll => load("all-scroll"),
|
|
|
|
MouseCursor::ContextMenu => load("context-menu"),
|
|
|
|
|
|
|
|
MouseCursor::NoDrop => loadn(&["no-drop", "circle"]),
|
|
|
|
MouseCursor::NotAllowed => load("crossed_circle"),
|
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
|
|
|
|
/// Resize cursors
|
2016-04-17 04:37:53 +10:00
|
|
|
MouseCursor::EResize => load("right_side"),
|
|
|
|
MouseCursor::NResize => load("top_side"),
|
|
|
|
MouseCursor::NeResize => load("top_right_corner"),
|
|
|
|
MouseCursor::NwResize => load("top_left_corner"),
|
|
|
|
MouseCursor::SResize => load("bottom_side"),
|
|
|
|
MouseCursor::SeResize => load("bottom_right_corner"),
|
|
|
|
MouseCursor::SwResize => load("bottom_left_corner"),
|
|
|
|
MouseCursor::WResize => load("left_side"),
|
|
|
|
MouseCursor::EwResize => load("h_double_arrow"),
|
|
|
|
MouseCursor::NsResize => load("v_double_arrow"),
|
|
|
|
MouseCursor::NwseResize => loadn(&["bd_double_arrow", "size_bdiag"]),
|
|
|
|
MouseCursor::NeswResize => loadn(&["fd_double_arrow", "size_fdiag"]),
|
|
|
|
MouseCursor::ColResize => loadn(&["split_h", "h_double_arrow"]),
|
|
|
|
MouseCursor::RowResize => loadn(&["split_v", "v_double_arrow"]),
|
|
|
|
|
|
|
|
MouseCursor::Text => loadn(&["text", "xterm"]),
|
|
|
|
MouseCursor::VerticalText => load("vertical-text"),
|
|
|
|
|
|
|
|
MouseCursor::Wait => load("watch"),
|
|
|
|
|
|
|
|
MouseCursor::ZoomIn => load("zoom-in"),
|
|
|
|
MouseCursor::ZoomOut => load("zoom-out"),
|
|
|
|
|
2016-04-18 01:32:34 +10:00
|
|
|
MouseCursor::NoneCursor => self.create_empty_cursor(),
|
2015-05-17 19:19:06 +10:00
|
|
|
};
|
2016-04-17 04:37:53 +10:00
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
(self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, xcursor);
|
2016-04-17 04:37:53 +10:00
|
|
|
if xcursor != 0 {
|
|
|
|
(self.x.display.xlib.XFreeCursor)(self.x.display.display, xcursor);
|
|
|
|
}
|
|
|
|
self.x.display.check_errors().expect("Failed to set or free the cursor");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-18 02:14:53 +10:00
|
|
|
fn load_cursor(&self, name: &str) -> ffi::Cursor {
|
|
|
|
use std::ffi::CString;
|
|
|
|
unsafe {
|
|
|
|
let c_string = CString::new(name.as_bytes()).unwrap();
|
|
|
|
(self.x.display.xcursor.XcursorLibraryLoadCursor)(self.x.display.display, c_string.as_ptr())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_first_existing_cursor(&self, names :&[&str]) -> ffi::Cursor {
|
|
|
|
for name in names.iter() {
|
|
|
|
let xcursor = self.load_cursor(name);
|
|
|
|
if xcursor != 0 {
|
|
|
|
return xcursor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2016-04-18 01:32:34 +10:00
|
|
|
// TODO: This could maybe be cached. I don't think it's worth
|
|
|
|
// the complexity, since cursor changes are not so common,
|
|
|
|
// and this is just allocating a 1x1 pixmap...
|
2016-04-18 02:14:53 +10:00
|
|
|
fn create_empty_cursor(&self) -> ffi::Cursor {
|
2016-04-18 01:32:34 +10:00
|
|
|
use std::mem;
|
|
|
|
|
|
|
|
let data = 0;
|
|
|
|
unsafe {
|
|
|
|
let pixmap = (self.x.display.xlib.XCreateBitmapFromData)(self.x.display.display, self.x.window, &data, 1, 1);
|
|
|
|
if pixmap == 0 {
|
|
|
|
// Failed to allocate
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't care about this color, since it only fills bytes
|
|
|
|
// in the pixmap which are not 0 in the mask.
|
2016-04-18 02:14:53 +10:00
|
|
|
let dummy_color: ffi::XColor = mem::uninitialized();
|
2016-04-18 01:32:34 +10:00
|
|
|
let cursor = (self.x.display.xlib.XCreatePixmapCursor)(self.x.display.display,
|
|
|
|
pixmap,
|
|
|
|
pixmap,
|
|
|
|
&dummy_color as *const _ as *mut _,
|
|
|
|
&dummy_color as *const _ as *mut _, 0, 0);
|
|
|
|
(self.x.display.xlib.XFreePixmap)(self.x.display.display, pixmap);
|
|
|
|
cursor
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
2016-03-22 05:42:54 +11:00
|
|
|
use CursorState::{ Grab, Normal, Hide };
|
2015-05-26 05:01:39 +10:00
|
|
|
|
2015-05-17 19:19:06 +10:00
|
|
|
let mut cursor_state = self.cursor_state.lock().unwrap();
|
|
|
|
match (state, *cursor_state) {
|
2016-03-22 05:42:54 +11:00
|
|
|
(Normal, Normal) | (Hide, Hide) | (Grab, Grab) => return Ok(()),
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
|
|
|
|
match *cursor_state {
|
|
|
|
Grab => {
|
2015-05-17 19:19:06 +10:00
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime);
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().expect("Failed to call XUngrabPointer");
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
},
|
2016-03-22 05:42:54 +11:00
|
|
|
Normal => {},
|
|
|
|
Hide => {
|
2016-04-18 02:14:53 +10:00
|
|
|
// NB: Calling XDefineCursor with None (aka 0)
|
|
|
|
// as a value resets the cursor to the default.
|
2015-05-17 19:19:06 +10:00
|
|
|
unsafe {
|
2016-04-18 02:14:53 +10:00
|
|
|
(self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, 0);
|
2016-03-22 05:42:54 +11:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
|
2016-03-22 05:42:54 +11:00
|
|
|
*cursor_state = state;
|
|
|
|
match state {
|
|
|
|
Normal => Ok(()),
|
|
|
|
Hide => {
|
|
|
|
unsafe {
|
2016-04-18 02:14:53 +10:00
|
|
|
let cursor = self.create_empty_cursor();
|
2016-03-22 05:42:54 +11:00
|
|
|
(self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, cursor);
|
2016-04-18 02:14:53 +10:00
|
|
|
if cursor != 0 {
|
|
|
|
(self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor);
|
|
|
|
}
|
|
|
|
self.x.display.check_errors().expect("Failed to call XDefineCursor or free the empty cursor");
|
2016-03-22 05:42:54 +11:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
Grab => {
|
|
|
|
unsafe {
|
2015-05-17 19:19:06 +10:00
|
|
|
match (self.x.display.xlib.XGrabPointer)(
|
2016-06-30 13:28:03 +10:00
|
|
|
self.x.display.display, self.x.window, ffi::True,
|
2015-05-17 19:19:06 +10:00
|
|
|
(ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::EnterWindowMask |
|
|
|
|
ffi::LeaveWindowMask | ffi::PointerMotionMask | ffi::PointerMotionHintMask |
|
|
|
|
ffi::Button1MotionMask | ffi::Button2MotionMask | ffi::Button3MotionMask |
|
|
|
|
ffi::Button4MotionMask | ffi::Button5MotionMask | ffi::ButtonMotionMask |
|
|
|
|
ffi::KeymapStateMask) as libc::c_uint,
|
|
|
|
ffi::GrabModeAsync, ffi::GrabModeAsync,
|
|
|
|
self.x.window, 0, ffi::CurrentTime
|
|
|
|
) {
|
|
|
|
ffi::GrabSuccess => Ok(()),
|
|
|
|
ffi::AlreadyGrabbed | ffi::GrabInvalidTime |
|
|
|
|
ffi::GrabNotViewable | ffi::GrabFrozen
|
|
|
|
=> Err("cursor could not be grabbed".to_string()),
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn hidpi_factor(&self) -> f32 {
|
2017-03-03 23:25:26 +11:00
|
|
|
unsafe {
|
|
|
|
let x_px = (self.x.display.xlib.XDisplayWidth)(self.x.display.display, self.x.screen_id);
|
|
|
|
let y_px = (self.x.display.xlib.XDisplayHeight)(self.x.display.display, self.x.screen_id);
|
|
|
|
let x_mm = (self.x.display.xlib.XDisplayWidthMM)(self.x.display.display, self.x.screen_id);
|
|
|
|
let y_mm = (self.x.display.xlib.XDisplayHeightMM)(self.x.display.display, self.x.screen_id);
|
|
|
|
let ppmm = ((x_px as f32 * y_px as f32) / (x_mm as f32 * y_mm as f32)).sqrt();
|
|
|
|
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0) // quantize with 1/12 step size.
|
|
|
|
}
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
|
|
|
unsafe {
|
|
|
|
(self.x.display.xlib.XWarpPointer)(self.x.display.display, 0, self.x.window, 0, 0, 0, 0, x, y);
|
2015-12-24 20:57:08 +11:00
|
|
|
self.x.display.check_errors().map_err(|_| ())
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|
|
|
|
}
|
2017-04-23 06:52:35 +10:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn id(&self) -> WindowId { WindowId(self.x.window) }
|
2015-05-17 19:19:06 +10:00
|
|
|
}
|