1 use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr}; 2 3 use libc; 4 use parking_lot::Mutex; 5 6 use crate::window::CursorIcon; 7 8 use super::ffi; 9 10 /// A connection to an X server. 11 pub struct XConnection { 12 pub xlib: ffi::Xlib, 13 /// Exposes XRandR functions from version < 1.5 14 pub xrandr: ffi::Xrandr_2_2_0, 15 /// Exposes XRandR functions from version = 1.5 16 pub xrandr_1_5: Option<ffi::Xrandr>, 17 pub xcursor: ffi::Xcursor, 18 pub xinput2: ffi::XInput2, 19 pub xlib_xcb: ffi::Xlib_xcb, 20 pub xrender: ffi::Xrender, 21 pub display: *mut ffi::Display, 22 pub x11_fd: c_int, 23 pub latest_error: Mutex<Option<XError>>, 24 pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>, 25 } 26 27 unsafe impl Send for XConnection {} 28 unsafe impl Sync for XConnection {} 29 30 pub type XErrorHandler = 31 Option<unsafe extern "C" fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>; 32 33 impl XConnection { new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported>34 pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> { 35 // opening the libraries 36 let xlib = ffi::Xlib::open()?; 37 let xcursor = ffi::Xcursor::open()?; 38 let xrandr = ffi::Xrandr_2_2_0::open()?; 39 let xrandr_1_5 = ffi::Xrandr::open().ok(); 40 let xinput2 = ffi::XInput2::open()?; 41 let xlib_xcb = ffi::Xlib_xcb::open()?; 42 let xrender = ffi::Xrender::open()?; 43 44 unsafe { (xlib.XInitThreads)() }; 45 unsafe { (xlib.XSetErrorHandler)(error_handler) }; 46 47 // calling XOpenDisplay 48 let display = unsafe { 49 let display = (xlib.XOpenDisplay)(ptr::null()); 50 if display.is_null() { 51 return Err(XNotSupported::XOpenDisplayFailed); 52 } 53 display 54 }; 55 56 // Get X11 socket file descriptor 57 let fd = unsafe { (xlib.XConnectionNumber)(display) }; 58 59 Ok(XConnection { 60 xlib, 61 xrandr, 62 xrandr_1_5, 63 xcursor, 64 xinput2, 65 xlib_xcb, 66 xrender, 67 display, 68 x11_fd: fd, 69 latest_error: Mutex::new(None), 70 cursor_cache: Default::default(), 71 }) 72 } 73 74 /// Checks whether an error has been triggered by the previous function calls. 75 #[inline] check_errors(&self) -> Result<(), XError>76 pub fn check_errors(&self) -> Result<(), XError> { 77 let error = self.latest_error.lock().take(); 78 if let Some(error) = error { 79 Err(error) 80 } else { 81 Ok(()) 82 } 83 } 84 85 /// Ignores any previous error. 86 #[inline] ignore_error(&self)87 pub fn ignore_error(&self) { 88 *self.latest_error.lock() = None; 89 } 90 } 91 92 impl fmt::Debug for XConnection { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 94 self.display.fmt(f) 95 } 96 } 97 98 impl Drop for XConnection { 99 #[inline] drop(&mut self)100 fn drop(&mut self) { 101 unsafe { (self.xlib.XCloseDisplay)(self.display) }; 102 } 103 } 104 105 /// Error triggered by xlib. 106 #[derive(Debug, Clone)] 107 pub struct XError { 108 pub description: String, 109 pub error_code: u8, 110 pub request_code: u8, 111 pub minor_code: u8, 112 } 113 114 impl Error for XError {} 115 116 impl fmt::Display for XError { fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>117 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 118 write!( 119 formatter, 120 "X error: {} (code: {}, request code: {}, minor code: {})", 121 self.description, self.error_code, self.request_code, self.minor_code 122 ) 123 } 124 } 125 126 /// Error returned if this system doesn't have XLib or can't create an X connection. 127 #[derive(Clone, Debug)] 128 pub enum XNotSupported { 129 /// Failed to load one or several shared libraries. 130 LibraryOpenError(ffi::OpenError), 131 /// Connecting to the X server with `XOpenDisplay` failed. 132 XOpenDisplayFailed, // TODO: add better message 133 } 134 135 impl From<ffi::OpenError> for XNotSupported { 136 #[inline] from(err: ffi::OpenError) -> XNotSupported137 fn from(err: ffi::OpenError) -> XNotSupported { 138 XNotSupported::LibraryOpenError(err) 139 } 140 } 141 142 impl XNotSupported { description(&self) -> &'static str143 fn description(&self) -> &'static str { 144 match self { 145 XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries", 146 XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server", 147 } 148 } 149 } 150 151 impl Error for XNotSupported { 152 #[inline] source(&self) -> Option<&(dyn Error + 'static)>153 fn source(&self) -> Option<&(dyn Error + 'static)> { 154 match *self { 155 XNotSupported::LibraryOpenError(ref err) => Some(err), 156 _ => None, 157 } 158 } 159 } 160 161 impl fmt::Display for XNotSupported { fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>162 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 163 formatter.write_str(self.description()) 164 } 165 } 166