diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 0ff249a76..39069e212 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -310,7 +310,7 @@ impl State { for seat in self.common.shell.read().seats.iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&WinitVirtualDevice) { - seat.set_active_output(&self.backend.winit().output); + seat.set_active_output(&self.backend.winit().output, false); break; } } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index bd0e1b34b..e6a27aa23 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -534,7 +534,7 @@ impl State { for seat in self.common.shell.read().seats.iter() { let devices = seat.user_data().get::().unwrap(); if devices.has_device(&device) { - seat.set_active_output(&output); + seat.set_active_output(&output, false); break; } } diff --git a/src/input/actions.rs b/src/input/actions.rs index 3d017697d..62b18a724 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -550,7 +550,7 @@ impl State { WorkspaceDelta::new_shortcut(), &mut workspace_guard, ); - seat.set_active_output(&next_output); + seat.set_active_output(&next_output, false); res }; diff --git a/src/input/mod.rs b/src/input/mod.rs index 0cb26a9da..0ea71dea4 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -645,7 +645,7 @@ impl State { for session in cursor_sessions_for_output(&shell, ¤t_output) { session.set_cursor_pos(None); } - seat.set_active_output(&output); + seat.set_active_output(&output, false); } update_output_image_copy_cursor_position( diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index cab37a858..1350b41a1 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -522,7 +522,7 @@ impl Common { } if !shell.outputs().any(|o| o == &active_output) { if let Some(other) = shell.outputs().next() { - seat.set_active_output(other); + seat.set_active_output(other, true); } continue; } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 39d692e2d..70f0855c6 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -944,7 +944,7 @@ impl Workspaces { if let Some(new_output) = new_output { for seat in seats { if &seat.active_output() == output { - seat.set_active_output(&new_output); + seat.set_active_output(&new_output, true); } if seat.focused_output().as_ref() == Some(output) { seat.set_focused_output(None); @@ -3144,7 +3144,7 @@ impl Shell { node, focus_stack, .. })) => { let new_pos = if follow { - seat.set_active_output(to_output); + seat.set_active_output(to_output, false); self.workspaces .idx_for_handle(to_output, &to) .and_then(|to_idx| { @@ -3362,7 +3362,7 @@ impl Shell { let new_pos = if follow { if let Some(seat) = seat { - seat.set_active_output(&to_output); + seat.set_active_output(&to_output, false); } self.workspaces .idx_for_handle(&to_output, to) @@ -3473,7 +3473,7 @@ impl Shell { } let new_pos = if follow { if let Some(seat) = seat { - seat.set_active_output(&to_output); + seat.set_active_output(&to_output, false); } self.workspaces .idx_for_handle(&to_output, to) diff --git a/src/shell/seats.rs b/src/shell/seats.rs index 11211e907..64aefdb20 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -237,12 +237,31 @@ pub fn create_seat( ) }) .expect("Failed to load xkb configuration files"); - seat.add_pointer(); + + let pointer = seat.add_pointer(); + + // If possible, set the cursor's position to the center of the screen upon initialization + if let Some(position) = get_logical_center_of_output(output) { + pointer.set_location(position); + } + seat.add_touch(); seat } +fn get_logical_center_of_output(output: &Output) -> Option> { + output.current_mode().map(|mode| { + let output_position = output.current_location(); + let scale = output.current_scale().fractional_scale(); + let logical_size = mode.size.to_f64().to_logical(scale); + Point::new( + output_position.x as f64 + logical_size.w / 2.0, + output_position.y as f64 + logical_size.h / 2.0, + ) + }) +} + #[derive(Debug, Copy, Clone)] pub struct CursorGeometry { pub geometry: Rectangle, @@ -258,7 +277,7 @@ pub trait SeatExt { self.focused_output() .unwrap_or_else(|| self.active_output()) } - fn set_active_output(&self, output: &Output); + fn set_active_output(&self, output: &Output, move_cursor_to_center: bool); fn set_focused_output(&self, output: Option<&Output>); fn devices(&self) -> &Devices; fn supressed_keys(&self) -> &SupressedKeys; @@ -268,6 +287,12 @@ pub trait SeatExt { fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point)>; fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point)>); + fn get_pointer_position_relative_to_active_output(&self) -> Option>; + fn set_pointer_position_relative_to_active_output( + &self, + relative_position: Point, + ); + fn cursor_geometry( &self, loc: impl Into>, @@ -306,7 +331,7 @@ impl SeatExt for Seat { } } - fn set_active_output(&self, output: &Output) { + fn set_active_output(&self, output: &Output, move_cursor_to_center: bool) { *self .user_data() .get::() @@ -314,6 +339,15 @@ impl SeatExt for Seat { .0 .lock() .unwrap() = output.clone(); + + if move_cursor_to_center { + // Update the position of the cursor to the center + if let (Some(pointer), Some(position)) = + (self.get_pointer(), get_logical_center_of_output(output)) + { + pointer.set_location(position); + } + } } fn set_focused_output(&self, output: Option<&Output>) { @@ -369,6 +403,32 @@ impl SeatExt for Seat { *lock.0.lock().unwrap() = hint; } + fn get_pointer_position_relative_to_active_output(&self) -> Option> { + let output = self.active_output(); + + let pointer_position = self.get_pointer()?.current_location(); + let output_position = output.current_location(); + + Some(Point::new( + pointer_position.x - output_position.x as f64, + pointer_position.y - output_position.y as f64, + )) + } + fn set_pointer_position_relative_to_active_output( + &self, + relative_position: Point, + ) { + let output = self.active_output(); + let output_position = output.current_location(); + + if let Some(pointer) = self.get_pointer() { + pointer.set_location(Point::new( + relative_position.x + output_position.x as f64, + relative_position.y + output_position.y as f64, + )); + } + } + fn cursor_geometry( &self, loc: impl Into>, diff --git a/src/state.rs b/src/state.rs index 24ccd9e1e..c17c6d288 100644 --- a/src/state.rs +++ b/src/state.rs @@ -499,6 +499,20 @@ impl LockedBackend<'_> { ) -> Result<(), anyhow::Error> { let all_outputs = self.all_outputs(); + // Save the positions of the cursor relative to the output + let saved_cursor_positions = { + let guard = shell.read(); + guard + .seats + .iter() + .filter_map(|seat| { + let rel_pos = seat.get_pointer_position_relative_to_active_output()?; + let active_output = seat.active_output(); + Some((seat.clone(), active_output, rel_pos)) + }) + .collect::>() + }; + // update outputs, so that `OutputModeSource`s are correct for output in &all_outputs { // apply to Output @@ -528,6 +542,13 @@ impl LockedBackend<'_> { output.set_adaptive_sync(final_config.0.vrr); } + // Restore cursor position relative to active output + for (seat, saved_output, rel_pos) in &saved_cursor_positions { + if &seat.active_output() == saved_output { + seat.set_pointer_position_relative_to_active_output(*rel_pos); + } + } + match self { LockedBackend::Kms(state) => state.apply_config_for_outputs( test_only, diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 3da399529..6f895e268 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -115,7 +115,7 @@ impl ToplevelManagementHandler for State { && self.common.config.cosmic_conf.cursor_follows_focus && let Some(new_pos) = new_pos { - seat.set_active_output(output); + seat.set_active_output(output, false); if let Some(ptr) = seat.get_pointer() { let serial = SERIAL_COUNTER.next_serial(); ptr.motion(