summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 54d893c71ded8a7b561da77abfd4e427bb975c10 (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
//! A lightweight X11 window manager, inspired by dwm.
#![deny(clippy::all, clippy::pedantic, clippy::nursery)]

use clients::ClientState;
use conn_info::Connection;
pub use error::*;
use xcb::{
    x::{self, PropertyNotifyEvent},
    Connection as RawConnection, Event, Extension,
};

mod clients;
mod config;
mod conn_info;
mod error;
mod focus;
mod keys;

fn main() -> Result<()> {
    cleanup_process_children();

    let (conn, screen_num) =
        RawConnection::connect_with_extensions(None, &[], &[Extension::Xinerama])?;

    #[allow(clippy::cast_sign_loss)]
    let mut wm = WM::new(&conn, screen_num as usize)?;
    wm.event_loop()?;

    Ok(())
}

struct WM<'a> {
    conn: Connection<'a>,
    clients: ClientState,
}

impl<'a> WM<'a> {
    pub fn new(conn: &'a RawConnection, screen_num: usize) -> Result<Self> {
        let mut this = Self {
            conn: Connection::new(conn, screen_num)?,
            clients: ClientState::default(),
        };
        this.clients.update_geometry(&this.conn)?;

        Ok(this)
    }

    pub fn event_loop(&mut self) -> Result<()> {
        loop {
            match self.conn.wait_for_event() {
                Ok(x) => match x {
                    // See keys.rs
                    Event::X(x::Event::KeyPress(e)) => self.handle_key_press(&e),
                    Event::X(x::Event::MappingNotify(e)) => self.handle_mapping_notify(&e)?,

                    // See clients/mod.rs
                    Event::X(x::Event::ConfigureRequest(e)) => self.handle_configure_request(&e)?,
                    Event::X(x::Event::ConfigureNotify(e)) => self.handle_configure_notify(&e)?,
                    Event::X(x::Event::DestroyNotify(e)) => self.handle_destroy_notify(&e),
                    Event::X(x::Event::MapRequest(e)) => self.handle_map_request(&e)?,
                    Event::X(x::Event::UnmapNotify(e)) => self.handle_unmap_notify(&e),

                    // // See focus.rs
                    Event::X(x::Event::EnterNotify(e)) => self.handle_enter_notify(&e),
                    Event::X(x::Event::FocusIn(e)) => self.handle_focus_in(&e),

                    // // See below
                    Event::X(x::Event::PropertyNotify(e)) => self.handle_property_notify(&e),
                    _ => {}
                },
                Err(e) => {
                    eprintln!("error in event loop: {e:#?}\ncontinuing anyway");
                }
            };
            self.conn.flush()?;
        }
    }

    /// Handle a property notify event, by doing *todo*
    fn handle_property_notify(&mut self, e: &PropertyNotifyEvent) {
        if x::ATOM_WM_HINTS == e.atom() {
            let focused = self.clients.is_focused(e.window());
            if let Some(c) = self.clients.find_client_mut(e.window()) {
                c.sync_properties(&self.conn, focused);
            }
        }
    }
}

/// Cleanup this process' children and set some flags.
/// This is necessary when used with `startx`
fn cleanup_process_children() {
    // TODO: dont transform children into zombies when they terminate
    // TODO: cleanup zombies
    todo!()
}