summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-11-11 06:57:36 -0700
committerIrene Knapp <ireneista@irenes.space>2026-11-11 06:57:36 -0700
commit38c79addb0c8b50b3c1ed7e854d1036f59a72f0e (patch)
tree66b90e33bd2c059e22c87c5baaa590cf2cc171fc /src/main.rs
initial
Force-Push: yes
Change-Id: Ic5f8f9da1be3329b663d2dcb344fb29a78f1a57b
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs269
1 files changed, 269 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..c8bb422
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,269 @@
+use xcb::{ Connection, Xid };
+use xcb::x::{
+  self,
+  Atom,
+  ChangeProperty,
+  ClientMessageData, ClientMessageEvent,
+  Cw, CreateWindow,
+  Event,
+  EventMask,
+  InternAtom,
+  MapWindow,
+  PropMode,
+  Screen,
+  SendEvent, SendEventDest,
+  Window, WindowClass
+};
+
+
+struct Atoms {
+  wm_protocols: Atom,
+  wm_delete_window: Atom,
+  wm_type: Atom,
+  wm_type_dock: Atom,
+  wm_strut: Atom,
+  wm_strut_partial: Atom,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum ScreenSide { Top, Bottom, Left, Right }
+
+struct DockConfiguration {
+  thickness: u16,
+  side: ScreenSide,
+}
+
+#[derive(Debug)]
+struct Rectangle<T> {
+  left: T,
+  top: T,
+  width: T,
+  height: T,
+}
+
+
+fn main() -> xcb::Result<()> {
+  let (connection, screen_index) = Connection::connect(None)?;
+
+  let atoms = get_atoms(&connection)?;
+
+  let setup = connection.get_setup();
+  let screen = setup.roots().nth(screen_index as usize).unwrap();
+
+  let dock_configuration = DockConfiguration {
+    thickness: 50,
+    side: ScreenSide::Bottom,
+  };
+
+  let dock_bounds = compute_dock_bounds(&screen, &dock_configuration);
+
+  let window: Window = connection.generate_id();
+
+  println!("{}x{}", screen.width_in_pixels(), screen.height_in_pixels());
+  println!("dock {:?}", dock_bounds);
+
+  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)?;
+
+  set_string_property(&connection, &window, &x::ATOM_WM_NAME, b"grass bar")?;
+
+  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)?;
+
+  let cookie = connection.send_request_checked(&MapWindow { window });
+  connection.check_request(cookie)?;
+
+  loop {
+    match connection.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;
+	  }
+	}
+      }
+
+      event => { println!("unknown event {:?}", event); }
+    }
+  }
+
+  Ok(())
+}
+
+
+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(())
+}
+
+
+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)?;
+
+  Ok(())
+}
+
+
+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")?;
+
+  let wm_type = get_atom(&connection, b"_NET_WM_WINDOW_TYPE")?;
+  let wm_type_dock = get_atom(&connection, b"_NET_WM_WINDOW_TYPE_DOCK")?;
+
+  let wm_strut = get_atom(&connection, b"_NET_WM_STRUT")?;
+  let wm_strut_partial = get_atom(&connection, b"_NET_WM_STRUT_PARTIAL")?;
+
+  Ok(Atoms {
+    wm_protocols, wm_delete_window,
+    wm_type, wm_type_dock,
+    wm_strut, wm_strut_partial,
+  })
+}
+
+
+fn compute_dock_bounds(screen: &Screen, configuration: &DockConfiguration)
+  -> Rectangle<u16>
+{
+  if configuration.side == ScreenSide::Top
+     || configuration.side == ScreenSide::Bottom
+  {
+    let left = 0;
+    let width = screen.width_in_pixels();
+    let height = configuration.thickness;
+
+    let top = if configuration.side == ScreenSide::Bottom {
+      screen.height_in_pixels() - height
+    } else { 0 };
+
+    Rectangle { left, top, width, height }
+  } else {
+    let top = 0;
+    let height = screen.height_in_pixels();
+    let width = configuration.thickness;
+
+    let left = if configuration.side == ScreenSide::Right {
+      screen.width_in_pixels() - width
+    } else { 0 };
+
+    Rectangle { left, top, width, height }
+  }
+}
+
+
+fn set_dock_strut_properties(connection: &Connection, window: &Window,
+			     atoms: &Atoms, screen: &Screen,
+			     configuration: &DockConfiguration)
+  -> xcb::Result<()>
+{
+  let thickness = configuration.thickness as u32;
+
+  let strut_values = match 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)?;
+
+  let strut_partial_values = &[
+    strut_values[0],
+    strut_values[1],
+    strut_values[2],
+    strut_values[3],
+
+    0 as u32,
+    if configuration.side == ScreenSide::Left {
+      screen.height_in_pixels() as u32
+    } else { 0 as u32 },
+
+    0 as u32,
+    if configuration.side == ScreenSide::Right {
+      screen.height_in_pixels() as u32
+    } else { 0 as u32 },
+
+    0 as u32,
+    if configuration.side == ScreenSide::Top {
+      screen.width_in_pixels() as u32
+    } else { 0 as u32 },
+
+    0 as u32,
+    if 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)?;
+
+  Ok(())
+}
+