1use Location;
2use shell;
3use std::fs::File;
4use std::io::{Seek, SeekFrom, Write};
5use std::os::unix::io::AsRawFd;
6use std::sync::{Arc, Mutex};
7use tempfile::tempfile;
8use wayland_client::Proxy;
9use wayland_client::protocol::*;
10
11#[derive(Copy, Clone)]
12pub(crate) struct FrameMetadata {
13 pub(crate) dimensions: (i32, i32),
14 pub(crate) decorate: bool,
15 pub(crate) fullscreen: bool,
16 pub(crate) maximized: bool,
17 pub(crate) min_size: Option<(i32, i32)>,
18 pub(crate) max_size: Option<(i32, i32)>,
19 pub(crate) old_size: Option<(i32, i32)>,
20 pub(crate) activated: bool,
21 pub(crate) ready: bool,
22 pub(crate) need_redraw: bool,
23 pub(crate) ptr_location: Location,
24}
25
26impl FrameMetadata {
27 pub(crate) fn clamp_to_limits(&self, size: (i32, i32)) -> (i32, i32) {
28 use std::cmp::{max, min};
29 let (mut w, mut h) = size;
30 if self.decorate {
31 let (ww, hh) = ::subtract_borders(w, h);
32 w = ww;
33 h = hh;
34 }
35 if let Some((minw, minh)) = self.min_size {
36 w = max(minw, w);
37 h = max(minh, h);
38 }
39 if let Some((maxw, maxh)) = self.max_size {
40 w = min(maxw, w);
41 h = min(maxh, h);
42 }
43 (w, h)
44 }
45}
46
47pub struct Frame {
57 pub(crate) surface: wl_surface::WlSurface,
58 contents: wl_subsurface::WlSubsurface,
59 pub(crate) shell_surface: shell::Surface,
60 buffer: Option<wl_buffer::WlBuffer>,
61 tempfile: File,
62 pool: wl_shm_pool::WlShmPool,
63 pub(crate) pointer: Option<wl_pointer::WlPointer>,
64 pub(crate) meta: Arc<Mutex<FrameMetadata>>,
65 buffer_capacity: i32,
66}
67
68pub enum State<'output> {
70 Regular,
72 Minimized,
74 Maximized,
76 Fullscreen(Option<&'output wl_output::WlOutput>),
78}
79
80impl Frame {
81 pub(crate) fn new(user_surface: &wl_surface::WlSurface, width: i32, height: i32,
82 compositor: &wl_compositor::WlCompositor,
83 subcompositor: &wl_subcompositor::WlSubcompositor, shm: &wl_shm::WlShm,
84 shell: &shell::Shell)
85 -> Result<Frame, ()> {
86 if width <= 0 || height <= 0 {
87 return Err(());
88 }
89
90 let tempfile = match tempfile() {
91 Ok(t) => t,
92 Err(_) => return Err(()),
93 };
94
95 match tempfile.set_len(100) {
96 Ok(()) => {}
97 Err(_) => return Err(()),
98 };
99
100 let pool = shm.create_pool(tempfile.as_raw_fd(), 100);
101
102 let meta = Arc::new(Mutex::new(FrameMetadata {
103 dimensions: (width, height),
104 decorate: false,
105 fullscreen: false,
106 maximized: false,
107 min_size: None,
108 max_size: None,
109 old_size: None,
110 activated: true,
111 ready: !shell.needs_readiness(),
112 need_redraw: shell.needs_readiness(),
113 ptr_location: Location::None,
114 }));
115
116 let frame_surface = compositor.create_surface();
117 let contents = subcompositor
118 .get_subsurface(&user_surface, &frame_surface)
119 .expect("Provided Subcompositor was defunct");
120 contents.set_position(0, 0);
121 contents.set_desync();
122
123 let shell_surface = shell::Surface::from_shell(&frame_surface, shell);
124
125 let mut frame = Frame {
126 surface: frame_surface,
127 contents: contents,
128 shell_surface: shell_surface,
129 buffer: None,
130 tempfile: tempfile,
131 pool: pool,
132 pointer: None,
133 meta: meta,
134 buffer_capacity: 100,
135 };
136
137 frame.redraw();
138
139 Ok(frame)
140 }
141
142 pub(crate) fn redraw(&mut self) {
143 let mut meta = self.meta.lock().unwrap();
144 if !meta.ready {
145 return;
146 }
147
148 if !meta.decorate || meta.fullscreen {
149 self.tempfile.seek(SeekFrom::Start(0)).unwrap();
152 let _ = self.tempfile.write_all(&[0, 0, 0, 0]).unwrap();
153 self.tempfile.flush().unwrap();
154 if let Some(buffer) = self.buffer.take() {
155 buffer.destroy();
157 }
158 let buffer = self.pool
159 .create_buffer(0, 1, 1, 4, wl_shm::Format::Argb8888)
160 .expect("The pool cannot be defunct!");
161 self.surface.attach(Some(&buffer), 0, 0);
162 self.surface.commit();
163 return
164 }
165
166 let (w, h) = meta.dimensions;
167 let pxcount = ::theme::pxcount(w, h);
168
169 if pxcount * 4 > self.buffer_capacity {
170 self.tempfile.set_len((pxcount * 4) as u64).unwrap();
172 self.pool.resize(pxcount * 4);
173 self.buffer_capacity = pxcount * 4;
174 }
175 let mut mmap = unsafe {
177 ::memmap::MmapOptions::new()
178 .len(pxcount as usize * 4)
179 .map_mut(&self.tempfile)
180 .unwrap()
181 };
182 let _ = ::theme::draw_contents(
183 &mut *mmap,
184 w as u32,
185 h as u32,
186 meta.activated,
187 meta.maximized,
188 meta.max_size.is_none(),
189 meta.ptr_location,
190 );
191 mmap.flush().unwrap();
192 drop(mmap);
193
194 if let Some(buffer) = self.buffer.take() {
196 buffer.destroy();
198 }
199 let (full_w, full_h) = ::theme::add_borders(w, h);
200 let buffer = self.pool
201 .create_buffer(0, full_w, full_h, full_w * 4, wl_shm::Format::Argb8888)
202 .expect("The pool cannot be defunct!");
203 self.surface.attach(Some(&buffer), 0, 0);
204 if self.surface.version() >= 4 {
206 self.surface.damage_buffer(0, 0, full_w, full_h);
207 } else {
208 self.surface.damage(0, 0, full_w, full_h);
211 }
212 self.surface.commit();
213 self.buffer = Some(buffer);
214 meta.need_redraw = false;
215 }
216
217 pub fn refresh(&mut self) {
229 let need_redraw = self.meta.lock().unwrap().need_redraw;
230 if need_redraw {
231 self.redraw();
232 }
233 }
234
235 pub fn set_title(&self, title: String) {
240 self.shell_surface.set_title(title)
241 }
242
243 pub fn set_app_id(&self, app_id: String) {
251 self.shell_surface.set_app_id(app_id)
252 }
253
254 pub fn set_decorate(&mut self, decorate: bool) {
259 let mut meta = self.meta.lock().unwrap();
260 meta.decorate = decorate;
261 meta.need_redraw = true;
262 if decorate {
263 let (dx, dy) = ::theme::subsurface_offset();
264 self.contents.set_position(dx, dy);
265 } else {
266 self.contents.set_position(0, 0);
267 }
268 }
269
270 pub fn resize(&mut self, w: i32, h: i32) {
278 use std::cmp::max;
279 let w = max(w, 1);
280 let h = max(h, 1);
281 let mut meta = self.meta.lock().unwrap();
282 meta.dimensions = (w, h);
283 meta.need_redraw = true;
284 }
285
286 pub fn set_state(&mut self, state: State) {
288 match state {
289 State::Regular => {
290 self.shell_surface.unset_fullscreen();
291 self.shell_surface.unset_maximized();
292 }
293 State::Minimized => {
294 self.shell_surface.unset_fullscreen();
295 self.shell_surface.set_minimized();
296 }
297 State::Maximized => {
298 self.shell_surface.unset_fullscreen();
299 self.shell_surface.set_maximized();
300 }
301 State::Fullscreen(output) => {
302 self.shell_surface.set_fullscreen(output);
303 }
304 }
305 }
306
307 pub fn set_min_size(&mut self, size: Option<(i32, i32)>) {
314 let decorate = {
315 let mut meta = self.meta.lock().unwrap();
316 meta.min_size = size;
317 meta.decorate
318 };
319 self.shell_surface
320 .set_min_size(size.map(|(w, h)| if decorate {
321 ::theme::add_borders(w, h)
322 } else {
323 (w, h)
324 }));
325 }
326
327 pub fn set_max_size(&mut self, size: Option<(i32, i32)>) {
334 let decorate = {
335 let mut meta = self.meta.lock().unwrap();
336 meta.max_size = size;
337 meta.decorate
338 };
339 self.shell_surface
340 .set_max_size(size.map(|(w, h)| if decorate {
341 ::theme::add_borders(w, h)
342 } else {
343 (w, h)
344 }));
345 }
346}
347
348impl Drop for Frame {
349 fn drop(&mut self) {
350 self.shell_surface.destroy();
351 self.surface.destroy();
352 self.contents.destroy();
353 if let Some(buffer) = self.buffer.take() {
354 buffer.destroy();
355 }
356 self.pool.destroy();
357 if let Some(ref pointer) = self.pointer {
358 if pointer.version() >= 3 {
359 pointer.release();
360 }
361 }
362 }
363}