screencopy: Capture cursor for window capture
This commit is contained in:
parent
bcf3e43fcc
commit
cab52fbeef
3 changed files with 116 additions and 10 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -318,7 +318,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-protocols"
|
name = "cosmic-protocols"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::state::State;
|
use crate::{state::State, utils::prelude::SeatExt};
|
||||||
use id_tree::NodeId;
|
use id_tree::NodeId;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
|
@ -25,6 +25,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
@ -48,6 +49,7 @@ pub struct CosmicMapped {
|
||||||
element: CosmicMappedInternal,
|
element: CosmicMappedInternal,
|
||||||
|
|
||||||
// associated data
|
// associated data
|
||||||
|
last_cursor_position: Arc<Mutex<HashMap<usize, Point<f64, Logical>>>>,
|
||||||
|
|
||||||
//tiling
|
//tiling
|
||||||
pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>,
|
pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>,
|
||||||
|
|
@ -108,6 +110,45 @@ impl CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn active_window_offset(&self) -> Rectangle<i32, Logical> {
|
||||||
|
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<State>) -> Option<Point<f64, Logical>> {
|
||||||
|
self.last_cursor_position
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(&seat.id())
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_active(&self, window: &Window) {
|
pub fn set_active(&self, window: &Window) {
|
||||||
if let CosmicMappedInternal::Stack(stack) = &self.element {
|
if let CosmicMappedInternal::Stack(stack) = &self.element {
|
||||||
stack.set_active(window);
|
stack.set_active(window);
|
||||||
|
|
@ -565,6 +606,10 @@ impl KeyboardTarget<State> for CosmicMapped {
|
||||||
|
|
||||||
impl PointerTarget<State> for CosmicMapped {
|
impl PointerTarget<State> for CosmicMapped {
|
||||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||||
|
self.last_cursor_position
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(seat.id(), event.location);
|
||||||
match &self.element {
|
match &self.element {
|
||||||
CosmicMappedInternal::Stack(s) => PointerTarget::enter(s, seat, data, event),
|
CosmicMappedInternal::Stack(s) => PointerTarget::enter(s, seat, data, event),
|
||||||
CosmicMappedInternal::Window(w) => PointerTarget::enter(w, seat, data, event),
|
CosmicMappedInternal::Window(w) => PointerTarget::enter(w, seat, data, event),
|
||||||
|
|
@ -572,6 +617,10 @@ impl PointerTarget<State> for CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||||
|
self.last_cursor_position
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(seat.id(), event.location);
|
||||||
match &self.element {
|
match &self.element {
|
||||||
CosmicMappedInternal::Stack(s) => PointerTarget::motion(s, seat, data, event),
|
CosmicMappedInternal::Stack(s) => PointerTarget::motion(s, seat, data, event),
|
||||||
CosmicMappedInternal::Window(w) => PointerTarget::motion(w, seat, data, event),
|
CosmicMappedInternal::Window(w) => PointerTarget::motion(w, seat, data, event),
|
||||||
|
|
@ -593,6 +642,7 @@ impl PointerTarget<State> for CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||||
|
self.last_cursor_position.lock().unwrap().remove(&seat.id());
|
||||||
match &self.element {
|
match &self.element {
|
||||||
CosmicMappedInternal::Stack(s) => PointerTarget::leave(s, seat, data, serial, time),
|
CosmicMappedInternal::Stack(s) => PointerTarget::leave(s, seat, data, serial, time),
|
||||||
CosmicMappedInternal::Window(w) => PointerTarget::leave(w, seat, data, serial, time),
|
CosmicMappedInternal::Window(w) => PointerTarget::leave(w, seat, data, serial, time),
|
||||||
|
|
@ -623,6 +673,7 @@ impl From<CosmicWindow> for CosmicMapped {
|
||||||
fn from(w: CosmicWindow) -> Self {
|
fn from(w: CosmicWindow) -> Self {
|
||||||
CosmicMapped {
|
CosmicMapped {
|
||||||
element: CosmicMappedInternal::Window(w),
|
element: CosmicMappedInternal::Window(w),
|
||||||
|
last_cursor_position: Arc::new(Mutex::new(HashMap::new())),
|
||||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||||
last_geometry: Arc::new(Mutex::new(None)),
|
last_geometry: Arc::new(Mutex::new(None)),
|
||||||
resize_state: Arc::new(Mutex::new(None)),
|
resize_state: Arc::new(Mutex::new(None)),
|
||||||
|
|
@ -634,6 +685,7 @@ impl From<CosmicStack> for CosmicMapped {
|
||||||
fn from(s: CosmicStack) -> Self {
|
fn from(s: CosmicStack) -> Self {
|
||||||
CosmicMapped {
|
CosmicMapped {
|
||||||
element: CosmicMappedInternal::Stack(s),
|
element: CosmicMappedInternal::Stack(s),
|
||||||
|
last_cursor_position: Arc::new(Mutex::new(HashMap::new())),
|
||||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||||
last_geometry: Arc::new(Mutex::new(None)),
|
last_geometry: Arc::new(Mutex::new(None)),
|
||||||
resize_state: Arc::new(Mutex::new(None)),
|
resize_state: Arc::new(Mutex::new(None)),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ use smithay::{
|
||||||
surface::WaylandSurfaceRenderElement, AsRenderElements, RenderElementStates,
|
surface::WaylandSurfaceRenderElement, AsRenderElements, RenderElementStates,
|
||||||
},
|
},
|
||||||
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
||||||
Bind, BufferType, ExportMem, Offscreen, Renderer,
|
Bind, BufferType, ExportMem, ImportAll, Offscreen, Renderer,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::Window,
|
desktop::Window,
|
||||||
|
|
@ -37,7 +37,7 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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},
|
state::{BackendData, ClientState, Common, State},
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
wayland::protocols::{
|
wayland::protocols::{
|
||||||
|
|
@ -49,6 +49,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::data_device::get_dnd_icon;
|
||||||
|
|
||||||
pub type PendingScreencopyBuffers = RefCell<Vec<(Session, BufferParams)>>;
|
pub type PendingScreencopyBuffers = RefCell<Vec<(Session, BufferParams)>>;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|
@ -670,6 +672,12 @@ pub fn render_workspace_to_buffer(
|
||||||
.map_err(|err| (FailureReason::Unspec, err.into()))
|
.map_err(|err| (FailureReason::Unspec, err.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smithay::render_elements! {
|
||||||
|
pub WindowCaptureElement<R> where R: ImportAll;
|
||||||
|
WaylandElement=WaylandSurfaceRenderElement,
|
||||||
|
CursorElement=cursor::CursorRenderElement<R>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_window_to_buffer(
|
pub fn render_window_to_buffer(
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
|
|
@ -706,12 +714,58 @@ pub fn render_window_to_buffer(
|
||||||
Transform::Normal,
|
Transform::Normal,
|
||||||
|_node, renderer, dtr, age| {
|
|_node, renderer, dtr, age| {
|
||||||
// TODO cursor elements!
|
// TODO cursor elements!
|
||||||
let elements =
|
let mut elements = AsRenderElements::<Gles2Renderer>::render_elements::<
|
||||||
AsRenderElements::<Gles2Renderer>::render_elements::<WaylandSurfaceRenderElement>(
|
WindowCaptureElement<Gles2Renderer>,
|
||||||
window,
|
>(
|
||||||
(-geometry.loc.x, -geometry.loc.y).into(),
|
window,
|
||||||
Scale::from(1.0),
|
(-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)
|
dtr.render_output(renderer, age, &elements, CLEAR_COLOR, None)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue