Add decoration frame support to cairo-util
authorJason Ekstrand <jason@jlekstrand.net>
Mon, 14 Oct 2013 00:08:39 +0000 (19:08 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Mon, 14 Oct 2013 05:12:16 +0000 (22:12 -0700)
Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
shared/Makefile.am
shared/cairo-util.h
shared/frame.c [new file with mode: 0644]

index 2fcff7b..31fab5f 100644 (file)
@@ -10,6 +10,7 @@ libshared_la_SOURCES =                                \
        os-compatibility.h
 
 libshared_cairo_la_CFLAGS =                    \
+       -DDATADIR='"$(datadir)"'                \
        $(GCC_CFLAGS)                           \
        $(COMPOSITOR_CFLAGS)                    \
        $(PIXMAN_CFLAGS)                        \
@@ -29,4 +30,5 @@ libshared_cairo_la_SOURCES =                  \
        image-loader.c                          \
        image-loader.h                          \
        cairo-util.c                            \
+       frame.c                                 \
        cairo-util.h
index 7b40394..da1cef9 100644 (file)
@@ -23,6 +23,7 @@
 #ifndef _CAIRO_UTIL_H
 #define _CAIRO_UTIL_H
 
+#include <stdint.h>
 #include <cairo.h>
 
 void
@@ -86,4 +87,114 @@ enum theme_location {
 enum theme_location
 theme_get_location(struct theme *t, int x, int y, int width, int height, int flags);
 
+struct frame;
+
+enum frame_status {
+       FRAME_STATUS_NONE = 0,
+       FRAME_STATUS_REPAINT = 0x1,
+       FRAME_STATUS_MINIMIZE = 0x2,
+       FRAME_STATUS_MAXIMIZE = 0x4,
+       FRAME_STATUS_CLOSE = 0x8,
+       FRAME_STATUS_MENU = 0x10,
+       FRAME_STATUS_RESIZE = 0x20,
+       FRAME_STATUS_MOVE = 0x40,
+       FRAME_STATUS_ALL = 0x7f
+};
+
+enum frame_flag {
+       FRAME_FLAG_ACTIVE = 0x1,
+       FRAME_FLAG_MAXIMIZED = 0x2
+};
+
+enum {
+       FRAME_BUTTON_NONE = 0,
+       FRAME_BUTTON_CLOSE = 0x1,
+       FRAME_BUTTON_MAXIMIZE = 0x2,
+       FRAME_BUTTON_MINIMIZE = 0x4,
+       FRAME_BUTTON_ALL = 0x7
+};
+
+enum frame_button_state {
+       FRAME_BUTTON_RELEASED = 0,
+       FRAME_BUTTON_PRESSED = 1
+};
+
+struct frame *
+frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons,
+            const char *title);
+
+void
+frame_destroy(struct frame *frame);
+
+/* May set FRAME_STATUS_REPAINT */
+int
+frame_set_title(struct frame *frame, const char *title);
+
+/* May set FRAME_STATUS_REPAINT */
+void
+frame_set_flag(struct frame *frame, enum frame_flag flag);
+
+/* May set FRAME_STATUS_REPAINT */
+void
+frame_unset_flag(struct frame *frame, enum frame_flag flag);
+
+/* May set FRAME_STATUS_REPAINT */
+void
+frame_resize(struct frame *frame, int32_t width, int32_t height);
+
+/* May set FRAME_STATUS_REPAINT */
+void
+frame_resize_inside(struct frame *frame, int32_t width, int32_t height);
+
+int32_t
+frame_width(struct frame *frame);
+
+int32_t
+frame_height(struct frame *frame);
+
+void
+frame_interior(struct frame *frame, int32_t *x, int32_t *y,
+              int32_t *width, int32_t *height);
+void
+frame_input_rect(struct frame *frame, int32_t *x, int32_t *y,
+                int32_t *width, int32_t *height);
+void
+frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y,
+                 int32_t *width, int32_t *height);
+
+uint32_t
+frame_status(struct frame *frame);
+
+void
+frame_status_clear(struct frame *frame, enum frame_status status);
+
+/* May set FRAME_STATUS_REPAINT */
+enum theme_location
+frame_pointer_enter(struct frame *frame, void *pointer, int x, int y);
+
+/* May set FRAME_STATUS_REPAINT */
+enum theme_location
+frame_pointer_motion(struct frame *frame, void *pointer, int x, int y);
+
+/* May set FRAME_STATUS_REPAINT */
+void
+frame_pointer_leave(struct frame *frame, void *pointer);
+
+/* May set:
+ *     FRAME_STATUS_MINIMIZE
+ *     FRAME_STATUS_MAXIMIZE
+ *     FRAME_STATUS_CLOSE
+ *     FRAME_STATUS_MENU
+ *     FRAME_STATUS_RESIZE
+ *     FRAME_STATUS_MOVE
+ */
+enum theme_location
+frame_pointer_button(struct frame *frame, void *pointer,
+                    uint32_t button, enum frame_button_state state);
+
+/* TODO: Add Touch */
+
+void
+frame_repaint(struct frame *frame, cairo_t *cr);
+
 #endif
diff --git a/shared/frame.c b/shared/frame.c
new file mode 100644 (file)
index 0000000..508870c
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2012-2013 Collabora, Ltd.
+ * Copyright © 2013 Jason Ekstrand
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-util.h>
+#include <linux/input.h>
+
+#include "cairo-util.h"
+
+enum frame_button_flags {
+       FRAME_BUTTON_ALIGN_RIGHT = 0x1,
+       FRAME_BUTTON_DECORATED = 0x2,
+       FRAME_BUTTON_CLICK_DOWN = 0x4,
+};
+
+struct frame_button {
+       struct frame *frame;
+       struct wl_list link;    /* buttons_list */
+
+       cairo_surface_t *icon;
+       enum frame_button_flags flags;
+       int hover_count;
+       int press_count;
+
+       struct {
+               int x, y;
+               int width, height;
+       } allocation;
+
+       enum frame_status status_effect;
+};
+
+struct frame_pointer {
+       struct wl_list link;
+       void *data;
+
+       int x, y;
+
+       struct frame_button *hover_button;
+       int active;
+};
+
+struct frame {
+       int32_t width, height;
+       char *title;
+       uint32_t flags;
+       struct theme *theme;
+
+       struct {
+               int32_t x, y;
+               int32_t width, height;
+       } interior;
+       int shadow_margin;
+       int opaque_margin;
+       int geometry_dirty;
+
+       uint32_t status;
+
+       struct wl_list buttons;
+       struct wl_list pointers;
+};
+
+static struct frame_button *
+frame_button_create(struct frame *frame, const char *icon,
+                   enum frame_status status_effect,
+                   enum frame_button_flags flags)
+{
+       struct frame_button *button;
+
+       button = calloc(1, sizeof *button);
+       if (!button)
+               return NULL;
+
+       button->icon = cairo_image_surface_create_from_png(icon);
+       if (!button->icon) {
+               free(button);
+               return NULL;
+       }
+
+       button->frame = frame;
+       button->flags = flags;
+       button->status_effect = status_effect;
+
+       wl_list_insert(frame->buttons.prev, &button->link);
+
+       return button;
+}
+
+static void
+frame_button_destroy(struct frame_button *button)
+{
+       cairo_surface_destroy(button->icon);
+       free(button);
+}
+
+static void
+frame_button_enter(struct frame_button *button)
+{
+       if (!button->hover_count)
+               button->frame->status |= FRAME_STATUS_REPAINT;
+       button->hover_count++;
+}
+
+static void
+frame_button_leave(struct frame_button *button, struct frame_pointer *pointer)
+{
+       button->hover_count--;
+       if (!button->hover_count)
+               button->frame->status |= FRAME_STATUS_REPAINT;
+
+       /* In this case, we won't get a release */
+       if (pointer->active)
+               button->press_count--;
+}
+
+static void
+frame_button_press(struct frame_button *button)
+{
+       if (!button->press_count)
+               button->frame->status |= FRAME_STATUS_REPAINT;
+       button->press_count++;
+
+       if (button->flags & FRAME_BUTTON_CLICK_DOWN)
+               button->frame->status |= button->status_effect;
+}
+
+static void
+frame_button_release(struct frame_button *button)
+{
+       button->press_count--;
+       if (!button->press_count)
+               button->frame->status |= FRAME_STATUS_REPAINT;
+
+       if (!(button->flags & FRAME_BUTTON_CLICK_DOWN))
+               button->frame->status |= button->status_effect;
+}
+
+static void
+frame_button_repaint(struct frame_button *button, cairo_t *cr)
+{
+       int x, y;
+
+       if (!button->allocation.width)
+               return;
+       if (!button->allocation.height)
+               return;
+
+       x = button->allocation.x;
+       y = button->allocation.y;
+
+       cairo_save(cr);
+
+       if (button->flags & FRAME_BUTTON_DECORATED) {
+               cairo_set_line_width(cr, 1);
+
+               cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+               cairo_rectangle(cr, x, y, 25, 16);
+
+               cairo_stroke_preserve(cr);
+
+               if (button->press_count) {
+                       cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+               } else if (button->hover_count) {
+                       cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+               } else {
+                       cairo_set_source_rgb(cr, 0.88, 0.88, 0.88);
+               }
+
+               cairo_fill (cr);
+
+               x += 4;
+       }
+
+       cairo_set_source_surface(cr, button->icon, x, y);
+       cairo_paint(cr);
+
+       cairo_restore(cr);
+}
+
+static struct frame_pointer *
+frame_pointer_get(struct frame *frame, void *data)
+{
+       struct frame_pointer *pointer;
+
+       wl_list_for_each(pointer, &frame->pointers, link)
+               if (pointer->data == data)
+                       return pointer;
+
+       pointer = calloc(1, sizeof *pointer);
+       if (!pointer)
+               return NULL;
+
+       pointer->data = data;
+       wl_list_insert(&frame->pointers, &pointer->link);
+
+       return pointer;
+}
+
+static void
+frame_pointer_destroy(struct frame_pointer *pointer)
+{
+       wl_list_remove(&pointer->link);
+       free(pointer);
+}
+
+struct frame *
+frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons,
+            const char *title)
+{
+       struct frame *frame;
+       struct frame_button *button;
+
+       frame = calloc(1, sizeof *frame);
+       if (!frame)
+               return NULL;
+
+       frame->width = width;
+       frame->height = height;
+       frame->flags = 0;
+       frame->theme = t;
+       frame->status = FRAME_STATUS_REPAINT;
+       frame->geometry_dirty = 1;
+
+       if (title) {
+               frame->title = strdup(title);
+               if (!frame->title)
+                       goto free_frame;
+       }
+
+       wl_list_init(&frame->buttons);
+       wl_list_init(&frame->pointers);
+
+       button = frame_button_create(frame, DATADIR "/weston/icon_window.png",
+                                    FRAME_STATUS_MENU,
+                                    FRAME_BUTTON_CLICK_DOWN);
+       if (!button)
+               goto free_frame;
+
+       if (buttons & FRAME_BUTTON_CLOSE) {
+               button = frame_button_create(frame,
+                                            DATADIR "/weston/sign_close.png",
+                                            FRAME_STATUS_CLOSE,
+                                            FRAME_BUTTON_ALIGN_RIGHT |
+                                            FRAME_BUTTON_DECORATED);
+               if (!button)
+                       goto free_frame;
+       }
+
+       if (buttons & FRAME_BUTTON_MAXIMIZE) {
+               button = frame_button_create(frame,
+                                            DATADIR "/weston/sign_maximize.png",
+                                            FRAME_STATUS_MAXIMIZE,
+                                            FRAME_BUTTON_ALIGN_RIGHT |
+                                            FRAME_BUTTON_DECORATED);
+               if (!button)
+                       goto free_frame;
+       }
+
+       if (buttons & FRAME_BUTTON_MINIMIZE) {
+               button = frame_button_create(frame,
+                                            DATADIR "/weston/sign_minimize.png",
+                                            FRAME_STATUS_MINIMIZE,
+                                            FRAME_BUTTON_ALIGN_RIGHT |
+                                            FRAME_BUTTON_DECORATED);
+               if (!button)
+                       goto free_frame;
+       }
+
+       return frame;
+
+free_frame:
+       free(frame->title);
+       free(frame);
+       return NULL;
+}
+
+void
+frame_destroy(struct frame *frame)
+{
+       struct frame_button *button, *next;
+
+       wl_list_for_each_safe(button, next, &frame->buttons, link)
+               frame_button_destroy(button);
+
+       free(frame->title);
+       free(frame);
+}
+
+int
+frame_set_title(struct frame *frame, const char *title)
+{
+       char *dup = NULL;
+
+       if (title) {
+               dup = strdup(title);
+               if (!dup)
+                       return -1;
+       }
+
+       free(frame->title);
+       frame->title = dup;
+
+       frame->status |= FRAME_STATUS_REPAINT;
+
+       return 0;
+}
+
+void
+frame_set_flag(struct frame *frame, enum frame_flag flag)
+{
+       if (flag & FRAME_FLAG_MAXIMIZED && !(frame->flags & FRAME_FLAG_MAXIMIZED))
+               frame->geometry_dirty = 1;
+
+       frame->flags |= flag;
+       frame->status |= FRAME_STATUS_REPAINT;
+}
+
+void
+frame_unset_flag(struct frame *frame, enum frame_flag flag)
+{
+       if (flag & FRAME_FLAG_MAXIMIZED && frame->flags & FRAME_FLAG_MAXIMIZED)
+               frame->geometry_dirty = 1;
+
+       frame->flags &= ~flag;
+       frame->status |= FRAME_STATUS_REPAINT;
+}
+
+void
+frame_resize(struct frame *frame, int32_t width, int32_t height)
+{
+       frame->width = width;
+       frame->height = height;
+
+       frame->geometry_dirty = 1;
+       frame->status |= FRAME_STATUS_REPAINT;
+}
+
+void
+frame_resize_inside(struct frame *frame, int32_t width, int32_t height)
+{
+       struct theme *t = frame->theme;
+       int decoration_width, decoration_height;
+
+       if (frame->flags & FRAME_FLAG_MAXIMIZED) {
+               decoration_width = t->width * 2;
+               decoration_height = t->width + t->titlebar_height;
+       } else {
+               decoration_width = (t->width + t->margin) * 2;
+               decoration_height = t->width +
+                       t->titlebar_height + t->margin * 2;
+       }
+
+       frame_resize(frame, width + decoration_width,
+                    height + decoration_height);
+}
+
+int32_t
+frame_width(struct frame *frame)
+{
+       return frame->width;
+}
+
+int32_t
+frame_height(struct frame *frame)
+{
+       return frame->height;
+}
+
+static void
+frame_refresh_geometry(struct frame *frame)
+{
+       struct frame_button *button;
+       struct theme *t = frame->theme;
+       int x_l, x_r, y, w, h;
+       int32_t decoration_width, decoration_height;
+
+       if (!frame->geometry_dirty)
+               return;
+
+       if (frame->flags & FRAME_FLAG_MAXIMIZED) {
+               decoration_width = t->width * 2;
+               decoration_height = t->width + t->titlebar_height;
+
+               frame->interior.x = t->width;
+               frame->interior.y = t->titlebar_height;
+               frame->interior.width = frame->width - decoration_width;
+               frame->interior.height = frame->height - decoration_height;
+
+               frame->opaque_margin = 0;
+               frame->shadow_margin = 0;
+       } else {
+               decoration_width = (t->width + t->margin) * 2;
+               decoration_height = t->width +
+                       t->titlebar_height + t->margin * 2;
+
+               frame->interior.x = t->width + t->margin;
+               frame->interior.y = t->titlebar_height + t->margin;
+               frame->interior.width = frame->width - decoration_width;
+               frame->interior.height = frame->height - decoration_height;
+
+               frame->opaque_margin = t->margin + t->frame_radius;
+               frame->shadow_margin = t->margin;
+       }
+
+       x_r = frame->width - t->width - frame->shadow_margin;
+       x_l = t->width + frame->shadow_margin;
+       y = t->width + frame->shadow_margin;
+       wl_list_for_each(button, &frame->buttons, link) {
+               const int button_padding = 4;
+               w = cairo_image_surface_get_width(button->icon);
+               h = cairo_image_surface_get_height(button->icon);
+
+               if (button->flags & FRAME_BUTTON_DECORATED)
+                       w += 10;
+
+               if (button->flags & FRAME_BUTTON_ALIGN_RIGHT) {
+                       x_r -= w;
+
+                       button->allocation.x = x_r;
+                       button->allocation.y = y;
+                       button->allocation.width = w + 1;
+                       button->allocation.height = h + 1;
+
+                       x_r -= button_padding;
+               } else {
+                       button->allocation.x = x_l;
+                       button->allocation.y = y;
+                       button->allocation.width = w + 1;
+                       button->allocation.height = h + 1;
+
+                       x_l += w;
+                       x_l += button_padding;
+               }
+       }
+
+       frame->geometry_dirty = 0;
+}
+
+void
+frame_interior(struct frame *frame, int32_t *x, int32_t *y,
+               int32_t *width, int32_t *height)
+{
+       frame_refresh_geometry(frame);
+
+       if (x)
+               *x = frame->interior.x;
+       if (y)
+               *y = frame->interior.y;
+       if (width)
+               *width = frame->interior.width;
+       if (height)
+               *height = frame->interior.height;
+}
+
+void
+frame_input_rect(struct frame *frame, int32_t *x, int32_t *y,
+                int32_t *width, int32_t *height)
+{
+       frame_refresh_geometry(frame);
+
+       if (x)
+               *x = frame->shadow_margin;
+       if (y)
+               *y = frame->shadow_margin;
+       if (width)
+               *width = frame->width - frame->shadow_margin * 2;
+       if (height)
+               *height = frame->height - frame->shadow_margin * 2;
+}
+
+void
+frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y,
+                 int32_t *width, int32_t *height)
+{
+       frame_refresh_geometry(frame);
+
+       if (x)
+               *x = frame->opaque_margin;
+       if (y)
+               *y = frame->opaque_margin;
+       if (width)
+               *width = frame->width - frame->opaque_margin * 2;
+       if (height)
+               *height = frame->height - frame->opaque_margin * 2;
+}
+
+uint32_t
+frame_status(struct frame *frame)
+{
+       return frame->status;
+}
+
+void
+frame_status_clear(struct frame *frame, enum frame_status status)
+{
+       frame->status &= ~status;
+}
+
+static struct frame_button *
+frame_find_button(struct frame *frame, int x, int y)
+{
+       struct frame_button *button;
+       int rel_x, rel_y;
+
+       wl_list_for_each(button, &frame->buttons, link) {
+               rel_x = x - button->allocation.x;
+               rel_y = y - button->allocation.y;
+
+               if (0 <= rel_x && rel_x < button->allocation.width &&
+                   0 <= rel_y && rel_y < button->allocation.height)
+                       return button;
+       }
+
+       return NULL;
+}
+
+enum theme_location
+frame_pointer_enter(struct frame *frame, void *data, int x, int y)
+{
+       return frame_pointer_motion(frame, data, x, y);
+}
+
+enum theme_location
+frame_pointer_motion(struct frame *frame, void *data, int x, int y)
+{
+       struct frame_pointer *pointer = frame_pointer_get(frame, data);
+       struct frame_button *button = frame_find_button(frame, x, y);
+       enum theme_location location;
+
+       location = theme_get_location(frame->theme, x, y,
+                                     frame->width, frame->height,
+                                     frame->flags & FRAME_FLAG_MAXIMIZED ?
+                                     THEME_FRAME_MAXIMIZED : 0);
+       if (!pointer)
+               return location;
+
+       pointer->x = x;
+       pointer->y = y;
+
+       if (pointer->hover_button == button)
+               return location;
+
+       if (pointer->hover_button)
+               frame_button_leave(pointer->hover_button, pointer);
+
+       /* No drags */
+       pointer->active = 0;
+       pointer->hover_button = button;
+
+       if (pointer->hover_button)
+               frame_button_enter(pointer->hover_button);
+
+       return location;
+}
+
+void
+frame_pointer_leave(struct frame *frame, void *data)
+{
+       struct frame_pointer *pointer = frame_pointer_get(frame, data);
+       if (!pointer)
+               return;
+
+       if (pointer->hover_button)
+               frame_button_leave(pointer->hover_button, pointer);
+
+       frame_pointer_destroy(pointer);
+}
+
+enum theme_location
+frame_pointer_button(struct frame *frame, void *data,
+                    uint32_t button, enum frame_button_state state)
+{
+       struct frame_pointer *pointer = frame_pointer_get(frame, data);
+       enum theme_location location;
+
+       location = theme_get_location(frame->theme, pointer->x, pointer->y,
+                                     frame->width, frame->height,
+                                     frame->flags & FRAME_FLAG_MAXIMIZED ?
+                                     THEME_FRAME_MAXIMIZED : 0);
+
+       if (!pointer)
+               return location;
+
+       if (button == BTN_RIGHT) {
+               if (state == FRAME_BUTTON_PRESSED &&
+                   location == THEME_LOCATION_TITLEBAR)
+                       frame->status |= FRAME_STATUS_MENU;
+
+       } else if (button == BTN_LEFT && state == FRAME_BUTTON_PRESSED) {
+               if (pointer->hover_button) {
+                       pointer->active = 1;
+                       frame_button_press(pointer->hover_button);
+                       return location;
+               } else {
+                       switch (location) {
+                       case THEME_LOCATION_TITLEBAR:
+                               frame->status |= FRAME_STATUS_MOVE;
+                               break;
+                       case THEME_LOCATION_RESIZING_TOP:
+                       case THEME_LOCATION_RESIZING_BOTTOM:
+                       case THEME_LOCATION_RESIZING_LEFT:
+                       case THEME_LOCATION_RESIZING_RIGHT:
+                       case THEME_LOCATION_RESIZING_TOP_LEFT:
+                       case THEME_LOCATION_RESIZING_TOP_RIGHT:
+                       case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
+                       case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
+                               frame->status |= FRAME_STATUS_RESIZE;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       } else if (button == BTN_LEFT && state == FRAME_BUTTON_RELEASED) {
+               if (pointer->hover_button && pointer->active)
+                       frame_button_release(pointer->hover_button);
+
+               pointer->active = 0;
+       }
+
+       return location;
+}
+
+void
+frame_repaint(struct frame *frame, cairo_t *cr)
+{
+       struct frame_button *button;
+       uint32_t flags = 0;
+
+       frame_refresh_geometry(frame);
+
+       if (frame->flags & FRAME_FLAG_MAXIMIZED)
+               flags |= THEME_FRAME_MAXIMIZED;
+
+       if (frame->flags & FRAME_FLAG_ACTIVE)
+               flags |= THEME_FRAME_ACTIVE;
+
+       cairo_save(cr);
+       theme_render_frame(frame->theme, cr, frame->width, frame->height,
+                          frame->title, flags);
+       cairo_restore(cr);
+
+       wl_list_for_each(button, &frame->buttons, link)
+               frame_button_repaint(button, cr);
+
+       frame_status_clear(frame, FRAME_STATUS_REPAINT);
+}