window.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. use x11::{xlib,xinput2};
  2. use std::ffi::CString;
  3. use std::{mem,ptr};
  4. use std::os::raw::{c_int,c_uchar};
  5. #[derive(Debug,Clone,Copy)]
  6. pub struct Size {
  7. pub wd: i32,
  8. pub ht: i32,
  9. }
  10. pub struct Display {
  11. pub display: *mut xlib::_XDisplay,
  12. pub screen: i32,
  13. }
  14. impl Display {
  15. pub fn create() -> Result<Display, failure::Error> {
  16. let display = unsafe { xlib::XOpenDisplay(ptr::null()) };
  17. if display.is_null() {
  18. bail!("Unable to open X11 display");
  19. }
  20. let screen = unsafe { xlib::XDefaultScreen(display) };
  21. Ok(Display { display, screen })
  22. }
  23. pub fn get_width(&mut self) -> i32 {
  24. unsafe {
  25. let s = xlib::XScreenOfDisplay(self.display, self.screen);
  26. xlib::XWidthOfScreen(s)
  27. }
  28. }
  29. pub fn get_widths(&mut self) -> Result<Vec<(i32,i32)>, failure::Error> {
  30. if unsafe { x11::xinerama::XineramaIsActive(self.display) != 0 } {
  31. let mut screens = 0;
  32. let screen_info = unsafe { x11::xinerama::XineramaQueryScreens(self.display, &mut screens) };
  33. let mut widths = Vec::new();
  34. for i in 0..screens {
  35. unsafe {
  36. let si = screen_info.offset(i as isize).as_ref().ok_or(format_err!("bad pointer"))?;
  37. widths.push((si.x_org as i32, si.width as i32));
  38. }
  39. }
  40. Ok(widths)
  41. } else {
  42. Ok(vec![(0, self.get_width())])
  43. }
  44. }
  45. }
  46. /// All the state needed to keep around to run this sort of
  47. /// application!
  48. pub struct Window {
  49. pub display: *mut xlib::_XDisplay,
  50. pub screen: i32,
  51. pub window: u64,
  52. // these two are interned strings kept around because we want to
  53. // check against them a _lot_, to find out if an event is a quit
  54. // event
  55. pub wm_protocols: u64,
  56. pub wm_delete_window: u64,
  57. // The width and height of the window
  58. pub width: i32,
  59. pub height: i32,
  60. }
  61. impl Window {
  62. /// Create a new Window from a given Display and with the desire
  63. /// width and height
  64. pub fn create(
  65. d: Display,
  66. Size { wd: width, ht: height }: Size,
  67. ) -> Result<Window, failure::Error> {
  68. unsafe {
  69. let display = d.display;
  70. let screen = d.screen;
  71. let window = xlib::XCreateSimpleWindow(
  72. display,
  73. xlib::XRootWindow(display, screen),
  74. 0,
  75. 0,
  76. width as u32,
  77. height as u32,
  78. 1,
  79. xlib::XBlackPixel(display, screen),
  80. xlib::XWhitePixel(display, screen),
  81. );
  82. let wm_protocols = {
  83. let cstr = CString::new("WM_PROTOCOLS")?;
  84. xlib::XInternAtom(display, cstr.as_ptr(), 0)
  85. };
  86. let wm_delete_window = {
  87. let cstr = CString::new("WM_DELETE_WINDOW")?;
  88. xlib::XInternAtom(display, cstr.as_ptr(), 0)
  89. };
  90. Ok(Window {
  91. display,
  92. screen,
  93. window,
  94. wm_protocols,
  95. wm_delete_window,
  96. width,
  97. height,
  98. })
  99. }
  100. }
  101. /// for this application, we might eventually care about the
  102. /// mouse, so make sure we notify x11 that we care about those
  103. pub fn set_input_masks(&mut self) -> Result<(), failure::Error> {
  104. let mut opcode = 0;
  105. let mut event = 0;
  106. let mut error = 0;
  107. let xinput_str = CString::new("XInputExtension")?;
  108. unsafe {
  109. xlib::XQueryExtension(
  110. self.display,
  111. xinput_str.as_ptr(),
  112. &mut opcode,
  113. &mut event,
  114. &mut error,
  115. );
  116. }
  117. let mut mask: [c_uchar;1] = [0];
  118. let mut input_event_mask = xinput2::XIEventMask {
  119. deviceid: xinput2::XIAllMasterDevices,
  120. mask_len: mask.len() as i32,
  121. mask: mask.as_mut_ptr(),
  122. };
  123. let events = &[
  124. xinput2::XI_ButtonPress,
  125. xinput2::XI_ButtonRelease,
  126. ];
  127. for &event in events {
  128. xinput2::XISetMask(&mut mask, event);
  129. }
  130. match unsafe {
  131. xinput2::XISelectEvents(
  132. self.display,
  133. self.window,
  134. &mut input_event_mask,
  135. 1,
  136. )
  137. } {
  138. status if status as u8 == xlib::Success => (),
  139. err => bail!("Failed to select events {:?}", err)
  140. }
  141. Ok(())
  142. }
  143. pub fn set_protocols(&mut self) -> Result<(), failure::Error> {
  144. let mut protocols = [self.intern("WM_DELETE_WINDOW")?];
  145. unsafe {
  146. xlib::XSetWMProtocols(
  147. self.display,
  148. self.window,
  149. protocols.as_mut_ptr(),
  150. protocols.len() as c_int,
  151. );
  152. }
  153. Ok(())
  154. }
  155. /// Set the name of the window to the desired string
  156. pub fn set_title(&mut self, name: &str) -> Result<(), failure::Error> {
  157. unsafe {
  158. xlib::XStoreName(
  159. self.display,
  160. self.window,
  161. CString::new(name)?.as_ptr(),
  162. );
  163. }
  164. Ok(())
  165. }
  166. /// Map the window to the screen
  167. pub fn map(&mut self) {
  168. unsafe {
  169. xlib::XMapWindow(self.display, self.window);
  170. }
  171. }
  172. /// Intern a string in the x server
  173. pub fn intern(&mut self, s: &str) -> Result<u64, failure::Error> {
  174. unsafe {
  175. let cstr = CString::new(s)?;
  176. Ok(xlib::XInternAtom(self.display, cstr.as_ptr(), 0))
  177. }
  178. }
  179. /// Modify the supplied property to the noted value.
  180. pub fn change_property<T: XProperty>(
  181. &mut self,
  182. prop: &str,
  183. val: &[T]
  184. ) -> Result<(), failure::Error>
  185. {
  186. let prop = self.intern(prop)?;
  187. unsafe {
  188. let len = val.len();
  189. T::with_ptr(val, self, |w, typ, ptr| {
  190. xlib::XChangeProperty(
  191. w.display,
  192. w.window,
  193. prop,
  194. typ,
  195. 32,
  196. xlib::PropModeReplace,
  197. ptr,
  198. len as c_int,
  199. );
  200. })?;
  201. }
  202. Ok(())
  203. }
  204. /// Get the Cairo drawing surface corresponding to the whole
  205. /// window
  206. pub fn get_cairo_surface(&mut self) -> cairo::Surface {
  207. unsafe {
  208. let s = cairo_sys::cairo_xlib_surface_create(
  209. self.display,
  210. self.window,
  211. xlib::XDefaultVisual(self.display, self.screen),
  212. self.width,
  213. self.height,
  214. );
  215. cairo::Surface::from_raw_none(s)
  216. }
  217. }
  218. /// handle a single event, wrapping it as an 'Event'. This is
  219. /// pretty useless right now, but the plan is to make it easier to
  220. /// handle things like keyboard input and mouse input later. This
  221. /// will also only return values for events we care about
  222. pub fn handle(&mut self) -> Option<Event> {
  223. let mut e = unsafe { mem::uninitialized() };
  224. unsafe { xlib::XNextEvent(self.display, &mut e) };
  225. match e.get_type() {
  226. // Is it a quit event? We gotta do some tedious string
  227. // comparison to find out
  228. xlib::ClientMessage => {
  229. let xclient: xlib::XClientMessageEvent = From::from(e);
  230. if xclient.message_type == self.wm_protocols && xclient.format == 32 {
  231. let protocol = xclient.data.get_long(0) as xlib::Atom;
  232. if protocol == self.wm_delete_window {
  233. return Some(Event::QuitEvent);
  234. }
  235. }
  236. }
  237. // Is it a show event?
  238. xlib::Expose => return Some(Event::ShowEvent),
  239. // otherwise, it might be a mouse press event
  240. xlib::GenericEvent => {
  241. let mut cookie: xlib::XGenericEventCookie = From::from(e);
  242. unsafe { xlib::XGetEventData(self.display, &mut cookie) };
  243. match cookie.evtype {
  244. xinput2::XI_ButtonPress => {
  245. let data: &xinput2::XIDeviceEvent =
  246. unsafe { mem::transmute(cookie.data) };
  247. return Some(Event::MouseEvent { x: data.event_x, y: data.event_y });
  248. }
  249. _ => (),
  250. }
  251. }
  252. _ => (),
  253. }
  254. None
  255. }
  256. /// True if there are any pending events.
  257. pub fn has_events(&mut self) -> bool {
  258. unsafe {
  259. xlib::XPending(self.display) != 0
  260. }
  261. }
  262. /// Did you know that X11 uses a file descriptor underneath the
  263. /// surface to wait on events? This lets us use select on it!
  264. pub fn get_fd(&mut self) -> i32 {
  265. unsafe {
  266. xlib::XConnectionNumber(self.display)
  267. }
  268. }
  269. }
  270. /// Always close the display when we're done.
  271. impl Drop for Window {
  272. fn drop(&mut self) {
  273. unsafe {
  274. xlib::XCloseDisplay(self.display);
  275. }
  276. }
  277. }
  278. /// A trait for abstracting over different values which are allowed
  279. /// for xlib properties
  280. pub trait XProperty : Sized {
  281. fn with_ptr(
  282. xs: &[Self],
  283. w: &mut Window,
  284. f: impl FnOnce(&mut Window, u64, *const u8),
  285. ) -> Result<(), failure::Error> ;
  286. }
  287. impl XProperty for i64 {
  288. fn with_ptr(
  289. xs: &[Self],
  290. w: &mut Window,
  291. f: impl FnOnce(&mut Window, u64, *const u8),
  292. ) -> Result<(), failure::Error> {
  293. f(w, xlib::XA_CARDINAL, unsafe { mem::transmute(xs.as_ptr()) });
  294. Ok(())
  295. }
  296. }
  297. impl XProperty for &str {
  298. fn with_ptr(
  299. xs: &[Self],
  300. w: &mut Window,
  301. f: impl FnOnce(&mut Window, u64, *const u8),
  302. ) -> Result<(), failure::Error> {
  303. let xs: Result<Vec<u64>, failure::Error> =
  304. xs.iter().map(|s| w.intern(s)).collect();
  305. f(w, xlib::XA_ATOM, unsafe { mem::transmute(xs?.as_ptr()) });
  306. Ok(())
  307. }
  308. }
  309. /// An ADT of only the events we care about, wrapped in a high-level
  310. /// way
  311. #[derive(Debug)]
  312. pub enum Event {
  313. MouseEvent { x:f64, y: f64 },
  314. ShowEvent,
  315. QuitEvent,
  316. }