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