From 95327685c151c44430f025afae06114376921b64 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 28 May 2022 18:18:29 +0200 Subject: [PATCH] wayland: implement surface transformations - buffer scale - buffer transform - viewporter --- src/cursor.rs | 4 +- src/fixed.rs | 21 ++ src/globals.rs | 2 + src/ifs.rs | 1 + src/ifs/wl_output.rs | 23 +- src/ifs/wl_surface.rs | 317 +++++++++++++++++++++++++--- src/ifs/wl_surface/cursor.rs | 5 +- src/ifs/wl_surface/wl_subsurface.rs | 9 +- src/ifs/wl_surface/wp_viewport.rs | 107 ++++++++++ src/ifs/wl_surface/xwindow.rs | 17 +- src/ifs/wp_viewporter.rs | 104 +++++++++ src/rect.rs | 5 + src/render/renderer/framebuffer.rs | 2 +- src/render/renderer/renderer.rs | 68 ++++-- wire/wp_viewport.txt | 16 ++ wire/wp_viewporter.txt | 9 + 16 files changed, 635 insertions(+), 75 deletions(-) create mode 100644 src/ifs/wl_surface/wp_viewport.rs create mode 100644 src/ifs/wp_viewporter.rs create mode 100644 wire/wp_viewport.txt create mode 100644 wire/wp_viewporter.txt diff --git a/src/cursor.rs b/src/cursor.rs index 84b8c09f..f7dbab34 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -221,7 +221,7 @@ impl Cursor for StaticCursor { } fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { - renderer.render_texture(&self.image.tex, x, y, ARGB8888); + renderer.render_texture(&self.image.tex, x, y, ARGB8888, None, None); } fn get_hotspot(&self) -> (i32, i32) { @@ -252,7 +252,7 @@ impl Cursor for AnimatedCursor { fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { let img = &self.images[self.idx.get()]; - renderer.render_texture(&img.tex, x, y, ARGB8888); + renderer.render_texture(&img.tex, x, y, ARGB8888, None, None); } fn get_hotspot(&self) -> (i32, i32) { diff --git a/src/fixed.rs b/src/fixed.rs index 3f9a44e4..7e7afe1d 100644 --- a/src/fixed.rs +++ b/src/fixed.rs @@ -1,4 +1,5 @@ use std::{ + cmp::Ordering, fmt::{Debug, Display, Formatter}, ops::{Add, AddAssign, Sub, SubAssign}, }; @@ -8,6 +9,10 @@ use std::{ pub struct Fixed(pub i32); impl Fixed { + pub fn is_integer(self) -> bool { + self.0 & 255 == 0 + } + pub fn from_f64(f: f64) -> Self { Self((f * 256.0) as i32) } @@ -20,6 +25,10 @@ impl Fixed { Self(i >> 8) } + pub fn to_int(self) -> i32 { + self.0 >> 8 + } + pub fn from_int(i: i32) -> Self { Self(i << 8) } @@ -33,6 +42,18 @@ impl Fixed { } } +impl PartialEq for Fixed { + fn eq(&self, other: &i32) -> bool { + self.0 == *other << 8 + } +} + +impl PartialOrd for Fixed { + fn partial_cmp(&self, other: &i32) -> Option { + self.0.partial_cmp(&(*other << 8)) + } +} + impl Debug for Fixed { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Debug::fmt(&self.to_f64(), f) diff --git a/src/globals.rs b/src/globals.rs index 3e53cee7..f015d728 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -19,6 +19,7 @@ use { wl_shm::WlShmGlobal, wl_subcompositor::WlSubcompositorGlobal, wp_presentation::WpPresentationGlobal, + wp_viewporter::WpViewporterGlobal, xdg_wm_base::XdgWmBaseGlobal, zwlr_layer_shell_v1::ZwlrLayerShellV1Global, zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global, @@ -141,6 +142,7 @@ impl Globals { add_singleton!(ZwlrScreencopyManagerV1Global); add_singleton!(ZwpRelativePointerManagerV1Global); add_singleton!(ExtSessionLockManagerV1Global); + add_singleton!(WpViewporterGlobal); if backend.supports_idle() { add_singleton!(ZwpIdleInhibitManagerV1Global); diff --git a/src/ifs.rs b/src/ifs.rs index 63fe5e89..18621aba 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -22,6 +22,7 @@ pub mod wl_subcompositor; pub mod wl_surface; pub mod wp_presentation; pub mod wp_presentation_feedback; +pub mod wp_viewporter; pub mod xdg_positioner; pub mod xdg_wm_base; pub mod zwlr_layer_shell_v1; diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 285321b4..fc4e43fa 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -45,21 +45,14 @@ const SP_VERTICAL_RGB: i32 = 4; #[allow(dead_code)] const SP_VERTICAL_BGR: i32 = 5; -const TF_NORMAL: i32 = 0; -#[allow(dead_code)] -const TF_90: i32 = 1; -#[allow(dead_code)] -const TF_180: i32 = 2; -#[allow(dead_code)] -const TF_270: i32 = 3; -#[allow(dead_code)] -const TF_FLIPPED: i32 = 4; -#[allow(dead_code)] -const TF_FLIPPED_90: i32 = 5; -#[allow(dead_code)] -const TF_FLIPPED_180: i32 = 6; -#[allow(dead_code)] -const TF_FLIPPED_270: i32 = 7; +pub const TF_NORMAL: i32 = 0; +pub const TF_90: i32 = 1; +pub const TF_180: i32 = 2; +pub const TF_270: i32 = 3; +pub const TF_FLIPPED: i32 = 4; +pub const TF_FLIPPED_90: i32 = 5; +pub const TF_FLIPPED_180: i32 = 6; +pub const TF_FLIPPED_270: i32 = 7; const MODE_CURRENT: u32 = 1; #[allow(dead_code)] diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 47834de3..dacf865d 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1,6 +1,7 @@ pub mod cursor; pub mod ext_session_lock_surface_v1; pub mod wl_subsurface; +pub mod wp_viewport; pub mod xdg_surface; pub mod xwindow; pub mod zwlr_layer_surface_v1; @@ -14,10 +15,14 @@ use { ifs::{ wl_buffer::WlBuffer, wl_callback::WlCallback, + wl_output::{ + TF_180, TF_270, TF_90, TF_FLIPPED, TF_FLIPPED_180, TF_FLIPPED_270, TF_FLIPPED_90, + TF_NORMAL, + }, wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::{ - cursor::CursorSurface, wl_subsurface::WlSubsurface, xdg_surface::XdgSurfaceError, - zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error, + cursor::CursorSurface, wl_subsurface::WlSubsurface, wp_viewport::WpViewport, + xdg_surface::XdgSurfaceError, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error, }, wp_presentation_feedback::WpPresentationFeedback, }, @@ -57,6 +62,124 @@ const INVALID_TRANSFORM: u32 = 1; #[allow(dead_code)] const INVALID_SIZE: u32 = 2; +#[derive(Copy, Clone, Debug, PartialEq)] +enum Transform { + Normal, + Rotate90, + Rotate180, + Rotate270, + Flipped, + Flipped90, + Flipped180, + Flipped270, +} + +impl Transform { + fn swaps_dimensions(self) -> bool { + match self { + Transform::Normal => false, + Transform::Rotate90 => true, + Transform::Rotate180 => false, + Transform::Rotate270 => true, + Transform::Flipped => false, + Transform::Flipped90 => true, + Transform::Flipped180 => false, + Transform::Flipped270 => true, + } + } +} + +#[derive(Default, Debug)] +struct BufferPoint { + x: f32, + y: f32, +} + +#[derive(Default, Debug)] +struct BufferPoints { + top_right: BufferPoint, + top_left: BufferPoint, + bottom_right: BufferPoint, + bottom_left: BufferPoint, +} + +impl Transform { + fn apply_inv_sized(self, x1: f32, y1: f32, width: f32, height: f32) -> BufferPoints { + let x2 = x1 + width; + let y2 = y1 + height; + self.apply_inv(x1, y1, x2, y2) + } + + fn apply_inv(self, x1: f32, y1: f32, x2: f32, y2: f32) -> BufferPoints { + macro_rules! bp { + ( + $tl_x:expr, $tl_y:expr, + $tr_x:expr, $tr_y:expr, + $br_x:expr, $br_y:expr, + $bl_x:expr, $bl_y:expr, + ) => { + BufferPoints { + top_left: BufferPoint { x: $tl_x, y: $tl_y }, + top_right: BufferPoint { x: $tr_x, y: $tr_y }, + bottom_right: BufferPoint { x: $br_x, y: $br_y }, + bottom_left: BufferPoint { x: $bl_x, y: $bl_y }, + } + }; + } + use Transform::*; + match self { + Normal => bp! { + x1, y1, + x2, y1, + x2, y2, + x1, y2, + }, + Rotate90 => bp! { + y1, x2, + y1, x1, + y2, x1, + y2, x2, + }, + Rotate180 => bp! { + x2, y2, + x1, y2, + x1, y1, + x2, y1, + }, + Rotate270 => bp! { + y2, x1, + y2, x2, + y1, x2, + y1, x1, + }, + Flipped => bp! { + x2, y1, + x1, y1, + x1, y2, + x2, y2, + }, + Flipped90 => bp! { + y1, x1, + y1, x2, + y2, x2, + y2, x1, + }, + Flipped180 => bp! { + x1, y2, + x2, y2, + x2, y1, + x1, y1, + }, + Flipped270 => bp! { + y2, x2, + y2, x1, + y1, x1, + y1, x2, + }, + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum SurfaceRole { None, @@ -93,6 +216,12 @@ pub struct WlSurface { pending: PendingState, input_region: Cell>>, opaque_region: Cell>>, + buffer_points: RefCell, + pub buffer_points_norm: RefCell<[f32; 8]>, + buffer_transform: Cell, + buffer_scale: Cell, + src_rect: Cell>, + dst_size: Cell>, pub extents: Cell, pub buffer_abs_pos: Cell, pub need_extents_update: Cell, @@ -109,6 +238,7 @@ pub struct WlSurface { pub dnd_icons: SmallMap, 1>, pub tracker: Tracker, idle_inhibitors: CopyHashMap>, + viewporter: CloneCell>>, } impl Debug for WlSurface { @@ -188,6 +318,10 @@ struct PendingState { frame_request: RefCell>>, damage: Cell, presentation_feedback: RefCell>>, + src_rect: Cell>>, + dst_size: Cell>>, + scale: Cell>, + transform: Cell>, } #[derive(Default)] @@ -213,6 +347,12 @@ impl WlSurface { pending: Default::default(), input_region: Default::default(), opaque_region: Default::default(), + buffer_points: Default::default(), + buffer_points_norm: Default::default(), + buffer_transform: Cell::new(Transform::Normal), + buffer_scale: Cell::new(1), + src_rect: Cell::new(None), + dst_size: Cell::new(None), extents: Default::default(), buffer_abs_pos: Cell::new(Default::default()), need_extents_update: Default::default(), @@ -229,6 +369,7 @@ impl WlSurface { dnd_icons: Default::default(), tracker: Default::default(), idle_inhibitors: Default::default(), + viewporter: Default::default(), } } @@ -327,10 +468,7 @@ impl WlSurface { fn calculate_extents(&self) { let old_extents = self.extents.get(); - let mut extents = Rect::new_empty(0, 0); - if let Some(b) = self.buffer.get() { - extents = b.rect; - } + let mut extents = self.buffer_abs_pos.get().at_point(0, 0); let children = self.children.borrow(); if let Some(children) = &*children { for ss in children.subsurfaces.values() { @@ -472,24 +610,46 @@ impl WlSurface { } } } + let mut scale_changed = false; + if let Some(scale) = self.pending.scale.take() { + scale_changed = true; + self.buffer_scale.set(scale); + } + let mut buffer_transform_changed = false; + if let Some(transform) = self.pending.transform.take() { + buffer_transform_changed = true; + self.buffer_transform.set(transform); + } + let mut viewport_changed = false; + if let Some(dst_size) = self.pending.dst_size.take() { + viewport_changed = true; + self.dst_size.set(dst_size); + } + if let Some(src_rect) = self.pending.src_rect.take() { + viewport_changed = true; + self.src_rect.set(src_rect); + } + if viewport_changed { + if let Some(rect) = self.src_rect.get() { + if self.dst_size.get().is_none() { + if !rect[2].is_integer() || !rect[3].is_integer() { + return Err(WlSurfaceError::NonIntegerViewportSize); + } + } + } + } + let mut buffer_changed = false; + let mut old_raw_size = None; if let Some(buffer_change) = self.pending.buffer.take() { - let mut old_size = None; - let mut new_size = None; + buffer_changed = true; if let Some(buffer) = self.buffer.take() { - old_size = Some(buffer.rect); + old_raw_size = Some(buffer.rect); if !buffer.destroyed() { buffer.send_release(); } } if let Some((dx, dy, buffer)) = buffer_change { let _ = buffer.update_texture(); - new_size = Some(buffer.rect); - self.buffer_abs_pos.set( - self.buffer_abs_pos - .get() - .with_size(buffer.rect.width(), buffer.rect.height()) - .unwrap(), - ); self.buffer.set(Some(buffer)); self.buf_x.fetch_add(dx); self.buf_y.fetch_add(dy); @@ -506,9 +666,94 @@ impl WlSurface { cursor.set_hotspot(0, 0); } } - if old_size != new_size { + } + let transform_changed = viewport_changed || scale_changed || buffer_transform_changed; + if buffer_changed || transform_changed { + let mut buffer_points = self.buffer_points.borrow_mut(); + let mut buffer_points_norm = self.buffer_points_norm.borrow_mut(); + let mut new_size = None; + if let Some(src_rect) = self.src_rect.get() { + if transform_changed { + let [mut x1, mut y1, mut width, mut height] = src_rect.map(|v| v.to_f64() as _); + let scale = self.buffer_scale.get(); + if scale != 1 { + let scale = scale as f32; + x1 *= scale; + y1 *= scale; + width *= scale; + height *= scale; + } + *buffer_points = self + .buffer_transform + .get() + .apply_inv_sized(x1, y1, width, height); + } + let size = match self.dst_size.get() { + Some(ds) => ds, + None => (src_rect[2].to_int(), src_rect[3].to_int()), + }; + new_size = Some(size); + } else if let Some(size) = self.dst_size.get() { + new_size = Some(size); + } + if let Some(buffer) = self.buffer.get() { + if new_size.is_none() { + let (mut width, mut height) = buffer.rect.size(); + if self.buffer_transform.get().swaps_dimensions() { + mem::swap(&mut width, &mut height); + } + let scale = self.buffer_scale.get(); + if scale != 1 { + width = (width + scale - 1) / scale; + height = (height + scale - 1) / scale; + } + new_size = Some((width, height)); + } + if transform_changed || Some(buffer.rect) != old_raw_size { + if self.src_rect.get().is_none() { + *buffer_points = self + .buffer_transform + .get() + .apply_inv_sized(0.0, 0.0, 1.0, 1.0); + let points = &*buffer_points; + *buffer_points_norm = [ + points.top_right.x, + points.top_right.y, + points.top_left.x, + points.top_left.y, + points.bottom_right.x, + points.bottom_right.y, + points.bottom_left.x, + points.bottom_left.y, + ]; + } else { + let width = buffer.rect.width() as f32; + let height = buffer.rect.height() as f32; + let points = &*buffer_points; + *buffer_points_norm = [ + points.top_right.x / width, + points.top_right.y / height, + points.top_left.x / width, + points.top_left.y / height, + points.bottom_right.x / width, + points.bottom_right.y / height, + points.bottom_left.x / width, + points.bottom_left.y / height, + ]; + for &v in buffer_points_norm.iter() { + if v > 1.0 { + return Err(WlSurfaceError::ViewportOutsideBuffer); + } + } + } + } + } + let (width, height) = new_size.unwrap_or_default(); + if (width, height) != self.buffer_abs_pos.get().size() { self.need_extents_update.set(true); } + self.buffer_abs_pos + .set(self.buffer_abs_pos.get().with_size(width, height).unwrap()); for (_, cursor) in &self.cursors { cursor.handle_buffer_change(); } @@ -549,12 +794,29 @@ impl WlSurface { } fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> { - let _req: SetBufferTransform = self.parse(parser)?; + let req: SetBufferTransform = self.parse(parser)?; + use Transform::*; + let tf = match req.transform { + TF_NORMAL => Normal, + TF_90 => Rotate90, + TF_180 => Rotate180, + TF_270 => Rotate270, + TF_FLIPPED => Flipped, + TF_FLIPPED_90 => Flipped90, + TF_FLIPPED_180 => Flipped180, + TF_FLIPPED_270 => Flipped270, + _ => return Err(WlSurfaceError::UnknownBufferTransform(req.transform)), + }; + self.pending.transform.set(Some(tf)); Ok(()) } fn set_buffer_scale(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> { - let _req: SetBufferScale = self.parse(parser)?; + let req: SetBufferScale = self.parse(parser)?; + if req.scale < 1 { + return Err(WlSurfaceError::NonPositiveBufferScale); + } + self.pending.scale.set(Some(req.scale)); Ok(()) } @@ -565,15 +827,12 @@ impl WlSurface { } fn find_surface_at(self: &Rc, x: i32, y: i32) -> Option<(Rc, i32, i32)> { - let buffer = match self.buffer.get() { - Some(b) => b, - _ => return None, - }; + let rect = self.buffer_abs_pos.get().at_point(0, 0); let children = self.children.borrow(); let children = match children.deref() { Some(c) => c, _ => { - return if buffer.rect.contains(x, y) { + return if rect.contains(x, y) { Some((self.clone(), x, y)) } else { None @@ -598,7 +857,7 @@ impl WlSurface { if let Some(res) = ss(&children.above) { return Some(res); } - if buffer.rect.contains(x, y) { + if rect.contains(x, y) { return Some((self.clone(), x, y)); } if let Some(res) = ss(&children.below) { @@ -868,6 +1127,14 @@ pub enum WlSurfaceError { ReloObjectStillExists, #[error("Parsing failed")] MsgParserError(#[source] Box), + #[error("Buffer scale is not positive")] + NonPositiveBufferScale, + #[error("Unknown buffer transform {0}")] + UnknownBufferTransform(i32), + #[error("Viewport source is not integer-sized and destination size is not set")] + NonIntegerViewportSize, + #[error("Viewport source is not contained in the attached buffer")] + ViewportOutsideBuffer, } efrom!(WlSurfaceError, ClientError); efrom!(WlSurfaceError, XdgSurfaceError); diff --git a/src/ifs/wl_surface/cursor.rs b/src/ifs/wl_surface/cursor.rs index f051aec1..223ca2ca 100644 --- a/src/ifs/wl_surface/cursor.rs +++ b/src/ifs/wl_surface/cursor.rs @@ -50,10 +50,7 @@ impl CursorSurface { } pub fn handle_buffer_change(&self) { - let (width, height) = match self.surface.buffer.get() { - Some(b) => (b.rect.width(), b.rect.height()), - _ => (0, 0), - }; + let (width, height) = self.surface.buffer_abs_pos.get().size(); self.extents .set(Rect::new_sized(0, 0, width, height).unwrap()); self.update_extents(); diff --git a/src/ifs/wl_surface/wl_subsurface.rs b/src/ifs/wl_surface/wl_subsurface.rs index ed9c5b15..908227ab 100644 --- a/src/ifs/wl_surface/wl_subsurface.rs +++ b/src/ifs/wl_surface/wl_subsurface.rs @@ -282,12 +282,9 @@ impl SurfaceExt for WlSubsurface { self.node.borrow_mut().replace(v); } if let Some((x, y)) = self.pending.position.take() { - if let Some(buffer) = self.surface.buffer.get() { - self.position.set(buffer.rect.move_(x, y)); - self.parent.need_extents_update.set(true); - } else { - self.position.set(Rect::new_empty(x, y)); - } + self.position + .set(self.surface.buffer_abs_pos.get().at_point(x, y)); + self.parent.need_extents_update.set(true); } } diff --git a/src/ifs/wl_surface/wp_viewport.rs b/src/ifs/wl_surface/wp_viewport.rs new file mode 100644 index 00000000..a240373c --- /dev/null +++ b/src/ifs/wl_surface/wp_viewport.rs @@ -0,0 +1,107 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_surface::WlSurface, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{wp_viewport::*, WpViewportId}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpViewport { + pub id: WpViewportId, + pub client: Rc, + pub surface: Rc, + pub tracker: Tracker, +} + +impl WpViewport { + pub fn new(id: WpViewportId, surface: &Rc) -> Self { + Self { + id, + client: surface.client.clone(), + surface: surface.clone(), + tracker: Default::default(), + } + } + + pub fn install(self: &Rc) -> Result<(), WpViewportError> { + if self.surface.viewporter.get().is_some() { + return Err(WpViewportError::ViewportExists); + } + self.surface.viewporter.set(Some(self.clone())); + Ok(()) + } + + fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> { + let _req: Destroy = self.client.parse(self, msg)?; + self.surface.pending.src_rect.set(Some(None)); + self.surface.pending.dst_size.set(Some(None)); + self.surface.viewporter.take(); + self.client.remove_obj(self)?; + Ok(()) + } + + fn set_source(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> { + let req: SetSource = self.client.parse(self, msg)?; + let rect = if req.x == -1 && req.y == -1 && req.width == -1 && req.height == -1 { + None + } else { + let invalid = req.x < 0 || req.y < 0 || req.width <= 0 || req.height <= 0; + if invalid { + return Err(WpViewportError::InvalidSourceRect); + } + Some([req.x, req.y, req.width, req.height]) + }; + self.surface.pending.src_rect.set(Some(rect)); + Ok(()) + } + + fn set_destination(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewportError> { + let req: SetDestination = self.client.parse(self, msg)?; + let size = if req.width == -1 && req.height == -1 { + None + } else if req.width <= 0 || req.height <= 0 { + return Err(WpViewportError::InvalidDestRect); + } else { + Some((req.width, req.height)) + }; + self.surface.pending.dst_size.set(Some(size)); + Ok(()) + } +} + +object_base! { + WpViewport; + + DESTROY => destroy, + SET_SOURCE => set_source, + SET_DESTINATION => set_destination, +} + +impl Object for WpViewport { + fn num_requests(&self) -> u32 { + SET_DESTINATION + 1 + } +} + +simple_add_obj!(WpViewport); + +#[derive(Debug, Error)] +pub enum WpViewportError { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("The surface already has a viewport")] + ViewportExists, + #[error("Rectangle is empty or outside the first quadrant")] + InvalidSourceRect, + #[error("Rectangle is empty")] + InvalidDestRect, +} +efrom!(WpViewportError, MsgParserError); +efrom!(WpViewportError, ClientError); diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index 76cf18f7..abae97a3 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -343,15 +343,14 @@ impl Node for Xwindow { } fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { - if let Some(buffer) = self.surface.buffer.get() { - if x < buffer.rect.width() && y < buffer.rect.height() { - tree.push(FoundNode { - node: self.surface.clone(), - x, - y, - }); - return FindTreeResult::AcceptsInput; - } + let rect = self.surface.buffer_abs_pos.get(); + if x < rect.width() && y < rect.height() { + tree.push(FoundNode { + node: self.surface.clone(), + x, + y, + }); + return FindTreeResult::AcceptsInput; } FindTreeResult::Other } diff --git a/src/ifs/wp_viewporter.rs b/src/ifs/wp_viewporter.rs new file mode 100644 index 00000000..d128c7e9 --- /dev/null +++ b/src/ifs/wp_viewporter.rs @@ -0,0 +1,104 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wl_surface::wp_viewport::{WpViewport, WpViewportError}, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{wp_viewporter::*, WpViewporterId}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpViewporterGlobal { + pub name: GlobalName, +} + +impl WpViewporterGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WpViewporterId, + client: &Rc, + _version: u32, + ) -> Result<(), WpViewporterError> { + let obj = Rc::new(WpViewporter { + id, + client: client.clone(), + tracker: Default::default(), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +global_base!(WpViewporterGlobal, WpViewporter, WpViewporterError); + +impl Global for WpViewporterGlobal { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +simple_add_global!(WpViewporterGlobal); + +pub struct WpViewporter { + pub id: WpViewporterId, + pub client: Rc, + pub tracker: Tracker, +} + +impl WpViewporter { + fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewporterError> { + let _req: Destroy = self.client.parse(self, msg)?; + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_viewport(&self, msg: MsgParser<'_, '_>) -> Result<(), WpViewporterError> { + let req: GetViewport = self.client.parse(self, msg)?; + let surface = self.client.lookup(req.surface)?; + let viewport = Rc::new(WpViewport::new(req.id, &surface)); + track!(self.client, viewport); + viewport.install()?; + self.client.add_client_obj(&viewport)?; + Ok(()) + } +} + +object_base! { + WpViewporter; + + DESTROY => destroy, + GET_VIEWPORT => get_viewport, +} + +impl Object for WpViewporter { + fn num_requests(&self) -> u32 { + GET_VIEWPORT + 1 + } +} + +simple_add_obj!(WpViewporter); + +#[derive(Debug, Error)] +pub enum WpViewporterError { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WpViewportError(#[from] WpViewportError), +} +efrom!(WpViewporterError, MsgParserError); +efrom!(WpViewporterError, ClientError); diff --git a/src/rect.rs b/src/rect.rs index 26b14fc5..d99e3117 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -61,6 +61,7 @@ impl RectOverflow { } impl Rect { + #[allow(dead_code)] pub fn new_empty(x: i32, y: i32) -> Self { Self { x1: x, @@ -204,4 +205,8 @@ impl Rect { pub fn position(&self) -> (i32, i32) { (self.x1, self.y1) } + + pub fn size(&self) -> (i32, i32) { + (self.width(), self.height()) + } } diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs index 79161966..a4fe1aad 100644 --- a/src/render/renderer/framebuffer.rs +++ b/src/render/renderer/framebuffer.rs @@ -62,7 +62,7 @@ impl Framebuffer { on_output: false, result: &mut RenderResult::default(), }; - renderer.render_texture(texture, x, y, XRGB8888); + renderer.render_texture(texture, x, y, XRGB8888, None, None); unsafe { glFlush(); } diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 096bdea3..bb7df184 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -122,10 +122,24 @@ impl Renderer<'_> { let c = theme.colors.unfocused_title_background.get(); self.fill_boxes2(&rd.inactive_workspaces, &c, x, y); for title in &rd.titles { - self.render_texture(&title.tex, x + title.tex_x, y + title.tex_y, ARGB8888); + self.render_texture( + &title.tex, + x + title.tex_x, + y + title.tex_y, + ARGB8888, + None, + None, + ); } if let Some(status) = &rd.status { - self.render_texture(&status.tex, x + status.tex_x, y + status.tex_y, ARGB8888); + self.render_texture( + &status.tex, + x + status.tex_x, + y + status.tex_y, + ARGB8888, + None, + None, + ); } } if let Some(ws) = output.workspace.get() { @@ -209,7 +223,7 @@ impl Renderer<'_> { if let Some(tex) = placeholder.texture.get() { let x = x + (pos.width() - tex.width()) / 2; let y = y + (pos.height() - tex.height()) / 2; - self.render_texture(&tex, x, y, &ARGB8888); + self.render_texture(&tex, x, y, &ARGB8888, None, None); } } @@ -234,7 +248,7 @@ impl Renderer<'_> { self.fill_boxes2(std::slice::from_ref(lar), &c, x, y); } for title in &rd.titles { - self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888); + self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888, None, None); } } if let Some(child) = container.mono_child.get() { @@ -287,6 +301,8 @@ impl Renderer<'_> { return; } }; + let tpoints = surface.buffer_points_norm.borrow_mut(); + let size = surface.buffer_abs_pos.get().size(); if let Some(children) = children.deref() { macro_rules! render { ($children:expr) => { @@ -300,10 +316,10 @@ impl Renderer<'_> { }; } render!(&children.below); - self.render_buffer(&buffer, x, y); + self.render_buffer(&buffer, x, y, &tpoints, size); render!(&children.above); } else { - self.render_buffer(&buffer, x, y); + self.render_buffer(&buffer, x, y, &tpoints, size); } if self.on_output { { @@ -317,13 +333,28 @@ impl Renderer<'_> { } } - pub fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) { + pub fn render_buffer( + &mut self, + buffer: &WlBuffer, + x: i32, + y: i32, + tpoints: &[f32; 8], + tsize: (i32, i32), + ) { if let Some(tex) = buffer.texture.get() { - self.render_texture(&tex, x, y, buffer.format); + self.render_texture(&tex, x, y, buffer.format, Some(tpoints), Some(tsize)); } } - pub fn render_texture(&mut self, texture: &Texture, x: i32, y: i32, format: &Format) { + pub fn render_texture( + &mut self, + texture: &Texture, + x: i32, + y: i32, + format: &Format, + tpoints: Option<&[f32; 8]>, + tsize: Option<(i32, i32)>, + ) { assert!(rc_eq(&self.ctx.ctx, &texture.ctx.ctx)); unsafe { glActiveTexture(GL_TEXTURE0); @@ -346,15 +377,26 @@ impl Renderer<'_> { glUniform1i(prog.tex, 0); - let texcoord: [f32; 8] = [1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]; + static DEFAULT_TEXCOORD: [f32; 8] = [1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]; + + let texcoord: &[f32; 8] = match tpoints { + None => &DEFAULT_TEXCOORD, + Some(tp) => tp, + }; let f_width = self.fb.width as f32; let f_height = self.fb.height as f32; + let (twidth, theight) = if let Some(size) = tsize { + size + } else { + (texture.gl.width, texture.gl.height) + }; + let x1 = 2.0 * (x as f32 / f_width) - 1.0; let y1 = 2.0 * (y as f32 / f_height) - 1.0; - let x2 = 2.0 * ((x + texture.gl.width) as f32 / f_width) - 1.0; - let y2 = 2.0 * ((y + texture.gl.height) as f32 / f_height) - 1.0; + let x2 = 2.0 * ((x + twidth) as f32 / f_width) - 1.0; + let y2 = 2.0 * ((y + theight) as f32 / f_height) - 1.0; let pos: [f32; 8] = [ x2, y1, // top right @@ -413,7 +455,7 @@ impl Renderer<'_> { [Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()]; self.fill_boxes(&title_underline, &uc); if let Some(title) = floating.title_texture.get() { - self.render_texture(&title, x + bw, y + bw, ARGB8888); + self.render_texture(&title, x + bw, y + bw, ARGB8888, None, None); } let body = Rect::new_sized( x + bw, diff --git a/wire/wp_viewport.txt b/wire/wp_viewport.txt new file mode 100644 index 00000000..3c90fd41 --- /dev/null +++ b/wire/wp_viewport.txt @@ -0,0 +1,16 @@ +# requests + +msg destroy = 0 { +} + +msg set_source = 1 { + x: fixed, + y: fixed, + width: fixed, + height: fixed, +} + +msg set_destination = 2 { + width: i32, + height: i32, +} diff --git a/wire/wp_viewporter.txt b/wire/wp_viewporter.txt new file mode 100644 index 00000000..6f9012a1 --- /dev/null +++ b/wire/wp_viewporter.txt @@ -0,0 +1,9 @@ +# requests + +msg destroy = 0 { +} + +msg get_viewport = 1 { + id: id(wp_viewport), + surface: id(wl_surface), +}