1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
pub mod multi;
pub mod raw;
pub mod slot;

use std::io;

use wayland_client::{
    globals::{BindError, GlobalList},
    protocol::wl_shm,
    Connection, Dispatch, QueueHandle, WEnum,
};

use crate::{
    error::GlobalError,
    globals::{GlobalData, ProvidesBoundGlobal},
};

pub trait ShmHandler {
    fn shm_state(&mut self) -> &mut Shm;
}

#[derive(Debug)]
pub struct Shm {
    wl_shm: wl_shm::WlShm,
    formats: Vec<wl_shm::Format>,
}

impl From<wl_shm::WlShm> for Shm {
    fn from(wl_shm: wl_shm::WlShm) -> Self {
        Self { wl_shm, formats: Vec::new() }
    }
}

impl Shm {
    pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Shm, BindError>
    where
        State: Dispatch<wl_shm::WlShm, GlobalData, State> + ShmHandler + 'static,
    {
        let wl_shm = globals.bind(qh, 1..=1, GlobalData)?;
        // Compositors must advertise Argb8888 and Xrgb8888, so let's reserve space for those formats.
        Ok(Shm { wl_shm, formats: Vec::with_capacity(2) })
    }

    pub fn wl_shm(&self) -> &wl_shm::WlShm {
        &self.wl_shm
    }

    /// Returns the formats supported in memory pools.
    pub fn formats(&self) -> &[wl_shm::Format] {
        &self.formats[..]
    }
}

impl ProvidesBoundGlobal<wl_shm::WlShm, 1> for Shm {
    fn bound_global(&self) -> Result<wl_shm::WlShm, GlobalError> {
        Ok(self.wl_shm.clone())
    }
}

/// An error that may occur when creating a pool.
#[derive(Debug, thiserror::Error)]
pub enum CreatePoolError {
    /// The wl_shm global is not bound.
    #[error(transparent)]
    Global(#[from] GlobalError),

    /// Error while allocating the shared memory.
    #[error(transparent)]
    Create(#[from] io::Error),
}

/// Delegates the handling of [`wl_shm`] to some [`Shm`].
///
/// This macro requires two things, the type that will delegate to [`Shm`] and a closure specifying how
/// to obtain the state object.
///
/// ```
/// use smithay_client_toolkit::shm::{ShmHandler, Shm};
/// use smithay_client_toolkit::delegate_shm;
///
/// struct ExampleApp {
///     /// The state object that will be our delegate.
///     shm: Shm,
/// }
///
/// // Use the macro to delegate wl_shm to Shm.
/// delegate_shm!(ExampleApp);
///
/// // You must implement the ShmHandler trait to provide a way to access the Shm from your data type.
/// impl ShmHandler for ExampleApp {
///     fn shm_state(&mut self) -> &mut Shm {
///         &mut self.shm
///     }
/// }
#[macro_export]
macro_rules! delegate_shm {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
            [
                $crate::reexports::client::protocol::wl_shm::WlShm: $crate::globals::GlobalData
            ] => $crate::shm::Shm
        );
    };
}

impl<D> Dispatch<wl_shm::WlShm, GlobalData, D> for Shm
where
    D: Dispatch<wl_shm::WlShm, GlobalData> + ShmHandler,
{
    fn event(
        state: &mut D,
        _proxy: &wl_shm::WlShm,
        event: wl_shm::Event,
        _: &GlobalData,
        _: &Connection,
        _: &QueueHandle<D>,
    ) {
        match event {
            wl_shm::Event::Format { format } => {
                match format {
                    WEnum::Value(format) => {
                        state.shm_state().formats.push(format);
                        log::debug!(target: "sctk", "supported wl_shm format {:?}", format);
                    }

                    // Ignore formats we don't know about.
                    WEnum::Unknown(raw) => {
                        log::debug!(target: "sctk", "Unknown supported wl_shm format {:x}", raw);
                    }
                };
            }

            _ => unreachable!(),
        }
    }
}