summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: ea420092c4288796ecb30629b803d7f2a0d7634f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use atoms::InternedAtoms;
use clients::ClientList;
use thiserror::Error;
use xcb::{
    x::{self, ChangeWindowAttributes, Screen, Window},
    Connection, Extension,
};

mod atoms;
mod clients;

type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Error)]
pub enum Error {
    #[error("xcb returned screen that doesn't exist")]
    NoSuchScreen,

    #[error("other wm is running")]
    OtherWMRunning,

    #[error("connection error: {0}")]
    ConnectionError(#[from] xcb::ConnError),

    #[error("generic xcb error: {0}")]
    XCBError(#[from] xcb::Error),
}

fn main() -> Result<()> {
    // todo: cli stuff
    let display_name = ":1";

    cleanup_process_children();

    let (conn, screen_num) =
        Connection::connect_with_extensions(Some(display_name), &[], &[Extension::Xinerama])?;

    let mut wm = WM::new(&conn, screen_num)?;
    wm.event_loop()?;

    Ok(())
}

struct WM<'a> {
    conn: &'a Connection,
    screen_num: i32,

    root: Window,
    clients: ClientList,
    atoms: InternedAtoms,
}

impl WM<'_> {
    fn new<'a>(conn: &'a Connection, screen_num: i32) -> Result<WM<'a>> {
        // Fetch root window
        let setup = conn.get_setup();
        let screen = setup
            .roots()
            .nth(screen_num as usize)
            .ok_or(Error::NoSuchScreen)?;

        // Check no other WM is running
        let cookie = conn.send_request_checked(&ChangeWindowAttributes {
            window: screen.root(),
            value_list: &[
                x::Cw::BackPixel(screen.white_pixel()),
                x::Cw::EventMask(x::EventMask::SUBSTRUCTURE_REDIRECT),
            ],
        });

        conn.check_request(cookie)
            .map_err(|_| Error::OtherWMRunning)?;

        Ok(WM {
            atoms: InternedAtoms::new_with(conn)?,
            clients: Default::default(),
            conn,
            screen_num,
            root: screen.root(),
        })
    }

    fn event_loop(&mut self) -> Result<()> {
        self.update_geometry()?;

        Ok(())
    }
}
fn cleanup_process_children() {
    // todo: dont transform children into zombies when they terminate
    // todo: cleanup zombies
}

impl<'a> std::fmt::Debug for WM<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("WM")
            .field("screen_num", &self.screen_num)
            .field("root", &self.root)
            .field("clients", &self.clients)
            .field("atoms", &self.atoms)
            .finish()
    }
}