From df208fa6c8191bb578eaddd5ca80c9828d303eed Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Wed, 30 Jun 2021 19:58:13 +0200 Subject: [PATCH] clamp cursor with current output height move fullscreen output geometry calculation to a helper function rename with_outputs to with_client_outputs and take client instead of generic resource --- anvil/src/input_handler.rs | 13 +++- anvil/src/output_map.rs | 10 +-- anvil/src/shell.rs | 128 +++++++++++++------------------------ anvil/src/udev.rs | 2 +- anvil/src/winit.rs | 2 +- src/wayland/output/mod.rs | 39 +++++------ 6 files changed, 78 insertions(+), 116 deletions(-) diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 6c2032b..2a2b803 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -265,11 +265,18 @@ impl AnvilState { } let (pos_x, pos_y) = pos; - let (max_x, max_y) = self.output_map.borrow().size(); + let output_map = self.output_map.borrow(); + let max_x = output_map.width(); let clamped_x = pos_x.max(0.0).min(max_x as f64); - let clamped_y = pos_y.max(0.0).min(max_y as f64); + let max_y = output_map.height(clamped_x as i32); - (clamped_x, clamped_y) + if let Some(max_y) = max_y { + let clamped_y = pos_y.max(0.0).min(max_y as f64); + + (clamped_x, clamped_y) + } else { + (clamped_x, pos_y) + } } } diff --git a/anvil/src/output_map.rs b/anvil/src/output_map.rs index 9605a3e..00027da 100644 --- a/anvil/src/output_map.rs +++ b/anvil/src/output_map.rs @@ -212,18 +212,12 @@ impl OutputMap { .fold(0u32, |acc, output| acc + output.geometry.width as u32) } - pub fn height(&self) -> u32 { + pub fn height(&self, x: i32) -> Option { // This is a simplification, we only arrange the outputs on the y axis side-by-side - // so that the max height is simply the max of all output heights. self.outputs .iter() + .find(|output| x >= output.geometry.x && x < (output.geometry.x + output.geometry.width)) .map(|output| output.geometry.height as u32) - .max() - .unwrap_or_default() - } - - pub fn size(&self) -> (u32, u32) { - (self.width(), self.height()) } pub fn is_empty(&self) -> bool { diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index bc535f1..33117f5 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -297,6 +297,36 @@ pub struct ShellHandles { pub output_map: Rc>, } +fn fullscreen_output_geometry( + wl_surface: &wl_surface::WlSurface, + wl_output: Option<&wl_output::WlOutput>, + window_map: &WindowMap, + output_map: &OutputMap, +) -> Option { + // First test if a specific output has been requested + // if the requested output is not found ignore the request + if let Some(wl_output) = wl_output { + return output_map.find(&wl_output, |_, geometry| geometry).ok(); + } + + // There is no output preference, try to find the output + // where the window is currently active + let window_location = window_map + .find(wl_surface) + .and_then(|kind| window_map.location(&kind)); + + if let Some(location) = window_location { + let window_output = output_map.find_by_position(location, |_, geometry| geometry).ok(); + + if let Some(result) = window_output { + return Some(result); + } + } + + // Fallback to primary output + output_map.with_primary(|_, geometry| geometry).ok() +} + pub fn init_shell(display: Rc>, log: ::slog::Logger) -> ShellHandles { // Create the compositor compositor_init( @@ -510,49 +540,14 @@ pub fn init_shell(display: Rc>, log: ::sl return; }; - // Use the specified preferred output or else the current output the window - // is shown or the primary output - let output = output - .map(|output| { - xdg_output_map - .borrow() - .find(&output, |_, geometry| (Some(output.clone()), geometry)) - .ok() - }) - .flatten() - .or_else(|| { - let xdg_window_map = xdg_window_map.borrow(); + let output_geometry = fullscreen_output_geometry( + wl_surface, + output.as_ref(), + &xdg_window_map.borrow(), + &xdg_output_map.borrow(), + ); - xdg_window_map - .find(wl_surface) - .and_then(|kind| xdg_window_map.location(&kind)) - .and_then(|position| { - xdg_output_map - .borrow() - .find_by_position(position, |output, geometry| { - let mut window_output: Option = None; - output.with_output(wl_surface, |_, output| { - window_output = Some(output.to_owned()); - }); - (window_output, geometry) - }) - .ok() - }) - }) - .or_else(|| { - xdg_output_map - .borrow() - .with_primary(|output, geometry| { - let mut primary_output: Option = None; - output.with_output(wl_surface, |_, output| { - primary_output = Some(output.to_owned()); - }); - (primary_output, geometry) - }) - .ok() - }); - - if let Some((output, geometry)) = output { + if let Some(geometry) = output_geometry { if let Some(surface) = surface.get_surface() { let mut xdg_window_map = xdg_window_map.borrow_mut(); if let Some(kind) = xdg_window_map.find(surface) { @@ -676,49 +671,14 @@ pub fn init_shell(display: Rc>, log: ::sl return; }; - // Use the specified preferred output or else the current output the window - // is shown or the primary output - let output = output - .map(|output| { - shell_output_map - .borrow() - .find(&output, |_, geometry| (Some(output.clone()), geometry)) - .ok() - }) - .flatten() - .or_else(|| { - let shell_window_map = shell_window_map.borrow(); + let output_geometry = fullscreen_output_geometry( + wl_surface, + output.as_ref(), + &shell_window_map.borrow(), + &shell_output_map.borrow(), + ); - shell_window_map - .find(wl_surface) - .and_then(|kind| shell_window_map.location(&kind)) - .and_then(|position| { - shell_output_map - .borrow() - .find_by_position(position, |output, geometry| { - let mut window_output: Option = None; - output.with_output(wl_surface, |_, output| { - window_output = Some(output.to_owned()); - }); - (window_output, geometry) - }) - .ok() - }) - }) - .or_else(|| { - shell_output_map - .borrow() - .with_primary(|output, geometry| { - let mut primary_output: Option = None; - output.with_output(wl_surface, |_, output| { - primary_output = Some(output.to_owned()); - }); - (primary_output, geometry) - }) - .ok() - }); - - if let Some((_, geometry)) = output { + if let Some(geometry) = output_geometry { shell_window_map .borrow_mut() .insert(SurfaceKind::Wl(surface), (geometry.x, geometry.y)); diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index cb04bf7..3f4f84f 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -613,7 +613,7 @@ impl AnvilState { device_backend.dev_id, crtc, &mut *self.window_map.borrow_mut(), - &mut self.backend_data.output_map, + &self.backend_data.output_map, &*self.output_map.borrow(), &self.pointer_location, &device_backend.pointer_image, diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 540b57b..f3dd1da 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -22,7 +22,7 @@ use slog::Logger; use crate::drawing::*; use crate::state::{AnvilState, Backend}; -pub const OUTPUT_NAME: &'static str = "winit"; +pub const OUTPUT_NAME: &str = "winit"; pub struct WinitData; diff --git a/src/wayland/output/mod.rs b/src/wayland/output/mod.rs index b58d3ab..676b5bc 100644 --- a/src/wayland/output/mod.rs +++ b/src/wayland/output/mod.rs @@ -53,16 +53,13 @@ use std::{ sync::{Arc, Mutex}, }; -use wayland_server::{ - protocol::wl_output::{Mode as WMode, WlOutput}, - Display, Filter, Global, Main, +use wayland_server::protocol::{ + wl_output::{Subpixel, Transform}, + wl_surface, }; use wayland_server::{ - protocol::{ - wl_output::{Subpixel, Transform}, - wl_surface, - }, - Resource, + protocol::wl_output::{Mode as WMode, WlOutput}, + Client, Display, Filter, Global, Main, }; use slog::{info, o, trace, warn}; @@ -321,33 +318,37 @@ impl Output { .any(|o| o.as_ref().equals(output.as_ref())) } - /// This function allows to run a `Fn` for the matching - /// client outputs for a specific `Resource`. - pub fn with_output(&self, resource: &R, mut f: F) + /// This function allows to run a [FnMut] on every + /// [WlOutput] matching the same [Client] as provided + pub fn with_client_outputs(&self, client: Client, mut f: F) where - R: AsRef>, - II: wayland_server::Interface, - F: FnMut(&R, &WlOutput), + F: FnMut(&WlOutput), { - let tmp = resource.as_ref(); self.inner .lock() .unwrap() .instances .iter() - .filter(|output| output.as_ref().same_client_as(tmp)) - .for_each(|output| f(resource, output)) + .filter(|output| match output.as_ref().client() { + Some(output_client) => output_client.equals(&client), + None => false, + }) + .for_each(|output| f(output)) } /// Sends `wl_surface.enter` for the provided surface /// with the matching client output pub fn enter(&self, surface: &wl_surface::WlSurface) { - self.with_output(surface, |surface, output| surface.enter(output)) + if let Some(client) = surface.as_ref().client() { + self.with_client_outputs(client, |output| surface.enter(output)) + } } /// Sends `wl_surface.leave` for the provided surface /// with the matching client output pub fn leave(&self, surface: &wl_surface::WlSurface) { - self.with_output(surface, |surface, output| surface.leave(output)) + if let Some(client) = surface.as_ref().client() { + self.with_client_outputs(client, |output| surface.leave(output)) + } } }