1 | use smithay_client_toolkit::reexports::client::{ |
2 | backend::ObjectId, |
3 | protocol::{wl_subsurface::WlSubsurface, wl_surface::WlSurface}, |
4 | Dispatch, Proxy, QueueHandle, |
5 | }; |
6 | |
7 | use smithay_client_toolkit::{ |
8 | compositor::SurfaceData, |
9 | subcompositor::{SubcompositorState, SubsurfaceData}, |
10 | }; |
11 | |
12 | use crate::theme::{BORDER_SIZE, HEADER_SIZE, RESIZE_HANDLE_SIZE}; |
13 | use crate::{pointer::Location, wl_typed::WlTyped}; |
14 | |
15 | /// The decoration's 'parts'. |
16 | #[derive (Debug)] |
17 | pub struct DecorationParts { |
18 | parts: [Part; 5], |
19 | } |
20 | |
21 | impl DecorationParts { |
22 | // XXX keep in sync with `Self;:new`. |
23 | // Order is important. The lower the number, the earlier the part gets drawn. |
24 | // Because the header can overlap other parts, we draw it last. |
25 | pub const TOP: usize = 0; |
26 | pub const LEFT: usize = 1; |
27 | pub const RIGHT: usize = 2; |
28 | pub const BOTTOM: usize = 3; |
29 | pub const HEADER: usize = 4; |
30 | |
31 | pub fn new<State>( |
32 | base_surface: &WlTyped<WlSurface, SurfaceData>, |
33 | subcompositor: &SubcompositorState, |
34 | queue_handle: &QueueHandle<State>, |
35 | ) -> Self |
36 | where |
37 | State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static, |
38 | { |
39 | // XXX the order must be in sync with associated constants. |
40 | let parts = [ |
41 | // Top. |
42 | Part::new( |
43 | base_surface, |
44 | subcompositor, |
45 | queue_handle, |
46 | Rect { |
47 | x: -(BORDER_SIZE as i32), |
48 | y: -(HEADER_SIZE as i32 + BORDER_SIZE as i32), |
49 | width: 0, // Defined by `Self::resize`. |
50 | height: BORDER_SIZE, |
51 | }, |
52 | Some(Rect { |
53 | x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32, |
54 | y: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32, |
55 | width: 0, // Defined by `Self::resize`. |
56 | height: RESIZE_HANDLE_SIZE, |
57 | }), |
58 | ), |
59 | // Left. |
60 | Part::new( |
61 | base_surface, |
62 | subcompositor, |
63 | queue_handle, |
64 | Rect { |
65 | x: -(BORDER_SIZE as i32), |
66 | y: -(HEADER_SIZE as i32), |
67 | width: BORDER_SIZE, |
68 | height: 0, // Defined by `Self::resize`. |
69 | }, |
70 | Some(Rect { |
71 | x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32, |
72 | y: 0, |
73 | width: RESIZE_HANDLE_SIZE, |
74 | height: 0, // Defined by `Self::resize`. |
75 | }), |
76 | ), |
77 | // Right. |
78 | Part::new( |
79 | base_surface, |
80 | subcompositor, |
81 | queue_handle, |
82 | Rect { |
83 | x: 0, // Defined by `Self::resize`. |
84 | y: -(HEADER_SIZE as i32), |
85 | width: BORDER_SIZE, |
86 | height: 0, // Defined by `Self::resize`. |
87 | }, |
88 | Some(Rect { |
89 | x: 0, |
90 | y: 0, |
91 | width: RESIZE_HANDLE_SIZE, |
92 | height: 0, // Defined by `Self::resize`. |
93 | }), |
94 | ), |
95 | // Bottom. |
96 | Part::new( |
97 | base_surface, |
98 | subcompositor, |
99 | queue_handle, |
100 | Rect { |
101 | x: -(BORDER_SIZE as i32), |
102 | y: 0, // Defined by `Self::resize`. |
103 | width: 0, // Defined by `Self::resize`. |
104 | height: BORDER_SIZE, |
105 | }, |
106 | Some(Rect { |
107 | x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32, |
108 | y: 0, |
109 | width: 0, // Defined by `Self::resize`, |
110 | height: RESIZE_HANDLE_SIZE, |
111 | }), |
112 | ), |
113 | // Header. |
114 | Part::new( |
115 | base_surface, |
116 | subcompositor, |
117 | queue_handle, |
118 | Rect { |
119 | x: 0, |
120 | y: -(HEADER_SIZE as i32), |
121 | width: 0, // Defined by `Self::resize`. |
122 | height: HEADER_SIZE, |
123 | }, |
124 | None, |
125 | ), |
126 | ]; |
127 | |
128 | Self { parts } |
129 | } |
130 | |
131 | pub fn parts(&self) -> std::iter::Enumerate<std::slice::Iter<Part>> { |
132 | self.parts.iter().enumerate() |
133 | } |
134 | |
135 | pub fn hide(&self) { |
136 | for part in self.parts.iter() { |
137 | part.subsurface.set_sync(); |
138 | part.surface.attach(None, 0, 0); |
139 | part.surface.commit(); |
140 | } |
141 | } |
142 | |
143 | pub fn hide_borders(&self) { |
144 | for (_, part) in self.parts().filter(|(idx, _)| *idx != Self::HEADER) { |
145 | part.surface.attach(None, 0, 0); |
146 | part.surface.commit(); |
147 | } |
148 | } |
149 | |
150 | // These unwraps are guaranteed to succeed because the affected options are filled above |
151 | // and then never emptied afterwards. |
152 | #[allow (clippy::unwrap_used)] |
153 | pub fn resize(&mut self, width: u32, height: u32) { |
154 | self.parts[Self::HEADER].surface_rect.width = width; |
155 | |
156 | self.parts[Self::BOTTOM].surface_rect.width = width + 2 * BORDER_SIZE; |
157 | self.parts[Self::BOTTOM].surface_rect.y = height as i32; |
158 | self.parts[Self::BOTTOM].input_rect.as_mut().unwrap().width = |
159 | self.parts[Self::BOTTOM].surface_rect.width - (BORDER_SIZE * 2) |
160 | + (RESIZE_HANDLE_SIZE * 2); |
161 | |
162 | self.parts[Self::TOP].surface_rect.width = self.parts[Self::BOTTOM].surface_rect.width; |
163 | self.parts[Self::TOP].input_rect.as_mut().unwrap().width = |
164 | self.parts[Self::TOP].surface_rect.width - (BORDER_SIZE * 2) + (RESIZE_HANDLE_SIZE * 2); |
165 | |
166 | self.parts[Self::LEFT].surface_rect.height = height + HEADER_SIZE; |
167 | self.parts[Self::LEFT].input_rect.as_mut().unwrap().height = |
168 | self.parts[Self::LEFT].surface_rect.height; |
169 | |
170 | self.parts[Self::RIGHT].surface_rect.height = self.parts[Self::LEFT].surface_rect.height; |
171 | self.parts[Self::RIGHT].surface_rect.x = width as i32; |
172 | self.parts[Self::RIGHT].input_rect.as_mut().unwrap().height = |
173 | self.parts[Self::RIGHT].surface_rect.height; |
174 | } |
175 | |
176 | pub fn header(&self) -> &Part { |
177 | &self.parts[Self::HEADER] |
178 | } |
179 | |
180 | pub fn side_height(&self) -> u32 { |
181 | self.parts[Self::LEFT].surface_rect.height |
182 | } |
183 | |
184 | pub fn find_surface(&self, surface: &ObjectId) -> Location { |
185 | let pos = match self |
186 | .parts |
187 | .iter() |
188 | .position(|part| &part.surface.id() == surface) |
189 | { |
190 | Some(pos) => pos, |
191 | None => return Location::None, |
192 | }; |
193 | |
194 | match pos { |
195 | Self::HEADER => Location::Head, |
196 | Self::TOP => Location::Top, |
197 | Self::BOTTOM => Location::Bottom, |
198 | Self::LEFT => Location::Left, |
199 | Self::RIGHT => Location::Right, |
200 | _ => unreachable!(), |
201 | } |
202 | } |
203 | } |
204 | |
205 | #[derive (Debug, Clone, Copy)] |
206 | pub struct Rect { |
207 | pub x: i32, |
208 | pub y: i32, |
209 | pub width: u32, |
210 | pub height: u32, |
211 | } |
212 | |
213 | #[derive (Debug)] |
214 | pub struct Part { |
215 | pub surface: WlTyped<WlSurface, SurfaceData>, |
216 | pub subsurface: WlTyped<WlSubsurface, SubsurfaceData>, |
217 | |
218 | /// Positioned relative to the main surface. |
219 | pub surface_rect: Rect, |
220 | /// Positioned relative to the local surface, aka. `surface_rect`. |
221 | /// |
222 | /// `None` if it fully covers `surface_rect`. |
223 | pub input_rect: Option<Rect>, |
224 | } |
225 | |
226 | impl Part { |
227 | fn new<State>( |
228 | parent: &WlTyped<WlSurface, SurfaceData>, |
229 | subcompositor: &SubcompositorState, |
230 | queue_handle: &QueueHandle<State>, |
231 | surface_rect: Rect, |
232 | input_rect: Option<Rect>, |
233 | ) -> Part |
234 | where |
235 | State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static, |
236 | { |
237 | let (subsurface, surface) = |
238 | subcompositor.create_subsurface(parent.inner().clone(), queue_handle); |
239 | |
240 | let subsurface = WlTyped::wrap::<State>(subsurface); |
241 | let surface = WlTyped::wrap::<State>(surface); |
242 | |
243 | // Sync with the parent surface. |
244 | subsurface.set_sync(); |
245 | |
246 | Part { |
247 | surface, |
248 | subsurface, |
249 | surface_rect, |
250 | input_rect, |
251 | } |
252 | } |
253 | } |
254 | |
255 | impl Drop for Part { |
256 | fn drop(&mut self) { |
257 | self.subsurface.destroy(); |
258 | self.surface.destroy(); |
259 | } |
260 | } |
261 | |