smithay/wayland/compositor/mod.rs
1//! Utilities for handling surfaces, subsurfaces and regions
2//!
3//! This module provides automatic handling of surfaces, subsurfaces
4//! and region Wayland objects, by registering an implementation for
5//! for the [`wl_compositor`](wayland_server::protocol::wl_compositor)
6//! and [`wl_subcompositor`](wayland_server::protocol::wl_subcompositor) globals.
7//!
8//! ## Why use this implementation
9//!
10//! This implementation does a simple job: it stores in a coherent way the state of
11//! surface trees with subsurfaces, to provide you direct access to the tree
12//! structure and all surface attributes, and handles the application of double-buffered
13//! state.
14//!
15//! As such, you can, given a root surface with a role requiring it to be displayed,
16//! you can iterate over the whole tree of subsurfaces to recover all the metadata you
17//! need to display the subsurface tree.
18//!
19//! This implementation will not do anything more than present you the metadata specified by the
20//! client in a coherent and practical way. All the logic regarding drawing itself, and
21//! the positioning of windows (surface trees) one relative to another is out of its scope.
22//!
23//! ## How to use it
24//!
25//! ### Initialization
26//!
27//! To initialize this implementation create the [`CompositorState`], store it inside your `State` struct
28//! and implement the [`CompositorHandler`], as shown in this example:
29//!
30//! ```
31//! # extern crate wayland_server;
32//! # #[macro_use] extern crate smithay;
33//! use smithay::wayland::compositor::{CompositorState, CompositorClientState, CompositorHandler};
34//!
35//! # struct State { compositor_state: CompositorState }
36//! # struct ClientState { compositor_state: CompositorClientState }
37//! # impl wayland_server::backend::ClientData for ClientState {}
38//! # let mut display = wayland_server::Display::<State>::new().unwrap();
39//! // Create the compositor state
40//! let compositor_state = CompositorState::new::<State>(
41//! &display.handle(),
42//! );
43//!
44//! // insert the CompositorState into your state
45//! // ..
46//!
47//! // implement the necessary traits
48//! impl CompositorHandler for State {
49//! fn compositor_state(&mut self) -> &mut CompositorState {
50//! &mut self.compositor_state
51//! }
52//!
53//! fn client_compositor_state<'a>(&self, client: &'a wayland_server::Client) -> &'a CompositorClientState {
54//! &client.get_data::<ClientState>().unwrap().compositor_state
55//! }
56//!
57//! fn commit(&mut self, surface: &wayland_server::protocol::wl_surface::WlSurface) {
58//! // called on every buffer commit.
59//! // .. your implementation ..
60//! }
61//! }
62//!
63//! smithay::delegate_dispatch2!(State);
64//!
65//! // You're now ready to go!
66//! ```
67//!
68//! ### Use the surface states
69//!
70//! The main access to surface states is done through the [`with_states`] function, which
71//! gives you access to the [`SurfaceData`] instance associated with this surface. It acts
72//! as a general purpose container for associating state to a surface, double-buffered or
73//! not. See its documentation for more details.
74//!
75//! ### State application and hooks
76//!
77//! On commit of a surface several steps are taken to update the state of the surface. Actions
78//! are taken by smithay in the following order:
79//!
80//! 1. Pre Commit hooks registered to this surface are invoked. Such hooks can be registered using
81//! the [`add_pre_commit_hook`] function. They are typically used by protocol extensions that
82//! add state to a surface and need to check on commit that client did not request an
83//! illegal state before it is applied on commit.
84//! 2. The pending state is either applied and made current, or cached for later application
85//! is the surface is a synchronize subsurface. If the current state is applied, state
86//! of the synchronized children subsurface are applied as well at this point.
87//! 3. Post Commit hooks registered to this surface are invoked. Such hooks can be registered using
88//! the [`add_post_commit_hook`] function. They are typically used by abstractions that further process
89//! the state.
90//! 4. Your implementation of [`CompositorHandler::commit`] is invoked, so that you can access
91//! the new current state of the surface. The state of sync children subsurfaces of your
92//! surface may have changed as well, so this is the place to check it, using functions
93//! like [`with_surface_tree_upward`] or [`with_surface_tree_downward`]. On the other hand,
94//! if the surface is a sync subsurface, its current state will note have changed as
95//! the result of that commit. You can check if it is using [`is_sync_subsurface`].
96//! 5. If the surface is destroyed, destruction hooks are invoked. Such hooks can be registered
97//! using the [`add_destruction_hook`] function. They are typically used to cleanup associated
98//! state.
99//!
100//! ### Surface roles
101//!
102//! The wayland protocol specifies that a surface needs to be assigned a role before it can
103//! be displayed. Furthermore, a surface can only have a single role during its whole lifetime.
104//! Smithay represents this role as a `&'static str` identifier, that can only be set once
105//! on a surface. See [`give_role`] and [`get_role`] for details. This module manages the
106//! subsurface role, which is identified by the string `"subsurface"`.
107
108mod cache;
109mod handlers;
110mod transaction;
111mod tree;
112
113use std::cell::RefCell;
114use std::sync::Arc;
115use std::sync::atomic::Ordering;
116use std::{any::Any, sync::Mutex};
117
118pub use self::cache::{Cacheable, CachedState, MultiCache};
119pub use self::handlers::{RegionUserData, SubsurfaceCachedState, SubsurfaceUserData, SurfaceUserData};
120use self::transaction::TransactionQueue;
121pub use self::transaction::{Barrier, Blocker, BlockerState};
122pub use self::tree::{AlreadyHasRole, TraversalAction};
123use self::tree::{PrivateSurfaceData, SuggestedSurfaceState};
124use crate::input::touch::FrameMarker;
125use crate::utils::Transform;
126pub use crate::utils::hook::HookId;
127use crate::utils::{Buffer, Logical, Point, Rectangle, user_data::UserDataMap};
128use crate::wayland::GlobalData;
129use atomic_float::AtomicF64;
130use wayland_server::backend::GlobalId;
131use wayland_server::protocol::wl_compositor::WlCompositor;
132use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
133use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface::WlSurface};
134use wayland_server::{Client, DisplayHandle, GlobalDispatch, Resource};
135
136/// The role of a subsurface surface.
137pub const SUBSURFACE_ROLE: &str = "subsurface";
138
139/// Description of a part of a surface that
140/// should be considered damaged and needs to be redrawn
141#[derive(Debug, PartialEq, Eq)]
142pub enum Damage {
143 /// A rectangle containing the damaged zone, in surface coordinates
144 Surface(Rectangle<i32, Logical>),
145 /// A rectangle containing the damaged zone, in buffer coordinates
146 ///
147 /// Note: Buffer scaling must be taken into consideration
148 Buffer(Rectangle<i32, Buffer>),
149}
150
151/// The state container associated with a surface
152///
153/// This general-purpose container provides 2 main storages:
154///
155/// - the `data_map` storage has typemap semantics and allows you
156/// to associate and access non-buffered data to the surface
157/// - the `cached_state` storages allows you to associate state to
158/// the surface that follows the double-buffering semantics associated
159/// with the `commit` procedure of surfaces, also with typemap-like
160/// semantics
161///
162/// See the respective documentation of each container for its usage.
163///
164/// By default, all surfaces have a [`SurfaceAttributes`] cached state,
165/// and subsurface also have a [`SubsurfaceCachedState`] state as well.
166#[derive(Debug)]
167pub struct SurfaceData {
168 /// The current role of the surface.
169 ///
170 /// If `None` if the surface has not yet been assigned a role
171 pub role: Option<&'static str>,
172 /// The non-buffered typemap storage of this surface
173 pub data_map: UserDataMap,
174 /// The double-buffered typemap storage of this surface
175 pub cached_state: MultiCache,
176}
177
178/// New buffer assignation for a surface
179#[derive(Debug)]
180pub enum BufferAssignment {
181 /// The surface no longer has a buffer attached to it
182 Removed,
183 /// A new buffer has been attached
184 NewBuffer(wl_buffer::WlBuffer),
185}
186
187/// General state associated with a surface
188///
189/// The fields `buffer`, `damage` and `frame_callbacks` should be
190/// reset (by clearing their contents) once you have adequately
191/// processed them, as their contents are aggregated from commit to commit.
192#[derive(Debug)]
193pub struct SurfaceAttributes {
194 /// Buffer defining the contents of the surface
195 ///
196 /// You are free to set this field to `None` to avoid processing it several
197 /// times. It'll be set to `Some(...)` if the user attaches a buffer (or `NULL`) to
198 /// the surface, and be left to `None` if the user does not attach anything.
199 pub buffer: Option<BufferAssignment>,
200
201 /// Location of the new buffer relative to the previous one
202 ///
203 /// The x and y arguments specify the location of the new pending buffer's upper left corner,
204 /// relative to the current buffer's upper left corner, in surface-local coordinates.
205 ///
206 /// In other words, the x and y, combined with the new surface size define in which directions
207 /// the surface's size changes.
208 ///
209 /// You are free to set this field to `None` to avoid processing it several times.
210 pub buffer_delta: Option<Point<i32, Logical>>,
211
212 /// Scale of the contents of the buffer, for higher-resolution contents.
213 ///
214 /// If it matches the one of the output displaying this surface, no change
215 /// is necessary.
216 pub buffer_scale: i32,
217 /// Transform under which interpret the contents of the buffer
218 ///
219 /// If it matches the one of the output displaying this surface, no change
220 /// is necessary.
221 pub buffer_transform: wl_output::Transform,
222 /// Region of the surface that is guaranteed to be opaque
223 ///
224 /// By default the whole surface is potentially transparent
225 pub opaque_region: Option<RegionAttributes>,
226 /// Region of the surface that is sensitive to user input
227 ///
228 /// By default the whole surface should be sensitive
229 pub input_region: Option<RegionAttributes>,
230 /// Damage rectangle
231 ///
232 /// Hint provided by the client to suggest that only this part
233 /// of the surface was changed and needs to be redrawn
234 pub damage: Vec<Damage>,
235 /// The frame callbacks associated with this surface for the commit
236 ///
237 /// The server must send the notifications so that a client
238 /// will not send excessive updates, while still allowing
239 /// the highest possible update rate for clients that wait for the reply
240 /// before drawing again. The server should give some time for the client
241 /// to draw and commit after sending the frame callback events to let it
242 /// hit the next output refresh.
243 ///
244 /// A server should avoid signaling the frame callbacks if the
245 /// surface is not visible in any way, e.g. the surface is off-screen,
246 /// or completely obscured by other opaque surfaces.
247 ///
248 /// An example possibility would be to trigger it once the frame
249 /// associated with this commit has been displayed on the screen.
250 pub frame_callbacks: Vec<wl_callback::WlCallback>,
251
252 pub(crate) client_scale: f64,
253}
254
255impl Default for SurfaceAttributes {
256 fn default() -> SurfaceAttributes {
257 SurfaceAttributes {
258 buffer: None,
259 buffer_delta: None,
260 buffer_scale: 1,
261 buffer_transform: wl_output::Transform::Normal,
262 opaque_region: None,
263 input_region: None,
264 damage: Vec::new(),
265 frame_callbacks: Vec::new(),
266 client_scale: 1.,
267 }
268 }
269}
270
271/// Kind of a rectangle part of a region
272#[derive(Copy, Clone, Debug)]
273pub enum RectangleKind {
274 /// This rectangle should be added to the region
275 Add,
276 /// The intersection of this rectangle with the region should
277 /// be removed from the region
278 Subtract,
279}
280
281/// Description of the contents of a region
282///
283/// A region is defined as an union and difference of rectangle.
284///
285/// This struct contains an ordered `Vec` containing the rectangles defining
286/// a region. They should be added or subtracted in this order to compute the
287/// actual contents of the region.
288#[derive(Clone, Debug, Default)]
289pub struct RegionAttributes {
290 /// List of rectangle part of this region
291 pub rects: Vec<(RectangleKind, Rectangle<i32, Logical>)>,
292}
293
294impl RegionAttributes {
295 /// Checks whether given point is inside the region.
296 pub fn contains<P: Into<Point<i32, Logical>>>(&self, point: P) -> bool {
297 let point: Point<i32, Logical> = point.into();
298 let mut contains = false;
299 for (kind, rect) in &self.rects {
300 if rect.contains(point) {
301 match kind {
302 RectangleKind::Add => contains = true,
303 RectangleKind::Subtract => contains = false,
304 }
305 }
306 }
307 contains
308 }
309}
310
311/// Access the data of a surface tree from bottom to top
312///
313/// You provide three closures, a "filter", a "processor" and a "post filter".
314///
315/// The first closure is initially called on a surface to determine if its children
316/// should be processed as well. It returns a `TraversalAction<T>` reflecting that.
317///
318/// The second closure is supposed to do the actual processing. The processing closure for
319/// a surface may be called after the processing closure of some of its children, depending
320/// on the stack ordering the client requested. Here the surfaces are processed in the same
321/// order as they are supposed to be drawn: from the farthest of the screen to the nearest.
322///
323/// The third closure is called once all the subtree of a node has been processed, and gives
324/// an opportunity for early-stopping. If it returns `true` the processing will continue,
325/// while if it returns `false` it'll stop.
326///
327/// The arguments provided to the closures are, in this order:
328///
329/// - The surface object itself
330/// - a mutable reference to its surface attribute data
331/// - a mutable reference to its role data,
332/// - a custom value that is passed in a fold-like manner, but only from the output of a parent
333/// to its children. See [`TraversalAction`] for details.
334///
335/// If the surface not managed by the `CompositorGlobal` that provided this token, this
336/// will panic (having more than one compositor is not supported).
337pub fn with_surface_tree_upward<F1, F2, F3, T>(
338 surface: &WlSurface,
339 initial: T,
340 filter: F1,
341 processor: F2,
342 post_filter: F3,
343) where
344 F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
345 F2: FnMut(&WlSurface, &SurfaceData, &T),
346 F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
347{
348 PrivateSurfaceData::map_tree(surface, &initial, filter, processor, post_filter, false);
349}
350
351/// Access the data of a surface tree from top to bottom
352///
353/// Behavior is the same as [`with_surface_tree_upward`], but the processing is done in the reverse order,
354/// from the nearest of the screen to the deepest.
355///
356/// This would typically be used to find out which surface of a subsurface tree has been clicked for example.
357pub fn with_surface_tree_downward<F1, F2, F3, T>(
358 surface: &WlSurface,
359 initial: T,
360 filter: F1,
361 processor: F2,
362 post_filter: F3,
363) where
364 F1: FnMut(&WlSurface, &SurfaceData, &T) -> TraversalAction<T>,
365 F2: FnMut(&WlSurface, &SurfaceData, &T),
366 F3: FnMut(&WlSurface, &SurfaceData, &T) -> bool,
367{
368 PrivateSurfaceData::map_tree(surface, &initial, filter, processor, post_filter, true);
369}
370
371/// Retrieve the parent of this surface
372///
373/// Returns `None` is this surface is a root surface
374pub fn get_parent(surface: &WlSurface) -> Option<WlSurface> {
375 PrivateSurfaceData::get_parent(surface)
376}
377
378/// Retrieve the children of this surface
379pub fn get_children(surface: &WlSurface) -> Vec<WlSurface> {
380 PrivateSurfaceData::get_children(surface)
381}
382
383/// Check if this subsurface is a synchronized subsurface
384pub fn is_sync_subsurface(surface: &WlSurface) -> bool {
385 self::handlers::is_effectively_sync(surface)
386}
387
388/// Get the current role of this surface
389pub fn get_role(surface: &WlSurface) -> Option<&'static str> {
390 PrivateSurfaceData::get_role(surface)
391}
392
393/// Register that this surface has given role
394///
395/// Fails if the surface already has a role.
396pub fn give_role(surface: &WlSurface, role: &'static str) -> Result<(), AlreadyHasRole> {
397 PrivateSurfaceData::set_role(surface, role)
398}
399
400/// Access the states associated to this surface
401pub fn with_states<F, T>(surface: &WlSurface, f: F) -> T
402where
403 F: FnOnce(&SurfaceData) -> T,
404{
405 PrivateSurfaceData::with_states(surface, f)
406}
407
408/// Send the `scale` and `transform` preferences for the given surface when it supports them.
409///
410/// The new state is only send when it differs from the already cached one on the calling thread.
411pub fn send_surface_state(surface: &WlSurface, data: &SurfaceData, scale: i32, transform: Transform) {
412 if surface.version() < 6 {
413 return;
414 }
415
416 // NOTE we insert default for checks below to work properly.
417 let mut storage = data
418 .data_map
419 .get_or_insert(|| RefCell::new(SuggestedSurfaceState::default()))
420 .borrow_mut();
421
422 if storage.scale != scale {
423 surface.preferred_buffer_scale(scale);
424 storage.scale = scale;
425 }
426
427 let transform = transform.into();
428 if storage.transform != transform {
429 surface.preferred_buffer_transform(transform);
430 storage.transform = transform;
431 }
432}
433
434/// Retrieve the metadata associated with a `wl_region`
435///
436/// If the region is not managed by the `CompositorGlobal` that provided this token, this
437/// will panic (having more than one compositor is not supported).
438pub fn get_region_attributes(region: &wl_region::WlRegion) -> RegionAttributes {
439 match region.data::<RegionUserData>() {
440 Some(data) => data.inner.lock().unwrap().clone(),
441 None => panic!("Accessing the data of foreign regions is not supported."),
442 }
443}
444
445/// Register a pre-commit hook to be invoked on surface commit
446///
447/// It'll be invoked on surface commit, *before* the new state is merged into the current state.
448///
449/// Protocol implementations should use this for error checking, but they should **not** apply
450/// state changes here, since the commit may be further arbitrarily delayed by blockers. Use a
451/// post-commit hook to apply state changes (i.e. copy last acked state to current).
452///
453/// Compositors should use this for adding blockers if needed, e.g. the DMA-BUF readiness blocker.
454pub fn add_pre_commit_hook<D, F>(surface: &WlSurface, hook: F) -> HookId
455where
456 F: Fn(&mut D, &DisplayHandle, &WlSurface) + Send + Sync + 'static,
457 D: 'static,
458{
459 let (user_state_type_id, user_state_type) = surface.data::<SurfaceUserData>().unwrap().user_state_type;
460 assert_eq!(
461 std::any::TypeId::of::<D>(),
462 user_state_type_id,
463 "D has to equal D used in CompositorState::new<D>(), {} != {}",
464 std::any::type_name::<D>(),
465 user_state_type,
466 );
467
468 let hook = move |state: &mut dyn Any, dh: &DisplayHandle, surface: &WlSurface| {
469 let state = state.downcast_mut::<D>().unwrap();
470 hook(state, dh, surface);
471 };
472 PrivateSurfaceData::add_pre_commit_hook(surface, hook)
473}
474
475/// Register a post-commit hook to be invoked on surface commit
476///
477/// It'll be invoked on surface commit, *after* the new state is merged into the current state,
478/// after all commit blockers complete.
479///
480/// Protocol implementations should apply state changes here, i.e. copy last acked state into
481/// current.
482pub fn add_post_commit_hook<D, F>(surface: &WlSurface, hook: F) -> HookId
483where
484 F: Fn(&mut D, &DisplayHandle, &WlSurface) + Send + Sync + 'static,
485 D: 'static,
486{
487 let (user_state_type_id, user_state_type) = surface.data::<SurfaceUserData>().unwrap().user_state_type;
488 assert_eq!(
489 std::any::TypeId::of::<D>(),
490 user_state_type_id,
491 "D has to equal D used in CompositorState::new<D>(), {} != {}",
492 std::any::type_name::<D>(),
493 user_state_type,
494 );
495
496 let hook = move |state: &mut dyn Any, dh: &DisplayHandle, surface: &WlSurface| {
497 let state = state.downcast_mut::<D>().unwrap();
498 hook(state, dh, surface);
499 };
500 PrivateSurfaceData::add_post_commit_hook(surface, hook)
501}
502
503/// Register a destruction hook to be invoked on surface destruction
504///
505/// It'll be invoked when the surface is destroyed (either explicitly by the client or on
506/// client disconnect).
507///
508/// D generic is the compositor state, same as used in `CompositorState::new<D>()`
509pub fn add_destruction_hook<D, F>(surface: &WlSurface, hook: F) -> HookId
510where
511 F: Fn(&mut D, &WlSurface) + Send + Sync + 'static,
512 D: 'static,
513{
514 let (user_state_type_id, user_state_type) = surface.data::<SurfaceUserData>().unwrap().user_state_type;
515 assert_eq!(
516 std::any::TypeId::of::<D>(),
517 user_state_type_id,
518 "D has to equal D used in CompositorState::new<D>(), {} != {}",
519 std::any::type_name::<D>(),
520 user_state_type,
521 );
522
523 let hook = move |state: &mut dyn Any, surface: &WlSurface| {
524 let state = state.downcast_mut::<D>().unwrap();
525 hook(state, surface);
526 };
527 PrivateSurfaceData::add_destruction_hook(surface, hook)
528}
529
530/// Unregister a pre-commit hook
531pub fn remove_pre_commit_hook(surface: &WlSurface, hook_id: &HookId) {
532 PrivateSurfaceData::remove_pre_commit_hook(surface, hook_id)
533}
534
535/// Unregister a post-commit hook
536pub fn remove_post_commit_hook(surface: &WlSurface, hook_id: &HookId) {
537 PrivateSurfaceData::remove_post_commit_hook(surface, hook_id)
538}
539
540/// Unregister a destruction hook
541pub fn remove_destruction_hook(surface: &WlSurface, hook_id: &HookId) {
542 PrivateSurfaceData::remove_destruction_hook(surface, hook_id)
543}
544
545/// Adds a blocker for the currently queued up state changes of the given surface.
546///
547/// Blockers will delay the pending state to be applied on the next commit until
548/// all of them return the state `Released`. Any blocker returning `Cancelled` will
549/// discard all changes.
550///
551/// The module will only evaluate blocker states on commit. If a blocker
552/// becomes ready later, a call to [`CompositorClientState::blocker_cleared`] is necessary
553/// to trigger a re-evaluation.
554pub fn add_blocker(surface: &WlSurface, blocker: impl Blocker + Send + 'static) {
555 PrivateSurfaceData::add_blocker(surface, blocker)
556}
557
558/// Handler trait for compositor
559pub trait CompositorHandler {
560 /// [CompositorState] getter
561 fn compositor_state(&mut self) -> &mut CompositorState;
562 /// [CompositorClientState] getter
563 ///
564 /// The compositor implementation needs some state to be client specific.
565 /// Downstream is expected to store this inside its `ClientData` implementation(s)
566 /// to ensure automatic cleanup of the state, when the client disconnects.
567 fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState;
568
569 /// New surface handler.
570 ///
571 /// This handler can be used to setup hooks (see [`add_pre_commit_hook`]/[`add_post_commit_hook`]/[`add_destruction_hook`]),
572 /// but not much else. The surface has no role or attached data at this point and cannot be rendered.
573 fn new_surface(&mut self, surface: &WlSurface) {
574 let _ = surface;
575 }
576
577 /// New subsurface handler.
578 ///
579 /// This handler can be used to run extra logic when subsurface is getting created. This
580 /// is an addition to [`new_surface`], which will be run for the subsurface surface anyway.
581 ///
582 /// When your compositor knows beforehand where it'll position subsurfaces it can send
583 /// [`send_surface_state`] to them.
584 ///
585 /// [`new_surface`]: Self::new_surface
586 fn new_subsurface(&mut self, surface: &WlSurface, parent: &WlSurface) {
587 let _ = surface;
588 let _ = parent;
589 }
590
591 /// Surface commit handler
592 ///
593 /// This is called when any changed state from a commit actually becomes visible.
594 /// That might be some time after the actual commit has taken place, if the
595 /// state changes are delayed by an added blocker (see [`add_blocker`]).
596 ///
597 /// If you need to handle a commit as soon as it occurs, you might want to consider
598 /// using a pre-commit hook (see [`add_pre_commit_hook`]).
599 fn commit(&mut self, surface: &WlSurface);
600
601 /// The surface was destroyed.
602 ///
603 /// This allows the compositor to clean up any uses of the surface.
604 ///
605 /// Note: Destruction might happen explicitly by the client, or implicitly
606 /// when the client quits. In case of implicit destruction the order the
607 /// callbacks are called in is undefined.
608 fn destroyed(&mut self, _surface: &WlSurface) {}
609}
610
611/// State of a compositor
612#[derive(Debug)]
613pub struct CompositorState {
614 compositor: GlobalId,
615 subcompositor: GlobalId,
616 surfaces: Vec<WlSurface>,
617}
618
619/// Per-client state of a compositor
620#[derive(Debug)]
621pub struct CompositorClientState {
622 queue: Mutex<Option<TransactionQueue>>,
623 scale_override: Arc<AtomicF64>,
624 last_touch_frame: Mutex<Option<FrameMarker>>,
625}
626
627impl Default for CompositorClientState {
628 fn default() -> Self {
629 CompositorClientState {
630 queue: Mutex::new(None),
631 scale_override: Arc::new(AtomicF64::new(1.)),
632 last_touch_frame: Mutex::new(None),
633 }
634 }
635}
636
637impl CompositorClientState {
638 /// To be called, when a previously added blocker (via [`add_blocker`])
639 /// got `Released` or `Cancelled` from being `Pending` previously for any
640 /// surface belonging to this client.
641 pub fn blocker_cleared<D: CompositorHandler + 'static>(&self, state: &mut D, dh: &DisplayHandle) {
642 let transactions = if let Some(queue) = self.queue.lock().unwrap().as_mut() {
643 queue.take_ready()
644 } else {
645 Vec::new()
646 };
647
648 for transaction in transactions {
649 transaction.apply(dh, state)
650 }
651 }
652
653 /// Set an additionally mapping between smithay's `Logical` coordinate space
654 /// and this clients logical coordinate space.
655 ///
656 /// This is used in the same way as if the client was setting the
657 /// surface.buffer_scale on every surface i.e a value of 2.0 will make
658 /// the windows appear smaller on a regular DPI monitor.
659 ///
660 /// Only the minimal set of protocols used by xwayland are guaranteed to be supported.
661 ///
662 /// Buffer sizes are unaffected.
663 pub fn set_client_scale(&self, new_scale: f64) {
664 self.scale_override.store(new_scale, Ordering::Release);
665 }
666
667 /// Get the scale factor of the additional mapping between smithay's `Logical`
668 /// coordinate space and this clients logical coordinate space.
669 ///
670 /// This is mainly intended to support out-of-tree protocol implementations.
671 pub fn client_scale(&self) -> f64 {
672 self.scale_override.load(Ordering::Acquire)
673 }
674
675 pub(crate) fn clone_client_scale(&self) -> Arc<AtomicF64> {
676 self.scale_override.clone()
677 }
678
679 pub(crate) fn set_last_touch_frame(&self, frame_marker: FrameMarker) {
680 *self.last_touch_frame.lock().unwrap() = Some(frame_marker);
681 }
682
683 pub(crate) fn last_touch_frame(&self) -> Option<FrameMarker> {
684 *self.last_touch_frame.lock().unwrap()
685 }
686}
687
688impl CompositorState {
689 /// Create new [`wl_compositor`] version 5 and [`wl_subcompositor`] globals.
690 ///
691 /// It returns the two global handles, in case you wish to remove these globals from
692 /// the event loop in the future.
693 ///
694 /// [`wl_compositor`]: wayland_server::protocol::wl_compositor
695 /// [`wl_subcompositor`]: wayland_server::protocol::wl_subcompositor
696 pub fn new<D>(display: &DisplayHandle) -> Self
697 where
698 D: GlobalDispatch<WlCompositor, GlobalData> + GlobalDispatch<WlSubcompositor, GlobalData> + 'static,
699 {
700 Self::new_with_version::<D>(display, 5)
701 }
702
703 /// The same as [`new`], but binds at least version 6 of [`wl_compositor`].
704 ///
705 /// This means that for clients to scale and apply transformation with
706 /// non-default values [`send_surface_state`] must be used.
707 ///
708 /// [`new`]: Self::new
709 /// [`wl_compositor`]: wayland_server::protocol::wl_compositor
710 pub fn new_v6<D>(display: &DisplayHandle) -> Self
711 where
712 D: GlobalDispatch<WlCompositor, GlobalData> + GlobalDispatch<WlSubcompositor, GlobalData> + 'static,
713 {
714 Self::new_with_version::<D>(display, 6)
715 }
716
717 fn new_with_version<D>(display: &DisplayHandle, version: u32) -> Self
718 where
719 D: GlobalDispatch<WlCompositor, GlobalData> + GlobalDispatch<WlSubcompositor, GlobalData> + 'static,
720 {
721 let compositor = display.create_global::<D, WlCompositor, _>(version, GlobalData);
722 let subcompositor = display.create_global::<D, WlSubcompositor, _>(1, GlobalData);
723
724 CompositorState {
725 compositor,
726 subcompositor,
727 surfaces: Vec::new(),
728 }
729 }
730
731 /// Get id of compositor global
732 pub fn compositor_global(&self) -> GlobalId {
733 self.compositor.clone()
734 }
735
736 /// Get id of subcompositor global
737 pub fn subcompositor_global(&self) -> GlobalId {
738 self.subcompositor.clone()
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 fn region_attributes_empty() {
748 let region = RegionAttributes { rects: vec![] };
749 assert!(!region.contains((0, 0)));
750 }
751
752 #[test]
753 fn region_attributes_add() {
754 let region = RegionAttributes {
755 rects: vec![(RectangleKind::Add, Rectangle::from_size((10, 10).into()))],
756 };
757
758 assert!(region.contains((0, 0)));
759 }
760
761 #[test]
762 fn region_attributes_add_subtract() {
763 let region = RegionAttributes {
764 rects: vec![
765 (RectangleKind::Add, Rectangle::from_size((10, 10).into())),
766 (RectangleKind::Subtract, Rectangle::from_size((5, 5).into())),
767 ],
768 };
769
770 assert!(!region.contains((0, 0)));
771 assert!(region.contains((5, 5)));
772 }
773
774 #[test]
775 fn region_attributes_add_subtract_add() {
776 let region = RegionAttributes {
777 rects: vec![
778 (RectangleKind::Add, Rectangle::from_size((10, 10).into())),
779 (RectangleKind::Subtract, Rectangle::from_size((5, 5).into())),
780 (RectangleKind::Add, Rectangle::new((2, 2).into(), (2, 2).into())),
781 ],
782 };
783
784 assert!(!region.contains((0, 0)));
785 assert!(region.contains((5, 5)));
786 assert!(region.contains((2, 2)));
787 }
788}