wip: screencopy
This commit is contained in:
parent
dd100d65e4
commit
5a4df346a8
26 changed files with 2046 additions and 327 deletions
36
Cargo.lock
generated
36
Cargo.lock
generated
|
|
@ -309,7 +309,7 @@ dependencies = [
|
|||
"smithay-egui",
|
||||
"thiserror",
|
||||
"wayland-backend",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xcursor",
|
||||
"xdg",
|
||||
"xkbcommon 0.4.1",
|
||||
|
|
@ -323,7 +323,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"wayland-backend",
|
||||
"wayland-protocols 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wayland-server",
|
||||
]
|
||||
|
||||
|
|
@ -1503,7 +1503,6 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
|||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/Smithay//smithay?rev=0c7dbfa8#0c7dbfa830496df064a721f9cbbff6b474e6a268"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags",
|
||||
|
|
@ -1807,8 +1806,7 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
|||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.1.0-beta.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fe32234ad38fee0755aeec26db486a4b788dd3cc2c9dc86ceda31bb38a53f32"
|
||||
source = "git+https://github.com/Smithay/wayland-rs?rev=13f6a9be#13f6a9beb05eabac56aa372e1d38b12bfa987982"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
|
|
@ -1861,8 +1859,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wayland-egl"
|
||||
version = "0.30.0-beta.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfc1309ddfeabd942c09c21e9db2ed3de81b84d0ea7ebcdd2e503a0be0fe9c5f"
|
||||
source = "git+https://github.com/Smithay/wayland-rs?rev=13f6a9be#13f6a9beb05eabac56aa372e1d38b12bfa987982"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wayland-backend",
|
||||
|
|
@ -1889,7 +1886,7 @@ checksum = "ca770dc814b3c93db1b4ba12a5bdfe899f8d68f9b4b06fc31e53959261cd0c39"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"wayland-backend",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wayland-server",
|
||||
]
|
||||
|
||||
|
|
@ -1902,7 +1899,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"wayland-backend",
|
||||
"wayland-protocols 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wayland-server",
|
||||
]
|
||||
|
||||
|
|
@ -1915,7 +1912,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"wayland-backend",
|
||||
"wayland-protocols 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wayland-server",
|
||||
]
|
||||
|
||||
|
|
@ -1942,18 +1939,28 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.30.0-beta.12"
|
||||
source = "git+https://github.com/Smithay/wayland-rs?rev=13f6a9be#13f6a9beb05eabac56aa372e1d38b12bfa987982"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-server"
|
||||
version = "0.30.0-beta.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d54b4a800b230f4fb0f42cae245cc0aaa02bf14bf7c1c3a5e1a822d05fd2cb3"
|
||||
source = "git+https://github.com/Smithay/wayland-rs?rev=13f6a9be#13f6a9beb05eabac56aa372e1d38b12bfa987982"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"downcast-rs",
|
||||
"nix 0.25.0",
|
||||
"thiserror",
|
||||
"wayland-backend",
|
||||
"wayland-scanner 0.30.0-beta.12",
|
||||
"wayland-scanner 0.30.0-beta.12 (git+https://github.com/Smithay/wayland-rs?rev=13f6a9be)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1970,8 +1977,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.30.0-beta.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1117fe4570fe063122ba2b1b1e39e56fb1a73921d395f9288af06af0dd1c7f55"
|
||||
source = "git+https://github.com/Smithay/wayland-rs?rev=13f6a9be#13f6a9beb05eabac56aa372e1d38b12bfa987982"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"libc",
|
||||
|
|
|
|||
|
|
@ -61,4 +61,11 @@ debug = true
|
|||
lto = "fat"
|
||||
|
||||
[patch."https://github.com/Smithay/smithay.git"]
|
||||
smithay = { git = "https://github.com/Smithay//smithay", rev = "0c7dbfa8" }
|
||||
#smithay = { git = "https://github.com/Smithay//smithay", rev = "5671502cd" }
|
||||
smithay = { path = "../smithay" }
|
||||
|
||||
[patch.crates-io]
|
||||
wayland-server = { git = "https://github.com/Smithay/wayland-rs", rev = "13f6a9be" }
|
||||
wayland-backend = { git = "https://github.com/Smithay/wayland-rs", rev = "13f6a9be" }
|
||||
wayland-egl = { git = "https://github.com/Smithay/wayland-rs", rev = "13f6a9be" }
|
||||
wayland-sys = { git = "https://github.com/Smithay/wayland-rs", rev = "13f6a9be" }
|
||||
|
|
|
|||
|
|
@ -9,18 +9,23 @@ use crate::{
|
|||
shell::Shell,
|
||||
state::{BackendData, ClientState, Common, Data},
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::screencopy::UserdataExt,
|
||||
protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::{dmabuf::Dmabuf, gbm::GbmDevice, Format},
|
||||
drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType},
|
||||
drm::{DrmDevice, DrmEvent, DrmNode, GbmBufferedSurface, NodeType},
|
||||
egl::{EGLContext, EGLDevice, EGLDisplay},
|
||||
input::InputEvent,
|
||||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||
renderer::{
|
||||
damage::DamageTrackedRenderer,
|
||||
element::RenderElementStates,
|
||||
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
||||
multigpu::{egl::EglGlesBackend, GpuManager},
|
||||
Bind,
|
||||
|
|
@ -61,7 +66,7 @@ mod socket;
|
|||
use session_fd::*;
|
||||
use socket::*;
|
||||
|
||||
use super::render::GlMultiRenderer;
|
||||
use super::render::{CursorMode, GlMultiRenderer};
|
||||
|
||||
pub struct KmsState {
|
||||
devices: HashMap<DrmNode, Device>,
|
||||
|
|
@ -89,7 +94,7 @@ pub struct Surface {
|
|||
damage_tracker: DamageTrackedRenderer,
|
||||
connector: connector::Handle,
|
||||
output: Output,
|
||||
last_submit: Option<DrmEventTime>,
|
||||
last_submit: Option<RenderElementStates>,
|
||||
refresh_rate: u32,
|
||||
vrr: bool,
|
||||
pending: bool,
|
||||
|
|
@ -123,12 +128,11 @@ pub fn init_backend(
|
|||
}
|
||||
data.state.process_input_event(event);
|
||||
for output in data.state.common.shell.outputs() {
|
||||
if let Err(err) = data
|
||||
.state
|
||||
.backend
|
||||
.kms()
|
||||
.schedule_render(&data.state.common.event_loop_handle, output)
|
||||
{
|
||||
if let Err(err) = data.state.backend.kms().schedule_render(
|
||||
&data.state.common.event_loop_handle,
|
||||
output,
|
||||
None,
|
||||
) {
|
||||
slog_scope::crit!(
|
||||
"Error scheduling event loop for output {}: {:?}",
|
||||
output.name(),
|
||||
|
|
@ -267,12 +271,16 @@ pub fn init_backend(
|
|||
surface.pending = false;
|
||||
}
|
||||
for output in data.state.common.shell.outputs() {
|
||||
if let Err(err) = data
|
||||
.state
|
||||
.backend
|
||||
.kms()
|
||||
.schedule_render(&data.state.common.event_loop_handle, output)
|
||||
{
|
||||
let sessions = output.pending_buffers().collect::<Vec<_>>();
|
||||
if let Err(err) = data.state.backend.kms().schedule_render(
|
||||
&data.state.common.event_loop_handle,
|
||||
output,
|
||||
if !sessions.is_empty() {
|
||||
Some(sessions)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
) {
|
||||
slog_scope::crit!(
|
||||
"Error scheduling event loop for output {}: {:?}",
|
||||
output.name(),
|
||||
|
|
@ -372,9 +380,12 @@ impl State {
|
|||
if let Some(surface) = device.surfaces.get_mut(&crtc) {
|
||||
match surface.surface.as_mut().map(|x| x.frame_submitted()) {
|
||||
Some(Ok(_)) => {
|
||||
surface.last_submit = metadata.take().map(|data| data.time);
|
||||
let _submit_time = metadata.take().map(|data| data.time);
|
||||
surface.pending = false;
|
||||
data.state.common.send_frames(&surface.output);
|
||||
data.state.common.send_frames(
|
||||
&surface.output,
|
||||
&surface.last_submit.take().unwrap(),
|
||||
);
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
slog_scope::warn!("Failed to submit frame: {}", err)
|
||||
|
|
@ -749,6 +760,7 @@ impl Surface {
|
|||
api: &mut GpuManager<EglGlesBackend<Gles2Renderer>>,
|
||||
target_node: &DrmNode,
|
||||
state: &mut Common,
|
||||
screencopy: Option<&[(ScreencopySession, BufferParams)]>,
|
||||
) -> Result<()> {
|
||||
if self.surface.is_none() {
|
||||
return Ok(());
|
||||
|
|
@ -766,18 +778,20 @@ impl Surface {
|
|||
.bind(buffer.clone())
|
||||
.with_context(|| "Failed to bind buffer")?;
|
||||
|
||||
match render::render_output(
|
||||
match render::render_output::<_, Gles2Renderbuffer, _>(
|
||||
Some(&render_node),
|
||||
&mut renderer,
|
||||
&mut self.damage_tracker,
|
||||
age as usize,
|
||||
state,
|
||||
&self.output,
|
||||
false,
|
||||
CursorMode::All,
|
||||
screencopy.map(|sessions| (buffer, sessions)),
|
||||
#[cfg(feature = "debug")]
|
||||
Some(&mut self.fps),
|
||||
) {
|
||||
Ok(_) => {
|
||||
Ok((_damage, states)) => {
|
||||
self.last_submit = Some(states);
|
||||
surface
|
||||
.queue_buffer()
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
|
|
@ -893,7 +907,16 @@ impl KmsState {
|
|||
|
||||
shell.refresh_outputs();
|
||||
if recreated {
|
||||
if let Err(err) = self.schedule_render(loop_handle, output) {
|
||||
let sessions = output.pending_buffers().collect::<Vec<_>>();
|
||||
if let Err(err) = self.schedule_render(
|
||||
loop_handle,
|
||||
output,
|
||||
if !sessions.is_empty() {
|
||||
Some(sessions)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
) {
|
||||
slog_scope::crit!(
|
||||
"Error scheduling event loop for output {}: {:?}",
|
||||
output.name(),
|
||||
|
|
@ -956,6 +979,7 @@ impl KmsState {
|
|||
&mut self,
|
||||
loop_handle: &LoopHandle<'_, Data>,
|
||||
output: &Output,
|
||||
mut screencopy_sessions: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) -> Result<(), InsertError<Timer>> {
|
||||
if let Some((device, crtc, surface)) = self
|
||||
.devices
|
||||
|
|
@ -1001,7 +1025,13 @@ impl KmsState {
|
|||
&mut backend.api,
|
||||
&device.render_node,
|
||||
&mut data.state.common,
|
||||
screencopy_sessions.as_deref(),
|
||||
) {
|
||||
if let Some(sessions) = screencopy_sessions.as_mut() {
|
||||
for (session, params) in sessions.drain(..) {
|
||||
data.state.common.still_pending(session, params);
|
||||
}
|
||||
}
|
||||
if backend.session.is_active() {
|
||||
slog_scope::error!("Error rendering: {}", err);
|
||||
return TimeoutAction::ToDuration(Duration::from_secs_f64(
|
||||
|
|
|
|||
|
|
@ -172,8 +172,8 @@ pub fn draw_dnd_icon<R: Renderer + ImportAll>(
|
|||
)
|
||||
}
|
||||
|
||||
struct CursorState {
|
||||
cursor: Cursor,
|
||||
pub struct CursorState {
|
||||
pub cursor: Cursor,
|
||||
current_image: RefCell<Option<Image>>,
|
||||
image_cache: RefCell<HashMap<(TypeId, usize), Vec<(Image, Box<dyn Any + 'static>)>>>,
|
||||
}
|
||||
|
|
@ -219,7 +219,7 @@ where
|
|||
|
||||
if let CursorImageStatus::Surface(ref wl_surface) = cursor_status {
|
||||
return draw_surface_cursor(wl_surface, location.to_i32_round(), scale);
|
||||
} else if draw_default {
|
||||
} else if draw_default && CursorImageStatus::Default == cursor_status {
|
||||
let integer_scale = scale.x.max(scale.y).ceil() as u32;
|
||||
|
||||
let seat_userdata = seat.user_data();
|
||||
|
|
|
|||
|
|
@ -11,19 +11,28 @@ use crate::{
|
|||
layout::floating::SeatMoveGrabState, CosmicMappedRenderElement, WorkspaceRenderElement,
|
||||
},
|
||||
state::Common,
|
||||
wayland::handlers::data_device::get_dnd_icon,
|
||||
wayland::{
|
||||
handlers::{data_device::get_dnd_icon, screencopy::render_to_buffer},
|
||||
protocols::{
|
||||
screencopy::{BufferParams, Session as ScreencopySession},
|
||||
workspace::WorkspaceHandle,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::dmabuf::Dmabuf,
|
||||
drm::DrmNode,
|
||||
renderer::{
|
||||
damage::{
|
||||
DamageTrackedRenderer, DamageTrackedRendererError as RenderError, OutputNoMode,
|
||||
},
|
||||
element::RenderElementStates,
|
||||
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
||||
multigpu::{egl::EglGlesBackend, MultiFrame, MultiRenderer},
|
||||
ImportAll, ImportMem, Renderer,
|
||||
Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter,
|
||||
},
|
||||
},
|
||||
output::Output,
|
||||
|
|
@ -42,7 +51,7 @@ pub type GlMultiRenderer<'a> = MultiRenderer<
|
|||
>;
|
||||
pub type GlMultiFrame = MultiFrame<EglGlesBackend<Gles2Renderer>, EglGlesBackend<Gles2Renderer>>;
|
||||
|
||||
static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
|
||||
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
|
||||
|
||||
smithay::render_elements! {
|
||||
pub CosmicElement<R> where R: ImportAll;
|
||||
|
|
@ -53,11 +62,18 @@ smithay::render_elements! {
|
|||
//EguiFrame=EguiFrame,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CursorMode {
|
||||
None,
|
||||
NotDefault,
|
||||
All,
|
||||
}
|
||||
|
||||
pub fn cursor_elements<E, R>(
|
||||
renderer: &mut R,
|
||||
state: &Common,
|
||||
output: &Output,
|
||||
hardware_cursor: bool,
|
||||
mode: CursorMode,
|
||||
) -> Vec<E>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem,
|
||||
|
|
@ -76,6 +92,7 @@ where
|
|||
.shell
|
||||
.map_global_to_space(pointer.current_location().to_i32_round(), output);
|
||||
|
||||
if mode != CursorMode::None {
|
||||
elements.extend(
|
||||
cursor::draw_cursor(
|
||||
renderer,
|
||||
|
|
@ -83,11 +100,12 @@ where
|
|||
location,
|
||||
scale.into(),
|
||||
&state.start_time,
|
||||
!hardware_cursor,
|
||||
mode != CursorMode::NotDefault,
|
||||
)
|
||||
.into_iter()
|
||||
.map(E::from),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(wl_surface) = get_dnd_icon(seat) {
|
||||
elements.extend(
|
||||
|
|
@ -112,21 +130,30 @@ where
|
|||
elements
|
||||
}
|
||||
|
||||
pub fn render_output<R>(
|
||||
pub fn render_output<R, Target, Source>(
|
||||
gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
damage_tracker: &mut DamageTrackedRenderer,
|
||||
age: usize,
|
||||
state: &mut Common,
|
||||
output: &Output,
|
||||
hardware_cursor: bool,
|
||||
cursor_mode: CursorMode,
|
||||
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
|
||||
#[cfg(feature = "debug")] mut fps: Option<&mut Fps>,
|
||||
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
||||
) -> Result<(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates), RenderError<R>>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem,
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
+ ImportMem
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<Target>
|
||||
+ Bind<Source>
|
||||
+ Blit<Source>,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
Source: Clone,
|
||||
{
|
||||
let idx = state.shell.workspaces.active_num(output);
|
||||
let handle = state.shell.workspaces.active(output).handle;
|
||||
render_workspace(
|
||||
gpu,
|
||||
renderer,
|
||||
|
|
@ -134,24 +161,34 @@ where
|
|||
age,
|
||||
state,
|
||||
output,
|
||||
idx,
|
||||
hardware_cursor,
|
||||
&handle,
|
||||
cursor_mode,
|
||||
screencopy,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_workspace<R>(
|
||||
_gpu: Option<&DrmNode>,
|
||||
pub fn render_workspace<R, Target, Source>(
|
||||
gpu: Option<&DrmNode>,
|
||||
renderer: &mut R,
|
||||
damage_tracker: &mut DamageTrackedRenderer,
|
||||
age: usize,
|
||||
state: &mut Common,
|
||||
output: &Output,
|
||||
idx: usize,
|
||||
hardware_cursor: bool,
|
||||
handle: &WorkspaceHandle,
|
||||
cursor_mode: CursorMode,
|
||||
screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>,
|
||||
#[cfg(feature = "debug")] mut fps: Option<&mut Fps>,
|
||||
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
||||
) -> Result<(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates), RenderError<R>>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem,
|
||||
R: Renderer
|
||||
+ ImportAll
|
||||
+ ImportMem
|
||||
+ ExportMem
|
||||
+ Bind<Dmabuf>
|
||||
+ Offscreen<Target>
|
||||
+ Bind<Source>
|
||||
+ Blit<Source>,
|
||||
Source: Clone,
|
||||
<R as Renderer>::TextureId: Clone + 'static,
|
||||
{
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -159,14 +196,9 @@ where
|
|||
fps.start();
|
||||
}
|
||||
|
||||
let workspace = &state
|
||||
.shell
|
||||
.workspaces
|
||||
.get(idx, output)
|
||||
.ok_or(OutputNoMode)?;
|
||||
let workspace = state.shell.space_for_handle(&handle).ok_or(OutputNoMode)?;
|
||||
|
||||
let mut elements: Vec<CosmicElement<R>> =
|
||||
cursor_elements(renderer, state, output, hardware_cursor);
|
||||
let mut elements: Vec<CosmicElement<R>> = cursor_elements(renderer, state, output, cursor_mode);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
|
|
@ -219,5 +251,39 @@ where
|
|||
fps.end();
|
||||
}
|
||||
|
||||
if let Some((source, buffers)) = screencopy {
|
||||
if res.is_ok() {
|
||||
for (session, params) in buffers {
|
||||
match render_to_buffer(
|
||||
gpu.cloned(),
|
||||
renderer,
|
||||
&session,
|
||||
params,
|
||||
output.current_transform(),
|
||||
|_node, renderer, dtr, age| {
|
||||
let res = dtr.damage_output(age, &elements, slog_scope::logger())?;
|
||||
|
||||
if let (Some(ref damage), _) = &res {
|
||||
for rect in damage {
|
||||
renderer
|
||||
.blit_from(source.clone(), *rect, *rect, TextureFilter::Nearest)
|
||||
.map_err(RenderError::Rendering)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
) {
|
||||
Ok(true) => {} // success
|
||||
Ok(false) => state.still_pending(session.clone(), params.clone()),
|
||||
Err(err) => {
|
||||
slog_scope::warn!("Error rendering to screencopy session: {}", err);
|
||||
session.failed(FailureReason::Unspec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ use crate::{
|
|||
input::Devices,
|
||||
state::{BackendData, Common, Data},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use smithay::{
|
||||
backend::{
|
||||
renderer::{damage::DamageTrackedRenderer, ImportDma, ImportEgl},
|
||||
renderer::{damage::DamageTrackedRenderer, gles2::Gles2Renderbuffer, ImportDma, ImportEgl},
|
||||
winit::{self, WinitEvent, WinitGraphicsBackend, WinitVirtualDevice},
|
||||
},
|
||||
desktop::layer_map_for_output,
|
||||
|
|
@ -26,11 +27,14 @@ use std::cell::RefCell;
|
|||
#[cfg(feature = "debug")]
|
||||
use crate::state::Fps;
|
||||
|
||||
use super::render::CursorMode;
|
||||
|
||||
pub struct WinitState {
|
||||
// The winit backend currently has no notion of multiple windows
|
||||
pub backend: WinitGraphicsBackend,
|
||||
output: Output,
|
||||
damage_tracker: DamageTrackedRenderer,
|
||||
screencopy: Vec<(ScreencopySession, BufferParams)>,
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps,
|
||||
}
|
||||
|
|
@ -42,24 +46,34 @@ impl WinitState {
|
|||
.with_context(|| "Failed to bind buffer")?;
|
||||
let age = self.backend.buffer_age().unwrap_or(0);
|
||||
|
||||
match render::render_output(
|
||||
let surface = self.backend.egl_surface();
|
||||
match render::render_output::<_, Gles2Renderbuffer, _>(
|
||||
None,
|
||||
self.backend.renderer(),
|
||||
&mut self.damage_tracker,
|
||||
age,
|
||||
state,
|
||||
&self.output,
|
||||
true,
|
||||
CursorMode::NotDefault,
|
||||
if !self.screencopy.is_empty() {
|
||||
Some((surface, &self.screencopy))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(feature = "debug")]
|
||||
Some(&mut self.fps),
|
||||
) {
|
||||
Ok(damage) => {
|
||||
state.send_frames(&self.output);
|
||||
Ok((damage, states)) => {
|
||||
self.screencopy.clear();
|
||||
self.backend
|
||||
.submit(damage.as_ref().map(|x| &**x))
|
||||
.submit(damage.as_deref())
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
state.send_frames(&self.output, &states);
|
||||
}
|
||||
Err(err) => {
|
||||
for (session, params) in self.screencopy.drain(..) {
|
||||
state.still_pending(session, params)
|
||||
}
|
||||
anyhow::bail!("Rendering failed: {}", err);
|
||||
}
|
||||
};
|
||||
|
|
@ -92,6 +106,12 @@ 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(
|
||||
|
|
@ -186,6 +206,7 @@ pub fn init_backend(
|
|||
backend,
|
||||
output: output.clone(),
|
||||
damage_tracker: DamageTrackedRenderer::from_output(&output),
|
||||
screencopy: Vec::new(),
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps::default(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::{
|
|||
input::Devices,
|
||||
state::{BackendData, Common, Data},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::{BufferParams, Session as ScreencopySession},
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use smithay::{
|
||||
|
|
@ -14,7 +15,9 @@ use smithay::{
|
|||
egl::{EGLContext, EGLDisplay},
|
||||
input::{Event, InputEvent},
|
||||
renderer::{
|
||||
damage::DamageTrackedRenderer, gles2::Gles2Renderer, Bind, ImportDma, ImportEgl,
|
||||
damage::DamageTrackedRenderer,
|
||||
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
||||
Bind, ImportDma, ImportEgl,
|
||||
},
|
||||
x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface},
|
||||
},
|
||||
|
|
@ -121,6 +124,7 @@ impl X11State {
|
|||
render: ping.clone(),
|
||||
dirty: false,
|
||||
pending: true,
|
||||
screencopy: Vec::new(),
|
||||
#[cfg(feature = "debug")]
|
||||
fps: Fps::default(),
|
||||
});
|
||||
|
|
@ -130,9 +134,16 @@ impl X11State {
|
|||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn schedule_render(&mut self, output: &Output) {
|
||||
pub fn schedule_render(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
screencopy: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) {
|
||||
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();
|
||||
}
|
||||
|
|
@ -172,6 +183,7 @@ impl X11State {
|
|||
pub struct Surface {
|
||||
window: Window,
|
||||
damage_tracker: DamageTrackedRenderer,
|
||||
screencopy: Vec<(ScreencopySession, BufferParams)>,
|
||||
surface: X11Surface,
|
||||
output: Output,
|
||||
render: ping::Ping,
|
||||
|
|
@ -192,27 +204,36 @@ impl Surface {
|
|||
.buffer()
|
||||
.with_context(|| "Failed to allocate buffer")?;
|
||||
renderer
|
||||
.bind(buffer)
|
||||
.bind(buffer.clone())
|
||||
.with_context(|| "Failed to bind buffer")?;
|
||||
|
||||
match render::render_output(
|
||||
match render::render_output::<_, Gles2Renderbuffer, _>(
|
||||
None,
|
||||
renderer,
|
||||
&mut self.damage_tracker,
|
||||
age as usize,
|
||||
state,
|
||||
&self.output,
|
||||
true,
|
||||
render::CursorMode::NotDefault,
|
||||
if !self.screencopy.is_empty() {
|
||||
Some((buffer, &self.screencopy))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(feature = "debug")]
|
||||
Some(&mut self.fps),
|
||||
) {
|
||||
Ok(_) => {
|
||||
state.send_frames(&self.output);
|
||||
Ok((_damage, states)) => {
|
||||
self.screencopy.clear();
|
||||
self.surface
|
||||
.submit()
|
||||
.with_context(|| "Failed to submit buffer for display")?;
|
||||
state.send_frames(&self.output, &states);
|
||||
}
|
||||
Err(err) => {
|
||||
for (session, params) in self.screencopy.drain(..) {
|
||||
state.still_pending(session, params)
|
||||
}
|
||||
self.surface.reset_buffers();
|
||||
anyhow::bail!("Rendering failed: {}", err);
|
||||
}
|
||||
|
|
@ -417,8 +438,7 @@ impl State {
|
|||
self.process_input_event(event);
|
||||
// TODO actually figure out the output
|
||||
for output in self.common.shell.outputs() {
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, output);
|
||||
self.backend.x11().schedule_render(output, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use crate::{
|
|||
shell::{focus::target::PointerFocusTarget, layout::floating::SeatMoveGrabState, Workspace}, // shell::grabs::SeatMoveGrabState
|
||||
state::Common,
|
||||
utils::prelude::*,
|
||||
wayland::{handlers::screencopy::ScreencopySessions, protocols::screencopy::Session},
|
||||
};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
use smithay::{
|
||||
backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, InputBackend,
|
||||
|
|
@ -525,6 +527,14 @@ impl State {
|
|||
.cloned()
|
||||
.unwrap_or(current_output.clone());
|
||||
if output != current_output {
|
||||
for session in sessions_for_output(&self.common, ¤t_output) {
|
||||
session.cursor_leave(seat, InputType::Pointer);
|
||||
}
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
session.cursor_enter(seat, InputType::Pointer);
|
||||
}
|
||||
|
||||
seat.set_active_output(&output);
|
||||
}
|
||||
let output_geometry = output.geometry();
|
||||
|
|
@ -546,6 +556,19 @@ impl State {
|
|||
output_geometry,
|
||||
&workspace,
|
||||
);
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output.geometry().size.to_f64(),
|
||||
),
|
||||
&self.common.start_time,
|
||||
) {
|
||||
session.cursor_info(seat, InputType::Pointer, geometry, offset);
|
||||
}
|
||||
}
|
||||
seat.get_pointer().unwrap().motion(
|
||||
self,
|
||||
under,
|
||||
|
|
@ -834,3 +857,44 @@ impl State {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator<Item = Session> {
|
||||
let workspace = state.shell.active_space(&output);
|
||||
let maybe_fullscreen = workspace.get_fullscreen(&output);
|
||||
workspace
|
||||
.screencopy_sessions
|
||||
.iter()
|
||||
.map(|s| (&**s).clone())
|
||||
.chain(
|
||||
maybe_fullscreen
|
||||
.and_then(|w| w.user_data().get::<ScreencopySessions>())
|
||||
.map(|sessions| {
|
||||
sessions
|
||||
.0
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|s| (&**s).clone())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,10 +73,13 @@ fn main() -> Result<()> {
|
|||
|
||||
// do we need to trigger another render
|
||||
if data.state.common.dirty_flag.swap(false, Ordering::SeqCst) {
|
||||
// TODO: Render workspace sessions
|
||||
for output in data.state.common.shell.outputs() {
|
||||
data.state
|
||||
.backend
|
||||
.schedule_render(&data.state.common.event_loop_handle, output)
|
||||
data.state.backend.schedule_render(
|
||||
&data.state.common.event_loop_handle,
|
||||
output,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl CosmicMapped {
|
|||
pub fn set_tiled(&self, tiled: bool) {
|
||||
for toplevel in match &self.element {
|
||||
// we use the tiled state of stack windows anyway to get rid of decorations
|
||||
CosmicMappedInternal::Stack(s) => None,
|
||||
CosmicMappedInternal::Stack(_) => None,
|
||||
CosmicMappedInternal::Window(w) => Some(w.window.toplevel()),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
use crate::state::State;
|
||||
use crate::{
|
||||
state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions,
|
||||
};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
|
|
@ -29,6 +32,9 @@ use std::{
|
|||
pub struct CosmicStack {
|
||||
windows: Arc<Mutex<Vec<Window>>>,
|
||||
active: Arc<AtomicUsize>,
|
||||
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
|
||||
previous_keyboard: Arc<AtomicUsize>,
|
||||
previous_pointer: Arc<AtomicUsize>,
|
||||
pub(super) header: Arc<Mutex<Option<HeaderBar>>>,
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +74,9 @@ impl CosmicStack {
|
|||
.iter()
|
||||
.position(|w| w == window)
|
||||
{
|
||||
self.active.store(val, Ordering::SeqCst)
|
||||
let old = self.active.swap(val, Ordering::SeqCst);
|
||||
self.previous_keyboard.store(old, Ordering::SeqCst);
|
||||
self.previous_pointer.store(old, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +100,69 @@ impl CosmicStack {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboard_leave_if_previous(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
serial: Serial,
|
||||
) -> usize {
|
||||
let active = self.active.load(Ordering::SeqCst);
|
||||
let previous = self.previous_keyboard.swap(active, Ordering::SeqCst);
|
||||
if previous != active {
|
||||
KeyboardTarget::leave(&self.windows.lock().unwrap()[previous], seat, data, serial);
|
||||
// TODO: KeyboardTarget::enter(&self.windows.lock().unwrap()[active], seat, data, serial, seat.keys())
|
||||
}
|
||||
active
|
||||
}
|
||||
|
||||
fn pointer_leave_if_previous(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
location: Point<f64, Logical>,
|
||||
) -> usize {
|
||||
let active = self.active.load(Ordering::SeqCst);
|
||||
let previous = self.previous_pointer.swap(active, Ordering::SeqCst);
|
||||
if previous != active {
|
||||
if let Some(sessions) = self.windows.lock().unwrap()[previous]
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
{
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
PointerTarget::leave(
|
||||
&self.windows.lock().unwrap()[previous],
|
||||
seat,
|
||||
data,
|
||||
serial,
|
||||
time,
|
||||
);
|
||||
if let Some(sessions) = self.windows.lock().unwrap()[active]
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
{
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
PointerTarget::enter(
|
||||
&self.windows.lock().unwrap()[active],
|
||||
seat,
|
||||
data,
|
||||
&MotionEvent {
|
||||
location,
|
||||
serial,
|
||||
time,
|
||||
},
|
||||
);
|
||||
}
|
||||
active
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for CosmicStack {
|
||||
|
|
@ -158,6 +229,8 @@ impl KeyboardTarget<State> for CosmicStack {
|
|||
keys: Vec<KeysymHandle<'_>>,
|
||||
serial: Serial,
|
||||
) {
|
||||
let active = self.active.load(Ordering::SeqCst);
|
||||
self.previous_keyboard.store(active, Ordering::SeqCst);
|
||||
KeyboardTarget::enter(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
|
|
@ -167,12 +240,8 @@ impl KeyboardTarget<State> for CosmicStack {
|
|||
)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial) {
|
||||
KeyboardTarget::leave(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
serial,
|
||||
)
|
||||
let active = self.keyboard_leave_if_previous(seat, data, serial);
|
||||
KeyboardTarget::leave(&self.windows.lock().unwrap()[active], seat, data, serial)
|
||||
}
|
||||
fn key(
|
||||
&self,
|
||||
|
|
@ -183,8 +252,9 @@ impl KeyboardTarget<State> for CosmicStack {
|
|||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
let active = self.keyboard_leave_if_previous(seat, data, serial);
|
||||
KeyboardTarget::key(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
&self.windows.lock().unwrap()[active],
|
||||
seat,
|
||||
data,
|
||||
key,
|
||||
|
|
@ -200,8 +270,9 @@ impl KeyboardTarget<State> for CosmicStack {
|
|||
modifiers: ModifiersState,
|
||||
serial: Serial,
|
||||
) {
|
||||
let active = self.keyboard_leave_if_previous(seat, data, serial);
|
||||
KeyboardTarget::modifiers(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
&self.windows.lock().unwrap()[active],
|
||||
seat,
|
||||
data,
|
||||
modifiers,
|
||||
|
|
@ -212,6 +283,16 @@ impl KeyboardTarget<State> for CosmicStack {
|
|||
|
||||
impl PointerTarget<State> for CosmicStack {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
if let Some(sessions) = self.active().user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
*self.last_location.lock().unwrap() = Some((event.location, event.serial, event.time));
|
||||
let active = self.active.load(Ordering::SeqCst);
|
||||
self.previous_pointer.store(active, Ordering::SeqCst);
|
||||
|
||||
PointerTarget::enter(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
|
|
@ -220,14 +301,27 @@ impl PointerTarget<State> for CosmicStack {
|
|||
)
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
PointerTarget::motion(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
data,
|
||||
event,
|
||||
)
|
||||
let active =
|
||||
self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location);
|
||||
|
||||
if let Some(sessions) = self.active().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.start_time)
|
||||
{
|
||||
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PointerTarget::motion(&self.windows.lock().unwrap()[active], seat, data, event)
|
||||
}
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||
if let Some((location, _serial, _time)) = self.last_location.lock().unwrap().clone() {
|
||||
self.pointer_leave_if_previous(seat, data, event.serial, event.time, location);
|
||||
}
|
||||
|
||||
PointerTarget::button(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
|
|
@ -236,6 +330,10 @@ impl PointerTarget<State> for CosmicStack {
|
|||
)
|
||||
}
|
||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||
if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() {
|
||||
self.pointer_leave_if_previous(seat, data, serial, time, location);
|
||||
}
|
||||
|
||||
PointerTarget::axis(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
|
|
@ -244,6 +342,15 @@ impl PointerTarget<State> for CosmicStack {
|
|||
)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() {
|
||||
self.pointer_leave_if_previous(seat, data, serial, time, location);
|
||||
}
|
||||
if let Some(sessions) = self.active().user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
PointerTarget::leave(
|
||||
&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)],
|
||||
seat,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::state::State;
|
||||
use crate::{
|
||||
state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions,
|
||||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::KeyState,
|
||||
|
|
@ -187,9 +189,30 @@ impl KeyboardTarget<State> for CosmicWindow {
|
|||
|
||||
impl PointerTarget<State> for CosmicWindow {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
|
||||
if let Some(sessions) = self.window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_enter(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
PointerTarget::enter(&self.window, seat, data, event)
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
|
||||
if let Some(sessions) = self.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.start_time)
|
||||
{
|
||||
session.cursor_info(seat, InputType::Pointer, geo, hotspot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PointerTarget::motion(&self.window, seat, data, event)
|
||||
}
|
||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||
|
|
@ -199,6 +222,14 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
PointerTarget::axis(&self.window, seat, data, frame)
|
||||
}
|
||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
|
||||
|
||||
if let Some(sessions) = self.window.user_data().get::<ScreencopySessions>() {
|
||||
for session in &*sessions.0.borrow() {
|
||||
session.cursor_leave(seat, InputType::Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
PointerTarget::leave(&self.window, seat, data, serial, time)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ use smithay::{
|
|||
desktop::space::SpaceElement,
|
||||
input::{
|
||||
pointer::{
|
||||
AxisFrame, ButtonEvent, Focus, GrabStartData as PointerGrabStartData, MotionEvent,
|
||||
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent,
|
||||
PointerGrab, PointerInnerHandle,
|
||||
},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial},
|
||||
utils::{IsAlive, Logical, Point, Serial},
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
||||
|
|
|
|||
|
|
@ -189,11 +189,11 @@ impl FloatingLayout {
|
|||
&mut self,
|
||||
mapped: &CosmicMapped,
|
||||
seat: &Seat<State>,
|
||||
serial: Serial,
|
||||
_serial: Serial,
|
||||
start_data: PointerGrabStartData<State>,
|
||||
edges: ResizeEdge,
|
||||
) -> Option<ResizeSurfaceGrab> {
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
if seat.get_pointer().is_some() {
|
||||
let location = self.space.element_location(&mapped).unwrap();
|
||||
let size = mapped.geometry().size;
|
||||
|
||||
|
|
@ -204,8 +204,6 @@ impl FloatingLayout {
|
|||
location,
|
||||
size,
|
||||
))
|
||||
|
||||
//pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ impl WorkspaceMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn active_num(&self, output: &Output) -> usize {
|
||||
fn active_num(&self, output: &Output) -> usize {
|
||||
match self {
|
||||
WorkspaceMode::Global(set) => set.active,
|
||||
WorkspaceMode::OutputBound(sets) => {
|
||||
|
|
@ -691,7 +691,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn outputs_for_surface<'a>(
|
||||
pub fn visible_outputs_for_surface<'a>(
|
||||
&'a self,
|
||||
surface: &'a WlSurface,
|
||||
) -> impl Iterator<Item = Output> + 'a {
|
||||
|
|
@ -703,7 +703,7 @@ impl Shell {
|
|||
Some(output) => {
|
||||
Box::new(std::iter::once(output.clone())) as Box<dyn Iterator<Item = Output>>
|
||||
}
|
||||
None => Box::new(self.workspaces.spaces().flat_map(|w| {
|
||||
None => Box::new(self.outputs().map(|o| self.active_space(o)).flat_map(|w| {
|
||||
w.mapped()
|
||||
.find(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||
.into_iter()
|
||||
|
|
@ -712,6 +712,47 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn workspaces_for_surface(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
) -> impl Iterator<Item = (WorkspaceHandle, Output)> {
|
||||
match self.outputs.iter().find(|o| {
|
||||
let map = layer_map_for_output(o);
|
||||
map.layer_for_surface(surface, WindowSurfaceType::ALL)
|
||||
.is_some()
|
||||
}) {
|
||||
Some(output) => self
|
||||
.workspaces
|
||||
.spaces()
|
||||
.filter(move |workspace| {
|
||||
workspace
|
||||
.floating_layer
|
||||
.space
|
||||
.outputs()
|
||||
.any(|o| o == output)
|
||||
})
|
||||
.map(|w| (w.handle.clone(), output.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
None => self
|
||||
.workspaces
|
||||
.spaces()
|
||||
.filter_map(|w| {
|
||||
if let Some(mapped) = w
|
||||
.mapped()
|
||||
.find(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||
{
|
||||
let outputs = w.outputs_for_element(mapped);
|
||||
Some(std::iter::repeat(w.handle.clone()).zip(outputs).fuse())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
pub fn element_for_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> {
|
||||
self.workspaces
|
||||
.spaces()
|
||||
|
|
@ -730,6 +771,14 @@ impl Shell {
|
|||
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
||||
}
|
||||
|
||||
pub fn space_for_handle(&self, handle: &WorkspaceHandle) -> Option<&Workspace> {
|
||||
self.workspaces.spaces().find(|w| &w.handle == handle)
|
||||
}
|
||||
|
||||
pub fn space_for_handle_mut(&mut self, handle: &WorkspaceHandle) -> Option<&mut Workspace> {
|
||||
self.workspaces.spaces_mut().find(|w| &w.handle == handle)
|
||||
}
|
||||
|
||||
pub fn outputs(&self) -> impl Iterator<Item = &Output> {
|
||||
self.outputs.iter()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
use crate::{
|
||||
shell::{
|
||||
element::CosmicWindow,
|
||||
layout::{
|
||||
shell::layout::{
|
||||
floating::{FloatingLayout, MoveSurfaceGrab},
|
||||
tiling::TilingLayout,
|
||||
},
|
||||
},
|
||||
state::State,
|
||||
utils::prelude::*,
|
||||
wayland::protocols::workspace::WorkspaceHandle,
|
||||
wayland::{
|
||||
handlers::screencopy::DropableSession,
|
||||
protocols::{
|
||||
screencopy::{BufferParams, Session as ScreencopySession},
|
||||
workspace::WorkspaceHandle,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
|
@ -17,21 +20,18 @@ use smithay::{
|
|||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
|
||||
ImportAll, Renderer,
|
||||
},
|
||||
desktop::{
|
||||
layer_map_for_output, space::SpaceElement, Kind, LayerSurface, Space, Window,
|
||||
WindowSurfaceType,
|
||||
},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, Kind, LayerSurface, Window},
|
||||
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
||||
output::{Output, WeakOutput},
|
||||
output::Output,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle},
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
render_elements,
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial},
|
||||
wayland::shell::wlr_layer::Layer,
|
||||
};
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{
|
||||
element::CosmicMapped,
|
||||
|
|
@ -48,6 +48,8 @@ pub struct Workspace {
|
|||
pub fullscreen: HashMap<Output, Window>,
|
||||
pub handle: WorkspaceHandle,
|
||||
pub focus_stack: FocusStacks,
|
||||
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
||||
pub screencopy_sessions: Vec<DropableSession>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -62,6 +64,8 @@ impl Workspace {
|
|||
fullscreen: HashMap::new(),
|
||||
handle,
|
||||
focus_stack: FocusStacks::default(),
|
||||
pending_buffers: Vec::new(),
|
||||
screencopy_sessions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
73
src/state.rs
73
src/state.rs
|
|
@ -7,12 +7,18 @@ use crate::{
|
|||
shell::Shell,
|
||||
utils::prelude::*,
|
||||
wayland::protocols::{
|
||||
drm::WlDrmState, output_configuration::OutputConfigurationState,
|
||||
drm::WlDrmState,
|
||||
output_configuration::OutputConfigurationState,
|
||||
screencopy::{BufferParams, Session as ScreencopySession},
|
||||
workspace::WorkspaceClientState,
|
||||
},
|
||||
};
|
||||
use smithay::{
|
||||
backend::drm::DrmNode,
|
||||
backend::{
|
||||
drm::DrmNode,
|
||||
renderer::element::{default_primary_scanout_output_compare, RenderElementStates},
|
||||
},
|
||||
desktop::utils::{surface_primary_scanout_output, update_surface_primary_scanout_output},
|
||||
input::{Seat, SeatState},
|
||||
output::{Mode as OutputMode, Output, Scale},
|
||||
reexports::{
|
||||
|
|
@ -33,7 +39,7 @@ use std::{
|
|||
cell::RefCell,
|
||||
ffi::OsString,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
time::Instant,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
#[cfg(feature = "debug")]
|
||||
use std::{collections::VecDeque, time::Duration};
|
||||
|
|
@ -185,14 +191,19 @@ impl BackendData {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn schedule_render(&mut self, loop_handle: &LoopHandle<'_, Data>, output: &Output) {
|
||||
pub fn schedule_render(
|
||||
&mut self,
|
||||
loop_handle: &LoopHandle<'_, Data>,
|
||||
output: &Output,
|
||||
screencopy: Option<Vec<(ScreencopySession, BufferParams)>>,
|
||||
) {
|
||||
match self {
|
||||
BackendData::Winit(_) => {} // We cannot do this on the winit backend.
|
||||
BackendData::Winit(ref mut state) => state.pending_screencopy(screencopy), // 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),
|
||||
BackendData::X11(ref mut state) => state.schedule_render(output, screencopy),
|
||||
BackendData::Kms(ref mut state) => {
|
||||
if let Err(err) = state.schedule_render(loop_handle, output) {
|
||||
if let Err(err) = state.schedule_render(loop_handle, output, screencopy) {
|
||||
slog_scope::crit!("Failed to schedule event, are we shutting down? {:?}", err);
|
||||
}
|
||||
}
|
||||
|
|
@ -348,17 +359,53 @@ impl Common {
|
|||
self.last_active_seat.as_ref().expect("No seat?")
|
||||
}
|
||||
|
||||
pub fn send_frames(&self, output: &Output) {
|
||||
let workspace = self.shell.active_space(output);
|
||||
workspace.mapped().for_each(|mapped| {
|
||||
if workspace.outputs_for_element(mapped).any(|o| &o == output) {
|
||||
pub fn send_frames(&self, output: &Output, render_element_states: &RenderElementStates) {
|
||||
let time = self.start_time.elapsed();
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
|
||||
let active = self.shell.active_space(output);
|
||||
active.mapped().for_each(|mapped| {
|
||||
if active.outputs_for_element(mapped).any(|o| &o == output) {
|
||||
let window = mapped.active_window();
|
||||
window.send_frame(self.start_time.elapsed().as_millis() as u32)
|
||||
window.with_surfaces(|surface, states| {
|
||||
update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states,
|
||||
render_element_states,
|
||||
default_primary_scanout_output_compare,
|
||||
)
|
||||
});
|
||||
window.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
}
|
||||
});
|
||||
|
||||
for space in self
|
||||
.shell
|
||||
.workspaces
|
||||
.spaces()
|
||||
.filter(|w| w.handle != active.handle)
|
||||
{
|
||||
space.mapped().for_each(|mapped| {
|
||||
if space.outputs_for_element(mapped).any(|o| &o == output) {
|
||||
let window = mapped.active_window();
|
||||
window.send_frame(output, time, throttle, |_, _| None);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let map = smithay::desktop::layer_map_for_output(output);
|
||||
for layer_surface in map.layers() {
|
||||
layer_surface.send_frame(self.start_time.elapsed().as_millis() as u32)
|
||||
layer_surface.with_surfaces(|surface, states| {
|
||||
update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states,
|
||||
render_element_states,
|
||||
default_primary_scanout_output_compare,
|
||||
)
|
||||
});
|
||||
layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,18 @@
|
|||
use crate::input::{ActiveOutput, SeatId};
|
||||
use std::{cell::RefCell, sync::Mutex};
|
||||
|
||||
use crate::{
|
||||
backend::render::cursor::CursorState,
|
||||
input::{ActiveOutput, SeatId},
|
||||
};
|
||||
use smithay::{
|
||||
input::Seat,
|
||||
desktop::utils::bbox_from_surface_tree,
|
||||
input::{
|
||||
pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
utils::{Logical, Rectangle, Transform},
|
||||
utils::{Buffer, IsAlive, Logical, Point, Rectangle, Transform},
|
||||
wayland::compositor::with_states,
|
||||
};
|
||||
|
||||
pub use crate::shell::{Shell, Workspace};
|
||||
|
|
@ -34,6 +44,11 @@ pub trait SeatExt {
|
|||
|
||||
fn active_output(&self) -> Output;
|
||||
fn set_active_output(&self, output: &Output);
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
loc: impl Into<Point<f64, Buffer>>,
|
||||
start_time: &std::time::Instant,
|
||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)>;
|
||||
}
|
||||
|
||||
impl SeatExt for Seat<State> {
|
||||
|
|
@ -56,4 +71,63 @@ impl SeatExt for Seat<State> {
|
|||
.0
|
||||
.borrow_mut() = output.clone();
|
||||
}
|
||||
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
loc: impl Into<Point<f64, Buffer>>,
|
||||
start_time: &std::time::Instant,
|
||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)> {
|
||||
let location = loc.into().to_i32_round();
|
||||
|
||||
let cursor_status = self
|
||||
.user_data()
|
||||
.get::<RefCell<CursorImageStatus>>()
|
||||
.map(|cell| {
|
||||
let mut cursor_status = cell.borrow_mut();
|
||||
if let CursorImageStatus::Surface(ref surface) = *cursor_status {
|
||||
if !surface.alive() {
|
||||
*cursor_status = CursorImageStatus::Default;
|
||||
}
|
||||
}
|
||||
cursor_status.clone()
|
||||
})
|
||||
.unwrap_or(CursorImageStatus::Default);
|
||||
|
||||
match cursor_status {
|
||||
CursorImageStatus::Surface(surface) => {
|
||||
let hotspot = with_states(&surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot
|
||||
});
|
||||
let geo = bbox_from_surface_tree(&surface, (location.x, location.y));
|
||||
let buffer_geo = Rectangle::from_loc_and_size(
|
||||
(geo.loc.x, geo.loc.y),
|
||||
geo.size.to_buffer(1, Transform::Normal),
|
||||
);
|
||||
Some((buffer_geo, (hotspot.x, hotspot.y).into()))
|
||||
}
|
||||
CursorImageStatus::Default => {
|
||||
let seat_userdata = self.user_data();
|
||||
seat_userdata.insert_if_missing(CursorState::default);
|
||||
let state = seat_userdata.get::<CursorState>().unwrap();
|
||||
let frame = state
|
||||
.cursor
|
||||
.get_image(1, start_time.elapsed().as_millis() as u32);
|
||||
|
||||
Some((
|
||||
Rectangle::from_loc_and_size(
|
||||
location,
|
||||
(frame.width as i32, frame.height as i32),
|
||||
),
|
||||
(frame.xhot as i32, frame.yhot as i32).into(),
|
||||
))
|
||||
}
|
||||
CursorImageStatus::Hidden => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{state::BackendData, utils::prelude::*};
|
||||
use crate::{
|
||||
state::BackendData,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::screencopy::UserdataExt,
|
||||
protocols::screencopy::{BufferParams, Session as ScreencopySession, SessionType},
|
||||
},
|
||||
};
|
||||
use smithay::{
|
||||
backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state},
|
||||
delegate_compositor,
|
||||
desktop::{layer_map_for_output, Kind, LayerSurface, PopupKind, WindowSurfaceType},
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::IsAlive,
|
||||
wayland::{
|
||||
compositor::{with_states, CompositorHandler, CompositorState},
|
||||
shell::{
|
||||
|
|
@ -18,11 +26,13 @@ use smithay::{
|
|||
};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::screencopy::{self, PendingScreencopyBuffers};
|
||||
|
||||
impl State {
|
||||
fn early_import_surface(&mut self, surface: &WlSurface) {
|
||||
let mut import_nodes = std::collections::HashSet::new();
|
||||
let dh = &self.common.display_handle;
|
||||
for output in self.common.shell.outputs_for_surface(&surface) {
|
||||
for output in self.common.shell.visible_outputs_for_surface(&surface) {
|
||||
if let BackendData::Kms(ref mut kms_state) = &mut self.backend {
|
||||
if let Some(target) = kms_state.target_node_for_output(&output) {
|
||||
if import_nodes.insert(target) {
|
||||
|
|
@ -155,8 +165,38 @@ impl CompositorHandler for State {
|
|||
if let Some(element) = self.common.shell.element_for_surface(surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&element) {
|
||||
crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location(
|
||||
element, workspace,
|
||||
element.clone(),
|
||||
workspace,
|
||||
);
|
||||
workspace.commit(surface);
|
||||
}
|
||||
|
||||
// handle window screencopy sessions
|
||||
let active = element.active_window();
|
||||
if active.toplevel().wl_surface() == surface {
|
||||
for (session, params) in active.pending_buffers() {
|
||||
let window = active.clone();
|
||||
self.common.event_loop_handle.insert_idle(move |data| {
|
||||
if !session.alive() {
|
||||
return;
|
||||
}
|
||||
|
||||
match screencopy::render_window_to_buffer(
|
||||
&mut data.state,
|
||||
&session,
|
||||
params.clone(),
|
||||
&window,
|
||||
) {
|
||||
// rendering yielded no damage, buffer is still pending
|
||||
Ok(false) => data.state.common.still_pending(session, params),
|
||||
Ok(true) => {} // success
|
||||
Err((reason, err)) => {
|
||||
slog_scope::warn!("Screencopy session failed: {}", err);
|
||||
session.failed(reason);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,9 +206,6 @@ impl CompositorHandler for State {
|
|||
|
||||
// and refresh smithays internal state
|
||||
self.common.shell.popups.commit(surface);
|
||||
for workspace in self.common.shell.workspaces.spaces_mut() {
|
||||
workspace.commit(surface);
|
||||
}
|
||||
|
||||
// re-arrange layer-surfaces (commits may change size and positioning)
|
||||
if let Some(output) = self.common.shell.outputs().find(|o| {
|
||||
|
|
@ -179,10 +216,93 @@ impl CompositorHandler for State {
|
|||
layer_map_for_output(output).arrange();
|
||||
}
|
||||
|
||||
// here we store additional workspace_sessions, we should handle, when rendering the corresponding output anyway
|
||||
let mut scheduled_sessions: Option<Vec<(ScreencopySession, BufferParams)>> = None;
|
||||
|
||||
// lets check which workspaces this surface belongs to
|
||||
let active_spaces = self
|
||||
.common
|
||||
.shell
|
||||
.outputs()
|
||||
.map(|o| (o.clone(), self.common.shell.active_space(o).handle.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
for (handle, output) in self.common.shell.workspaces_for_surface(surface) {
|
||||
let workspace = self.common.shell.space_for_handle_mut(&handle).unwrap();
|
||||
if !workspace.pending_buffers.is_empty() {
|
||||
// TODO: replace with drain_filter....
|
||||
let mut i = 0;
|
||||
while i < workspace.pending_buffers.len() {
|
||||
if let SessionType::Workspace(o, w) =
|
||||
workspace.pending_buffers[i].0.session_type()
|
||||
{
|
||||
if active_spaces.contains(&(o.clone(), w)) {
|
||||
// surface is on an active workspace/output combo, add to workspace_sessions
|
||||
let (session, params) = workspace.pending_buffers.remove(i);
|
||||
scheduled_sessions
|
||||
.get_or_insert_with(Vec::new)
|
||||
.push((session, params));
|
||||
} else if handle == w && output == o {
|
||||
// surface is visible on an offscreen workspace session, schedule a new render
|
||||
let (session, params) = workspace.pending_buffers.remove(i);
|
||||
let output = output.clone();
|
||||
self.common.event_loop_handle.insert_idle(move |data| {
|
||||
if !session.alive() {
|
||||
return;
|
||||
}
|
||||
match screencopy::render_workspace_to_buffer(
|
||||
&mut data.state,
|
||||
&session,
|
||||
params.clone(),
|
||||
&output,
|
||||
&handle,
|
||||
) {
|
||||
Ok(false) => {
|
||||
// rendering yielded no new damage, buffer still pending
|
||||
data.state.common.still_pending(session, params);
|
||||
}
|
||||
Ok(true) => {}
|
||||
Err((reason, err)) => {
|
||||
slog_scope::warn!("Screencopy session failed: {}", err);
|
||||
session.failed(reason);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// schedule a new render
|
||||
for output in self.common.shell.outputs_for_surface(surface) {
|
||||
self.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
for output in self.common.shell.visible_outputs_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<_>>()
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub mod layer_shell;
|
|||
pub mod output;
|
||||
pub mod output_configuration;
|
||||
pub mod primary_selection;
|
||||
pub mod screencopy;
|
||||
pub mod seat;
|
||||
pub mod shm;
|
||||
pub mod toplevel_info;
|
||||
|
|
|
|||
795
src/wayland/handlers/screencopy.rs
Normal file
795
src/wayland/handlers/screencopy.rs
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashSet,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::{
|
||||
FailureReason, InputType,
|
||||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::dmabuf::Dmabuf,
|
||||
drm::DrmNode,
|
||||
egl::EGLDevice,
|
||||
renderer::{
|
||||
buffer_dimensions, buffer_type,
|
||||
damage::{DamageTrackedRenderer, DamageTrackedRendererError},
|
||||
element::{
|
||||
surface::WaylandSurfaceRenderElement, AsRenderElements, RenderElementStates,
|
||||
},
|
||||
gles2::{Gles2Renderbuffer, Gles2Renderer},
|
||||
Bind, BufferType, ExportMem, Offscreen, Renderer,
|
||||
},
|
||||
},
|
||||
desktop::Window,
|
||||
output::Output,
|
||||
reexports::wayland_server::{
|
||||
protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
|
||||
Resource,
|
||||
},
|
||||
utils::{Physical, Rectangle, Scale, Transform},
|
||||
wayland::{
|
||||
dmabuf::get_dmabuf,
|
||||
shm::{with_buffer_contents, with_buffer_contents_mut},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::render::{render_output, render_workspace, CursorMode, CLEAR_COLOR},
|
||||
state::{BackendData, ClientState, Common, State},
|
||||
utils::prelude::OutputExt,
|
||||
wayland::protocols::{
|
||||
screencopy::{
|
||||
BufferInfo, BufferParams, CursorMode as ScreencopyCursorMode, CursorSession,
|
||||
ScreencopyHandler, Session, SessionType,
|
||||
},
|
||||
workspace::WorkspaceHandle,
|
||||
},
|
||||
};
|
||||
|
||||
pub type PendingScreencopyBuffers = RefCell<Vec<(Session, BufferParams)>>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ScreencopySessions(pub RefCell<Vec<DropableSession>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DropableSession(Session, FailureReason);
|
||||
impl Deref for DropableSession {
|
||||
type Target = Session;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for DropableSession {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
impl Drop for DropableSession {
|
||||
fn drop(&mut self) {
|
||||
self.0.failed(self.1);
|
||||
}
|
||||
}
|
||||
impl PartialEq<Session> for DropableSession {
|
||||
fn eq(&self, other: &Session) -> bool {
|
||||
&self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
pub type SessionDTR = RefCell<DamageTrackedRenderer>;
|
||||
|
||||
impl ScreencopyHandler for State {
|
||||
fn capture_output(&mut self, output: Output, session: Session) -> Vec<BufferInfo> {
|
||||
let formats = match formats_for_output(&output, &mut self.backend) {
|
||||
Ok(formats) => formats,
|
||||
Err(reason) => {
|
||||
session.failed(reason);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
for seat in self.common.seats() {
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
if output
|
||||
.geometry()
|
||||
.contains(pointer.current_location().to_i32_round())
|
||||
{
|
||||
session.cursor_enter(seat, InputType::Pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
session
|
||||
.user_data()
|
||||
.insert_if_missing(|| SessionDTR::new(DamageTrackedRenderer::from_output(&output)));
|
||||
output
|
||||
.user_data()
|
||||
.insert_if_missing(ScreencopySessions::default);
|
||||
output
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow_mut()
|
||||
.push(DropableSession(session, FailureReason::OutputDisabled));
|
||||
|
||||
formats
|
||||
}
|
||||
|
||||
fn capture_workspace(
|
||||
&mut self,
|
||||
handle: WorkspaceHandle,
|
||||
output: Output,
|
||||
session: Session,
|
||||
) -> Vec<BufferInfo> {
|
||||
let formats = match formats_for_output(&output, &mut self.backend) {
|
||||
Ok(formats) => formats,
|
||||
Err(reason) => {
|
||||
session.failed(reason);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
let workspace = match self.common.shell.space_for_handle_mut(&handle) {
|
||||
Some(workspace) => workspace,
|
||||
None => {
|
||||
session.failed(FailureReason::Unspec);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
session
|
||||
.user_data()
|
||||
.insert_if_missing(|| SessionDTR::new(DamageTrackedRenderer::from_output(&output)));
|
||||
|
||||
workspace
|
||||
.screencopy_sessions
|
||||
.push(DropableSession(session, FailureReason::InvalidOutput));
|
||||
|
||||
formats
|
||||
}
|
||||
|
||||
fn capture_toplevel(&mut self, toplevel: Window, session: Session) -> Vec<BufferInfo> {
|
||||
let surface = toplevel.toplevel().wl_surface();
|
||||
let size = toplevel
|
||||
.bbox_with_popups()
|
||||
.size
|
||||
.to_buffer(1, Transform::Normal);
|
||||
|
||||
let mut _kms_renderer = None;
|
||||
let renderer = match self.backend {
|
||||
BackendData::Kms(ref mut kms) => {
|
||||
let node = self
|
||||
.common
|
||||
.display_handle
|
||||
.get_client(surface.id())
|
||||
.ok()
|
||||
.and_then(|client| client.get_data::<ClientState>().unwrap().drm_node.clone())
|
||||
.unwrap_or(kms.primary.clone());
|
||||
_kms_renderer = Some(kms.api.renderer::<Gles2Renderbuffer>(&node, &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!(),
|
||||
};
|
||||
|
||||
let mut formats = vec![
|
||||
BufferInfo::Shm {
|
||||
format: ShmFormat::Abgr8888,
|
||||
size,
|
||||
stride: 0,
|
||||
},
|
||||
BufferInfo::Shm {
|
||||
format: ShmFormat::Xbgr8888,
|
||||
size,
|
||||
stride: 0,
|
||||
},
|
||||
];
|
||||
|
||||
if let Some(node) = EGLDevice::device_for_display(renderer.egl_context().display())
|
||||
.ok()
|
||||
.and_then(|device| device.try_get_render_node().ok().flatten())
|
||||
{
|
||||
formats.extend(
|
||||
renderer
|
||||
.egl_context()
|
||||
.dmabuf_render_formats()
|
||||
.iter()
|
||||
.map(|format| format.code)
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.map(|format| BufferInfo::Dmabuf { node, format, size }),
|
||||
);
|
||||
}
|
||||
|
||||
let size = toplevel.geometry().size.to_physical(1);
|
||||
session.user_data().insert_if_missing(|| {
|
||||
SessionDTR::new(DamageTrackedRenderer::new(size, 1.0, Transform::Normal))
|
||||
});
|
||||
toplevel
|
||||
.user_data()
|
||||
.insert_if_missing(ScreencopySessions::default);
|
||||
toplevel
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow_mut()
|
||||
.push(DropableSession(session, FailureReason::ToplevelDestroyed));
|
||||
|
||||
formats
|
||||
}
|
||||
|
||||
fn capture_cursor(&mut self, _session: CursorSession) -> Vec<BufferInfo> {
|
||||
unimplemented!("We don't advertise the capture cursor mode")
|
||||
}
|
||||
|
||||
fn buffer_attached(&mut self, session: Session, params: BufferParams, on_damage: bool) {
|
||||
// verify buffer size
|
||||
let buffer_size = match buffer_dimensions(¶ms.buffer) {
|
||||
Some(size) => size.to_logical(1, Transform::Normal),
|
||||
None => {
|
||||
slog_scope::warn!("Error during screencopy session: Buffer has no size");
|
||||
session.failed(FailureReason::InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match session.session_type() {
|
||||
SessionType::Output(output) | SessionType::Workspace(output, _) => {
|
||||
let mode = match output.current_mode() {
|
||||
Some(mode) => mode,
|
||||
None => {
|
||||
slog_scope::warn!("Error during screencopy session: Output has no mode");
|
||||
session.failed(FailureReason::InvalidOutput);
|
||||
return;
|
||||
}
|
||||
}
|
||||
.size;
|
||||
|
||||
if buffer_size.to_physical(1) != mode {
|
||||
slog_scope::warn!("Error during screencopy session: Buffer size doesn't match");
|
||||
session.failed(FailureReason::InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SessionType::Window(window) => {
|
||||
let geometry = window.geometry();
|
||||
if buffer_size != geometry.size {
|
||||
slog_scope::warn!("Error during screencopy session: Buffer size doesn't match");
|
||||
session.failed(FailureReason::InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if !matches!(
|
||||
buffer_type(¶ms.buffer),
|
||||
Some(BufferType::Shm) | Some(BufferType::Dma)
|
||||
) {
|
||||
slog_scope::warn!("Error during screencopy session: Buffer is neither shm or dma");
|
||||
session.failed(FailureReason::InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(BufferType::Shm) = buffer_type(¶ms.buffer) {
|
||||
if with_buffer_contents(¶ms.buffer, |_, info| {
|
||||
info.format != ShmFormat::Abgr8888 && info.format != ShmFormat::Xbgr8888
|
||||
})
|
||||
.unwrap()
|
||||
{
|
||||
slog_scope::warn!("Error during screencopy session: Invalid shm buffer format");
|
||||
session.failed(FailureReason::InvalidBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if on_damage {
|
||||
match session.session_type() {
|
||||
SessionType::Output(output) => {
|
||||
output
|
||||
.user_data()
|
||||
.insert_if_missing(PendingScreencopyBuffers::default);
|
||||
output
|
||||
.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.push((session, params));
|
||||
}
|
||||
SessionType::Workspace(_output, handle) => {
|
||||
match self.common.shell.space_for_handle_mut(&handle) {
|
||||
Some(workspace) => workspace.pending_buffers.push((session, params)),
|
||||
None => session.failed(FailureReason::OutputDisabled),
|
||||
};
|
||||
}
|
||||
SessionType::Window(window) => {
|
||||
window
|
||||
.user_data()
|
||||
.insert_if_missing(PendingScreencopyBuffers::default);
|
||||
window
|
||||
.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.push((session, params));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
} else {
|
||||
let result = match session.session_type() {
|
||||
SessionType::Output(output) => {
|
||||
render_output_to_buffer(self, &session, params, &output)
|
||||
}
|
||||
SessionType::Workspace(output, handle) => {
|
||||
render_workspace_to_buffer(self, &session, params, &output, &handle)
|
||||
}
|
||||
SessionType::Window(window) => {
|
||||
render_window_to_buffer(self, &session, params, &window)
|
||||
}
|
||||
_ => unreachable!("Session types not supported"),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(false) => {
|
||||
// client didn't wanna wait for damage, so it gets empty damage
|
||||
session.commit_buffer(
|
||||
match session.session_type() {
|
||||
SessionType::Output(output) | SessionType::Workspace(output, _) => {
|
||||
output.current_transform()
|
||||
}
|
||||
_ => Transform::Normal,
|
||||
},
|
||||
Vec::new(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(true) => {} // success
|
||||
Err((reason, error)) => {
|
||||
slog_scope::warn!("Error during screencopy session: {}", error);
|
||||
session.failed(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_session_destroyed(&mut self, _session: CursorSession) {
|
||||
unreachable!("We currently don't support cursor sessions");
|
||||
}
|
||||
|
||||
fn session_destroyed(&mut self, session: Session) {
|
||||
match session.session_type() {
|
||||
SessionType::Output(output) => {
|
||||
if let Some(pending_buffers) = output.user_data().get::<PendingScreencopyBuffers>()
|
||||
{
|
||||
pending_buffers.borrow_mut().retain(|(s, _)| s != &session);
|
||||
}
|
||||
if let Some(sessions) = output.user_data().get::<ScreencopySessions>() {
|
||||
sessions.0.borrow_mut().retain(|s| s != &session);
|
||||
}
|
||||
}
|
||||
SessionType::Workspace(_, handle) => {
|
||||
if let Some(workspace) = self.common.shell.space_for_handle_mut(&handle) {
|
||||
workspace.pending_buffers.retain(|(s, _)| s != &session);
|
||||
workspace.screencopy_sessions.retain(|s| s != &session);
|
||||
}
|
||||
}
|
||||
SessionType::Window(window) => {
|
||||
if let Some(pending_buffers) = window.user_data().get::<PendingScreencopyBuffers>()
|
||||
{
|
||||
pending_buffers.borrow_mut().retain(|(s, _)| s != &session);
|
||||
}
|
||||
if let Some(sessions) = window.user_data().get::<ScreencopySessions>() {
|
||||
sessions.0.borrow_mut().retain(|s| s != &session);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn formats_for_output(
|
||||
output: &Output,
|
||||
backend: &mut BackendData,
|
||||
) -> Result<Vec<BufferInfo>, FailureReason> {
|
||||
let mode = match output.current_mode() {
|
||||
Some(mode) => mode.size.to_logical(1).to_buffer(1, Transform::Normal),
|
||||
None => {
|
||||
return Err(FailureReason::OutputDisabled);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
_kms_renderer = Some(kms.api.renderer::<Gles2Renderbuffer>(&node, &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!(),
|
||||
};
|
||||
|
||||
let mut formats = vec![
|
||||
BufferInfo::Shm {
|
||||
format: ShmFormat::Abgr8888,
|
||||
size: mode,
|
||||
stride: 0,
|
||||
},
|
||||
BufferInfo::Shm {
|
||||
format: ShmFormat::Xbgr8888,
|
||||
size: mode,
|
||||
stride: 0,
|
||||
},
|
||||
];
|
||||
|
||||
if let Some(node) = EGLDevice::device_for_display(renderer.egl_context().display())
|
||||
.ok()
|
||||
.and_then(|device| device.try_get_render_node().ok().flatten())
|
||||
{
|
||||
formats.extend(
|
||||
renderer
|
||||
.egl_context()
|
||||
.dmabuf_render_formats()
|
||||
.iter()
|
||||
.map(|format| format.code)
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.map(|format| BufferInfo::Dmabuf {
|
||||
node,
|
||||
format,
|
||||
size: mode,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(formats)
|
||||
}
|
||||
|
||||
fn node_from_params(
|
||||
params: &BufferParams,
|
||||
backend: &BackendData,
|
||||
output: Option<&Output>,
|
||||
) -> Option<DrmNode> {
|
||||
match buffer_type(¶ms.buffer) {
|
||||
Some(BufferType::Dma) if params.node.is_some() => params.node.clone(),
|
||||
Some(BufferType::Shm) | Some(BufferType::Dma) => match backend {
|
||||
BackendData::Kms(kms) => Some(
|
||||
output
|
||||
.and_then(|output| kms.target_node_for_output(output))
|
||||
.unwrap_or(kms.primary),
|
||||
),
|
||||
_ => None,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_renderer<R, Target>(
|
||||
renderer: &mut R,
|
||||
buffer: &WlBuffer,
|
||||
) -> Result<(), <R as Renderer>::Error>
|
||||
where
|
||||
R: Bind<Dmabuf> + Offscreen<Target>,
|
||||
{
|
||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||
renderer.bind(dmabuf)?;
|
||||
} else {
|
||||
let size = buffer_dimensions(buffer).unwrap();
|
||||
let render_buffer = renderer.create_buffer(size)?;
|
||||
renderer.bind(render_buffer)?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn submit_buffer<R>(
|
||||
session: &Session,
|
||||
buffer: &WlBuffer,
|
||||
renderer: &mut R,
|
||||
transform: Transform,
|
||||
damage: Vec<Rectangle<i32, Physical>>,
|
||||
) -> Result<(), <R as Renderer>::Error>
|
||||
where
|
||||
R: ExportMem,
|
||||
{
|
||||
if matches!(buffer_type(buffer), Some(BufferType::Shm)) {
|
||||
let buffer_size = buffer_dimensions(buffer).unwrap();
|
||||
with_buffer_contents_mut(buffer, |data, _info| {
|
||||
let mapping =
|
||||
renderer.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), buffer_size))?;
|
||||
let gl_data = renderer.map_texture(&mapping)?;
|
||||
data.copy_from_slice(gl_data);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap()?;
|
||||
}
|
||||
|
||||
session.commit_buffer(transform, damage, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render_to_buffer<F, R, Target>(
|
||||
node: Option<DrmNode>,
|
||||
renderer: &mut R,
|
||||
session: &Session,
|
||||
params: &BufferParams,
|
||||
transform: Transform,
|
||||
render_fn: F,
|
||||
) -> Result<bool, DamageTrackedRendererError<R>>
|
||||
where
|
||||
R: Bind<Dmabuf> + Offscreen<Target> + ExportMem,
|
||||
F: FnOnce(
|
||||
Option<&DrmNode>,
|
||||
&mut R,
|
||||
&mut DamageTrackedRenderer,
|
||||
usize,
|
||||
) -> Result<
|
||||
(Option<Vec<Rectangle<i32, Physical>>>, RenderElementStates),
|
||||
DamageTrackedRendererError<R>,
|
||||
>,
|
||||
{
|
||||
prepare_renderer(renderer, ¶ms.buffer).map_err(DamageTrackedRendererError::Rendering)?;
|
||||
|
||||
let mut dtr = session
|
||||
.user_data()
|
||||
.get::<SessionDTR>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
|
||||
let res = render_fn(node.as_ref(), renderer, &mut *dtr, params.age as usize)?;
|
||||
|
||||
if let (Some(damage), _) = res {
|
||||
submit_buffer(session, ¶ms.buffer, renderer, transform, damage)
|
||||
.map_err(DamageTrackedRendererError::Rendering)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_output_to_buffer(
|
||||
state: &mut State,
|
||||
session: &Session,
|
||||
params: BufferParams,
|
||||
output: &Output,
|
||||
) -> Result<bool, (FailureReason, anyhow::Error)> {
|
||||
let node = node_from_params(¶ms, &mut state.backend, Some(output));
|
||||
let mut _tmp_multirenderer = None;
|
||||
let renderer = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
_tmp_multirenderer = Some(
|
||||
kms.api
|
||||
.renderer::<Gles2Renderbuffer>(node.as_ref().unwrap(), node.as_ref().unwrap())
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))?,
|
||||
);
|
||||
_tmp_multirenderer.as_mut().unwrap().as_mut()
|
||||
}
|
||||
BackendData::Winit(winit) => winit.backend.renderer(),
|
||||
BackendData::X11(x11) => &mut x11.renderer,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let common = &mut state.common;
|
||||
render_to_buffer::<_, _, Gles2Renderbuffer>(
|
||||
node,
|
||||
renderer,
|
||||
session,
|
||||
¶ms,
|
||||
output.current_transform(),
|
||||
|node, renderer, dtr, age| {
|
||||
render_output::<_, Gles2Renderbuffer, Dmabuf>(
|
||||
node,
|
||||
renderer,
|
||||
dtr,
|
||||
age,
|
||||
common,
|
||||
&output,
|
||||
match session.cursor_mode() {
|
||||
ScreencopyCursorMode::Embedded => CursorMode::All,
|
||||
ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => {
|
||||
CursorMode::None
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
},
|
||||
)
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))
|
||||
}
|
||||
|
||||
pub fn render_workspace_to_buffer(
|
||||
state: &mut State,
|
||||
session: &Session,
|
||||
params: BufferParams,
|
||||
output: &Output,
|
||||
handle: &WorkspaceHandle,
|
||||
) -> Result<bool, (FailureReason, anyhow::Error)> {
|
||||
let node = node_from_params(¶ms, &mut state.backend, Some(output));
|
||||
let mut _tmp_multirenderer = None;
|
||||
let renderer = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
_tmp_multirenderer = Some(
|
||||
kms.api
|
||||
.renderer::<Gles2Renderbuffer>(node.as_ref().unwrap(), node.as_ref().unwrap())
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))?,
|
||||
);
|
||||
_tmp_multirenderer.as_mut().unwrap().as_mut()
|
||||
}
|
||||
BackendData::Winit(winit) => winit.backend.renderer(),
|
||||
BackendData::X11(x11) => &mut x11.renderer,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let common = &mut state.common;
|
||||
render_to_buffer::<_, _, Gles2Renderbuffer>(
|
||||
node,
|
||||
renderer,
|
||||
session,
|
||||
¶ms,
|
||||
output.current_transform(),
|
||||
|node, renderer, dtr, age| {
|
||||
render_workspace::<_, Gles2Renderbuffer, Dmabuf>(
|
||||
node,
|
||||
renderer,
|
||||
dtr,
|
||||
age,
|
||||
common,
|
||||
&output,
|
||||
handle,
|
||||
match session.cursor_mode() {
|
||||
ScreencopyCursorMode::Embedded => CursorMode::All,
|
||||
ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => {
|
||||
CursorMode::None
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
},
|
||||
)
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))
|
||||
}
|
||||
|
||||
pub fn render_window_to_buffer(
|
||||
state: &mut State,
|
||||
session: &Session,
|
||||
params: BufferParams,
|
||||
window: &Window,
|
||||
) -> Result<bool, (FailureReason, anyhow::Error)> {
|
||||
let geometry = window.geometry();
|
||||
|
||||
let node = node_from_params(¶ms, &mut state.backend, None);
|
||||
let mut _tmp_multirenderer = None;
|
||||
let renderer = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
_tmp_multirenderer = Some(
|
||||
kms.api
|
||||
.renderer::<Gles2Renderbuffer>(node.as_ref().unwrap(), node.as_ref().unwrap())
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))?,
|
||||
);
|
||||
_tmp_multirenderer.as_mut().unwrap().as_mut()
|
||||
}
|
||||
BackendData::Winit(winit) => winit.backend.renderer(),
|
||||
BackendData::X11(x11) => &mut x11.renderer,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render_to_buffer::<_, _, Gles2Renderbuffer>(
|
||||
node,
|
||||
renderer,
|
||||
session,
|
||||
¶ms,
|
||||
Transform::Normal,
|
||||
|_node, renderer, dtr, age| {
|
||||
// TODO cursor elements!
|
||||
let elements =
|
||||
AsRenderElements::<Gles2Renderer>::render_elements::<WaylandSurfaceRenderElement>(
|
||||
window,
|
||||
(-geometry.loc.x, -geometry.loc.y).into(),
|
||||
Scale::from(1.0),
|
||||
);
|
||||
|
||||
dtr.render_output(renderer, age, &elements, CLEAR_COLOR, None)
|
||||
},
|
||||
)
|
||||
.map_err(|err| (FailureReason::Unspec, err.into()))
|
||||
}
|
||||
|
||||
impl Common {
|
||||
pub fn still_pending(&mut self, session: Session, params: BufferParams) {
|
||||
match session.session_type() {
|
||||
SessionType::Output(output) => {
|
||||
if output
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.map_or(false, |sessions| {
|
||||
sessions.0.borrow().iter().any(|s| &*s == &session)
|
||||
})
|
||||
{
|
||||
output
|
||||
.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.push((session, params));
|
||||
}
|
||||
}
|
||||
SessionType::Workspace(_output, handle) => {
|
||||
if let Some(space) = self.shell.space_for_handle_mut(&handle) {
|
||||
if space.screencopy_sessions.iter().any(|s| s == &session) {
|
||||
space.pending_buffers.push((session, params));
|
||||
}
|
||||
}
|
||||
}
|
||||
SessionType::Window(window) => {
|
||||
if window
|
||||
.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.map_or(false, |sessions| {
|
||||
sessions.0.borrow().iter().any(|s| &*s == &session)
|
||||
})
|
||||
{
|
||||
window
|
||||
.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.unwrap()
|
||||
.borrow_mut()
|
||||
.push((session, params));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UserdataExt {
|
||||
fn sessions(&self) -> Vec<Session>;
|
||||
fn pending_buffers(
|
||||
&self,
|
||||
) -> std::iter::Flatten<std::option::IntoIter<std::vec::IntoIter<(Session, BufferParams)>>>;
|
||||
}
|
||||
|
||||
impl UserdataExt for Output {
|
||||
fn sessions(&self) -> Vec<Session> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions.0.borrow().iter().map(|s| s.0.clone()).collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn pending_buffers(
|
||||
&self,
|
||||
) -> std::iter::Flatten<std::option::IntoIter<std::vec::IntoIter<(Session, BufferParams)>>>
|
||||
{
|
||||
self.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.map(|pending| pending.borrow_mut().split_off(0).into_iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl UserdataExt for Window {
|
||||
fn sessions(&self) -> Vec<Session> {
|
||||
self.user_data()
|
||||
.get::<ScreencopySessions>()
|
||||
.map_or(Vec::new(), |sessions| {
|
||||
sessions.0.borrow().iter().map(|s| s.0.clone()).collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn pending_buffers(
|
||||
&self,
|
||||
) -> std::iter::Flatten<std::option::IntoIter<std::vec::IntoIter<(Session, BufferParams)>>>
|
||||
{
|
||||
self.user_data()
|
||||
.get::<PendingScreencopyBuffers>()
|
||||
.map(|pending| pending.borrow_mut().split_off(0).into_iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
|
@ -20,8 +20,7 @@ use smithay::{
|
|||
wayland::{
|
||||
seat::WaylandFocus,
|
||||
shell::xdg::{
|
||||
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler,
|
||||
XdgShellState,
|
||||
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -68,18 +67,6 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
|
||||
if let Configure::Toplevel(configure) = configure {
|
||||
// If we would re-position the window inside the grab we would get a weird jittery animation.
|
||||
// We only want to resize once the client has acknoledged & commited the new size,
|
||||
// so we need to carefully track the state through different handlers.
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface) {
|
||||
//TODO
|
||||
//crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(window, configure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
let kind = PopupKind::Xdg(surface);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ use std::{
|
|||
};
|
||||
|
||||
use cosmic_protocols::screencopy::v1::server::{
|
||||
zcosmic_screencopy_manager_v1::{self, CursorMode, ZcosmicScreencopyManagerV1},
|
||||
zcosmic_screencopy_session_v1::{self, FailureReason, InputType, ZcosmicScreencopySessionV1},
|
||||
zcosmic_screencopy_manager_v1::{self, CursorMode as WlCursorMode, ZcosmicScreencopyManagerV1},
|
||||
zcosmic_screencopy_session_v1::{
|
||||
self, BufferType, FailureReason, InputType, ZcosmicScreencopySessionV1,
|
||||
},
|
||||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
|
@ -15,15 +17,20 @@ use smithay::{
|
|||
drm::{DrmNode, NodeType},
|
||||
},
|
||||
desktop::Window,
|
||||
input::{Seat, SeatHandler},
|
||||
output::Output,
|
||||
reexports::wayland_server::{
|
||||
protocol::{
|
||||
wl_buffer::WlBuffer, wl_output::WlOutput, wl_seat::WlSeat, wl_shm::Format as ShmFormat,
|
||||
},
|
||||
protocol::{wl_buffer::WlBuffer, wl_output, wl_seat::WlSeat, wl_shm::Format as ShmFormat},
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
},
|
||||
utils::{user_data::UserDataMap, Buffer, Point, Rectangle, Size, Transform},
|
||||
utils::{user_data::UserDataMap, Buffer, IsAlive, Physical, Point, Rectangle, Size, Transform},
|
||||
};
|
||||
use wayland_backend::{protocol::WEnum, server::GlobalId};
|
||||
use wayland_backend::{
|
||||
protocol::WEnum,
|
||||
server::{GlobalId, ObjectId},
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
use super::{
|
||||
toplevel_info::window_from_handle,
|
||||
|
|
@ -36,7 +43,7 @@ pub struct ScreencopyState {
|
|||
}
|
||||
|
||||
pub struct ScreencopyGlobalData {
|
||||
cursor_modes: Vec<CursorMode>,
|
||||
cursor_modes: Vec<WlCursorMode>,
|
||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +61,7 @@ impl ScreencopyState {
|
|||
+ ScreencopyHandler
|
||||
+ WorkspaceHandler
|
||||
+ 'static,
|
||||
I: IntoIterator<Item = CursorMode>,
|
||||
I: IntoIterator<Item = WlCursorMode>,
|
||||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
ScreencopyState {
|
||||
|
|
@ -92,6 +99,7 @@ pub enum BufferInfo {
|
|||
pub struct SessionDataInnerInner {
|
||||
gone: bool,
|
||||
pending_buffer: Option<BufferParams>,
|
||||
_type: SessionType,
|
||||
aux: AuxData,
|
||||
}
|
||||
|
||||
|
|
@ -104,12 +112,29 @@ impl SessionDataInnerInner {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum SessionType {
|
||||
Output(Output),
|
||||
Workspace(Output, WorkspaceHandle),
|
||||
Window(Window),
|
||||
Cursor(Seat<State>),
|
||||
#[doc(hidden)]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AuxData {
|
||||
Normal { cursor_sessions: Vec<CursorSession> },
|
||||
Normal { cursor: CursorMode },
|
||||
Cursor { seat: WlSeat },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CursorMode {
|
||||
Captured(Vec<CursorSession>),
|
||||
Embedded,
|
||||
None,
|
||||
}
|
||||
|
||||
impl AuxData {
|
||||
pub fn seat(&self) -> &WlSeat {
|
||||
match self {
|
||||
|
|
@ -118,10 +143,19 @@ impl AuxData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn cursor_sessions(&self) -> &[CursorSession] {
|
||||
pub fn cursor(&self) -> &CursorMode {
|
||||
match self {
|
||||
AuxData::Normal { cursor_sessions } => &cursor_sessions,
|
||||
_ => unreachable!("Unwrapped cursor_session from aux data"),
|
||||
AuxData::Normal { cursor } => &cursor,
|
||||
_ => unreachable!("Unwrapped cursor from aux data"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CursorMode {
|
||||
pub fn sessions<'a>(&'a self) -> impl Iterator<Item = &'a CursorSession> {
|
||||
match self {
|
||||
CursorMode::Captured(sessions) => Some(sessions.iter()).into_iter().flatten(),
|
||||
_ => None.into_iter().flatten(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -136,30 +170,150 @@ pub type SessionData = Arc<SessionDataInner>;
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Session {
|
||||
obj: ZcosmicScreencopySessionV1,
|
||||
obj: SessionResource,
|
||||
data: SessionData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum SessionResource {
|
||||
Alive(ZcosmicScreencopySessionV1),
|
||||
Destroyed(ObjectId),
|
||||
}
|
||||
|
||||
impl SessionResource {
|
||||
fn client(&self) -> Option<Client> {
|
||||
match self {
|
||||
SessionResource::Alive(obj) => obj.client(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_info(
|
||||
&self,
|
||||
_type: BufferType,
|
||||
node: Option<String>,
|
||||
format: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
stride: u32,
|
||||
) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.buffer_info(_type, node, format, width, height, stride)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_done(&self) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.init_done()
|
||||
}
|
||||
}
|
||||
|
||||
fn transform(&self, transform: wl_output::Transform) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.transform(transform)
|
||||
}
|
||||
}
|
||||
|
||||
fn damage(&self, x: u32, y: u32, w: u32, h: u32) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.damage(x, y, w, h)
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_time(&self, time_sec_hi: u32, time_sec_lo: u32, time_nsec: u32) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.commit_time(time_sec_hi, time_sec_lo, time_nsec)
|
||||
}
|
||||
}
|
||||
|
||||
fn ready(&self) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.ready()
|
||||
}
|
||||
}
|
||||
|
||||
fn failed(&self, reason: FailureReason) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.failed(reason)
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_enter(&self, wl_seat: &WlSeat, input_type: InputType) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.cursor_enter(wl_seat, input_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_info(
|
||||
&self,
|
||||
wl_seat: &WlSeat,
|
||||
input_type: InputType,
|
||||
x: i32,
|
||||
y: i32,
|
||||
w: i32,
|
||||
h: i32,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.cursor_info(wl_seat, input_type, x, y, w, h, dx, dy)
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_leave(&self, wl_seat: &WlSeat, input_type: InputType) {
|
||||
if let SessionResource::Alive(obj) = self {
|
||||
obj.cursor_leave(wl_seat, input_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SessionResource {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(SessionResource::Alive(obj1), SessionResource::Alive(obj2)) => obj1 == obj2,
|
||||
(SessionResource::Alive(obj), SessionResource::Destroyed(id))
|
||||
| (SessionResource::Destroyed(id), SessionResource::Alive(obj)) => obj.id() == *id,
|
||||
(SessionResource::Destroyed(id1), SessionResource::Destroyed(id2)) => id1 == id2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Session {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.obj == other.obj
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle Alive
|
||||
|
||||
// TODO: Better errors
|
||||
|
||||
impl Session {
|
||||
pub fn cursor_enter(&self, seat: &WlSeat, input_type: InputType) {
|
||||
self.obj.cursor_enter(seat, input_type)
|
||||
pub fn cursor_enter<D: SeatHandler + 'static>(&self, seat: &Seat<D>, input_type: InputType) {
|
||||
if !self.alive() {
|
||||
return;
|
||||
}
|
||||
if let Some(client) = self.obj.client() {
|
||||
for wl_seat in seat.client_seats(&client) {
|
||||
self.obj.cursor_enter(&wl_seat, input_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_info(
|
||||
pub fn cursor_info<D: SeatHandler + 'static>(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
seat: &Seat<D>,
|
||||
input_type: InputType,
|
||||
geometry: Rectangle<i32, Buffer>,
|
||||
offset: Point<i32, Buffer>,
|
||||
) {
|
||||
if !self.alive() {
|
||||
return;
|
||||
}
|
||||
if let Some(client) = self.obj.client() {
|
||||
for wl_seat in seat.client_seats(&client) {
|
||||
self.obj.cursor_info(
|
||||
seat,
|
||||
&wl_seat,
|
||||
input_type,
|
||||
geometry.loc.x,
|
||||
geometry.loc.y,
|
||||
|
|
@ -169,9 +323,9 @@ impl Session {
|
|||
offset.y,
|
||||
);
|
||||
let data = self.data.inner.lock().unwrap();
|
||||
for cursor_session in data.aux.cursor_sessions() {
|
||||
for cursor_session in data.aux.cursor().sessions() {
|
||||
cursor_session.obj.cursor_info(
|
||||
seat,
|
||||
&wl_seat,
|
||||
input_type,
|
||||
geometry.loc.x,
|
||||
geometry.loc.y,
|
||||
|
|
@ -182,19 +336,31 @@ impl Session {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_leave(&self, seat: &WlSeat, input_type: InputType) {
|
||||
self.obj.cursor_leave(seat, input_type)
|
||||
pub fn cursor_leave<D: SeatHandler + 'static>(&self, seat: &Seat<D>, input_type: InputType) {
|
||||
if !self.alive() {
|
||||
return;
|
||||
}
|
||||
if let Some(client) = self.obj.client() {
|
||||
for wl_seat in seat.client_seats(&client) {
|
||||
self.obj.cursor_leave(&wl_seat, input_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_sessions(&self) -> impl Iterator<Item = CursorSession> {
|
||||
if !self.alive() {
|
||||
return Vec::new().into_iter();
|
||||
}
|
||||
self.data
|
||||
.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.aux
|
||||
.cursor_sessions()
|
||||
.iter()
|
||||
.cursor()
|
||||
.sessions()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
|
|
@ -203,9 +369,12 @@ impl Session {
|
|||
pub fn commit_buffer(
|
||||
&self,
|
||||
transform: Transform,
|
||||
damage: Vec<Rectangle<i32, Buffer>>,
|
||||
damage: Vec<Rectangle<i32, Physical>>,
|
||||
time: Option<Duration>,
|
||||
) {
|
||||
if !self.alive() {
|
||||
return;
|
||||
}
|
||||
self.obj.transform(transform.into());
|
||||
for rect in damage {
|
||||
self.obj.damage(
|
||||
|
|
@ -225,6 +394,9 @@ impl Session {
|
|||
}
|
||||
|
||||
pub fn failed(&self, reason: FailureReason) {
|
||||
if !self.alive() {
|
||||
return;
|
||||
}
|
||||
self.obj.failed(reason);
|
||||
self.data.inner.lock().unwrap().gone = true;
|
||||
}
|
||||
|
|
@ -232,11 +404,25 @@ impl Session {
|
|||
pub fn user_data(&self) -> &UserDataMap {
|
||||
&self.data.user_data
|
||||
}
|
||||
|
||||
pub fn session_type(&self) -> SessionType {
|
||||
self.data.inner.lock().unwrap()._type.clone()
|
||||
}
|
||||
|
||||
pub fn cursor_mode(&self) -> CursorMode {
|
||||
self.data.inner.lock().unwrap().aux.cursor().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for Session {
|
||||
fn alive(&self) -> bool {
|
||||
!self.data.inner.lock().unwrap().gone
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CursorSession {
|
||||
obj: ZcosmicScreencopySessionV1,
|
||||
obj: SessionResource,
|
||||
data: SessionData,
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +467,13 @@ impl CursorSession {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
impl IsAlive for CursorSession {
|
||||
fn alive(&self) -> bool {
|
||||
!self.data.inner.lock().unwrap().gone
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BufferParams {
|
||||
pub buffer: WlBuffer,
|
||||
pub node: Option<DrmNode>,
|
||||
|
|
@ -289,33 +481,28 @@ pub struct BufferParams {
|
|||
}
|
||||
|
||||
pub trait ScreencopyHandler {
|
||||
fn capture_output(
|
||||
&mut self,
|
||||
output: WlOutput,
|
||||
cursor: CursorMode,
|
||||
session: Session,
|
||||
) -> Vec<BufferInfo>;
|
||||
fn capture_output(&mut self, output: Output, session: Session) -> Vec<BufferInfo>;
|
||||
|
||||
fn capture_workspace(
|
||||
&mut self,
|
||||
workspace: WorkspaceHandle,
|
||||
output: WlOutput,
|
||||
cursor: CursorMode,
|
||||
output: Output,
|
||||
session: Session,
|
||||
) -> Vec<BufferInfo>;
|
||||
|
||||
fn capture_toplevel(
|
||||
&mut self,
|
||||
toplevel: Window,
|
||||
cursor: CursorMode,
|
||||
session: Session,
|
||||
) -> Vec<BufferInfo>;
|
||||
fn capture_toplevel(&mut self, toplevel: Window, session: Session) -> Vec<BufferInfo>;
|
||||
|
||||
fn capture_cursor(&mut self, session: CursorSession) -> Vec<BufferInfo>;
|
||||
|
||||
fn buffer_attached(&mut self, session: Session, buffer: BufferParams, on_damage: bool);
|
||||
|
||||
fn session_destroyed(&mut self, session: Session);
|
||||
fn cursor_session_destroyed(&mut self, session: CursorSession) {
|
||||
let _ = session;
|
||||
}
|
||||
|
||||
fn session_destroyed(&mut self, session: Session) {
|
||||
let _ = session;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> GlobalDispatch<ZcosmicScreencopyManagerV1, ScreencopyGlobalData, D> for ScreencopyState
|
||||
|
|
@ -349,8 +536,9 @@ where
|
|||
fn init_session<D>(
|
||||
data_init: &mut DataInit<'_, D>,
|
||||
session: New<ZcosmicScreencopySessionV1>,
|
||||
cursor: WEnum<CursorMode>,
|
||||
) -> Option<(Session, CursorMode)>
|
||||
cursor: WEnum<WlCursorMode>,
|
||||
_type: SessionType,
|
||||
) -> Option<Session>
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicScreencopyManagerV1, ScreencopyGlobalData>
|
||||
+ Dispatch<ZcosmicScreencopyManagerV1, ()>
|
||||
|
|
@ -364,25 +552,29 @@ where
|
|||
gone: false,
|
||||
pending_buffer: None,
|
||||
aux: AuxData::Normal {
|
||||
cursor_sessions: Vec::new(),
|
||||
cursor: match cursor.into_result() {
|
||||
Ok(WlCursorMode::Capture) => CursorMode::Captured(Vec::new()),
|
||||
Ok(WlCursorMode::Embedded) => CursorMode::Embedded,
|
||||
_ => CursorMode::None,
|
||||
},
|
||||
},
|
||||
_type,
|
||||
}),
|
||||
user_data: UserDataMap::new(),
|
||||
});
|
||||
let session = data_init.init(session, data.clone());
|
||||
|
||||
let cursor_mode = match cursor.into_result() {
|
||||
Ok(mode) => mode,
|
||||
Err(err) => {
|
||||
if let Err(err) = cursor.into_result() {
|
||||
slog_scope::warn!("Client did send unknown cursor mode: {}", err);
|
||||
session.failed(FailureReason::UnknownInput);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let session = Session {
|
||||
obj: SessionResource::Alive(session),
|
||||
data,
|
||||
};
|
||||
|
||||
let session = Session { obj: session, data };
|
||||
|
||||
Some((session, cursor_mode))
|
||||
Some(session)
|
||||
}
|
||||
|
||||
impl<D> Dispatch<ZcosmicScreencopyManagerV1, (), D> for ScreencopyState
|
||||
|
|
@ -408,68 +600,119 @@ where
|
|||
session,
|
||||
output,
|
||||
cursor,
|
||||
} => {
|
||||
let (session, cursor_mode) = match init_session(data_init, session, cursor) {
|
||||
} => match Output::from_resource(&output) {
|
||||
Some(output) => {
|
||||
let session = match init_session(
|
||||
data_init,
|
||||
session,
|
||||
cursor,
|
||||
SessionType::Output(output.clone()),
|
||||
) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let formats = state.capture_output(output, cursor_mode, session.clone());
|
||||
let formats = state.capture_output(output, session.clone());
|
||||
if !session.data.inner.lock().unwrap().gone {
|
||||
send_formats(&session.obj, formats);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let session =
|
||||
match init_session(data_init, session, cursor, SessionType::Unknown) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
session.failed(FailureReason::InvalidOutput);
|
||||
return;
|
||||
}
|
||||
},
|
||||
zcosmic_screencopy_manager_v1::Request::CaptureToplevel {
|
||||
session,
|
||||
toplevel,
|
||||
cursor,
|
||||
} => {
|
||||
let (session, cursor_mode) = match init_session(data_init, session, cursor) {
|
||||
} => match window_from_handle(toplevel) {
|
||||
Some(window) => {
|
||||
let session = match init_session(
|
||||
data_init,
|
||||
session,
|
||||
cursor,
|
||||
SessionType::Window(window.clone()),
|
||||
) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
match window_from_handle(toplevel) {
|
||||
Some(window) => {
|
||||
let formats = state.capture_toplevel(window, cursor_mode, session.clone());
|
||||
|
||||
let formats = state.capture_toplevel(window, session.clone());
|
||||
if !session.data.inner.lock().unwrap().gone {
|
||||
send_formats(&session.obj, formats);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let session =
|
||||
match init_session(data_init, session, cursor, SessionType::Unknown) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
session.obj.failed(FailureReason::ToplevelDestroyed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
zcosmic_screencopy_manager_v1::Request::CaptureWorkspace {
|
||||
session,
|
||||
workspace,
|
||||
output,
|
||||
cursor,
|
||||
} => {
|
||||
let (session, cursor_mode) = match init_session(data_init, session, cursor) {
|
||||
} => match Output::from_resource(&output) {
|
||||
Some(output) => match state.workspace_state().workspace_handle(&workspace) {
|
||||
Some(handle) => {
|
||||
let session = match init_session(
|
||||
data_init,
|
||||
session,
|
||||
cursor,
|
||||
SessionType::Workspace(output.clone(), handle.clone()),
|
||||
) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
match state.workspace_state().workspace_handle(&workspace) {
|
||||
Some(handle) => {
|
||||
let formats =
|
||||
state.capture_workspace(handle, output, cursor_mode, session.clone());
|
||||
let formats = state.capture_workspace(handle, output, session.clone());
|
||||
if !session.data.inner.lock().unwrap().gone {
|
||||
send_formats(&session.obj, formats);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let session =
|
||||
match init_session(data_init, session, cursor, SessionType::Unknown) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
session.failed(FailureReason::InvalidOutput);
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let session =
|
||||
match init_session(data_init, session, cursor, SessionType::Unknown) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
session.failed(FailureReason::InvalidOutput);
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -481,6 +724,7 @@ where
|
|||
+ Dispatch<ZcosmicScreencopyManagerV1, ()>
|
||||
+ Dispatch<ZcosmicScreencopySessionV1, SessionData>
|
||||
+ ScreencopyHandler
|
||||
+ WorkspaceHandler
|
||||
+ 'static,
|
||||
{
|
||||
fn request(
|
||||
|
|
@ -493,7 +737,11 @@ where
|
|||
data_init: &mut DataInit<'_, D>,
|
||||
) {
|
||||
match request {
|
||||
zcosmic_screencopy_session_v1::Request::CaptureCursor { session, seat } => {
|
||||
zcosmic_screencopy_session_v1::Request::CaptureCursor {
|
||||
session,
|
||||
seat: wl_seat,
|
||||
} => match Seat::from_resource(&wl_seat) {
|
||||
Some(seat) => {
|
||||
{
|
||||
let resource_data = data.inner.lock().unwrap();
|
||||
if resource_data.is_cursor() || resource_data.gone {
|
||||
|
|
@ -506,18 +754,38 @@ where
|
|||
inner: Mutex::new(SessionDataInnerInner {
|
||||
gone: false,
|
||||
pending_buffer: None,
|
||||
aux: AuxData::Cursor { seat },
|
||||
aux: AuxData::Cursor { seat: wl_seat },
|
||||
_type: SessionType::Cursor(seat),
|
||||
}),
|
||||
user_data: UserDataMap::new(),
|
||||
});
|
||||
let session = data_init.init(session, data.clone());
|
||||
|
||||
let cursor_session = CursorSession { obj: session, data };
|
||||
let cursor_session = CursorSession {
|
||||
obj: SessionResource::Alive(session),
|
||||
data,
|
||||
};
|
||||
let formats = state.capture_cursor(cursor_session.clone());
|
||||
if !cursor_session.data.inner.lock().unwrap().gone {
|
||||
send_formats(&cursor_session.obj, formats);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let session = match init_session(
|
||||
data_init,
|
||||
session,
|
||||
WEnum::Value(WlCursorMode::Capture),
|
||||
SessionType::Unknown,
|
||||
) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
session.failed(FailureReason::UnknownInput);
|
||||
return;
|
||||
}
|
||||
},
|
||||
zcosmic_screencopy_session_v1::Request::AttachBuffer { buffer, node, age } => {
|
||||
if data.inner.lock().unwrap().gone {
|
||||
resource.failed(FailureReason::Unspec);
|
||||
|
|
@ -541,7 +809,7 @@ where
|
|||
|
||||
if let Some(buffer) = data.inner.lock().unwrap().pending_buffer.take() {
|
||||
let session = Session {
|
||||
obj: resource.clone(),
|
||||
obj: SessionResource::Alive(resource.clone()),
|
||||
data: data.clone(),
|
||||
};
|
||||
state.buffer_attached(
|
||||
|
|
@ -563,9 +831,30 @@ where
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
state: &mut D,
|
||||
_client: wayland_backend::server::ClientId,
|
||||
resource: wayland_backend::server::ObjectId,
|
||||
data: &SessionData,
|
||||
) {
|
||||
if data.inner.lock().unwrap().is_cursor() {
|
||||
let session = CursorSession {
|
||||
obj: SessionResource::Destroyed(resource),
|
||||
data: data.clone(),
|
||||
};
|
||||
state.cursor_session_destroyed(session)
|
||||
} else {
|
||||
let session = Session {
|
||||
obj: SessionResource::Destroyed(resource),
|
||||
data: data.clone(),
|
||||
};
|
||||
state.session_destroyed(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_formats(session: &ZcosmicScreencopySessionV1, formats: Vec<BufferInfo>) {
|
||||
fn send_formats(session: &SessionResource, formats: Vec<BufferInfo>) {
|
||||
for format in formats {
|
||||
match format {
|
||||
BufferInfo::Dmabuf { node, format, size } => {
|
||||
|
|
|
|||
|
|
@ -413,9 +413,9 @@ fn send_toplevel_to_client<D>(
|
|||
.iter()
|
||||
.filter(|o| !handle_state.outputs.contains(o))
|
||||
{
|
||||
new_output.with_client_outputs(&client, |wl_output| {
|
||||
instance.output_enter(wl_output);
|
||||
});
|
||||
for wl_output in new_output.client_outputs(&client) {
|
||||
instance.output_enter(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
for old_output in handle_state
|
||||
|
|
@ -423,9 +423,9 @@ fn send_toplevel_to_client<D>(
|
|||
.iter()
|
||||
.filter(|o| !state.outputs.contains(o))
|
||||
{
|
||||
old_output.with_client_outputs(&client, |wl_output| {
|
||||
instance.output_leave(wl_output);
|
||||
});
|
||||
for wl_output in old_output.client_outputs(&client) {
|
||||
instance.output_leave(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
handle_state.outputs = state.outputs.clone();
|
||||
|
|
|
|||
|
|
@ -182,12 +182,12 @@ where
|
|||
let window = window_from_handle(toplevel).unwrap();
|
||||
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
||||
let mut toplevel_state = toplevel_state.lock().unwrap();
|
||||
if let Some(client) = surface.client_id() {
|
||||
if let Some(client) = surface.client() {
|
||||
if width == 0 && height == 0 {
|
||||
toplevel_state.rectangles.remove(&client);
|
||||
toplevel_state.rectangles.remove(&client.id());
|
||||
} else {
|
||||
toplevel_state.rectangles.insert(
|
||||
client,
|
||||
client.id(),
|
||||
(
|
||||
surface,
|
||||
Rectangle::from_loc_and_size((x, y), (width, height)),
|
||||
|
|
@ -207,7 +207,7 @@ where
|
|||
if !mng_state
|
||||
.instances
|
||||
.iter()
|
||||
.any(|i| i.client_id().map(|c| c == client).unwrap_or(false))
|
||||
.any(|i| i.client().map(|c| c.id() == client).unwrap_or(false))
|
||||
{
|
||||
for toplevel in state.toplevel_info_state_mut().toplevels.iter() {
|
||||
if let Some(toplevel_state) = toplevel.user_data().get::<ToplevelState>() {
|
||||
|
|
|
|||
|
|
@ -821,9 +821,9 @@ where
|
|||
.iter()
|
||||
.filter(|o| !handle_state.outputs.contains(o))
|
||||
{
|
||||
new_output.with_client_outputs(&client, |wl_output| {
|
||||
instance.output_enter(wl_output);
|
||||
});
|
||||
for wl_output in new_output.client_outputs(&client) {
|
||||
instance.output_enter(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
for old_output in handle_state
|
||||
|
|
@ -831,9 +831,9 @@ where
|
|||
.iter()
|
||||
.filter(|o| !group.outputs.contains(o))
|
||||
{
|
||||
old_output.with_client_outputs(&client, |wl_output| {
|
||||
instance.output_leave(wl_output);
|
||||
});
|
||||
for wl_output in old_output.client_outputs(&client) {
|
||||
instance.output_leave(&wl_output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
handle_state.outputs = group.outputs.clone();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue