| 1 | use std::mem; |
| 2 | use std::os::unix::io::OwnedFd; |
| 3 | use std::sync::MutexGuard; |
| 4 | use std::sync::{ |
| 5 | atomic::{AtomicI32, Ordering}, |
| 6 | Arc, Mutex, |
| 7 | }; |
| 8 | |
| 9 | use wayland_client::{ |
| 10 | globals::{BindError, GlobalList}, |
| 11 | protocol::{ |
| 12 | wl_callback, wl_compositor, wl_output, wl_region, |
| 13 | wl_surface::{self, WlSurface}, |
| 14 | }, |
| 15 | Connection, Dispatch, Proxy, QueueHandle, WEnum, |
| 16 | }; |
| 17 | |
| 18 | use crate::{ |
| 19 | error::GlobalError, |
| 20 | globals::{GlobalData, ProvidesBoundGlobal}, |
| 21 | output::{OutputData, OutputHandler, OutputState, ScaleWatcherHandle}, |
| 22 | }; |
| 23 | |
| 24 | pub trait CompositorHandler: Sized { |
| 25 | /// The surface has either been moved into or out of an output and the output has a different scale factor. |
| 26 | fn scale_factor_changed( |
| 27 | &mut self, |
| 28 | conn: &Connection, |
| 29 | qh: &QueueHandle<Self>, |
| 30 | surface: &wl_surface::WlSurface, |
| 31 | new_factor: i32, |
| 32 | ); |
| 33 | |
| 34 | /// The surface has either been moved into or out of an output and the output has different transform. |
| 35 | fn transform_changed( |
| 36 | &mut self, |
| 37 | conn: &Connection, |
| 38 | qh: &QueueHandle<Self>, |
| 39 | surface: &wl_surface::WlSurface, |
| 40 | new_transform: wl_output::Transform, |
| 41 | ); |
| 42 | |
| 43 | /// A frame callback has been completed. |
| 44 | /// |
| 45 | /// Frame callbacks are used to avoid updating surfaces that are not currently visible. If a |
| 46 | /// frame callback is requested prior to committing a surface, the client should avoid drawing |
| 47 | /// to that surface until the callback completes. See the |
| 48 | /// [`WlSurface::frame`](wl_surface::WlSurface::frame) request for more details. |
| 49 | /// |
| 50 | /// This function will be called if you request a frame callback by passing the surface itself |
| 51 | /// as the userdata (`surface.frame(&queue, &surface)`); you can also implement [`Dispatch`] |
| 52 | /// for other values to more easily dispatch rendering for specific surface types. |
| 53 | fn frame( |
| 54 | &mut self, |
| 55 | conn: &Connection, |
| 56 | qh: &QueueHandle<Self>, |
| 57 | surface: &wl_surface::WlSurface, |
| 58 | time: u32, |
| 59 | ); |
| 60 | |
| 61 | /// The surface has entered an output. |
| 62 | fn surface_enter( |
| 63 | &mut self, |
| 64 | conn: &Connection, |
| 65 | qh: &QueueHandle<Self>, |
| 66 | surface: &wl_surface::WlSurface, |
| 67 | output: &wl_output::WlOutput, |
| 68 | ); |
| 69 | |
| 70 | /// The surface has left an output. |
| 71 | fn surface_leave( |
| 72 | &mut self, |
| 73 | conn: &Connection, |
| 74 | qh: &QueueHandle<Self>, |
| 75 | surface: &wl_surface::WlSurface, |
| 76 | output: &wl_output::WlOutput, |
| 77 | ); |
| 78 | } |
| 79 | |
| 80 | pub trait SurfaceDataExt: Send + Sync { |
| 81 | fn surface_data(&self) -> &SurfaceData; |
| 82 | } |
| 83 | |
| 84 | impl SurfaceDataExt for SurfaceData { |
| 85 | fn surface_data(&self) -> &SurfaceData { |
| 86 | self |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | #[derive (Clone, Debug)] |
| 91 | pub struct CompositorState { |
| 92 | wl_compositor: wl_compositor::WlCompositor, |
| 93 | } |
| 94 | |
| 95 | impl CompositorState { |
| 96 | /// The maximum API version for WlCompositor that this object will bind. |
| 97 | // Note: if bumping this version number, check if the changes to the wayland XML cause an API |
| 98 | // break in the rust interfaces. If it does, be sure to remove other ProvidesBoundGlobal |
| 99 | // impls; if it does not, consider adding one for the previous (compatible) version. |
| 100 | pub const API_VERSION_MAX: u32 = 6; |
| 101 | |
| 102 | pub fn bind<State>( |
| 103 | globals: &GlobalList, |
| 104 | qh: &QueueHandle<State>, |
| 105 | ) -> Result<CompositorState, BindError> |
| 106 | where |
| 107 | State: Dispatch<wl_compositor::WlCompositor, GlobalData, State> + 'static, |
| 108 | { |
| 109 | let wl_compositor = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?; |
| 110 | Ok(CompositorState { wl_compositor }) |
| 111 | } |
| 112 | |
| 113 | pub fn wl_compositor(&self) -> &wl_compositor::WlCompositor { |
| 114 | &self.wl_compositor |
| 115 | } |
| 116 | |
| 117 | pub fn create_surface<D>(&self, qh: &QueueHandle<D>) -> wl_surface::WlSurface |
| 118 | where |
| 119 | D: Dispatch<wl_surface::WlSurface, SurfaceData> + 'static, |
| 120 | { |
| 121 | self.create_surface_with_data(qh, Default::default()) |
| 122 | } |
| 123 | |
| 124 | pub fn create_surface_with_data<D, U>( |
| 125 | &self, |
| 126 | qh: &QueueHandle<D>, |
| 127 | data: U, |
| 128 | ) -> wl_surface::WlSurface |
| 129 | where |
| 130 | D: Dispatch<wl_surface::WlSurface, U> + 'static, |
| 131 | U: SurfaceDataExt + 'static, |
| 132 | { |
| 133 | self.wl_compositor.create_surface(qh, data) |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /// Data associated with a [`WlSurface`](wl_surface::WlSurface). |
| 138 | #[derive (Debug)] |
| 139 | pub struct SurfaceData { |
| 140 | /// The scale factor of the output with the highest scale factor. |
| 141 | pub(crate) scale_factor: AtomicI32, |
| 142 | |
| 143 | /// Parent surface used when creating subsurfaces. |
| 144 | /// |
| 145 | /// For top-level surfaces this is always `None`. |
| 146 | pub(crate) parent_surface: Option<WlSurface>, |
| 147 | |
| 148 | /// The inner mutable storage. |
| 149 | inner: Mutex<SurfaceDataInner>, |
| 150 | } |
| 151 | |
| 152 | impl SurfaceData { |
| 153 | /// Create a new surface that initially reports the given scale factor and parent. |
| 154 | pub fn new(parent_surface: Option<WlSurface>, scale_factor: i32) -> Self { |
| 155 | Self { |
| 156 | scale_factor: AtomicI32::new(scale_factor), |
| 157 | parent_surface, |
| 158 | inner: Default::default(), |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /// The scale factor of the output with the highest scale factor. |
| 163 | pub fn scale_factor(&self) -> i32 { |
| 164 | self.scale_factor.load(Ordering::Relaxed) |
| 165 | } |
| 166 | |
| 167 | /// The suggest transform for the surface. |
| 168 | pub fn transform(&self) -> wl_output::Transform { |
| 169 | self.inner.lock().unwrap().transform |
| 170 | } |
| 171 | |
| 172 | /// The parent surface used for this surface. |
| 173 | /// |
| 174 | /// The surface is `Some` for primarily for subsurfaces, |
| 175 | /// since they must have a parent surface. |
| 176 | pub fn parent_surface(&self) -> Option<&WlSurface> { |
| 177 | self.parent_surface.as_ref() |
| 178 | } |
| 179 | |
| 180 | /// The outputs the surface is currently inside. |
| 181 | pub fn outputs(&self) -> impl Iterator<Item = wl_output::WlOutput> { |
| 182 | self.inner.lock().unwrap().outputs.clone().into_iter() |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | impl Default for SurfaceData { |
| 187 | fn default() -> Self { |
| 188 | Self::new(parent_surface:None, scale_factor:1) |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | #[derive (Debug)] |
| 193 | struct SurfaceDataInner { |
| 194 | /// The transform of the given surface. |
| 195 | transform: wl_output::Transform, |
| 196 | |
| 197 | /// The outputs the surface is currently inside. |
| 198 | outputs: Vec<wl_output::WlOutput>, |
| 199 | |
| 200 | /// A handle to the OutputInfo callback that dispatches scale updates. |
| 201 | watcher: Option<ScaleWatcherHandle>, |
| 202 | } |
| 203 | |
| 204 | impl Default for SurfaceDataInner { |
| 205 | fn default() -> Self { |
| 206 | Self { transform: wl_output::Transform::Normal, outputs: Vec::new(), watcher: None } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /// An owned [`WlSurface`](wl_surface::WlSurface). |
| 211 | /// |
| 212 | /// This destroys the surface on drop. |
| 213 | #[derive (Debug)] |
| 214 | pub struct Surface(wl_surface::WlSurface); |
| 215 | |
| 216 | impl Surface { |
| 217 | pub fn new<D>( |
| 218 | compositor: &impl ProvidesBoundGlobal< |
| 219 | wl_compositor::WlCompositor, |
| 220 | { CompositorState::API_VERSION_MAX }, |
| 221 | >, |
| 222 | qh: &QueueHandle<D>, |
| 223 | ) -> Result<Self, GlobalError> |
| 224 | where |
| 225 | D: Dispatch<wl_surface::WlSurface, SurfaceData> + 'static, |
| 226 | { |
| 227 | Self::with_data(compositor, qh, Default::default()) |
| 228 | } |
| 229 | |
| 230 | pub fn with_data<D, U>( |
| 231 | compositor: &impl ProvidesBoundGlobal< |
| 232 | wl_compositor::WlCompositor, |
| 233 | { CompositorState::API_VERSION_MAX }, |
| 234 | >, |
| 235 | qh: &QueueHandle<D>, |
| 236 | data: U, |
| 237 | ) -> Result<Self, GlobalError> |
| 238 | where |
| 239 | D: Dispatch<wl_surface::WlSurface, U> + 'static, |
| 240 | U: Send + Sync + 'static, |
| 241 | { |
| 242 | Ok(Surface(compositor.bound_global()?.create_surface(qh, data))) |
| 243 | } |
| 244 | |
| 245 | pub fn wl_surface(&self) -> &wl_surface::WlSurface { |
| 246 | &self.0 |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | impl From<wl_surface::WlSurface> for Surface { |
| 251 | fn from(surface: wl_surface::WlSurface) -> Self { |
| 252 | Surface(surface) |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | impl Drop for Surface { |
| 257 | fn drop(&mut self) { |
| 258 | self.0.destroy(); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | #[macro_export ] |
| 263 | macro_rules! delegate_compositor { |
| 264 | ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { |
| 265 | $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface: []); |
| 266 | $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface-only: $crate::compositor::SurfaceData); |
| 267 | }; |
| 268 | ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, surface: [$($surface: ty),*$(,)?]) => { |
| 269 | $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface: [ $($surface),* ]); |
| 270 | }; |
| 271 | (@{$($ty:tt)*}; surface: []) => { |
| 272 | $crate::reexports::client::delegate_dispatch!($($ty)*: |
| 273 | [ |
| 274 | $crate::reexports::client::protocol::wl_compositor::WlCompositor: $crate::globals::GlobalData |
| 275 | ] => $crate::compositor::CompositorState |
| 276 | ); |
| 277 | $crate::reexports::client::delegate_dispatch!($($ty)*: |
| 278 | [ |
| 279 | $crate::reexports::client::protocol::wl_callback::WlCallback: $crate::reexports::client::protocol::wl_surface::WlSurface |
| 280 | ] => $crate::compositor::CompositorState |
| 281 | ); |
| 282 | }; |
| 283 | (@{$($ty:tt)*}; surface-only: $surface:ty) => { |
| 284 | $crate::reexports::client::delegate_dispatch!($($ty)*: |
| 285 | [ |
| 286 | $crate::reexports::client::protocol::wl_surface::WlSurface: $surface |
| 287 | ] => $crate::compositor::CompositorState |
| 288 | ); |
| 289 | }; |
| 290 | (@$ty:tt; surface: [ $($surface:ty),+ ]) => { |
| 291 | $crate::delegate_compositor!(@$ty; surface: []); |
| 292 | $( |
| 293 | $crate::delegate_compositor!(@$ty; surface-only: $surface); |
| 294 | )* |
| 295 | }; |
| 296 | } |
| 297 | |
| 298 | impl<D, U> Dispatch<wl_surface::WlSurface, U, D> for CompositorState |
| 299 | where |
| 300 | D: Dispatch<wl_surface::WlSurface, U> + CompositorHandler + OutputHandler + 'static, |
| 301 | U: SurfaceDataExt + 'static, |
| 302 | { |
| 303 | fn event( |
| 304 | state: &mut D, |
| 305 | surface: &wl_surface::WlSurface, |
| 306 | event: wl_surface::Event, |
| 307 | data: &U, |
| 308 | conn: &Connection, |
| 309 | qh: &QueueHandle<D>, |
| 310 | ) { |
| 311 | let data = data.surface_data(); |
| 312 | let mut inner = data.inner.lock().unwrap(); |
| 313 | |
| 314 | let mut enter_or_leave_output: Option<(wl_output::WlOutput, bool)> = None; |
| 315 | |
| 316 | match event { |
| 317 | wl_surface::Event::Enter { output } => { |
| 318 | inner.outputs.push(output.clone()); |
| 319 | enter_or_leave_output.replace((output, true)); |
| 320 | } |
| 321 | wl_surface::Event::Leave { output } => { |
| 322 | inner.outputs.retain(|o| o != &output); |
| 323 | enter_or_leave_output.replace((output, false)); |
| 324 | } |
| 325 | wl_surface::Event::PreferredBufferScale { factor } => { |
| 326 | let current_scale = data.scale_factor.load(Ordering::Relaxed); |
| 327 | drop(inner); |
| 328 | data.scale_factor.store(factor, Ordering::Relaxed); |
| 329 | if current_scale != factor { |
| 330 | state.scale_factor_changed(conn, qh, surface, factor); |
| 331 | } |
| 332 | return; |
| 333 | } |
| 334 | wl_surface::Event::PreferredBufferTransform { transform } => { |
| 335 | // Only handle known values. |
| 336 | if let WEnum::Value(transform) = transform { |
| 337 | let old_transform = std::mem::replace(&mut inner.transform, transform); |
| 338 | drop(inner); |
| 339 | if old_transform != transform { |
| 340 | state.transform_changed(conn, qh, surface, transform); |
| 341 | } |
| 342 | } |
| 343 | return; |
| 344 | } |
| 345 | _ => unreachable!(), |
| 346 | } |
| 347 | |
| 348 | // NOTE: with v6 we don't need any special handling of the scale factor, everything |
| 349 | // was handled from the above, so return. |
| 350 | if surface.version() >= 6 { |
| 351 | drop(inner); |
| 352 | match enter_or_leave_output { |
| 353 | Some((output, true)) => state.surface_enter(conn, qh, surface, &output), |
| 354 | Some((output, false)) => state.surface_leave(conn, qh, surface, &output), |
| 355 | None => {} |
| 356 | }; |
| 357 | |
| 358 | return; |
| 359 | } |
| 360 | |
| 361 | inner.watcher.get_or_insert_with(|| { |
| 362 | // Avoid storing the WlSurface inside the closure as that would create a reference |
| 363 | // cycle. Instead, store the ID and re-create the proxy. |
| 364 | let id = surface.id(); |
| 365 | OutputState::add_scale_watcher(state, move |state, conn, qh, _| { |
| 366 | let id = id.clone(); |
| 367 | if let Ok(surface) = wl_surface::WlSurface::from_id(conn, id) { |
| 368 | if let Some(data) = surface.data::<U>() { |
| 369 | let data = data.surface_data(); |
| 370 | let inner = data.inner.lock().unwrap(); |
| 371 | dispatch_surface_state_updates(state, conn, qh, &surface, data, inner); |
| 372 | } |
| 373 | } |
| 374 | }) |
| 375 | }); |
| 376 | |
| 377 | dispatch_surface_state_updates(state, conn, qh, surface, data, inner); |
| 378 | |
| 379 | match enter_or_leave_output { |
| 380 | Some((output, true)) => state.surface_enter(conn, qh, surface, &output), |
| 381 | Some((output, false)) => state.surface_leave(conn, qh, surface, &output), |
| 382 | None => {} |
| 383 | }; |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | fn dispatch_surface_state_updates<D, U>( |
| 388 | state: &mut D, |
| 389 | conn: &Connection, |
| 390 | qh: &QueueHandle<D>, |
| 391 | surface: &WlSurface, |
| 392 | data: &SurfaceData, |
| 393 | mut inner: MutexGuard<SurfaceDataInner>, |
| 394 | ) where |
| 395 | D: Dispatch<wl_surface::WlSurface, U> + CompositorHandler + OutputHandler + 'static, |
| 396 | U: SurfaceDataExt + 'static, |
| 397 | { |
| 398 | let current_scale = data.scale_factor.load(Ordering::Relaxed); |
| 399 | let (factor, transform) = match inner |
| 400 | .outputs |
| 401 | .iter() |
| 402 | .filter_map(|output| { |
| 403 | output |
| 404 | .data::<OutputData>() |
| 405 | .map(|data| data.with_output_info(|info| (info.scale_factor, info.transform))) |
| 406 | }) |
| 407 | // NOTE: reduce will only work for more than 1 element, thus we map transform to normal |
| 408 | // since we can't guess which one to use. With the exactly one output, the corrent |
| 409 | // transform will be passed instead. |
| 410 | .reduce(|acc, props| (acc.0.max(props.0), wl_output::Transform::Normal)) |
| 411 | { |
| 412 | None => return, |
| 413 | Some(props) => props, |
| 414 | }; |
| 415 | |
| 416 | data.scale_factor.store(factor, Ordering::Relaxed); |
| 417 | let old_transform = mem::replace(&mut inner.transform, transform); |
| 418 | // Drop the mutex before we send of any events. |
| 419 | drop(inner); |
| 420 | |
| 421 | if factor != current_scale { |
| 422 | state.scale_factor_changed(conn, qh, surface, factor); |
| 423 | } |
| 424 | |
| 425 | if transform != old_transform { |
| 426 | state.transform_changed(conn, qh, surface, transform); |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | /// A trivial wrapper around a [`WlRegion`][wl_region::WlRegion]. |
| 431 | /// |
| 432 | /// This destroys the region on drop. |
| 433 | #[derive (Debug)] |
| 434 | pub struct Region(wl_region::WlRegion); |
| 435 | |
| 436 | impl Region { |
| 437 | pub fn new( |
| 438 | compositor: &impl ProvidesBoundGlobal< |
| 439 | wl_compositor::WlCompositor, |
| 440 | { CompositorState::API_VERSION_MAX }, |
| 441 | >, |
| 442 | ) -> Result<Region, GlobalError> { |
| 443 | compositor |
| 444 | .bound_global() |
| 445 | .map(|c| { |
| 446 | c.send_constructor(wl_compositor::Request::CreateRegion {}, Arc::new(RegionData)) |
| 447 | .unwrap_or_else(|_| Proxy::inert(c.backend().clone())) |
| 448 | }) |
| 449 | .map(Region) |
| 450 | } |
| 451 | |
| 452 | pub fn add(&self, x: i32, y: i32, width: i32, height: i32) { |
| 453 | self.0.add(x, y, width, height) |
| 454 | } |
| 455 | |
| 456 | pub fn subtract(&self, x: i32, y: i32, width: i32, height: i32) { |
| 457 | self.0.subtract(x, y, width, height) |
| 458 | } |
| 459 | |
| 460 | pub fn wl_region(&self) -> &wl_region::WlRegion { |
| 461 | &self.0 |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | impl Drop for Region { |
| 466 | fn drop(&mut self) { |
| 467 | self.0.destroy() |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | struct RegionData; |
| 472 | |
| 473 | impl wayland_client::backend::ObjectData for RegionData { |
| 474 | fn event( |
| 475 | self: Arc<Self>, |
| 476 | _: &wayland_client::backend::Backend, |
| 477 | _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>, |
| 478 | ) -> Option<Arc<(dyn wayland_client::backend::ObjectData + 'static)>> { |
| 479 | unreachable!("wl_region has no events" ); |
| 480 | } |
| 481 | fn destroyed(&self, _: wayland_client::backend::ObjectId) {} |
| 482 | } |
| 483 | |
| 484 | impl<D> Dispatch<wl_compositor::WlCompositor, GlobalData, D> for CompositorState |
| 485 | where |
| 486 | D: Dispatch<wl_compositor::WlCompositor, GlobalData> + CompositorHandler, |
| 487 | { |
| 488 | fn event( |
| 489 | _: &mut D, |
| 490 | _: &wl_compositor::WlCompositor, |
| 491 | _: wl_compositor::Event, |
| 492 | _: &GlobalData, |
| 493 | _: &Connection, |
| 494 | _: &QueueHandle<D>, |
| 495 | ) { |
| 496 | unreachable!("wl_compositor has no events" ) |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | impl ProvidesBoundGlobal<wl_compositor::WlCompositor, { CompositorState::API_VERSION_MAX }> |
| 501 | for CompositorState |
| 502 | { |
| 503 | fn bound_global(&self) -> Result<wl_compositor::WlCompositor, GlobalError> { |
| 504 | Ok(self.wl_compositor.clone()) |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | impl<D> Dispatch<wl_callback::WlCallback, wl_surface::WlSurface, D> for CompositorState |
| 509 | where |
| 510 | D: Dispatch<wl_callback::WlCallback, wl_surface::WlSurface> + CompositorHandler, |
| 511 | { |
| 512 | fn event( |
| 513 | state: &mut D, |
| 514 | _: &wl_callback::WlCallback, |
| 515 | event: wl_callback::Event, |
| 516 | surface: &wl_surface::WlSurface, |
| 517 | conn: &Connection, |
| 518 | qh: &QueueHandle<D>, |
| 519 | ) { |
| 520 | match event { |
| 521 | wl_callback::Event::Done { callback_data: u32 } => { |
| 522 | state.frame(conn, qh, surface, time:callback_data); |
| 523 | } |
| 524 | |
| 525 | _ => unreachable!(), |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | |