summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/main.rs394
1 files changed, 267 insertions, 127 deletions
diff --git a/src/main.rs b/src/main.rs
index c8bb422..c80fdb8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,21 +1,194 @@
-use xcb::{ Connection, Xid };
+use std::borrow::Borrow;
+use std::sync::Arc;
+use std::ops::Deref;
+
+use xcb::{ Xid };
 use xcb::x::{
   self,
   Atom,
   ChangeProperty,
-  ClientMessageData, ClientMessageEvent,
+  ClientMessageData,
   Cw, CreateWindow,
-  Event,
   EventMask,
   InternAtom,
   MapWindow,
   PropMode,
   Screen,
-  SendEvent, SendEventDest,
-  Window, WindowClass
+  WindowClass
 };
 
 
+struct Connection(xcb::Connection);
+
+struct Display {
+  connection: Connection,
+  screen_index: i32,
+  atoms: Atoms,
+}
+
+struct Window {
+  display: Arc<Display>,
+  window_id: x::Window,
+}
+
+
+trait HasWindow {
+  fn window(&self) -> &Window;
+
+  fn window_id(&self) -> x::Window { self.window().window_id }
+}
+
+impl HasWindow for Window {
+  fn window(&self) -> &Window { self }
+}
+
+impl<T: Deref<Target=Window>> HasWindow for T {
+  fn window(&self) -> &Window { &*self }
+}
+
+
+trait HasConnection {
+  fn connection(&self) -> &Connection;
+}
+
+impl HasConnection for Connection {
+  fn connection(&self) -> &Connection { self }
+}
+
+impl<T: HasDisplay> HasConnection for T {
+  fn connection(&self) -> &Connection { &self.display().connection }
+}
+
+
+trait HasDisplay {
+  fn display(&self) -> &Display;
+
+  fn atoms(&self) -> &Atoms { &self.display().atoms }
+}
+
+impl HasDisplay for Display {
+  fn display(&self) -> &Display { self }
+}
+
+impl<T: HasWindow> HasDisplay for T {
+  fn display(&self) -> &Display { self.window().display.borrow() }
+}
+
+
+trait ConnectionExt: HasConnection {
+  fn get_atom(&self, name: &[u8]) -> xcb::Result<Atom> {
+    let connection = self.connection();
+
+    let cookie = connection.0.send_request(&InternAtom {
+      only_if_exists: true,
+      name: name,
+    });
+
+    Ok(connection.0.wait_for_reply(cookie)?.atom())
+  }
+
+  fn get_atoms(&self) -> xcb::Result<Atoms> {
+    let wm_protocols = self.get_atom(b"WM_PROTOCOLS")?;
+    let wm_delete_window = self.get_atom(b"WM_DELETE_WINDOW")?;
+
+    let wm_type = self.get_atom(b"_NET_WM_WINDOW_TYPE")?;
+    let wm_type_dock = self.get_atom(b"_NET_WM_WINDOW_TYPE_DOCK")?;
+
+    let wm_strut = self.get_atom(b"_NET_WM_STRUT")?;
+    let wm_strut_partial = self.get_atom(b"_NET_WM_STRUT_PARTIAL")?;
+
+    Ok(Atoms {
+      wm_protocols, wm_delete_window,
+      wm_type, wm_type_dock,
+      wm_strut, wm_strut_partial,
+    })
+  }
+}
+
+impl<T: HasConnection> ConnectionExt for T { }
+
+
+trait DisplayExt: HasDisplay + HasConnection {
+  fn screen(&self) -> &Screen {
+    let setup = self.connection().0.get_setup();
+    let screen_index = self.display().screen_index as usize;
+
+    &setup.roots().nth(screen_index).unwrap()
+  }
+}
+
+impl<T: HasDisplay + HasConnection> DisplayExt for T { }
+
+
+trait WindowExt: HasWindow + HasDisplay + HasConnection {
+  fn map_window(&self) -> xcb::Result<()> {
+    let display = self.display();
+
+    let cookie = display.connection.0.send_request_checked(&MapWindow {
+      window: self.window_id()
+    });
+    display.connection.0.check_request(cookie)?;
+
+    Ok(())
+  }
+
+  fn set_string_property(&self, property: &Atom, value: &[u8])
+    -> xcb::Result<()>
+  {
+    let connection = self.connection();
+
+    let cookie = connection.0.send_request_checked(&ChangeProperty {
+      mode: PropMode::Replace,
+      window: self.window_id(),
+      property: *property,
+      r#type: x::ATOM_STRING,
+      data: value,
+    });
+    connection.0.check_request(cookie)?;
+
+    Ok(())
+  }
+
+
+  fn set_atom_property(&self, property: &Atom, values: &[Atom])
+    -> xcb::Result<()>
+  {
+    let connection = self.connection();
+
+    let cookie = connection.0.send_request_checked(&ChangeProperty {
+      mode: PropMode::Replace,
+      window: self.window_id(),
+      property: *property,
+      r#type: x::ATOM_ATOM,
+      data: values,
+    });
+    connection.0.check_request(cookie)?;
+
+    Ok(())
+  }
+
+
+  fn set_cardinal_property(&self, property: &Atom, values: &[u32])
+    -> xcb::Result<()>
+  {
+    let connection = self.connection();
+
+    let cookie = connection.0.send_request_checked(&ChangeProperty {
+      mode: PropMode::Replace,
+      window: self.window_id(),
+      property: *property,
+      r#type: x::ATOM_CARDINAL,
+      data: values,
+    });
+    connection.0.check_request(cookie)?;
+
+    Ok(())
+  }
+}
+
+impl<T: HasWindow + HasDisplay + HasConnection> WindowExt for T { }
+
+
 struct Atoms {
   wm_protocols: Atom,
   wm_delete_window: Atom,
@@ -25,6 +198,7 @@ struct Atoms {
   wm_strut_partial: Atom,
 }
 
+
 #[derive(Clone, Copy, PartialEq, Eq)]
 enum ScreenSide { Top, Bottom, Left, Right }
 
@@ -33,6 +207,16 @@ struct DockConfiguration {
   side: ScreenSide,
 }
 
+struct Dock {
+  configuration: DockConfiguration,
+  window: Window,
+}
+
+impl HasWindow for Dock {
+  fn window(&self) -> &Window { &self.window }
+}
+
+
 #[derive(Debug)]
 struct Rectangle<T> {
   left: T,
@@ -42,69 +226,56 @@ struct Rectangle<T> {
 }
 
 
-fn main() -> xcb::Result<()> {
-  let (connection, screen_index) = Connection::connect(None)?;
-
-  let atoms = get_atoms(&connection)?;
+impl Display {
+  fn new() -> xcb::Result<Display> {
+    let (connection, screen_index) = xcb::Connection::connect(None)?;
+    let connection = Connection(connection);
 
-  let setup = connection.get_setup();
-  let screen = setup.roots().nth(screen_index as usize).unwrap();
+    let atoms = connection.get_atoms()?;
 
-  let dock_configuration = DockConfiguration {
-    thickness: 50,
-    side: ScreenSide::Bottom,
-  };
+    Ok(Display { connection, screen_index, atoms })
+  }
+}
 
-  let dock_bounds = compute_dock_bounds(&screen, &dock_configuration);
 
-  let window: Window = connection.generate_id();
+impl Window {
+  fn new(display: Arc<Display>, create_window: &mut CreateWindow)
+    -> xcb::Result<Window>
+  {
+    let display_ref: &Display = display.borrow();
+    let connection: &Connection = &display_ref.connection;
 
-  println!("{}x{}", screen.width_in_pixels(), screen.height_in_pixels());
-  println!("dock {:?}", dock_bounds);
+    let window_id: x::Window = connection.0.generate_id();
 
-  let cookie = connection.send_request_checked(&CreateWindow {
-    depth: x::COPY_FROM_PARENT as u8,
-    wid: window,
-    parent: screen.root(),
-    x: dock_bounds.left as i16,
-    y: dock_bounds.top as i16,
-    width: dock_bounds.width,
-    height: dock_bounds.height,
-    border_width: 0,
-    class: WindowClass::InputOutput,
-    visual: screen.root_visual(),
-    value_list: &[
-      Cw::BackPixel(screen.black_pixel()),
-      Cw::EventMask(EventMask::EXPOSURE | EventMask::BUTTON_PRESS),
-    ],
-  });
-  connection.check_request(cookie)?;
+    create_window.wid = window_id;
+    let cookie = connection.0.send_request_checked(create_window);
+    connection.0.check_request(cookie)?;
 
-  set_string_property(&connection, &window, &x::ATOM_WM_NAME, b"grass bar")?;
+    Ok(Window { display, window_id })
+  }
+}
 
-  set_atom_property(&connection, &window,
-		    &atoms.wm_protocols, &[atoms.wm_delete_window])?;
 
-  set_atom_property(&connection, &window,
-		    &atoms.wm_type, &[atoms.wm_type_dock])?;
-  set_dock_strut_properties(&connection, &window, &atoms, &screen,
-			    &dock_configuration)?;
+fn main() -> xcb::Result<()> {
+  let display = Arc::new(Display::new()?);
 
-  let cookie = connection.send_request_checked(&MapWindow { window });
-  connection.check_request(cookie)?;
+  let _dock = Dock::new(Arc::clone(&display), DockConfiguration {
+    thickness: 50,
+    side: ScreenSide::Bottom,
+  })?;
 
   loop {
-    match connection.wait_for_event()? {
+    match display.connection.0.wait_for_event()? {
       xcb::Event::X(x::Event::ButtonPress(event)) => {
         println!("click {:?}", event);
       }
 
       xcb::Event::X(x::Event::ClientMessage(event)) => {
-	if let ClientMessageData::Data32([atom, ..]) = event.data() {
-	  if atom == atoms.wm_delete_window.resource_id() {
-	    break;
-	  }
-	}
+        if let ClientMessageData::Data32([atom, ..]) = event.data() {
+          if atom == display.atoms.wm_delete_window.resource_id() {
+            break;
+          }
+        }
       }
 
       event => { println!("unknown event {:?}", event); }
@@ -115,76 +286,46 @@ fn main() -> xcb::Result<()> {
 }
 
 
-fn get_atom(connection: &Connection, name: &[u8]) -> xcb::Result<Atom> {
-  let cookie = connection.send_request(&InternAtom {
-    only_if_exists: true,
-    name: name,
-  });
-
-  Ok(connection.wait_for_reply(cookie)?.atom())
-}
-
-
-fn set_string_property(connection: &Connection, window: &Window,
-                       property: &Atom, value: &[u8])
-  -> xcb::Result<()>
-{
-  let cookie = connection.send_request_checked(&ChangeProperty {
-    mode: PropMode::Replace,
-    window: *window,
-    property: *property,
-    r#type: x::ATOM_STRING,
-    data: value,
-  });
-  connection.check_request(cookie)?;
-
-  Ok(())
-}
-
-
-fn set_atom_property(connection: &Connection, window: &Window, property: &Atom, values: &[Atom]) -> xcb::Result<()> {
-  let cookie = connection.send_request_checked(&ChangeProperty {
-    mode: PropMode::Replace,
-    window: *window,
-    property: *property,
-    r#type: x::ATOM_ATOM,
-    data: values,
-  });
-  connection.check_request(cookie)?;
-
-  Ok(())
-}
+impl Dock {
+  fn new(display: Arc<Display>, configuration: DockConfiguration)
+    -> xcb::Result<Dock>
+  {
+    let screen = display.screen();
 
+    let dock_bounds = compute_dock_bounds(&screen, &configuration);
 
-fn set_cardinal_property(connection: &Connection, window: &Window, property: &Atom, values: &[u32]) -> xcb::Result<()> {
-  let cookie = connection.send_request_checked(&ChangeProperty {
-    mode: PropMode::Replace,
-    window: *window,
-    property: *property,
-    r#type: x::ATOM_CARDINAL,
-    data: values,
-  });
-  connection.check_request(cookie)?;
+    let window = Window::new(Arc::clone(&display), &mut CreateWindow {
+      depth: x::COPY_FROM_PARENT as u8,
+      wid: x::Window::none(),
+      parent: screen.root(),
+      x: dock_bounds.left as i16,
+      y: dock_bounds.top as i16,
+      width: dock_bounds.width,
+      height: dock_bounds.height,
+      border_width: 0,
+      class: WindowClass::InputOutput,
+      visual: screen.root_visual(),
+      value_list: &[
+        Cw::BackPixel(screen.black_pixel()),
+        Cw::EventMask(EventMask::EXPOSURE | EventMask::BUTTON_PRESS),
+      ],
+    })?;
+    let dock = Dock { window, configuration };
 
-  Ok(())
-}
+    dock.set_string_property(&x::ATOM_WM_NAME, b"grass bar")?;
 
+    dock.set_atom_property(&display.atoms.wm_protocols,
+                           &[display.atoms.wm_delete_window])?;
 
-fn get_atoms(connection: &Connection) -> xcb::Result<Atoms> {
-  let wm_protocols = get_atom(&connection, b"WM_PROTOCOLS")?;
-  let wm_delete_window = get_atom(&connection, b"WM_DELETE_WINDOW")?;
+    dock.set_atom_property(&display.atoms.wm_type,
+                           &[display.atoms.wm_type_dock])?;
 
-  let wm_type = get_atom(&connection, b"_NET_WM_WINDOW_TYPE")?;
-  let wm_type_dock = get_atom(&connection, b"_NET_WM_WINDOW_TYPE_DOCK")?;
+    set_dock_strut_properties(&dock, &screen)?;
 
-  let wm_strut = get_atom(&connection, b"_NET_WM_STRUT")?;
-  let wm_strut_partial = get_atom(&connection, b"_NET_WM_STRUT_PARTIAL")?;
+    dock.map_window()?;
 
-  Ok(Atoms {
-    wm_protocols, wm_delete_window,
-    wm_type, wm_type_dock,
-    wm_strut, wm_strut_partial,
-  })
+    Ok(dock)
+  }
 }
 
 
@@ -217,21 +358,21 @@ fn compute_dock_bounds(screen: &Screen, configuration: &DockConfiguration)
 }
 
 
-fn set_dock_strut_properties(connection: &Connection, window: &Window,
-			     atoms: &Atoms, screen: &Screen,
-			     configuration: &DockConfiguration)
+fn set_dock_strut_properties(dock: &Dock, screen: &Screen)
   -> xcb::Result<()>
 {
-  let thickness = configuration.thickness as u32;
+  let window = dock.window();
+  let atoms = dock.atoms();
+
+  let thickness = dock.configuration.thickness as u32;
 
-  let strut_values = match configuration.side {
+  let strut_values = match dock.configuration.side {
     ScreenSide::Left => &[thickness, 0 as u32, 0 as u32, 0 as u32],
     ScreenSide::Right => &[0 as u32, thickness, 0 as u32, 0 as u32],
     ScreenSide::Top => &[0 as u32, 0 as u32, thickness, 0 as u32],
     ScreenSide::Bottom => &[0 as u32, 0 as u32, 0 as u32, thickness],
   };
-  println!("struts {:?}", strut_values);
-  set_cardinal_property(&connection, &window, &atoms.wm_strut, strut_values)?;
+  window.set_cardinal_property(&atoms.wm_strut, strut_values)?;
 
   let strut_partial_values = &[
     strut_values[0],
@@ -240,29 +381,28 @@ fn set_dock_strut_properties(connection: &Connection, window: &Window,
     strut_values[3],
 
     0 as u32,
-    if configuration.side == ScreenSide::Left {
+    if dock.configuration.side == ScreenSide::Left {
       screen.height_in_pixels() as u32
     } else { 0 as u32 },
 
     0 as u32,
-    if configuration.side == ScreenSide::Right {
+    if dock.configuration.side == ScreenSide::Right {
       screen.height_in_pixels() as u32
     } else { 0 as u32 },
 
     0 as u32,
-    if configuration.side == ScreenSide::Top {
+    if dock.configuration.side == ScreenSide::Top {
       screen.width_in_pixels() as u32
     } else { 0 as u32 },
 
     0 as u32,
-    if configuration.side == ScreenSide::Bottom {
+    if dock.configuration.side == ScreenSide::Bottom {
       screen.width_in_pixels() as u32
     } else { 0 as u32 },
   ];
-  println!("partial {:?}", strut_partial_values);
 
-  set_cardinal_property(&connection, &window, &atoms.wm_strut_partial,
-			strut_partial_values)?;
+  window.set_cardinal_property(&atoms.wm_strut_partial,
+                               strut_partial_values)?;
 
   Ok(())
 }