diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index da5a37d..1cf3e86 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -839,7 +839,7 @@ impl EGLBufferReader { pub fn egl_buffer_dimensions( &self, buffer: &WlBuffer, - ) -> Option> { + ) -> Option> { if !buffer.as_ref().is_alive() { debug!(self.logger, "Suplied buffer is no longer alive"); return None; diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index c620dc5..7026569 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -13,7 +13,7 @@ use cgmath::{prelude::*, Matrix3, Vector2, Vector3}; mod shaders; mod version; -use super::{Bind, Frame, Renderer, Texture, TextureFilter, Transform, Unbind}; +use super::{Bind, Frame, Renderer, Texture, TextureFilter, Unbind}; use crate::backend::allocator::{ dmabuf::{Dmabuf, WeakDmabuf}, Format, @@ -23,7 +23,7 @@ use crate::backend::egl::{ EGLContext, EGLSurface, MakeCurrentError, }; use crate::backend::SwapBuffersError; -use crate::utils::{Buffer, Physical, Rectangle, Size}; +use crate::utils::{Buffer, Physical, Rectangle, Size, Transform}; #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] use super::ImportEgl; @@ -1250,7 +1250,7 @@ impl Frame for Gles2Frame { texture: &Self::TextureId, src: Rectangle, dest: Rectangle, - damage: &[Rectangle], + damage: &[Rectangle], transform: Transform, alpha: f32, ) -> Result<(), Self::Error> { @@ -1266,7 +1266,7 @@ impl Frame for Gles2Frame { assert_eq!(mat, mat * transform.invert().matrix()); assert_eq!(transform.matrix(), Matrix3::::identity()); } - mat = mat * transform.invert().matrix(); + mat = mat * transform.matrix(); mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5)); // this matrix should be regular, we can expect invert to succeed @@ -1290,24 +1290,24 @@ impl Frame for Gles2Frame { let damage = damage .iter() .map(|rect| { + let src = src.size.to_f64(); let rect = rect.to_f64(); let rect_constrained_loc = rect .loc - .constrain(Rectangle::from_extemities((0f64, 0f64), dest.size.to_point())); - let rect_clamped_size = rect.size.clamp( - (0f64, 0f64), - (dest.size.to_point() - rect_constrained_loc).to_size(), - ); + .constrain(Rectangle::from_extemities((0f64, 0f64), src.to_point())); + let rect_clamped_size = rect + .size + .clamp((0f64, 0f64), (src.to_point() - rect_constrained_loc).to_size()); let rect = Rectangle::from_loc_and_size(rect_constrained_loc, rect_clamped_size); - let rect_transformed = self.transformation().transform_rect_in(rect, &dest.size); + let rect_transformed = self.transformation().transform_rect_in(rect, &src); [ - (rect_transformed.loc.x / dest.size.w) as f32, - (rect_transformed.loc.y / dest.size.h) as f32, - (rect_transformed.size.w / dest.size.w) as f32, - (rect_transformed.size.h / dest.size.h) as f32, + (rect_transformed.loc.x / src.w) as f32, + (rect_transformed.loc.y / src.h) as f32, + (rect_transformed.size.w / src.w) as f32, + (rect_transformed.size.h / src.h) as f32, ] }) .flatten() diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index b624697..39c8f91 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -10,7 +10,7 @@ use std::collections::HashSet; use std::error::Error; -use crate::utils::{Buffer, Coordinate, Physical, Point, Rectangle, Size}; +use crate::utils::{Buffer, Physical, Point, Rectangle, Size, Transform}; #[cfg(feature = "wayland_frontend")] use crate::wayland::compositor::SurfaceData; @@ -35,27 +35,6 @@ use crate::backend::egl::{ #[cfg(feature = "wayland_frontend")] pub mod utils; -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -/// Possible transformations to two-dimensional planes -pub enum Transform { - /// Identity transformation (plane is unaltered when applied) - Normal, - /// Plane is rotated by 90 degrees - _90, - /// Plane is rotated by 180 degrees - _180, - /// Plane is rotated by 270 degrees - _270, - /// Plane is flipped vertically - Flipped, - /// Plane is flipped vertically and rotated by 90 degrees - Flipped90, - /// Plane is flipped vertically and rotated by 180 degrees - Flipped180, - /// Plane is flipped vertically and rotated by 270 degrees - Flipped270, -} - #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] /// Texture filtering methods pub enum TextureFilter { @@ -74,70 +53,11 @@ impl Transform { Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0), Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0), Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0), - Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0), + Transform::Flipped90 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0), Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0), - Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0), + Transform::Flipped270 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0), } } - - /// Inverts any 90-degree transformation into 270-degree transformations and vise versa. - /// - /// Flipping is preserved and 180/Normal transformation are uneffected. - pub fn invert(&self) -> Transform { - match self { - Transform::Normal => Transform::Normal, - Transform::Flipped => Transform::Flipped, - Transform::_90 => Transform::_270, - Transform::_180 => Transform::_180, - Transform::_270 => Transform::_90, - Transform::Flipped90 => Transform::Flipped270, - Transform::Flipped180 => Transform::Flipped180, - Transform::Flipped270 => Transform::Flipped90, - } - } - - /// Transformed size after applying this transformation. - pub fn transform_size(&self, size: Size) -> Size { - if *self == Transform::_90 - || *self == Transform::_270 - || *self == Transform::Flipped90 - || *self == Transform::Flipped270 - { - (size.h, size.w).into() - } else { - size - } - } - - /// Transforms a rectangle inside an area of a given size by applying this transformation - pub fn transform_rect_in( - &self, - rect: Rectangle, - area: &Size, - ) -> Rectangle { - let size = self.transform_size(rect.size); - - let loc = match *self { - Transform::Normal => rect.loc, - Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(), - Transform::_180 => ( - area.w - rect.loc.x - rect.size.w, - area.h - rect.loc.y - rect.size.h, - ) - .into(), - Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(), - Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(), - Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(), - Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(), - Transform::Flipped270 => ( - area.h - rect.loc.y - rect.size.h, - area.w - rect.loc.x - rect.size.w, - ) - .into(), - }; - - Rectangle::from_loc_and_size(loc, size) - } } #[cfg(feature = "wayland_frontend")] @@ -217,7 +137,7 @@ pub trait Frame { texture_scale: i32, output_scale: f64, src_transform: Transform, - damage: &[Rectangle], + damage: &[Rectangle], alpha: f32, ) -> Result<(), Self::Error> { self.render_texture_from_to( @@ -227,7 +147,7 @@ pub trait Frame { pos, texture .size() - .to_logical(texture_scale) + .to_logical(texture_scale, src_transform) .to_f64() .to_physical(output_scale), ), @@ -245,7 +165,7 @@ pub trait Frame { texture: &Self::TextureId, src: Rectangle, dst: Rectangle, - damage: &[Rectangle], + damage: &[Rectangle], src_transform: Transform, alpha: f32, ) -> Result<(), Self::Error>; @@ -540,7 +460,7 @@ pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option { /// /// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`]) #[cfg(feature = "wayland_frontend")] -pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option> { +pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option> { use crate::backend::allocator::Buffer; if let Some(buf) = buffer.as_ref().user_data().get::() { @@ -560,102 +480,3 @@ pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::Normal; - - assert_eq!(rect, transform.transform_rect_in(rect, &size)) - } - - #[test] - fn transform_rect_90() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::_90; - - assert_eq!( - Rectangle::from_loc_and_size((30, 10), (40, 30)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_180() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::_180; - - assert_eq!( - Rectangle::from_loc_and_size((30, 30), (30, 40)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_270() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::_270; - - assert_eq!( - Rectangle::from_loc_and_size((20, 30), (40, 30)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_f() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::Flipped; - - assert_eq!( - Rectangle::from_loc_and_size((30, 20), (30, 40)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_f90() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 80)); - let transform = Transform::Flipped90; - - assert_eq!( - Rectangle::from_loc_and_size((20, 10), (40, 30)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_f180() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::Flipped180; - - assert_eq!( - Rectangle::from_loc_and_size((10, 30), (30, 40)), - transform.transform_rect_in(rect, &size) - ) - } - - #[test] - fn transform_rect_f270() { - let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); - let size = Size::from((70, 90)); - let transform = Transform::Flipped270; - - assert_eq!( - Rectangle::from_loc_and_size((30, 30), (40, 30)), - transform.transform_rect_in(rect, &size) - ) - } -} diff --git a/src/backend/renderer/utils.rs b/src/backend/renderer/utils.rs index 1b38f01..e267916 100644 --- a/src/backend/renderer/utils.rs +++ b/src/backend/renderer/utils.rs @@ -2,7 +2,7 @@ use crate::{ backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture}, - utils::{Logical, Physical, Point, Rectangle, Size}, + utils::{Buffer, Logical, Point, Rectangle, Size, Transform}, wayland::compositor::{ is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState, SurfaceAttributes, TraversalAction, @@ -15,8 +15,9 @@ use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface}; #[derive(Default)] pub(crate) struct SurfaceState { - pub(crate) buffer_dimensions: Option>, + pub(crate) buffer_dimensions: Option>, pub(crate) buffer_scale: i32, + pub(crate) buffer_transform: Transform, pub(crate) buffer: Option, pub(crate) texture: Option>, #[cfg(feature = "desktop")] @@ -30,6 +31,7 @@ impl SurfaceState { // new contents self.buffer_dimensions = buffer_dimensions(&buffer); self.buffer_scale = attrs.buffer_scale; + self.buffer_transform = attrs.buffer_transform.into(); if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) { if &old_buffer != self.buffer.as_ref().unwrap() { old_buffer.release(); @@ -52,6 +54,13 @@ impl SurfaceState { None => {} } } + + /// Returns the size of the surface. + pub fn surface_size(&self) -> Option> { + self.buffer_dimensions + .as_ref() + .map(|dim| dim.to_logical(self.buffer_scale, self.buffer_transform)) + } } /// Handler to let smithay take over buffer management. @@ -110,10 +119,6 @@ where T: Texture + 'static, { let mut result = Ok(()); - let damage = damage - .iter() - .map(|geo| geo.to_f64().to_physical(scale).to_i32_up()) - .collect::>(); with_surface_tree_upward( surface, location, @@ -125,17 +130,20 @@ where // Import a new buffer if necessary if data.texture.is_none() { if let Some(buffer) = data.buffer.as_ref() { - let damage = attributes + let buffer_damage = attributes .damage .iter() .map(|dmg| match dmg { Damage::Buffer(rect) => *rect, - // TODO also apply transformations - Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale), + Damage::Surface(rect) => rect.to_buffer( + attributes.buffer_scale, + attributes.buffer_transform.into(), + &data.surface_size().unwrap(), + ), }) .collect::>(); - match renderer.import_buffer(buffer, Some(states), &damage) { + match renderer.import_buffer(buffer, Some(states), &buffer_damage) { Some(Ok(m)) => { data.texture = Some(Box::new(m)); } @@ -169,10 +177,12 @@ where let mut location = *location; if let Some(data) = states.data_map.get::>() { let mut data = data.borrow_mut(); + let dimensions = data.surface_size(); let buffer_scale = data.buffer_scale; - let buffer_dimensions = data.buffer_dimensions; + let buffer_transform = data.buffer_transform; let attributes = states.cached_state.current::(); if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::()) { + let dimensions = dimensions.unwrap(); // we need to re-extract the subsurface offset, as the previous closure // only passes it to our children let mut surface_offset = (0, 0).into(); @@ -182,23 +192,19 @@ where location += current.location; } - let rect = Rectangle::::from_loc_and_size( - surface_offset.to_f64().to_physical(scale).to_i32_round(), - buffer_dimensions - .unwrap_or_default() - .to_logical(buffer_scale) - .to_f64() - .to_physical(scale) - .to_i32_round(), - ); - let new_damage = damage + let damage = damage .iter() .cloned() - .flat_map(|geo| geo.intersection(rect)) + // first move the damage by the surface offset in logical space .map(|mut geo| { - geo.loc -= rect.loc; + // make the damage relative to the surfaec + geo.loc -= surface_offset; geo }) + // then clamp to surface size again in logical space + .flat_map(|geo| geo.intersection(Rectangle::from_loc_and_size((0, 0), dimensions))) + // lastly transform it into buffer space + .map(|geo| geo.to_buffer(buffer_scale, buffer_transform, &dimensions)) .collect::>(); // TODO: Take wp_viewporter into account @@ -208,7 +214,7 @@ where buffer_scale, scale, attributes.buffer_transform.into(), - &new_damage, + &damage, 1.0, ) { result = Err(err); diff --git a/src/desktop/space/mod.rs b/src/desktop/space/mod.rs index 11bd6e8..934a47d 100644 --- a/src/desktop/space/mod.rs +++ b/src/desktop/space/mod.rs @@ -2,12 +2,12 @@ //! rendering helpers to add custom elements or different clients to a space. use crate::{ - backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform}, + backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer}, desktop::{ layer::{layer_map_for_output, LayerSurface}, window::Window, }, - utils::{Logical, Point, Rectangle}, + utils::{Logical, Point, Rectangle, Transform}, wayland::{ compositor::{ get_parent, is_sync_subsurface, with_surface_tree_downward, SubsurfaceCachedState, @@ -372,7 +372,7 @@ impl Space { |wl_surface, states, &loc| { let data = states.data_map.get::>(); - if let Some(size) = data.and_then(|d| d.borrow().size()) { + if let Some(size) = data.and_then(|d| d.borrow().surface_size()) { let surface_rectangle = Rectangle { loc, size }; if output_geometry.overlaps(surface_rectangle) { diff --git a/src/desktop/utils.rs b/src/desktop/utils.rs index 380ae4e..b855691 100644 --- a/src/desktop/utils.rs +++ b/src/desktop/utils.rs @@ -3,7 +3,7 @@ use crate::{ backend::renderer::utils::SurfaceState, desktop::Space, - utils::{Logical, Point, Rectangle, Size}, + utils::{Logical, Point, Rectangle}, wayland::{ compositor::{ with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState, @@ -17,15 +17,9 @@ use wayland_server::protocol::wl_surface; use std::cell::RefCell; impl SurfaceState { - /// Returns the size of the surface. - pub fn size(&self) -> Option> { - self.buffer_dimensions - .map(|dims| dims.to_logical(self.buffer_scale)) - } - fn contains_point>>(&self, attrs: &SurfaceAttributes, point: P) -> bool { let point = point.into(); - let size = match self.size() { + let size = match self.surface_size() { None => return false, // If the surface has no size, it can't have an input region. Some(size) => size, }; @@ -71,7 +65,7 @@ where let mut loc = *loc; let data = states.data_map.get::>(); - if let Some(size) = data.and_then(|d| d.borrow().size()) { + if let Some(size) = data.and_then(|d| d.borrow().surface_size()) { if states.role == Some("subsurface") { let current = states.cached_state.current::(); loc += current.location; @@ -148,7 +142,11 @@ where damage.extend(attributes.damage.iter().map(|dmg| { let mut rect = match dmg { - Damage::Buffer(rect) => rect.to_logical(attributes.buffer_scale), + Damage::Buffer(rect) => rect.to_logical( + attributes.buffer_scale, + attributes.buffer_transform.into(), + &data.buffer_dimensions.unwrap(), + ), Damage::Surface(rect) => *rect, }; rect.loc += location; diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index b4a14a3..98c2848 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -406,10 +406,11 @@ impl Point { #[inline] /// Convert this logical point to buffer coordinate space according to given scale factor - pub fn to_buffer(self, scale: N) -> Point { + pub fn to_buffer(self, scale: N, transformation: Transform, area: &Size) -> Point { + let point = transformation.transform_point_in(self, area); Point { - x: self.x.upscale(scale), - y: self.y.upscale(scale), + x: point.x.upscale(scale), + y: point.y.upscale(scale), _kind: std::marker::PhantomData, } } @@ -430,10 +431,11 @@ impl Point { impl Point { #[inline] /// Convert this physical point to logical coordinate space according to given scale factor - pub fn to_logical(self, scale: N) -> Point { + pub fn to_logical(self, scale: N, transform: Transform, area: &Size) -> Point { + let point = transform.invert().transform_point_in(self, area); Point { - x: self.x.downscale(scale), - y: self.y.downscale(scale), + x: point.x.downscale(scale), + y: point.y.downscale(scale), _kind: std::marker::PhantomData, } } @@ -667,12 +669,12 @@ impl Size { #[inline] /// Convert this logical size to buffer coordinate space according to given scale factor - pub fn to_buffer(self, scale: N) -> Size { - Size { + pub fn to_buffer(self, scale: N, transformation: Transform) -> Size { + transformation.transform_size(Size { w: self.w.upscale(scale), h: self.h.upscale(scale), _kind: std::marker::PhantomData, - } + }) } } @@ -691,12 +693,12 @@ impl Size { impl Size { #[inline] /// Convert this physical point to logical coordinate space according to given scale factor - pub fn to_logical(self, scale: N) -> Size { - Size { + pub fn to_logical(self, scale: N, transformation: Transform) -> Size { + transformation.invert().transform_size(Size { w: self.w.downscale(scale), h: self.h.downscale(scale), _kind: std::marker::PhantomData, - } + }) } } @@ -969,10 +971,24 @@ impl Rectangle { /// Convert this logical rectangle to buffer coordinate space according to given scale factor #[inline] - pub fn to_buffer(self, scale: N) -> Rectangle { + pub fn to_buffer( + self, + scale: N, + transformation: Transform, + area: &Size, + ) -> Rectangle { + let rect = transformation.transform_rect_in(self, area); Rectangle { - loc: self.loc.to_buffer(scale), - size: self.size.to_buffer(scale), + loc: Point { + x: rect.loc.x.upscale(scale), + y: rect.loc.y.upscale(scale), + _kind: std::marker::PhantomData, + }, + size: Size { + w: rect.size.w.upscale(scale), + h: rect.size.h.upscale(scale), + _kind: std::marker::PhantomData, + }, } } } @@ -991,10 +1007,24 @@ impl Rectangle { impl Rectangle { /// Convert this physical rectangle to logical coordinate space according to given scale factor #[inline] - pub fn to_logical(self, scale: N) -> Rectangle { + pub fn to_logical( + self, + scale: N, + transformation: Transform, + area: &Size, + ) -> Rectangle { + let rect = transformation.invert().transform_rect_in(self, area); Rectangle { - loc: self.loc.to_logical(scale), - size: self.size.to_logical(scale), + loc: Point { + x: rect.loc.x.downscale(scale), + y: rect.loc.y.downscale(scale), + _kind: std::marker::PhantomData, + }, + size: Size { + w: rect.size.w.downscale(scale), + h: rect.size.h.downscale(scale), + _kind: std::marker::PhantomData, + }, } } } @@ -1071,3 +1101,207 @@ impl Default for Rectangle { } } } + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +/// Possible transformations to two-dimensional planes +pub enum Transform { + /// Identity transformation (plane is unaltered when applied) + Normal, + /// Plane is rotated by 90 degrees + _90, + /// Plane is rotated by 180 degrees + _180, + /// Plane is rotated by 270 degrees + _270, + /// Plane is flipped vertically + Flipped, + /// Plane is flipped vertically and rotated by 90 degrees + Flipped90, + /// Plane is flipped vertically and rotated by 180 degrees + Flipped180, + /// Plane is flipped vertically and rotated by 270 degrees + Flipped270, +} + +impl Default for Transform { + fn default() -> Transform { + Transform::Normal + } +} + +impl Transform { + /// Inverts any 90-degree transformation into 270-degree transformations and vise versa. + /// + /// Flipping is preserved and 180/Normal transformation are uneffected. + pub fn invert(&self) -> Transform { + match self { + Transform::Normal => Transform::Normal, + Transform::Flipped => Transform::Flipped, + Transform::_90 => Transform::_270, + Transform::_180 => Transform::_180, + Transform::_270 => Transform::_90, + Transform::Flipped90 => Transform::Flipped270, + Transform::Flipped180 => Transform::Flipped180, + Transform::Flipped270 => Transform::Flipped90, + } + } + + /// Transforms a point inside an area of a given size by applying this transformation. + pub fn transform_point_in( + &self, + point: Point, + area: &Size, + ) -> Point { + match *self { + Transform::Normal => point, + Transform::_90 => (area.h - point.y, point.x).into(), + Transform::_180 => (area.w - point.x, area.h - point.y).into(), + Transform::_270 => (point.y, area.w - point.x).into(), + Transform::Flipped => (area.w - point.x, point.y).into(), + Transform::Flipped90 => (point.y, point.x).into(), + Transform::Flipped180 => (point.x, area.h - point.y).into(), + Transform::Flipped270 => (area.h - point.y, area.w - point.x).into(), + } + } + + /// Transformed size after applying this transformation. + pub fn transform_size(&self, size: Size) -> Size { + if *self == Transform::_90 + || *self == Transform::_270 + || *self == Transform::Flipped90 + || *self == Transform::Flipped270 + { + (size.h, size.w).into() + } else { + size + } + } + + /// Transforms a rectangle inside an area of a given size by applying this transformation. + pub fn transform_rect_in( + &self, + rect: Rectangle, + area: &Size, + ) -> Rectangle { + let size = self.transform_size(rect.size); + + let loc = match *self { + Transform::Normal => rect.loc, + Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(), + Transform::_180 => ( + area.w - rect.loc.x - rect.size.w, + area.h - rect.loc.y - rect.size.h, + ) + .into(), + Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(), + Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(), + Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(), + Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(), + Transform::Flipped270 => ( + area.h - rect.loc.y - rect.size.h, + area.w - rect.loc.x - rect.size.w, + ) + .into(), + }; + + Rectangle::from_loc_and_size(loc, size) + } +} + +#[cfg(test)] +mod tests { + use super::{Logical, Rectangle, Size, Transform}; + + #[test] + fn transform_rect_ident() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Normal; + + assert_eq!(rect, transform.transform_rect_in(rect, &size)) + } + + #[test] + fn transform_rect_90() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_90; + + assert_eq!( + Rectangle::from_loc_and_size((30, 10), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_180() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_180; + + assert_eq!( + Rectangle::from_loc_and_size((30, 30), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_270() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_270; + + assert_eq!( + Rectangle::from_loc_and_size((20, 30), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped; + + assert_eq!( + Rectangle::from_loc_and_size((30, 20), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f90() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 80)); + let transform = Transform::Flipped90; + + assert_eq!( + Rectangle::from_loc_and_size((20, 10), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f180() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped180; + + assert_eq!( + Rectangle::from_loc_and_size((10, 30), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f270() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped270; + + assert_eq!( + Rectangle::from_loc_and_size((30, 30), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index de3d678..bbe8e55 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -10,7 +10,7 @@ pub mod x11rb; pub(crate) mod ids; pub mod user_data; -pub use self::geometry::{Buffer, Coordinate, Logical, Physical, Point, Raw, Rectangle, Size}; +pub use self::geometry::{Buffer, Coordinate, Logical, Physical, Point, Raw, Rectangle, Size, Transform}; /// This resource is not managed by Smithay #[derive(Debug)]