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 { 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 { 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 { 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 { 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(()) }