wayland: Explicitly handle destroyed surfaces

This commit is contained in:
Victoria Brekenfeld 2022-11-08 10:32:53 +01:00
parent 78b9b07cec
commit 38c0acb943
7 changed files with 191 additions and 157 deletions

View file

@ -9,11 +9,7 @@ use smithay::{
};
use anyhow::{Context, Result};
use std::{
ffi::OsString,
os::unix::prelude::AsRawFd,
sync::{atomic::Ordering, Arc},
};
use std::{ffi::OsString, os::unix::prelude::AsRawFd, sync::Arc};
pub mod backend;
pub mod config;
@ -71,18 +67,6 @@ fn main() -> Result<()> {
data.state.common.shell.refresh();
state::Common::refresh_focus(&mut data.state);
// 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,
None,
)
}
}
// send out events
let _ = data.display.flush_clients();
})?;

View file

@ -39,7 +39,6 @@ use smithay::{
use std::{
cell::RefCell,
ffi::OsString,
sync::{atomic::AtomicBool, Arc},
time::{Duration, Instant},
};
#[cfg(feature = "debug")]
@ -75,7 +74,6 @@ pub struct Common {
//pub output_conf: ConfigurationManager,
pub shell: Shell,
pub dirty_flag: Arc<AtomicBool>,
seats: Vec<Seat<State>>,
last_active_seat: Option<Seat<State>>,
@ -242,11 +240,6 @@ impl State {
let shell = Shell::new(&config, dh);
#[cfg(not(feature = "debug"))]
let dirty_flag = Arc::new(AtomicBool::new(false));
#[cfg(feature = "debug")]
let dirty_flag = log.dirty_flag.clone();
State {
common: Common {
config,
@ -256,7 +249,6 @@ impl State {
event_loop_signal: signal,
shell,
dirty_flag,
seats: Vec::new(),
last_active_seat: None,

View file

@ -1,19 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
state::BackendData,
utils::prelude::*,
wayland::{
handlers::screencopy::UserdataExt,
protocols::screencopy::{BufferParams, Session as ScreencopySession, SessionType},
},
};
use crate::{state::BackendData, utils::prelude::*, wayland::protocols::screencopy::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::{
@ -26,7 +18,7 @@ use smithay::{
};
use std::sync::Mutex;
use super::screencopy::{self, PendingScreencopyBuffers};
use super::screencopy::PendingScreencopyBuffers;
impl State {
fn early_import_surface(&mut self, surface: &WlSurface) {
@ -170,36 +162,11 @@ impl CompositorHandler for State {
);
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);
}
}
});
}
}
}
//handle window screencopy sessions
self.schedule_window_session(surface);
// We need to know every potential output for importing to the right gpu and scheduling a render,
// so call this only after every potential surface map operation has been done.
self.early_import_surface(surface);
@ -216,66 +183,7 @@ 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!();
}
}
}
}
let mut scheduled_sessions = self.schedule_workspace_sessions(surface);
// schedule a new render
for output in self.common.shell.visible_outputs_for_surface(surface) {

View file

@ -3,7 +3,7 @@
use crate::utils::prelude::*;
use smithay::{
delegate_layer_shell,
desktop::{LayerSurface, PopupKind},
desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType},
output::Output,
reexports::wayland_server::protocol::wl_output::WlOutput,
wayland::shell::{
@ -14,6 +14,8 @@ use smithay::{
},
};
use super::screencopy::PendingScreencopyBuffers;
impl WlrLayerShellHandler for State {
fn shell_state(&mut self) -> &mut WlrLayerShellState {
&mut self.common.shell.layer_shell_state
@ -26,7 +28,6 @@ impl WlrLayerShellHandler for State {
_layer: Layer,
namespace: String,
) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
let seat = self.common.last_active_seat().clone();
let output = wl_output
.as_ref()
@ -51,6 +52,42 @@ impl WlrLayerShellHandler for State {
.unwrap();
}
}
fn layer_destroyed(&mut self, surface: WlrLayerSurface) {
let maybe_output = self
.common
.shell
.outputs()
.find(|o| {
let map = layer_map_for_output(o);
map.layer_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
.is_some()
})
.cloned();
if let Some(output) = maybe_output {
let mut map = layer_map_for_output(&output);
let layer = map
.layer_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
.unwrap()
.clone();
map.unmap_layer(&layer);
// collect screencopy sessions needing an update
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(sessions.borrow_mut().drain(..));
}
self.backend.schedule_render(
&self.common.event_loop_handle,
&output,
scheduled_sessions,
);
}
}
}
delegate_layer_shell!(State);

View file

@ -19,28 +19,3 @@ pub mod viewporter;
pub mod wl_drm;
pub mod workspace;
pub mod xdg_shell;
use crate::state::Common;
use smithay::{
reexports::wayland_server::protocol::wl_surface::WlSurface,
wayland::compositor::{add_destruction_hook, with_states},
};
fn mark_dirty_on_drop(state: &Common, wl_surface: &WlSurface) {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let dirty = state.dirty_flag.clone();
struct DirtyFlag(Arc<AtomicBool>);
with_states(wl_surface, |data| {
data.data_map.insert_if_missing(|| DirtyFlag(dirty));
});
add_destruction_hook(wl_surface, |data| {
if let Some(DirtyFlag(dirty)) = data.data_map.get::<DirtyFlag>() {
dirty.store(true, Ordering::SeqCst);
}
})
}

View file

@ -26,10 +26,10 @@ use smithay::{
desktop::Window,
output::Output,
reexports::wayland_server::{
protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat, wl_surface::WlSurface},
Resource,
},
utils::{Physical, Rectangle, Scale, Transform},
utils::{IsAlive, Physical, Rectangle, Scale, Transform},
wayland::{
dmabuf::get_dmabuf,
shm::{with_buffer_contents, with_buffer_contents_mut},
@ -869,4 +869,105 @@ impl UserdataExt for Window {
}
}
impl State {
pub fn schedule_window_session(&mut self, surface: &WlSurface) {
if let Some(element) = self.common.shell.element_for_surface(surface).cloned() {
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 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);
}
}
});
}
}
}
}
pub fn schedule_workspace_sessions(
&mut self,
surface: &WlSurface,
) -> Option<Vec<(Session, BufferParams)>> {
// here we store additional workspace_sessions, we should handle, when rendering the corresponding output anyway
let mut output_sessions: Option<Vec<(Session, 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);
output_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 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!();
}
}
}
}
output_sessions
}
}
delegate_screencopy!(State);

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::utils::prelude::*;
use crate::{utils::prelude::*, wayland::protocols::screencopy::SessionType};
use smithay::{
delegate_xdg_shell,
desktop::{
@ -26,6 +26,8 @@ use smithay::{
};
use std::cell::Cell;
use super::screencopy::PendingScreencopyBuffers;
pub mod popup;
pub type PopupGrabData = Cell<Option<PopupGrab<State>>>;
@ -36,8 +38,6 @@ impl XdgShellHandler for State {
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
let seat = self.common.last_active_seat().clone();
let window = Window::new(Kind::Xdg(surface));
self.common.shell.toplevel_info_state.new_toplevel(&window);
@ -46,8 +46,6 @@ impl XdgShellHandler for State {
}
fn new_popup(&mut self, surface: PopupSurface, positioner: PositionerState) {
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
surface.with_pending_state(|state| {
state.geometry = positioner.get_geometry();
state.positioner = positioner;
@ -280,6 +278,45 @@ impl XdgShellHandler for State {
}
}
}
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
let outputs = self
.common
.shell
.visible_outputs_for_surface(surface.wl_surface())
.collect::<Vec<_>>();
for output in outputs.iter() {
self.common.shell.active_space_mut(output).refresh();
}
// screencopy
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
for output in outputs.into_iter() {
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(sessions.borrow_mut().drain(..));
}
self.backend.schedule_render(
&self.common.event_loop_handle,
&output,
scheduled_sessions.as_ref().map(|sessions| {
sessions
.iter()
.filter(|(s, _)| match s.session_type() {
SessionType::Output(o) | SessionType::Workspace(o, _)
if o == output =>
{
true
}
_ => false,
})
.cloned()
.collect::<Vec<_>>()
}),
);
}
}
}
fn check_grab_preconditions(