Skip to content

Commit

Permalink
added cursor_follows_focus and focus_follows_cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
skewballfox authored and Drakulix committed Sep 4, 2024
1 parent 52280e9 commit 7da0bc4
Show file tree
Hide file tree
Showing 22 changed files with 836 additions and 376 deletions.
2 changes: 2 additions & 0 deletions cosmic-comp-config/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
pub use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap};
use serde::{Deserialize, Serialize};

// Note: For the following values, None is used to represent the system default
// Configuration for input devices
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct InputConfig {
pub state: DeviceState,
Expand Down
9 changes: 9 additions & 0 deletions cosmic-comp-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ pub struct CosmicCompConfig {
pub autotile_behavior: TileBehavior,
/// Active hint enabled
pub active_hint: bool,
/// Enables changing keyboard focus to windows when the cursor passes into them
pub focus_follows_cursor: bool,
/// Enables warping the cursor to the focused window when focus changes due to keyboard input
pub cursor_follows_focus: bool,
/// The delay in milliseconds before focus follows mouse (if enabled)
pub focus_follows_cursor_delay: u64,
/// Let X11 applications scale themselves
pub descale_xwayland: bool,
}
Expand Down Expand Up @@ -50,6 +56,9 @@ impl Default for CosmicCompConfig {
autotile: Default::default(),
autotile_behavior: Default::default(),
active_hint: true,
focus_follows_cursor: false,
cursor_follows_focus: false,
focus_follows_cursor_delay: 250,
descale_xwayland: false,
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/backend/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ where
.lock()
.unwrap()
.is_some();
let active_output = last_active_seat.active_output();
let focused_output = last_active_seat.focused_or_active_output();
let output_size = output.geometry().size;
let output_scale = output.current_scale().fractional_scale();

Expand All @@ -670,7 +670,7 @@ where
.iter()
.find(|w| w.handle == current.0)
.ok_or(OutputNoMode)?;
let is_active_space = workspace.outputs().any(|o| o == &active_output);
let is_active_space = workspace.outputs().any(|o| o == &focused_output);

let has_fullscreen = workspace
.fullscreen
Expand Down Expand Up @@ -775,7 +775,7 @@ where
.space_for_handle(&previous)
.ok_or(OutputNoMode)?;
let has_fullscreen = workspace.fullscreen.is_some();
let is_active_space = workspace.outputs().any(|o| o == &active_output);
let is_active_space = workspace.outputs().any(|o| o == &focused_output);

let percentage = match start {
WorkspaceDelta::Shortcut(st) => ease(
Expand Down
18 changes: 18 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,24 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
state.common.update_xwayland_scale();
}
}
"focus_follows_cursor" => {
let new = get_config::<bool>(&config, "focus_follows_cursor");
if new != state.common.config.cosmic_conf.focus_follows_cursor {
state.common.config.cosmic_conf.focus_follows_cursor = new;
}
}
"cursor_follows_focus" => {
let new = get_config::<bool>(&config, "cursor_follows_focus");
if new != state.common.config.cosmic_conf.cursor_follows_focus {
state.common.config.cosmic_conf.cursor_follows_focus = new;
}
}
"focus_follows_cursor_delay" => {
let new = get_config::<u64>(&config, "focus_follows_cursor_delay");
if new != state.common.config.cosmic_conf.focus_follows_cursor_delay {
state.common.config.cosmic_conf.focus_follows_cursor_delay = new;
}
}
_ => {}
}
}
Expand Down
731 changes: 418 additions & 313 deletions src/input/mod.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/shell/element/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl CosmicWindowInternal {
pub fn current_focus(&self) -> Option<Focus> {
unsafe { Focus::from_u8(self.pointer_entered.load(Ordering::SeqCst)) }
}

/// returns if the window has any current or pending server-side decorations
pub fn has_ssd(&self, pending: bool) -> bool {
!self.window.is_decorated(pending)
}
Expand Down
126 changes: 98 additions & 28 deletions src/shell/focus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use smithay::{
utils::{IsAlive, Serial, SERIAL_COUNTER},
wayland::{
seat::WaylandFocus,
selection::data_device::set_data_device_focus,
selection::primary_selection::set_primary_focus,
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
shell::wlr_layer::{KeyboardInteractivity, Layer},
},
};
use std::{borrow::Cow, sync::Mutex};
use std::{borrow::Cow, mem, sync::Mutex};

use tracing::{debug, trace};

use self::target::{KeyboardFocusTarget, WindowGroup};
Expand All @@ -31,6 +31,7 @@ pub struct FocusStack<'a>(pub(super) Option<&'a IndexSet<CosmicMapped>>);
pub struct FocusStackMut<'a>(pub(super) &'a mut IndexSet<CosmicMapped>);

impl<'a> FocusStack<'a> {
/// returns the last unminimized window in the focus stack that is still alive
pub fn last(&self) -> Option<&CosmicMapped> {
self.0
.as_ref()
Expand Down Expand Up @@ -93,11 +94,15 @@ impl ActiveFocus {
}

impl Shell {
/// Set the keyboard focus to the given target
/// Note: `update_cursor` is used to determine whether to update the pointer location if cursor_follows_focus is enabled
/// if the focus change was due to a pointer event, this should be set to false
pub fn set_focus(
state: &mut State,
target: Option<&KeyboardFocusTarget>,
seat: &Seat<State>,
serial: Option<Serial>,
update_cursor: bool,
) {
let element = match target {
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
Expand All @@ -115,6 +120,7 @@ impl Shell {
if mapped.is_minimized() {
return;
}

state
.common
.shell
Expand All @@ -123,15 +129,7 @@ impl Shell {
.append_focus_stack(&mapped, seat);
}

// update keyboard focus
if let Some(keyboard) = seat.get_keyboard() {
ActiveFocus::set(seat, target.cloned());
keyboard.set_focus(
state,
target.cloned(),
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
);
}
update_focus_state(seat, target, state, serial, update_cursor);

state.common.shell.write().unwrap().update_active();
}
Expand All @@ -144,7 +142,8 @@ impl Shell {
// update FocusStack and notify layouts about new focus (if any window)
let workspace = self.space_for_mut(&mapped);
let workspace = if workspace.is_none() {
self.active_space_mut(&seat.active_output())
//should this be the active output or the focused output?
self.active_space_mut(&seat.focused_or_active_output())
} else {
workspace.unwrap()
};
Expand Down Expand Up @@ -218,6 +217,77 @@ impl Shell {
}
}

/// Internal, used to ensure that ActiveFocus, KeyboardFocusTarget, and FocusedOutput are all in sync
fn update_focus_state(
seat: &Seat<State>,
target: Option<&KeyboardFocusTarget>,
state: &mut State,
serial: Option<Serial>,
should_update_cursor: bool,
) {
// update keyboard focus
if let Some(keyboard) = seat.get_keyboard() {
if should_update_cursor && state.common.config.cosmic_conf.cursor_follows_focus {
if ActiveFocus::get(seat).as_ref() != target && target.is_some() {
//need to borrow mutably for surface under
let mut shell = state.common.shell.write().unwrap();
// get the top left corner of the target element
let geometry = shell.focused_geometry(target.unwrap());
//to avoid the nested mutable borrow of state
if geometry.is_some() {
let top_left = geometry.unwrap().loc.to_f64();

// create a pointer target from the target element
let output = shell
.outputs()
.find(|output| output.geometry().to_f64().contains(top_left))
.cloned()
.unwrap_or(seat.active_output());

let focus = shell
.surface_under(top_left, &output)
.map(|(focus, loc)| (focus, loc.as_logical()));
//drop here to avoid multiple mutable borrows
mem::drop(shell);
seat.get_pointer().unwrap().motion(
state,
focus,
&MotionEvent {
location: top_left.as_logical(),
serial: SERIAL_COUNTER.next_serial(),
time: 0,
},
);
}
}
}

ActiveFocus::set(seat, target.cloned());
keyboard.set_focus(
state,
target.cloned(),
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
);

//update the focused output or set it to the active output
if target.is_some() {
// Get focused output calls visible_output_for_surface internally
// what should happen if the target is some, but it's not visible?
// should this be an error?
seat.set_focused_output(
state
.common
.shell
.read()
.unwrap()
.get_focused_output(target.unwrap()),
)
} else {
seat.set_focused_output(None);
};
}
}

fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapped) {
if floating_layer.mapped().any(|m| m == focused) {
floating_layer.space.raise_element(focused, true);
Expand Down Expand Up @@ -264,13 +334,16 @@ impl Common {
update_pointer_focus(state, &seat);

let mut shell = state.common.shell.write().unwrap();
let output = seat.active_output();
let output = seat.focused_or_active_output();

// If the focused or active output is not in the list of outputs, switch to the first output
if !shell.outputs().any(|o| o == &output) {
if let Some(other) = shell.outputs().next() {
seat.set_active_output(other);
}
continue;
}

let last_known_focus = ActiveFocus::get(&seat);

if let Some(target) = last_known_focus {
Expand All @@ -291,15 +364,14 @@ impl Common {
if let Some(new) = popup_grab.current_grab() {
trace!("restore focus to previous popup grab");
std::mem::drop(shell);

if let Some(keyboard) = seat.get_keyboard() {
keyboard.set_focus(
state,
Some(new.clone()),
SERIAL_COUNTER.next_serial(),
);
}
ActiveFocus::set(&seat, Some(new));
// TODO: verify whether cursor should be updated at end of popup grab
update_focus_state(
seat,
Some(&new),
state,
Some(SERIAL_COUNTER.next_serial()),
false,
);
seat.user_data()
.get_or_insert::<PopupGrabData, _>(PopupGrabData::default)
.set(Some(popup_grab));
Expand Down Expand Up @@ -337,12 +409,10 @@ impl Common {
// update keyboard focus
let target = update_focus_target(&*shell, &seat, &output);
std::mem::drop(shell);
//I can probably feature gate this condition
debug!("Restoring focus to {:?}", target.as_ref());

if let Some(keyboard) = seat.get_keyboard() {
debug!("Restoring focus to {:?}", target.as_ref());
keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial());
ActiveFocus::set(&seat, target);
}
update_focus_state(seat, target.as_ref(), state, None, false);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/shell/grabs/menu/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) {
let seat = shell.seats.last_active().clone();
if let Some(new_focus) = shell.toggle_stacking(&seat, mapped) {
std::mem::drop(shell);
Shell::set_focus(state, Some(&new_focus), &seat, None);
Shell::set_focus(state, Some(&new_focus), &seat, None, false);
}
}

Expand Down Expand Up @@ -52,7 +52,7 @@ fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
);
if let Some((target, _)) = res {
std::mem::drop(shell);
Shell::set_focus(state, Some(&target), &seat, None);
Shell::set_focus(state, Some(&target), &seat, None, true);
}
}
}
Expand Down Expand Up @@ -85,7 +85,7 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
);
if let Some((target, _point)) = res {
std::mem::drop(shell);
Shell::set_focus(state, Some(&target), &seat, None)
Shell::set_focus(state, Some(&target), &seat, None, true)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/shell/grabs/menu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ impl Item {
}
}

/// Menu that comes up when right-clicking an application header bar
pub struct ContextMenu {
items: Vec<Item>,
selected: AtomicBool,
Expand Down
1 change: 1 addition & 0 deletions src/shell/grabs/moving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ impl Drop for MoveGrab {
Some(&KeyboardFocusTarget::from(mapped)),
&seat,
Some(serial),
false,
)
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/shell/layout/floating/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ impl FloatingLayout {
self.space.element_geometry(elem).map(RectExt::as_local)
}

pub fn element_under(&mut self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
pub fn element_under(&self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
self.space
.element_under(location.as_logical())
.map(|(mapped, _)| mapped.clone().into())
Expand Down
5 changes: 1 addition & 4 deletions src/shell/layout/tiling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3088,10 +3088,7 @@ impl TilingLayout {
None
}

pub fn element_under(
&mut self,
location_f64: Point<f64, Local>,
) -> Option<KeyboardFocusTarget> {
pub fn element_under(&self, location_f64: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
let location = location_f64.to_i32_round();

for (mapped, geo) in self.mapped() {
Expand Down
Loading

0 comments on commit 7da0bc4

Please sign in to comment.