1 use std::cmp::max;
2 use std::io::Read;
3 use std::sync::{Arc, Mutex};
4 
5 use andrew::shapes::rectangle;
6 use andrew::text;
7 use andrew::text::fontconfig;
8 use andrew::{Canvas, Endian};
9 
10 use wayland_client::protocol::{
11     wl_compositor, wl_pointer, wl_seat, wl_shm, wl_subcompositor, wl_subsurface, wl_surface,
12 };
13 use wayland_client::Proxy;
14 
15 use wayland_client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
16 use wayland_client::protocol::wl_pointer::RequestsTrait as PointerRequests;
17 use wayland_client::protocol::wl_subcompositor::RequestsTrait as SubcompRequests;
18 use wayland_client::protocol::wl_subsurface::RequestsTrait as SubsurfaceRequests;
19 use wayland_client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
20 
21 use super::{ButtonState, Frame, FrameRequest, Theme};
22 use pointer::{AutoPointer, AutoThemer};
23 use utils::DoubleMemPool;
24 
25 /*
26  * Drawing theme definitions
27  */
28 
29 const BORDER_SIZE: u32 = 12;
30 const HEADER_SIZE: u32 = 32;
31 const BUTTON_SPACE: u32 = 10;
32 const ROUNDING_SIZE: u32 = 5;
33 
34 // Defining the theme
35 struct DefaultTheme;
36 
37 impl Theme for DefaultTheme {
38     // Used for header color
get_primary_color(&self, active: bool) -> [u8; 4]39     fn get_primary_color(&self, active: bool) -> [u8; 4] {
40         if active {
41             [0xFF, 0x80, 0x80, 0x80]
42         } else {
43             [0xFF, 0x60, 0x60, 0x60]
44         }
45     }
46 
get_secondary_color(&self, _active: bool) -> [u8; 4]47     fn get_secondary_color(&self, _active: bool) -> [u8; 4] {
48         [0x00, 0x00, 0x00, 0x00]
49     }
50 
get_close_button_color(&self, state: ButtonState) -> [u8; 4]51     fn get_close_button_color(&self, state: ButtonState) -> [u8; 4] {
52         match state {
53             ButtonState::Hovered => [0xFF, 0xFF, 0x40, 0x40],
54             ButtonState::Idle => [0xFF, 0xB0, 0x40, 0x40],
55             _ => [0x00, 0x00, 0x00, 0x00],
56         }
57     }
58 
get_maximize_button_color(&self, state: ButtonState) -> [u8; 4]59     fn get_maximize_button_color(&self, state: ButtonState) -> [u8; 4] {
60         match state {
61             ButtonState::Hovered => [0xFF, 0xFF, 0xFF, 0x40],
62             ButtonState::Idle => [0xFF, 0xB0, 0xB0, 0x40],
63             ButtonState::Disabled => [0xFF, 0x80, 0x80, 0x20],
64         }
65     }
66 
get_minimize_button_color(&self, state: ButtonState) -> [u8; 4]67     fn get_minimize_button_color(&self, state: ButtonState) -> [u8; 4] {
68         match state {
69             ButtonState::Hovered => [0xFF, 0x40, 0xFF, 0x40],
70             ButtonState::Idle => [0xFF, 0x40, 0xB0, 0x40],
71             _ => [0x00, 0x00, 0x00, 0x00],
72         }
73     }
74 }
75 
76 /*
77  * Utilities
78  */
79 
80 const HEAD: usize = 0;
81 const TOP: usize = 1;
82 const BOTTOM: usize = 2;
83 const LEFT: usize = 3;
84 const RIGHT: usize = 4;
85 
86 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
87 enum Location {
88     None,
89     Head,
90     Top,
91     TopRight,
92     Right,
93     BottomRight,
94     Bottom,
95     BottomLeft,
96     Left,
97     TopLeft,
98     Button(UIButton),
99 }
100 
101 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
102 enum UIButton {
103     Minimize,
104     Maximize,
105     Close,
106 }
107 
108 struct Part {
109     surface: Proxy<wl_surface::WlSurface>,
110     subsurface: Proxy<wl_subsurface::WlSubsurface>,
111 }
112 
113 impl Part {
new( parent: &Proxy<wl_surface::WlSurface>, compositor: &Proxy<wl_compositor::WlCompositor>, subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>, ) -> Part114     fn new(
115         parent: &Proxy<wl_surface::WlSurface>,
116         compositor: &Proxy<wl_compositor::WlCompositor>,
117         subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
118     ) -> Part {
119         let surface = compositor
120             .create_surface(|surface| surface.implement(|_, _| {}, ()))
121             .unwrap();
122         let subsurface = subcompositor
123             .get_subsurface(&surface, parent, |subsurface| {
124                 subsurface.implement(|_, _| {}, ())
125             })
126             .unwrap();
127         Part {
128             surface,
129             subsurface,
130         }
131     }
132 }
133 
134 impl Drop for Part {
drop(&mut self)135     fn drop(&mut self) {
136         self.subsurface.destroy();
137         self.surface.destroy();
138     }
139 }
140 
141 struct PointerUserData {
142     location: Location,
143     position: (f64, f64),
144     seat: Proxy<wl_seat::WlSeat>,
145 }
146 
147 /*
148  * The core frame
149  */
150 
151 struct Inner {
152     parts: [Part; 5],
153     size: Mutex<(u32, u32)>,
154     resizable: Arc<Mutex<bool>>,
155     implem: Mutex<Box<FnMut(FrameRequest, u32) + Send>>,
156     maximized: Arc<Mutex<bool>>,
157 }
158 
159 impl Inner {
find_surface(&self, surface: &Proxy<wl_surface::WlSurface>) -> Location160     fn find_surface(&self, surface: &Proxy<wl_surface::WlSurface>) -> Location {
161         if surface.equals(&self.parts[HEAD].surface) {
162             Location::Head
163         } else if surface.equals(&self.parts[TOP].surface) {
164             Location::Top
165         } else if surface.equals(&self.parts[BOTTOM].surface) {
166             Location::Bottom
167         } else if surface.equals(&self.parts[LEFT].surface) {
168             Location::Left
169         } else if surface.equals(&self.parts[RIGHT].surface) {
170             Location::Right
171         } else {
172             Location::None
173         }
174     }
175 }
176 
precise_location(old: Location, width: u32, x: f64, y: f64) -> Location177 fn precise_location(old: Location, width: u32, x: f64, y: f64) -> Location {
178     match old {
179         Location::Head | Location::Button(_) => find_button(x, y, width),
180 
181         Location::Top | Location::TopLeft | Location::TopRight => {
182             if x <= f64::from(BORDER_SIZE) {
183                 Location::TopLeft
184             } else if x >= f64::from(width + BORDER_SIZE) {
185                 Location::TopRight
186             } else {
187                 Location::Top
188             }
189         }
190 
191         Location::Bottom | Location::BottomLeft | Location::BottomRight => {
192             if x <= f64::from(BORDER_SIZE) {
193                 Location::BottomLeft
194             } else if x >= f64::from(width + BORDER_SIZE) {
195                 Location::BottomRight
196             } else {
197                 Location::Bottom
198             }
199         }
200 
201         other => other,
202     }
203 }
204 
find_button(x: f64, y: f64, w: u32) -> Location205 fn find_button(x: f64, y: f64, w: u32) -> Location {
206     if (w >= 24 + 2 * BUTTON_SPACE)
207         && (x >= f64::from(w - 24 - BUTTON_SPACE))
208         && (x <= f64::from(w - BUTTON_SPACE))
209         && (y <= f64::from(HEADER_SIZE) / 2.0 + 8.0)
210         && (y >= f64::from(HEADER_SIZE) / 2.0 - 8.0)
211     {
212         Location::Button(UIButton::Close)
213     } else if (w >= 56 + 2 * BUTTON_SPACE)
214         && (x >= f64::from(w - 56 - BUTTON_SPACE))
215         && (x <= f64::from(w - 32 - BUTTON_SPACE))
216         && (y <= f64::from(HEADER_SIZE) / 2.0 + 8.0)
217         && (y >= f64::from(HEADER_SIZE) / 2.0 - 8.0)
218     {
219         Location::Button(UIButton::Maximize)
220     } else if (w >= 88 + 2 * BUTTON_SPACE)
221         && (x >= f64::from(w - 88 - BUTTON_SPACE))
222         && (x <= f64::from(w - 64 - BUTTON_SPACE))
223         && (y <= f64::from(HEADER_SIZE) / 2.0 + 8.0)
224         && (y >= f64::from(HEADER_SIZE) / 2.0 - 8.0)
225     {
226         Location::Button(UIButton::Minimize)
227     } else {
228         Location::Head
229     }
230 }
231 
232 /// A minimalistic set of decorations
233 ///
234 /// This class draws minimalistic decorations, which are arguably not very
235 /// beautiful, but functional.
236 pub struct BasicFrame {
237     inner: Arc<Inner>,
238     pools: DoubleMemPool,
239     active: bool,
240     hidden: bool,
241     pointers: Vec<AutoPointer>,
242     themer: AutoThemer,
243     surface_version: u32,
244     theme: Box<Theme>,
245     title: Option<String>,
246     font_data: Option<Vec<u8>>,
247 }
248 
249 impl Frame for BasicFrame {
250     type Error = ::std::io::Error;
init( base_surface: &Proxy<wl_surface::WlSurface>, compositor: &Proxy<wl_compositor::WlCompositor>, subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>, shm: &Proxy<wl_shm::WlShm>, implementation: Box<FnMut(FrameRequest, u32) + Send>, ) -> Result<BasicFrame, ::std::io::Error>251     fn init(
252         base_surface: &Proxy<wl_surface::WlSurface>,
253         compositor: &Proxy<wl_compositor::WlCompositor>,
254         subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
255         shm: &Proxy<wl_shm::WlShm>,
256         implementation: Box<FnMut(FrameRequest, u32) + Send>,
257     ) -> Result<BasicFrame, ::std::io::Error> {
258         let parts = [
259             Part::new(base_surface, compositor, subcompositor),
260             Part::new(base_surface, compositor, subcompositor),
261             Part::new(base_surface, compositor, subcompositor),
262             Part::new(base_surface, compositor, subcompositor),
263             Part::new(base_surface, compositor, subcompositor),
264         ];
265         let inner = Arc::new(Inner {
266             parts,
267             size: Mutex::new((1, 1)),
268             resizable: Arc::new(Mutex::new(true)),
269             implem: Mutex::new(implementation),
270             maximized: Arc::new(Mutex::new(false)),
271         });
272         let my_inner = inner.clone();
273         // Send a Refresh request on callback from DoubleMemPool as it will be fired when
274         // None was previously returned from `pool()` and the draw was postponed
275         let pools = DoubleMemPool::new(&shm, move || {
276             (&mut *my_inner.implem.lock().unwrap())(FrameRequest::Refresh, 0);
277         })?;
278         Ok(BasicFrame {
279             inner,
280             pools,
281             active: false,
282             hidden: false,
283             pointers: Vec::new(),
284             themer: AutoThemer::init(None, compositor.clone(), &shm),
285             surface_version: compositor.version(),
286             theme: Box::new(DefaultTheme),
287             title: None,
288             font_data: None,
289         })
290     }
291 
new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>)292     fn new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>) {
293         use self::wl_pointer::Event;
294         let inner = self.inner.clone();
295         let pointer = self.themer.theme_pointer_with_impl(
296             seat,
297             move |event, pointer: AutoPointer| {
298                 let data: &Mutex<PointerUserData> = pointer.user_data().unwrap();
299                 let data = &mut *data.lock().unwrap();
300                 let (width, _) = *(inner.size.lock().unwrap());
301                 let resizable = *(inner.resizable.lock().unwrap());
302                 match event {
303                     Event::Enter {
304                         serial,
305                         surface,
306                         surface_x,
307                         surface_y,
308                     } => {
309                         data.location = precise_location(
310                             inner.find_surface(&surface),
311                             width,
312                             surface_x,
313                             surface_y,
314                         );
315                         data.position = (surface_x, surface_y);
316                         if resizable {
317                             change_pointer(&pointer, data.location, Some(serial));
318                         }
319                     }
320                     Event::Leave { serial, .. } => {
321                         data.location = Location::None;
322                         if resizable {
323                             change_pointer(&pointer, data.location, Some(serial));
324                         }
325                     }
326                     Event::Motion {
327                         surface_x,
328                         surface_y,
329                         ..
330                     } => {
331                         data.position = (surface_x, surface_y);
332                         let newpos = precise_location(data.location, width, surface_x, surface_y);
333                         if newpos != data.location {
334                             match (newpos, data.location) {
335                                 (Location::Button(_), _) | (_, Location::Button(_)) => {
336                                     // pointer movement involves a button, request refresh
337                                     (&mut *inner.implem.lock().unwrap())(FrameRequest::Refresh, 0);
338                                 }
339                                 _ => (),
340                             }
341                             // we changed of part of the decoration, pointer image
342                             // may need to be changed
343                             data.location = newpos;
344                             if resizable {
345                                 change_pointer(&pointer, data.location, None);
346                             }
347                         }
348                     }
349                     Event::Button {
350                         serial,
351                         button,
352                         state,
353                         ..
354                     } => {
355                         if state == wl_pointer::ButtonState::Pressed && button == 0x110 {
356                             // left click
357                             let req = request_for_location(
358                                 data.location,
359                                 &data.seat,
360                                 *(inner.maximized.lock().unwrap()),
361                                 resizable,
362                             );
363                             if let Some(req) = req {
364                                 (&mut *inner.implem.lock().unwrap())(req, serial);
365                             }
366                         }
367                     }
368                     _ => {}
369                 }
370             },
371             Mutex::new(PointerUserData {
372                 location: Location::None,
373                 position: (0.0, 0.0),
374                 seat: seat.clone(),
375             }),
376         );
377         self.pointers.push(pointer);
378     }
379 
set_active(&mut self, active: bool) -> bool380     fn set_active(&mut self, active: bool) -> bool {
381         if self.active != active {
382             self.active = active;
383             true
384         } else {
385             false
386         }
387     }
388 
set_hidden(&mut self, hidden: bool)389     fn set_hidden(&mut self, hidden: bool) {
390         self.hidden = hidden;
391     }
392 
set_maximized(&mut self, maximized: bool) -> bool393     fn set_maximized(&mut self, maximized: bool) -> bool {
394         let mut my_maximized = self.inner.maximized.lock().unwrap();
395         if *my_maximized != maximized {
396             *my_maximized = maximized;
397             true
398         } else {
399             false
400         }
401     }
402 
set_resizable(&mut self, resizable: bool)403     fn set_resizable(&mut self, resizable: bool) {
404         *(self.inner.resizable.lock().unwrap()) = resizable;
405     }
406 
resize(&mut self, newsize: (u32, u32))407     fn resize(&mut self, newsize: (u32, u32)) {
408         *(self.inner.size.lock().unwrap()) = newsize;
409     }
410 
redraw(&mut self)411     fn redraw(&mut self) {
412         if self.hidden {
413             // don't draw the borders
414             for p in &self.inner.parts {
415                 p.surface.attach(None, 0, 0);
416                 p.surface.commit();
417             }
418             return;
419         }
420         let (width, height) = *(self.inner.size.lock().unwrap());
421 
422         {
423             // grab the current pool
424             let pool = match self.pools.pool() {
425                 Some(pool) => pool,
426                 None => return,
427             };
428             // resize the pool as appropriate
429             let pxcount = (HEADER_SIZE * width)
430                 + max(
431                     (width + 2 * BORDER_SIZE) * BORDER_SIZE,
432                     (height + HEADER_SIZE) * BORDER_SIZE,
433                 );
434 
435             pool.resize(4 * pxcount as usize)
436                 .expect("I/O Error while redrawing the borders");
437 
438             // draw the grey header bar
439             {
440                 let mmap = pool.mmap();
441                 {
442                     let color = self.theme.get_primary_color(self.active);
443 
444                     let mut header_canvas = Canvas::new(
445                         &mut mmap[0..HEADER_SIZE as usize * width as usize * 4],
446                         width as usize,
447                         HEADER_SIZE as usize,
448                         width as usize * 4,
449                         Endian::native(),
450                     );
451                     header_canvas.clear();
452 
453                     let header_bar = rectangle::Rectangle::new(
454                         (0, 0),
455                         (width as usize - 1, HEADER_SIZE as usize - 1),
456                         Some((
457                             HEADER_SIZE as usize,
458                             color,
459                             rectangle::Sides::TOP,
460                             Some(ROUNDING_SIZE as usize),
461                         )),
462                         None,
463                     );
464                     header_canvas.draw(&header_bar);
465 
466                     draw_buttons(
467                         &mut header_canvas,
468                         width,
469                         true,
470                         &self
471                             .pointers
472                             .iter()
473                             .flat_map(|p| {
474                                 if p.is_alive() {
475                                     let data: &Mutex<PointerUserData> = p.user_data().unwrap();
476                                     Some(data.lock().unwrap().location)
477                                 } else {
478                                     None
479                                 }
480                             })
481                             .collect::<Vec<Location>>(),
482                         &*self.theme,
483                     );
484 
485                     if let Some(title) = self.title.clone() {
486                         // If theres no stored font data, find the first ttf regular sans font and
487                         // store it
488                         if self.font_data.is_none() {
489                             if let Some(font) = fontconfig::FontConfig::new()
490                                 .unwrap()
491                                 .get_regular_family_fonts("sans")
492                                 .unwrap()
493                                 .iter()
494                                 .filter_map(|p| {
495                                     if p.extension().unwrap() == "ttf" {
496                                         Some(p)
497                                     } else {
498                                         None
499                                     }
500                                 })
501                                 .nth(0)
502                             {
503                                 let mut font_data = Vec::new();
504                                 if let Ok(mut file) = ::std::fs::File::open(font) {
505                                     match file.read_to_end(&mut font_data) {
506                                         Ok(_) => self.font_data = Some(font_data),
507                                         Err(err) => eprintln!("Could not read font file: {}", err),
508                                     }
509                                 }
510                             }
511                         }
512 
513                         // Create text from stored title and font data
514                         if let Some(ref font_data) = self.font_data {
515                             let mut title_text = text::Text::new(
516                                 (0, HEADER_SIZE as usize / 2 - 8),
517                                 [0, 0, 0, 255],
518                                 font_data,
519                                 17.0,
520                                 1.0,
521                                 title,
522                             );
523 
524                             // Check if text is bigger then the avaliable width
525                             if (width as isize - 88 - 4 * BUTTON_SPACE as isize)
526                                 > (title_text.get_width() + BUTTON_SPACE as usize) as isize
527                             {
528                                 title_text.pos.0 =
529                                     (width as usize) / 2 - (title_text.get_width() / 2);
530                                 // Adjust position for buttons if both compete for space
531                                 if (width as usize) / 2 + (title_text.get_width() / 2)
532                                     > (width - 88 - 2 * 2 * BUTTON_SPACE) as usize
533                                 {
534                                     title_text.pos.0 -= ((width as usize) / 2
535                                         + (title_text.get_width() / 2))
536                                         - (width - 88 - 2 * 2 * BUTTON_SPACE) as usize;
537                                 }
538                                 header_canvas.draw(&title_text);
539                             }
540                         }
541                     }
542                 }
543 
544                 // For each pixel in borders
545                 {
546                     for b in &mut mmap[HEADER_SIZE as usize * width as usize * 4..] {
547                         *b = 0x00;
548                     }
549                 }
550                 if let Err(err) = mmap.flush() {
551                     eprintln!(
552                         "[SCTK] Basic frame: failed to flush frame memory map: {}",
553                         err
554                     );
555                 }
556             }
557 
558             // Create the buffers
559             // -> head-subsurface
560             let buffer = pool.buffer(
561                 0,
562                 width as i32,
563                 HEADER_SIZE as i32,
564                 4 * width as i32,
565                 wl_shm::Format::Argb8888,
566             );
567             self.inner.parts[HEAD]
568                 .subsurface
569                 .set_position(0, -(HEADER_SIZE as i32));
570             self.inner.parts[HEAD].surface.attach(Some(&buffer), 0, 0);
571             if self.surface_version >= 4 {
572                 self.inner.parts[HEAD].surface.damage_buffer(
573                     0,
574                     0,
575                     width as i32,
576                     HEADER_SIZE as i32,
577                 );
578             } else {
579                 // surface is old and does not support damage_buffer, so we damage
580                 // in surface coordinates and hope it is not rescaled
581                 self.inner.parts[HEAD]
582                     .surface
583                     .damage(0, 0, width as i32, HEADER_SIZE as i32);
584             }
585             self.inner.parts[HEAD].surface.commit();
586 
587             // -> top-subsurface
588             let buffer = pool.buffer(
589                 4 * (width * HEADER_SIZE) as i32,
590                 (width + 2 * BORDER_SIZE) as i32,
591                 BORDER_SIZE as i32,
592                 4 * (width + 2 * BORDER_SIZE) as i32,
593                 wl_shm::Format::Argb8888,
594             );
595             self.inner.parts[TOP].subsurface.set_position(
596                 -(BORDER_SIZE as i32),
597                 -(HEADER_SIZE as i32 + BORDER_SIZE as i32),
598             );
599             self.inner.parts[TOP].surface.attach(Some(&buffer), 0, 0);
600             if self.surface_version >= 4 {
601                 self.inner.parts[TOP].surface.damage_buffer(
602                     0,
603                     0,
604                     (width + 2 * BORDER_SIZE) as i32,
605                     BORDER_SIZE as i32,
606                 );
607             } else {
608                 // surface is old and does not support damage_buffer, so we damage
609                 // in surface coordinates and hope it is not rescaled
610                 self.inner.parts[TOP].surface.damage(
611                     0,
612                     0,
613                     (width + 2 * BORDER_SIZE) as i32,
614                     BORDER_SIZE as i32,
615                 );
616             }
617             self.inner.parts[TOP].surface.commit();
618 
619             // -> bottom-subsurface
620             let buffer = pool.buffer(
621                 4 * (width * HEADER_SIZE) as i32,
622                 (width + 2 * BORDER_SIZE) as i32,
623                 BORDER_SIZE as i32,
624                 4 * (width + 2 * BORDER_SIZE) as i32,
625                 wl_shm::Format::Argb8888,
626             );
627             self.inner.parts[BOTTOM]
628                 .subsurface
629                 .set_position(-(BORDER_SIZE as i32), height as i32);
630             self.inner.parts[BOTTOM].surface.attach(Some(&buffer), 0, 0);
631             if self.surface_version >= 4 {
632                 self.inner.parts[BOTTOM].surface.damage_buffer(
633                     0,
634                     0,
635                     (width + 2 * BORDER_SIZE) as i32,
636                     BORDER_SIZE as i32,
637                 );
638             } else {
639                 // surface is old and does not support damage_buffer, so we damage
640                 // in surface coordinates and hope it is not rescaled
641                 self.inner.parts[BOTTOM].surface.damage(
642                     0,
643                     0,
644                     (width + 2 * BORDER_SIZE) as i32,
645                     BORDER_SIZE as i32,
646                 );
647             }
648             self.inner.parts[BOTTOM].surface.commit();
649 
650             // -> left-subsurface
651             let buffer = pool.buffer(
652                 4 * (width * HEADER_SIZE) as i32,
653                 BORDER_SIZE as i32,
654                 (height + HEADER_SIZE) as i32,
655                 4 * (BORDER_SIZE as i32),
656                 wl_shm::Format::Argb8888,
657             );
658             self.inner.parts[LEFT]
659                 .subsurface
660                 .set_position(-(BORDER_SIZE as i32), -(HEADER_SIZE as i32));
661             self.inner.parts[LEFT].surface.attach(Some(&buffer), 0, 0);
662             if self.surface_version >= 4 {
663                 self.inner.parts[LEFT].surface.damage_buffer(
664                     0,
665                     0,
666                     BORDER_SIZE as i32,
667                     (height + HEADER_SIZE) as i32,
668                 );
669             } else {
670                 // surface is old and does not support damage_buffer, so we damage
671                 // in surface coordinates and hope it is not rescaled
672                 self.inner.parts[LEFT].surface.damage(
673                     0,
674                     0,
675                     BORDER_SIZE as i32,
676                     (height + HEADER_SIZE) as i32,
677                 );
678             }
679             self.inner.parts[LEFT].surface.commit();
680 
681             // -> right-subsurface
682             let buffer = pool.buffer(
683                 4 * (width * HEADER_SIZE) as i32,
684                 BORDER_SIZE as i32,
685                 (height + HEADER_SIZE) as i32,
686                 4 * (BORDER_SIZE as i32),
687                 wl_shm::Format::Argb8888,
688             );
689             self.inner.parts[RIGHT]
690                 .subsurface
691                 .set_position(width as i32, -(HEADER_SIZE as i32));
692             self.inner.parts[RIGHT].surface.attach(Some(&buffer), 0, 0);
693             if self.surface_version >= 4 {
694                 self.inner.parts[RIGHT].surface.damage_buffer(
695                     0,
696                     0,
697                     BORDER_SIZE as i32,
698                     (height + HEADER_SIZE) as i32,
699                 );
700             } else {
701                 // surface is old and does not support damage_buffer, so we damage
702                 // in surface coordinates and hope it is not rescaled
703                 self.inner.parts[RIGHT].surface.damage(
704                     0,
705                     0,
706                     BORDER_SIZE as i32,
707                     (height + HEADER_SIZE) as i32,
708                 );
709             }
710             self.inner.parts[RIGHT].surface.commit();
711         }
712     }
713 
subtract_borders(&self, width: i32, height: i32) -> (i32, i32)714     fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32) {
715         if self.hidden {
716             (width, height)
717         } else {
718             (width, height - HEADER_SIZE as i32)
719         }
720     }
721 
add_borders(&self, width: i32, height: i32) -> (i32, i32)722     fn add_borders(&self, width: i32, height: i32) -> (i32, i32) {
723         if self.hidden {
724             (width, height)
725         } else {
726             (width, height + HEADER_SIZE as i32)
727         }
728     }
729 
location(&self) -> (i32, i32)730     fn location(&self) -> (i32, i32) {
731         if self.hidden {
732             (0, 0)
733         } else {
734             (0, -(HEADER_SIZE as i32))
735         }
736     }
737 
set_theme<T: Theme>(&mut self, theme: T)738     fn set_theme<T: Theme>(&mut self, theme: T) {
739         self.theme = Box::new(theme)
740     }
741 
set_title(&mut self, title: String)742     fn set_title(&mut self, title: String) {
743         self.title = Some(title);
744     }
745 }
746 
747 impl Drop for BasicFrame {
drop(&mut self)748     fn drop(&mut self) {
749         for ptr in self.pointers.drain(..) {
750             if ptr.version() >= 3 {
751                 ptr.release();
752             }
753         }
754     }
755 }
756 
change_pointer(pointer: &AutoPointer, location: Location, serial: Option<u32>)757 fn change_pointer(pointer: &AutoPointer, location: Location, serial: Option<u32>) {
758     let name = match location {
759         Location::Top => "top_side",
760         Location::TopRight => "top_right_corner",
761         Location::Right => "right_side",
762         Location::BottomRight => "bottom_right_corner",
763         Location::Bottom => "bottom_side",
764         Location::BottomLeft => "bottom_left_corner",
765         Location::Left => "left_side",
766         Location::TopLeft => "top_left_corner",
767         _ => "left_ptr",
768     };
769     if pointer.set_cursor(name, serial).is_err() {
770         eprintln!("[SCTK] Basic frame: failed to set cursor");
771     }
772 }
773 
request_for_location( location: Location, seat: &Proxy<wl_seat::WlSeat>, maximized: bool, resizable: bool, ) -> Option<FrameRequest>774 fn request_for_location(
775     location: Location,
776     seat: &Proxy<wl_seat::WlSeat>,
777     maximized: bool,
778     resizable: bool,
779 ) -> Option<FrameRequest> {
780     use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
781     match location {
782         Location::Top if resizable => Some(FrameRequest::Resize(seat.clone(), ResizeEdge::Top)),
783         Location::TopLeft if resizable => {
784             Some(FrameRequest::Resize(seat.clone(), ResizeEdge::TopLeft))
785         }
786         Location::Left if resizable => Some(FrameRequest::Resize(seat.clone(), ResizeEdge::Left)),
787         Location::BottomLeft if resizable => {
788             Some(FrameRequest::Resize(seat.clone(), ResizeEdge::BottomLeft))
789         }
790         Location::Bottom if resizable => {
791             Some(FrameRequest::Resize(seat.clone(), ResizeEdge::Bottom))
792         }
793         Location::BottomRight if resizable => {
794             Some(FrameRequest::Resize(seat.clone(), ResizeEdge::BottomRight))
795         }
796         Location::Right if resizable => Some(FrameRequest::Resize(seat.clone(), ResizeEdge::Right)),
797         Location::TopRight if resizable => {
798             Some(FrameRequest::Resize(seat.clone(), ResizeEdge::TopRight))
799         }
800         Location::Head => Some(FrameRequest::Move(seat.clone())),
801         Location::Button(UIButton::Close) => Some(FrameRequest::Close),
802         Location::Button(UIButton::Maximize) => {
803             if maximized {
804                 Some(FrameRequest::UnMaximize)
805             } else {
806                 Some(FrameRequest::Maximize)
807             }
808         }
809         Location::Button(UIButton::Minimize) => Some(FrameRequest::Minimize),
810         _ => None,
811     }
812 }
813 
draw_buttons( canvas: &mut Canvas, width: u32, maximizable: bool, mouses: &[Location], theme: &Theme, )814 fn draw_buttons(
815     canvas: &mut Canvas,
816     width: u32,
817     maximizable: bool,
818     mouses: &[Location],
819     theme: &Theme,
820 ) {
821     // draw up to 3 buttons, depending on the width of the window
822     // color of the button depends on whether a pointer is on it, and the maximizable
823     // button can be disabled
824     // buttons are 24x16
825     if width >= 24 + 2 * BUTTON_SPACE {
826         // draw the red button
827         let button_state = if mouses
828             .iter()
829             .any(|&l| l == Location::Button(UIButton::Close))
830         {
831             ButtonState::Hovered
832         } else {
833             ButtonState::Idle
834         };
835         let color = theme.get_close_button_color(button_state);
836         let red_button = rectangle::Rectangle::new(
837             (
838                 (width - 24 - BUTTON_SPACE) as usize,
839                 (HEADER_SIZE / 2 - 8) as usize,
840             ),
841             (24, 16),
842             None,
843             Some(color),
844         );
845         canvas.draw(&red_button);
846     }
847 
848     if width >= 56 + 2 * BUTTON_SPACE {
849         // draw the yellow button
850         let button_state = if !maximizable {
851             ButtonState::Disabled
852         } else if mouses
853             .iter()
854             .any(|&l| l == Location::Button(UIButton::Maximize))
855         {
856             ButtonState::Hovered
857         } else {
858             ButtonState::Idle
859         };
860         let color = theme.get_maximize_button_color(button_state);
861 
862         let yellow_button = rectangle::Rectangle::new(
863             (
864                 (width - 56 - BUTTON_SPACE) as usize,
865                 (HEADER_SIZE / 2 - 8) as usize,
866             ),
867             (24, 16),
868             None,
869             Some(color),
870         );
871         canvas.draw(&yellow_button);
872     }
873 
874     if width >= 88 + 2 * BUTTON_SPACE {
875         // draw the green button
876         let button_state = if mouses
877             .iter()
878             .any(|&l| l == Location::Button(UIButton::Minimize))
879         {
880             ButtonState::Hovered
881         } else {
882             ButtonState::Idle
883         };
884         let color = theme.get_minimize_button_color(button_state);
885         let green_button = rectangle::Rectangle::new(
886             (
887                 (width - 88 - BUTTON_SPACE) as usize,
888                 (HEADER_SIZE / 2 - 8) as usize,
889             ),
890             (24, 16),
891             None,
892             Some(color),
893         );
894         canvas.draw(&green_button);
895     }
896 }
897