window.rs 9.5 KB

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