Implement warp-mouse-to-focus

This commit is contained in:
FluxTape
2024-02-26 18:47:46 +01:00
committed by Ivan Molodetskikh
parent 0c57815fbf
commit 741bee461c
7 changed files with 214 additions and 18 deletions
+5
View File
@@ -69,6 +69,8 @@ pub struct Input {
pub touch: Touch,
#[knuffel(child)]
pub disable_power_key_handling: bool,
#[knuffel(child)]
pub warp_mouse_to_focus: bool,
}
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)]
@@ -1592,6 +1594,8 @@ mod tests {
}
disable-power-key-handling
warp-mouse-to-focus
}
output "eDP-1" {
@@ -1731,6 +1735,7 @@ mod tests {
map_to_output: Some("eDP-1".to_owned()),
},
disable_power_key_handling: true,
warp_mouse_to_focus: true,
},
outputs: vec![Output {
off: false,
+3
View File
@@ -67,6 +67,9 @@ input {
// Uncomment this if you would like to configure the power button elsewhere
// (i.e. logind.conf).
// disable-power-key-handling
// Uncomment this to make the mouse warp to the center of newly focused windows.
// warp-mouse-to-focus
}
// You can configure outputs by their name, which you can find
+78 -16
View File
@@ -411,139 +411,166 @@ impl State {
}
Action::MoveColumnLeft => {
self.niri.layout.move_left();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnRight => {
self.niri.layout.move_right();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnToFirst => {
self.niri.layout.move_column_to_first();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnToLast => {
self.niri.layout.move_column_to_last();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowDown => {
self.niri.layout.move_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowUp => {
self.niri.layout.move_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowDownOrToWorkspaceDown => {
self.niri.layout.move_down_or_to_workspace_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowUpOrToWorkspaceUp => {
self.niri.layout.move_up_or_to_workspace_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::ConsumeOrExpelWindowLeft => {
self.niri.layout.consume_or_expel_window_left();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::ConsumeOrExpelWindowRight => {
self.niri.layout.consume_or_expel_window_right();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusColumnLeft => {
self.niri.layout.focus_left();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusColumnRight => {
self.niri.layout.focus_right();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusColumnFirst => {
self.niri.layout.focus_column_first();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusColumnLast => {
self.niri.layout.focus_column_last();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWindowDown => {
self.niri.layout.focus_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWindowUp => {
self.niri.layout.focus_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWindowOrWorkspaceDown => {
self.niri.layout.focus_window_or_workspace_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWindowOrWorkspaceUp => {
self.niri.layout.focus_window_or_workspace_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowToWorkspaceDown => {
self.niri.layout.move_to_workspace_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowToWorkspaceUp => {
self.niri.layout.move_to_workspace_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveWindowToWorkspace(idx) => {
let idx = idx.saturating_sub(1) as usize;
self.niri.layout.move_to_workspace(idx);
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnToWorkspaceDown => {
self.niri.layout.move_column_to_workspace_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnToWorkspaceUp => {
self.niri.layout.move_column_to_workspace_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::MoveColumnToWorkspace(idx) => {
let idx = idx.saturating_sub(1) as usize;
self.niri.layout.move_column_to_workspace(idx);
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWorkspaceDown => {
self.niri.layout.switch_workspace_down();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWorkspaceUp => {
self.niri.layout.switch_workspace_up();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::FocusWorkspace(idx) => {
let idx = idx.saturating_sub(1) as usize;
self.niri.layout.switch_workspace(idx);
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
@@ -559,11 +586,14 @@ impl State {
}
Action::ConsumeWindowIntoColumn => {
self.niri.layout.consume_into_column();
// This does not cause immediate focus or window size change, so warping mouse to
// focus won't do anything here.
// FIXME: granular
self.niri.queue_redraw_all();
}
Action::ExpelWindowFromColumn => {
self.niri.layout.expel_from_column();
self.maybe_warp_cursor_to_focus();
// FIXME: granular
self.niri.queue_redraw_all();
}
@@ -581,81 +611,105 @@ impl State {
Action::FocusMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::FocusMonitorRight => {
if let Some(output) = self.niri.output_right() {
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::FocusMonitorDown => {
if let Some(output) = self.niri.output_down() {
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::FocusMonitorUp => {
if let Some(output) = self.niri.output_up() {
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWindowToMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.move_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWindowToMonitorRight => {
if let Some(output) = self.niri.output_right() {
self.niri.layout.move_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWindowToMonitorDown => {
if let Some(output) = self.niri.output_down() {
self.niri.layout.move_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWindowToMonitorUp => {
if let Some(output) = self.niri.output_up() {
self.niri.layout.move_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveColumnToMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.move_column_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveColumnToMonitorRight => {
if let Some(output) = self.niri.output_right() {
self.niri.layout.move_column_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveColumnToMonitorDown => {
if let Some(output) = self.niri.output_down() {
self.niri.layout.move_column_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveColumnToMonitorUp => {
if let Some(output) = self.niri.output_up() {
self.niri.layout.move_column_to_output(&output);
self.niri.layout.focus_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::SetColumnWidth(change) => {
@@ -672,25 +726,33 @@ impl State {
Action::MoveWorkspaceToMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWorkspaceToMonitorRight => {
if let Some(output) = self.niri.output_right() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWorkspaceToMonitorDown => {
if let Some(output) = self.niri.output_down() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
Action::MoveWorkspaceToMonitorUp => {
if let Some(output) = self.niri.output_up() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
if !self.maybe_warp_cursor_to_focus_centered() {
self.move_cursor_to_output(&output);
}
}
}
}
+8
View File
@@ -583,6 +583,14 @@ impl<W: LayoutElement> Monitor<W> {
self.clean_up_workspaces();
}
/// Returns the geometry of the active tile relative to and clamped to the output.
///
/// During animations, assumes the final view position.
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<i32, Logical>> {
// FIXME: switch gesture.
self.active_workspace_ref().active_tile_visual_rectangle()
}
pub fn window_under(
&self,
pos_within_output: Point<f64, Logical>,
+38
View File
@@ -181,6 +181,15 @@ impl OutputId {
}
}
impl ViewOffsetAdjustment {
pub fn target_view_offset(&self) -> f64 {
match self {
ViewOffsetAdjustment::Animation(anim) => anim.to(),
ViewOffsetAdjustment::Gesture(gesture) => gesture.current_view_offset,
}
}
}
impl ColumnWidth {
fn resolve(self, options: &Options, view_width: i32) -> i32 {
match self {
@@ -1124,6 +1133,31 @@ impl<W: LayoutElement> Workspace<W> {
first.chain(rest)
}
fn active_column_ref(&self) -> Option<&Column<W>> {
if self.columns.is_empty() {
return None;
}
Some(&self.columns[self.active_column_idx])
}
/// Returns the geometry of the active tile relative to and clamped to the view.
///
/// During animations, assumes the final view position.
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<i32, Logical>> {
let col = self.active_column_ref()?;
let view_pos = self
.view_offset_adj
.as_ref()
.map_or(self.view_offset, |adj| adj.target_view_offset() as i32);
let tile_pos = Point::from((-view_pos, col.tile_y(col.active_tile_idx)));
let tile_size = col.active_tile_ref().tile_size();
let tile_rect = Rectangle::from_loc_and_size(tile_pos, tile_size);
let view = Rectangle::from_loc_and_size((0, 0), self.view_size);
view.intersection(tile_rect)
}
pub fn window_under(
&self,
pos: Point<f64, Logical>,
@@ -2010,6 +2044,10 @@ impl<W: LayoutElement> Column<W> {
pos
})
}
fn active_tile_ref(&self) -> &Tile<W> {
&self.tiles[self.active_tile_idx]
}
}
fn compute_new_view_offset(
+78 -2
View File
@@ -112,7 +112,7 @@ use crate::ui::hotkey_overlay::HotkeyOverlay;
use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
use crate::utils::spawning::CHILD_ENV;
use crate::utils::{
center, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
};
use crate::window::Unmapped;
use crate::{animation, niri_render_elements};
@@ -197,7 +197,6 @@ pub struct Niri {
// popup grabs are active (which means the real keyboard focus is on a popup descending from
// this toplevel surface).
pub keyboard_focus: Option<WlSurface>,
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
pub is_fdo_idle_inhibited: Arc<AtomicBool>,
@@ -314,6 +313,11 @@ struct SurfaceFrameThrottlingState {
last_sent_at: RefCell<Option<(Output, u32)>>,
}
pub enum CenterCoords {
Seperately,
Both,
}
#[derive(Default)]
pub struct WindowOffscreenId(pub RefCell<Option<Id>>);
@@ -401,6 +405,78 @@ impl State {
self.niri.queue_redraw_all();
}
/// Moves cursor within the specified rectangle, only adjusting coordinates if needed.
fn move_cursor_to_rect(&mut self, rect: Rectangle<f64, Logical>, mode: CenterCoords) -> bool {
let pointer = &self.niri.seat.get_pointer().unwrap();
let cur_loc = pointer.current_location();
let x_in_bound = cur_loc.x >= rect.loc.x && cur_loc.x <= rect.loc.x + rect.size.w;
let y_in_bound = cur_loc.y >= rect.loc.y && cur_loc.y <= rect.loc.y + rect.size.h;
let p = match mode {
CenterCoords::Seperately => {
if x_in_bound && y_in_bound {
return false;
} else if y_in_bound {
// adjust x
Point::from((rect.loc.x + rect.size.w / 2.0, cur_loc.y))
} else if x_in_bound {
// adjust y
Point::from((cur_loc.x, rect.loc.y + rect.size.h / 2.0))
} else {
// adjust x and y
center_f64(rect)
}
}
CenterCoords::Both => {
if x_in_bound && y_in_bound {
return false;
} else {
// adjust x and y
center_f64(rect)
}
}
};
self.move_cursor(p);
true
}
pub fn move_cursor_to_focused_tile(&mut self, mode: CenterCoords) -> bool {
let Some(output) = self.niri.layout.active_output() else {
return false;
};
let output = output.clone();
let monitor = self.niri.layout.monitor_for_output(&output).unwrap();
let mut rv = false;
let rect = monitor.active_tile_visual_rectangle();
if let Some(rect) = rect {
let output_geo = self.niri.global_space.output_geometry(&output).unwrap();
let mut rect = rect;
rect.loc += output_geo.loc;
rv = self.move_cursor_to_rect(rect.to_f64(), mode);
}
rv
}
pub fn maybe_warp_cursor_to_focus(&mut self) -> bool {
if !self.niri.config.borrow().input.warp_mouse_to_focus {
return false;
}
self.move_cursor_to_focused_tile(CenterCoords::Seperately)
}
pub fn maybe_warp_cursor_to_focus_centered(&mut self) -> bool {
if !self.niri.config.borrow().input.warp_mouse_to_focus {
return false;
}
self.move_cursor_to_focused_tile(CenterCoords::Both)
}
pub fn refresh_pointer_focus(&mut self) {
let _span = tracy_client::span!("Niri::refresh_pointer_focus");
+4
View File
@@ -40,6 +40,10 @@ pub fn center(rect: Rectangle<i32, Logical>) -> Point<i32, Logical> {
rect.loc + rect.size.downscale(2).to_point()
}
pub fn center_f64(rect: Rectangle<f64, Logical>) -> Point<f64, Logical> {
rect.loc + rect.size.downscale(2.0).to_point()
}
pub fn output_size(output: &Output) -> Size<i32, Logical> {
let output_scale = output.current_scale().integer_scale();
let output_transform = output.current_transform();