srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/x11/src/platform/events.rs
blob: ef970f1702c7cee8ef58a7afa9e75708d8328e77 (plain) (blame)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use super::*;

impl X11Platform {

    pub(super) fn handle_event(&mut self, event: XEvent) -> PlatformResult<Option<Event>> {
        match event {
            XEvent::MapRequest(ev) => self.manage_new_window(ev.window),
            XEvent::ConfigureRequest(ev) => {
                if self.xid_to_core.contains_key(&ev.window) {
                    // We own layout for managed clients; just ack with a
                    // synthetic ConfigureNotify carrying real geometry.
                    let geom = self.conn.get_geometry(ev.window).map_err(err)?.reply().map_err(err)?;
                    let notify = x11rb::protocol::xproto::ConfigureNotifyEvent {
                        response_type: x11rb::protocol::xproto::CONFIGURE_NOTIFY_EVENT,
                        sequence: 0,
                        event: ev.window,
                        window: ev.window,
                        above_sibling: x11rb::NONE,
                        x: geom.x,
                        y: geom.y,
                        width: geom.width,
                        height: geom.height,
                        border_width: 0,
                        override_redirect: false,
                    };
                    self.conn.send_event(false, ev.window, EventMask::STRUCTURE_NOTIFY, notify).map_err(err)?;
                } else {
                    let aux = ConfigureWindowAux::from_configure_request(&ev);
                    self.conn.configure_window(ev.window, &aux).map_err(err)?;
                }
                self.conn.flush().map_err(err)?;
                Ok(None)
            }
            XEvent::UnmapNotify(ev) => Ok(self.unmanage(ev.window)),
            XEvent::DestroyNotify(ev) => Ok(self.unmanage(ev.window)),
            XEvent::ButtonPress(ev) => {
                let (x, y) = (ev.root_x as i32, ev.root_y as i32);
                let hit = self.wm.borrow().hit_test(x, y);
                if let Some((id, hit)) = hit {
                    self.raise_and_focus(id)?;
                    match hit {
                        TitlebarHit::Drag => self.wm.borrow_mut().start_drag(id, x, y),
                        TitlebarHit::Close => self.request_close(id)?,
                        TitlebarHit::Maximize => {
                            self.wm.borrow_mut().toggle_maximize(id);
                            self.sync_geometry(id)?;
                        }
                        TitlebarHit::Minimize => {
                            self.wm.borrow_mut().minimize_window(id);
                            if let Some(frame) = self.frame_for(id) {
                                self.conn.unmap_window(frame).map_err(err)?;
                            }
                        }
                        TitlebarHit::Resize(edge) => self.wm.borrow_mut().start_resize(id, edge, x, y),
                    }
                    self.conn.flush().map_err(err)?;
                }
                // Let the click through to the client (we grabbed it SYNC).
                self.conn.allow_events(x11rb::protocol::xproto::Allow::REPLAY_POINTER, ev.time).map_err(err)?;
                self.conn.flush().map_err(err)?;
                Ok(Some(Event::MouseButtonPress { button: MouseButton::Left, x, y }))
            }
            XEvent::ButtonRelease(ev) => {
                let mut wm = self.wm.borrow_mut();
                let was_dragging = wm.is_dragging();
                let was_resizing = wm.is_resizing();
                let dragged_id = wm.focused_id();
                if was_dragging {
                    wm.end_drag();
                } else if was_resizing {
                    wm.end_resize();
                }
                drop(wm);
                if was_dragging || was_resizing {
                    if let Some(id) = dragged_id {
                        self.sync_geometry(id)?;
                    }
                }
                Ok(Some(Event::MouseButtonRelease { button: MouseButton::Left, x: ev.root_x as i32, y: ev.root_y as i32 }))
            }
            XEvent::MotionNotify(ev) => {
                let (x, y) = (ev.root_x as i32, ev.root_y as i32);
                let mut wm = self.wm.borrow_mut();
                let id = wm.focused_id();
                if wm.is_dragging() {
                    wm.update_drag(x, y);
                } else if wm.is_resizing() {
                    wm.update_resize(x, y);
                } else {
                    return Ok(Some(Event::MouseMotion { x, y }));
                }
                drop(wm);
                if let Some(id) = id {
                    self.sync_geometry(id)?;
                }
                Ok(Some(Event::MouseMotion { x, y }))
            }
            XEvent::KeyPress(ev) => {
                let keysym = self.keycode_to_keysym(ev.detail);
                let Some(key_name) = keysyms::keysym_to_name(keysym) else { return Ok(None) };
                Ok(Some(Event::KeyPress { key_name, modifiers: Self::modifiers_from_state(ev.state.into()) }))
            }
            XEvent::KeyRelease(ev) => {
                let keysym = self.keycode_to_keysym(ev.detail);
                let Some(key_name) = keysyms::keysym_to_name(keysym) else { return Ok(None) };
                Ok(Some(Event::KeyRelease { key_name, modifiers: Self::modifiers_from_state(ev.state.into()) }))
            }
            XEvent::Expose(ev) => {
                let target = self.frames.iter().find(|(_, f)| f.frame == ev.window).map(|(&id, _)| id);
                if let Some(id) = target {
                    let w = self.wm.borrow().window(id).cloned_for_render();
                    if let Some(w) = w {
                        let focused = self.wm.borrow().focused_id() == Some(id);
                        let _ = self.redraw_decoration(id, &w, focused);
                    }
                }
                Ok(None)
            }
            _ => Ok(None),
        }
    }
}