Browse Source

Some cleanup and lots of comments

Getty Ritter 5 years ago
parent
commit
538d88739b
2 changed files with 137 additions and 78 deletions
  1. 60 28
      src/main.rs
  2. 77 50
      src/window.rs

+ 60 - 28
src/main.rs

@@ -3,60 +3,72 @@ mod window;
 use std::os::unix::io::AsRawFd;
 use pango::LayoutExt;
 
-use window::{Display,Event,Window};
+use window::{Display,Event,Size,Window};
 
 fn main() {
+    // set up the display and the window
     let mut d = Display::create();
-    let width = d.get_width();
-    let mut w = Window::create(d, width, 36);
-    w.change_property(
-        "_NET_WM_WINDOW_TYPE",
-        &["_NET_WM_WINDOW_TYPE_DOCK"],
-    );
-
+    let size = Size {
+        wd: d.get_width(),
+        // TODO: this should be a function of font size
+        ht: 36,
+    };
+    let mut w = Window::create(d, size);
+    // set some window-manager properties: this is a dock
+    w.change_property("_NET_WM_WINDOW_TYPE", &["_NET_WM_WINDOW_TYPE_DOCK"]);
+    // ...and should push other windows out of the way
+    w.change_property("_NET_WM_STRUT", &[0i64, 0, size.ht as i64, 0],);
     w.change_property(
         "_NET_WM_STRUT_PARTIAL",
-        &[
-            0,            0, 36, 0,
-            0,            0,  0, 0,
-            0, width as i64,  0, 0,
+        &[ 0, 0, size.ht as i64, 0,
+           0, 0, 0, 0,
+           0, size.wd as i64, 0, 0,
         ],
     );
-    w.change_property(
-        "_NET_WM_STRUT",
-        &[0i64, 0, 36, 0],
-    );
 
+    // we won't ever see this, but for good measure.
     w.set_title("rbar");
-
+    // we care about some input events!
     w.set_input_masks();
-
     w.set_protocols();
+    // and now show it!
     w.map();
 
+    // let's grab the cairo context here
     let surf = w.get_cairo_surface();
     let ctx = cairo::Context::new(&surf);
 
+    // we do some grossness with file descriptors later, so we need
+    // the file descriptors we care about here
     let window_fd = w.get_fd();
+    let stdin_fd = std::io::stdin().as_raw_fd();
 
     let mut fds = unsafe { std::mem::uninitialized() };
+    // To begin with, our left-hand side---which normally is whatever
+    // was last passed in on stdin---will start as a generic
+    // message...
     let mut input = format!("Loading...");
-    let stdin_fd = std::io::stdin().as_raw_fd();
+    // And let's get a buffered stdin handle now
     let mut stdin = std::io::BufReader::new(std::io::stdin());
+    // In the absence of other events, let's refresh every five
+    // seconds. Or whatever.
     let mut timer = libc::timeval {
         tv_sec: 5,
         tv_usec: 0,
     };
-    draw(&ctx, "[1]", width);
+    // do an initial pass at drawing the bar!
+    draw(&ctx, &input, size);
 
+    // we're gonna keep looping until we don't
     loop {
-        use std::io::BufRead;
-
         unsafe {
+            // set up the FD set to be the X11 fd and the state of stdin
             libc::FD_ZERO(&mut fds);
             libc::FD_SET(window_fd, &mut fds);
             libc::FD_SET(stdin_fd, &mut fds);
 
+            // this will block until there's input on either of the
+            // above FDs or until five seconds have passed, whichever comes first
             libc::select(
                 window_fd + 1,
                 &mut fds,
@@ -66,47 +78,67 @@ fn main() {
             );
         }
 
+        // if we _did_ have input on stdin, then read it in: that'll
+        // be our new left-hand text
         if unsafe { libc::FD_ISSET(stdin_fd, &mut fds) } {
+            use std::io::BufRead;
             input = String::new();
             stdin.read_line(&mut input).unwrap();
             if input == "" {
                 break;
             }
-            draw(&ctx, &input, width);
+            draw(&ctx, &input, size);
         }
 
+        // if we have X11 events, handle them. If any one was a quit
+        // event, then just... quit.
         while w.has_events() {
-            draw(&ctx, &input, width);
             match w.handle() {
-                Event::QuitEvent => break,
-                Event::ShowEvent =>
-                    draw(&ctx, &input, width),
+                Some(Event::QuitEvent) => break,
                 _e => (),
             }
         }
 
+        // otherwise, draw the thing!
+        draw(&ctx, &input, size);
     }
 }
 
 
-fn draw(ctx: &cairo::Context, left: &str, width: i32) {
+/// Do our Cairo drawing. This needs to be refactored to allow for
+/// more configurability in terms of what gets written!
+fn draw(ctx: &cairo::Context, left: &str, Size { wd: width, ..}: Size) {
+    // for the current time on the right
     let now = time::now();
 
+    // the background is... gray-ish? this'll be configurable eventually
     ctx.set_source_rgb(0.1, 0.1, 0.1);
     ctx.paint();
+
+    // and the text is white
     ctx.set_source_rgb(1.0, 1.0, 1.0);
 
+    // Our pango layout handles placing and drawing text
     let layout = pangocairo::functions::create_layout(&ctx).unwrap();
+    // for the time, we want to align it to the right
     layout.set_alignment(pango::Alignment::Right);
+    // allow for the whole width of the bar, minus a small fixed amount
     layout.set_width((width - 20) * pango::SCALE);
+    // this should also be configurable, but Fira Mono is a good font
     let mut font = pango::FontDescription::from_string("Fira Mono 18");
     font.set_weight(pango::Weight::Bold);
     layout.set_font_description(&font);
+    // start drawing in the top-left
     ctx.move_to(10.0, 4.0);
+    //The text here is just the nicely-rendered current time
     layout.set_text(&time::strftime("%a %b %d %H:%M", &now).unwrap());
+    // and draw it
     pangocairo::functions::show_layout(&ctx, &layout);
 
+    // We can reuse the same layout, but starting from the left
     layout.set_alignment(pango::Alignment::Left);
+    // and now we write whatever the "current text" is...
     layout.set_text(left);
+    // and draw that!
     pangocairo::functions::show_layout(&ctx, &layout);
 }

+ 77 - 50
src/window.rs

@@ -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;
 }
- */