aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2023-10-02 18:48:41 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2023-10-02 18:48:41 +0400
commit382c049b5a67194761b75181bcd0506791602060 (patch)
treecb16a2fd6fbca493dcc09db6f484fce6173dc31b /src
parent9168f08038b1e3d7b643519191e9c70eac639ddc (diff)
downloadniri-382c049b5a67194761b75181bcd0506791602060.tar.gz
niri-382c049b5a67194761b75181bcd0506791602060.tar.bz2
niri-382c049b5a67194761b75181bcd0506791602060.zip
Send output enter/leave to pointer and DnD surfaces
This allows them to apply the right scale factor.
Diffstat (limited to 'src')
-rw-r--r--src/cursor.rs4
-rw-r--r--src/main.rs1
-rw-r--r--src/niri.rs90
3 files changed, 91 insertions, 4 deletions
diff --git a/src/cursor.rs b/src/cursor.rs
index bfc6e818..1ed3600f 100644
--- a/src/cursor.rs
+++ b/src/cursor.rs
@@ -91,6 +91,10 @@ impl Cursor {
})
.clone()
}
+
+ pub fn get_cached_hotspot(&self, scale: i32) -> Option<Point<i32, Physical>> {
+ self.cache.get(&scale).map(|(_, hotspot)| *hotspot)
+ }
}
fn load_xcursor(theme: &str) -> anyhow::Result<Vec<Image>> {
diff --git a/src/main.rs b/src/main.rs
index 4e994d9f..e8b4350b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -116,6 +116,7 @@ fn main() {
// These should be called periodically, before flushing the clients.
state.niri.monitor_set.refresh();
+ state.niri.refresh_pointer_outputs();
state.niri.popups.cleanup();
state.update_focus();
diff --git a/src/niri.rs b/src/niri.rs
index 2c817f21..37d4e4c5 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -12,6 +12,7 @@ use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as
use anyhow::Context;
use sd_notify::NotifyState;
use smithay::backend::allocator::Fourcc;
+use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::surface::{
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
@@ -24,10 +25,10 @@ use smithay::backend::renderer::element::{
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
use smithay::backend::renderer::{Bind, ExportMem, Frame, ImportAll, Offscreen, Renderer};
use smithay::desktop::utils::{
- send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
- surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
- take_presentation_feedback_surface_tree, update_surface_primary_scanout_output,
- OutputPresentationFeedback,
+ bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree,
+ send_frames_surface_tree, surface_presentation_feedback_flags_from_states,
+ surface_primary_scanout_output, take_presentation_feedback_surface_tree,
+ update_surface_primary_scanout_output, OutputPresentationFeedback,
};
use smithay::desktop::{layer_map_for_output, PopupManager, Space, Window, WindowSurfaceType};
use smithay::input::keyboard::XkbConfig;
@@ -1072,6 +1073,86 @@ impl Niri {
pointer_elements
}
+ pub fn refresh_pointer_outputs(&self) {
+ let _span = tracy_client::span!("Niri::refresh_pointer_outputs");
+
+ match &self.cursor_image {
+ CursorImageStatus::Hidden | CursorImageStatus::Named(_) => {
+ // There's no cursor surface, but there might be a DnD icon.
+ let Some(surface) = &self.dnd_icon else {
+ return;
+ };
+
+ let pointer_pos = self.seat.get_pointer().unwrap().current_location();
+
+ for output in self.global_space.outputs() {
+ let geo = self.global_space.output_geometry(output).unwrap();
+
+ // The default cursor is rendered at the right scale for each output, which
+ // means that it may have a different hotspot for each output.
+ let output_scale = output.current_scale().integer_scale();
+ let Some(hotspot) = self.default_cursor.get_cached_hotspot(output_scale) else {
+ // Oh well; it'll get cached next time we render.
+ continue;
+ };
+ let hotspot = hotspot.to_logical(output_scale);
+
+ let surface_pos = pointer_pos.to_i32_round() - hotspot;
+ let bbox = bbox_from_surface_tree(surface, surface_pos);
+
+ if let Some(mut overlap) = geo.intersection(bbox) {
+ overlap.loc -= surface_pos;
+ output_update(output, Some(overlap), surface);
+ } else {
+ output_update(output, None, surface);
+ }
+ }
+ }
+ CursorImageStatus::Surface(surface) => {
+ let hotspot = with_states(surface, |states| {
+ states
+ .data_map
+ .get::<Mutex<CursorImageAttributes>>()
+ .unwrap()
+ .lock()
+ .unwrap()
+ .hotspot
+ });
+
+ let pointer_pos = self.seat.get_pointer().unwrap().current_location();
+ let surface_pos = pointer_pos.to_i32_round() - hotspot;
+ let bbox = bbox_from_surface_tree(surface, surface_pos);
+
+ let dnd = self
+ .dnd_icon
+ .as_ref()
+ .map(|surface| (surface, bbox_from_surface_tree(surface, surface_pos)));
+
+ for output in self.global_space.outputs() {
+ let geo = self.global_space.output_geometry(output).unwrap();
+
+ // Compute pointer surface overlap.
+ if let Some(mut overlap) = geo.intersection(bbox) {
+ overlap.loc -= surface_pos;
+ output_update(output, Some(overlap), surface);
+ } else {
+ output_update(output, None, surface);
+ }
+
+ // Compute DnD icon surface overlap.
+ if let Some((surface, bbox)) = dnd {
+ if let Some(mut overlap) = geo.intersection(bbox) {
+ overlap.loc -= surface_pos;
+ output_update(output, Some(overlap), surface);
+ } else {
+ output_update(output, None, surface);
+ }
+ }
+ }
+ }
+ }
+ }
+
fn render(
&mut self,
renderer: &mut GlesRenderer,
@@ -1571,6 +1652,7 @@ render_elements! {
Monitor = MonitorRenderElement<R>,
Wayland = WaylandSurfaceRenderElement<R>,
DefaultPointer = TextureRenderElement<<R as Renderer>::TextureId>,
+ X = SolidColorRenderElement,
}
#[derive(Default)]