Commit 24fca1aad608
tarball4 files changed+68-6
@@ -12,6 +12,11 @@ gaps = { inner = 8, outer = 8 }
1212
# What a window move does when it meets a neighbour. swap trades places,1313
# merge joins its lane. invert-move does the other one for the next move.1414
move = "swap"15
+# Focus whatever window the pointer moves onto.16
+focus_follows_mouse = false17
+# Put the pointer in the middle of a window focused from the keyboard, a18
+# rule or a new map. Bare metal only, a nested pointer belongs to the host.19
+mouse_follows_focus = false1520
1621
[decoration]1722
# Borders sit in the gap around each window, so keep them under half the inner gap.@@ -31,6 +31,8 @@ pub struct General {
3131
/// What a window move does when it meets a neighbour.3232
#[serde(rename = "move")]3333
pub move_mode: MoveMode,34
+ pub focus_follows_mouse: bool,35
+ pub mouse_follows_focus: bool,3436
}3537
3638
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]@@ -49,6 +51,8 @@ impl Default for General {
4951
mod_key: Modifier::Super,5052
gaps: Gaps::default(),5153
move_mode: MoveMode::Swap,54
+ focus_follows_mouse: false,55
+ mouse_follows_focus: false,5256
}5357
}5458
}@@ -20,6 +20,7 @@ impl State {
2020
let Some(pointer) = self.input.seat.get_pointer() else {2121
return;2222
};23
+ focus_on_hover(self, pos);2324
let under = self.surface_under(pos);2425
pointer.motion(2526
self,@@ -149,20 +150,67 @@ fn focus_under_pointer(state: &mut State) {
149150
if state.wm.layer_focus.take().is_some() {150151
state.sync_focus();151152
}152
- let id = state153
- .wm154
- .space155
- .element_under(pos)156
- .and_then(|(w, _)| crate::wm::root(w))157
- .and_then(|s| state.window_id(&s));153
+ let id = window_at(state, pos);158154
if let Some(id) = id159155
&& state.wm.layout.focused() != Some(id)160156
{157
+ state.input.followed = Some(id);161158
state.wm.layout.focus(id);162159
state.apply_layout();163160
}164161
}165162
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
+fn focus_on_hover(state: &mut State, pos: Point<f64, Logical>) {170
+ if !state.settings.config.general.focus_follows_mouse171
+ || state.services.lock.active()172
+ || state173
+ .layer_under(pos, &[Layer::Overlay, Layer::Top])174
+ .is_some()175
+ {176
+ return;177
+ }178
+ let Some(id) = window_at(state, pos) else {179
+ return;180
+ };181
+ if state.wm.layout.focused() == Some(id) {182
+ return;183
+ }184
+ state.wm.layer_focus = None;185
+ state.input.followed = Some(id);186
+ state.wm.layout.focus(id);187
+ state.apply_layout();188
+}189
+190
+impl State {191
+ /// Puts the pointer in the middle of a window that gained focus from192
+ /// anything but the pointer.193
+ pub fn follow_focus(&mut self) {194
+ let focused = self.wm.layout.focused();195
+ if focused == std::mem::replace(&mut self.input.followed, focused)196
+ || !self.settings.config.general.mouse_follows_focus197
+ || self.backend.drm().is_none()198
+ || self.services.lock.active()199
+ {200
+ return;201
+ }202
+ let Some((_, r, _)) = focused.and_then(|id| self.wm.get(id)?.placed) else {203
+ return;204
+ };205
+ self.pointer_to(206
+ Point::from((r.x + r.w / 2.0, r.y + r.h / 2.0)),207
+ InputTime::now(),208
+ );209
+ self.pointer_frame();210
+ self.dirty.cursor = true;211
+ }212
+}213
+166214
const BTN_LEFT: u32 = 0x110;167215
const BTN_RIGHT: u32 = 0x111;168216
@@ -49,6 +49,9 @@ pub struct Input {
4949
pub suppressed: Vec<Keycode>,5050
pub drag: Option<crate::input::Drag>,5151
pub move_inverted: bool,52
+ /// The focus mouse-follows-focus last saw, so pointer-made focus53
+ /// changes can mark themselves as already followed.54
+ pub followed: Option<WindowId>,5255
pub cursor: crate::cursor::Cursor,5356
pub cursor_status: CursorImageStatus,5457
}@@ -167,6 +170,7 @@ impl State {
167170
suppressed: Vec::new(),168171
drag: None,169172
move_inverted: false,173
+ followed: None,170174
cursor: crate::cursor::Cursor::load(),171175
cursor_status: CursorImageStatus::default_named(),172176
},@@ -343,6 +347,7 @@ impl State {
343347
}344348
}345349
self.sync_focus();350
+ self.follow_focus();346351
self.update_inhibit();347352
self.dirty.redraw = true;348353
self.dirty.snapshot = true;