diff --git a/deploy-notes.md b/deploy-notes.md index 5a287532..1ef1a2f9 100644 --- a/deploy-notes.md +++ b/deploy-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Needs jay-config release. +- Needs jay-toml-config release. - Needs jay-compositor release. # 1.1.0 diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index fcc42e91..8675d299 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -10,7 +10,10 @@ use { logging, Config, ConfigEntry, ConfigEntryGen, PollableId, WireMode, VERSION, }, exec::Command, - input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat}, + input::{ + acceleration::AccelProfile, capability::Capability, FocusFollowsMouseMode, InputDevice, + Seat, + }, keyboard::{ mods::{Modifiers, RELEASE}, syms::KeySym, @@ -924,6 +927,10 @@ impl Client { self.send(&ClientMessage::SetForward { seat, forward }) } + pub fn set_focus_follows_mouse_mode(&self, seat: Seat, mode: FocusFollowsMouseMode) { + self.send(&ClientMessage::SetFocusFollowsMouseMode { seat, mode }) + } + pub fn parse_keymap(&self, keymap: &str) -> Keymap { let res = self.send_with_response(&ClientMessage::ParseKeymap { keymap }); get_response!(res, Keymap(0), ParseKeymap { keymap }); diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 4d170c9a..f0bc6e91 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -1,6 +1,9 @@ use { crate::{ - input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat}, + input::{ + acceleration::AccelProfile, capability::Capability, FocusFollowsMouseMode, InputDevice, + Seat, + }, keyboard::{mods::Modifiers, syms::KeySym, Keymap}, logging::LogLevel, theme::{colors::Colorable, sized::Resizable, Color}, @@ -464,6 +467,10 @@ pub enum ClientMessage<'a> { mod_mask: Modifiers, sym: KeySym, }, + SetFocusFollowsMouseMode { + seat: Seat, + mode: FocusFollowsMouseMode, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index e42b5787..ace0f0fe 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -390,6 +390,21 @@ impl Seat { pub fn consume(self) { self.set_forward(false) } + + /// Sets the focus-follows-mouse mode. + pub fn set_focus_follows_mouse_mode(self, mode: FocusFollowsMouseMode) { + get!().set_focus_follows_mouse_mode(self, mode); + } +} + +/// A focus-follows-mouse mode. +#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum FocusFollowsMouseMode { + /// When the mouse moves and enters a toplevel, that toplevel gets the keyboard focus. + True, + /// The keyboard focus changes only when clicking on a window or the previously + /// focused window becomes invisible. + False, } /// Returns all seats. diff --git a/release-notes.md b/release-notes.md index 7e377eee..5652ff38 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,6 +4,7 @@ - Add support for xdg-dialog-v1. - Add support for ext-transient-seat-v1. - Add support for wp-drm-lease-v1. +- Focus-follows-mouse can now be disabled. # 1.1.0 (2024-04-22) diff --git a/src/config/handler.rs b/src/config/handler.rs index a3061c8d..a5e74c19 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -41,7 +41,7 @@ use { Capability, CAP_GESTURE, CAP_KEYBOARD, CAP_POINTER, CAP_SWITCH, CAP_TABLET_PAD, CAP_TABLET_TOOL, CAP_TOUCH, }, - InputDevice, Seat, + FocusFollowsMouseMode, InputDevice, Seat, }, keyboard::{mods::Modifiers, syms::KeySym, Keymap}, logging::LogLevel, @@ -324,6 +324,20 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_focus_follows_mouse_mode( + &self, + seat: Seat, + mode: FocusFollowsMouseMode, + ) -> Result<(), CphError> { + let seat = self.get_seat(seat)?; + let focus_follows_mouse = match mode { + FocusFollowsMouseMode::True => true, + FocusFollowsMouseMode::False => false, + }; + seat.set_focus_follows_mouse(focus_follows_mouse); + Ok(()) + } + fn handle_set_status(&self, status: &str) { self.state.set_status(status); } @@ -1791,6 +1805,9 @@ impl ConfigProxyHandler { } => self .handle_add_shortcut(seat, mod_mask, mods, sym) .wrn("add_shortcut")?, + ClientMessage::SetFocusFollowsMouseMode { seat, mode } => self + .handle_set_focus_follows_mouse_mode(seat, mode) + .wrn("set_focus_follows_mouse_mode")?, } Ok(()) } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index abfda057..eae653fa 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -181,6 +181,7 @@ pub struct WlSeatGlobal { input_method: CloneCell>>, input_method_grab: CloneCell>>, forward: Cell, + focus_follows_mouse: Cell, } const CHANGE_CURSOR_MOVED: u32 = 1 << 0; @@ -250,6 +251,7 @@ impl WlSeatGlobal { input_method: Default::default(), input_method_grab: Default::default(), forward: Cell::new(false), + focus_follows_mouse: Cell::new(true), }); state.add_cursor_size(*DEFAULT_CURSOR_SIZE); let seat = slf.clone(); @@ -1165,6 +1167,10 @@ impl WlSeatGlobal { pub fn select_workspace(self: &Rc, selector: impl WorkspaceSelector) { self.pointer_owner.select_workspace(self, selector); } + + pub fn set_focus_follows_mouse(&self, focus_follows_mouse: bool) { + self.focus_follows_mouse.set(focus_follows_mouse); + } } global_base!(WlSeatGlobal, WlSeat, WlSeatError); diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index deb943cb..4dc90780 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -756,7 +756,10 @@ impl WlSeatGlobal { // Enter callbacks impl WlSeatGlobal { pub fn enter_toplevel(self: &Rc, n: Rc) { - if n.tl_accepts_keyboard_focus() && self.changes.get().contains(CHANGE_CURSOR_MOVED) { + if n.tl_accepts_keyboard_focus() + && self.changes.get().contains(CHANGE_CURSOR_MOVED) + && self.focus_follows_mouse.get() + { self.focus_toplevel(n); } } diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 7f7e1a62..e7436ba6 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -312,6 +312,7 @@ pub struct Config { pub inputs: Vec, pub idle: Option, pub explicit_sync_enabled: Option, + pub focus_follows_mouse: bool, } #[derive(Debug, Error)] diff --git a/toml-config/src/config/parsers/config.rs b/toml-config/src/config/parsers/config.rs index 837bfdba..27038a31 100644 --- a/toml-config/src/config/parsers/config.rs +++ b/toml-config/src/config/parsers/config.rs @@ -97,7 +97,7 @@ impl Parser for ConfigParser<'_> { _, idle_val, ), - (explicit_sync, repeat_rate_val, complex_shortcuts_val), + (explicit_sync, repeat_rate_val, complex_shortcuts_val, focus_follows_mouse), ) = ext.extract(( ( opt(val("keymap")), @@ -127,6 +127,7 @@ impl Parser for ConfigParser<'_> { recover(opt(bol("explicit-sync"))), opt(val("repeat-rate")), opt(val("complex-shortcuts")), + recover(opt(bol("focus-follows-mouse"))), ), ))?; let mut keymap = None; @@ -307,6 +308,7 @@ impl Parser for ConfigParser<'_> { render_device, inputs, idle, + focus_follows_mouse: focus_follows_mouse.despan().unwrap_or(true), }) } } diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 33ba0b6d..52b3f6a9 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -15,7 +15,9 @@ use { config, config_dir, exec::{set_env, unset_env, Command}, get_workspace, - input::{get_seat, input_devices, on_new_input_device, InputDevice, Seat}, + input::{ + get_seat, input_devices, on_new_input_device, FocusFollowsMouseMode, InputDevice, Seat, + }, is_reload, keyboard::{Keymap, ModifiedKeySym}, logging::set_log_level, @@ -869,6 +871,12 @@ fn load_config(initial_load: bool, persistent: &Rc) { } } }); + persistent + .seat + .set_focus_follows_mouse_mode(match config.focus_follows_mouse { + true => FocusFollowsMouseMode::True, + false => FocusFollowsMouseMode::False, + }); } fn create_command(exec: &Exec) -> Command { diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index ebf1ba64..5b5ac4da 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -569,6 +569,10 @@ "idle": { "description": "The configuration of the idle timeout.\n\nChanging thise field after compositor startup has no effect. Use `jay idle`\nor a `configure-idle` action to change the idle timeout at runtime.\n\n- Example:\n\n ```toml\n idle.minutes = 10\n ```\n", "$ref": "#/$defs/Idle" + }, + "focus-follows-mouse": { + "type": "boolean", + "description": "Configures whether moving the mouse over a window automatically moves the keyboard\nfocus to that window.\n\nThe default is `true`.\n" } }, "required": [] diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index c2e7e11e..0c950fac 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1086,6 +1086,15 @@ The table has the following fields: The value of this field should be a [Idle](#types-Idle). +- `focus-follows-mouse` (optional): + + Configures whether moving the mouse over a window automatically moves the keyboard + focus to that window. + + The default is `true`. + + The value of this field should be a boolean. + ### `Connector` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 4454b080..c445ef6e 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -2039,6 +2039,14 @@ Config: ```toml idle.minutes = 10 ``` + focus-follows-mouse: + kind: boolean + required: false + description: | + Configures whether moving the mouse over a window automatically moves the keyboard + focus to that window. + + The default is `true`. Idle: