From 5814626dbe0589c0e765adcc1d719f26ee73e7c1 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 12:10:36 +0300 Subject: [PATCH 01/17] anvil.window_map: rename input_bbox to bbox Since this value turns out to be more useful than for just input fast path. --- anvil/src/window_map.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 5d59831..10e0ef0 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -46,10 +46,11 @@ where struct Window { location: (i32, i32), - /// A bounding box over the input areas of this window and its children. + /// A bounding box over this window and its children. /// - /// Used for the fast path of the check in `matching`. - input_bbox: Rectangle, + /// Used for the fast path of the check in `matching`, and as the fall-back for the window + /// geometry if that's not set explicitly. + bbox: Rectangle, toplevel: Kind, } @@ -71,7 +72,7 @@ where where F: Fn(&SurfaceAttributes, (f64, f64)) -> bool, { - if !self.input_bbox.contains((point.0 as i32, point.1 as i32)) { + if !self.bbox.contains((point.0 as i32, point.1 as i32)) { return None; } // need to check more carefully @@ -143,7 +144,7 @@ where |_, _, _, _| true, ); } - self.input_bbox = Rectangle { + self.bbox = Rectangle { x: min_x, y: min_y, width: max_x - min_x, @@ -179,7 +180,7 @@ where pub fn insert(&mut self, toplevel: Kind, location: (i32, i32)) { let mut window = Window { location, - input_bbox: Rectangle::default(), + bbox: Rectangle::default(), toplevel, }; window.self_update(self.ctoken, &self.get_size); From 533a006bd1be4baf0463345c004676cf77e83cf5 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 15:16:00 +0300 Subject: [PATCH 02/17] anvil.window_map: fix bbox computation It said max_y = y + w instead of y + h. --- anvil/src/window_map.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 10e0ef0..3498293 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -122,21 +122,17 @@ where x += subdata.location.0; y += subdata.location.1; } - // update the bounding box - if x < min_x { - min_x = x; - } - if y < min_y { - min_y = y; - } - if x + w > max_x { - max_x = x + w; - } - if y + h > max_y { - max_y = y + w; - } + + // Update the bounding box. + min_x = min_x.min(x); + min_y = min_y.min(y); + max_x = max_x.max(x + w); + max_y = max_y.max(y + h); + TraversalAction::DoChildren((x, y)) } else { + // If the parent surface is unmapped, then the child surfaces are hidden as + // well, no need to consider them here. TraversalAction::SkipChildren } }, From ebb3a055020066fb74389567b9ffcd1dd1166059 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 15:55:48 +0300 Subject: [PATCH 03/17] anvil: make get_size and contains_point methods This removes the need to store them as generic members, and will ease the addition of new methods. --- anvil/src/shell.rs | 87 ++++++++++++++++++----------------------- anvil/src/window_map.rs | 57 +++++++++++---------------- 2 files changed, 62 insertions(+), 82 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 26a7378..9737173 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -41,11 +41,7 @@ define_roles!(Roles => [ CursorImage, CursorImageRole ] ); -pub type MyWindowMap = WindowMap< - Roles, - fn(&SurfaceAttributes) -> Option<(i32, i32)>, - fn(&SurfaceAttributes, (f64, f64)) -> bool, ->; +pub type MyWindowMap = WindowMap; pub type MyCompositorToken = CompositorToken; @@ -122,11 +118,7 @@ pub fn init_shell( ); // Init a window map, to track the location of our windows - let window_map = Rc::new(RefCell::new(WindowMap::new( - compositor_token, - get_size as _, - contains_point as _, - ))); + let window_map = Rc::new(RefCell::new(WindowMap::new(compositor_token))); // init the xdg_shell let xdg_window_map = window_map.clone(); @@ -281,6 +273,43 @@ pub struct SurfaceData { pub input_region: Option, } +impl SurfaceData { + /// Returns the size of the surface. + pub fn size(&self) -> Option<(i32, i32)> { + self.dimensions + } + + /// Checks if the surface's input region contains the point. + pub fn contains_point(&self, point: (f64, f64)) -> bool { + let (w, h) = match self.size() { + None => return false, // If the surface has no size, it can't have an input region. + Some(wh) => wh, + }; + + let rect = Rectangle { + x: 0, + y: 0, + width: w, + height: h, + }; + + let point = (point.0 as i32, point.1 as i32); + + // The input region is always within the surface itself, so if the surface itself doesn't contain the + // point we can return false. + if !rect.contains(point) { + return false; + } + + // If there's no input region, we're done. + if self.input_region.is_none() { + return true; + } + + self.input_region.as_ref().unwrap().contains(point) + } +} + fn surface_commit( surface: &wl_surface::WlSurface, token: CompositorToken, @@ -316,41 +345,3 @@ fn surface_commit( } }); } - -fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { - attrs - .user_data - .get::() - .and_then(|data| data.dimensions) -} - -fn contains_point(attrs: &SurfaceAttributes, point: (f64, f64)) -> bool { - let (w, h) = match get_size(attrs) { - None => return false, // If the surface has no size, it can't have an input region. - Some(wh) => wh, - }; - - let rect = Rectangle { - x: 0, - y: 0, - width: w, - height: h, - }; - - let point = (point.0 as i32, point.1 as i32); - - // The input region is always within the surface itself, so if the surface itself doesn't contain the - // point we can return false. - if !rect.contains(point) { - return false; - } - - let input_region = &attrs.user_data.get::().unwrap().input_region; - - // If there's no input region, we're done. - if input_region.is_none() { - return true; - } - - input_region.as_ref().unwrap().contains(point) -} diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 3498293..8fb1cdb 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -4,7 +4,7 @@ use smithay::{ reexports::wayland_server::protocol::wl_surface, utils::Rectangle, wayland::{ - compositor::{roles::Role, CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction}, + compositor::{roles::Role, CompositorToken, SubsurfaceRole, TraversalAction}, shell::{ legacy::{ShellSurface, ShellSurfaceRole}, xdg::{ToplevelSurface, XdgSurfaceRole}, @@ -12,6 +12,8 @@ use smithay::{ }, }; +use crate::shell::SurfaceData; + pub enum Kind { Xdg(ToplevelSurface), Wl(ShellSurface), @@ -60,18 +62,11 @@ where { /// Finds the topmost surface under this point if any and returns it together with the location of this /// surface. - /// - /// You need to provide a `contains_point` function which checks if the point (in surface-local - /// coordinates) is within the input region of the given `SurfaceAttributes`. - fn matching( + fn matching( &self, point: (f64, f64), ctoken: CompositorToken, - contains_point: F, - ) -> Option<(wl_surface::WlSurface, (f64, f64))> - where - F: Fn(&SurfaceAttributes, (f64, f64)) -> bool, - { + ) -> Option<(wl_surface::WlSurface, (f64, f64))> { if !self.bbox.contains((point.0 as i32, point.1 as i32)) { return None; } @@ -82,13 +77,18 @@ where wl_surface, self.location, |wl_surface, attributes, role, &(mut x, mut y)| { + let data = attributes.user_data.get::(); + if let Ok(subdata) = Role::::data(role) { x += subdata.location.0; y += subdata.location.1; } let surface_local_point = (point.0 - x as f64, point.1 - y as f64); - if contains_point(attributes, surface_local_point) { + if data + .map(|data| data.contains_point(surface_local_point)) + .unwrap_or(false) + { *found.borrow_mut() = Some((wl_surface.clone(), (x as f64, y as f64))); } @@ -104,10 +104,7 @@ where found.into_inner() } - fn self_update(&mut self, ctoken: CompositorToken, get_size: F) - where - F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, - { + fn self_update(&mut self, ctoken: CompositorToken) { let (base_x, base_y) = self.location; let (mut min_x, mut min_y, mut max_x, mut max_y) = (base_x, base_y, base_x, base_y); if let Some(wl_surface) = self.toplevel.get_surface() { @@ -115,9 +112,9 @@ where wl_surface, (base_x, base_y), |_, attributes, role, &(mut x, mut y)| { - // The input region is intersected with the surface size, so the surface size - // can serve as an approximation for the input bounding box. - if let Some((w, h)) = get_size(attributes) { + let data = attributes.user_data.get::(); + + if let Some((w, h)) = data.and_then(SurfaceData::size) { if let Ok(subdata) = Role::::data(role) { x += subdata.location.0; y += subdata.location.1; @@ -149,27 +146,19 @@ where } } -pub struct WindowMap { +pub struct WindowMap { ctoken: CompositorToken, windows: Vec>, - /// A function returning the surface size. - get_size: F, - /// A function that checks if the point is in the surface's input region. - contains_point: G, } -impl WindowMap +impl WindowMap where - F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, - G: Fn(&SurfaceAttributes, (f64, f64)) -> bool, R: Role + Role + Role + 'static, { - pub fn new(ctoken: CompositorToken, get_size: F, contains_point: G) -> Self { + pub fn new(ctoken: CompositorToken) -> Self { WindowMap { ctoken, windows: Vec::new(), - get_size, - contains_point, } } @@ -179,13 +168,13 @@ where bbox: Rectangle::default(), toplevel, }; - window.self_update(self.ctoken, &self.get_size); + window.self_update(self.ctoken); self.windows.insert(0, window); } pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> { for w in &self.windows { - if let Some(surface) = w.matching(point, self.ctoken, &self.contains_point) { + if let Some(surface) = w.matching(point, self.ctoken) { return Some(surface); } } @@ -198,7 +187,7 @@ where ) -> Option<(wl_surface::WlSurface, (f64, f64))> { let mut found = None; for (i, w) in self.windows.iter().enumerate() { - if let Some(surface) = w.matching(point, self.ctoken, &self.contains_point) { + if let Some(surface) = w.matching(point, self.ctoken) { found = Some((i, surface)); break; } @@ -224,7 +213,7 @@ where pub fn refresh(&mut self) { self.windows.retain(|w| w.toplevel.alive()); for w in &mut self.windows { - w.self_update(self.ctoken, &self.get_size); + w.self_update(self.ctoken); } } @@ -244,7 +233,7 @@ where pub fn set_location(&mut self, toplevel: &Kind, location: (i32, i32)) { if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) { w.location = location; - w.self_update(self.ctoken, &self.get_size); + w.self_update(self.ctoken); } } } From 9fd9db82aec5cc971ceaa0c52799a71094b6c879 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 16:22:48 +0300 Subject: [PATCH 04/17] anvil.shell: store geometry in SurfaceData --- anvil/src/shell.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 9737173..b4057a4 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -270,6 +270,7 @@ pub struct SurfaceData { pub buffer: Option, pub texture: Option, pub dimensions: Option<(i32, i32)>, + pub geometry: Option, pub input_region: Option, } @@ -315,10 +316,15 @@ fn surface_commit( token: CompositorToken, buffer_utils: &BufferUtils, ) { + let geometry = token + .with_role_data(surface, |role: &mut XdgSurfaceRole| role.window_geometry) + .unwrap_or(None); + token.with_surface_data(surface, |attributes| { attributes.user_data.insert_if_missing(SurfaceData::default); let data = attributes.user_data.get_mut::().unwrap(); + data.geometry = geometry; data.input_region = attributes.input_region.clone(); // we retrieve the contents of the associated buffer and copy it From 546ff48af5dae31d091c888e4a240b1e08cf6c19 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 16:23:04 +0300 Subject: [PATCH 05/17] anvil.window_map: add WindowMap::geometry --- anvil/src/window_map.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 8fb1cdb..f54bda6 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -144,6 +144,16 @@ where height: max_y - min_y, }; } + + /// Returns the geometry of this window. + pub fn geometry(&self, ctoken: CompositorToken) -> Rectangle { + // It's the set geometry with the full bounding box as the fallback. + ctoken + .with_surface_data(self.toplevel.get_surface().unwrap(), |attributes| { + attributes.user_data.get::().unwrap().geometry + }) + .unwrap_or(self.bbox) + } } pub struct WindowMap { @@ -236,4 +246,12 @@ where w.self_update(self.ctoken); } } + + /// Returns the geometry of the toplevel, if it exists. + pub fn geometry(&self, toplevel: &Kind) -> Option { + self.windows + .iter() + .find(|w| w.toplevel.equals(toplevel)) + .map(|w| w.geometry(self.ctoken)) + } } From ca7e91a98c8866da49afc6ef6d41c17c03789bd8 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 16:31:15 +0300 Subject: [PATCH 06/17] anvil.shell: implement the Resize request Currently doesn't move the window when resizing left or top. --- anvil/src/shell.rs | 200 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 3 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index b4057a4..92c34de 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -7,9 +7,12 @@ use std::{ use rand; use smithay::{ - reexports::wayland_server::{ - protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface}, - Display, + reexports::{ + wayland_protocols::xdg_shell::server::xdg_toplevel, + wayland_server::{ + protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface}, + Display, + }, }, utils::Rectangle, wayland::{ @@ -95,6 +98,95 @@ impl PointerGrab for MoveSurfaceGrab { } } +struct ResizeSurfaceGrab { + start_data: GrabStartData, + toplevel: SurfaceKind, + edges: wl_shell_surface::Resize, + initial_window_size: (i32, i32), + last_window_size: (i32, i32), +} + +impl PointerGrab for ResizeSurfaceGrab { + fn motion( + &mut self, + _handle: &mut PointerInnerHandle<'_>, + location: (f64, f64), + _focus: Option<(wl_surface::WlSurface, (f64, f64))>, + serial: u32, + _time: u32, + ) { + let mut dx = location.0 - self.start_data.location.0; + let mut dy = location.1 - self.start_data.location.1; + + let left_right = wl_shell_surface::Resize::Left | wl_shell_surface::Resize::Right; + let top_bottom = wl_shell_surface::Resize::Top | wl_shell_surface::Resize::Bottom; + let new_window_width = if self.edges.intersects(left_right) { + if self.edges.intersects(wl_shell_surface::Resize::Left) { + dx = -dx; + } + + ((self.initial_window_size.0 as f64 + dx) as i32).max(1) + } else { + self.initial_window_size.0 + }; + let new_window_height = if self.edges.intersects(top_bottom) { + if self.edges.intersects(wl_shell_surface::Resize::Top) { + dy = -dy; + } + + ((self.initial_window_size.1 as f64 + dy) as i32).max(1) + } else { + self.initial_window_size.1 + }; + + self.last_window_size = (new_window_width, new_window_height); + + match &self.toplevel { + SurfaceKind::Xdg(xdg) => xdg.send_configure(ToplevelConfigure { + size: Some(self.last_window_size), + states: vec![xdg_toplevel::State::Resizing], + serial, + }), + SurfaceKind::Wl(wl) => wl.send_configure( + (self.last_window_size.0 as u32, self.last_window_size.1 as u32), + self.edges, + ), + } + } + + fn button( + &mut self, + handle: &mut PointerInnerHandle<'_>, + button: u32, + state: ButtonState, + serial: u32, + time: u32, + ) { + handle.button(button, state, serial, time); + if handle.current_pressed().is_empty() { + // No more buttons are pressed, release the grab. + handle.unset_grab(serial, time); + + if let SurfaceKind::Xdg(xdg) = &self.toplevel { + // Send the final configure without the resizing state. + xdg.send_configure(ToplevelConfigure { + size: Some(self.last_window_size), + states: vec![], + serial, + }); + } + } + } + + fn axis(&mut self, handle: &mut PointerInnerHandle<'_>, details: AxisFrame) { + handle.axis(details) + } + + fn start_data(&self) -> &GrabStartData { + &self.start_data + } +} + pub fn init_shell( display: &mut Display, buffer_utils: BufferUtils, @@ -188,6 +280,63 @@ pub fn init_shell( pointer.set_grab(grab, serial); } + XdgRequest::Resize { + surface, + seat, + serial, + edges, + } => { + let seat = Seat::from_resource(&seat).unwrap(); + // TODO: touch resize. + let pointer = seat.get_pointer().unwrap(); + + // Check that this surface has a click grab. + if !pointer.has_grab(serial) { + return; + } + + let start_data = pointer.grab_start_data().unwrap(); + + // If the focus was for a different surface, ignore the request. + if start_data.focus.is_none() + || !start_data + .focus + .as_ref() + .unwrap() + .0 + .as_ref() + .same_client_as(surface.get_surface().unwrap().as_ref()) + { + return; + } + + let toplevel = SurfaceKind::Xdg(surface); + let initial_window_location = xdg_window_map.borrow().location(&toplevel).unwrap(); + let geometry = xdg_window_map.borrow().geometry(&toplevel).unwrap(); + let initial_window_size = (geometry.width, geometry.height); + + let edges = match edges { + xdg_toplevel::ResizeEdge::Top => wl_shell_surface::Resize::Top, + xdg_toplevel::ResizeEdge::Bottom => wl_shell_surface::Resize::Bottom, + xdg_toplevel::ResizeEdge::Left => wl_shell_surface::Resize::Left, + xdg_toplevel::ResizeEdge::TopLeft => wl_shell_surface::Resize::TopLeft, + xdg_toplevel::ResizeEdge::BottomLeft => wl_shell_surface::Resize::BottomLeft, + xdg_toplevel::ResizeEdge::Right => wl_shell_surface::Resize::Right, + xdg_toplevel::ResizeEdge::TopRight => wl_shell_surface::Resize::TopRight, + xdg_toplevel::ResizeEdge::BottomRight => wl_shell_surface::Resize::BottomRight, + _ => return, + }; + + let grab = ResizeSurfaceGrab { + start_data, + toplevel, + edges, + initial_window_size, + last_window_size: initial_window_size, + }; + + pointer.set_grab(grab, serial); + } _ => (), }, log.clone(), @@ -256,6 +405,51 @@ pub fn init_shell( pointer.set_grab(grab, serial); } + ShellRequest::Resize { + surface, + seat, + serial, + edges, + } => { + let seat = Seat::from_resource(&seat).unwrap(); + // TODO: touch resize. + let pointer = seat.get_pointer().unwrap(); + + // Check that this surface has a click grab. + if !pointer.has_grab(serial) { + return; + } + + let start_data = pointer.grab_start_data().unwrap(); + + // If the focus was for a different surface, ignore the request. + if start_data.focus.is_none() + || !start_data + .focus + .as_ref() + .unwrap() + .0 + .as_ref() + .same_client_as(surface.get_surface().unwrap().as_ref()) + { + return; + } + + let toplevel = SurfaceKind::Wl(surface); + let initial_window_location = shell_window_map.borrow().location(&toplevel).unwrap(); + let geometry = shell_window_map.borrow().geometry(&toplevel).unwrap(); + let initial_window_size = (geometry.width, geometry.height); + + let grab = ResizeSurfaceGrab { + start_data, + toplevel, + edges, + initial_window_size, + last_window_size: initial_window_size, + }; + + pointer.set_grab(grab, serial); + } _ => (), } }, From 33a9e242edaf5160f8e9ef1bb8d58d7ccfff44de Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:38:52 +0300 Subject: [PATCH 07/17] wayland.shell: implement Clone for toplevels --- src/wayland/shell/legacy/mod.rs | 11 +++++++++++ src/wayland/shell/xdg/mod.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/wayland/shell/legacy/mod.rs b/src/wayland/shell/legacy/mod.rs index 4814738..575fd10 100644 --- a/src/wayland/shell/legacy/mod.rs +++ b/src/wayland/shell/legacy/mod.rs @@ -92,6 +92,17 @@ pub struct ShellSurface { token: CompositorToken, } +// We implement Clone manually because #[derive(..)] would require R: Clone. +impl Clone for ShellSurface { + fn clone(&self) -> Self { + Self { + wl_surface: self.wl_surface.clone(), + shell_surface: self.shell_surface.clone(), + token: self.token.clone(), + } + } +} + impl ShellSurface where R: Role + 'static, diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index cd20236..a3a20da 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -472,6 +472,7 @@ where } } +#[derive(Clone)] pub(crate) enum ToplevelKind { Xdg(xdg_toplevel::XdgToplevel), ZxdgV6(zxdg_toplevel_v6::ZxdgToplevelV6), @@ -484,6 +485,17 @@ pub struct ToplevelSurface { token: CompositorToken, } +// We implement Clone manually because #[derive(..)] would require R: Clone. +impl Clone for ToplevelSurface { + fn clone(&self) -> Self { + Self { + wl_surface: self.wl_surface.clone(), + shell_surface: self.shell_surface.clone(), + token: self.token.clone(), + } + } +} + impl ToplevelSurface where R: Role + 'static, From 6d36375f270313bdbb4264e539ce0a0f1d4e460c Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:39:09 +0300 Subject: [PATCH 08/17] anvil.window_map: implement Clone for Kind --- anvil/src/window_map.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index f54bda6..f98672b 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -19,6 +19,16 @@ pub enum Kind { Wl(ShellSurface), } +// We implement Clone manually because #[derive(..)] would require R: Clone. +impl Clone for Kind { + fn clone(&self) -> Self { + match self { + Kind::Xdg(xdg) => Kind::Xdg(xdg.clone()), + Kind::Wl(wl) => Kind::Wl(wl.clone()), + } + } +} + impl Kind where R: Role + Role + Role + 'static, From 223b523f809a9c0a1ad97dc5fd95c8f843a6ac1c Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:39:27 +0300 Subject: [PATCH 09/17] anvil.window_map: add WindowMap::find --- anvil/src/window_map.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index f98672b..9a8fd9f 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -241,6 +241,21 @@ where self.windows.clear(); } + /// Finds the toplevel corresponding to the given `WlSurface`. + pub fn find(&self, surface: &wl_surface::WlSurface) -> Option> { + self.windows.iter().find_map(|w| { + if w.toplevel + .get_surface() + .map(|s| s.as_ref().equals(surface.as_ref())) + .unwrap_or(false) + { + Some(w.toplevel.clone()) + } else { + None + } + }) + } + /// Returns the location of the toplevel, if it exists. pub fn location(&self, toplevel: &Kind) -> Option<(i32, i32)> { self.windows From 9eb51b843905a57dbe66467348e1e1f997ab803f Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:42:50 +0300 Subject: [PATCH 10/17] shell.xdg: add XdgRequest::AckConfigure This will be used for convenient resize state tracking. --- src/wayland/shell/xdg/mod.rs | 7 +++++++ src/wayland/shell/xdg/xdg_handlers.rs | 6 ++++++ src/wayland/shell/xdg/zxdgv6_handlers.rs | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index a3a20da..ed757ad 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -941,4 +941,11 @@ pub enum XdgRequest { /// location of the menu request location: (i32, i32), }, + /// A surface has acknowledged a configure serial. + AckConfigure { + /// The surface. + surface: wl_surface::WlSurface, + /// The configure serial. + serial: u32, + }, } diff --git a/src/wayland/shell/xdg/xdg_handlers.rs b/src/wayland/shell/xdg/xdg_handlers.rs index b07c9ad..2b92d84 100644 --- a/src/wayland/shell/xdg/xdg_handlers.rs +++ b/src/wayland/shell/xdg/xdg_handlers.rs @@ -333,6 +333,12 @@ where role_data.configured = true; }) .expect("xdg_surface exists but surface has not shell_surface role?!"); + + let mut user_impl = data.shell_data.user_impl.borrow_mut(); + (&mut *user_impl)(XdgRequest::AckConfigure { + surface: data.wl_surface.clone(), + serial, + }); } _ => unreachable!(), } diff --git a/src/wayland/shell/xdg/zxdgv6_handlers.rs b/src/wayland/shell/xdg/zxdgv6_handlers.rs index fd11640..35e77f7 100644 --- a/src/wayland/shell/xdg/zxdgv6_handlers.rs +++ b/src/wayland/shell/xdg/zxdgv6_handlers.rs @@ -349,6 +349,12 @@ fn xdg_surface_implementation( role_data.configured = true; }) .expect("xdg_surface exists but surface has not shell_surface role?!"); + + let mut user_impl = data.shell_data.user_impl.borrow_mut(); + (&mut *user_impl)(XdgRequest::AckConfigure { + surface: data.wl_surface.clone(), + serial, + }); } _ => unreachable!(), } From ab45cdecdcf60a986c46e0860e29e09438fb172a Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:43:32 +0300 Subject: [PATCH 11/17] anvil.window_map: add refresh_toplevel --- anvil/src/window_map.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 9a8fd9f..c014626 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -237,6 +237,13 @@ where } } + /// Refreshes the state of the toplevel, if it exists. + pub fn refresh_toplevel(&mut self, toplevel: &Kind) { + if let Some(w) = self.windows.iter_mut().find(|w| w.toplevel.equals(toplevel)) { + w.self_update(self.ctoken); + } + } + pub fn clear(&mut self) { self.windows.clear(); } From 09d7f597d4c9070c986a46ca264fdc801ad15d93 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:44:39 +0300 Subject: [PATCH 12/17] anvil.shell: refresh toplevels on commit This updates the toplevel state in the WindowMap as soon as it's committed. It will be used to update the toplevel location on top-left resize, but this is a better approach in general than the current update-every-drawn-frame. I think we should update the WindowMap state as soon as possible, and only when necessary. --- anvil/src/shell.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 92c34de..2f2d7d7 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -197,11 +197,19 @@ pub fn init_shell( Arc>>, Rc>, ) { + // TODO: this is awkward... + let almost_window_map = Rc::new(RefCell::new(None::>>)); + let almost_window_map_compositor = almost_window_map.clone(); + // Create the compositor let (compositor_token, _, _) = compositor_init( display, move |request, surface, ctoken| match request { - SurfaceEvent::Commit => surface_commit(&surface, ctoken, &buffer_utils), + SurfaceEvent::Commit => { + let window_map = almost_window_map_compositor.borrow(); + let window_map = window_map.as_ref().unwrap(); + surface_commit(&surface, ctoken, &buffer_utils, &*window_map) + } SurfaceEvent::Frame { callback } => callback .implement_closure(|_, _| unreachable!(), None::, ()) .done(0), @@ -211,6 +219,7 @@ pub fn init_shell( // Init a window map, to track the location of our windows let window_map = Rc::new(RefCell::new(WindowMap::new(compositor_token))); + *almost_window_map.borrow_mut() = Some(window_map.clone()); // init the xdg_shell let xdg_window_map = window_map.clone(); @@ -509,12 +518,13 @@ fn surface_commit( surface: &wl_surface::WlSurface, token: CompositorToken, buffer_utils: &BufferUtils, + window_map: &RefCell, ) { let geometry = token .with_role_data(surface, |role: &mut XdgSurfaceRole| role.window_geometry) .unwrap_or(None); - token.with_surface_data(surface, |attributes| { + let refresh = token.with_surface_data(surface, |attributes| { attributes.user_data.insert_if_missing(SurfaceData::default); let data = attributes.user_data.get_mut::().unwrap(); @@ -543,5 +553,12 @@ fn surface_commit( } None => {} } + + window_map.borrow().find(surface) }); + + if let Some(toplevel) = refresh { + let mut window_map = window_map.borrow_mut(); + window_map.refresh_toplevel(&toplevel); + } } From adbab32bd821b0c05b73df70cf54fc1b670cf19d Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 08:46:15 +0300 Subject: [PATCH 13/17] anvil.shell: update location on top-left resizing --- anvil/src/shell.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 2 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 2f2d7d7..8292362 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -100,6 +100,7 @@ impl PointerGrab for MoveSurfaceGrab { struct ResizeSurfaceGrab { start_data: GrabStartData, + ctoken: MyCompositorToken, toplevel: SurfaceKind, edges: wl_shell_surface::Resize, initial_window_size: (i32, i32), @@ -174,6 +175,26 @@ impl PointerGrab for ResizeSurfaceGrab { states: vec![], serial, }); + + self.ctoken + .with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| { + let data = attrs.user_data.get_mut::().unwrap(); + if let ResizeState::Resizing(resize_data) = data.resize_state { + data.resize_state = ResizeState::WaitingForFinalAck(resize_data, serial); + } else { + panic!("invalid resize state: {:?}", data.resize_state); + } + }); + } else { + self.ctoken + .with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| { + let data = attrs.user_data.get_mut::().unwrap(); + if let ResizeState::Resizing(resize_data) = data.resize_state { + data.resize_state = ResizeState::WaitingForCommit(resize_data); + } else { + panic!("invalid resize state: {:?}", data.resize_state); + } + }); } } } @@ -319,7 +340,7 @@ pub fn init_shell( return; } - let toplevel = SurfaceKind::Xdg(surface); + let toplevel = SurfaceKind::Xdg(surface.clone()); let initial_window_location = xdg_window_map.borrow().location(&toplevel).unwrap(); let geometry = xdg_window_map.borrow().geometry(&toplevel).unwrap(); let initial_window_size = (geometry.width, geometry.height); @@ -336,8 +357,18 @@ pub fn init_shell( _ => return, }; + compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| { + attrs.user_data.get_mut::().unwrap().resize_state = + ResizeState::Resizing(ResizeData { + edges, + initial_window_location, + initial_window_size, + }); + }); + let grab = ResizeSurfaceGrab { start_data, + ctoken: compositor_token, toplevel, edges, initial_window_size, @@ -346,6 +377,36 @@ pub fn init_shell( pointer.set_grab(grab, serial); } + XdgRequest::AckConfigure { surface, .. } => { + let waiting_for_serial = compositor_token.with_surface_data(&surface, |attrs| { + if let Some(data) = attrs.user_data.get_mut::() { + if let ResizeState::WaitingForFinalAck(_, serial) = data.resize_state { + return Some(serial); + } + } + + None + }); + + if let Some(serial) = waiting_for_serial { + let acked = compositor_token + .with_role_data(&surface, |role: &mut XdgSurfaceRole| { + !role.pending_configures.contains(&serial) + }) + .unwrap(); + + if acked { + compositor_token.with_surface_data(&surface, |attrs| { + let data = attrs.user_data.get_mut::().unwrap(); + if let ResizeState::WaitingForFinalAck(resize_data, _) = data.resize_state { + data.resize_state = ResizeState::WaitingForCommit(resize_data); + } else { + unreachable!() + } + }) + } + } + } _ => (), }, log.clone(), @@ -444,13 +505,23 @@ pub fn init_shell( return; } - let toplevel = SurfaceKind::Wl(surface); + let toplevel = SurfaceKind::Wl(surface.clone()); let initial_window_location = shell_window_map.borrow().location(&toplevel).unwrap(); let geometry = shell_window_map.borrow().geometry(&toplevel).unwrap(); let initial_window_size = (geometry.width, geometry.height); + compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| { + attrs.user_data.get_mut::().unwrap().resize_state = + ResizeState::Resizing(ResizeData { + edges, + initial_window_location, + initial_window_size, + }); + }); + let grab = ResizeSurfaceGrab { start_data, + ctoken: compositor_token, toplevel, edges, initial_window_size, @@ -468,6 +539,36 @@ pub fn init_shell( (compositor_token, xdg_shell_state, wl_shell_state, window_map) } +/// Information about the resize operation. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct ResizeData { + /// The edges the surface is being resized with. + edges: wl_shell_surface::Resize, + /// The initial window location. + initial_window_location: (i32, i32), + /// The initial window size (geometry width and height). + initial_window_size: (i32, i32), +} + +/// State of the resize operation. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum ResizeState { + /// The surface is not being resized. + NotResizing, + /// The surface is currently being resized. + Resizing(ResizeData), + /// The resize has finished, and the surface needs to ack the final configure. + WaitingForFinalAck(ResizeData, u32), + /// The resize has finished, and the surface needs to commit its final state. + WaitingForCommit(ResizeData), +} + +impl Default for ResizeState { + fn default() -> Self { + ResizeState::NotResizing + } +} + #[derive(Default)] pub struct SurfaceData { pub buffer: Option, @@ -475,6 +576,7 @@ pub struct SurfaceData { pub dimensions: Option<(i32, i32)>, pub geometry: Option, pub input_region: Option, + pub resize_state: ResizeState, } impl SurfaceData { @@ -560,5 +662,52 @@ fn surface_commit( if let Some(toplevel) = refresh { let mut window_map = window_map.borrow_mut(); window_map.refresh_toplevel(&toplevel); + // Get the geometry outside since it uses the token, and so would block inside. + let Rectangle { width, height, .. } = window_map.geometry(&toplevel).unwrap(); + + let new_location = token.with_surface_data(surface, |attributes| { + let data = attributes.user_data.get_mut::().unwrap(); + + let mut new_location = None; + + // If the window is being resized by top or left, its location must be adjusted + // accordingly. + match data.resize_state { + ResizeState::Resizing(resize_data) + | ResizeState::WaitingForFinalAck(resize_data, _) + | ResizeState::WaitingForCommit(resize_data) => { + let ResizeData { + edges, + initial_window_location, + initial_window_size, + } = resize_data; + + if edges.intersects(wl_shell_surface::Resize::TopLeft) { + let mut location = window_map.location(&toplevel).unwrap(); + + if edges.intersects(wl_shell_surface::Resize::Left) { + location.0 = initial_window_location.0 + (initial_window_size.0 - width); + } + if edges.intersects(wl_shell_surface::Resize::Top) { + location.1 = initial_window_location.1 + (initial_window_size.1 - height); + } + + new_location = Some(location); + } + } + ResizeState::NotResizing => (), + } + + // Finish resizing. + if let ResizeState::WaitingForCommit(_) = data.resize_state { + data.resize_state = ResizeState::NotResizing; + } + + new_location + }); + + if let Some(location) = new_location { + window_map.set_location(&toplevel, location); + } } } From 097445bc2008a93e9189fbb2b95dcf66ddcdaa84 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 09:23:34 +0300 Subject: [PATCH 14/17] shell.xdg: fix two copy-paste errors --- src/wayland/shell/xdg/xdg_handlers.rs | 2 +- src/wayland/shell/xdg/zxdgv6_handlers.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wayland/shell/xdg/xdg_handlers.rs b/src/wayland/shell/xdg/xdg_handlers.rs index 2b92d84..ea36f6e 100644 --- a/src/wayland/shell/xdg/xdg_handlers.rs +++ b/src/wayland/shell/xdg/xdg_handlers.rs @@ -473,7 +473,7 @@ where } xdg_toplevel::Request::SetMinSize { width, height } => { with_surface_toplevel_data(&data.shell_data, &toplevel, |toplevel_data| { - toplevel_data.max_size = (width, height); + toplevel_data.min_size = (width, height); }); } xdg_toplevel::Request::SetMaximized => { diff --git a/src/wayland/shell/xdg/zxdgv6_handlers.rs b/src/wayland/shell/xdg/zxdgv6_handlers.rs index 35e77f7..84499d4 100644 --- a/src/wayland/shell/xdg/zxdgv6_handlers.rs +++ b/src/wayland/shell/xdg/zxdgv6_handlers.rs @@ -491,7 +491,7 @@ where } zxdg_toplevel_v6::Request::SetMinSize { width, height } => { with_surface_toplevel_data::(&toplevel, |toplevel_data| { - toplevel_data.max_size = (width, height); + toplevel_data.min_size = (width, height); }); } zxdg_toplevel_v6::Request::SetMaximized => { From e76f9f14edffc5b74c0d964bf10ed7dfb1d4cae3 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 09:24:27 +0300 Subject: [PATCH 15/17] anvil.shell: store min_size and max_size --- anvil/src/shell.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 8292362..0812d6d 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -25,7 +25,7 @@ use smithay::{ }, xdg::{ xdg_shell_init, PopupConfigure, ShellState as XdgShellState, ToplevelConfigure, XdgRequest, - XdgSurfaceRole, + XdgSurfacePendingState, XdgSurfaceRole, }, }, SERIAL_COUNTER as SCOUNTER, @@ -577,6 +577,14 @@ pub struct SurfaceData { pub geometry: Option, pub input_region: Option, pub resize_state: ResizeState, + /// Minimum width and height, as requested by the surface. + /// + /// `0` means unlimited. + pub min_size: (i32, i32), + /// Maximum width and height, as requested by the surface. + /// + /// `0` means unlimited. + pub max_size: (i32, i32), } impl SurfaceData { @@ -622,9 +630,17 @@ fn surface_commit( buffer_utils: &BufferUtils, window_map: &RefCell, ) { - let geometry = token - .with_role_data(surface, |role: &mut XdgSurfaceRole| role.window_geometry) - .unwrap_or(None); + let mut geometry = None; + let mut min_size = (0, 0); + let mut max_size = (0, 0); + let _ = token.with_role_data(surface, |role: &mut XdgSurfaceRole| { + if let XdgSurfacePendingState::Toplevel(ref state) = role.pending_state { + min_size = state.min_size; + max_size = state.max_size; + } + + geometry = role.window_geometry; + }); let refresh = token.with_surface_data(surface, |attributes| { attributes.user_data.insert_if_missing(SurfaceData::default); @@ -632,6 +648,8 @@ fn surface_commit( data.geometry = geometry; data.input_region = attributes.input_region.clone(); + data.min_size = min_size; + data.max_size = max_size; // we retrieve the contents of the associated buffer and copy it match attributes.buffer.take() { From c0d0d0d2b86b5a32f791029b671c2112aee6e1e9 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 8 Feb 2020 09:24:50 +0300 Subject: [PATCH 16/17] anvil.shell: respect min and max size in resize --- anvil/src/shell.rs | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 0812d6d..561fcc2 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -119,26 +119,50 @@ impl PointerGrab for ResizeSurfaceGrab { let mut dx = location.0 - self.start_data.location.0; let mut dy = location.1 - self.start_data.location.1; + let mut new_window_width = self.initial_window_size.0; + let mut new_window_height = self.initial_window_size.1; + let left_right = wl_shell_surface::Resize::Left | wl_shell_surface::Resize::Right; let top_bottom = wl_shell_surface::Resize::Top | wl_shell_surface::Resize::Bottom; - let new_window_width = if self.edges.intersects(left_right) { + + if self.edges.intersects(left_right) { if self.edges.intersects(wl_shell_surface::Resize::Left) { dx = -dx; } - ((self.initial_window_size.0 as f64 + dx) as i32).max(1) - } else { - self.initial_window_size.0 - }; - let new_window_height = if self.edges.intersects(top_bottom) { + new_window_width = (self.initial_window_size.0 as f64 + dx) as i32; + } + + if self.edges.intersects(top_bottom) { if self.edges.intersects(wl_shell_surface::Resize::Top) { dy = -dy; } - ((self.initial_window_size.1 as f64 + dy) as i32).max(1) + new_window_height = (self.initial_window_size.1 as f64 + dy) as i32; + } + + let (min_size, max_size) = + self.ctoken + .with_surface_data(self.toplevel.get_surface().unwrap(), |attrs| { + let data = attrs.user_data.get::().unwrap(); + (data.min_size, data.max_size) + }); + + let min_width = min_size.0.max(1); + let min_height = min_size.1.max(1); + let max_width = if max_size.0 == 0 { + i32::max_value() } else { - self.initial_window_size.1 + max_size.0 }; + let max_height = if max_size.1 == 0 { + i32::max_value() + } else { + max_size.1 + }; + + new_window_width = new_window_width.max(min_width).min(max_width); + new_window_height = new_window_height.max(min_height).min(max_height); self.last_window_size = (new_window_width, new_window_height); From e1396d2e1a544d309f7aff6394ccccf548d65227 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 11 Feb 2020 11:06:36 +0300 Subject: [PATCH 17/17] anvil.shell: add our own ResizeEdge It mirrors the one in wl_shell_surface and lets us not depend on it. --- anvil/Cargo.toml | 1 + anvil/src/shell.rs | 83 +++++++++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index 08ef6bd..21cf6f9 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -14,6 +14,7 @@ rand = "0.6" glium = { version = "0.23.0", default-features = false } wayland-server = "0.23" xkbcommon = "0.4.0" +bitflags = "1.2.1" [dependencies.smithay] path = ".." diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 561fcc2..5b12012 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -4,6 +4,7 @@ use std::{ sync::{Arc, Mutex}, }; +use bitflags::bitflags; use rand; use smithay::{ @@ -98,11 +99,53 @@ impl PointerGrab for MoveSurfaceGrab { } } +bitflags! { + struct ResizeEdge: u32 { + const NONE = 0; + const TOP = 1; + const BOTTOM = 2; + const LEFT = 4; + const TOP_LEFT = 5; + const BOTTOM_LEFT = 6; + const RIGHT = 8; + const TOP_RIGHT = 9; + const BOTTOM_RIGHT = 10; + } +} + +impl From for ResizeEdge { + #[inline] + fn from(x: wl_shell_surface::Resize) -> Self { + Self::from_bits(x.bits()).unwrap() + } +} + +impl From for wl_shell_surface::Resize { + #[inline] + fn from(x: ResizeEdge) -> Self { + Self::from_bits(x.bits()).unwrap() + } +} + +impl From for ResizeEdge { + #[inline] + fn from(x: xdg_toplevel::ResizeEdge) -> Self { + Self::from_bits(x.to_raw()).unwrap() + } +} + +impl From for xdg_toplevel::ResizeEdge { + #[inline] + fn from(x: ResizeEdge) -> Self { + Self::from_raw(x.bits()).unwrap() + } +} + struct ResizeSurfaceGrab { start_data: GrabStartData, ctoken: MyCompositorToken, toplevel: SurfaceKind, - edges: wl_shell_surface::Resize, + edges: ResizeEdge, initial_window_size: (i32, i32), last_window_size: (i32, i32), } @@ -122,11 +165,11 @@ impl PointerGrab for ResizeSurfaceGrab { let mut new_window_width = self.initial_window_size.0; let mut new_window_height = self.initial_window_size.1; - let left_right = wl_shell_surface::Resize::Left | wl_shell_surface::Resize::Right; - let top_bottom = wl_shell_surface::Resize::Top | wl_shell_surface::Resize::Bottom; + let left_right = ResizeEdge::LEFT | ResizeEdge::RIGHT; + let top_bottom = ResizeEdge::TOP | ResizeEdge::BOTTOM; if self.edges.intersects(left_right) { - if self.edges.intersects(wl_shell_surface::Resize::Left) { + if self.edges.intersects(ResizeEdge::LEFT) { dx = -dx; } @@ -134,7 +177,7 @@ impl PointerGrab for ResizeSurfaceGrab { } if self.edges.intersects(top_bottom) { - if self.edges.intersects(wl_shell_surface::Resize::Top) { + if self.edges.intersects(ResizeEdge::TOP) { dy = -dy; } @@ -174,7 +217,7 @@ impl PointerGrab for ResizeSurfaceGrab { }), SurfaceKind::Wl(wl) => wl.send_configure( (self.last_window_size.0 as u32, self.last_window_size.1 as u32), - self.edges, + self.edges.into(), ), } } @@ -369,22 +412,10 @@ pub fn init_shell( let geometry = xdg_window_map.borrow().geometry(&toplevel).unwrap(); let initial_window_size = (geometry.width, geometry.height); - let edges = match edges { - xdg_toplevel::ResizeEdge::Top => wl_shell_surface::Resize::Top, - xdg_toplevel::ResizeEdge::Bottom => wl_shell_surface::Resize::Bottom, - xdg_toplevel::ResizeEdge::Left => wl_shell_surface::Resize::Left, - xdg_toplevel::ResizeEdge::TopLeft => wl_shell_surface::Resize::TopLeft, - xdg_toplevel::ResizeEdge::BottomLeft => wl_shell_surface::Resize::BottomLeft, - xdg_toplevel::ResizeEdge::Right => wl_shell_surface::Resize::Right, - xdg_toplevel::ResizeEdge::TopRight => wl_shell_surface::Resize::TopRight, - xdg_toplevel::ResizeEdge::BottomRight => wl_shell_surface::Resize::BottomRight, - _ => return, - }; - compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| { attrs.user_data.get_mut::().unwrap().resize_state = ResizeState::Resizing(ResizeData { - edges, + edges: edges.into(), initial_window_location, initial_window_size, }); @@ -394,7 +425,7 @@ pub fn init_shell( start_data, ctoken: compositor_token, toplevel, - edges, + edges: edges.into(), initial_window_size, last_window_size: initial_window_size, }; @@ -537,7 +568,7 @@ pub fn init_shell( compositor_token.with_surface_data(surface.get_surface().unwrap(), move |attrs| { attrs.user_data.get_mut::().unwrap().resize_state = ResizeState::Resizing(ResizeData { - edges, + edges: edges.into(), initial_window_location, initial_window_size, }); @@ -547,7 +578,7 @@ pub fn init_shell( start_data, ctoken: compositor_token, toplevel, - edges, + edges: edges.into(), initial_window_size, last_window_size: initial_window_size, }; @@ -567,7 +598,7 @@ pub fn init_shell( #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct ResizeData { /// The edges the surface is being resized with. - edges: wl_shell_surface::Resize, + edges: ResizeEdge, /// The initial window location. initial_window_location: (i32, i32), /// The initial window size (geometry width and height). @@ -724,13 +755,13 @@ fn surface_commit( initial_window_size, } = resize_data; - if edges.intersects(wl_shell_surface::Resize::TopLeft) { + if edges.intersects(ResizeEdge::TOP_LEFT) { let mut location = window_map.location(&toplevel).unwrap(); - if edges.intersects(wl_shell_surface::Resize::Left) { + if edges.intersects(ResizeEdge::LEFT) { location.0 = initial_window_location.0 + (initial_window_size.0 - width); } - if edges.intersects(wl_shell_surface::Resize::Top) { + if edges.intersects(ResizeEdge::TOP) { location.1 = initial_window_location.1 + (initial_window_size.1 - height); }