From cab52fbeefe4e713855222096e047dfda5856198 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 4 Nov 2022 16:57:42 +0100 Subject: [PATCH] screencopy: Capture cursor for window capture --- Cargo.lock | 2 +- src/shell/element/mod.rs | 54 ++++++++++++++++++++++- src/wayland/handlers/screencopy.rs | 70 ++++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc6d9a43..3336870d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,7 +318,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#aee21196b19591e83c04c803bfa1a865fda46337" +source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#a3e0aa740a3e0f8f7b486fef0d62fa09a1dfa328" dependencies = [ "bitflags", "wayland-backend", diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 021a85a9..590b3046 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,4 +1,4 @@ -use crate::state::State; +use crate::{state::State, utils::prelude::SeatExt}; use id_tree::NodeId; use smithay::{ backend::{ @@ -25,6 +25,7 @@ use smithay::{ }, }; use std::{ + collections::HashMap, hash::Hash, sync::{Arc, Mutex}, }; @@ -48,6 +49,7 @@ pub struct CosmicMapped { element: CosmicMappedInternal, // associated data + last_cursor_position: Arc>>>, //tiling pub(super) tiling_node_id: Arc>>, @@ -108,6 +110,45 @@ impl CosmicMapped { } } + pub fn active_window_offset(&self) -> Rectangle { + match &self.element { + CosmicMappedInternal::Stack(stack) => { + let location = ( + 0, + stack + .header + .lock() + .unwrap() + .as_ref() + .map_or(0, |header| header.height()), + ); + let size = stack.active().geometry().size; + Rectangle::from_loc_and_size(location, size) + } + CosmicMappedInternal::Window(win) => { + let location = ( + 0, + win.header + .lock() + .unwrap() + .as_ref() + .map_or(0, |header| header.height()), + ); + let size = win.window.geometry().size; + Rectangle::from_loc_and_size(location, size) + } + _ => unreachable!(), + } + } + + pub fn cursor_position(&self, seat: &Seat) -> Option> { + self.last_cursor_position + .lock() + .unwrap() + .get(&seat.id()) + .cloned() + } + pub fn set_active(&self, window: &Window) { if let CosmicMappedInternal::Stack(stack) = &self.element { stack.set_active(window); @@ -565,6 +606,10 @@ impl KeyboardTarget for CosmicMapped { impl PointerTarget for CosmicMapped { fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + self.last_cursor_position + .lock() + .unwrap() + .insert(seat.id(), event.location); match &self.element { CosmicMappedInternal::Stack(s) => PointerTarget::enter(s, seat, data, event), CosmicMappedInternal::Window(w) => PointerTarget::enter(w, seat, data, event), @@ -572,6 +617,10 @@ impl PointerTarget for CosmicMapped { } } fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + self.last_cursor_position + .lock() + .unwrap() + .insert(seat.id(), event.location); match &self.element { CosmicMappedInternal::Stack(s) => PointerTarget::motion(s, seat, data, event), CosmicMappedInternal::Window(w) => PointerTarget::motion(w, seat, data, event), @@ -593,6 +642,7 @@ impl PointerTarget for CosmicMapped { } } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + self.last_cursor_position.lock().unwrap().remove(&seat.id()); match &self.element { CosmicMappedInternal::Stack(s) => PointerTarget::leave(s, seat, data, serial, time), CosmicMappedInternal::Window(w) => PointerTarget::leave(w, seat, data, serial, time), @@ -623,6 +673,7 @@ impl From for CosmicMapped { fn from(w: CosmicWindow) -> Self { CosmicMapped { element: CosmicMappedInternal::Window(w), + last_cursor_position: Arc::new(Mutex::new(HashMap::new())), tiling_node_id: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), @@ -634,6 +685,7 @@ impl From for CosmicMapped { fn from(s: CosmicStack) -> Self { CosmicMapped { element: CosmicMappedInternal::Stack(s), + last_cursor_position: Arc::new(Mutex::new(HashMap::new())), tiling_node_id: Arc::new(Mutex::new(None)), last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index ddaa7cdd..fbc3ae65 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -20,7 +20,7 @@ use smithay::{ surface::WaylandSurfaceRenderElement, AsRenderElements, RenderElementStates, }, gles2::{Gles2Renderbuffer, Gles2Renderer}, - Bind, BufferType, ExportMem, Offscreen, Renderer, + Bind, BufferType, ExportMem, ImportAll, Offscreen, Renderer, }, }, desktop::Window, @@ -37,7 +37,7 @@ use smithay::{ }; use crate::{ - backend::render::{render_output, render_workspace, CursorMode, CLEAR_COLOR}, + backend::render::{cursor, render_output, render_workspace, CursorMode, CLEAR_COLOR}, state::{BackendData, ClientState, Common, State}, utils::prelude::OutputExt, wayland::protocols::{ @@ -49,6 +49,8 @@ use crate::{ }, }; +use super::data_device::get_dnd_icon; + pub type PendingScreencopyBuffers = RefCell>; #[derive(Debug, Default)] @@ -670,6 +672,12 @@ pub fn render_workspace_to_buffer( .map_err(|err| (FailureReason::Unspec, err.into())) } +smithay::render_elements! { + pub WindowCaptureElement where R: ImportAll; + WaylandElement=WaylandSurfaceRenderElement, + CursorElement=cursor::CursorRenderElement, +} + pub fn render_window_to_buffer( state: &mut State, session: &Session, @@ -706,12 +714,58 @@ pub fn render_window_to_buffer( Transform::Normal, |_node, renderer, dtr, age| { // TODO cursor elements! - let elements = - AsRenderElements::::render_elements::( - window, - (-geometry.loc.x, -geometry.loc.y).into(), - Scale::from(1.0), - ); + let mut elements = AsRenderElements::::render_elements::< + WindowCaptureElement, + >( + window, + (-geometry.loc.x, -geometry.loc.y).into(), + Scale::from(1.0), + ); + + for seat in state.common.seats() { + if let Some(location) = { + // we need to find the mapped element in that case + if let Some(mapped) = state + .common + .shell + .element_for_surface(window.toplevel().wl_surface()) + { + mapped.cursor_position(seat).and_then(|mut p| { + p -= mapped.active_window_offset().loc.to_f64(); + if p.x < 0. || p.y < 0. { + None + } else { + Some(p) + } + }) + } else { + None + } + } { + if session.cursor_mode() == ScreencopyCursorMode::Embedded { + elements.extend( + cursor::draw_cursor( + renderer, + seat, + location, + 1.0.into(), + &state.common.start_time, + true, + ) + .into_iter() + .map(WindowCaptureElement::from), + ); + } + + if let Some(wl_surface) = get_dnd_icon(seat) { + elements.extend( + cursor::draw_dnd_icon(&wl_surface, location.to_i32_round(), 1.0) + .into_iter() + .map(WindowCaptureElement::from), + ); + } + } + } dtr.render_output(renderer, age, &elements, CLEAR_COLOR, None) },