use std::mem; use std::ptr; use std::str; use std::sync::Arc; use std::ops::{Deref, DerefMut}; use std::os::raw::{c_char, c_double, c_int, c_long, c_short, c_uchar, c_uint, c_ulong}; use super::{ffi, XConnection, XError}; use events::ModifiersState; pub struct XSmartPointer<'a, T> { xconn: &'a Arc, pub ptr: *mut T, } impl<'a, T> XSmartPointer<'a, T> { // You're responsible for only passing things to this that should be XFree'd. // Returns None if ptr is null. pub fn new(xconn: &'a Arc, ptr: *mut T) -> Option { if !ptr.is_null() { Some(XSmartPointer { xconn, ptr, }) } else { None } } } impl<'a, T> Deref for XSmartPointer<'a, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.ptr } } } impl<'a, T> DerefMut for XSmartPointer<'a, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } } } impl<'a, T> Drop for XSmartPointer<'a, T> { fn drop(&mut self) { unsafe { (self.xconn.xlib.XFree)(self.ptr as *mut _); } } } pub unsafe fn get_atom(xconn: &Arc, name: &[u8]) -> Result { let atom_name: *const c_char = name.as_ptr() as _; let atom = (xconn.xlib.XInternAtom)(xconn.display, atom_name, ffi::False); xconn.check_errors().map(|_| atom) } pub unsafe fn send_client_msg( xconn: &Arc, window: c_ulong, // the window this is "about"; not necessarily this window target_window: c_ulong, // the window we're sending to message_type: ffi::Atom, event_mask: Option, data: (c_long, c_long, c_long, c_long, c_long), ) -> Result<(), XError> { let mut event: ffi::XClientMessageEvent = mem::uninitialized(); event.type_ = ffi::ClientMessage; event.display = xconn.display; event.window = window; event.message_type = message_type; event.format = 32; event.data = ffi::ClientMessageData::new(); event.data.set_long(0, data.0); event.data.set_long(1, data.1); event.data.set_long(2, data.2); event.data.set_long(3, data.3); event.data.set_long(4, data.4); let event_mask = event_mask.unwrap_or(ffi::NoEventMask); (xconn.xlib.XSendEvent)( xconn.display, target_window, ffi::False, event_mask, &mut event.into(), ); xconn.check_errors().map(|_| ()) } #[derive(Debug, Clone)] pub enum GetPropertyError { XError(XError), TypeMismatch(ffi::Atom), FormatMismatch(c_int), NothingAllocated, } impl GetPropertyError { pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool { if let GetPropertyError::TypeMismatch(actual_type) = *self { actual_type == t } else { false } } } pub unsafe fn get_property( xconn: &Arc, window: c_ulong, property: ffi::Atom, property_type: ffi::Atom, ) -> Result, GetPropertyError> { let mut data = Vec::new(); let mut done = false; while !done { let mut actual_type: ffi::Atom = mem::uninitialized(); let mut actual_format: c_int = mem::uninitialized(); let mut byte_count: c_ulong = mem::uninitialized(); let mut bytes_after: c_ulong = mem::uninitialized(); let mut buf: *mut c_uchar = ptr::null_mut(); (xconn.xlib.XGetWindowProperty)( xconn.display, window, property, (data.len() / 4) as c_long, 1024, ffi::False, property_type, &mut actual_type, &mut actual_format, &mut byte_count, &mut bytes_after, &mut buf, ); if let Err(e) = xconn.check_errors() { return Err(GetPropertyError::XError(e)); } if actual_type != property_type { return Err(GetPropertyError::TypeMismatch(actual_type)); } // Fun fact: actual_format ISN'T the size of the type; it's more like a really bad enum let format_mismatch = match actual_format as usize { 8 => mem::size_of::() != mem::size_of::(), 16 => mem::size_of::() != mem::size_of::(), 32 => mem::size_of::() != mem::size_of::(), _ => true, // this won't actually be reached; the XError condition above is triggered }; if format_mismatch { return Err(GetPropertyError::FormatMismatch(actual_format)); } if !buf.is_null() { let mut buf = Vec::from_raw_parts(buf as *mut T, byte_count as usize, byte_count as usize); data.append(&mut buf); } else { return Err(GetPropertyError::NothingAllocated); } done = bytes_after == 0; } Ok(data) } impl From for ModifiersState { fn from(mods: ffi::XIModifierState) -> Self { let state = mods.effective as c_uint; ModifiersState { alt: state & ffi::Mod1Mask != 0, shift: state & ffi::ShiftMask != 0, ctrl: state & ffi::ControlMask != 0, logo: state & ffi::Mod4Mask != 0, } } } #[derive(Debug)] pub struct PointerState { #[allow(dead_code)] root: ffi::Window, #[allow(dead_code)] child: ffi::Window, #[allow(dead_code)] root_x: c_double, #[allow(dead_code)] root_y: c_double, #[allow(dead_code)] win_x: c_double, #[allow(dead_code)] win_y: c_double, #[allow(dead_code)] buttons: ffi::XIButtonState, modifiers: ffi::XIModifierState, #[allow(dead_code)] group: ffi::XIGroupState, #[allow(dead_code)] relative_to_window: bool, } impl PointerState { pub fn get_modifier_state(&self) -> ModifiersState { self.modifiers.into() } } pub unsafe fn query_pointer( xconn: &Arc, window: ffi::Window, device_id: c_int, ) -> Result { let mut root_return = mem::uninitialized(); let mut child_return = mem::uninitialized(); let mut root_x_return = mem::uninitialized(); let mut root_y_return = mem::uninitialized(); let mut win_x_return = mem::uninitialized(); let mut win_y_return = mem::uninitialized(); let mut buttons_return = mem::uninitialized(); let mut modifiers_return = mem::uninitialized(); let mut group_return = mem::uninitialized(); let relative_to_window = (xconn.xinput2.XIQueryPointer)( xconn.display, device_id, window, &mut root_return, &mut child_return, &mut root_x_return, &mut root_y_return, &mut win_x_return, &mut win_y_return, &mut buttons_return, &mut modifiers_return, &mut group_return, ) == ffi::True; xconn.check_errors()?; Ok(PointerState { root: root_return, child: child_return, root_x: root_x_return, root_y: root_y_return, win_x: win_x_return, win_y: win_y_return, buttons: buttons_return, modifiers: modifiers_return, group: group_return, relative_to_window, }) } unsafe fn lookup_utf8_inner( xconn: &Arc, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent, buffer: &mut [u8], ) -> (ffi::KeySym, ffi::Status, c_int) { let mut keysym: ffi::KeySym = 0; let mut status: ffi::Status = 0; let count = (xconn.xlib.Xutf8LookupString)( ic, key_event, buffer.as_mut_ptr() as *mut c_char, buffer.len() as c_int, &mut keysym, &mut status, ); (keysym, status, count) } pub unsafe fn lookup_utf8( xconn: &Arc, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent, ) -> String { const INIT_BUFF_SIZE: usize = 16; // Buffer allocated on heap instead of stack, due to the possible reallocation let mut buffer: Vec = vec![mem::uninitialized(); INIT_BUFF_SIZE]; let (_, status, mut count) = lookup_utf8_inner( xconn, ic, key_event, &mut buffer, ); // Buffer overflowed, dynamically reallocate if status == ffi::XBufferOverflow { buffer = vec![mem::uninitialized(); count as usize]; let (_, _, new_count) = lookup_utf8_inner( xconn, ic, key_event, &mut buffer, ); count = new_count; } str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string() } #[derive(Debug)] pub struct FrameExtents { pub left: c_ulong, pub right: c_ulong, pub top: c_ulong, pub bottom: c_ulong, } impl FrameExtents { pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self { FrameExtents { left, right, top, bottom } } pub fn from_border(border: c_ulong) -> Self { Self::new(border, border, border, border) } } #[derive(Debug)] pub struct WindowGeometry { pub x: c_int, pub y: c_int, pub width: c_uint, pub height: c_uint, pub frame: FrameExtents, } impl WindowGeometry { pub fn get_position(&self) -> (i32, i32) { (self.x as _, self.y as _) } pub fn get_inner_position(&self) -> (i32, i32) { ( self.x.saturating_add(self.frame.left as c_int) as _, self.y.saturating_add(self.frame.top as c_int) as _, ) } pub fn get_inner_size(&self) -> (u32, u32) { (self.width as _, self.height as _) } pub fn get_outer_size(&self) -> (u32, u32) { ( self.width.saturating_add( self.frame.left.saturating_add(self.frame.right) as c_uint ) as _, self.height.saturating_add( self.frame.top.saturating_add(self.frame.bottom) as c_uint ) as _, ) } }