summaryrefslogtreecommitdiff
path: root/src/focus.rs
blob: b26381ee924f07ddbb8247394a759118e6485d20 (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
use xcb::x::{
    self, DeleteProperty, EnterNotifyEvent, FocusInEvent, InputFocus, NotifyDetail, NotifyMode,
    SetInputFocus, Window,
};

use crate::{error::*, WM};

impl WM<'_> {
    /// When a new window is entered, focus it.
    pub fn handle_enter_notify(&mut self, e: EnterNotifyEvent) -> Result<()> {
        if (e.mode() != NotifyMode::Normal || e.detail() == NotifyDetail::Inferior)
            && e.event() != self.root
        {
            return Ok(());
        }

        self.focus_window(e.event());
        self.conn.flush()?;

        Ok(())
    }

    /// When a new window requests focus, focus it.
    pub fn handle_focus_in(&mut self, e: FocusInEvent) -> Result<()> {
        if self
            .clients
            .focused_mut()
            .map(|c| c.window() != e.event())
            .unwrap_or(true)
        {
            self.focus_window(e.event());
            self.conn.flush()?;
        }

        Ok(())
    }

    /// Attempt to focus the given window, even if it isn't managed.
    /// This function sends multiple requests without checking them, so `conn.flush()` should be called after.
    pub fn focus_window(&mut self, window: Window) {
        if let Some((mon, i)) = self.clients.find_client_pos(window) {
            self.refocus(mon, i);
        } else {
            self.conn.send_request(&SetInputFocus {
                revert_to: InputFocus::PointerRoot,
                focus: window,
                time: x::CURRENT_TIME,
            });
            self.conn.send_request(&DeleteProperty {
                window,
                property: self.atoms.net_active_window,
            });
        }
    }

    /// Refocus on the client with the given co-ordinates, setting X11 properties as required.
    /// If the given index is invalid, focus on the root instead.
    /// This function sends multiple requests without checking them, so `conn.flush()` should be called after.
    pub fn refocus(&mut self, mon: usize, i: usize) {
        self.unfocus();
        if let Some(new) = self.clients.set_focused(mon, i) {
            new.set_border(self.conn, self.colours.border_focused());
            // TODO: reset urgent flag
            // TODO: something to do with grabbuttons
            // TODO: set input focus
            // TODO: set active window
            // TODO: send wmtakefocus event
        } else {
            // TODO: focus on root
        }
    }

    /// Unfocus the currently focused window, if it exists.
    /// This function sends multiple requests without checking them, so `conn.flush()` should be called after.
    pub fn unfocus(&mut self) {
        if let Some(old) = self.clients.focused_mut() {
            old.set_border(self.conn, self.colours.border_normal());
        }
    }
}