Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use smithay::output::Output;
use smithay::reexports::rustix::fs::{fcntl_setfl, OFlags};
use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
use smithay::reexports::wayland_server::protocol::wl_pointer::WlPointer;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::Resource;
use smithay::utils::{Logical, Point, Rectangle, Serial};
Expand All @@ -38,6 +39,7 @@ use smithay::wayland::keyboard_shortcuts_inhibit::{
};
use smithay::wayland::output::OutputHandler;
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler};
use smithay::wayland::pointer_warp::PointerWarpHandler;
use smithay::wayland::security_context::{
SecurityContext, SecurityContextHandler, SecurityContextListenerSource,
};
Expand Down Expand Up @@ -89,7 +91,9 @@ use crate::protocols::virtual_pointer::{
VirtualPointerInputBackend, VirtualPointerManagerState, VirtualPointerMotionAbsoluteEvent,
VirtualPointerMotionEvent,
};
use crate::utils::{output_size, send_scale_transform};
use crate::utils::{
get_surface_size, get_surface_toplevel_coords, output_size, send_scale_transform,
};
use crate::{
delegate_ext_workspace, delegate_foreign_toplevel, delegate_gamma_control,
delegate_mutter_x11_interop, delegate_output_management, delegate_screencopy,
Expand Down Expand Up @@ -219,6 +223,62 @@ impl PointerConstraintsHandler for State {
}
delegate_pointer_constraints!(State);

impl PointerWarpHandler for State {
fn warp_pointer(
&mut self,
surface: WlSurface,
_pointer: WlPointer,
pos: Point<f64, Logical>,
serial: Serial,
) {
let Some(seat_pointer) = &self.niri.seat.get_pointer() else {
return;
};

let Some(last_serial) = seat_pointer.last_enter() else {
return;
};

if serial != last_serial {
return;
}

let surface_size = get_surface_size(&surface).to_f64();
if pos.x < 0. || pos.y < 0. || pos.x > surface_size.w || pos.y > surface_size.h {
return;
}

let Some((mapped, output)) = self.niri.layout.find_window_and_output(&surface) else {
return;
};

let Some(output) = output else {
return;
};

let Some(output_geo) = self.niri.global_space.output_geometry(output) else {
return;
};

let Some(monitor) = self.niri.layout.monitor_for_output(output) else {
return;
};

let Some(rect) = monitor.window_visual_rectangle(&mapped.window) else {
return;
};

let mut coords = pos;
coords += rect.loc;
coords += get_surface_toplevel_coords(&surface).to_f64();
coords += output_geo.loc.to_f64();

self.move_cursor(coords);
}
}

smithay::delegate_pointer_warp!(State);

impl InputMethodHandler for State {
fn new_popup(&mut self, surface: PopupSurface) {
let popup = PopupKind::InputMethod(surface);
Expand Down
16 changes: 16 additions & 0 deletions src/layout/floating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,22 @@ impl<W: LayoutElement> FloatingSpace<W> {
self.working_area.intersection(window_rect)
}

/// Returns the geometry of the window matching id relative to and clamped to the working area.
///
/// During animations, assumes the final tile position.
pub fn window_visual_rectangle(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
for (tile, pos) in self.tiles_with_offsets() {
if tile.window().id() == id {
let window_pos = pos + tile.window_loc();
let window_size = tile.window_size();
let window_rect = Rectangle::new(window_pos, window_size);
return self.working_area.intersection(window_rect);
}
}

None
}

pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
for (tile, pos) in self.tiles_with_offsets() {
if tile.window().id() == id {
Expand Down
13 changes: 13 additions & 0 deletions src/layout/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,19 @@ impl<W: LayoutElement> Monitor<W> {
self.active_workspace_ref().active_window_visual_rectangle()
}

/// Returns the geometry of the window matching id relative to and clamped to the view.
///
/// During animations, assumes the final view position.
pub fn window_visual_rectangle(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
if self.overview_open {
return None;
}

self.workspaces
.iter()
.find_map(|ws| ws.window_visual_rectangle(id))
}

fn workspace_size(&self, zoom: f64) -> Size<f64, Logical> {
let ws_size = self.view_size.upscale(zoom);
let scale = self.scale.fractional_scale();
Expand Down
23 changes: 23 additions & 0 deletions src/layout/scrolling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2559,6 +2559,29 @@ impl<W: LayoutElement> ScrollingSpace<W> {
view.intersection(window_rect)
}

/// Returns the geometry of the window matching id relative to and clamped to the view.
///
/// During animations, assumes the final view position.
pub fn window_visual_rectangle(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
let final_view_offset = self.view_offset.target();
let view_off = Point::from((-final_view_offset, 0.));

for col in &self.columns {
for (tile, pos) in col.tiles() {
if tile.window().id() == id {
let window_pos = view_off + pos + tile.window_loc();
let window_size = tile.window_size();
let window_rect = Rectangle::new(window_pos, window_size);

let view = Rectangle::from_size(self.view_size);
return view.intersection(window_rect);
}
}
}

None
}

pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
for col in &self.columns {
for (tile, pos) in col.tiles() {
Expand Down
8 changes: 8 additions & 0 deletions src/layout/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,14 @@ impl<W: LayoutElement> Workspace<W> {
}
}

pub fn window_visual_rectangle(&self, window: &W::Id) -> Option<Rectangle<f64, Logical>> {
if self.floating.has_window(window) {
self.floating.window_visual_rectangle(window)
} else {
self.scrolling.window_visual_rectangle(window)
}
}

pub fn popup_target_rect(&self, window: &W::Id) -> Option<Rectangle<f64, Logical>> {
if self.floating.has_window(window) {
self.floating.popup_target_rect(window)
Expand Down
4 changes: 4 additions & 0 deletions src/niri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ use smithay::wayland::keyboard_shortcuts_inhibit::{
use smithay::wayland::output::OutputManagerState;
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};
use smithay::wayland::pointer_gestures::PointerGesturesState;
use smithay::wayland::pointer_warp::PointerWarpManager;
use smithay::wayland::presentation::PresentationState;
use smithay::wayland::relative_pointer::RelativePointerManagerState;
use smithay::wayland::security_context::SecurityContextState;
Expand Down Expand Up @@ -298,6 +299,7 @@ pub struct Niri {
pub pointer_gestures_state: PointerGesturesState,
pub relative_pointer_state: RelativePointerManagerState,
pub pointer_constraints_state: PointerConstraintsState,
pub pointer_warp_manager: PointerWarpManager,
pub idle_notifier_state: IdleNotifierState<State>,
pub idle_inhibit_manager_state: IdleInhibitManagerState,
pub data_device_state: DataDeviceState,
Expand Down Expand Up @@ -2298,6 +2300,7 @@ impl Niri {
let pointer_gestures_state = PointerGesturesState::new::<State>(&display_handle);
let relative_pointer_state = RelativePointerManagerState::new::<State>(&display_handle);
let pointer_constraints_state = PointerConstraintsState::new::<State>(&display_handle);
let pointer_warp_manager = PointerWarpManager::new::<State>(&display_handle);
let idle_notifier_state = IdleNotifierState::new(&display_handle, event_loop.clone());
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(&display_handle);
let data_device_state = DataDeviceState::new::<State>(&display_handle);
Expand Down Expand Up @@ -2542,6 +2545,7 @@ impl Niri {
pointer_gestures_state,
relative_pointer_state,
pointer_constraints_state,
pointer_warp_manager,
idle_notifier_state,
idle_inhibit_manager_state,
data_device_state,
Expand Down
35 changes: 34 additions & 1 deletion src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::{Client, DisplayHandle, Resource as _};
use smithay::utils::{Coordinate, Logical, Point, Rectangle, Size, Transform};
use smithay::wayland::compositor::{send_surface_state, with_states, SurfaceData};
use smithay::wayland::compositor::{
get_parent, send_surface_state, with_states, SubsurfaceCachedState, SurfaceData,
};
use smithay::wayland::fractional_scale::with_fractional_scale;
use smithay::wayland::shell::xdg::{
ToplevelCachedState, ToplevelConfigure, ToplevelState, ToplevelSurface, XdgToplevelSurfaceData,
Expand Down Expand Up @@ -592,6 +594,37 @@ pub fn show_screenshot_notification(image_path: Option<&Path>) -> anyhow::Result
Ok(())
}

/// Computes this surface's location relative to its toplevel wl_surface's geometry.
///
/// This function will go up the parent stack and add up the relative locations. Useful for
/// transitive subsurfaces.
pub fn get_surface_toplevel_coords(surface: &WlSurface) -> Point<i32, Logical> {
let mut offset = (0, 0).into();
let mut current = surface.clone();
while let Some(parent) = get_parent(&current) {
offset += with_states(&current, |states| {
states
.cached_state
.get::<SubsurfaceCachedState>()
.current()
.location
});
current = parent;
}

offset
}

pub fn get_surface_size(surface: &WlSurface) -> Size<i32, Logical> {
with_states(&surface, |states| {
states
.data_map
.get::<RendererSurfaceStateUserData>()
.and_then(|d| d.lock().unwrap().surface_size())
.unwrap_or_default()
})
}

#[inline(never)]
pub fn cause_panic() {
let a = Duration::from_secs(1);
Expand Down