cosmic-screencopy-v2
This commit is contained in:
parent
973cfed87b
commit
b40d153809
27 changed files with 3647 additions and 2743 deletions
|
|
@ -1,19 +1,23 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
backend::render::{workspace_elements, CLEAR_COLOR},
|
||||
backend::render::{
|
||||
element::{CosmicElement, DamageElement},
|
||||
workspace_elements, CLEAR_COLOR,
|
||||
},
|
||||
config::OutputConfig,
|
||||
shell::Shell,
|
||||
state::{BackendData, Common, Fps, SurfaceDmabufFeedback},
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::screencopy::{render_session, UserdataExt},
|
||||
protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
handlers::screencopy::{submit_buffer, FrameHolder, SessionData},
|
||||
protocols::screencopy::{
|
||||
FailureReason, Frame as ScreencopyFrame, Session as ScreencopySession,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
|
||||
use libc::dev_t;
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
|
@ -31,8 +35,8 @@ use smithay::{
|
|||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||
renderer::{
|
||||
buffer_dimensions,
|
||||
damage::{Error as RenderError, RenderOutputResult},
|
||||
element::Element,
|
||||
damage::Error as RenderError,
|
||||
element::{Element, RenderElementStates},
|
||||
gles::GlesRenderbuffer,
|
||||
glow::GlowRenderer,
|
||||
multigpu::{gbm::GbmGlesBackend, Error as MultiError, GpuManager},
|
||||
|
|
@ -66,7 +70,7 @@ use smithay::{
|
|||
Client, DisplayHandle, Weak,
|
||||
},
|
||||
},
|
||||
utils::{DeviceFd, Size, Transform},
|
||||
utils::{Buffer as BufferCoords, DeviceFd, Physical, Rectangle, Size, Transform},
|
||||
wayland::{
|
||||
dmabuf::{get_dmabuf, DmabufFeedbackBuilder, DmabufGlobal},
|
||||
drm_lease::{DrmLease, DrmLeaseState},
|
||||
|
|
@ -82,6 +86,7 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
fmt,
|
||||
path::PathBuf,
|
||||
sync::mpsc::Receiver,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
|
|
@ -93,6 +98,14 @@ use super::render::{element::AsGlowRenderer, init_shaders, CursorMode, GlMultiRe
|
|||
// for now we assume we need at least 3ms
|
||||
const MIN_DISPLAY_TIME: Duration = Duration::from_millis(3);
|
||||
|
||||
/*
|
||||
TODO Screencopy:
|
||||
- Fixup blit, include additional damage
|
||||
- Send accurate presentation time
|
||||
- Handle early exit no damage case
|
||||
- input...
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsState {
|
||||
pub devices: HashMap<DrmNode, Device>,
|
||||
|
|
@ -154,7 +167,10 @@ pub struct Surface {
|
|||
pub type GbmDrmCompositor = DrmCompositor<
|
||||
GbmAllocator<DrmDeviceFd>,
|
||||
GbmDevice<DrmDeviceFd>,
|
||||
Option<OutputPresentationFeedback>,
|
||||
Option<(
|
||||
OutputPresentationFeedback,
|
||||
Receiver<(ScreencopyFrame, Vec<Rectangle<i32, BufferCoords>>)>,
|
||||
)>,
|
||||
DrmDeviceFd,
|
||||
>;
|
||||
|
||||
|
|
@ -192,7 +208,6 @@ pub fn init_backend(
|
|||
&state.common.event_loop_handle,
|
||||
output,
|
||||
None,
|
||||
None,
|
||||
) {
|
||||
error!(
|
||||
?err,
|
||||
|
|
@ -328,16 +343,10 @@ pub fn init_backend(
|
|||
surface.pending = false;
|
||||
}
|
||||
for output in state.common.shell.outputs() {
|
||||
let sessions = output.pending_buffers().collect::<Vec<_>>();
|
||||
if let Err(err) = state.backend.kms().schedule_render(
|
||||
&state.common.event_loop_handle,
|
||||
output,
|
||||
None,
|
||||
if !sessions.is_empty() {
|
||||
Some(sessions)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
) {
|
||||
error!(
|
||||
?err,
|
||||
|
|
@ -472,7 +481,7 @@ impl State {
|
|||
|
||||
match surface.surface.as_mut().map(|x| x.frame_submitted()) {
|
||||
Some(Ok(feedback)) => {
|
||||
if let Some(mut feedback) = feedback.flatten() {
|
||||
if let Some((mut feedback, frames)) = feedback.flatten() {
|
||||
let submit_time =
|
||||
match metadata.take().map(|data| data.time) {
|
||||
Some(DrmEventTime::Monotonic(tp)) => Some(tp),
|
||||
|
|
@ -511,6 +520,14 @@ impl State {
|
|||
seq as u64,
|
||||
flags,
|
||||
);
|
||||
|
||||
while let Ok((frame, damage)) = frames.recv() {
|
||||
frame.success(
|
||||
surface.output.current_transform(),
|
||||
damage,
|
||||
clock,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
surface.pending = false;
|
||||
|
|
@ -533,21 +550,11 @@ impl State {
|
|||
};
|
||||
|
||||
if let Some((output, avg_time)) = rescheduled {
|
||||
let mut scheduled_sessions =
|
||||
state.workspace_session_for_output(&output);
|
||||
let mut output_sessions = output.pending_buffers().peekable();
|
||||
if output_sessions.peek().is_some() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(output_sessions);
|
||||
}
|
||||
|
||||
let _estimated_rendertime = std::cmp::max(avg_time, MIN_DISPLAY_TIME);
|
||||
if let Err(err) = state.backend.kms().schedule_render(
|
||||
&state.common.event_loop_handle,
|
||||
&output,
|
||||
None, //Some(estimated_rendertime),
|
||||
scheduled_sessions,
|
||||
) {
|
||||
warn!(?err, "Failed to schedule render.");
|
||||
}
|
||||
|
|
@ -1160,7 +1167,6 @@ impl Surface {
|
|||
render_node: Option<&DrmNode>,
|
||||
target_node: &DrmNode,
|
||||
state: &mut Common,
|
||||
screencopy: Option<&[(ScreencopySession, BufferParams)]>,
|
||||
) -> Result<()> {
|
||||
if self.surface.is_none() {
|
||||
return Ok(());
|
||||
|
|
@ -1192,7 +1198,7 @@ impl Surface {
|
|||
.map(|((w, start), idx)| (w.handle, idx, start));
|
||||
let workspace = (workspace.handle, idx);
|
||||
|
||||
let elements = workspace_elements(
|
||||
let mut elements = workspace_elements(
|
||||
Some(&render_node),
|
||||
&mut renderer,
|
||||
state,
|
||||
|
|
@ -1208,6 +1214,65 @@ impl Surface {
|
|||
})?;
|
||||
self.fps.elements();
|
||||
|
||||
let frames: Vec<(
|
||||
ScreencopySession,
|
||||
ScreencopyFrame,
|
||||
Result<(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates), OutputNoMode>,
|
||||
)> = self
|
||||
.output
|
||||
.take_pending_frames()
|
||||
.into_iter()
|
||||
.map(|(session, frame)| {
|
||||
let additional_damage = frame.damage();
|
||||
let session_data = session.user_data().get::<SessionData>().unwrap();
|
||||
let mut damage_tracking = session_data.borrow_mut();
|
||||
|
||||
let old_len = if !additional_damage.is_empty() {
|
||||
let area = self
|
||||
.output
|
||||
.current_mode()
|
||||
.unwrap()
|
||||
/* TODO: Mode is Buffer..., why is this Physical in the first place */
|
||||
.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64();
|
||||
|
||||
let old_len = elements.len();
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
rect.to_f64()
|
||||
.to_logical(
|
||||
self.output.current_scale().fractional_scale(),
|
||||
self.output.current_transform(),
|
||||
&area,
|
||||
)
|
||||
.to_i32_round()
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::into),
|
||||
);
|
||||
|
||||
Some(old_len)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let buffer = frame.buffer();
|
||||
let age = damage_tracking.age_for_buffer(&buffer);
|
||||
let res = damage_tracking.dt.damage_output(age, &elements);
|
||||
|
||||
if let Some(old_len) = old_len {
|
||||
elements.truncate(old_len);
|
||||
}
|
||||
|
||||
std::mem::drop(damage_tracking);
|
||||
(session, frame, res)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let res = compositor.render_frame(
|
||||
&mut renderer,
|
||||
&elements,
|
||||
|
|
@ -1217,8 +1282,13 @@ impl Surface {
|
|||
|
||||
match res {
|
||||
Ok(frame_result) => {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
let feedback = if !frame_result.is_empty {
|
||||
Some(state.take_presentation_feedback(&self.output, &frame_result.states))
|
||||
Some((
|
||||
state.take_presentation_feedback(&self.output, &frame_result.states),
|
||||
rx,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
@ -1228,93 +1298,152 @@ impl Surface {
|
|||
elem.sync.wait();
|
||||
}
|
||||
}
|
||||
|
||||
match compositor.queue_frame(feedback) {
|
||||
Ok(()) => {
|
||||
self.pending = true;
|
||||
}
|
||||
Err(FrameError::EmptyFrame) => {
|
||||
tracing::debug!("Stopped rendering");
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err).with_context(|| "Failed to submit result for display")
|
||||
}
|
||||
};
|
||||
x @ Ok(()) | x @ Err(FrameError::EmptyFrame) => {
|
||||
for (session, frame, res) in frames {
|
||||
let damage = match res {
|
||||
Ok((damage, _)) => damage,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to screencopy");
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.reset();
|
||||
frame.fail(FailureReason::Unknown);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(screencopy) = screencopy {
|
||||
for (session, params) in screencopy {
|
||||
match render_session(
|
||||
Some(*render_node),
|
||||
&mut renderer,
|
||||
&session,
|
||||
params,
|
||||
self.output.current_transform(),
|
||||
|_node, buffer, renderer, dt, age| {
|
||||
let res = dt.damage_output(age, &elements)?;
|
||||
let mut sync = SyncPoint::default();
|
||||
|
||||
let mut sync = SyncPoint::default();
|
||||
if let (Some(ref damage), _) = &res {
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).ok_or(
|
||||
RenderError::Rendering(MultiError::ImportFailed),
|
||||
)?;
|
||||
let format =
|
||||
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| OutputNoMode)? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(
|
||||
renderer, format, size,
|
||||
)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
renderer
|
||||
.bind(render_buffer)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
|
||||
let (output_size, output_scale, output_transform) = (
|
||||
self.output.current_mode().ok_or(OutputNoMode)?.size,
|
||||
self.output.current_scale().fractional_scale(),
|
||||
self.output.current_transform(),
|
||||
);
|
||||
sync = frame_result
|
||||
.blit_frame_result(
|
||||
output_size,
|
||||
output_transform,
|
||||
output_scale,
|
||||
renderer,
|
||||
damage.iter().copied(),
|
||||
// TODO: Filter cursor element
|
||||
elements.iter().map(|e| e.id().clone()),
|
||||
if let Some(ref damage) = damage {
|
||||
let buffer = frame.buffer();
|
||||
if let Ok(dmabuf) = get_dmabuf(&buffer) {
|
||||
renderer
|
||||
.bind(dmabuf)
|
||||
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(&buffer).ok_or(RenderError::<
|
||||
GlMultiRenderer,
|
||||
>::Rendering(
|
||||
MultiError::ImportFailed,
|
||||
))?;
|
||||
let format =
|
||||
with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| OutputNoMode)? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(
|
||||
&mut renderer,
|
||||
format,
|
||||
size,
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
BlitFrameResultError::Rendering(err) => {
|
||||
RenderError::Rendering(err)
|
||||
}
|
||||
BlitFrameResultError::Export(_) => {
|
||||
RenderError::Rendering(MultiError::DeviceMissing)
|
||||
}
|
||||
})?;
|
||||
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
|
||||
renderer
|
||||
.bind(render_buffer)
|
||||
.map_err(RenderError::<GlMultiRenderer>::Rendering)?;
|
||||
}
|
||||
|
||||
Ok(RenderOutputResult {
|
||||
damage: res.0,
|
||||
states: res.1,
|
||||
sync,
|
||||
})
|
||||
},
|
||||
) {
|
||||
Ok(true) => {} // success
|
||||
Ok(false) => state.still_pending(session.clone(), params.clone()),
|
||||
Err(err) => {
|
||||
warn!(?err, "Error rendering to screencopy session.");
|
||||
session.failed(FailureReason::Unspec);
|
||||
let (output_size, output_scale, output_transform) = (
|
||||
self.output.current_mode().ok_or(OutputNoMode)?.size,
|
||||
self.output.current_scale().fractional_scale(),
|
||||
self.output.current_transform(),
|
||||
);
|
||||
|
||||
let filter = (!session.draw_cursor())
|
||||
.then(|| {
|
||||
elements.iter().filter_map(|elem| {
|
||||
if let CosmicElement::Cursor(_) = elem {
|
||||
Some(elem.id().clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
match frame_result
|
||||
.blit_frame_result(
|
||||
output_size,
|
||||
output_transform,
|
||||
output_scale,
|
||||
&mut renderer,
|
||||
damage.iter().copied(),
|
||||
filter,
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
BlitFrameResultError::Rendering(err) => {
|
||||
RenderError::<GlMultiRenderer>::Rendering(err)
|
||||
}
|
||||
BlitFrameResultError::Export(_) => {
|
||||
RenderError::<GlMultiRenderer>::Rendering(
|
||||
MultiError::DeviceMissing,
|
||||
)
|
||||
}
|
||||
}) {
|
||||
Ok(new_sync) => {
|
||||
sync = new_sync;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to screencopy");
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.reset();
|
||||
frame.fail(FailureReason::Unknown);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let transform = self.output.current_transform();
|
||||
|
||||
match submit_buffer(frame, &mut renderer, transform, damage, sync) {
|
||||
Ok(Some((frame, damage))) => {
|
||||
if frame_result.is_empty {
|
||||
frame.success(transform, damage, state.clock.now());
|
||||
} else {
|
||||
let _ = tx.send((frame, damage));
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.reset();
|
||||
tracing::warn!(?err, "Failed to screencopy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if x.is_ok() {
|
||||
self.pending = true;
|
||||
} else {
|
||||
tracing::debug!("Stopped rendering");
|
||||
}
|
||||
}
|
||||
self.fps.screencopy();
|
||||
}
|
||||
Err(err) => {
|
||||
for (session, frame, _) in frames {
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.reset();
|
||||
frame.fail(FailureReason::Unknown);
|
||||
}
|
||||
return Err(err).with_context(|| "Failed to submit result for display");
|
||||
}
|
||||
};
|
||||
|
||||
state.send_frames(&self.output, &frame_result.states, |source_node| {
|
||||
Some(
|
||||
|
|
@ -1494,17 +1623,7 @@ impl KmsState {
|
|||
};
|
||||
|
||||
if recreated {
|
||||
let sessions = output.pending_buffers().collect::<Vec<_>>();
|
||||
if let Err(err) = self.schedule_render(
|
||||
loop_handle,
|
||||
output,
|
||||
None,
|
||||
if !sessions.is_empty() {
|
||||
Some(sessions)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
) {
|
||||
if let Err(err) = self.schedule_render(loop_handle, output, None) {
|
||||
error!(
|
||||
?err,
|
||||
"Error scheduling event loop for output {}.",
|
||||
|
|
@ -1592,7 +1711,6 @@ impl KmsState {
|
|||
loop_handle: &LoopHandle<'_, State>,
|
||||
output: &Output,
|
||||
estimated_rendertime: Option<Duration>,
|
||||
mut screencopy_sessions: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) -> Result<(), InsertError<Timer>> {
|
||||
if let Some((device, crtc, surface)) = self
|
||||
.devices
|
||||
|
|
@ -1601,13 +1719,6 @@ impl KmsState {
|
|||
.find(|(_, _, s)| s.output == *output)
|
||||
{
|
||||
if surface.surface.is_none() {
|
||||
if let Some(sessions) = screencopy_sessions {
|
||||
loop_handle.insert_idle(move |state| {
|
||||
for (session, params) in sessions.into_iter() {
|
||||
state.common.still_pending(session, params);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if !surface.scheduled && !surface.pending {
|
||||
|
|
@ -1655,16 +1766,9 @@ impl KmsState {
|
|||
Some(&render_device.render_node),
|
||||
&target_node,
|
||||
common,
|
||||
screencopy_sessions.as_deref(),
|
||||
)
|
||||
} else {
|
||||
surface.render_output(
|
||||
&mut backend.api,
|
||||
None,
|
||||
&target_node,
|
||||
common,
|
||||
screencopy_sessions.as_deref(),
|
||||
)
|
||||
surface.render_output(&mut backend.api, None, &target_node, common)
|
||||
};
|
||||
|
||||
profiling::finish_frame!();
|
||||
|
|
@ -1687,24 +1791,11 @@ impl KmsState {
|
|||
};
|
||||
}
|
||||
|
||||
if let Some(sessions) = screencopy_sessions.as_mut() {
|
||||
for (session, params) in sessions.drain(..) {
|
||||
state.common.still_pending(session, params);
|
||||
}
|
||||
}
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
)?);
|
||||
trace!(?surface.render_timer_token, ?crtc, "Frame scheduled");
|
||||
surface.scheduled = true;
|
||||
} else {
|
||||
if let Some(sessions) = screencopy_sessions {
|
||||
loop_handle.insert_idle(|state| {
|
||||
for (session, params) in sessions.into_iter() {
|
||||
state.common.still_pending(session, params);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ use smithay::{
|
|||
},
|
||||
reexports::wayland_server::protocol::wl_surface,
|
||||
render_elements,
|
||||
utils::{IsAlive, Logical, Monotonic, Point, Scale, Time, Transform},
|
||||
utils::{
|
||||
Buffer as BufferCoords, IsAlive, Logical, Monotonic, Point, Scale, Size, Time, Transform,
|
||||
},
|
||||
wayland::compositor::{get_role, with_states},
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashMap, io::Read, sync::Mutex, time::Duration};
|
||||
|
|
@ -156,12 +158,12 @@ pub fn draw_surface_cursor<R>(
|
|||
surface: &wl_surface::WlSurface,
|
||||
location: impl Into<Point<i32, Logical>>,
|
||||
scale: impl Into<Scale<f64>>,
|
||||
) -> Vec<CursorRenderElement<R>>
|
||||
) -> Vec<(CursorRenderElement<R>, Point<i32, BufferCoords>)>
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
let mut position = location.into();
|
||||
let position = location.into();
|
||||
let scale = scale.into();
|
||||
let h = with_states(&surface, |states| {
|
||||
states
|
||||
|
|
@ -171,8 +173,12 @@ where
|
|||
.lock()
|
||||
.unwrap()
|
||||
.hotspot
|
||||
.to_buffer(
|
||||
1,
|
||||
Transform::Normal,
|
||||
&Size::from((1, 1)), /* Size doesn't matter for Transform::Normal */
|
||||
)
|
||||
});
|
||||
position -= h;
|
||||
|
||||
render_elements_from_surface_tree(
|
||||
renderer,
|
||||
|
|
@ -182,6 +188,9 @@ where
|
|||
1.0,
|
||||
Kind::Cursor,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|elem| (elem, h))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[profiling::function]
|
||||
|
|
@ -190,7 +199,7 @@ pub fn draw_dnd_icon<R>(
|
|||
surface: &wl_surface::WlSurface,
|
||||
location: impl Into<Point<i32, Logical>>,
|
||||
scale: impl Into<Scale<f64>>,
|
||||
) -> Vec<CursorRenderElement<R>>
|
||||
) -> Vec<WaylandSurfaceRenderElement<R>>
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
|
|
@ -307,7 +316,7 @@ pub fn draw_cursor<R>(
|
|||
scale: Scale<f64>,
|
||||
time: Time<Monotonic>,
|
||||
draw_default: bool,
|
||||
) -> Vec<CursorRenderElement<R>>
|
||||
) -> Vec<(CursorRenderElement<R>, Point<i32, BufferCoords>)>
|
||||
where
|
||||
R: Renderer + ImportMem + ImportAll,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
|
|
@ -367,20 +376,23 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
let hotspot = Point::<i32, Logical>::from((frame.xhot as i32, frame.yhot as i32)).to_f64();
|
||||
let hotspot = Point::<i32, BufferCoords>::from((frame.xhot as i32, frame.yhot as i32));
|
||||
*state.current_image.borrow_mut() = Some(frame);
|
||||
|
||||
return vec![CursorRenderElement::Static(
|
||||
MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
(location - hotspot).to_physical(scale),
|
||||
pointer_image,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Kind::Cursor,
|
||||
)
|
||||
.expect("Failed to import cursor bitmap"),
|
||||
return vec![(
|
||||
CursorRenderElement::Static(
|
||||
MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
location.to_physical(scale),
|
||||
pointer_image,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Kind::Cursor,
|
||||
)
|
||||
.expect("Failed to import cursor bitmap"),
|
||||
),
|
||||
hotspot,
|
||||
)];
|
||||
} else {
|
||||
Vec::new()
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@ use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement};
|
|||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{
|
||||
surface::WaylandSurfaceRenderElement,
|
||||
utils::{Relocate, RelocateRenderElement},
|
||||
Element, RenderElement, UnderlyingStorage,
|
||||
Element, Id, RenderElement, UnderlyingStorage,
|
||||
},
|
||||
glow::{GlowFrame, GlowRenderer},
|
||||
utils::CommitCounter,
|
||||
Frame, ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
utils::{Buffer as BufferCoords, Physical, Point, Rectangle, Scale},
|
||||
utils::{Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale},
|
||||
};
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -24,8 +26,10 @@ where
|
|||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
Workspace(RelocateRenderElement<WorkspaceRenderElement<R>>),
|
||||
Cursor(CursorRenderElement<R>),
|
||||
Cursor(RelocateRenderElement<CursorRenderElement<R>>),
|
||||
Dnd(WaylandSurfaceRenderElement<R>),
|
||||
MoveGrab(CosmicMappedRenderElement<R>),
|
||||
AdditionalDamage(DamageElement),
|
||||
#[cfg(feature = "debug")]
|
||||
Egui(TextureRenderElement<GlesTexture>),
|
||||
}
|
||||
|
|
@ -36,21 +40,25 @@ where
|
|||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
||||
fn id(&self) -> &Id {
|
||||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.id(),
|
||||
CosmicElement::Cursor(elem) => elem.id(),
|
||||
CosmicElement::Dnd(elem) => elem.id(),
|
||||
CosmicElement::MoveGrab(elem) => elem.id(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.id(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.current_commit(),
|
||||
CosmicElement::Cursor(elem) => elem.current_commit(),
|
||||
CosmicElement::Dnd(elem) => elem.current_commit(),
|
||||
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.current_commit(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.current_commit(),
|
||||
}
|
||||
|
|
@ -60,7 +68,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.src(),
|
||||
CosmicElement::Cursor(elem) => elem.src(),
|
||||
CosmicElement::Dnd(elem) => elem.src(),
|
||||
CosmicElement::MoveGrab(elem) => elem.src(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.src(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.src(),
|
||||
}
|
||||
|
|
@ -70,7 +80,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.geometry(scale),
|
||||
CosmicElement::Cursor(elem) => elem.geometry(scale),
|
||||
CosmicElement::Dnd(elem) => elem.geometry(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.geometry(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.geometry(scale),
|
||||
}
|
||||
|
|
@ -80,7 +92,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.location(scale),
|
||||
CosmicElement::Cursor(elem) => elem.location(scale),
|
||||
CosmicElement::Dnd(elem) => elem.location(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.location(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.location(scale),
|
||||
}
|
||||
|
|
@ -90,7 +104,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.transform(),
|
||||
CosmicElement::Cursor(elem) => elem.transform(),
|
||||
CosmicElement::Dnd(elem) => elem.transform(),
|
||||
CosmicElement::MoveGrab(elem) => elem.transform(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.transform(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.transform(),
|
||||
}
|
||||
|
|
@ -99,12 +115,14 @@ where
|
|||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
commit: Option<smithay::backend::renderer::utils::CommitCounter>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> Vec<Rectangle<i32, Physical>> {
|
||||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::Cursor(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::Dnd(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
||||
}
|
||||
|
|
@ -114,7 +132,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::Cursor(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::Dnd(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
||||
}
|
||||
|
|
@ -124,7 +144,9 @@ where
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.alpha(),
|
||||
CosmicElement::Cursor(elem) => elem.alpha(),
|
||||
CosmicElement::Dnd(elem) => elem.alpha(),
|
||||
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.alpha(),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.alpha(),
|
||||
}
|
||||
|
|
@ -142,7 +164,11 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::MoveGrab(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::AdditionalDamage(elem) => {
|
||||
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
|
||||
|
|
@ -154,7 +180,9 @@ impl RenderElement<GlowRenderer> for CosmicElement<GlowRenderer> {
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Cursor(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => elem.underlying_storage(renderer),
|
||||
}
|
||||
|
|
@ -172,7 +200,11 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::MoveGrab(elem) => elem.draw(frame, src, dst, damage),
|
||||
CosmicElement::AdditionalDamage(elem) => {
|
||||
RenderElement::<GlMultiRenderer<'a>>::draw(elem, frame, src, dst, damage)
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
let elem = {
|
||||
|
|
@ -189,7 +221,9 @@ impl<'a> RenderElement<GlMultiRenderer<'a>> for CosmicElement<GlMultiRenderer<'a
|
|||
match self {
|
||||
CosmicElement::Workspace(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Cursor(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::Dnd(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer),
|
||||
CosmicElement::AdditionalDamage(elem) => elem.underlying_storage(renderer),
|
||||
#[cfg(feature = "debug")]
|
||||
CosmicElement::Egui(elem) => {
|
||||
let glow_renderer = renderer.glow_renderer_mut();
|
||||
|
|
@ -219,17 +253,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<R> From<CursorRenderElement<R>> for CosmicElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
fn from(elem: CursorRenderElement<R>) -> Self {
|
||||
Self::Cursor(elem)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> From<CosmicMappedRenderElement<R>> for CosmicElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
|
|
@ -241,6 +264,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<R> From<DamageElement> for CosmicElement<R>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
fn from(elem: DamageElement) -> Self {
|
||||
Self::AdditionalDamage(elem)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
|
||||
where
|
||||
|
|
@ -304,3 +338,58 @@ impl<'renderer, 'frame> AsGlowFrame<'frame> for GlMultiFrame<'renderer, 'frame>
|
|||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DamageElement {
|
||||
id: Id,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
}
|
||||
|
||||
impl DamageElement {
|
||||
pub fn new(geometry: Rectangle<i32, Logical>) -> DamageElement {
|
||||
DamageElement {
|
||||
id: Id::new(),
|
||||
geometry,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for DamageElement {
|
||||
fn id(&self) -> &Id {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
CommitCounter::default()
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, BufferCoords> {
|
||||
Rectangle::from_loc_and_size((0.0, 0.0), (1.0, 1.0))
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
self.geometry.to_f64().to_physical(scale).to_i32_round()
|
||||
}
|
||||
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: Scale<f64>,
|
||||
_commit: Option<CommitCounter>,
|
||||
) -> Vec<Rectangle<i32, Physical>> {
|
||||
vec![Rectangle::from_loc_and_size(
|
||||
(0, 0),
|
||||
self.geometry(scale).size,
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Renderer> RenderElement<R> for DamageElement {
|
||||
fn draw(
|
||||
&self,
|
||||
_frame: &mut <R as Renderer>::Frame<'_>,
|
||||
_src: Rectangle<f64, BufferCoords>,
|
||||
_dst: Rectangle<i32, Physical>,
|
||||
_damage: &[Rectangle<i32, Physical>],
|
||||
) -> Result<(), <R as Renderer>::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use std::{
|
|||
#[cfg(feature = "debug")]
|
||||
use crate::debug::fps_ui;
|
||||
use crate::{
|
||||
backend::render::element::DamageElement,
|
||||
shell::{
|
||||
focus::target::WindowGroup,
|
||||
grabs::{SeatMenuGrabState, SeatMoveGrabState},
|
||||
|
|
@ -23,19 +24,13 @@ use crate::{
|
|||
wayland::{
|
||||
handlers::{
|
||||
data_device::get_dnd_icon,
|
||||
screencopy::{render_session, WORKSPACE_OVERVIEW_NAMESPACE},
|
||||
},
|
||||
protocols::{
|
||||
screencopy::{
|
||||
BufferParams, CursorMode as ScreencopyCursorMode, Session as ScreencopySession,
|
||||
},
|
||||
workspace::WorkspaceHandle,
|
||||
screencopy::{render_session, FrameHolder, SessionData, WORKSPACE_OVERVIEW_NAMESPACE},
|
||||
},
|
||||
protocols::workspace::WorkspaceHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use cosmic_comp_config::workspace::WorkspaceLayout;
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
|
||||
use keyframe::{ease, functions::EaseInOutCubic};
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
|
@ -61,19 +56,17 @@ use smithay::{
|
|||
},
|
||||
desktop::{layer_map_for_output, PopupManager},
|
||||
output::{Output, OutputNoMode},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Scale},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Scale, Transform},
|
||||
wayland::{
|
||||
dmabuf::get_dmabuf,
|
||||
shell::wlr_layer::Layer,
|
||||
shm::{shm_format_to_fourcc, with_buffer_contents},
|
||||
},
|
||||
};
|
||||
use tracing::warn;
|
||||
|
||||
pub mod animations;
|
||||
|
||||
pub mod cursor;
|
||||
use self::cursor::CursorRenderElement;
|
||||
pub mod element;
|
||||
use self::element::{AsGlowRenderer, CosmicElement};
|
||||
|
||||
|
|
@ -392,17 +385,16 @@ pub enum CursorMode {
|
|||
}
|
||||
|
||||
#[profiling::function]
|
||||
pub fn cursor_elements<'frame, E, R>(
|
||||
pub fn cursor_elements<'frame, R>(
|
||||
renderer: &mut R,
|
||||
state: &Common,
|
||||
output: &Output,
|
||||
mode: CursorMode,
|
||||
) -> Vec<E>
|
||||
) -> Vec<CosmicElement<R>>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
E: From<CursorRenderElement<R>> + From<CosmicMappedRenderElement<R>>,
|
||||
{
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let mut elements = Vec::new();
|
||||
|
|
@ -425,7 +417,13 @@ where
|
|||
mode != CursorMode::NotDefault,
|
||||
)
|
||||
.into_iter()
|
||||
.map(E::from),
|
||||
.map(|(elem, hotspot)| {
|
||||
CosmicElement::Cursor(RelocateRenderElement::from_element(
|
||||
elem,
|
||||
Point::from((-hotspot.x, -hotspot.y)),
|
||||
Relocate::Relative,
|
||||
))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +431,7 @@ where
|
|||
elements.extend(
|
||||
cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), scale)
|
||||
.into_iter()
|
||||
.map(E::from),
|
||||
.map(CosmicElement::Dnd),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +442,7 @@ where
|
|||
.unwrap()
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|state| state.render::<E, R>(renderer, seat, output, theme))
|
||||
.map(|state| state.render::<CosmicElement<R>, R>(renderer, seat, output, theme))
|
||||
{
|
||||
elements.extend(grab_elements);
|
||||
}
|
||||
|
|
@ -940,7 +938,7 @@ where
|
|||
}
|
||||
|
||||
#[profiling::function]
|
||||
pub fn render_output<R, Target, OffTarget, Source>(
|
||||
pub fn render_output<R, Target, OffTarget>(
|
||||
gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
target: Target,
|
||||
|
|
@ -949,7 +947,6 @@ pub fn render_output<R, Target, OffTarget, Source>(
|
|||
state: &mut Common,
|
||||
output: &Output,
|
||||
cursor_mode: CursorMode,
|
||||
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
|
||||
fps: Option<&mut Fps>,
|
||||
) -> Result<RenderOutputResult, RenderError<R>>
|
||||
where
|
||||
|
|
@ -960,14 +957,14 @@ where
|
|||
+ Bind<Dmabuf>
|
||||
+ Bind<Target>
|
||||
+ Offscreen<OffTarget>
|
||||
+ Blit<Source>
|
||||
+ Blit<Target>
|
||||
+ AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
<R as Renderer>::Error: From<GlesError>,
|
||||
CosmicElement<R>: RenderElement<R>,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||
Source: Clone,
|
||||
Target: Clone,
|
||||
{
|
||||
let (previous_workspace, workspace) = state.shell.workspaces.active(output);
|
||||
let (previous_idx, idx) = state.shell.workspaces.active_num(output);
|
||||
|
|
@ -979,38 +976,128 @@ where
|
|||
let result = render_workspace(
|
||||
gpu,
|
||||
renderer,
|
||||
target,
|
||||
target.clone(),
|
||||
damage_tracker,
|
||||
age,
|
||||
None,
|
||||
state,
|
||||
output,
|
||||
previous_workspace,
|
||||
workspace,
|
||||
cursor_mode,
|
||||
screencopy,
|
||||
fps,
|
||||
false,
|
||||
);
|
||||
|
||||
result
|
||||
match result {
|
||||
Ok((res, mut elements)) => {
|
||||
for (session, frame) in output.take_pending_frames() {
|
||||
if let Some((frame, damage)) = render_session(
|
||||
renderer,
|
||||
&session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
output.current_transform(),
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
let old_len = if !additional_damage.is_empty() {
|
||||
let area = output
|
||||
.current_mode()
|
||||
.ok_or(RenderError::OutputNoMode(OutputNoMode))
|
||||
.map(
|
||||
|mode| {
|
||||
mode.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64()
|
||||
}, /* TODO: Mode is Buffer..., why is this Physical in the first place */
|
||||
)?;
|
||||
|
||||
let old_len = elements.len();
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
rect.to_f64()
|
||||
.to_logical(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&area,
|
||||
)
|
||||
.to_i32_round()
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::into),
|
||||
);
|
||||
|
||||
Some(old_len)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res = dt.damage_output(age, &elements)?;
|
||||
|
||||
if let Some(old_len) = old_len {
|
||||
elements.truncate(old_len);
|
||||
}
|
||||
|
||||
if let (Some(ref damage), _) = &res {
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let format = with_buffer_contents(buffer, |_, _, data| {
|
||||
shm_format_to_fourcc(data.format)
|
||||
})
|
||||
.map_err(|_| OutputNoMode)? // eh, we have to do some error
|
||||
.expect(
|
||||
"We should be able to convert all hardcoded shm screencopy formats",
|
||||
);
|
||||
let render_buffer = renderer
|
||||
.create_buffer(format, size)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
renderer
|
||||
.bind(render_buffer)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
for rect in damage {
|
||||
renderer
|
||||
.blit_from(target.clone(), *rect, *rect, TextureFilter::Nearest)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(RenderOutputResult {
|
||||
damage: res.0,
|
||||
sync: SyncPoint::default(),
|
||||
states: res.1,
|
||||
})
|
||||
},
|
||||
)? {
|
||||
frame.success(output.current_transform(), damage, state.clock.now());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
#[profiling::function]
|
||||
pub fn render_workspace<R, Target, OffTarget, Source>(
|
||||
pub fn render_workspace<R, Target, OffTarget>(
|
||||
gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
target: Target,
|
||||
damage_tracker: &mut OutputDamageTracker,
|
||||
age: usize,
|
||||
additional_damage: Option<Vec<Rectangle<i32, Logical>>>,
|
||||
state: &mut Common,
|
||||
output: &Output,
|
||||
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
|
||||
current: (WorkspaceHandle, usize),
|
||||
mut cursor_mode: CursorMode,
|
||||
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
|
||||
cursor_mode: CursorMode,
|
||||
mut fps: Option<&mut Fps>,
|
||||
exclude_workspace_overview: bool,
|
||||
) -> Result<RenderOutputResult, RenderError<R>>
|
||||
) -> Result<(RenderOutputResult, Vec<CosmicElement<R>>), RenderError<R>>
|
||||
where
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
|
|
@ -1019,14 +1106,12 @@ where
|
|||
+ Bind<Dmabuf>
|
||||
+ Bind<Target>
|
||||
+ Offscreen<OffTarget>
|
||||
+ Blit<Source>
|
||||
+ AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
<R as Renderer>::Error: From<GlesError>,
|
||||
CosmicElement<R>: RenderElement<R>,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||
Source: Clone,
|
||||
{
|
||||
if let Some(ref mut fps) = fps {
|
||||
fps.start();
|
||||
|
|
@ -1039,22 +1124,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let screencopy_contains_embedded = screencopy.as_ref().map_or(false, |(_, sessions)| {
|
||||
sessions
|
||||
.iter()
|
||||
.any(|(s, _)| s.cursor_mode() == ScreencopyCursorMode::Embedded)
|
||||
});
|
||||
// cursor handling without a cursor_plane in this case is horrible.
|
||||
// because what if some session disagree and/or the backend wants to render with a different mode?
|
||||
// It seems we would need to render to an offscreen buffer in those cases (and do multiple renders, which messes with damage tracking).
|
||||
// So for now, we just pick the worst mode (embedded), if any requires it.
|
||||
//
|
||||
// Once we move to a cursor_plane, the default framebuffer will never contain a cursor and we can just composite the cursor for each session separately on top (or not).
|
||||
if screencopy_contains_embedded {
|
||||
cursor_mode = CursorMode::All;
|
||||
};
|
||||
|
||||
let elements: Vec<CosmicElement<R>> = workspace_elements(
|
||||
let mut elements: Vec<CosmicElement<R>> = workspace_elements(
|
||||
gpu,
|
||||
renderer,
|
||||
state,
|
||||
|
|
@ -1065,6 +1135,18 @@ where
|
|||
&mut fps,
|
||||
exclude_workspace_overview,
|
||||
)?;
|
||||
|
||||
if let Some(additional_damage) = additional_damage {
|
||||
let output_geo = output.geometry().to_local(&output).as_logical();
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.filter_map(|rect| rect.intersection(output_geo))
|
||||
.map(DamageElement::new)
|
||||
.map(Into::<CosmicElement<R>>::into),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(fps) = fps.as_mut() {
|
||||
fps.elements();
|
||||
}
|
||||
|
|
@ -1081,62 +1163,6 @@ where
|
|||
fps.render();
|
||||
}
|
||||
|
||||
if let Some((source, buffers)) = screencopy {
|
||||
if res.is_ok() {
|
||||
for (session, params) in buffers {
|
||||
match render_session(
|
||||
gpu.cloned(),
|
||||
renderer,
|
||||
&session,
|
||||
params,
|
||||
output.current_transform(),
|
||||
|_node, buffer, renderer, dt, age| {
|
||||
let res = dt.damage_output(age, &elements)?;
|
||||
|
||||
if let (Some(ref damage), _) = &res {
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf).map_err(RenderError::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let format =
|
||||
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| OutputNoMode)? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer = renderer
|
||||
.create_buffer(format, size)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
renderer
|
||||
.bind(render_buffer)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
for rect in damage {
|
||||
renderer
|
||||
.blit_from(source.clone(), *rect, *rect, TextureFilter::Nearest)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(RenderOutputResult {
|
||||
damage: res.0,
|
||||
sync: SyncPoint::default(),
|
||||
states: res.1,
|
||||
})
|
||||
},
|
||||
) {
|
||||
Ok(true) => {} // success
|
||||
Ok(false) => state.still_pending(session.clone(), params.clone()),
|
||||
Err(err) => {
|
||||
warn!(?err, "Error rendering to screencopy session.");
|
||||
session.failed(FailureReason::Unspec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(fps) = fps.as_mut() {
|
||||
fps.screencopy();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
if let Some(ref mut fps) = fps {
|
||||
if let Some(rd) = fps.rd.as_mut() {
|
||||
|
|
@ -1147,5 +1173,5 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
res
|
||||
res.map(|res| (res, elements))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{
|
|||
input::Devices,
|
||||
state::{BackendData, Common},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use smithay::{
|
||||
|
|
@ -45,7 +44,6 @@ pub struct WinitState {
|
|||
pub backend: WinitGraphicsBackend<GlowRenderer>,
|
||||
output: Output,
|
||||
damage_tracker: OutputDamageTracker,
|
||||
screencopy: Vec<(ScreencopySession, BufferParams)>,
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps,
|
||||
}
|
||||
|
|
@ -59,7 +57,7 @@ impl WinitState {
|
|||
let age = self.backend.buffer_age().unwrap_or(0);
|
||||
|
||||
let surface = self.backend.egl_surface();
|
||||
match render::render_output::<_, _, GlesRenderbuffer, _>(
|
||||
match render::render_output::<_, _, GlesRenderbuffer>(
|
||||
None,
|
||||
self.backend.renderer(),
|
||||
surface.clone(),
|
||||
|
|
@ -68,11 +66,6 @@ impl WinitState {
|
|||
state,
|
||||
&self.output,
|
||||
CursorMode::NotDefault,
|
||||
if !self.screencopy.is_empty() {
|
||||
Some((surface, &self.screencopy))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(not(feature = "debug"))]
|
||||
None,
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -85,7 +78,6 @@ impl WinitState {
|
|||
self.backend
|
||||
.submit(damage.as_deref())
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
self.screencopy.clear();
|
||||
#[cfg(feature = "debug")]
|
||||
self.fps.displayed();
|
||||
state.send_frames(&self.output, &states, |_| None);
|
||||
|
|
@ -104,9 +96,6 @@ impl WinitState {
|
|||
}
|
||||
}
|
||||
Err(err) => {
|
||||
for (session, params) in self.screencopy.drain(..) {
|
||||
state.still_pending(session, params)
|
||||
}
|
||||
anyhow::bail!("Rendering failed: {}", err);
|
||||
}
|
||||
};
|
||||
|
|
@ -136,12 +125,6 @@ impl WinitState {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_screencopy(&mut self, new: Option<Vec<(ScreencopySession, BufferParams)>>) {
|
||||
if let Some(sessions) = new {
|
||||
self.screencopy.extend(sessions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_backend(
|
||||
|
|
@ -233,7 +216,6 @@ pub fn init_backend(
|
|||
backend,
|
||||
output: output.clone(),
|
||||
damage_tracker: OutputDamageTracker::from_output(&output),
|
||||
screencopy: Vec::new(),
|
||||
#[cfg(feature = "debug")]
|
||||
fps,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{
|
|||
input::Devices,
|
||||
state::{BackendData, Common},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use smithay::{
|
||||
|
|
@ -150,7 +149,6 @@ impl X11State {
|
|||
render: ping.clone(),
|
||||
dirty: false,
|
||||
pending: true,
|
||||
screencopy: Vec::new(),
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps::new(&mut self.renderer),
|
||||
});
|
||||
|
|
@ -160,16 +158,9 @@ impl X11State {
|
|||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn schedule_render(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
screencopy: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) {
|
||||
pub fn schedule_render(&mut self, output: &Output) {
|
||||
if let Some(surface) = self.surfaces.iter_mut().find(|s| s.output == *output) {
|
||||
surface.dirty = true;
|
||||
if let Some(sessions) = screencopy {
|
||||
surface.screencopy.extend(sessions);
|
||||
}
|
||||
if !surface.pending {
|
||||
surface.render.ping();
|
||||
}
|
||||
|
|
@ -210,7 +201,6 @@ impl X11State {
|
|||
pub struct Surface {
|
||||
window: Window,
|
||||
damage_tracker: OutputDamageTracker,
|
||||
screencopy: Vec<(ScreencopySession, BufferParams)>,
|
||||
surface: X11Surface,
|
||||
output: Output,
|
||||
render: ping::Ping,
|
||||
|
|
@ -226,7 +216,7 @@ impl Surface {
|
|||
.surface
|
||||
.buffer()
|
||||
.with_context(|| "Failed to allocate buffer")?;
|
||||
match render::render_output::<_, _, GlesRenderbuffer, _>(
|
||||
match render::render_output::<_, _, GlesRenderbuffer>(
|
||||
None,
|
||||
renderer,
|
||||
buffer.clone(),
|
||||
|
|
@ -235,18 +225,12 @@ impl Surface {
|
|||
state,
|
||||
&self.output,
|
||||
render::CursorMode::NotDefault,
|
||||
if !self.screencopy.is_empty() {
|
||||
Some((buffer, &self.screencopy))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(not(feature = "debug"))]
|
||||
None,
|
||||
#[cfg(feature = "debug")]
|
||||
Some(&mut self.fps),
|
||||
) {
|
||||
Ok(RenderOutputResult { damage, states, .. }) => {
|
||||
self.screencopy.clear();
|
||||
self.surface
|
||||
.submit()
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
|
|
@ -268,9 +252,6 @@ impl Surface {
|
|||
}
|
||||
}
|
||||
Err(err) => {
|
||||
for (session, params) in self.screencopy.drain(..) {
|
||||
state.still_pending(session, params)
|
||||
}
|
||||
self.surface.reset_buffers();
|
||||
anyhow::bail!("Rendering failed: {}", err);
|
||||
}
|
||||
|
|
@ -540,7 +521,7 @@ impl State {
|
|||
self.process_input_event(event, false);
|
||||
// TODO actually figure out the output
|
||||
for output in self.common.shell.outputs() {
|
||||
self.backend.x11().schedule_render(output, None);
|
||||
self.backend.x11().schedule_render(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,13 @@ use crate::{
|
|||
state::Common,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::{screencopy::ScreencopySessions, xdg_activation::ActivationContext},
|
||||
protocols::screencopy::Session,
|
||||
handlers::{screencopy::SessionHolder, xdg_activation::ActivationContext},
|
||||
protocols::screencopy::{BufferConstraints, CursorSession},
|
||||
},
|
||||
};
|
||||
use calloop::{timer::Timer, RegistrationToken};
|
||||
use cosmic_comp_config::{workspace::WorkspaceLayout, TileBehavior};
|
||||
use cosmic_config::ConfigSet;
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
use smithay::{
|
||||
backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent,
|
||||
|
|
@ -45,7 +44,10 @@ use smithay::{
|
|||
Seat, SeatState,
|
||||
},
|
||||
output::Output,
|
||||
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
||||
reexports::{
|
||||
input::Device as InputDevice,
|
||||
wayland_server::{protocol::wl_shm::Format as ShmFormat, DisplayHandle},
|
||||
},
|
||||
utils::{Point, Serial, SERIAL_COUNTER},
|
||||
wayland::{
|
||||
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
||||
|
|
@ -777,18 +779,13 @@ impl State {
|
|||
}
|
||||
|
||||
if output != current_output {
|
||||
for session in sessions_for_output(&self.common, ¤t_output) {
|
||||
session.cursor_leave(&seat, InputType::Pointer);
|
||||
for session in cursor_sessions_for_output(&self.common, ¤t_output) {
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
session.cursor_enter(&seat, InputType::Pointer);
|
||||
}
|
||||
|
||||
seat.set_active_output(&output);
|
||||
}
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
for session in cursor_sessions_for_output(&self.common, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
|
|
@ -797,7 +794,19 @@ impl State {
|
|||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
session.cursor_info(&seat, InputType::Pointer, geometry, offset);
|
||||
if session
|
||||
.current_constraints()
|
||||
.map(|constraint| constraint.size != geometry.size)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
session.update_constraints(BufferConstraints {
|
||||
size: geometry.size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
});
|
||||
}
|
||||
session.set_cursor_hotspot(offset);
|
||||
session.set_cursor_pos(Some(geometry.loc));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -823,7 +832,7 @@ impl State {
|
|||
let under = State::surface_under(position, &output, &mut self.common.shell)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
for session in cursor_sessions_for_output(&self.common, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
|
|
@ -832,7 +841,19 @@ impl State {
|
|||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
session.cursor_info(&seat, InputType::Pointer, geometry, offset);
|
||||
if session
|
||||
.current_constraints()
|
||||
.map(|constraint| constraint.size != geometry.size)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
session.update_constraints(BufferConstraints {
|
||||
size: geometry.size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
});
|
||||
}
|
||||
session.set_cursor_hotspot(offset);
|
||||
session.set_cursor_pos(Some(geometry.loc));
|
||||
}
|
||||
}
|
||||
let ptr = seat.get_pointer().unwrap();
|
||||
|
|
@ -2406,51 +2427,22 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator<Item = Session> {
|
||||
fn cursor_sessions_for_output(
|
||||
state: &Common,
|
||||
output: &Output,
|
||||
) -> impl Iterator<Item = CursorSession> {
|
||||
let workspace = state.shell.active_space(&output);
|
||||
let maybe_fullscreen = workspace.get_fullscreen();
|
||||
workspace
|
||||
.screencopy_sessions
|
||||
.iter()
|
||||
.map(|s| (&**s).clone())
|
||||
.cursor_sessions()
|
||||
.into_iter()
|
||||
.chain(
|
||||
maybe_fullscreen
|
||||
.as_ref()
|
||||
.and_then(|w| {
|
||||
if let Some(sessions) = w.user_data().get::<ScreencopySessions>() {
|
||||
Some(
|
||||
sessions
|
||||
.0
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|s| (&**s).clone())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|w| w.cursor_sessions())
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.chain(
|
||||
output
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.map(|sessions| {
|
||||
sessions
|
||||
.0
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|s| (&**s).clone())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.into_iter()
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.chain(output.cursor_sessions().into_iter())
|
||||
}
|
||||
|
||||
// TODO Is it possible to determine mapping for external touchscreen?
|
||||
|
|
|
|||
35
src/main.rs
35
src/main.rs
|
|
@ -13,12 +13,8 @@ use std::{env, ffi::OsString, process, sync::Arc};
|
|||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::{
|
||||
state::BackendData,
|
||||
utils::prelude::SeatExt,
|
||||
wayland::{
|
||||
handlers::{compositor::client_compositor_state, screencopy::PendingScreencopyBuffers},
|
||||
protocols::screencopy::SessionType,
|
||||
},
|
||||
state::BackendData, utils::prelude::SeatExt,
|
||||
wayland::handlers::compositor::client_compositor_state,
|
||||
};
|
||||
|
||||
pub mod backend;
|
||||
|
|
@ -121,30 +117,9 @@ fn main() -> Result<()> {
|
|||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
{
|
||||
let mut scheduled_sessions = state.workspace_session_for_output(&output);
|
||||
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(sessions.borrow_mut().drain(..));
|
||||
}
|
||||
state.backend.schedule_render(
|
||||
&state.common.event_loop_handle,
|
||||
&output,
|
||||
scheduled_sessions.as_ref().map(|sessions| {
|
||||
sessions
|
||||
.iter()
|
||||
.filter(|(s, _)| match s.session_type() {
|
||||
SessionType::Output(o) | SessionType::Workspace(o, _)
|
||||
if o == output =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
);
|
||||
state
|
||||
.backend
|
||||
.schedule_render(&state.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
iced::{IcedElement, Program},
|
||||
prelude::*,
|
||||
},
|
||||
wayland::handlers::screencopy::ScreencopySessions,
|
||||
wayland::handlers::screencopy::SessionHolder,
|
||||
};
|
||||
use calloop::LoopHandle;
|
||||
use cosmic::{
|
||||
|
|
@ -22,7 +22,6 @@ use cosmic::{
|
|||
iced_widget::scrollable::AbsoluteOffset,
|
||||
theme, widget as cosmic_widget, Apply, Element as CosmicElement,
|
||||
};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
use once_cell::sync::Lazy;
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
|
@ -48,7 +47,7 @@ use smithay::{
|
|||
},
|
||||
output::Output,
|
||||
render_elements,
|
||||
utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size},
|
||||
utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
use std::{
|
||||
|
|
@ -59,6 +58,7 @@ use std::{
|
|||
atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
mod tab;
|
||||
|
|
@ -494,17 +494,28 @@ impl CosmicStack {
|
|||
if previous != active {
|
||||
let windows = p.windows.lock().unwrap();
|
||||
if let Some(previous) = windows.get(previous) {
|
||||
if let Some(sessions) = previous.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
for session in previous.cursor_sessions() {
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
PointerTarget::leave(previous, seat, data, serial, time);
|
||||
}
|
||||
|
||||
if let Some(sessions) = windows[active].user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
for session in windows[active].cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
location
|
||||
.to_buffer(
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
&windows[active].geometry().size.to_f64(),
|
||||
)
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) =
|
||||
seat.cursor_geometry((0.0, 0.0), Duration::from_millis(time as u64).into())
|
||||
{
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
PointerTarget::enter(
|
||||
|
|
@ -1121,12 +1132,6 @@ impl PointerTarget<State> for CosmicStack {
|
|||
let mut event = event.clone();
|
||||
if self.0.with_program(|p| {
|
||||
let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)];
|
||||
if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
let geo = active_window.geometry();
|
||||
let loc = event.location.to_i32_round::<i32>();
|
||||
let (old_focus, shape) = if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x < 0 {
|
||||
|
|
@ -1170,11 +1175,33 @@ impl PointerTarget<State> for CosmicStack {
|
|||
p.previous_pointer.store(active, Ordering::SeqCst);
|
||||
|
||||
PointerTarget::enter(active_window, seat, data, &event);
|
||||
|
||||
for session in active_window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(1.0, Transform::Normal, &geo.size.to_f64())
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if old_focus == Focus::Window {
|
||||
PointerTarget::leave(active_window, seat, data, event.serial, event.time);
|
||||
|
||||
for session in active_window.cursor_sessions() {
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
}
|
||||
|
||||
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
||||
|
|
@ -1203,17 +1230,6 @@ impl PointerTarget<State> for CosmicStack {
|
|||
|
||||
let (previous, next) = self.0.with_program(|p| {
|
||||
let active_window = &p.windows.lock().unwrap()[active];
|
||||
if let Some(sessions) = active_window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
|
||||
if let Some((geo, hotspot)) =
|
||||
seat.cursor_geometry(buffer_loc, data.common.clock.now())
|
||||
{
|
||||
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let geo = active_window.geometry();
|
||||
let loc = event.location.to_i32_round::<i32>();
|
||||
let (next, shape) = if loc.y - geo.loc.y < 0 && loc.x - geo.loc.x < 0 {
|
||||
|
|
@ -1247,12 +1263,33 @@ impl PointerTarget<State> for CosmicStack {
|
|||
PointerTarget::motion(active_window, seat, data, &event);
|
||||
}
|
||||
|
||||
for session in active_window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(1.0, Transform::Normal, &geo.size.to_f64())
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return (previous, Focus::Window);
|
||||
};
|
||||
|
||||
let previous = p.swap_focus(next);
|
||||
if previous == Focus::Window {
|
||||
PointerTarget::leave(active_window, seat, data, event.serial, event.time);
|
||||
|
||||
for session in active_window.cursor_sessions() {
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
}
|
||||
|
||||
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
||||
|
|
@ -1468,13 +1505,10 @@ impl PointerTarget<State> for CosmicStack {
|
|||
}
|
||||
|
||||
let previous = self.0.with_program(|p| {
|
||||
if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
for session in
|
||||
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].cursor_sessions()
|
||||
{
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
|
||||
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ use crate::{
|
|||
iced::{IcedElement, Program},
|
||||
prelude::*,
|
||||
},
|
||||
wayland::handlers::screencopy::ScreencopySessions,
|
||||
wayland::handlers::screencopy::SessionHolder,
|
||||
};
|
||||
use calloop::LoopHandle;
|
||||
use cosmic::{iced::Command, widget::mouse_area, Apply};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
|
|
@ -39,7 +38,7 @@ use smithay::{
|
|||
output::Output,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
render_elements,
|
||||
utils::{Buffer as BufferCoords, IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||
utils::{Buffer as BufferCoords, IsAlive, Logical, Point, Rectangle, Serial, Size, Transform},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
use std::{
|
||||
|
|
@ -50,6 +49,7 @@ use std::{
|
|||
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
use wayland_backend::server::ObjectId;
|
||||
|
||||
|
|
@ -576,12 +576,6 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
let mut event = event.clone();
|
||||
if self.0.with_program(|p| {
|
||||
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
if p.has_ssd(false) {
|
||||
let geo = p.window.geometry();
|
||||
let loc = event.location.to_i32_round::<i32>();
|
||||
|
|
@ -624,6 +618,27 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
let mut event = event.clone();
|
||||
event.location.y -= SSD_HEIGHT as f64;
|
||||
PointerTarget::enter(&p.window, seat, data, &event);
|
||||
|
||||
for session in p.window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
&p.window.geometry().size.to_f64(),
|
||||
)
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
@ -640,6 +655,22 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
} else {
|
||||
p.swap_focus(Focus::Window);
|
||||
PointerTarget::enter(&p.window, seat, data, &event);
|
||||
for session in p.window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(1.0, Transform::Normal, &p.window.geometry().size.to_f64())
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}) {
|
||||
|
|
@ -651,17 +682,6 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
let mut event = event.clone();
|
||||
if let Some((previous, next)) = self.0.with_program(|p| {
|
||||
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale
|
||||
if let Some((geo, hotspot)) =
|
||||
seat.cursor_geometry(buffer_loc, data.common.clock.now())
|
||||
{
|
||||
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.has_ssd(false) {
|
||||
let geo = p.window.geometry();
|
||||
let loc = event.location.to_i32_round::<i32>();
|
||||
|
|
@ -693,6 +713,27 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
PointerTarget::motion(&p.window, seat, data, &event);
|
||||
}
|
||||
|
||||
for session in p.window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
&p.window.geometry().size.to_f64(),
|
||||
)
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return Some((previous, Focus::Window));
|
||||
};
|
||||
|
||||
|
|
@ -713,6 +754,24 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
} else {
|
||||
p.swap_focus(Focus::Window);
|
||||
PointerTarget::motion(&p.window, seat, data, &event);
|
||||
|
||||
for session in p.window.cursor_sessions() {
|
||||
session.set_cursor_pos(Some(
|
||||
event
|
||||
.location
|
||||
.to_buffer(1.0, Transform::Normal, &p.window.geometry().size.to_f64())
|
||||
.to_i32_round(),
|
||||
));
|
||||
if let Some((_, hotspot)) = seat.cursor_geometry(
|
||||
(0.0, 0.0),
|
||||
Duration::from_millis(event.time as u64).into(),
|
||||
) {
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
} else {
|
||||
session.set_cursor_hotspot((0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}) {
|
||||
|
|
@ -801,10 +860,8 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
let previous = self.0.with_program(|p| {
|
||||
if let Some(sessions) = p.window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
for session in p.window.cursor_sessions() {
|
||||
session.set_cursor_pos(None);
|
||||
}
|
||||
|
||||
let cursor_state = seat.user_data().get::<CursorState>().unwrap();
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ use crate::{
|
|||
state::State,
|
||||
utils::{prelude::*, tween::EaseRectangle},
|
||||
wayland::{
|
||||
handlers::screencopy::DropableSession,
|
||||
handlers::screencopy::ScreencopySessions,
|
||||
protocols::{
|
||||
screencopy::{BufferParams, Session as ScreencopySession},
|
||||
toplevel_info::ToplevelInfoState,
|
||||
workspace::{WorkspaceHandle, WorkspaceUpdateGuard},
|
||||
},
|
||||
|
|
@ -83,8 +82,7 @@ pub struct Workspace {
|
|||
|
||||
pub handle: WorkspaceHandle,
|
||||
pub focus_stack: FocusStacks,
|
||||
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
||||
pub screencopy_sessions: Vec<DropableSession>,
|
||||
pub screencopy: ScreencopySessions,
|
||||
pub output_stack: VecDeque<String>,
|
||||
pub pending_tokens: HashSet<XdgActivationToken>,
|
||||
pub(super) backdrop_id: Id,
|
||||
|
|
@ -272,8 +270,7 @@ impl Workspace {
|
|||
fullscreen: None,
|
||||
handle,
|
||||
focus_stack: FocusStacks::default(),
|
||||
pending_buffers: Vec::new(),
|
||||
screencopy_sessions: Vec::new(),
|
||||
screencopy: ScreencopySessions::default(),
|
||||
output_stack: {
|
||||
let mut queue = VecDeque::new();
|
||||
queue.push_back(output_name);
|
||||
|
|
|
|||
56
src/state.rs
56
src/state.rs
|
|
@ -7,14 +7,12 @@ use crate::{
|
|||
shell::{grabs::SeatMoveGrabState, Shell},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::{
|
||||
drm::WlDrmState,
|
||||
output_configuration::OutputConfigurationState,
|
||||
screencopy::{BufferParams, ScreencopyState, Session as ScreencopySession},
|
||||
drm::WlDrmState, image_source::ImageSourceState,
|
||||
output_configuration::OutputConfigurationState, screencopy::ScreencopyState,
|
||||
workspace::WorkspaceClientState,
|
||||
},
|
||||
};
|
||||
use anyhow::Context;
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_manager_v1::CursorMode;
|
||||
use i18n_embed::{
|
||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||
DesktopLanguageRequester,
|
||||
|
|
@ -177,6 +175,7 @@ pub struct Common {
|
|||
pub presentation_state: PresentationState,
|
||||
pub primary_selection_state: PrimarySelectionState,
|
||||
pub data_control_state: Option<DataControlState>,
|
||||
pub image_source_state: ImageSourceState,
|
||||
pub screencopy_state: ScreencopyState,
|
||||
pub seat_state: SeatState<State>,
|
||||
pub session_lock_manager_state: SessionLockManagerState,
|
||||
|
|
@ -270,19 +269,14 @@ impl BackendData {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn schedule_render(
|
||||
&mut self,
|
||||
loop_handle: &LoopHandle<'_, State>,
|
||||
output: &Output,
|
||||
screencopy: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) {
|
||||
pub fn schedule_render(&mut self, loop_handle: &LoopHandle<'_, State>, output: &Output) {
|
||||
match self {
|
||||
BackendData::Winit(ref mut state) => state.pending_screencopy(screencopy), // We cannot do this on the winit backend.
|
||||
BackendData::Winit(_) => {} // We cannot do this on the winit backend.
|
||||
// Winit has a very strict render-loop and skipping frames breaks atleast the wayland winit-backend.
|
||||
// Swapping with damage (which should be empty on these frames) is likely good enough anyway.
|
||||
BackendData::X11(ref mut state) => state.schedule_render(output, screencopy),
|
||||
BackendData::X11(ref mut state) => state.schedule_render(output),
|
||||
BackendData::Kms(ref mut state) => {
|
||||
if let Err(err) = state.schedule_render(loop_handle, output, None, screencopy) {
|
||||
if let Err(err) = state.schedule_render(loop_handle, output, None) {
|
||||
error!(?err, "Failed to schedule event, are we shutting down?");
|
||||
}
|
||||
}
|
||||
|
|
@ -375,11 +369,10 @@ impl State {
|
|||
OutputConfigurationState::new(dh, client_should_see_privileged_protocols);
|
||||
let presentation_state = PresentationState::new::<Self>(dh, clock.id() as u32);
|
||||
let primary_selection_state = PrimarySelectionState::new::<Self>(dh);
|
||||
let screencopy_state = ScreencopyState::new::<Self, _, _>(
|
||||
dh,
|
||||
vec![CursorMode::Embedded, CursorMode::Hidden],
|
||||
client_should_see_privileged_protocols,
|
||||
);
|
||||
let image_source_state =
|
||||
ImageSourceState::new::<Self, _>(dh, client_should_see_privileged_protocols);
|
||||
let screencopy_state =
|
||||
ScreencopyState::new::<Self, _>(dh, client_should_see_privileged_protocols);
|
||||
let shm_state =
|
||||
ShmState::new::<Self>(dh, vec![wl_shm::Format::Xbgr8888, wl_shm::Format::Abgr8888]);
|
||||
let seat_state = SeatState::<Self>::new();
|
||||
|
|
@ -441,6 +434,7 @@ impl State {
|
|||
data_device_state,
|
||||
dmabuf_state,
|
||||
fractional_scale_state,
|
||||
image_source_state,
|
||||
screencopy_state,
|
||||
shm_state,
|
||||
seat_state,
|
||||
|
|
@ -890,7 +884,6 @@ struct PendingFrame {
|
|||
start: Instant,
|
||||
duration_elements: Option<Duration>,
|
||||
duration_render: Option<Duration>,
|
||||
duration_screencopy: Option<Duration>,
|
||||
duration_displayed: Option<Duration>,
|
||||
}
|
||||
|
||||
|
|
@ -899,7 +892,6 @@ pub struct Frame {
|
|||
pub start: Instant,
|
||||
pub duration_elements: Duration,
|
||||
pub duration_render: Duration,
|
||||
pub duration_screencopy: Option<Duration>,
|
||||
pub duration_displayed: Duration,
|
||||
}
|
||||
|
||||
|
|
@ -909,16 +901,11 @@ impl Frame {
|
|||
}
|
||||
|
||||
fn frame_time(&self) -> Duration {
|
||||
self.duration_elements
|
||||
+ self.duration_render
|
||||
+ self.duration_screencopy.clone().unwrap_or(Duration::ZERO)
|
||||
self.duration_elements + self.duration_render
|
||||
}
|
||||
|
||||
fn time_to_display(&self) -> Duration {
|
||||
self.duration_elements
|
||||
+ self.duration_render
|
||||
+ self.duration_screencopy.clone().unwrap_or(Duration::ZERO)
|
||||
+ self.duration_displayed
|
||||
self.duration_elements + self.duration_render + self.duration_displayed
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -928,7 +915,6 @@ impl From<PendingFrame> for Frame {
|
|||
start: pending.start,
|
||||
duration_elements: pending.duration_elements.unwrap_or(Duration::ZERO),
|
||||
duration_render: pending.duration_render.unwrap_or(Duration::ZERO),
|
||||
duration_screencopy: pending.duration_screencopy,
|
||||
duration_displayed: pending.duration_displayed.unwrap_or(Duration::ZERO),
|
||||
}
|
||||
}
|
||||
|
|
@ -942,7 +928,6 @@ impl Fps {
|
|||
start: Instant::now(),
|
||||
duration_elements: None,
|
||||
duration_render: None,
|
||||
duration_screencopy: None,
|
||||
duration_displayed: None,
|
||||
});
|
||||
}
|
||||
|
|
@ -962,23 +947,12 @@ impl Fps {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn screencopy(&mut self) {
|
||||
if let Some(frame) = self.pending_frame.as_mut() {
|
||||
frame.duration_screencopy = Some(
|
||||
Instant::now().duration_since(frame.start)
|
||||
- frame.duration_elements.clone().unwrap_or(Duration::ZERO)
|
||||
- frame.duration_render.clone().unwrap_or(Duration::ZERO),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn displayed(&mut self) {
|
||||
if let Some(mut frame) = self.pending_frame.take() {
|
||||
frame.duration_displayed = Some(
|
||||
Instant::now().duration_since(frame.start)
|
||||
- frame.duration_elements.clone().unwrap_or(Duration::ZERO)
|
||||
- frame.duration_render.clone().unwrap_or(Duration::ZERO)
|
||||
- frame.duration_screencopy.clone().unwrap_or(Duration::ZERO),
|
||||
- frame.duration_render.clone().unwrap_or(Duration::ZERO),
|
||||
);
|
||||
|
||||
self.frames.push_back(frame.into());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*,
|
||||
wayland::protocols::screencopy::SessionType,
|
||||
};
|
||||
use crate::{shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*};
|
||||
use calloop::Interest;
|
||||
use smithay::{
|
||||
backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state},
|
||||
|
|
@ -29,8 +26,6 @@ use smithay::{
|
|||
};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::screencopy::PendingScreencopyBuffers;
|
||||
|
||||
impl State {
|
||||
fn toplevel_ensure_initial_configure(&mut self, toplevel: &ToplevelSurface) -> bool {
|
||||
// send the initial configure if relevant
|
||||
|
|
@ -251,14 +246,11 @@ impl CompositorHandler for State {
|
|||
workspace.commit(surface);
|
||||
}
|
||||
}
|
||||
|
||||
//handle window screencopy sessions
|
||||
self.schedule_window_session(surface);
|
||||
|
||||
// and refresh smithays internal state
|
||||
self.common.shell.popups.commit(surface);
|
||||
}
|
||||
|
||||
// and refresh smithays internal state
|
||||
self.common.shell.popups.commit(surface);
|
||||
|
||||
// re-arrange layer-surfaces (commits may change size and positioning)
|
||||
let layer_output = self
|
||||
.common
|
||||
|
|
@ -277,34 +269,10 @@ impl CompositorHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
let mut scheduled_sessions = self.schedule_workspace_sessions(surface);
|
||||
|
||||
// schedule a new render
|
||||
if let Some(output) = self.common.shell.visible_output_for_surface(surface) {
|
||||
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(sessions.borrow_mut().drain(..));
|
||||
}
|
||||
|
||||
self.backend.schedule_render(
|
||||
&self.common.event_loop_handle,
|
||||
&output,
|
||||
scheduled_sessions.as_ref().map(|sessions| {
|
||||
sessions
|
||||
.iter()
|
||||
.filter(|(s, _)| match s.session_type() {
|
||||
SessionType::Output(o) | SessionType::Workspace(o, _)
|
||||
if &o == output =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
);
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
src/wayland/handlers/image_source.rs
Normal file
4
src/wayland/handlers/image_source.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
use crate::state::State;
|
||||
use crate::wayland::protocols::image_source::delegate_image_source;
|
||||
|
||||
delegate_image_source!(State);
|
||||
|
|
@ -14,8 +14,6 @@ use smithay::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::screencopy::PendingScreencopyBuffers;
|
||||
|
||||
impl WlrLayerShellHandler for State {
|
||||
fn shell_state(&mut self) -> &mut WlrLayerShellState {
|
||||
&mut self.common.shell.layer_shell_state
|
||||
|
|
@ -76,19 +74,8 @@ impl WlrLayerShellHandler for State {
|
|||
|
||||
self.common.shell.workspaces.recalculate();
|
||||
|
||||
// collect screencopy sessions needing an update
|
||||
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
|
||||
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(sessions.borrow_mut().drain(..));
|
||||
}
|
||||
|
||||
self.backend.schedule_render(
|
||||
&self.common.event_loop_handle,
|
||||
&output,
|
||||
scheduled_sessions,
|
||||
);
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ pub mod dmabuf;
|
|||
pub mod drm;
|
||||
pub mod drm_lease;
|
||||
pub mod fractional_scale;
|
||||
pub mod image_source;
|
||||
pub mod input_method;
|
||||
pub mod keyboard_shortcuts_inhibit;
|
||||
pub mod layer_shell;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
415
src/wayland/handlers/screencopy/mod.rs
Normal file
415
src/wayland/handlers/screencopy/mod.rs
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::{Fourcc, Modifier},
|
||||
egl::EGLDevice,
|
||||
renderer::{
|
||||
damage::OutputDamageTracker,
|
||||
gles::{Capability, GlesRenderer},
|
||||
glow::GlowRenderer,
|
||||
utils::with_renderer_surface_state,
|
||||
},
|
||||
},
|
||||
desktop::space::SpaceElement,
|
||||
output::Output,
|
||||
reexports::wayland_server::protocol::wl_shm::Format as ShmFormat,
|
||||
utils::{Buffer as BufferCoords, Point, Size, Transform},
|
||||
wayland::{dmabuf::get_dmabuf, seat::WaylandFocus},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
shell::CosmicSurface,
|
||||
state::{BackendData, State},
|
||||
utils::prelude::{
|
||||
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
|
||||
},
|
||||
wayland::protocols::{
|
||||
image_source::ImageSourceData,
|
||||
screencopy::{
|
||||
delegate_screencopy, BufferConstraints, CursorSession, DmabufConstraints, Frame,
|
||||
ScreencopyHandler, ScreencopyState, Session,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mod render;
|
||||
mod user_data;
|
||||
pub use self::render::*;
|
||||
use self::user_data::*;
|
||||
pub use self::user_data::{FrameHolder, ScreencopySessions, SessionData, SessionHolder};
|
||||
|
||||
pub const WORKSPACE_OVERVIEW_NAMESPACE: &str = "cosmic-workspace-overview";
|
||||
|
||||
impl ScreencopyHandler for State {
|
||||
fn screencopy_state(&mut self) -> &mut ScreencopyState {
|
||||
&mut self.common.screencopy_state
|
||||
}
|
||||
|
||||
fn capture_source(&mut self, source: &ImageSourceData) -> Option<BufferConstraints> {
|
||||
match source {
|
||||
ImageSourceData::Output(weak) => weak
|
||||
.upgrade()
|
||||
.and_then(|output| constraints_for_output(&output, &mut self.backend)),
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
let workspace = self.common.shell.workspaces.space_for_handle(&handle)?;
|
||||
constraints_for_output(workspace.output(), &mut self.backend)
|
||||
}
|
||||
ImageSourceData::Toplevel(window) => {
|
||||
constraints_for_toplevel(window, &mut self.backend)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn capture_cursor_source(&mut self, _source: &ImageSourceData) -> Option<BufferConstraints> {
|
||||
let size = if let Some((geometry, _)) = self
|
||||
.common
|
||||
.last_active_seat()
|
||||
.cursor_geometry((0.0, 0.0), self.common.clock.now())
|
||||
{
|
||||
geometry.size
|
||||
} else {
|
||||
Size::from((64, 64))
|
||||
};
|
||||
|
||||
Some(BufferConstraints {
|
||||
size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn new_session(&mut self, session: Session) {
|
||||
match session.source() {
|
||||
ImageSourceData::Output(weak) => {
|
||||
let Some(mut output) = weak.upgrade() else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
|
||||
session.user_data().insert_if_missing(|| {
|
||||
RefCell::new(SessionUserData::new(OutputDamageTracker::from_output(
|
||||
&output,
|
||||
)))
|
||||
});
|
||||
|
||||
output.add_session(session);
|
||||
}
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
|
||||
else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
|
||||
session.user_data().insert_if_missing(|| {
|
||||
RefCell::new(SessionUserData::new(OutputDamageTracker::from_output(
|
||||
workspace.output(),
|
||||
)))
|
||||
});
|
||||
workspace.add_session(session);
|
||||
}
|
||||
ImageSourceData::Toplevel(mut toplevel) => {
|
||||
let size = toplevel.geometry().size.to_physical(1);
|
||||
session.user_data().insert_if_missing(|| {
|
||||
RefCell::new(SessionUserData::new(OutputDamageTracker::new(
|
||||
size,
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
)))
|
||||
});
|
||||
toplevel.add_session(session);
|
||||
}
|
||||
ImageSourceData::Destroyed => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn new_cursor_session(&mut self, session: CursorSession) {
|
||||
let (pointer_loc, pointer_size, hotspot) = {
|
||||
let seat = self.common.last_active_seat();
|
||||
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
let pointer_loc = pointer.current_location().to_i32_round().as_global();
|
||||
|
||||
let (pointer_size, hotspot) = if let Some((geometry, hotspot)) =
|
||||
seat.cursor_geometry((0.0, 0.0), self.common.clock.now())
|
||||
{
|
||||
(geometry.size, hotspot)
|
||||
} else {
|
||||
(Size::from((64, 64)), Point::from((0, 0)))
|
||||
};
|
||||
|
||||
(pointer_loc, pointer_size, hotspot)
|
||||
};
|
||||
|
||||
session.user_data().insert_if_missing(|| {
|
||||
RefCell::new(SessionUserData::new(OutputDamageTracker::new(
|
||||
pointer_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
)))
|
||||
});
|
||||
|
||||
match session.source() {
|
||||
ImageSourceData::Output(weak) => {
|
||||
let Some(mut output) = weak.upgrade() else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
|
||||
if output.geometry().contains(pointer_loc) {
|
||||
let buffer_pos = pointer_loc
|
||||
.to_local(&output)
|
||||
.as_logical()
|
||||
.to_f64()
|
||||
.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output
|
||||
.current_mode()
|
||||
.map(|mode| {
|
||||
mode.size
|
||||
.to_f64()
|
||||
.to_logical(output.current_scale().fractional_scale())
|
||||
})
|
||||
.unwrap_or(Size::from((0.0, 0.0))),
|
||||
)
|
||||
.to_i32_round();
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
session.set_cursor_pos(Some(buffer_pos));
|
||||
}
|
||||
|
||||
output.add_cursor_session(session);
|
||||
}
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
|
||||
else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
|
||||
let output = workspace.output().clone();
|
||||
if output.geometry().contains(pointer_loc) {
|
||||
let buffer_pos = pointer_loc
|
||||
.to_local(&output)
|
||||
.as_logical()
|
||||
.to_f64()
|
||||
.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output
|
||||
.current_mode()
|
||||
.map(|mode| {
|
||||
mode.size
|
||||
.to_f64()
|
||||
.to_logical(output.current_scale().fractional_scale())
|
||||
})
|
||||
.unwrap_or(Size::from((0.0, 0.0))),
|
||||
)
|
||||
.to_i32_round();
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
session.set_cursor_pos(Some(buffer_pos));
|
||||
}
|
||||
|
||||
workspace.add_cursor_session(session);
|
||||
}
|
||||
ImageSourceData::Toplevel(mut toplevel) => {
|
||||
if let Some(element) = self.common.shell.element_for_surface(&toplevel) {
|
||||
if element.has_active_window(&toplevel) {
|
||||
if let Some(workspace) = self.common.shell.space_for(element) {
|
||||
if let Some(geometry) = workspace.element_geometry(element) {
|
||||
let mut surface_geo = element.active_window_geometry().as_local();
|
||||
surface_geo.loc += geometry.loc;
|
||||
let global_geo = surface_geo.to_global(workspace.output());
|
||||
if global_geo.contains(pointer_loc) {
|
||||
let buffer_pos = (pointer_loc - global_geo.loc)
|
||||
.as_logical()
|
||||
.to_buffer(1, Transform::Normal, &toplevel.geometry().size);
|
||||
session.set_cursor_hotspot(hotspot);
|
||||
session.set_cursor_pos(Some(buffer_pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toplevel.add_cursor_session(session);
|
||||
}
|
||||
ImageSourceData::Destroyed => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self, session: Session, frame: Frame) {
|
||||
match session.source() {
|
||||
ImageSourceData::Output(weak) => {
|
||||
let Some(mut output) = weak.upgrade() else {
|
||||
session.stop(); // will fail the frame as well
|
||||
return;
|
||||
};
|
||||
|
||||
output.add_frame(session, frame);
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
render_workspace_to_buffer(self, session, frame, handle)
|
||||
}
|
||||
ImageSourceData::Toplevel(toplevel) => {
|
||||
render_window_to_buffer(self, session, frame, &toplevel)
|
||||
}
|
||||
ImageSourceData::Destroyed => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_frame(&mut self, session: CursorSession, frame: Frame) {
|
||||
if !session.has_cursor() {
|
||||
frame.success(Transform::Normal, Vec::new(), self.common.clock.now());
|
||||
return;
|
||||
}
|
||||
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
render_cursor_to_buffer(self, &session, frame, &seat);
|
||||
}
|
||||
|
||||
fn frame_aborted(&mut self, frame: Frame) {
|
||||
for mut output in self.common.shell.outputs().cloned() {
|
||||
output.remove_frame(&frame)
|
||||
}
|
||||
}
|
||||
|
||||
fn session_destroyed(&mut self, session: Session) {
|
||||
match session.source() {
|
||||
ImageSourceData::Output(weak) => {
|
||||
if let Some(mut output) = weak.upgrade() {
|
||||
output.remove_session(session);
|
||||
}
|
||||
}
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
|
||||
{
|
||||
workspace.remove_session(session)
|
||||
}
|
||||
}
|
||||
ImageSourceData::Toplevel(mut toplevel) => toplevel.remove_session(session),
|
||||
ImageSourceData::Destroyed => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_session_destroyed(&mut self, session: CursorSession) {
|
||||
match session.source() {
|
||||
ImageSourceData::Output(weak) => {
|
||||
if let Some(mut output) = weak.upgrade() {
|
||||
output.remove_cursor_session(session);
|
||||
}
|
||||
}
|
||||
ImageSourceData::Workspace(handle) => {
|
||||
if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
|
||||
{
|
||||
workspace.remove_cursor_session(session)
|
||||
}
|
||||
}
|
||||
ImageSourceData::Toplevel(mut toplevel) => toplevel.remove_cursor_session(session),
|
||||
ImageSourceData::Destroyed => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn constraints_for_output(output: &Output, backend: &mut BackendData) -> Option<BufferConstraints> {
|
||||
let mode = match output.current_mode() {
|
||||
Some(mode) => mode.size.to_logical(1).to_buffer(1, Transform::Normal),
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut _kms_renderer = None;
|
||||
let renderer = match backend {
|
||||
BackendData::Kms(ref mut kms) => {
|
||||
let node = kms
|
||||
.target_node_for_output(&output)
|
||||
.unwrap_or(kms.primary_node);
|
||||
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
|
||||
_kms_renderer.as_mut().unwrap().as_mut()
|
||||
}
|
||||
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
|
||||
BackendData::X11(ref mut x11) => &mut x11.renderer,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Some(constraints_for_renderer(mode, renderer))
|
||||
}
|
||||
|
||||
fn constraints_for_toplevel(
|
||||
surface: &CosmicSurface,
|
||||
backend: &mut BackendData,
|
||||
) -> Option<BufferConstraints> {
|
||||
let size = surface.geometry().size.to_buffer(1, Transform::Normal);
|
||||
let wl_surface = surface.wl_surface()?;
|
||||
|
||||
let mut _kms_renderer = None;
|
||||
let renderer = match backend {
|
||||
BackendData::Kms(ref mut kms) => {
|
||||
let dma_node = with_renderer_surface_state(&wl_surface, |state| {
|
||||
let buffer = state.buffer()?;
|
||||
let dmabuf = get_dmabuf(&*buffer).ok()?;
|
||||
dmabuf.node()
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let node = dma_node.unwrap_or(kms.primary_node);
|
||||
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
|
||||
_kms_renderer.as_mut().unwrap().as_mut()
|
||||
}
|
||||
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
|
||||
BackendData::X11(ref mut x11) => &mut x11.renderer,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Some(constraints_for_renderer(size, renderer))
|
||||
}
|
||||
|
||||
fn constraints_for_renderer(
|
||||
size: Size<i32, BufferCoords>,
|
||||
renderer: &mut GlowRenderer,
|
||||
) -> BufferConstraints {
|
||||
let mut constraints = BufferConstraints {
|
||||
size,
|
||||
shm: vec![ShmFormat::Abgr8888, ShmFormat::Xbgr8888],
|
||||
dma: None,
|
||||
};
|
||||
|
||||
if (renderer as &dyn Borrow<GlesRenderer>)
|
||||
.borrow()
|
||||
.capabilities()
|
||||
.contains(&Capability::ColorTransformations)
|
||||
{
|
||||
constraints
|
||||
.shm
|
||||
.extend([ShmFormat::Abgr2101010, ShmFormat::Xbgr2101010]);
|
||||
}
|
||||
|
||||
if let Some(node) = EGLDevice::device_for_display(renderer.egl_context().display())
|
||||
.ok()
|
||||
.and_then(|device| device.try_get_render_node().ok().flatten())
|
||||
{
|
||||
constraints.dma = Some(DmabufConstraints {
|
||||
node,
|
||||
formats: renderer
|
||||
.egl_context()
|
||||
.dmabuf_render_formats()
|
||||
.iter()
|
||||
.fold(
|
||||
HashMap::<Fourcc, Vec<Modifier>>::new(),
|
||||
|mut map, format| {
|
||||
map.entry(format.code).or_default().push(format.modifier);
|
||||
map
|
||||
},
|
||||
)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>(),
|
||||
});
|
||||
}
|
||||
|
||||
constraints
|
||||
}
|
||||
|
||||
delegate_screencopy!(State);
|
||||
864
src/wayland/handlers/screencopy/render.rs
Normal file
864
src/wayland/handlers/screencopy/render.rs
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
use smithay::{
|
||||
backend::{
|
||||
allocator::{dmabuf::Dmabuf, format::get_transparent, Buffer, Fourcc},
|
||||
renderer::{
|
||||
buffer_dimensions, buffer_type,
|
||||
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
|
||||
element::{
|
||||
surface::WaylandSurfaceRenderElement,
|
||||
utils::{Relocate, RelocateRenderElement},
|
||||
AsRenderElements, RenderElement,
|
||||
},
|
||||
gles::{GlesError, GlesRenderbuffer},
|
||||
sync::SyncPoint,
|
||||
utils::with_renderer_surface_state,
|
||||
Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
|
||||
},
|
||||
},
|
||||
desktop::space::SpaceElement,
|
||||
input::Seat,
|
||||
output::{Output, OutputNoMode},
|
||||
reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
|
||||
utils::{
|
||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size,
|
||||
Transform,
|
||||
},
|
||||
wayland::{
|
||||
dmabuf::get_dmabuf,
|
||||
seat::WaylandFocus,
|
||||
shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut},
|
||||
},
|
||||
};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::{
|
||||
backend::render::{
|
||||
cursor,
|
||||
element::{AsGlowRenderer, CosmicElement, DamageElement},
|
||||
render_workspace, CursorMode, CLEAR_COLOR,
|
||||
},
|
||||
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
|
||||
state::{BackendData, Common, State},
|
||||
utils::prelude::SeatExt,
|
||||
wayland::{
|
||||
handlers::screencopy::{
|
||||
constraints_for_output, constraints_for_toplevel, SessionData, SessionUserData,
|
||||
},
|
||||
protocols::{
|
||||
screencopy::{BufferConstraints, CursorSession, FailureReason, Frame, Session},
|
||||
workspace::WorkspaceHandle,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::data_device::get_dnd_icon;
|
||||
|
||||
pub fn submit_buffer<R>(
|
||||
frame: Frame,
|
||||
renderer: &mut R,
|
||||
transform: Transform,
|
||||
damage: Option<Vec<Rectangle<i32, Physical>>>,
|
||||
sync: SyncPoint,
|
||||
) -> Result<Option<(Frame, Vec<Rectangle<i32, BufferCoords>>)>, <R as Renderer>::Error>
|
||||
where
|
||||
R: ExportMem,
|
||||
{
|
||||
let Some(damage) = damage else {
|
||||
frame.success(
|
||||
transform,
|
||||
None,
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or(Duration::ZERO),
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let buffer = frame.buffer();
|
||||
if matches!(buffer_type(&buffer), Some(BufferType::Shm)) {
|
||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||
if let Err(err) = with_buffer_contents_mut(&buffer, |ptr, len, data| {
|
||||
let offset = data.offset as i32;
|
||||
let width = data.width as i32;
|
||||
let height = data.height as i32;
|
||||
let stride = data.stride as i32;
|
||||
let format = shm_format_to_fourcc(data.format)
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
|
||||
// number of bytes per pixel
|
||||
// TODO: compute from data.format
|
||||
let pixelsize = 4i32;
|
||||
|
||||
// ensure consistency, the SHM handler of smithay should ensure this
|
||||
assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= len);
|
||||
|
||||
// ensure rendering is done
|
||||
renderer.wait(&sync)?;
|
||||
|
||||
let format = get_transparent(format).unwrap_or(format);
|
||||
let mapping = renderer
|
||||
.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), buffer_size), format)?;
|
||||
let gl_data = renderer.map_texture(&mapping)?;
|
||||
assert!((width * height * pixelsize) as usize <= gl_data.len());
|
||||
|
||||
for i in 0..height {
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping::<u8>(
|
||||
gl_data.as_ptr().offset((width * pixelsize * i) as isize),
|
||||
ptr.offset((offset + stride * i) as isize),
|
||||
(width * pixelsize) as usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap()
|
||||
{
|
||||
frame.fail(FailureReason::Unknown);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some((
|
||||
frame,
|
||||
damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
let logical = rect.to_logical(1);
|
||||
logical.to_buffer(1, transform, &logical.size)
|
||||
})
|
||||
.collect(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn render_session<F, R>(
|
||||
renderer: &mut R,
|
||||
session: &SessionData,
|
||||
frame: Frame,
|
||||
transform: Transform,
|
||||
render_fn: F,
|
||||
) -> Result<Option<(Frame, Vec<Rectangle<i32, BufferCoords>>)>, DTError<R>>
|
||||
where
|
||||
R: ExportMem,
|
||||
F: FnOnce(
|
||||
&WlBuffer,
|
||||
&mut R,
|
||||
&mut OutputDamageTracker,
|
||||
usize,
|
||||
Vec<Rectangle<i32, BufferCoords>>,
|
||||
) -> Result<RenderOutputResult, DTError<R>>,
|
||||
{
|
||||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let mut session_damage_tracking = session.borrow_mut();
|
||||
|
||||
let buffer = frame.buffer();
|
||||
let age = session_damage_tracking.age_for_buffer(&buffer);
|
||||
let res = render_fn(
|
||||
&frame.buffer(),
|
||||
renderer,
|
||||
&mut session_damage_tracking.dt,
|
||||
age,
|
||||
frame.damage(),
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok(result) => submit_buffer(frame, renderer, transform, result.damage, result.sync)
|
||||
.map_err(DTError::Rendering),
|
||||
Err(err) => {
|
||||
frame.fail(FailureReason::Unknown);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_workspace_to_buffer(
|
||||
state: &mut State,
|
||||
session: Session,
|
||||
frame: Frame,
|
||||
handle: WorkspaceHandle,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let Some(workspace) = state.common.shell.workspaces.space_for_handle(&handle) else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
|
||||
let output = workspace.output().clone();
|
||||
let idx = state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.idx_for_handle(&output, &handle)
|
||||
.unwrap();
|
||||
let mode = output
|
||||
.current_mode()
|
||||
.map(|mode| mode.size.to_logical(1).to_buffer(1, Transform::Normal));
|
||||
|
||||
let buffer = frame.buffer();
|
||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||
if mode != Some(buffer_size) {
|
||||
let Some(constraints) = constraints_for_output(&output, &mut state.backend) else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
session.update_constraints(constraints);
|
||||
if let Some(data) = session.user_data().get::<SessionData>() {
|
||||
*data.borrow_mut() = SessionUserData::new(OutputDamageTracker::from_output(&output));
|
||||
}
|
||||
frame.fail(FailureReason::BufferConstraints);
|
||||
return;
|
||||
}
|
||||
|
||||
fn render_fn<R>(
|
||||
buffer: &WlBuffer,
|
||||
renderer: &mut R,
|
||||
dt: &mut OutputDamageTracker,
|
||||
age: usize,
|
||||
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
|
||||
draw_cursor: bool,
|
||||
common: &mut Common,
|
||||
output: &Output,
|
||||
handle: (WorkspaceHandle, usize),
|
||||
) -> Result<RenderOutputResult, DTError<R>>
|
||||
where
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
+ ImportMem
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<GlesRenderbuffer>
|
||||
+ Blit<Dmabuf>
|
||||
+ AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
<R as Renderer>::Error: From<GlesError>,
|
||||
CosmicElement<R>: RenderElement<R>,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
let cursor_mode = if draw_cursor {
|
||||
CursorMode::All
|
||||
} else {
|
||||
CursorMode::None
|
||||
};
|
||||
|
||||
let area = output
|
||||
.current_mode()
|
||||
.ok_or(DTError::OutputNoMode(OutputNoMode))
|
||||
.map(
|
||||
|mode| {
|
||||
mode.size
|
||||
.to_logical(1)
|
||||
.to_buffer(1, Transform::Normal)
|
||||
.to_f64()
|
||||
}, /* TODO: Mode is Buffer..., why is this Physical in the first place */
|
||||
)?;
|
||||
let additional_damage = (!additional_damage.is_empty()).then(|| {
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.map(|rect| {
|
||||
rect.to_f64()
|
||||
.to_logical(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&area,
|
||||
)
|
||||
.to_i32_round()
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
render_workspace::<_, _, GlesRenderbuffer>(
|
||||
None,
|
||||
renderer,
|
||||
dmabuf,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
common,
|
||||
&output,
|
||||
None,
|
||||
handle,
|
||||
cursor_mode,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.map(|res| res.0)
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let format =
|
||||
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
|
||||
.map_err(DTError::Rendering)?;
|
||||
render_workspace::<_, _, GlesRenderbuffer>(
|
||||
None,
|
||||
renderer,
|
||||
render_buffer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
common,
|
||||
&output,
|
||||
None,
|
||||
handle,
|
||||
cursor_mode,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.map(|res| res.0)
|
||||
}
|
||||
}
|
||||
|
||||
let draw_cursor = session.draw_cursor();
|
||||
let transform = output.current_transform();
|
||||
let common = &mut state.common;
|
||||
|
||||
let result = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
let render_node = kms
|
||||
.target_node_for_output(&output)
|
||||
.unwrap_or(kms.primary_node);
|
||||
let target_node = get_dmabuf(&buffer)
|
||||
.ok()
|
||||
.and_then(|dma| dma.node())
|
||||
.unwrap_or(render_node);
|
||||
|
||||
let buffer_format = match buffer_type(&buffer) {
|
||||
Some(BufferType::Dma) => Some(get_dmabuf(&buffer).unwrap().format().code),
|
||||
Some(BufferType::Shm) => {
|
||||
with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.unwrap()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let mut multirenderer = match kms.api.renderer(
|
||||
&render_node,
|
||||
&target_node,
|
||||
buffer_format.unwrap_or(Fourcc::Abgr8888),
|
||||
) {
|
||||
Ok(renderer) => renderer,
|
||||
Err(err) => {
|
||||
warn!(?err, "Couldn't use nodes for screencopy");
|
||||
frame.fail(FailureReason::Unknown);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match render_session::<_, _>(
|
||||
&mut multirenderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
transform,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
&output,
|
||||
(handle, idx),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
BackendData::Winit(winit) => match render_session::<_, _>(
|
||||
winit.backend.renderer(),
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
transform,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
&output,
|
||||
(handle, idx),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
BackendData::X11(x11) => match render_session::<_, _>(
|
||||
&mut x11.renderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
transform,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
&output,
|
||||
(handle, idx),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some((frame, damage)) = result {
|
||||
frame.success(transform, damage, common.clock.now())
|
||||
}
|
||||
}
|
||||
|
||||
smithay::render_elements! {
|
||||
pub WindowCaptureElement<R> where R: ImportAll + ImportMem;
|
||||
WaylandElement=WaylandSurfaceRenderElement<R>,
|
||||
CursorElement=RelocateRenderElement<cursor::CursorRenderElement<R>>,
|
||||
AdditionalDamage=DamageElement,
|
||||
}
|
||||
|
||||
pub fn render_window_to_buffer(
|
||||
state: &mut State,
|
||||
session: Session,
|
||||
frame: Frame,
|
||||
toplevel: &CosmicSurface,
|
||||
) {
|
||||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
if !toplevel.alive() {
|
||||
session.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer = frame.buffer();
|
||||
let geometry = toplevel.geometry();
|
||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||
if buffer_size != geometry.size.to_buffer(1, Transform::Normal) {
|
||||
let Some(constraints) = constraints_for_toplevel(toplevel, &mut state.backend) else {
|
||||
session.stop();
|
||||
return;
|
||||
};
|
||||
session.update_constraints(constraints);
|
||||
if let Some(data) = session.user_data().get::<SessionData>() {
|
||||
let size = geometry.size.to_physical(1);
|
||||
*data.borrow_mut() =
|
||||
SessionUserData::new(OutputDamageTracker::new(size, 1.0, Transform::Normal));
|
||||
}
|
||||
frame.fail(FailureReason::BufferConstraints);
|
||||
return;
|
||||
}
|
||||
|
||||
fn render_fn<R>(
|
||||
buffer: &WlBuffer,
|
||||
renderer: &mut R,
|
||||
dt: &mut OutputDamageTracker,
|
||||
age: usize,
|
||||
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
|
||||
draw_cursor: bool,
|
||||
common: &mut Common,
|
||||
window: &CosmicSurface,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
) -> Result<RenderOutputResult, DTError<R>>
|
||||
where
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
+ ImportMem
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<GlesRenderbuffer>
|
||||
+ Blit<Dmabuf>
|
||||
+ AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
<R as Renderer>::Error: From<GlesError>,
|
||||
CosmicElement<R>: RenderElement<R>,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
let mut elements = AsRenderElements::<R>::render_elements::<WindowCaptureElement<R>>(
|
||||
window,
|
||||
renderer,
|
||||
(-geometry.loc.x, -geometry.loc.y).into(),
|
||||
Scale::from(1.0),
|
||||
1.0,
|
||||
);
|
||||
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.filter_map(|rect| {
|
||||
let logical_rect = rect.to_logical(
|
||||
1,
|
||||
Transform::Normal,
|
||||
&geometry.size.to_buffer(1, Transform::Normal),
|
||||
);
|
||||
logical_rect.intersection(Rectangle::from_loc_and_size((0, 0), geometry.size))
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::<WindowCaptureElement<R>>::into),
|
||||
);
|
||||
|
||||
let seat = common.last_active_seat().clone();
|
||||
if let Some(location) = {
|
||||
if let Some(mapped) = common.shell.element_for_surface(window) {
|
||||
mapped.cursor_position(&seat).and_then(|mut p| {
|
||||
p -= mapped.active_window_offset().to_f64();
|
||||
if p.x < 0. || p.y < 0. {
|
||||
None
|
||||
} else {
|
||||
Some(p)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} {
|
||||
if draw_cursor {
|
||||
elements.extend(
|
||||
cursor::draw_cursor(
|
||||
renderer,
|
||||
&seat,
|
||||
location,
|
||||
1.0.into(),
|
||||
common.clock.now(),
|
||||
true,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(elem, hotspot)| {
|
||||
WindowCaptureElement::CursorElement(RelocateRenderElement::from_element(
|
||||
elem,
|
||||
Point::from((-hotspot.x, -hotspot.y)),
|
||||
Relocate::Relative,
|
||||
))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(wl_surface) = get_dnd_icon(&seat) {
|
||||
elements.extend(
|
||||
cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), 1.0)
|
||||
.into_iter()
|
||||
.map(WindowCaptureElement::from),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf).map_err(DTError::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let format =
|
||||
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
|
||||
.map_err(DTError::Rendering)?;
|
||||
renderer.bind(render_buffer).map_err(DTError::Rendering)?;
|
||||
}
|
||||
|
||||
dt.render_output(
|
||||
renderer,
|
||||
age,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
)
|
||||
}
|
||||
|
||||
let common = &mut state.common;
|
||||
let draw_cursor = session.draw_cursor();
|
||||
|
||||
let result = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
let node = get_dmabuf(&buffer)
|
||||
.ok()
|
||||
.and_then(|dmabuf| dmabuf.node())
|
||||
.or_else(|| {
|
||||
toplevel
|
||||
.wl_surface()
|
||||
.and_then(|wl_surface| {
|
||||
with_renderer_surface_state(&wl_surface, |state| {
|
||||
let buffer = state.buffer()?;
|
||||
let dmabuf = get_dmabuf(&*buffer).ok()?;
|
||||
dmabuf.node()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
.unwrap_or(kms.primary_node);
|
||||
|
||||
let mut multirenderer = match kms.api.single_renderer(&node) {
|
||||
Ok(renderer) => renderer,
|
||||
Err(err) => {
|
||||
warn!(?err, "Couldn't use node for screencopy");
|
||||
frame.fail(FailureReason::Unknown);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match render_session::<_, _>(
|
||||
&mut multirenderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
toplevel,
|
||||
geometry,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
BackendData::Winit(winit) => match render_session::<_, _>(
|
||||
winit.backend.renderer(),
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
toplevel,
|
||||
geometry,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
BackendData::X11(x11) => match render_session::<_, _>(
|
||||
&mut x11.renderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(
|
||||
buffer,
|
||||
renderer,
|
||||
dt,
|
||||
age,
|
||||
additional_damage,
|
||||
draw_cursor,
|
||||
common,
|
||||
toplevel,
|
||||
geometry,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some((frame, damage)) = result {
|
||||
frame.success(Transform::Normal, damage, common.clock.now())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_cursor_to_buffer(
|
||||
state: &mut State,
|
||||
session: &CursorSession,
|
||||
frame: Frame,
|
||||
seat: &Seat<State>,
|
||||
) {
|
||||
let buffer = frame.buffer();
|
||||
let cursor_size = seat
|
||||
.cursor_geometry((0.0, 0.0), state.common.clock.now())
|
||||
.map(|(geo, _hotspot)| geo.size)
|
||||
.unwrap_or_else(|| Size::from((64, 64)));
|
||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||
if buffer_size != cursor_size {
|
||||
let constraints = BufferConstraints {
|
||||
size: cursor_size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
};
|
||||
session.update_constraints(constraints);
|
||||
if let Some(data) = session.user_data().get::<SessionData>() {
|
||||
*data.borrow_mut() = SessionUserData::new(OutputDamageTracker::new(
|
||||
cursor_size.to_logical(1, Transform::Normal).to_physical(1),
|
||||
1.0,
|
||||
Transform::Normal,
|
||||
));
|
||||
}
|
||||
frame.fail(FailureReason::BufferConstraints);
|
||||
return;
|
||||
}
|
||||
|
||||
fn render_fn<R>(
|
||||
buffer: &WlBuffer,
|
||||
renderer: &mut R,
|
||||
dt: &mut OutputDamageTracker,
|
||||
age: usize,
|
||||
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
|
||||
common: &mut Common,
|
||||
seat: &Seat<State>,
|
||||
) -> Result<RenderOutputResult, DTError<R>>
|
||||
where
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
+ ImportMem
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<GlesRenderbuffer>
|
||||
+ Blit<Dmabuf>
|
||||
+ AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
<R as Renderer>::Error: From<GlesError>,
|
||||
CosmicElement<R>: RenderElement<R>,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
{
|
||||
let mut elements = cursor::draw_cursor(
|
||||
renderer,
|
||||
&seat,
|
||||
Point::from((0.0, 0.0)),
|
||||
1.0.into(),
|
||||
common.clock.now(),
|
||||
true,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative))
|
||||
.map(WindowCaptureElement::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
elements.extend(
|
||||
additional_damage
|
||||
.into_iter()
|
||||
.filter_map(|rect| {
|
||||
let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64)));
|
||||
logical_rect.intersection(Rectangle::from_loc_and_size((0, 0), (64, 64)))
|
||||
})
|
||||
.map(DamageElement::new)
|
||||
.map(Into::<WindowCaptureElement<R>>::into),
|
||||
);
|
||||
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf).map_err(DTError::Rendering)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let format =
|
||||
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
let render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
|
||||
.map_err(DTError::Rendering)?;
|
||||
renderer.bind(render_buffer).map_err(DTError::Rendering)?;
|
||||
}
|
||||
|
||||
dt.render_output(renderer, age, &elements, [0.0, 0.0, 0.0, 0.0])
|
||||
}
|
||||
|
||||
let common = &mut state.common;
|
||||
let result = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
let mut multirenderer = match kms.api.single_renderer(&kms.primary_node) {
|
||||
Ok(renderer) => renderer,
|
||||
Err(err) => {
|
||||
warn!(?err, "Couldn't use node for screencopy");
|
||||
frame.fail(FailureReason::Unknown);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match render_session::<_, _>(
|
||||
&mut multirenderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
BackendData::Winit(winit) => match render_session::<_, _>(
|
||||
winit.backend.renderer(),
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
BackendData::X11(x11) => match render_session::<_, _>(
|
||||
&mut x11.renderer,
|
||||
session.user_data().get::<SessionData>().unwrap(),
|
||||
frame,
|
||||
Transform::Normal,
|
||||
|buffer, renderer, dt, age, additional_damage| {
|
||||
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
|
||||
},
|
||||
) {
|
||||
Ok(frame) => frame,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to render to screencopy buffer");
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some((frame, damage)) = result {
|
||||
frame.success(Transform::Normal, damage, common.clock.now())
|
||||
}
|
||||
}
|
||||
357
src/wayland/handlers/screencopy/user_data.rs
Normal file
357
src/wayland/handlers/screencopy/user_data.rs
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{damage::OutputDamageTracker, utils::CommitCounter},
|
||||
output::Output,
|
||||
reexports::wayland_server::{protocol::wl_buffer::WlBuffer, Resource, Weak},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
shell::{CosmicSurface, Workspace},
|
||||
wayland::protocols::screencopy::{CursorSession, FailureReason, Frame, Session},
|
||||
};
|
||||
|
||||
type ScreencopySessionsData = RefCell<ScreencopySessions>;
|
||||
type PendingScreencopyBuffers = RefCell<Vec<(Session, DropableFrame)>>;
|
||||
|
||||
pub type SessionData = RefCell<SessionUserData>;
|
||||
|
||||
pub struct SessionUserData {
|
||||
pub dt: OutputDamageTracker,
|
||||
commit_counter: CommitCounter,
|
||||
buffer_age: HashMap<Weak<WlBuffer>, CommitCounter>,
|
||||
}
|
||||
|
||||
impl SessionUserData {
|
||||
pub fn new(tracker: OutputDamageTracker) -> SessionUserData {
|
||||
SessionUserData {
|
||||
dt: tracker,
|
||||
commit_counter: CommitCounter::default(),
|
||||
buffer_age: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn age_for_buffer(&mut self, buffer: &WlBuffer) -> usize {
|
||||
self.buffer_age.retain(|k, _| k.upgrade().is_ok());
|
||||
|
||||
let weak = buffer.downgrade();
|
||||
let age = self
|
||||
.commit_counter
|
||||
.distance(self.buffer_age.get(&weak).copied())
|
||||
.unwrap_or(0);
|
||||
self.buffer_age.insert(weak, self.commit_counter);
|
||||
|
||||
self.commit_counter.increment();
|
||||
age
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.commit_counter = CommitCounter::default();
|
||||
self.buffer_age.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ScreencopySessions {
|
||||
sessions: Vec<DropableSession>,
|
||||
cursor_sessions: Vec<DropableCursorSession>,
|
||||
}
|
||||
|
||||
pub trait SessionHolder {
|
||||
fn add_session(&mut self, session: Session);
|
||||
fn remove_session(&mut self, session: Session);
|
||||
fn sessions(&self) -> Vec<Session>;
|
||||
|
||||
fn add_cursor_session(&mut self, session: CursorSession);
|
||||
fn remove_cursor_session(&mut self, session: CursorSession);
|
||||
fn cursor_sessions(&self) -> Vec<CursorSession>;
|
||||
}
|
||||
|
||||
pub trait FrameHolder {
|
||||
fn add_frame(&mut self, session: Session, frame: Frame);
|
||||
fn remove_frame(&mut self, frame: &Frame);
|
||||
fn take_pending_frames(&self) -> Vec<(Session, Frame)>;
|
||||
}
|
||||
|
||||
impl SessionHolder for Output {
|
||||
fn add_session(&mut self, session: Session) {
|
||||
self.user_data()
|
||||
.insert_if_missing(ScreencopySessionsData::default);
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.sessions
|
||||
.push(DropableSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_session(&mut self, session: Session) {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.sessions
|
||||
.retain(|s| *s != session);
|
||||
}
|
||||
|
||||
fn sessions(&self) -> Vec<Session> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions
|
||||
.borrow()
|
||||
.sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn add_cursor_session(&mut self, session: CursorSession) {
|
||||
self.user_data()
|
||||
.insert_if_missing(ScreencopySessionsData::default);
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.cursor_sessions
|
||||
.push(DropableCursorSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_cursor_session(&mut self, session: CursorSession) {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.cursor_sessions
|
||||
.retain(|s| *s != session);
|
||||
}
|
||||
|
||||
fn cursor_sessions(&self) -> Vec<CursorSession> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions
|
||||
.borrow()
|
||||
.cursor_sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameHolder for Output {
|
||||
fn add_frame(&mut self, session: Session, frame: Frame) {
|
||||
self.user_data()
|
||||
.insert_if_missing(PendingScreencopyBuffers::default);
|
||||
self.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.push((session, DropableFrame(Some(frame))));
|
||||
}
|
||||
fn remove_frame(&mut self, frame: &Frame) {
|
||||
if let Some(pending) = self.user_data().get::<PendingScreencopyBuffers>() {
|
||||
pending.borrow_mut().retain(|(_, f)| f != frame);
|
||||
}
|
||||
}
|
||||
fn take_pending_frames(&self) -> Vec<(Session, Frame)> {
|
||||
self.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.map(|pending| {
|
||||
pending
|
||||
.borrow_mut()
|
||||
.split_off(0)
|
||||
.into_iter()
|
||||
.map(|(s, mut f)| (s, f.0.take().unwrap()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionHolder for Workspace {
|
||||
fn add_session(&mut self, session: Session) {
|
||||
self.screencopy
|
||||
.sessions
|
||||
.push(DropableSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_session(&mut self, session: Session) {
|
||||
self.screencopy.sessions.retain(|s| *s != session);
|
||||
}
|
||||
fn sessions(&self) -> Vec<Session> {
|
||||
self.screencopy
|
||||
.sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn add_cursor_session(&mut self, session: CursorSession) {
|
||||
self.screencopy
|
||||
.cursor_sessions
|
||||
.push(DropableCursorSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_cursor_session(&mut self, session: CursorSession) {
|
||||
self.screencopy.cursor_sessions.retain(|s| *s != session);
|
||||
}
|
||||
fn cursor_sessions(&self) -> Vec<CursorSession> {
|
||||
self.screencopy
|
||||
.cursor_sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionHolder for CosmicSurface {
|
||||
fn add_session(&mut self, session: Session) {
|
||||
self.user_data()
|
||||
.insert_if_missing(ScreencopySessionsData::default);
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.sessions
|
||||
.push(DropableSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_session(&mut self, session: Session) {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.sessions
|
||||
.retain(|s| *s != session);
|
||||
}
|
||||
fn sessions(&self) -> Vec<Session> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions
|
||||
.borrow()
|
||||
.sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn add_cursor_session(&mut self, session: CursorSession) {
|
||||
self.user_data()
|
||||
.insert_if_missing(ScreencopySessionsData::default);
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.cursor_sessions
|
||||
.push(DropableCursorSession(Some(session)));
|
||||
}
|
||||
|
||||
fn remove_cursor_session(&mut self, session: CursorSession) {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.cursor_sessions
|
||||
.retain(|s| *s != session);
|
||||
}
|
||||
|
||||
fn cursor_sessions(&self) -> Vec<CursorSession> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessionsData>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions
|
||||
.borrow()
|
||||
.cursor_sessions
|
||||
.iter()
|
||||
.flat_map(|s| s.0.clone())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DropableSession(Option<Session>);
|
||||
impl Deref for DropableSession {
|
||||
type Target = Session;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
impl DerefMut for DropableSession {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
impl PartialEq<Session> for DropableSession {
|
||||
fn eq(&self, other: &Session) -> bool {
|
||||
self.0.as_ref().map(|s| s == other).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
impl Drop for DropableSession {
|
||||
fn drop(&mut self) {
|
||||
if let Some(s) = self.0.take() {
|
||||
s.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DropableCursorSession(Option<CursorSession>);
|
||||
impl Deref for DropableCursorSession {
|
||||
type Target = CursorSession;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
impl DerefMut for DropableCursorSession {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
impl PartialEq<CursorSession> for DropableCursorSession {
|
||||
fn eq(&self, other: &CursorSession) -> bool {
|
||||
self.0.as_ref().map(|s| s == other).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
impl Drop for DropableCursorSession {
|
||||
fn drop(&mut self) {
|
||||
if let Some(s) = self.0.take() {
|
||||
s.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DropableFrame(Option<Frame>);
|
||||
impl Deref for DropableFrame {
|
||||
type Target = Frame;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
impl DerefMut for DropableFrame {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
impl PartialEq<Frame> for DropableFrame {
|
||||
fn eq(&self, other: &Frame) -> bool {
|
||||
self.0.as_ref().map(|f| f == other).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
impl Drop for DropableFrame {
|
||||
fn drop(&mut self) {
|
||||
if let Some(f) = self.0.take() {
|
||||
f.fail(FailureReason::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ impl SessionLockHandler for State {
|
|||
|
||||
for output in self.common.shell.outputs() {
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output, None);
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ impl SessionLockHandler for State {
|
|||
|
||||
for output in self.common.shell.outputs() {
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output, None);
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use crate::{
|
||||
shell::{element::CosmicWindow, grabs::ReleaseMode, CosmicMapped, CosmicSurface, ManagedLayer},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::SessionType,
|
||||
};
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
|
|
@ -30,10 +29,7 @@ use smithay::{
|
|||
use std::cell::Cell;
|
||||
use tracing::warn;
|
||||
|
||||
use super::{
|
||||
compositor::client_compositor_state, screencopy::PendingScreencopyBuffers,
|
||||
toplevel_management::ToplevelManagementExt,
|
||||
};
|
||||
use super::{compositor::client_compositor_state, toplevel_management::ToplevelManagementExt};
|
||||
|
||||
pub mod popup;
|
||||
|
||||
|
|
@ -403,32 +399,9 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
// screencopy
|
||||
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
|
||||
if let Some(output) = output.as_ref() {
|
||||
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(sessions.borrow_mut().drain(..));
|
||||
}
|
||||
self.backend.schedule_render(
|
||||
&self.common.event_loop_handle,
|
||||
&output,
|
||||
scheduled_sessions.as_ref().map(|sessions| {
|
||||
sessions
|
||||
.iter()
|
||||
.filter(|(s, _)| match s.session_type() {
|
||||
SessionType::Output(o) | SessionType::Workspace(o, _)
|
||||
if &o == output =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
);
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
343
src/wayland/protocols/image_source.rs
Normal file
343
src/wayland/protocols/image_source.rs
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
use super::{
|
||||
toplevel_info::window_from_handle,
|
||||
workspace::{WorkspaceHandle, WorkspaceHandler},
|
||||
};
|
||||
use crate::shell::CosmicSurface;
|
||||
use cosmic_protocols::image_source::v1::server::{
|
||||
zcosmic_image_source_v1::ZcosmicImageSourceV1,
|
||||
zcosmic_output_image_source_manager_v1::{
|
||||
Request as OutputSourceRequest, ZcosmicOutputImageSourceManagerV1,
|
||||
},
|
||||
zcosmic_toplevel_image_source_manager_v1::{
|
||||
Request as ToplevelSourceRequest, ZcosmicToplevelImageSourceManagerV1,
|
||||
},
|
||||
zcosmic_workspace_image_source_manager_v1::{
|
||||
Request as WorkspaceSourceRequest, ZcosmicWorkspaceImageSourceManagerV1,
|
||||
},
|
||||
};
|
||||
use smithay::{
|
||||
output::{Output, WeakOutput},
|
||||
reexports::wayland_server::{
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
},
|
||||
};
|
||||
use wayland_backend::server::GlobalId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImageSourceState {
|
||||
output_source_global: GlobalId,
|
||||
workspace_source_global: GlobalId,
|
||||
toplevel_source_global: GlobalId,
|
||||
}
|
||||
|
||||
pub struct OutputImageSourceManagerGlobalData {
|
||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||
}
|
||||
pub struct WorkspaceImageSourceManagerGlobalData {
|
||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||
}
|
||||
pub struct ToplevelImageSourceManagerGlobalData {
|
||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ImageSourceData {
|
||||
Output(WeakOutput),
|
||||
Workspace(WorkspaceHandle),
|
||||
Toplevel(CosmicSurface),
|
||||
Destroyed,
|
||||
}
|
||||
|
||||
impl ImageSourceState {
|
||||
pub fn new<D, F>(display: &DisplayHandle, client_filter: F) -> ImageSourceState
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData>
|
||||
+ Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
|
||||
+ GlobalDispatch<
|
||||
ZcosmicWorkspaceImageSourceManagerV1,
|
||||
WorkspaceImageSourceManagerGlobalData,
|
||||
> + Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
|
||||
+ GlobalDispatch<
|
||||
ZcosmicToplevelImageSourceManagerV1,
|
||||
ToplevelImageSourceManagerGlobalData,
|
||||
> + Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ WorkspaceHandler
|
||||
+ 'static,
|
||||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static,
|
||||
{
|
||||
ImageSourceState {
|
||||
output_source_global: display.create_global::<D, ZcosmicOutputImageSourceManagerV1, _>(
|
||||
1,
|
||||
OutputImageSourceManagerGlobalData {
|
||||
filter: Box::new(client_filter.clone()),
|
||||
},
|
||||
),
|
||||
workspace_source_global: display
|
||||
.create_global::<D, ZcosmicWorkspaceImageSourceManagerV1, _>(
|
||||
1,
|
||||
WorkspaceImageSourceManagerGlobalData {
|
||||
filter: Box::new(client_filter.clone()),
|
||||
},
|
||||
),
|
||||
toplevel_source_global: display
|
||||
.create_global::<D, ZcosmicToplevelImageSourceManagerV1, _>(
|
||||
1,
|
||||
ToplevelImageSourceManagerGlobalData {
|
||||
filter: Box::new(client_filter),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_source_id(&self) -> &GlobalId {
|
||||
&self.output_source_global
|
||||
}
|
||||
|
||||
pub fn workspace_source_id(&self) -> &GlobalId {
|
||||
&self.workspace_source_global
|
||||
}
|
||||
|
||||
pub fn toplevel_source_id(&self) -> &GlobalId {
|
||||
&self.toplevel_source_global
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData, D>
|
||||
for ImageSourceState
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData>
|
||||
+ Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ 'static,
|
||||
{
|
||||
fn bind(
|
||||
_state: &mut D,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<ZcosmicOutputImageSourceManagerV1>,
|
||||
_global_data: &OutputImageSourceManagerGlobalData,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
|
||||
fn can_view(client: Client, global_data: &OutputImageSourceManagerGlobalData) -> bool {
|
||||
(global_data.filter)(&client)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D>
|
||||
GlobalDispatch<ZcosmicWorkspaceImageSourceManagerV1, WorkspaceImageSourceManagerGlobalData, D>
|
||||
for ImageSourceState
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicWorkspaceImageSourceManagerV1, WorkspaceImageSourceManagerGlobalData>
|
||||
+ Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ 'static,
|
||||
{
|
||||
fn bind(
|
||||
_state: &mut D,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<ZcosmicWorkspaceImageSourceManagerV1>,
|
||||
_global_data: &WorkspaceImageSourceManagerGlobalData,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
|
||||
fn can_view(client: Client, global_data: &WorkspaceImageSourceManagerGlobalData) -> bool {
|
||||
(global_data.filter)(&client)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> GlobalDispatch<ZcosmicToplevelImageSourceManagerV1, ToplevelImageSourceManagerGlobalData, D>
|
||||
for ImageSourceState
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicToplevelImageSourceManagerV1, ToplevelImageSourceManagerGlobalData>
|
||||
+ Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ 'static,
|
||||
{
|
||||
fn bind(
|
||||
_state: &mut D,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<ZcosmicToplevelImageSourceManagerV1>,
|
||||
_global_data: &ToplevelImageSourceManagerGlobalData,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
|
||||
fn can_view(client: Client, global_data: &ToplevelImageSourceManagerGlobalData) -> bool {
|
||||
(global_data.filter)(&client)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZcosmicOutputImageSourceManagerV1, (), D> for ImageSourceState
|
||||
where
|
||||
D: Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ 'static,
|
||||
{
|
||||
fn request(
|
||||
_state: &mut D,
|
||||
_client: &Client,
|
||||
_resource: &ZcosmicOutputImageSourceManagerV1,
|
||||
request: <ZcosmicOutputImageSourceManagerV1 as Resource>::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
OutputSourceRequest::CreateSource { source, output } => {
|
||||
let data = match Output::from_resource(&output) {
|
||||
Some(output) => ImageSourceData::Output(output.downgrade()),
|
||||
None => ImageSourceData::Destroyed,
|
||||
};
|
||||
data_init.init(source, data);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut D,
|
||||
_client: wayland_backend::server::ClientId,
|
||||
_resource: &ZcosmicOutputImageSourceManagerV1,
|
||||
_data: &(),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZcosmicWorkspaceImageSourceManagerV1, (), D> for ImageSourceState
|
||||
where
|
||||
D: Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ WorkspaceHandler
|
||||
+ 'static,
|
||||
{
|
||||
fn request(
|
||||
state: &mut D,
|
||||
_client: &Client,
|
||||
_resource: &ZcosmicWorkspaceImageSourceManagerV1,
|
||||
request: <ZcosmicWorkspaceImageSourceManagerV1 as Resource>::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
WorkspaceSourceRequest::CreateSource { source, output } => {
|
||||
let data = match state.workspace_state().workspace_handle(&output) {
|
||||
Some(workspace) => ImageSourceData::Workspace(workspace),
|
||||
None => ImageSourceData::Destroyed,
|
||||
};
|
||||
data_init.init(source, data);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut D,
|
||||
_client: wayland_backend::server::ClientId,
|
||||
_resource: &ZcosmicWorkspaceImageSourceManagerV1,
|
||||
_data: &(),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZcosmicToplevelImageSourceManagerV1, (), D> for ImageSourceState
|
||||
where
|
||||
D: Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
|
||||
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
|
||||
+ 'static,
|
||||
{
|
||||
fn request(
|
||||
_state: &mut D,
|
||||
_client: &Client,
|
||||
_resource: &ZcosmicToplevelImageSourceManagerV1,
|
||||
request: <ZcosmicToplevelImageSourceManagerV1 as Resource>::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
ToplevelSourceRequest::CreateSource {
|
||||
source,
|
||||
toplevel_handle,
|
||||
} => {
|
||||
let data = match window_from_handle(toplevel_handle) {
|
||||
Some(toplevel) => ImageSourceData::Toplevel(toplevel),
|
||||
None => ImageSourceData::Destroyed,
|
||||
};
|
||||
data_init.init(source, data);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut D,
|
||||
_client: wayland_backend::server::ClientId,
|
||||
_resource: &ZcosmicToplevelImageSourceManagerV1,
|
||||
_data: &(),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZcosmicImageSourceV1, ImageSourceData, D> for ImageSourceState
|
||||
where
|
||||
D: Dispatch<ZcosmicImageSourceV1, ImageSourceData> + 'static,
|
||||
{
|
||||
fn request(
|
||||
_state: &mut D,
|
||||
_client: &Client,
|
||||
_resource: &ZcosmicImageSourceV1,
|
||||
request: <ZcosmicImageSourceV1 as Resource>::Request,
|
||||
_data: &ImageSourceData,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut D,
|
||||
_client: wayland_backend::server::ClientId,
|
||||
_resource: &ZcosmicImageSourceV1,
|
||||
_data: &ImageSourceData,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! delegate_image_source {
|
||||
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
|
||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_output_image_source_manager_v1::ZcosmicOutputImageSourceManagerV1: $crate::wayland::protocols::image_source::OutputImageSourceManagerGlobalData
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_output_image_source_manager_v1::ZcosmicOutputImageSourceManagerV1: ()
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_workspace_image_source_manager_v1::ZcosmicWorkspaceImageSourceManagerV1: $crate::wayland::protocols::image_source::WorkspaceImageSourceManagerGlobalData
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_workspace_image_source_manager_v1::ZcosmicWorkspaceImageSourceManagerV1: ()
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_toplevel_image_source_manager_v1::ZcosmicToplevelImageSourceManagerV1: $crate::wayland::protocols::image_source::ToplevelImageSourceManagerGlobalData
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_toplevel_image_source_manager_v1::ZcosmicToplevelImageSourceManagerV1: ()
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||
cosmic_protocols::image_source::v1::server::zcosmic_image_source_v1::ZcosmicImageSourceV1: $crate::wayland::protocols::image_source::ImageSourceData
|
||||
] => $crate::wayland::protocols::image_source::ImageSourceState);
|
||||
};
|
||||
}
|
||||
pub(crate) use delegate_image_source;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod drm;
|
||||
//pub mod export_dmabuf;
|
||||
pub mod image_source;
|
||||
pub mod output_configuration;
|
||||
pub mod screencopy;
|
||||
pub mod toplevel_info;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,12 +5,8 @@ use crate::{
|
|||
shell::{focus::target::KeyboardFocusTarget, grabs::ReleaseMode, CosmicSurface, Shell},
|
||||
state::State,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::{
|
||||
screencopy::PendingScreencopyBuffers, toplevel_management::ToplevelManagementExt,
|
||||
xdg_activation::ActivationContext,
|
||||
},
|
||||
protocols::screencopy::SessionType,
|
||||
wayland::handlers::{
|
||||
toplevel_management::ToplevelManagementExt, xdg_activation::ActivationContext,
|
||||
},
|
||||
};
|
||||
use smithay::{
|
||||
|
|
@ -354,36 +350,9 @@ impl XwmHandler for State {
|
|||
self.common.shell.refresh_active_space(output);
|
||||
}
|
||||
|
||||
// screencopy
|
||||
let mut scheduled_sessions = window
|
||||
.wl_surface()
|
||||
.map(|wl_surface| self.schedule_workspace_sessions(&wl_surface))
|
||||
.unwrap_or_default();
|
||||
|
||||
for output in outputs.into_iter() {
|
||||
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.extend(sessions.borrow_mut().drain(..));
|
||||
}
|
||||
self.backend.schedule_render(
|
||||
&self.common.event_loop_handle,
|
||||
&output,
|
||||
scheduled_sessions.as_ref().map(|sessions| {
|
||||
sessions
|
||||
.iter()
|
||||
.filter(|(s, _)| match s.session_type() {
|
||||
SessionType::Output(o) | SessionType::Workspace(o, _)
|
||||
if o == output =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
);
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue