use MouseCursor; use CreationError; use CreationError::OsError; use libc; use std::borrow::Borrow; use std::{mem, ptr, cmp}; use std::sync::{Arc, Mutex}; use std::os::raw::{c_int, c_long, c_uchar}; use std::thread; use std::time::Duration; use CursorState; use WindowAttributes; use FullScreenState; use platform::PlatformSpecificWindowBuilderAttributes; use platform::MonitorId as PlatformMonitorId; use window::MonitorId as RootMonitorId; use super::{ffi}; use super::{XConnection, WindowId, EventsLoop}; use super::MonitorId as X11MonitorId; // TODO: remove me fn with_c_str(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()) } pub struct XWindow { display: Arc, window: ffi::Window, root: ffi::Window, // screen we're using, original screen mode if we've switched fullscreen: Arc)>>, } unsafe impl Send for XWindow {} unsafe impl Sync for XWindow {} unsafe impl Send for Window {} unsafe impl Sync for Window {} impl XWindow { fn switch_to_fullscreen_mode(&self, monitor: i32, width: u16, height: u16) { let original_monitor = { let fullscreen = self.fullscreen.lock().unwrap(); fullscreen.0 }; if monitor != original_monitor { // We're setting fullscreen on a new screen so first revert the original screen self.switch_from_fullscreen_mode(); } let current_mode = unsafe { let mut mode_num: libc::c_int = mem::uninitialized(); let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized(); if (self.display.xf86vmode.XF86VidModeGetAllModeLines)(self.display.display, monitor, &mut mode_num, &mut modes) == 0 { eprintln!("[winit] Couldn't get current resolution mode"); return } ptr::read(*modes.offset(0)) }; let new_mode = unsafe { let mut mode_num: libc::c_int = mem::uninitialized(); let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized(); if (self.display.xf86vmode.XF86VidModeGetAllModeLines)(self.display.display, monitor, &mut mode_num, &mut modes) == 0 { // There are no modes, mighty weird eprintln!("[winit] X has no valid modes"); return } else { let matching_mode = (0 .. mode_num).map(|i| { let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m }).find(|m| m.hdisplay == width && m.vdisplay == height); if let Some(matching_mode) = matching_mode { 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 >= width && m.vdisplay >= height); match m { Some(m) => m, None => { eprintln!("[winit] Could not find a suitable graphics mode"); return } } } } }; if new_mode != current_mode { // We actually need to change modes self.set_mode(monitor, new_mode); let mut fullscreen = self.fullscreen.lock().unwrap(); if fullscreen.1.is_none() { // It's our first mode switch, save the original mode fullscreen.1 = Some(current_mode); } } } fn switch_from_fullscreen_mode(&self) { let (monitor, mode) = { let fullscreen = self.fullscreen.lock().unwrap(); (fullscreen.0, fullscreen.1) }; if let Some(mode) = mode { self.set_mode(monitor, mode); let mut fullscreen = self.fullscreen.lock().unwrap(); fullscreen.1 = None; } } pub fn set_mode(&self, monitor: i32, mode: ffi::XF86VidModeModeInfo) { unsafe { let mut mode_to_switch_to = mode; (self.display.xf86vmode.XF86VidModeSwitchToMode)( self.display.display, monitor, &mut mode_to_switch_to ); self.display.check_errors().expect("Failed to call XF86VidModeSwitchToMode"); (self.display.xf86vmode.XF86VidModeSetViewPort)(self.display.display, monitor, 0, 0); self.display.check_errors().expect("Failed to call XF86VidModeSetViewPort"); } } } impl Drop for XWindow { fn drop(&mut self) { // Make sure we return the display to the original resolution if we've changed it self.switch_from_fullscreen_mode(); } } pub struct Window { pub x: Arc, cursor_state: Mutex, } impl Window { pub fn new(ctx: &EventsLoop, window_attrs: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { let display = &ctx.display; 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); } 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 }; let screen_id = match pl_attribs.screen_id { Some(id) => id, None => match window_attrs.fullscreen { FullScreenState::Exclusive(RootMonitorId { inner: PlatformMonitorId::X(X11MonitorId(_, monitor)) }) => monitor as i32, _ => unsafe { (display.xlib.XDefaultScreen)(display.display) }, } }; // getting the root window let root = ctx.root; // creating let mut set_win_attr = { let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; 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 }; 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; if window_attrs.transparent { swa.background_pixel = 0; } swa.override_redirect = 0; swa }; let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask; if window_attrs.transparent { window_attributes |= ffi::CWBackPixel; } // finally creating the window let window = unsafe { let win = (display.xlib.XCreateWindow)(display.display, root, 0, 0, dimensions.0 as libc::c_uint, 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, &mut set_win_attr); display.check_errors().expect("Failed to call XCreateWindow"); win }; // 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 _); }); } // set visibility if window_attrs.visible { unsafe { (display.xlib.XMapRaised)(display.display, window); (display.xlib.XFlush)(display.display); } display.check_errors().expect("Failed to set window visibility"); } // Opt into handling window close unsafe { (display.xlib.XSetWMProtocols)(display.display, window, &ctx.wm_delete_window as *const _ as *mut _, 1); display.check_errors().expect("Failed to call XSetWMProtocols"); (display.xlib.XFlush)(display.display); display.check_errors().expect("Failed to call XFlush"); } // 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"))); } } // 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"); } // 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); }; } let window = Window { x: Arc::new(XWindow { display: display.clone(), window: window, root: root, fullscreen: Arc::new(Mutex::new((screen_id, None))), }), cursor_state: Mutex::new(CursorState::Normal), }; window.set_title(&window_attrs.title); window.set_decorations(window_attrs.decorations); window.set_maximized(window_attrs.maximized); window.set_fullscreen(window_attrs.fullscreen.clone()); if window_attrs.visible { unsafe { let ref x_window: &XWindow = window.x.borrow(); // XSetInputFocus generates an error if the window is not visible, // 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)); } } } // returning Ok(window) } fn set_netwm(display: &Arc, 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(&self, state: FullScreenState) { match state { FullScreenState::None => { self.x.switch_from_fullscreen_mode(); Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_FULLSCREEN", false); }, FullScreenState::Windowed => { self.x.switch_from_fullscreen_mode(); Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_FULLSCREEN", true); }, FullScreenState::Exclusive(RootMonitorId { inner: PlatformMonitorId::X(X11MonitorId(_, monitor)) }) => { if let Some(dimensions) = self.get_inner_size() { self.x.switch_to_fullscreen_mode(monitor as i32, dimensions.0 as u16, dimensions.1 as u16); Window::set_netwm(&self.x.display, self.x.window, self.x.root, "_NET_WM_STATE_FULLSCREEN", true); } else { eprintln!("[winit] Couldn't get window dimensions to go 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); } 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) }; 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); (self.x.display.xlib.XFlush)(self.x.display.display); }); self.x.display.check_errors().expect("Failed to set window title"); } 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"); } 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); self.x.display.check_errors().expect("Failed to call XMapRaised"); } } 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); self.x.display.check_errors().expect("Failed to call XUnmapWindow"); } } 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)) } } #[inline] 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); } self.x.display.check_errors().expect("Failed to call XMoveWindow"); } #[inline] pub fn get_inner_size(&self) -> Option<(u32, u32)> { self.get_geometry().map(|(_, _, w, h, _)| ((w as f32 / self.hidpi_factor()) as u32, (h as f32 / self.hidpi_factor()) as u32)) } #[inline] 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? } #[inline] 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); } self.x.display.check_errors().expect("Failed to call XResizeWindow"); } #[inline] pub fn get_xlib_display(&self) -> *mut libc::c_void { self.x.display.display as *mut libc::c_void } #[inline] pub fn get_xlib_screen_id(&self) -> *mut libc::c_void { let screen_id = { let fullscreen = self.x.fullscreen.lock().unwrap(); fullscreen.0 }; screen_id as *mut libc::c_void } #[inline] pub fn get_xlib_xconnection(&self) -> Arc { self.x.display.clone() } #[inline] pub fn platform_display(&self) -> *mut libc::c_void { self.x.display.display as *mut libc::c_void } #[inline] pub fn get_xlib_window(&self) -> *mut libc::c_void { self.x.window as *mut libc::c_void } #[inline] pub fn platform_window(&self) -> *mut libc::c_void { self.x.window as *mut libc::c_void } 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 _ } } pub fn set_cursor(&self, cursor: MouseCursor) { unsafe { let load = |name: &str| { self.load_cursor(name) }; let loadn = |names: &[&str]| { self.load_first_existing_cursor(names) }; // 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. let xcursor = match cursor { 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"), MouseCursor::Hand => loadn(&["hand2", "hand1"]), 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"), /// Resize cursors 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"), MouseCursor::NoneCursor => self.create_empty_cursor(), }; (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, xcursor); 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"); } } 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 } // 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... fn create_empty_cursor(&self) -> ffi::Cursor { 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. let dummy_color: ffi::XColor = mem::uninitialized(); 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 } } pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { use CursorState::{ Grab, Normal, Hide }; let mut cursor_state = self.cursor_state.lock().unwrap(); match (state, *cursor_state) { (Normal, Normal) | (Hide, Hide) | (Grab, Grab) => return Ok(()), _ => {}, } match *cursor_state { Grab => { unsafe { (self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime); self.x.display.check_errors().expect("Failed to call XUngrabPointer"); } }, Normal => {}, Hide => { // NB: Calling XDefineCursor with None (aka 0) // as a value resets the cursor to the default. unsafe { (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, 0); } }, } *cursor_state = state; match state { Normal => Ok(()), Hide => { unsafe { let cursor = self.create_empty_cursor(); (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, cursor); 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"); } Ok(()) }, Grab => { unsafe { match (self.x.display.xlib.XGrabPointer)( self.x.display.display, self.x.window, ffi::True, (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 { let screen_id = { let fullscreen = self.x.fullscreen.lock().unwrap(); fullscreen.0 }; unsafe { let x_px = (self.x.display.xlib.XDisplayWidth)(self.x.display.display, screen_id); let y_px = (self.x.display.xlib.XDisplayHeight)(self.x.display.display, screen_id); let x_mm = (self.x.display.xlib.XDisplayWidthMM)(self.x.display.display, screen_id); let y_mm = (self.x.display.xlib.XDisplayHeightMM)(self.x.display.display, 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. } } 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); self.x.display.check_errors().map_err(|_| ()) } } #[inline] pub fn id(&self) -> WindowId { WindowId(self.x.window) } }