|
@@ -4,6 +4,12 @@ use std::ffi::CString;
|
|
|
use std::{mem,ptr};
|
|
|
use std::os::raw::{c_int,c_uchar};
|
|
|
|
|
|
+#[derive(Debug,Clone,Copy)]
|
|
|
+pub struct Size {
|
|
|
+ pub wd: i32,
|
|
|
+ pub ht: i32,
|
|
|
+}
|
|
|
+
|
|
|
pub struct Display {
|
|
|
pub display: *mut xlib::_XDisplay,
|
|
|
pub screen: i32,
|
|
@@ -24,18 +30,29 @@ impl Display {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// All the state needed to keep around to run this sort of
|
|
|
+/// application!
|
|
|
pub struct Window {
|
|
|
pub display: *mut xlib::_XDisplay,
|
|
|
pub screen: i32,
|
|
|
pub window: u64,
|
|
|
+ // these two are interned strings kept around because we want to
|
|
|
+ // check against them a _lot_, to find out if an event is a quit
|
|
|
+ // event
|
|
|
pub wm_protocols: u64,
|
|
|
pub wm_delete_window: u64,
|
|
|
+ // The width and height of the window
|
|
|
pub width: i32,
|
|
|
pub height: i32,
|
|
|
}
|
|
|
|
|
|
impl Window {
|
|
|
- pub fn create(d: Display, width: i32, height: i32) -> Window {
|
|
|
+ /// Create a new Window from a given Display and with the desire
|
|
|
+ /// width and height
|
|
|
+ pub fn create(
|
|
|
+ d: Display,
|
|
|
+ Size { wd: width, ht: height }: Size,
|
|
|
+ ) -> Window {
|
|
|
unsafe {
|
|
|
let display = d.display;
|
|
|
let screen = d.screen;
|
|
@@ -44,8 +61,8 @@ impl Window {
|
|
|
xlib::XRootWindow(display, screen),
|
|
|
0,
|
|
|
0,
|
|
|
- 3840,
|
|
|
- 36,
|
|
|
+ width as u32,
|
|
|
+ height as u32,
|
|
|
1,
|
|
|
xlib::XBlackPixel(display, screen),
|
|
|
xlib::XWhitePixel(display, screen),
|
|
@@ -70,6 +87,8 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// for this application, we might eventually care about the
|
|
|
+ /// mouse, so make sure we notify x11 that we care about those
|
|
|
pub fn set_input_masks(&mut self) {
|
|
|
let mut opcode = 0;
|
|
|
let mut event = 0;
|
|
@@ -126,6 +145,7 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Set the name of the window to the desired string
|
|
|
pub fn set_title(&mut self, name: &str) {
|
|
|
unsafe {
|
|
|
xlib::XStoreName(
|
|
@@ -136,12 +156,14 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Map the window to the screen
|
|
|
pub fn map(&mut self) {
|
|
|
unsafe {
|
|
|
xlib::XMapWindow(self.display, self.window);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Intern a string in the x server
|
|
|
pub fn intern(&mut self, s: &str) -> u64 {
|
|
|
unsafe {
|
|
|
let cstr = CString::new(s).unwrap();
|
|
@@ -149,6 +171,7 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Modify the supplied property to the noted value.
|
|
|
pub fn change_property<T: XProperty>(&mut self, prop: &str, val: &[T]) {
|
|
|
let prop = self.intern(prop);
|
|
|
unsafe {
|
|
@@ -168,44 +191,53 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Get the Cairo drawing surface corresponding to the whole
|
|
|
+ /// window
|
|
|
pub fn get_cairo_surface(&mut self) -> cairo::Surface {
|
|
|
unsafe {
|
|
|
let s = cairo_sys::cairo_xlib_surface_create(
|
|
|
self.display,
|
|
|
self.window,
|
|
|
xlib::XDefaultVisual(self.display, self.screen),
|
|
|
- 3840,
|
|
|
- 64,
|
|
|
+ self.width,
|
|
|
+ self.height,
|
|
|
);
|
|
|
cairo::Surface::from_raw_none(s)
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
- pub fn handle(&mut self) -> Event {
|
|
|
- // to find out if we're getting a delete window event
|
|
|
+ }
|
|
|
|
|
|
+ /// handle a single event, wrapping it as an 'Event'. This is
|
|
|
+ /// pretty useless right now, but the plan is to make it easier to
|
|
|
+ /// handle things like keyboard input and mouse input later. This
|
|
|
+ /// will also only return values for events we care about
|
|
|
+ pub fn handle(&mut self) -> Option<Event> {
|
|
|
let mut e = unsafe { mem::uninitialized() };
|
|
|
unsafe { xlib::XNextEvent(self.display, &mut e) };
|
|
|
match e.get_type() {
|
|
|
+ // Is it a quit event? We gotta do some tedious string
|
|
|
+ // comparison to find out
|
|
|
xlib::ClientMessage => {
|
|
|
let xclient: xlib::XClientMessageEvent = From::from(e);
|
|
|
if xclient.message_type == self.wm_protocols && xclient.format == 32 {
|
|
|
let protocol = xclient.data.get_long(0) as xlib::Atom;
|
|
|
if protocol == self.wm_delete_window {
|
|
|
- return Event::QuitEvent;
|
|
|
+ return Some(Event::QuitEvent);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- xlib::Expose => return Event::ShowEvent,
|
|
|
+ // Is it a show event?
|
|
|
+ xlib::Expose => return Some(Event::ShowEvent),
|
|
|
|
|
|
+ // otherwise, it might be a mouse press event
|
|
|
xlib::GenericEvent => {
|
|
|
let mut cookie: xlib::XGenericEventCookie = From::from(e);
|
|
|
unsafe { xlib::XGetEventData(self.display, &mut cookie) };
|
|
|
match cookie.evtype {
|
|
|
xinput2::XI_ButtonPress => {
|
|
|
- let data: &xinput2::XIDeviceEvent = unsafe { mem::transmute(cookie.data) };
|
|
|
- return Event::MouseEvent { x: data.event_x, y: data.event_y };
|
|
|
+ let data: &xinput2::XIDeviceEvent =
|
|
|
+ unsafe { mem::transmute(cookie.data) };
|
|
|
+ return Some(Event::MouseEvent { x: data.event_x, y: data.event_y });
|
|
|
}
|
|
|
_ => (),
|
|
|
}
|
|
@@ -213,15 +245,18 @@ impl Window {
|
|
|
_ => (),
|
|
|
}
|
|
|
|
|
|
- Event::Other
|
|
|
+ None
|
|
|
}
|
|
|
|
|
|
+ /// True if there are any pending events.
|
|
|
pub fn has_events(&mut self) -> bool {
|
|
|
unsafe {
|
|
|
xlib::XPending(self.display) != 0
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Did you know that X11 uses a file descriptor underneath the
|
|
|
+ /// surface to wait on events? This lets us use select on it!
|
|
|
pub fn get_fd(&mut self) -> i32 {
|
|
|
unsafe {
|
|
|
xlib::XConnectionNumber(self.display)
|
|
@@ -229,59 +264,51 @@ impl Window {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Always close the display when we're done.
|
|
|
+impl Drop for Window {
|
|
|
+ fn drop(&mut self) {
|
|
|
+ unsafe {
|
|
|
+ xlib::XCloseDisplay(self.display);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// A trait for abstracting over different values which are allowed
|
|
|
+/// for xlib properties
|
|
|
pub trait XProperty : Sized {
|
|
|
- fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8));
|
|
|
+ fn with_ptr(
|
|
|
+ xs: &[Self],
|
|
|
+ w: &mut Window,
|
|
|
+ f: impl FnOnce(&mut Window, u64, *const u8),
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
impl XProperty for i64 {
|
|
|
- fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8)) {
|
|
|
+ fn with_ptr(
|
|
|
+ xs: &[Self],
|
|
|
+ w: &mut Window,
|
|
|
+ f: impl FnOnce(&mut Window, u64, *const u8),
|
|
|
+ ) {
|
|
|
f(w, xlib::XA_CARDINAL, unsafe { mem::transmute(xs.as_ptr()) })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
impl XProperty for &str {
|
|
|
- fn with_ptr(xs: &[Self], w: &mut Window, f: impl FnOnce(&mut Window, u64, *const u8)) {
|
|
|
+ fn with_ptr(
|
|
|
+ xs: &[Self],
|
|
|
+ w: &mut Window,
|
|
|
+ f: impl FnOnce(&mut Window, u64, *const u8),
|
|
|
+ ) {
|
|
|
let xs: Vec<u64> = xs.iter().map(|s| w.intern(s)).collect();
|
|
|
f(w, xlib::XA_ATOM, unsafe { mem::transmute(xs.as_ptr()) })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Drop for Window {
|
|
|
- fn drop(&mut self) {
|
|
|
- unsafe {
|
|
|
- xlib::XCloseDisplay(self.display);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
+/// An ADT of only the events we care about, wrapped in a high-level
|
|
|
+/// way
|
|
|
#[derive(Debug)]
|
|
|
pub enum Event {
|
|
|
MouseEvent { x:f64, y: f64 },
|
|
|
ShowEvent,
|
|
|
QuitEvent,
|
|
|
- Other,
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-cairo_surface_t *cairo_create_x11_surface0(int x, int y)
|
|
|
-{
|
|
|
- Display *dsp;
|
|
|
- Drawable da;
|
|
|
- int screen;
|
|
|
- cairo_surface_t *sfc;
|
|
|
-
|
|
|
- if ((dsp = XOpenDisplay(NULL)) == NULL)
|
|
|
- exit(1);
|
|
|
- screen = DefaultScreen(dsp);
|
|
|
- da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp),
|
|
|
- 0, 0, x, y, 0, 0, 0);
|
|
|
- XSelectInput(dsp, da, ButtonPressMask | KeyPressMask);
|
|
|
- XMapWindow(dsp, da);
|
|
|
-
|
|
|
- sfc = cairo_xlib_surface_create(dsp, da,
|
|
|
- DefaultVisual(dsp, screen), x, y);
|
|
|
- cairo_xlib_surface_set_size(sfc, x, y);
|
|
|
-
|
|
|
- return sfc;
|
|
|
}
|
|
|
- */
|