| 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 | |