atagen

monolith

public
Star0
Watch0
8ffa3fa4d4a9331f2638c1598a9f3c107e148296
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/monolith/src/input.rsRust283 lines8.8 KB
1
//! Input handling shared by every backend. Backends translate their events
2
//! into these calls, with pointer positions in global logical coordinates.
3
4
use crate::state::State;
5
use monolith_layout::{Rect, WindowId};
6
use smithay::backend::input::{ButtonState, InputTime, KeyState};
7
use smithay::backend::session::Session;
8
use smithay::input::keyboard::{FilterResult, Keycode, xkb};
9
use smithay::input::pointer::{AxisFrame, ButtonEvent, MotionEvent};
10
use smithay::utils::{Logical, Point, SERIAL_COUNTER};
11
use smithay::wayland::shell::wlr_layer::Layer;
12
13
impl State {
14
    pub fn pointer_to(&mut self, pos: Point<f64, Logical>, time: InputTime) {
15
        self.input.pointer_pos = pos;
16
        self.activity();
17
        if drag_to(self, pos) {
18
            return;
19
        }
20
        let Some(pointer) = self.input.seat.get_pointer() else {
21
            return;
22
        };
23
        focus_on_hover(self, pos, self.input.warping);
24
        let under = self.surface_under(pos);
25
        pointer.motion(
26
            self,
27
            under,
28
            &MotionEvent {
29
                location: pos,
30
                serial: SERIAL_COUNTER.next_serial(),
31
                time,
32
            },
33
        );
34
    }
35
36
    pub fn pointer_button(&mut self, button: u32, pressed: ButtonState, time: InputTime) {
37
        self.activity();
38
        if pressed == ButtonState::Released && self.input.drag.take().is_some() {
39
            return;
40
        }
41
        if pressed == ButtonState::Pressed && !self.services.lock.active() {
42
            focus_under_pointer(self);
43
            if start_drag(self, button) {
44
                return;
45
            }
46
        }
47
        let Some(pointer) = self.input.seat.get_pointer() else {
48
            return;
49
        };
50
        pointer.button(
51
            self,
52
            &ButtonEvent {
53
                button,
54
                state: pressed,
55
                serial: SERIAL_COUNTER.next_serial(),
56
                time,
57
            },
58
        );
59
    }
60
61
    pub fn pointer_axis(&mut self, frame: AxisFrame) {
62
        self.activity();
63
        if let Some(pointer) = self.input.seat.get_pointer() {
64
            pointer.axis(self, frame);
65
        }
66
    }
67
68
    pub fn pointer_frame(&mut self) {
69
        if let Some(pointer) = self.input.seat.get_pointer() {
70
            pointer.frame(self);
71
        }
72
    }
73
74
    pub fn key(&mut self, code: Keycode, pressed: KeyState, time: InputTime) {
75
        self.activity();
76
        let Some(keyboard) = self.input.seat.get_keyboard() else {
77
            return;
78
        };
79
        let action = keyboard.input(
80
            self,
81
            code,
82
            pressed,
83
            SERIAL_COUNTER.next_serial(),
84
            time,
85
            |state, mods, handle| {
86
                if pressed == KeyState::Released {
87
                    let before = state.input.suppressed.len();
88
                    state.input.suppressed.retain(|k| *k != code);
89
                    return if state.input.suppressed.len() < before {
90
                        FilterResult::Intercept(None)
91
                    } else {
92
                        FilterResult::Forward
93
                    };
94
                }
95
                let sym = handle.modified_sym().raw();
96
                if (xkb::keysyms::KEY_XF86Switch_VT_1..=xkb::keysyms::KEY_XF86Switch_VT_12)
97
                    .contains(&sym)
98
                {
99
                    state.input.suppressed.push(code);
100
                    let vt = (sym - xkb::keysyms::KEY_XF86Switch_VT_1 + 1) as i32;
101
                    return FilterResult::Intercept(Some(Action::Vt(vt)));
102
                }
103
                if state.services.lock.active() {
104
                    return FilterResult::Forward;
105
                }
106
                match state.settings.binds.find(mods, &handle.raw_syms()) {
107
                    Some(cmd) => {
108
                        tracing::debug!(?cmd, "bind");
109
                        state.input.suppressed.push(code);
110
                        FilterResult::Intercept(Some(Action::Run(cmd)))
111
                    }
112
                    None => FilterResult::Forward,
113
                }
114
            },
115
        );
116
        match action {
117
            Some(Some(Action::Run(cmd))) => {
118
                self.wm.layer_focus = None;
119
                if let Err(e) = self.run(cmd) {
120
                    tracing::warn!("bind failed: {e}");
121
                }
122
            }
123
            Some(Some(Action::Vt(vt))) => {
124
                if let Some(drm) = self.backend.drm_mut()
125
                    && let Err(e) = drm.session.change_vt(vt)
126
                {
127
                    tracing::warn!("switching to VT {vt} failed: {e}");
128
                }
129
            }
130
            _ => {}
131
        }
132
    }
133
}
134
135
enum Action {
136
    Run(monolith_ipc::Command),
137
    Vt(i32),
138
}
139
140
fn focus_under_pointer(state: &mut State) {
141
    let pos = state.input.pointer_pos;
142
    let layers = [Layer::Overlay, Layer::Top, Layer::Bottom, Layer::Background];
143
    if let Some((layer, surface, _)) = state.layer_under(pos, &layers)
144
        && layer.can_receive_keyboard_focus()
145
    {
146
        state.wm.layer_focus = Some(surface);
147
        state.sync_focus();
148
        return;
149
    }
150
    if state.wm.layer_focus.take().is_some() {
151
        state.sync_focus();
152
    }
153
    let id = window_at(state, pos);
154
    if let Some(id) = id
155
        && state.wm.layout.focused() != Some(id)
156
    {
157
        state.input.followed = Some(id);
158
        state.wm.layout.focus(id);
159
        state.apply_layout();
160
    }
161
}
162
163
fn window_at(state: &State, pos: Point<f64, Logical>) -> Option<WindowId> {
164
    let (w, _) = state.wm.space.element_under(pos)?;
165
    state.window_id(&crate::wm::root(w)?)
166
}
167
168
/// Layers above windows, like a bar, keep the focus where it was.
169
/// Focus changes only when the pointer enters another window. Moving
170
/// inside one, or being warped, leaves focus alone, so a new window keeps
171
/// it until the pointer actually goes somewhere else.
172
fn focus_on_hover(state: &mut State, pos: Point<f64, Logical>, warped: bool) {
173
    if !state.settings.config.general.focus_follows_mouse
174
        || state.services.lock.active()
175
        || state
176
            .layer_under(pos, &[Layer::Overlay, Layer::Top])
177
            .is_some()
178
    {
179
        return;
180
    }
181
    let under = window_at(state, pos);
182
    if std::mem::replace(&mut state.input.hovered, under) == under || warped {
183
        return;
184
    }
185
    let Some(id) = under else {
186
        return;
187
    };
188
    if state.wm.layout.focused() == Some(id) {
189
        return;
190
    }
191
    state.wm.layer_focus = None;
192
    state.input.followed = Some(id);
193
    state.wm.layout.focus(id);
194
    state.apply_layout();
195
}
196
197
impl State {
198
    /// Puts the pointer in the middle of a window that gained focus from
199
    /// anything but the pointer.
200
    pub fn follow_focus(&mut self) {
201
        let focused = self.wm.layout.focused();
202
        if focused == std::mem::replace(&mut self.input.followed, focused)
203
            || !self.settings.config.general.mouse_follows_focus
204
            || self.backend.drm().is_none()
205
            || self.services.lock.active()
206
        {
207
            return;
208
        }
209
        let Some((_, r, _)) = focused.and_then(|id| self.wm.get(id)?.placed) else {
210
            return;
211
        };
212
        self.input.warping = true;
213
        self.pointer_to(
214
            Point::from((r.x + r.w / 2.0, r.y + r.h / 2.0)),
215
            InputTime::now(),
216
        );
217
        self.input.warping = false;
218
        self.pointer_frame();
219
        self.dirty.cursor = true;
220
    }
221
}
222
223
const BTN_LEFT: u32 = 0x110;
224
const BTN_RIGHT: u32 = 0x111;
225
226
/// A floating window being moved or resized with Mod and a mouse button.
227
pub struct Drag {
228
    window: WindowId,
229
    from: Point<f64, Logical>,
230
    rect: Rect,
231
    resize: bool,
232
}
233
234
fn start_drag(state: &mut State, button: u32) -> bool {
235
    if button != BTN_LEFT && button != BTN_RIGHT {
236
        return false;
237
    }
238
    let held = state
239
        .input
240
        .seat
241
        .get_keyboard()
242
        .map(|k| k.modifier_state())
243
        .is_some_and(|m| crate::commands::held(&m).has(state.settings.config.general.mod_key));
244
    let Some(id) = state.wm.layout.focused() else {
245
        return false;
246
    };
247
    let Some(rect) = state
248
        .wm
249
        .layout
250
        .window(id)
251
        .filter(|w| w.floating && !w.fullscreen)
252
        .and_then(|w| w.float_rect)
253
    else {
254
        return false;
255
    };
256
    if !held {
257
        return false;
258
    }
259
    state.input.drag = Some(Drag {
260
        window: id,
261
        from: state.input.pointer_pos,
262
        rect,
263
        resize: button == BTN_RIGHT,
264
    });
265
    true
266
}
267
268
fn drag_to(state: &mut State, pos: Point<f64, Logical>) -> bool {
269
    let Some(d) = &state.input.drag else {
270
        return false;
271
    };
272
    let (dx, dy) = (pos.x - d.from.x, pos.y - d.from.y);
273
    let r = d.rect;
274
    let rect = if d.resize {
275
        Rect::new(r.x, r.y, (r.w + dx).max(32.0), (r.h + dy).max(32.0))
276
    } else {
277
        Rect::new(r.x + dx, r.y + dy, r.w, r.h)
278
    };
279
    let id = d.window;
280
    state.wm.layout.set_float_rect(id, rect);
281
    state.apply_layout();
282
    true
283
}