2023-08-13 12:46:53 +04:00
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
|
|
|
|
|
|
use smithay::backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state};
|
2024-02-24 18:17:51 +01:00
|
|
|
use smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData};
|
2023-09-03 14:22:04 +04:00
|
|
|
use smithay::reexports::calloop::Interest;
|
2023-08-07 19:45:55 +04:00
|
|
|
use smithay::reexports::wayland_server::protocol::wl_buffer;
|
|
|
|
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
2023-09-03 14:22:04 +04:00
|
|
|
use smithay::reexports::wayland_server::{Client, Resource};
|
2023-08-07 19:45:55 +04:00
|
|
|
use smithay::wayland::buffer::BufferHandler;
|
|
|
|
|
use smithay::wayland::compositor::{
|
2024-08-18 12:42:50 +03:00
|
|
|
add_blocker, add_pre_commit_hook, get_parent, is_sync_subsurface, remove_pre_commit_hook,
|
|
|
|
|
with_states, BufferAssignment, CompositorClientState, CompositorHandler, CompositorState,
|
|
|
|
|
SurfaceAttributes,
|
2023-08-07 19:44:40 +04:00
|
|
|
};
|
2023-09-03 14:22:04 +04:00
|
|
|
use smithay::wayland::dmabuf::get_dmabuf;
|
2024-05-10 16:58:53 +04:00
|
|
|
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
|
2023-08-07 19:45:55 +04:00
|
|
|
use smithay::wayland::shm::{ShmHandler, ShmState};
|
|
|
|
|
use smithay::{delegate_compositor, delegate_shm};
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2024-04-13 09:12:32 +04:00
|
|
|
use super::xdg_shell::add_mapped_toplevel_pre_commit_hook;
|
2024-11-16 22:35:34 +01:00
|
|
|
use crate::handlers::XDG_ACTIVATION_TOKEN_TIMEOUT;
|
2024-12-26 09:37:38 +03:00
|
|
|
use crate::layout::{ActivateWindow, AddWindowTarget};
|
2023-09-03 14:10:02 +04:00
|
|
|
use crate::niri::{ClientState, State};
|
2024-05-29 13:32:11 +03:00
|
|
|
use crate::utils::send_scale_transform;
|
2024-08-23 15:41:06 +03:00
|
|
|
use crate::utils::transaction::Transaction;
|
2024-03-19 14:41:17 +04:00
|
|
|
use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
|
2023-08-07 19:44:40 +04:00
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
impl CompositorHandler for State {
|
2023-08-07 19:44:40 +04:00
|
|
|
fn compositor_state(&mut self) -> &mut CompositorState {
|
2023-09-03 14:10:02 +04:00
|
|
|
&mut self.niri.compositor_state
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
|
|
|
|
&client.get_data::<ClientState>().unwrap().compositor_state
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-26 00:15:46 +04:00
|
|
|
fn new_subsurface(&mut self, surface: &WlSurface, parent: &WlSurface) {
|
2023-12-19 13:29:22 +04:00
|
|
|
let mut root = parent.clone();
|
|
|
|
|
while let Some(parent) = get_parent(&root) {
|
|
|
|
|
root = parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(output) = self.niri.output_for_root(&root) {
|
2024-05-29 13:32:11 +03:00
|
|
|
let scale = output.current_scale();
|
2023-10-26 00:15:46 +04:00
|
|
|
let transform = output.current_transform();
|
|
|
|
|
with_states(surface, |data| {
|
2024-05-29 13:32:11 +03:00
|
|
|
send_scale_transform(surface, data, scale, transform);
|
2023-10-26 00:15:46 +04:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 14:22:04 +04:00
|
|
|
fn new_surface(&mut self, surface: &WlSurface) {
|
2024-08-18 12:42:50 +03:00
|
|
|
self.add_default_dmabuf_pre_commit_hook(surface);
|
2023-09-03 14:22:04 +04:00
|
|
|
}
|
|
|
|
|
|
2023-08-07 19:44:40 +04:00
|
|
|
fn commit(&mut self, surface: &WlSurface) {
|
2023-08-15 18:17:12 +04:00
|
|
|
let _span = tracy_client::span!("CompositorHandler::commit");
|
2024-08-22 14:44:11 +03:00
|
|
|
trace!(surface = ?surface.id(), "commit");
|
2023-08-10 14:12:20 +04:00
|
|
|
|
2024-04-13 09:12:32 +04:00
|
|
|
on_commit_buffer_handler::<Self>(surface);
|
|
|
|
|
self.backend.early_import(surface);
|
|
|
|
|
|
|
|
|
|
if is_sync_subsurface(surface) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-10 08:53:35 +04:00
|
|
|
let mut root_surface = surface.clone();
|
|
|
|
|
while let Some(parent) = get_parent(&root_surface) {
|
|
|
|
|
root_surface = parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the cached root surface.
|
|
|
|
|
self.niri
|
|
|
|
|
.root_surface
|
|
|
|
|
.insert(surface.clone(), root_surface.clone());
|
|
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
if surface == &root_surface {
|
|
|
|
|
// This is a root surface commit. It might have mapped a previously-unmapped toplevel.
|
2023-09-03 14:10:02 +04:00
|
|
|
if let Entry::Occupied(entry) = self.niri.unmapped_windows.entry(surface.clone()) {
|
2023-08-13 12:46:53 +04:00
|
|
|
let is_mapped =
|
2024-02-08 13:51:54 +04:00
|
|
|
with_renderer_surface_state(surface, |state| state.buffer().is_some())
|
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
error!("no renderer surface state even though we use commit handler");
|
|
|
|
|
false
|
|
|
|
|
});
|
2023-08-13 12:46:53 +04:00
|
|
|
|
|
|
|
|
if is_mapped {
|
|
|
|
|
// The toplevel got mapped.
|
2024-11-16 22:35:34 +01:00
|
|
|
let Unmapped {
|
|
|
|
|
window,
|
|
|
|
|
state,
|
|
|
|
|
activation_token_data,
|
|
|
|
|
} = entry.remove();
|
2024-02-23 13:57:56 +04:00
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
window.on_commit();
|
|
|
|
|
|
2024-04-13 09:12:32 +04:00
|
|
|
let toplevel = window.toplevel().expect("no X11 support");
|
|
|
|
|
|
2024-12-26 09:37:38 +03:00
|
|
|
let (rules, width, is_full_width, output, workspace_id) =
|
2024-02-23 14:24:39 +04:00
|
|
|
if let InitialConfigureState::Configured {
|
2024-03-19 14:41:17 +04:00
|
|
|
rules,
|
2024-02-23 14:24:39 +04:00
|
|
|
width,
|
|
|
|
|
is_full_width,
|
|
|
|
|
output,
|
2024-05-11 22:40:30 +02:00
|
|
|
workspace_name,
|
2024-02-23 14:24:39 +04:00
|
|
|
} = state
|
|
|
|
|
{
|
2024-02-23 13:57:56 +04:00
|
|
|
// Check that the output is still connected.
|
|
|
|
|
let output =
|
|
|
|
|
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
|
|
|
|
|
|
2024-06-09 10:50:22 +00:00
|
|
|
// Check that the workspace still exists.
|
2024-12-26 09:37:38 +03:00
|
|
|
let workspace_id = workspace_name
|
|
|
|
|
.as_deref()
|
|
|
|
|
.and_then(|n| self.niri.layout.find_workspace_by_name(n))
|
|
|
|
|
.map(|(_, ws)| ws.id());
|
2024-05-11 22:40:30 +02:00
|
|
|
|
2024-12-26 09:37:38 +03:00
|
|
|
(rules, width, is_full_width, output, workspace_id)
|
2024-02-23 13:57:56 +04:00
|
|
|
} else {
|
|
|
|
|
error!("window map must happen after initial configure");
|
2024-05-11 22:40:30 +02:00
|
|
|
(ResolvedWindowRules::empty(), None, false, None, None)
|
2024-02-23 13:57:56 +04:00
|
|
|
};
|
|
|
|
|
|
2024-11-29 21:11:02 +03:00
|
|
|
// The GTK about dialog sets min/max size after the initial configure but
|
|
|
|
|
// before mapping, so we need to compute open_floating at the last possible
|
|
|
|
|
// moment, that is here.
|
|
|
|
|
let is_floating = rules.compute_open_floating(toplevel);
|
|
|
|
|
|
2024-04-13 09:12:32 +04:00
|
|
|
let parent = toplevel
|
2024-02-07 10:49:01 +04:00
|
|
|
.parent()
|
|
|
|
|
.and_then(|parent| self.niri.layout.find_window_and_output(&parent))
|
2024-02-23 13:57:56 +04:00
|
|
|
// Only consider the parent if we configured the window for the same
|
|
|
|
|
// output.
|
|
|
|
|
//
|
|
|
|
|
// Normally when we're following the parent, the configured output will be
|
|
|
|
|
// None. If the configured output is set, that means it was set explicitly
|
|
|
|
|
// by a window rule or a fullscreen request.
|
|
|
|
|
.filter(|(_, parent_output)| {
|
|
|
|
|
output.is_none() || output.as_ref() == Some(*parent_output)
|
|
|
|
|
})
|
2024-03-19 14:41:17 +04:00
|
|
|
.map(|(mapped, _)| mapped.window.clone());
|
2024-02-13 17:46:37 +04:00
|
|
|
|
2024-08-18 12:42:50 +03:00
|
|
|
// The mapped pre-commit hook deals with dma-bufs on its own.
|
|
|
|
|
self.remove_default_dmabuf_pre_commit_hook(toplevel.wl_surface());
|
2024-04-13 09:12:32 +04:00
|
|
|
let hook = add_mapped_toplevel_pre_commit_hook(toplevel);
|
|
|
|
|
let mapped = Mapped::new(window, rules, hook);
|
2024-03-19 14:41:17 +04:00
|
|
|
let window = mapped.window.clone();
|
2024-02-07 10:49:01 +04:00
|
|
|
|
2024-11-16 22:35:34 +01:00
|
|
|
// Check the token timestamp again in case the window took a while between
|
|
|
|
|
// requesting activation and mapping.
|
|
|
|
|
let activate = match activation_token_data
|
|
|
|
|
.filter(|token| token.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT)
|
|
|
|
|
{
|
|
|
|
|
Some(_) => ActivateWindow::Yes,
|
2024-11-29 11:37:33 +03:00
|
|
|
None => {
|
|
|
|
|
let config = self.niri.config.borrow();
|
|
|
|
|
if config.debug.strict_new_window_focus_policy {
|
|
|
|
|
ActivateWindow::No
|
|
|
|
|
} else {
|
|
|
|
|
ActivateWindow::Smart
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-16 22:35:34 +01:00
|
|
|
};
|
|
|
|
|
|
2024-12-26 09:37:38 +03:00
|
|
|
let target = if let Some(p) = &parent {
|
|
|
|
|
// Open dialogs next to their parent window.
|
|
|
|
|
AddWindowTarget::NextTo(p)
|
|
|
|
|
} else if let Some(id) = workspace_id {
|
|
|
|
|
AddWindowTarget::Workspace(id)
|
2024-02-13 17:46:37 +04:00
|
|
|
} else if let Some(output) = &output {
|
2024-12-26 09:37:38 +03:00
|
|
|
AddWindowTarget::Output(output)
|
2024-02-07 10:49:01 +04:00
|
|
|
} else {
|
2024-12-26 09:37:38 +03:00
|
|
|
AddWindowTarget::Auto
|
2024-02-07 10:49:01 +04:00
|
|
|
};
|
2024-12-26 09:37:38 +03:00
|
|
|
let output = self.niri.layout.add_window(
|
|
|
|
|
mapped,
|
|
|
|
|
target,
|
|
|
|
|
width,
|
|
|
|
|
is_full_width,
|
|
|
|
|
is_floating,
|
|
|
|
|
activate,
|
|
|
|
|
);
|
2024-02-07 10:49:01 +04:00
|
|
|
|
|
|
|
|
if let Some(output) = output.cloned() {
|
2024-02-07 11:32:02 +04:00
|
|
|
self.niri.layout.start_open_animation_for_window(&window);
|
2024-03-17 07:48:34 +04:00
|
|
|
|
2024-11-30 09:18:33 +03:00
|
|
|
let new_focus = self.niri.layout.focus().map(|m| &m.window);
|
|
|
|
|
if new_focus == Some(&window) {
|
2024-03-17 07:48:34 +04:00
|
|
|
self.maybe_warp_cursor_to_focus();
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output);
|
2023-09-12 19:41:50 +04:00
|
|
|
}
|
2023-08-13 12:46:53 +04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The toplevel remains unmapped.
|
2024-02-23 13:57:56 +04:00
|
|
|
let unmapped = entry.get();
|
|
|
|
|
if unmapped.needs_initial_configure() {
|
2024-02-24 18:31:54 +01:00
|
|
|
let toplevel = unmapped.window.toplevel().expect("no x11 support").clone();
|
2024-02-23 13:57:56 +04:00
|
|
|
self.queue_initial_configure(toplevel);
|
2024-02-13 17:46:37 +04:00
|
|
|
}
|
2023-08-13 12:46:53 +04:00
|
|
|
return;
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2023-08-13 12:46:53 +04:00
|
|
|
|
|
|
|
|
// This is a commit of a previously-mapped root or a non-toplevel root.
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some((mapped, output)) = self.niri.layout.find_window_and_output(surface) {
|
|
|
|
|
let window = mapped.window.clone();
|
|
|
|
|
let output = output.clone();
|
2023-12-24 17:38:13 +04:00
|
|
|
|
2024-06-27 11:36:24 +04:00
|
|
|
#[cfg(feature = "xdp-gnome-screencast")]
|
|
|
|
|
let id = mapped.id();
|
|
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
// This is a commit of a previously-mapped toplevel.
|
|
|
|
|
let is_mapped =
|
2024-02-08 13:51:54 +04:00
|
|
|
with_renderer_surface_state(surface, |state| state.buffer().is_some())
|
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
error!("no renderer surface state even though we use commit handler");
|
|
|
|
|
false
|
|
|
|
|
});
|
2023-08-13 12:46:53 +04:00
|
|
|
|
2024-04-13 09:12:32 +04:00
|
|
|
// Must start the close animation before window.on_commit().
|
2024-08-23 15:41:06 +03:00
|
|
|
let transaction = Transaction::new();
|
2024-04-13 09:12:32 +04:00
|
|
|
if !is_mapped {
|
2024-08-23 15:41:06 +03:00
|
|
|
let blocker = transaction.blocker();
|
2024-04-09 22:37:10 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
|
|
|
|
self.niri
|
|
|
|
|
.layout
|
2024-08-23 15:41:06 +03:00
|
|
|
.start_close_animation_for_window(renderer, &window, blocker);
|
2024-04-09 22:37:10 +04:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.on_commit();
|
|
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
if !is_mapped {
|
|
|
|
|
// The toplevel got unmapped.
|
2024-04-09 21:59:30 +04:00
|
|
|
//
|
|
|
|
|
// Test client: wleird-unmap.
|
2024-11-30 09:18:33 +03:00
|
|
|
let active_window = self.niri.layout.focus().map(|m| &m.window);
|
2024-04-09 21:59:30 +04:00
|
|
|
let was_active = active_window == Some(&window);
|
|
|
|
|
|
2024-06-27 11:36:24 +04:00
|
|
|
#[cfg(feature = "xdp-gnome-screencast")]
|
|
|
|
|
self.niri
|
|
|
|
|
.stop_casts_for_target(crate::pw_utils::CastTarget::Window {
|
2024-08-31 10:22:57 +03:00
|
|
|
id: id.get(),
|
2024-06-27 11:36:24 +04:00
|
|
|
});
|
|
|
|
|
|
2024-08-23 18:51:47 +03:00
|
|
|
self.niri.layout.remove_window(&window, transaction.clone());
|
2024-08-18 12:42:50 +03:00
|
|
|
self.add_default_dmabuf_pre_commit_hook(surface);
|
2024-02-23 13:57:56 +04:00
|
|
|
|
2024-08-23 18:51:47 +03:00
|
|
|
// If this is the only instance, then this transaction will complete
|
|
|
|
|
// immediately, so no need to set the timer.
|
|
|
|
|
if !transaction.is_last() {
|
|
|
|
|
transaction.register_deadline_timer(&self.niri.event_loop);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-09 21:59:30 +04:00
|
|
|
if was_active {
|
|
|
|
|
self.maybe_warp_cursor_to_focus();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-23 13:57:56 +04:00
|
|
|
// Newly-unmapped toplevels must perform the initial commit-configure sequence
|
|
|
|
|
// afresh.
|
|
|
|
|
let unmapped = Unmapped::new(window);
|
|
|
|
|
self.niri.unmapped_windows.insert(surface.clone(), unmapped);
|
|
|
|
|
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output);
|
2023-08-13 12:46:53 +04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-10 16:58:53 +04:00
|
|
|
let serial = with_states(surface, |states| {
|
|
|
|
|
let role = states
|
|
|
|
|
.data_map
|
|
|
|
|
.get::<XdgToplevelSurfaceData>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap();
|
|
|
|
|
role.configure_serial
|
|
|
|
|
});
|
|
|
|
|
if serial.is_none() {
|
|
|
|
|
error!("commit on a mapped surface without a configured serial");
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
// The toplevel remains mapped.
|
2024-05-10 16:58:53 +04:00
|
|
|
self.niri.layout.update_window(&window, serial);
|
2023-08-13 12:46:53 +04:00
|
|
|
|
2023-12-19 20:56:00 +04:00
|
|
|
// Popup placement depends on window size which might have changed.
|
2024-11-30 09:18:33 +03:00
|
|
|
self.update_reactive_popups(&window);
|
2023-12-19 20:56:00 +04:00
|
|
|
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output);
|
2023-08-13 12:46:53 +04:00
|
|
|
return;
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
|
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
// This is a commit of a non-toplevel root.
|
|
|
|
|
}
|
2023-08-10 09:58:26 +04:00
|
|
|
|
2023-08-13 12:46:53 +04:00
|
|
|
// This is a commit of a non-root or a non-toplevel root.
|
2023-10-05 09:25:07 +04:00
|
|
|
let root_window_output = self.niri.layout.find_window_and_output(&root_surface);
|
2024-03-19 14:41:17 +04:00
|
|
|
if let Some((mapped, output)) = root_window_output {
|
|
|
|
|
let window = mapped.window.clone();
|
|
|
|
|
let output = output.clone();
|
2023-08-13 12:46:53 +04:00
|
|
|
window.on_commit();
|
2024-05-10 16:58:53 +04:00
|
|
|
self.niri.layout.update_window(&window, None);
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output);
|
2023-08-13 12:46:53 +04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This might be a popup.
|
|
|
|
|
self.popups_handle_commit(surface);
|
2023-09-03 14:10:02 +04:00
|
|
|
if let Some(popup) = self.niri.popups.find_popup(surface) {
|
2023-12-19 13:29:22 +04:00
|
|
|
if let Some(output) = self.output_for_popup(&popup) {
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output.clone());
|
2023-08-13 12:46:53 +04:00
|
|
|
}
|
2024-09-10 09:52:31 +03:00
|
|
|
return;
|
2023-08-13 12:46:53 +04:00
|
|
|
}
|
2023-08-15 12:49:26 +04:00
|
|
|
|
|
|
|
|
// This might be a layer-shell surface.
|
2024-09-10 09:52:31 +03:00
|
|
|
if self.layer_shell_handle_commit(surface) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-09-06 11:56:50 +04:00
|
|
|
|
|
|
|
|
// This might be a cursor surface.
|
2024-09-10 09:52:31 +03:00
|
|
|
if matches!(
|
|
|
|
|
&self.niri.cursor_manager.cursor_image(),
|
|
|
|
|
CursorImageStatus::Surface(s) if s == &root_surface
|
|
|
|
|
) {
|
2024-02-24 18:17:51 +01:00
|
|
|
// In case the cursor surface has been committed handle the role specific
|
|
|
|
|
// buffer offset by applying the offset on the cursor image hotspot
|
|
|
|
|
if surface == &root_surface {
|
|
|
|
|
with_states(surface, |states| {
|
|
|
|
|
let cursor_image_attributes = states.data_map.get::<CursorImageSurfaceData>();
|
|
|
|
|
|
|
|
|
|
if let Some(mut cursor_image_attributes) =
|
|
|
|
|
cursor_image_attributes.map(|attrs| attrs.lock().unwrap())
|
|
|
|
|
{
|
|
|
|
|
let buffer_delta = states
|
|
|
|
|
.cached_state
|
|
|
|
|
.get::<SurfaceAttributes>()
|
|
|
|
|
.current()
|
|
|
|
|
.buffer_delta
|
|
|
|
|
.take();
|
|
|
|
|
if let Some(buffer_delta) = buffer_delta {
|
|
|
|
|
cursor_image_attributes.hotspot -= buffer_delta;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 11:56:50 +04:00
|
|
|
// FIXME: granular redraws for cursors.
|
|
|
|
|
self.niri.queue_redraw_all();
|
2024-09-10 09:52:31 +03:00
|
|
|
return;
|
2023-09-06 11:56:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This might be a DnD icon surface.
|
2024-02-24 18:17:51 +01:00
|
|
|
if matches!(&self.niri.dnd_icon, Some(icon) if icon.surface == root_surface) {
|
|
|
|
|
let dnd_icon = self.niri.dnd_icon.as_mut().unwrap();
|
|
|
|
|
|
|
|
|
|
// In case the dnd surface has been committed handle the role specific
|
|
|
|
|
// buffer offset by applying the offset on the dnd icon offset
|
|
|
|
|
if surface == &dnd_icon.surface {
|
|
|
|
|
with_states(&dnd_icon.surface, |states| {
|
|
|
|
|
let buffer_delta = states
|
|
|
|
|
.cached_state
|
|
|
|
|
.get::<SurfaceAttributes>()
|
|
|
|
|
.current()
|
|
|
|
|
.buffer_delta
|
|
|
|
|
.take()
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
dnd_icon.offset += buffer_delta;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 11:56:50 +04:00
|
|
|
// FIXME: granular redraws for cursors.
|
|
|
|
|
self.niri.queue_redraw_all();
|
2024-09-10 09:52:31 +03:00
|
|
|
return;
|
2023-09-06 11:56:50 +04:00
|
|
|
}
|
2023-10-13 13:30:11 +04:00
|
|
|
|
|
|
|
|
// This might be a lock surface.
|
|
|
|
|
if self.niri.is_locked() {
|
|
|
|
|
for (output, state) in &self.niri.output_state {
|
|
|
|
|
if let Some(lock_surface) = &state.lock_surface {
|
2024-05-17 15:59:49 +04:00
|
|
|
if lock_surface.wl_surface() == &root_surface {
|
2024-03-23 14:16:36 +04:00
|
|
|
self.niri.queue_redraw(&output.clone());
|
2024-09-10 09:52:31 +03:00
|
|
|
return;
|
2023-10-13 13:30:11 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
2024-04-10 08:53:35 +04:00
|
|
|
|
|
|
|
|
fn destroyed(&mut self, surface: &WlSurface) {
|
|
|
|
|
// Clients may destroy their subsurfaces before the main surface. Ensure we have a snapshot
|
|
|
|
|
// when that happens, so that the closing animation includes all these subsurfaces.
|
|
|
|
|
//
|
2024-05-15 20:30:02 +04:00
|
|
|
// Test client: alacritty with CSD <= 0.13 (it was fixed in winit afterwards:
|
|
|
|
|
// https://github.com/rust-windowing/winit/pull/3625).
|
|
|
|
|
//
|
|
|
|
|
// This is still not perfect, as this function is called already after the (first)
|
|
|
|
|
// subsurface is destroyed; in the case of alacritty, this is the top CSD shadow. But, it
|
|
|
|
|
// gets most of the job done.
|
2024-04-10 08:53:35 +04:00
|
|
|
if let Some(root) = self.niri.root_surface.get(surface) {
|
|
|
|
|
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(root) {
|
2024-05-01 19:00:11 +04:00
|
|
|
let window = mapped.window.clone();
|
2024-04-10 08:53:35 +04:00
|
|
|
self.backend.with_primary_renderer(|renderer| {
|
2024-05-01 19:00:11 +04:00
|
|
|
self.niri.layout.store_unmap_snapshot(renderer, &window);
|
2024-04-10 08:53:35 +04:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.niri
|
|
|
|
|
.root_surface
|
|
|
|
|
.retain(|k, v| k != surface && v != surface);
|
2024-08-18 12:42:50 +03:00
|
|
|
|
|
|
|
|
self.niri.dmabuf_pre_commit_hook.remove(surface);
|
2024-04-10 08:53:35 +04:00
|
|
|
}
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
|
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
impl BufferHandler for State {
|
2023-08-07 19:44:40 +04:00
|
|
|
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
impl ShmHandler for State {
|
2023-08-07 19:44:40 +04:00
|
|
|
fn shm_state(&self) -> &ShmState {
|
2023-09-03 14:10:02 +04:00
|
|
|
&self.niri.shm_state
|
2023-08-07 19:44:40 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-03 14:10:02 +04:00
|
|
|
delegate_compositor!(State);
|
|
|
|
|
delegate_shm!(State);
|
2024-08-18 12:42:50 +03:00
|
|
|
|
|
|
|
|
impl State {
|
|
|
|
|
pub fn add_default_dmabuf_pre_commit_hook(&mut self, surface: &WlSurface) {
|
|
|
|
|
let hook = 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()
|
|
|
|
|
.buffer
|
|
|
|
|
.as_ref()
|
|
|
|
|
.and_then(|assignment| match assignment {
|
|
|
|
|
BufferAssignment::NewBuffer(buffer) => get_dmabuf(buffer).cloned().ok(),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
if let Some(dmabuf) = maybe_dmabuf {
|
|
|
|
|
if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
|
|
|
|
|
if let Some(client) = surface.client() {
|
|
|
|
|
let res =
|
|
|
|
|
state
|
|
|
|
|
.niri
|
|
|
|
|
.event_loop
|
|
|
|
|
.insert_source(source, move |_, _, state| {
|
|
|
|
|
let display_handle = state.niri.display_handle.clone();
|
|
|
|
|
state
|
|
|
|
|
.client_compositor_state(&client)
|
|
|
|
|
.blocker_cleared(state, &display_handle);
|
|
|
|
|
Ok(())
|
|
|
|
|
});
|
|
|
|
|
if res.is_ok() {
|
|
|
|
|
add_blocker(surface, blocker);
|
|
|
|
|
trace!("added default dmabuf blocker");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let s = surface.clone();
|
|
|
|
|
if let Some(prev) = self.niri.dmabuf_pre_commit_hook.insert(s, hook) {
|
|
|
|
|
error!("tried to add dmabuf pre-commit hook when there was already one");
|
|
|
|
|
remove_pre_commit_hook(surface, prev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn remove_default_dmabuf_pre_commit_hook(&mut self, surface: &WlSurface) {
|
|
|
|
|
if let Some(hook) = self.niri.dmabuf_pre_commit_hook.remove(surface) {
|
|
|
|
|
remove_pre_commit_hook(surface, hook);
|
|
|
|
|
} else {
|
|
|
|
|
error!("tried to remove dmabuf pre-commit hook but there was none");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|