wip: screencopy

This commit is contained in:
Victoria Brekenfeld 2022-11-03 18:51:27 +01:00
parent dd100d65e4
commit 5a4df346a8
26 changed files with 2046 additions and 327 deletions

View file

@ -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<_>>()
}),
);
}
}
}

View file

@ -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;

View 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(&params.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(&params.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(&params.buffer) {
if with_buffer_contents(&params.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(&params.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, &params.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, &params.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(&params, &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,
&params,
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(&params, &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,
&params,
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(&params, &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,
&params,
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()
}
}

View file

@ -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);

View file

@ -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,65 +170,197 @@ 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>,
) {
self.obj.cursor_info(
seat,
input_type,
geometry.loc.x,
geometry.loc.y,
geometry.size.w,
geometry.size.h,
offset.x,
offset.y,
);
let data = self.data.inner.lock().unwrap();
for cursor_session in data.aux.cursor_sessions() {
cursor_session.obj.cursor_info(
seat,
input_type,
geometry.loc.x,
geometry.loc.y,
geometry.size.w,
geometry.size.h,
offset.x,
offset.y,
);
if !self.alive() {
return;
}
if let Some(client) = self.obj.client() {
for wl_seat in seat.client_seats(&client) {
self.obj.cursor_info(
&wl_seat,
input_type,
geometry.loc.x,
geometry.loc.y,
geometry.size.w,
geometry.size.h,
offset.x,
offset.y,
);
let data = self.data.inner.lock().unwrap();
for cursor_session in data.aux.cursor().sessions() {
cursor_session.obj.cursor_info(
&wl_seat,
input_type,
geometry.loc.x,
geometry.loc.y,
geometry.size.w,
geometry.size.h,
offset.x,
offset.y,
);
}
}
}
}
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) => {
slog_scope::warn!("Client did send unknown cursor mode: {}", err);
session.failed(FailureReason::UnknownInput);
return None;
}
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) {
Some(result) => result,
None => {
return;
} => 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, session.clone());
if !session.data.inner.lock().unwrap().gone {
send_formats(&session.obj, formats);
}
};
let formats = state.capture_output(output, cursor_mode, 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) {
Some(result) => result,
None => {
return;
}
};
match window_from_handle(toplevel) {
Some(window) => {
let formats = state.capture_toplevel(window, cursor_mode, session.clone());
if !session.data.inner.lock().unwrap().gone {
send_formats(&session.obj, formats);
} => 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;
}
}
None => {
session.obj.failed(FailureReason::ToplevelDestroyed);
return;
};
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) {
Some(result) => result,
None => {
return;
}
};
match state.workspace_state().workspace_handle(&workspace) {
} => match Output::from_resource(&output) {
Some(output) => match state.workspace_state().workspace_handle(&workspace) {
Some(handle) => {
let formats =
state.capture_workspace(handle, output, cursor_mode, session.clone());
let session = match init_session(
data_init,
session,
cursor,
SessionType::Workspace(output.clone(), handle.clone()),
) {
Some(result) => result,
None => {
return;
}
};
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,31 +737,55 @@ where
data_init: &mut DataInit<'_, D>,
) {
match request {
zcosmic_screencopy_session_v1::Request::CaptureCursor { session, seat } => {
{
let resource_data = data.inner.lock().unwrap();
if resource_data.is_cursor() || resource_data.gone {
resource.failed(FailureReason::Unspec);
return;
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 {
resource.failed(FailureReason::Unspec);
return;
}
}
let data = Arc::new(SessionDataInner {
inner: Mutex::new(SessionDataInnerInner {
gone: false,
pending_buffer: None,
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: 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);
}
}
let data = Arc::new(SessionDataInner {
inner: Mutex::new(SessionDataInnerInner {
gone: false,
pending_buffer: None,
aux: AuxData::Cursor { seat },
}),
user_data: UserDataMap::new(),
});
let session = data_init.init(session, data.clone());
let cursor_session = CursorSession { obj: 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 } => {

View file

@ -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();

View file

@ -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>() {

View file

@ -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();