use std::mem;
use std::os::unix::io::OwnedFd;
use std::sync::MutexGuard;
use std::sync::{
atomic::{AtomicI32, Ordering},
Arc, Mutex,
};
use wayland_client::{
globals::{BindError, GlobalList},
protocol::{
wl_callback, wl_compositor, wl_output, wl_region,
wl_surface::{self, WlSurface},
},
Connection, Dispatch, Proxy, QueueHandle, WEnum,
};
use crate::{
dispatch2::Dispatch2,
error::GlobalError,
globals::{GlobalData, ProvidesBoundGlobal},
output::{OutputData, OutputHandler, OutputState, ScaleWatcherHandle},
};
pub trait CompositorHandler: Sized {
fn scale_factor_changed(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
surface: &wl_surface::WlSurface,
new_factor: i32,
);
fn transform_changed(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
surface: &wl_surface::WlSurface,
new_transform: wl_output::Transform,
);
fn frame(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
surface: &wl_surface::WlSurface,
time: u32,
);
fn surface_enter(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
surface: &wl_surface::WlSurface,
output: &wl_output::WlOutput,
);
fn surface_leave(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
surface: &wl_surface::WlSurface,
output: &wl_output::WlOutput,
);
}
#[derive(Clone, Debug)]
pub struct CompositorState {
wl_compositor: wl_compositor::WlCompositor,
}
impl CompositorState {
pub const API_VERSION_MAX: u32 = 6;
pub fn bind<State>(
globals: &GlobalList,
qh: &QueueHandle<State>,
) -> Result<CompositorState, BindError>
where
State: Dispatch<wl_compositor::WlCompositor, GlobalData, State> + 'static,
{
let wl_compositor = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?;
Ok(CompositorState { wl_compositor })
}
pub fn wl_compositor(&self) -> &wl_compositor::WlCompositor {
&self.wl_compositor
}
pub fn create_surface<D>(&self, qh: &QueueHandle<D>) -> wl_surface::WlSurface
where
D: Dispatch<wl_surface::WlSurface, SurfaceData<()>> + 'static,
{
self.create_surface_with_data(qh, None, 1, ())
}
pub fn create_surface_with_data<D, U>(
&self,
qh: &QueueHandle<D>,
parent_surface: Option<WlSurface>,
scale_factor: i32,
data: U,
) -> wl_surface::WlSurface
where
D: Dispatch<wl_surface::WlSurface, SurfaceData<U>> + 'static,
U: Send + Sync + 'static,
{
let data = SurfaceData::new(parent_surface, scale_factor, data);
self.wl_compositor.create_surface(qh, data)
}
}
#[derive(Debug)]
pub struct SurfaceData<U> {
pub(crate) scale_factor: AtomicI32,
pub(crate) parent_surface: Option<WlSurface>,
inner: Mutex<SurfaceDataInner>,
udata: U,
}
impl<U> SurfaceData<U> {
pub fn new(parent_surface: Option<WlSurface>, scale_factor: i32, udata: U) -> Self {
Self {
scale_factor: AtomicI32::new(scale_factor),
parent_surface,
inner: Default::default(),
udata,
}
}
pub fn data(&self) -> &U {
&self.udata
}
pub fn data_mut(&mut self) -> &mut U {
&mut self.udata
}
pub fn scale_factor(&self) -> i32 {
self.scale_factor.load(Ordering::Relaxed)
}
pub fn transform(&self) -> wl_output::Transform {
self.inner.lock().unwrap().transform
}
pub fn parent_surface(&self) -> Option<&WlSurface> {
self.parent_surface.as_ref()
}
pub fn outputs(&self) -> impl Iterator<Item = wl_output::WlOutput> {
self.inner.lock().unwrap().outputs.clone().into_iter()
}
}
#[derive(Debug)]
struct SurfaceDataInner {
transform: wl_output::Transform,
outputs: Vec<wl_output::WlOutput>,
watcher: Option<ScaleWatcherHandle>,
}
impl Default for SurfaceDataInner {
fn default() -> Self {
Self { transform: wl_output::Transform::Normal, outputs: Vec::new(), watcher: None }
}
}
#[derive(Debug)]
pub struct Surface(wl_surface::WlSurface);
impl Surface {
pub fn new<D>(
compositor: &impl ProvidesBoundGlobal<
wl_compositor::WlCompositor,
{ CompositorState::API_VERSION_MAX },
>,
qh: &QueueHandle<D>,
) -> Result<Self, GlobalError>
where
D: Dispatch<wl_surface::WlSurface, SurfaceData<()>> + 'static,
{
Self::with_data(compositor, qh, None, 1, ())
}
pub fn with_data<D, U>(
compositor: &impl ProvidesBoundGlobal<
wl_compositor::WlCompositor,
{ CompositorState::API_VERSION_MAX },
>,
qh: &QueueHandle<D>,
parent_surface: Option<WlSurface>,
scale_factor: i32,
data: U,
) -> Result<Self, GlobalError>
where
D: Dispatch<wl_surface::WlSurface, SurfaceData<U>> + 'static,
U: Send + Sync + 'static,
{
let data = SurfaceData::new(parent_surface, scale_factor, data);
Ok(Surface(compositor.bound_global()?.create_surface(qh, data)))
}
pub fn wl_surface(&self) -> &wl_surface::WlSurface {
&self.0
}
}
impl From<wl_surface::WlSurface> for Surface {
fn from(surface: wl_surface::WlSurface) -> Self {
Surface(surface)
}
}
impl Drop for Surface {
fn drop(&mut self) {
self.0.destroy();
}
}
impl<D, U> Dispatch2<wl_surface::WlSurface, D> for SurfaceData<U>
where
D: CompositorHandler + OutputHandler + 'static,
U: Send + Sync + 'static,
{
fn event(
&self,
state: &mut D,
surface: &wl_surface::WlSurface,
event: wl_surface::Event,
conn: &Connection,
qh: &QueueHandle<D>,
) {
let mut inner = self.inner.lock().unwrap();
let mut enter_or_leave_output: Option<(wl_output::WlOutput, bool)> = None;
match event {
wl_surface::Event::Enter { output } => {
inner.outputs.push(output.clone());
enter_or_leave_output.replace((output, true));
}
wl_surface::Event::Leave { output } => {
inner.outputs.retain(|o| o != &output);
enter_or_leave_output.replace((output, false));
}
wl_surface::Event::PreferredBufferScale { factor } => {
let current_scale = self.scale_factor.load(Ordering::Relaxed);
drop(inner);
self.scale_factor.store(factor, Ordering::Relaxed);
if current_scale != factor {
state.scale_factor_changed(conn, qh, surface, factor);
}
return;
}
wl_surface::Event::PreferredBufferTransform { transform } => {
if let WEnum::Value(transform) = transform {
let old_transform = std::mem::replace(&mut inner.transform, transform);
drop(inner);
if old_transform != transform {
state.transform_changed(conn, qh, surface, transform);
}
}
return;
}
_ => unreachable!(),
}
if surface.version() >= 6 {
drop(inner);
match enter_or_leave_output {
Some((output, true)) => state.surface_enter(conn, qh, surface, &output),
Some((output, false)) => state.surface_leave(conn, qh, surface, &output),
None => {}
};
return;
}
inner.watcher.get_or_insert_with(|| {
let id = surface.id();
OutputState::add_scale_watcher(state, move |state, conn, qh, _| {
let id = id.clone();
if let Ok(surface) = wl_surface::WlSurface::from_id(conn, id) {
if let Some(data) = surface.data::<SurfaceData<U>>() {
let inner = data.inner.lock().unwrap();
dispatch_surface_state_updates(state, conn, qh, &surface, data, inner);
}
}
})
});
dispatch_surface_state_updates(state, conn, qh, surface, self, inner);
match enter_or_leave_output {
Some((output, true)) => state.surface_enter(conn, qh, surface, &output),
Some((output, false)) => state.surface_leave(conn, qh, surface, &output),
None => {}
};
}
}
fn dispatch_surface_state_updates<D, U>(
state: &mut D,
conn: &Connection,
qh: &QueueHandle<D>,
surface: &WlSurface,
data: &SurfaceData<U>,
mut inner: MutexGuard<SurfaceDataInner>,
) where
D: CompositorHandler + OutputHandler + 'static,
{
let current_scale = data.scale_factor.load(Ordering::Relaxed);
let (factor, transform) = match inner
.outputs
.iter()
.filter_map(|output| {
output
.data::<OutputData>()
.map(|data| data.with_output_info(|info| (info.scale_factor, info.transform)))
})
.reduce(|acc, props| (acc.0.max(props.0), wl_output::Transform::Normal))
{
None => return,
Some(props) => props,
};
data.scale_factor.store(factor, Ordering::Relaxed);
let old_transform = mem::replace(&mut inner.transform, transform);
drop(inner);
if factor != current_scale {
state.scale_factor_changed(conn, qh, surface, factor);
}
if transform != old_transform {
state.transform_changed(conn, qh, surface, transform);
}
}
#[derive(Debug)]
pub struct Region(wl_region::WlRegion);
impl Region {
pub fn new(
compositor: &impl ProvidesBoundGlobal<
wl_compositor::WlCompositor,
{ CompositorState::API_VERSION_MAX },
>,
) -> Result<Region, GlobalError> {
compositor
.bound_global()
.map(|c| {
c.send_constructor(wl_compositor::Request::CreateRegion {}, Arc::new(RegionData))
.unwrap_or_else(|_| Proxy::inert(c.backend().clone()))
})
.map(Region)
}
pub fn add(&self, x: i32, y: i32, width: i32, height: i32) {
self.0.add(x, y, width, height)
}
pub fn subtract(&self, x: i32, y: i32, width: i32, height: i32) {
self.0.subtract(x, y, width, height)
}
pub fn wl_region(&self) -> &wl_region::WlRegion {
&self.0
}
}
impl Drop for Region {
fn drop(&mut self) {
self.0.destroy()
}
}
struct RegionData;
impl wayland_client::backend::ObjectData for RegionData {
fn event(
self: Arc<Self>,
_: &wayland_client::backend::Backend,
_: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
) -> Option<Arc<dyn wayland_client::backend::ObjectData + 'static>> {
unreachable!("wl_region has no events");
}
fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
}
impl<D> Dispatch2<wl_compositor::WlCompositor, D> for GlobalData
where
D: CompositorHandler,
{
fn event(
&self,
_: &mut D,
_: &wl_compositor::WlCompositor,
_: wl_compositor::Event,
_: &Connection,
_: &QueueHandle<D>,
) {
unreachable!("wl_compositor has no events")
}
}
impl ProvidesBoundGlobal<wl_compositor::WlCompositor, { CompositorState::API_VERSION_MAX }>
for CompositorState
{
fn bound_global(&self) -> Result<wl_compositor::WlCompositor, GlobalError> {
Ok(self.wl_compositor.clone())
}
}
#[derive(Debug)]
pub struct FrameCallbackData(pub wl_surface::WlSurface);
impl<D> Dispatch2<wl_callback::WlCallback, D> for FrameCallbackData
where
D: CompositorHandler,
{
fn event(
&self,
state: &mut D,
_: &wl_callback::WlCallback,
event: wl_callback::Event,
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
wl_callback::Event::Done { callback_data } => {
state.frame(conn, qh, &self.0, callback_data);
}
_ => unreachable!(),
}
}
}