1use smithay_client_toolkit::reexports::client::{
2 backend::ObjectId,
3 protocol::{wl_subsurface::WlSubsurface, wl_surface::WlSurface},
4 Dispatch, Proxy, QueueHandle,
5};
6
7use smithay_client_toolkit::{
8 compositor::SurfaceData,
9 subcompositor::{SubcompositorState, SubsurfaceData},
10};
11
12use crate::theme::{BORDER_SIZE, HEADER_SIZE, RESIZE_HANDLE_SIZE};
13use crate::{pointer::Location, wl_typed::WlTyped};
14
15/// The decoration's 'parts'.
16#[derive(Debug)]
17pub struct DecorationParts {
18 parts: [Part; 5],
19}
20
21impl 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)]
206pub struct Rect {
207 pub x: i32,
208 pub y: i32,
209 pub width: u32,
210 pub height: u32,
211}
212
213#[derive(Debug)]
214pub 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
226impl 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
255impl Drop for Part {
256 fn drop(&mut self) {
257 self.subsurface.destroy();
258 self.surface.destroy();
259 }
260}
261