cosmic-comp/src/wayland/handlers/compositor.rs

304 lines
11 KiB
Rust
Raw Normal View History

// SPDX-License-Identifier: GPL-3.0-only
2024-03-12 19:42:48 +01:00
use crate::{shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*};
2023-05-12 20:01:37 +02:00
use calloop::Interest;
use smithay::{
backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state},
2022-07-04 16:00:29 +02:00
delegate_compositor,
2023-01-03 19:17:51 +01:00
desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType},
2023-05-12 20:01:37 +02:00
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
utils::SERIAL_COUNTER,
wayland::{
2023-05-12 20:01:37 +02:00
compositor::{
add_blocker, add_pre_commit_hook, with_states, BufferAssignment, CompositorClientState,
CompositorHandler, CompositorState, SurfaceAttributes,
},
dmabuf::get_dmabuf,
seat::WaylandFocus,
shell::{
2022-07-04 16:00:29 +02:00
wlr_layer::LayerSurfaceAttributes,
xdg::{
2022-07-04 16:00:29 +02:00
ToplevelSurface, XdgPopupSurfaceRoleAttributes, XdgToplevelSurfaceRoleAttributes,
},
},
},
2024-06-18 19:23:16 -07:00
xwayland::XWaylandClientData,
};
2022-07-04 16:00:29 +02:00
use std::sync::Mutex;
2024-04-10 15:49:08 +02:00
fn toplevel_ensure_initial_configure(toplevel: &ToplevelSurface) -> bool {
// send the initial configure if relevant
let initial_configure_sent = with_states(toplevel.wl_surface(), |states| {
states
.data_map
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
// TODO: query expected size from shell (without inserting and mapping)
toplevel.with_pending_state(|states| states.size = None);
toplevel.send_configure();
}
2024-04-10 15:49:08 +02:00
initial_configure_sent
}
2024-04-10 15:49:08 +02:00
fn xdg_popup_ensure_initial_configure(popup: &PopupKind) {
if let PopupKind::Xdg(ref popup) = popup {
let initial_configure_sent = with_states(popup.wl_surface(), |states| {
states
.data_map
2024-04-10 15:49:08 +02:00
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
2024-04-10 15:49:08 +02:00
// NOTE: This should never fail as the initial configure is always
// allowed.
popup.send_configure().expect("initial configure failed");
}
}
}
2024-04-10 15:49:08 +02:00
fn layer_surface_check_inital_configure(surface: &LayerSurface) -> bool {
// send the initial configure if relevant
let initial_configure_sent = with_states(surface.wl_surface(), |states| {
states
.data_map
.get::<Mutex<LayerSurfaceAttributes>>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
initial_configure_sent
}
2023-05-12 20:01:37 +02:00
pub fn client_compositor_state<'a>(client: &'a Client) -> &'a CompositorClientState {
if let Some(state) = client.get_data::<XWaylandClientData>() {
return &state.compositor_state;
}
if let Some(state) = client.get_data::<ClientState>() {
return &state.compositor_client_state;
}
panic!("Unknown client data type")
}
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.common.compositor_state
}
2023-05-12 20:01:37 +02:00
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
client_compositor_state(client)
}
fn new_surface(&mut self, surface: &WlSurface) {
add_pre_commit_hook::<Self, _>(surface, move |state, _dh, surface| {
let maybe_dmabuf = with_states(surface, |surface_data| {
surface_data
.cached_state
.get::<SurfaceAttributes>()
.pending()
2023-05-12 20:01:37 +02:00
.buffer
.as_ref()
.and_then(|assignment| match assignment {
BufferAssignment::NewBuffer(buffer) => get_dmabuf(buffer).ok().cloned(),
2023-05-12 20:01:37 +02:00
_ => None,
})
});
if let Some(dmabuf) = maybe_dmabuf {
if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
let client = surface.client().unwrap();
let res =
state
.common
.event_loop_handle
2023-09-29 21:33:16 +02:00
.insert_source(source, move |_, _, state| {
let dh = state.common.display_handle.clone();
state
2023-05-12 20:01:37 +02:00
.client_compositor_state(&client)
2023-09-29 21:33:16 +02:00
.blocker_cleared(state, &dh);
2023-05-12 20:01:37 +02:00
Ok(())
});
if res.is_ok() {
add_blocker(surface, blocker);
}
}
}
2024-03-26 16:45:30 +01:00
});
2023-05-12 20:01:37 +02:00
}
2022-08-31 13:01:23 +02:00
fn commit(&mut self, surface: &WlSurface) {
// first load the buffer for various smithay helper functions (which also initializes the RendererSurfaceState)
2023-05-12 20:01:37 +02:00
on_commit_buffer_handler::<Self>(surface);
2024-04-10 15:49:08 +02:00
// and refresh smithays internal state
self.common.on_commit(surface);
// handle initial configure events and map windows if necessary
let mapped = self.send_initial_configure_and_map(surface);
2024-04-10 15:49:08 +02:00
let mut shell = self.common.shell.write().unwrap();
// schedule a new render
if let Some(output) = shell.visible_output_for_surface(surface) {
self.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
if mapped {
return;
}
2024-04-10 15:49:08 +02:00
if let Some(popup) = self.common.popups.find_popup(surface) {
xdg_popup_ensure_initial_configure(&popup);
return;
}
if with_renderer_surface_state(surface, |state| state.buffer().is_none()).unwrap_or(false) {
// handle null-commits causing weird conflicts:
// session-lock disallows null commits
// if it was a move-grab?
2024-04-10 15:49:08 +02:00
let seat = shell.seats.last_active().clone();
let moved_window = seat
.user_data()
.get::<SeatMoveGrabState>()
.unwrap()
.borrow()
.as_ref()
.and_then(|state| {
state
.element()
.windows()
.any(|(s, _)| {
s.wl_surface()
.as_deref()
.map(|s| s == surface)
.unwrap_or(false)
})
.then(|| state.element())
});
if let Some(mut window) = moved_window {
if window.is_stack() {
let stack = window.stack_ref_mut().unwrap();
if let Some(i) = stack.surfaces().position(|s| {
s.wl_surface()
.as_deref()
.map(|s| s == surface)
.unwrap_or(false)
}) {
stack.remove_idx(i);
}
} else {
2024-04-10 15:49:08 +02:00
std::mem::drop(shell);
seat.get_pointer()
.unwrap()
.unset_grab(self, SERIAL_COUNTER.next_serial(), 0);
2024-04-10 15:49:08 +02:00
return;
}
2022-11-03 18:51:27 +01:00
}
2022-07-04 16:00:29 +02:00
// if it was a layer-shell surface?
// ignore, that will affect recompute normally
// if it was a sticky / floating / tiled window
// we could unmap, I guess?
// if it was an x11 surface => do nothing, we will get a separate UnmapNotify anyway
} else {
// handle some special cases, like grabs and changing layer surfaces
// 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.
2024-04-10 15:49:08 +02:00
if let Some(element) = shell.element_for_surface(surface).cloned() {
crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location(
element.clone(),
2024-04-10 15:49:08 +02:00
&mut *shell,
);
}
}
// re-arrange layer-surfaces (commits may change size and positioning)
2024-04-10 15:49:08 +02:00
let layer_output = shell
.outputs()
.find(|o| {
let map = layer_map_for_output(o);
map.layer_for_surface(surface, WindowSurfaceType::ALL)
.is_some()
})
.cloned();
2024-04-10 15:49:08 +02:00
if let Some(output) = layer_output {
let changed = layer_map_for_output(&output).arrange();
if changed {
2024-04-10 15:49:08 +02:00
shell.workspaces.recalculate();
}
2022-07-05 20:43:30 +02:00
}
}
}
impl State {
fn send_initial_configure_and_map(&mut self, surface: &WlSurface) -> bool {
let mut shell = self.common.shell.write().unwrap();
if let Some((window, _, _)) = shell
.pending_windows
.iter()
.find(|(window, _, _)| window.wl_surface().as_deref() == Some(surface))
.cloned()
{
if let Some(toplevel) = window.0.toplevel() {
if toplevel_ensure_initial_configure(&toplevel)
&& with_renderer_surface_state(&surface, |state| state.buffer().is_some())
.unwrap_or(false)
{
window.on_commit();
let res = shell.map_window(
&window,
&mut self.common.toplevel_info_state,
&mut self.common.workspace_state,
&self.common.event_loop_handle,
);
if let Some(target) = res {
let seat = shell.seats.last_active().clone();
std::mem::drop(shell);
Shell::set_focus(self, Some(&target), &seat, None);
return true;
}
}
}
}
if let Some((layer_surface, _, _)) = shell
.pending_layers
.iter()
.find(|(layer_surface, _, _)| layer_surface.wl_surface() == surface)
.cloned()
{
if !layer_surface_check_inital_configure(&layer_surface) {
// compute initial dimensions by mapping
if let Some(target) = shell.map_layer(&layer_surface) {
let seat = shell.seats.last_active().clone();
std::mem::drop(shell);
Shell::set_focus(self, Some(&target), &seat, None);
}
layer_surface.layer_surface().send_configure();
return true;
}
};
false
}
}
delegate_compositor!(State);