smithay_client_toolkit/shell/xdg/
fallback_frame.rs

1//! The default fallback frame which is intended to show some very basic derocations.
2
3use std::mem;
4use std::sync::Arc;
5use std::time::Duration;
6use std::{error::Error, num::NonZeroU32};
7
8use crate::reexports::client::{
9    protocol::{wl_shm, wl_subsurface::WlSubsurface, wl_surface::WlSurface},
10    Dispatch, Proxy, QueueHandle,
11};
12use crate::reexports::csd_frame::{
13    DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowManagerCapabilities, WindowState,
14};
15
16use crate::{
17    compositor::SurfaceData,
18    seat::pointer::CursorIcon,
19    shell::WaylandSurface,
20    shm::{slot::SlotPool, Shm},
21    subcompositor::{SubcompositorState, SubsurfaceData},
22};
23
24use wayland_backend::client::ObjectId;
25
26/// The size of the header bar.
27const HEADER_SIZE: u32 = 24;
28
29/// The size of the border.
30const BORDER_SIZE: u32 = 4;
31
32const HEADER: usize = 0;
33const TOP_BORDER: usize = 1;
34const RIGHT_BORDER: usize = 2;
35const BOTTOM_BORDER: usize = 3;
36const LEFT_BORDER: usize = 4;
37
38const BTN_ICON_COLOR: u32 = 0xFFCCCCCC;
39const BTN_HOVER_BG: u32 = 0xFF808080;
40
41const PRIMARY_COLOR_ACTIVE: u32 = 0xFF3A3A3A;
42const PRIMARY_COLOR_INACTIVE: u32 = 0xFF242424;
43
44/// The default ugly frame.
45#[derive(Debug)]
46pub struct FallbackFrame<State> {
47    /// The parent surface.
48    parent: WlSurface,
49
50    /// The latest window state.
51    state: WindowState,
52
53    /// The wm capabilities.
54    wm_capabilities: WindowManagerCapabilities,
55
56    /// Whether the frame is resizable.
57    resizable: bool,
58
59    /// Whether the frame is waiting for redraw.
60    dirty: bool,
61
62    /// The location of the mouse.
63    mouse_location: Location,
64
65    /// The location of the mouse.
66    mouse_coords: (i32, i32),
67
68    /// The frame rendering data. When `None` the frame is hidden.
69    render_data: Option<FrameRenderData>,
70
71    /// Whether the frame should sync with the parent.
72    ///
73    /// This should happen in reaction to scale or resize changes.
74    should_sync: bool,
75
76    /// The active scale factor of the frame.
77    scale_factor: f64,
78
79    /// The frame queue handle.
80    queue_handle: QueueHandle<State>,
81
82    /// The memory pool to use for drawing.
83    pool: SlotPool,
84
85    /// The subcompositor.
86    subcompositor: Arc<SubcompositorState>,
87
88    /// Buttons state.
89    buttons: [Option<UIButton>; 3],
90}
91
92impl<State> FallbackFrame<State>
93where
94    State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static,
95{
96    pub fn new(
97        parent: &impl WaylandSurface,
98        shm: &Shm,
99        subcompositor: Arc<SubcompositorState>,
100        queue_handle: QueueHandle<State>,
101    ) -> Result<Self, Box<dyn Error>> {
102        let parent = parent.wl_surface().clone();
103        let pool = SlotPool::new(1, shm)?;
104        let render_data = Some(FrameRenderData::new(&parent, &subcompositor, &queue_handle));
105
106        let wm_capabilities = WindowManagerCapabilities::all();
107        Ok(Self {
108            parent,
109            resizable: true,
110            state: WindowState::empty(),
111            wm_capabilities,
112            dirty: true,
113            scale_factor: 1.,
114            pool,
115            should_sync: true,
116            queue_handle,
117            subcompositor,
118            render_data,
119            mouse_location: Location::None,
120            mouse_coords: (0, 0),
121            buttons: Self::supported_buttons(wm_capabilities),
122        })
123    }
124
125    fn supported_buttons(wm_capabilities: WindowManagerCapabilities) -> [Option<UIButton>; 3] {
126        let maximize = wm_capabilities
127            .contains(WindowManagerCapabilities::MAXIMIZE)
128            .then_some(UIButton::Maximize);
129        let minimize = wm_capabilities
130            .contains(WindowManagerCapabilities::MINIMIZE)
131            .then_some(UIButton::Minimize);
132        [Some(UIButton::Close), maximize, minimize]
133    }
134
135    fn precise_location(
136        buttons: &[Option<UIButton>],
137        old: Location,
138        width: u32,
139        x: f64,
140        y: f64,
141    ) -> Location {
142        match old {
143            Location::Head | Location::Button(_) => Self::find_button(buttons, x, y, width),
144
145            Location::Top | Location::TopLeft | Location::TopRight => {
146                if x <= f64::from(BORDER_SIZE) {
147                    Location::TopLeft
148                } else if x >= f64::from(width - BORDER_SIZE) {
149                    Location::TopRight
150                } else {
151                    Location::Top
152                }
153            }
154
155            Location::Bottom | Location::BottomLeft | Location::BottomRight => {
156                if x <= f64::from(BORDER_SIZE) {
157                    Location::BottomLeft
158                } else if x >= f64::from(width - BORDER_SIZE) {
159                    Location::BottomRight
160                } else {
161                    Location::Bottom
162                }
163            }
164
165            other => other,
166        }
167    }
168
169    fn find_button(buttons: &[Option<UIButton>], x: f64, y: f64, w: u32) -> Location {
170        for (idx, &button) in buttons.iter().flatten().enumerate() {
171            let idx = idx as u32;
172            if w >= (idx + 1) * HEADER_SIZE
173                && x >= f64::from(w - (idx + 1) * HEADER_SIZE)
174                && x <= f64::from(w - idx * HEADER_SIZE)
175                && y <= f64::from(HEADER_SIZE)
176                && y >= f64::from(0)
177            {
178                return Location::Button(button);
179            }
180        }
181
182        Location::Head
183    }
184
185    #[inline]
186    fn part_index_for_surface(&mut self, surface_id: &ObjectId) -> Option<usize> {
187        self.render_data.as_ref()?.parts.iter().position(|part| &part.surface.id() == surface_id)
188    }
189
190    fn draw_buttons(
191        buttons: &[Option<UIButton>],
192        canvas: &mut [u8],
193        width: u32,
194        scale: u32,
195        is_active: bool,
196        mouse_location: &Location,
197    ) {
198        let scale = scale as usize;
199        for (idx, &button) in buttons.iter().flatten().enumerate() {
200            if width >= (idx + 1) as u32 * HEADER_SIZE {
201                if is_active && mouse_location == &Location::Button(button) {
202                    Self::draw_button(
203                        canvas,
204                        idx * HEADER_SIZE as usize,
205                        scale,
206                        width as usize,
207                        BTN_HOVER_BG.to_le_bytes(),
208                    );
209                }
210                Self::draw_icon(
211                    canvas,
212                    width as usize,
213                    idx * HEADER_SIZE as usize,
214                    scale,
215                    BTN_ICON_COLOR.to_le_bytes(),
216                    button,
217                );
218            }
219        }
220    }
221
222    fn draw_button(
223        canvas: &mut [u8],
224        x_offset: usize,
225        scale: usize,
226        width: usize,
227        btn_color: [u8; 4],
228    ) {
229        let h = HEADER_SIZE as usize;
230        let x_start = width - h - x_offset;
231        // main square
232        for y in 0..h * scale {
233            let canvas = &mut canvas
234                [(x_start + y * width) * 4 * scale..(x_start + y * width + h) * scale * 4];
235            for pixel in canvas.chunks_exact_mut(4) {
236                pixel[0] = btn_color[0];
237                pixel[1] = btn_color[1];
238                pixel[2] = btn_color[2];
239                pixel[3] = btn_color[3];
240            }
241        }
242    }
243
244    fn draw_icon(
245        canvas: &mut [u8],
246        width: usize,
247        x_offset: usize,
248        scale: usize,
249        icon_color: [u8; 4],
250        icon: UIButton,
251    ) {
252        let h = HEADER_SIZE as usize;
253        let sh = scale * h;
254        let x_start = width - h - x_offset;
255
256        match icon {
257            UIButton::Close => {
258                // Draw black rectangle
259                for y in sh / 4..3 * sh / 4 {
260                    let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
261                        ..(x_start + y * width + 3 * h / 4) * 4 * scale];
262                    for pixel in line.chunks_exact_mut(4) {
263                        pixel[0] = icon_color[0];
264                        pixel[1] = icon_color[1];
265                        pixel[2] = icon_color[2];
266                        pixel[3] = icon_color[3];
267                    }
268                }
269            }
270            UIButton::Maximize => {
271                // Draw an empty rectangle
272                for y in 2 * sh / 8..3 * sh / 8 {
273                    let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
274                        ..(x_start + y * width + 3 * h / 4) * 4 * scale];
275                    for pixel in line.chunks_exact_mut(4) {
276                        pixel[0] = icon_color[0];
277                        pixel[1] = icon_color[1];
278                        pixel[2] = icon_color[2];
279                        pixel[3] = icon_color[3];
280                    }
281                }
282                for y in 3 * sh / 8..5 * sh / 8 {
283                    let line = &mut canvas[(x_start + y * width + 2 * h / 8) * 4 * scale
284                        ..(x_start + y * width + 3 * h / 8) * 4 * scale];
285                    for pixel in line.chunks_exact_mut(4) {
286                        pixel[0] = icon_color[0];
287                        pixel[1] = icon_color[1];
288                        pixel[2] = icon_color[2];
289                        pixel[3] = icon_color[3];
290                    }
291                    let line = &mut canvas[(x_start + y * width + 5 * h / 8) * 4 * scale
292                        ..(x_start + y * width + 6 * h / 8) * 4 * scale];
293                    for pixel in line.chunks_exact_mut(4) {
294                        pixel[0] = icon_color[0];
295                        pixel[1] = icon_color[1];
296                        pixel[2] = icon_color[2];
297                        pixel[3] = icon_color[3];
298                    }
299                }
300                for y in 5 * sh / 8..6 * sh / 8 {
301                    let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
302                        ..(x_start + y * width + 3 * h / 4) * 4 * scale];
303                    for pixel in line.chunks_exact_mut(4) {
304                        pixel[0] = icon_color[0];
305                        pixel[1] = icon_color[1];
306                        pixel[2] = icon_color[2];
307                        pixel[3] = icon_color[3];
308                    }
309                }
310            }
311            UIButton::Minimize => {
312                // Draw an underline
313                for y in 5 * sh / 8..3 * sh / 4 {
314                    let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
315                        ..(x_start + y * width + 3 * h / 4) * 4 * scale];
316                    for pixel in line.chunks_exact_mut(4) {
317                        pixel[0] = icon_color[0];
318                        pixel[1] = icon_color[1];
319                        pixel[2] = icon_color[2];
320                        pixel[3] = icon_color[3];
321                    }
322                }
323            }
324        }
325    }
326}
327
328impl<State> DecorationsFrame for FallbackFrame<State>
329where
330    State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static,
331{
332    fn set_scaling_factor(&mut self, scale_factor: f64) {
333        self.scale_factor = scale_factor;
334        self.dirty = true;
335        self.should_sync = true;
336    }
337
338    fn on_click(
339        &mut self,
340        _timestamp: Duration,
341        click: FrameClick,
342        pressed: bool,
343    ) -> Option<FrameAction> {
344        // Handle alternate click before everything else.
345        if click == FrameClick::Alternate {
346            return if Location::Head != self.mouse_location
347                || !self.wm_capabilities.contains(WindowManagerCapabilities::WINDOW_MENU)
348            {
349                None
350            } else {
351                Some(FrameAction::ShowMenu(
352                    self.mouse_coords.0,
353                    self.mouse_coords.1 - HEADER_SIZE as i32,
354                ))
355            };
356        }
357
358        let resize = pressed && self.resizable;
359        match self.mouse_location {
360            Location::Head if pressed => Some(FrameAction::Move),
361            Location::Button(UIButton::Close) if !pressed => Some(FrameAction::Close),
362            Location::Button(UIButton::Minimize) if !pressed => Some(FrameAction::Minimize),
363            Location::Button(UIButton::Maximize)
364                if !pressed && !self.state.contains(WindowState::MAXIMIZED) =>
365            {
366                Some(FrameAction::Maximize)
367            }
368            Location::Button(UIButton::Maximize)
369                if !pressed && self.state.contains(WindowState::MAXIMIZED) =>
370            {
371                Some(FrameAction::UnMaximize)
372            }
373            Location::Top if resize => Some(FrameAction::Resize(ResizeEdge::Top)),
374            Location::TopLeft if resize => Some(FrameAction::Resize(ResizeEdge::TopLeft)),
375            Location::Left if resize => Some(FrameAction::Resize(ResizeEdge::Left)),
376            Location::BottomLeft if resize => Some(FrameAction::Resize(ResizeEdge::BottomLeft)),
377            Location::Bottom if resize => Some(FrameAction::Resize(ResizeEdge::Bottom)),
378            Location::BottomRight if resize => Some(FrameAction::Resize(ResizeEdge::BottomRight)),
379            Location::Right if resize => Some(FrameAction::Resize(ResizeEdge::Right)),
380            Location::TopRight if resize => Some(FrameAction::Resize(ResizeEdge::TopRight)),
381            _ => None,
382        }
383    }
384
385    fn click_point_moved(
386        &mut self,
387        _timestamp: Duration,
388        surface_id: &ObjectId,
389        x: f64,
390        y: f64,
391    ) -> Option<CursorIcon> {
392        let part_index = self.part_index_for_surface(surface_id)?;
393        let location = match part_index {
394            LEFT_BORDER => Location::Left,
395            RIGHT_BORDER => Location::Right,
396            BOTTOM_BORDER => Location::Bottom,
397            TOP_BORDER => Location::Top,
398            _ => Location::Head,
399        };
400
401        let old_location = self.mouse_location;
402        self.mouse_coords = (x as i32, y as i32);
403        self.mouse_location = Self::precise_location(
404            &self.buttons,
405            location,
406            self.render_data.as_ref().unwrap().parts[part_index].width,
407            x,
408            y,
409        );
410
411        // Set dirty if we moved the cursor between the buttons.
412        self.dirty |= (matches!(old_location, Location::Button(_))
413            || matches!(self.mouse_location, Location::Button(_)))
414            && old_location != self.mouse_location;
415
416        Some(match self.mouse_location {
417            Location::Top => CursorIcon::NResize,
418            Location::TopRight => CursorIcon::NeResize,
419            Location::Right => CursorIcon::EResize,
420            Location::BottomRight => CursorIcon::SeResize,
421            Location::Bottom => CursorIcon::SResize,
422            Location::BottomLeft => CursorIcon::SwResize,
423            Location::Left => CursorIcon::WResize,
424            Location::TopLeft => CursorIcon::NwResize,
425            _ => CursorIcon::Default,
426        })
427    }
428
429    fn click_point_left(&mut self) {
430        self.mouse_location = Location::None;
431        self.dirty = true;
432    }
433
434    fn set_hidden(&mut self, hidden: bool) {
435        if self.is_hidden() == hidden {
436            return;
437        }
438
439        if hidden {
440            self.render_data = None;
441        } else {
442            let _ = self.pool.resize(1);
443            self.render_data =
444                Some(FrameRenderData::new(&self.parent, &self.subcompositor, &self.queue_handle));
445        }
446    }
447
448    fn set_resizable(&mut self, resizable: bool) {
449        self.resizable = resizable;
450    }
451
452    fn update_state(&mut self, state: WindowState) {
453        let difference = self.state.symmetric_difference(state);
454        self.state = state;
455        self.dirty |= !difference
456            .intersection(WindowState::ACTIVATED | WindowState::FULLSCREEN | WindowState::MAXIMIZED)
457            .is_empty();
458    }
459
460    fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) {
461        let parts = &mut self.render_data.as_mut().expect("trying to resize hidden frame").parts;
462
463        let width = width.get();
464        let height = height.get();
465
466        parts[HEADER].width = width;
467
468        parts[TOP_BORDER].width = width + 2 * BORDER_SIZE;
469
470        parts[BOTTOM_BORDER].width = width + 2 * BORDER_SIZE;
471        parts[BOTTOM_BORDER].pos.1 = height as i32;
472
473        parts[LEFT_BORDER].height = height + HEADER_SIZE;
474
475        parts[RIGHT_BORDER].height = parts[LEFT_BORDER].height;
476        parts[RIGHT_BORDER].pos.0 = width as i32;
477
478        self.dirty = true;
479        self.should_sync = true;
480    }
481
482    fn subtract_borders(
483        &self,
484        width: NonZeroU32,
485        height: NonZeroU32,
486    ) -> (Option<NonZeroU32>, Option<NonZeroU32>) {
487        if self.state.contains(WindowState::FULLSCREEN) || self.render_data.is_none() {
488            (Some(width), Some(height))
489        } else {
490            (
491                NonZeroU32::new(width.get().saturating_sub(2 * BORDER_SIZE)),
492                NonZeroU32::new(height.get().saturating_sub(HEADER_SIZE + 2 * BORDER_SIZE)),
493            )
494        }
495    }
496
497    fn add_borders(&self, width: u32, height: u32) -> (u32, u32) {
498        if self.state.contains(WindowState::FULLSCREEN) || self.render_data.is_none() {
499            (width, height)
500        } else {
501            (width + 2 * BORDER_SIZE, height + (HEADER_SIZE + 2 * BORDER_SIZE))
502        }
503    }
504
505    fn is_hidden(&self) -> bool {
506        self.render_data.is_none()
507    }
508
509    fn location(&self) -> (i32, i32) {
510        if self.state.contains(WindowState::FULLSCREEN) || self.is_hidden() {
511            (0, 0)
512        } else {
513            self.render_data.as_ref().unwrap().parts[TOP_BORDER].pos
514        }
515    }
516
517    fn is_dirty(&self) -> bool {
518        self.dirty
519    }
520
521    fn draw(&mut self) -> bool {
522        let render_data = match self.render_data.as_mut() {
523            Some(render_data) => render_data,
524            None => return false,
525        };
526
527        // Reset the dirty bit and sync option.
528        self.dirty = false;
529        let should_sync = mem::take(&mut self.should_sync);
530
531        if self.state.contains(WindowState::FULLSCREEN) {
532            // Don't draw the decorations for the full screen surface.
533            for part in &render_data.parts {
534                part.surface.attach(None, 0, 0);
535                part.surface.commit();
536            }
537            return should_sync;
538        }
539
540        let is_active = self.state.contains(WindowState::ACTIVATED);
541        let fill_color =
542            if is_active { PRIMARY_COLOR_ACTIVE } else { PRIMARY_COLOR_INACTIVE }.to_le_bytes();
543
544        for (idx, part) in render_data.parts.iter().enumerate() {
545            // We don't support fractinal scaling here, so round up.
546            let scale = self.scale_factor.ceil() as i32;
547
548            let (buffer, canvas) = match self.pool.create_buffer(
549                part.width as i32 * scale,
550                part.height as i32 * scale,
551                part.width as i32 * 4 * scale,
552                wl_shm::Format::Argb8888,
553            ) {
554                Ok((buffer, canvas)) => (buffer, canvas),
555                Err(_) => continue,
556            };
557
558            // Fill the canvas.
559            for pixel in canvas.chunks_exact_mut(4) {
560                pixel[0] = fill_color[0];
561                pixel[1] = fill_color[1];
562                pixel[2] = fill_color[2];
563                pixel[3] = fill_color[3];
564            }
565
566            // Draw the buttons for the header.
567            if idx == HEADER {
568                Self::draw_buttons(
569                    &self.buttons,
570                    canvas,
571                    part.width,
572                    scale as u32,
573                    is_active,
574                    &self.mouse_location,
575                );
576            }
577
578            part.surface.set_buffer_scale(scale);
579            if should_sync {
580                part.subsurface.set_sync();
581            } else {
582                part.subsurface.set_desync();
583            }
584
585            // Update the subsurface position.
586            part.subsurface.set_position(part.pos.0, part.pos.1);
587
588            buffer.attach_to(&part.surface).expect("failed to attach the buffer");
589            if part.surface.version() >= 4 {
590                part.surface.damage_buffer(0, 0, i32::MAX, i32::MAX);
591            } else {
592                part.surface.damage(0, 0, i32::MAX, i32::MAX);
593            }
594
595            part.surface.commit();
596        }
597
598        should_sync
599    }
600
601    fn update_wm_capabilities(&mut self, capabilities: WindowManagerCapabilities) {
602        self.dirty |= self.wm_capabilities != capabilities;
603        self.wm_capabilities = capabilities;
604        self.buttons = Self::supported_buttons(capabilities);
605    }
606
607    fn set_title(&mut self, _: impl Into<String>) {}
608}
609
610/// Inner state to simplify dropping.
611#[derive(Debug)]
612struct FrameRenderData {
613    /// The header subsurface.
614    parts: [FramePart; 5],
615}
616
617impl FrameRenderData {
618    fn new<State>(
619        parent: &WlSurface,
620        subcompositor: &SubcompositorState,
621        queue_handle: &QueueHandle<State>,
622    ) -> Self
623    where
624        State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static,
625    {
626        let parts = [
627            // Header.
628            FramePart::new(
629                subcompositor.create_subsurface(parent.clone(), queue_handle),
630                0,
631                HEADER_SIZE,
632                (0, -(HEADER_SIZE as i32)),
633            ),
634            // Top border.
635            FramePart::new(
636                subcompositor.create_subsurface(parent.clone(), queue_handle),
637                0,
638                BORDER_SIZE,
639                (-(BORDER_SIZE as i32), -(HEADER_SIZE as i32 + BORDER_SIZE as i32)),
640            ),
641            // Right border.
642            FramePart::new(
643                subcompositor.create_subsurface(parent.clone(), queue_handle),
644                BORDER_SIZE,
645                0,
646                (0, -(HEADER_SIZE as i32)),
647            ),
648            // Bottom border.
649            FramePart::new(
650                subcompositor.create_subsurface(parent.clone(), queue_handle),
651                0,
652                BORDER_SIZE,
653                (-(BORDER_SIZE as i32), 0),
654            ),
655            // Left border.
656            FramePart::new(
657                subcompositor.create_subsurface(parent.clone(), queue_handle),
658                BORDER_SIZE,
659                0,
660                (-(BORDER_SIZE as i32), -(HEADER_SIZE as i32)),
661            ),
662        ];
663
664        Self { parts }
665    }
666}
667
668#[derive(Debug)]
669struct FramePart {
670    /// The surface used for the frame part.
671    subsurface: WlSubsurface,
672
673    /// The surface used for this part.
674    surface: WlSurface,
675
676    /// The width of the Frame part in logical pixels.
677    width: u32,
678
679    /// The height of the Frame part in logical pixels.
680    height: u32,
681
682    /// The position for the subsurface.
683    pos: (i32, i32),
684}
685
686impl FramePart {
687    fn new(surfaces: (WlSubsurface, WlSurface), width: u32, height: u32, pos: (i32, i32)) -> Self {
688        let (subsurface, surface) = surfaces;
689        // XXX sync subsurfaces with the main surface.
690        subsurface.set_sync();
691        Self { surface, subsurface, width, height, pos }
692    }
693}
694
695impl Drop for FramePart {
696    fn drop(&mut self) {
697        self.subsurface.destroy();
698        self.surface.destroy();
699    }
700}
701
702/// The location inside the
703#[derive(Debug, Copy, Clone, PartialEq, Eq)]
704enum Location {
705    /// The location doesn't belong to the frame.
706    None,
707    /// Header bar.
708    Head,
709    /// Top border.
710    Top,
711    /// Top right corner.
712    TopRight,
713    /// Right border.
714    Right,
715    /// Bottom right corner.
716    BottomRight,
717    /// Bottom border.
718    Bottom,
719    /// Bottom left corner.
720    BottomLeft,
721    /// Left border.
722    Left,
723    /// Top left corner.
724    TopLeft,
725    /// One of the buttons.
726    Button(UIButton),
727}
728
729/// The frame button.
730#[derive(Debug, Copy, Clone, PartialEq, Eq)]
731enum UIButton {
732    /// The minimize button, the left most.
733    Minimize,
734    /// The maximize button, in the middle.
735    Maximize,
736    /// The close botton, the right most.
737    Close,
738}