diff options
Diffstat (limited to 'matchbox/theme-engines/mb-wm-theme-cairo.c')
-rw-r--r-- | matchbox/theme-engines/mb-wm-theme-cairo.c | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/matchbox/theme-engines/mb-wm-theme-cairo.c b/matchbox/theme-engines/mb-wm-theme-cairo.c new file mode 100644 index 0000000..b045e89 --- /dev/null +++ b/matchbox/theme-engines/mb-wm-theme-cairo.c @@ -0,0 +1,829 @@ +#include "mb-wm-theme-cairo.h" +#include "mb-wm-theme-xml.h" + +#include <math.h> + +#ifdef USE_GTK +#ifndef GTK_DISABLE_DEPRECATED +#define GTK_DISABLE_DEPRECATED +#endif +#include <gtk/gtk.h> +#include <gtk/gtkwindow.h> +#include <gdk/gdkx.h> +#endif + +#include <cairo.h> +#include <cairo-xlib.h> + +#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 1, 0) +#define CAIRO_EXTEND_PAD CAIRO_EXTEND_NONE +#endif + +#include <pango/pango-context.h> +#include <pango/pangocairo.h> + +#define FRAME_TITLEBAR_HEIGHT 20 +#define FRAME_EDGE_SIZE 3 + +/* + * Forward declarations + */ +static void +mb_wm_theme_cairo_paint_decor (MBWMTheme *theme, MBWMDecor *decor); +static void +mb_wm_theme_cairo_paint_button (MBWMTheme *theme, MBWMDecorButton *button); +static void +mb_wm_theme_cairo_get_decor_dimensions (MBWMTheme *, MBWindowManagerClient *, + int *, int *, int *, int *); +static void +mb_wm_theme_cairo_get_button_size (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int *, int *); + +static void +mb_wm_theme_cairo_get_button_position (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int*, int*); + +static MBWMDecor * +mb_wm_theme_cairo_create_decor (MBWMTheme *, MBWindowManagerClient *, + MBWMDecorType); + +/*******************************************************************/ + +static void +mb_wm_theme_cairo_class_init (MBWMObjectClass *klass) +{ + MBWMThemeClass *t_class = MB_WM_THEME_CLASS (klass); + + t_class->paint_decor = mb_wm_theme_cairo_paint_decor; + t_class->paint_button = mb_wm_theme_cairo_paint_button; + t_class->decor_dimensions = mb_wm_theme_cairo_get_decor_dimensions; + t_class->button_size = mb_wm_theme_cairo_get_button_size; + t_class->button_position = mb_wm_theme_cairo_get_button_position; + t_class->create_decor = mb_wm_theme_cairo_create_decor; + +#ifdef MBWM_WANT_DEBUG + klass->klass_name = "MBWMThemeCairo"; +#endif +} + +static void +mb_wm_theme_cairo_destroy (MBWMObject *obj) +{ +} + +static int +mb_wm_theme_cairo_init (MBWMObject *obj, va_list vap) +{ + MBWMTheme *theme = MB_WM_THEME (obj); + +#ifdef USE_GTK + GtkWidget *gwin; + /* + * Plan here is to just get the GTK settings so we can follow + * colors set by widgets. + */ + + gtk_init (NULL, NULL); + + gwin = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_ensure_style (gwin); +#endif + + /* + * We do not support shaped windows, so reset the flag to avoid unnecessary + * ops if the xml theme set this + */ + theme->shaped = False; + + return 1; +} + + +int +mb_wm_theme_cairo_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMThemeCairoClass), + sizeof (MBWMThemeCairo), + mb_wm_theme_cairo_init, + mb_wm_theme_cairo_destroy, + mb_wm_theme_cairo_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_THEME, 0); + } + + return type; +} + +static void +construct_buttons (MBWMThemeCairo * theme, MBWMDecor * decor, MBWMXmlDecor *d) +{ + MBWindowManagerClient *client = decor->parent_client; + MBWindowManager *wm = client->wmref; + MBWMDecorButton *button; + + if (d) + { + MBWMList * l = d->buttons; + while (l) + { + MBWMXmlButton * b = l->data; + + button = mb_wm_decor_button_stock_new (wm, + b->type, + b->packing, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + l = l->next; + } + + return; + } + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonClose, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + +#if 0 + /* + * We probably do not want this in the default client, but for now + * it is useful for testing purposes + */ + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonFullscreen, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonHelp, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonAccept, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMinimize, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMenu, + MBWMDecorButtonPackStart, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); +#endif +} + +struct DecorData +{ + Pixmap xpix; + cairo_surface_t *surface; +}; + +static void +decordata_free (MBWMDecor * decor, void *data) +{ + struct DecorData * dd = data; + Display * xdpy = decor->parent_client->wmref->xdpy; + + XFreePixmap (xdpy, dd->xpix); + cairo_surface_destroy (dd->surface); + free (dd); +} + +static struct DecorData * +decordata_new () +{ + return mb_wm_util_malloc0 (sizeof (struct DecorData)); +} + +static MBWMDecor * +mb_wm_theme_cairo_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMDecor *decor = NULL; + MBWindowManager *wm = client->wmref; + MBWMXmlClient *c; + struct DecorData *dd; + + if (MB_WM_THEME (theme)->xml_clients && + (c = mb_wm_xml_client_find_by_type (MB_WM_THEME (theme)->xml_clients, + c_type))) + { + MBWMXmlDecor *d; + + d = mb_wm_xml_decor_find_by_type (c->decors, type); + + if (d) + { + dd = decordata_new (); + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + construct_buttons (MB_WM_THEME_CAIRO (theme), decor, d); + } + + return decor; + } + + switch (c_type) + { + case MBWMClientTypeApp: + switch (type) + { + case MBWMDecorTypeNorth: + dd = decordata_new (); + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + construct_buttons (MB_WM_THEME_CAIRO (theme), decor, NULL); + break; + default: + dd = decordata_new (); + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + } + break; + + case MBWMClientTypeDialog: + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + dd = decordata_new (); + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + } + + return decor; +} + +static void +mb_wm_theme_cairo_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMThemeCairo *c_theme = MB_WM_THEME_CAIRO (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (width) + *width = b->width; + + if (height) + *height = b->height; + + return; + } + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeApp: + case MBWMClientTypeDialog: + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (width) + *width = FRAME_TITLEBAR_HEIGHT-2; + + if (height) + *height = FRAME_TITLEBAR_HEIGHT-2; + } +} + +static void +mb_wm_theme_cairo_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMThemeCairo *c_theme = MB_WM_THEME_CAIRO (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (x) + if (b->x >= 0) + *x = b->x; + else + *x = 2; + + if (y) + if (b->y >= 0) + *y = b->y; + else + *y = 2; + + return; + } + } + + if (x) + *x = 2; + + if (y) + *y = 2; +} + +static void +mb_wm_theme_cairo_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMThemeCairo *c_theme = MB_WM_THEME_CAIRO (theme); + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + if (north) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeNorth))) + *north = d->height; + else + *north = FRAME_TITLEBAR_HEIGHT; + + if (south) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeSouth))) + *south = d->height; + else + *south = FRAME_EDGE_SIZE; + + if (west) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeWest))) + *west = d->width; + else + *west = FRAME_EDGE_SIZE; + + if (east) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeEast))) + *east = d->width; + else + *east = FRAME_EDGE_SIZE; + + return; + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeApp: + if (north) + *north = FRAME_TITLEBAR_HEIGHT; + + if (south) + *south = FRAME_EDGE_SIZE; + + if (west) + *west = FRAME_EDGE_SIZE; + + if (east) + *east = FRAME_EDGE_SIZE; + break; + + case MBWMClientTypeDialog: + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (north) + *north = 0; + + if (south) + *south = 0; + + if (west) + *west = 0; + + if (east) + *east = 0; + } +} + +static void +mb_wm_theme_cairo_paint_decor (MBWMTheme *theme, + MBWMDecor *decor) +{ + MBWMDecorType type; + const MBGeometry *geom; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + cairo_t *cr; + double x, y, w, h; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMColor clr_frame; + MBWMColor clr_bg2; + MBWMClientType c_type; + MBWMThemeCairo *c_theme = MB_WM_THEME_CAIRO (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + int font_size = 0; + const char *font_family = "Sans Serif"; + struct DecorData *dd; + + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.2; + clr_bg.g = 0.2; + clr_bg.b = 0.2; + + clr_bg2.r = 0.5; + clr_bg2.g = 0.5; + clr_bg2.b = 0.5; + + clr_frame.r = 0.0; + clr_frame.g = 0.0; + clr_frame.b = 0.0; + + client = mb_wm_decor_get_parent (decor); + xwin = mb_wm_decor_get_x_window (decor); + + if (client == NULL || xwin == None) + return; + + dd = mb_wm_decor_get_theme_data (decor); + + type = mb_wm_decor_get_type (decor); + geom = mb_wm_decor_get_geometry (decor); + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + if (d->clr_fg.set) + { + clr_fg.r = d->clr_fg.r; + clr_fg.g = d->clr_fg.g; + clr_fg.b = d->clr_fg.b; + } + + if (d->clr_bg.set) + { + clr_bg.r = d->clr_bg.r; + clr_bg.g = d->clr_bg.g; + clr_bg.b = d->clr_bg.b; + } + + if (d->clr_bg2.set) + { + clr_bg2.r = d->clr_bg2.r; + clr_bg2.g = d->clr_bg2.g; + clr_bg2.b = d->clr_bg2.b; + } + + if (d->clr_frame.set) + { + clr_frame.r = d->clr_frame.r; + clr_frame.g = d->clr_frame.g; + clr_frame.b = d->clr_frame.b; + } + + if (d->font_size) + font_size = d->font_size; + + if (d->font_family) + font_family = d->font_family; + } + + if (dd->xpix == None) + dd->xpix = XCreatePixmap (wm->xdpy, xwin, geom->width, geom->height, + DefaultDepth(wm->xdpy, wm->xscreen)); + + if (!dd->surface) + dd->surface = cairo_xlib_surface_create (wm->xdpy, + dd->xpix, + DefaultVisual(wm->xdpy, wm->xscreen), + geom->width, + geom->height); + + cr = cairo_create (dd->surface); + + cairo_set_line_width (cr, 1.0); + + + w = geom->width; h = geom->height; x = geom->x; y = geom->y; + + cairo_set_line_width (cr, 0.04); + + cairo_set_source_rgb (cr, clr_frame.r, clr_frame.g, clr_frame.b); + + cairo_rectangle( cr, 0, 0, w, h); + + cairo_fill (cr); + + cairo_set_source_rgb (cr, clr_bg.r, clr_bg.g, clr_bg.b); + + cairo_rectangle( cr, 1, 1, w-2, h-2); + + + cairo_fill (cr); + + if (mb_wm_decor_get_type(decor) == MBWMDecorTypeNorth) + { + cairo_font_extents_t font_extents; + int pack_start_x = mb_wm_decor_get_pack_start_x (decor); + + pattern = cairo_pattern_create_linear (0, 0, 0, h); + + cairo_pattern_add_color_stop_rgb (pattern, 0.0, + clr_bg2.r, clr_bg2.g, clr_bg2.b); + cairo_pattern_add_color_stop_rgb (pattern, 0.45, + clr_bg.r, clr_bg.g, clr_bg.b); + cairo_pattern_add_color_stop_rgb (pattern, 0.55, + clr_bg.r, clr_bg.g, clr_bg.b); + cairo_pattern_add_color_stop_rgb (pattern, 1.0, + clr_bg2.r, clr_bg2.g, clr_bg2.b); + + cairo_set_source (cr, pattern); + + cairo_rectangle( cr, 2, 2, w-4, h-3); + + cairo_fill (cr); + + cairo_set_source_rgb (cr, clr_fg.r, clr_fg.g, clr_fg.b); + + cairo_select_font_face (cr, + font_family, + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + + if (font_size) + cairo_set_font_size (cr, font_size); + else + cairo_set_font_size (cr, h - (h/6)); + + cairo_font_extents (cr, &font_extents); + + cairo_move_to (cr, + mb_wm_client_frame_west_width (client) + pack_start_x, + (h - (font_extents.ascent + font_extents.descent)) / 2 + + font_extents.ascent); + + cairo_show_text (cr, mb_wm_client_get_name(client)); + + cairo_pattern_destroy (pattern); + } + + cairo_set_source_rgb (cr, clr_bg.r, clr_bg.g, clr_bg.b); + + /* tweak borders a little so they all 'fit' */ + + switch (type) + { + case MBWMDecorTypeNorth: + cairo_rectangle( cr, 1, h-1, 1, 1); + cairo_rectangle( cr, w-2, h-1, 1, 1); + break; + case MBWMDecorTypeSouth: + cairo_rectangle( cr, 1, 0, 1, 1); + cairo_rectangle( cr, w-2, 0, 1, 1); + break; + case MBWMDecorTypeWest: + case MBWMDecorTypeEast: + cairo_rectangle( cr, 1, 0, 1, 1); + cairo_rectangle( cr, 1, h-1, 1, 1); + break; + default: + break; + } + + cairo_fill (cr); + + cairo_destroy (cr); + + XSetWindowBackgroundPixmap (wm->xdpy, xwin, dd->xpix); + XClearWindow (wm->xdpy, xwin); +} + +static void +mb_wm_theme_cairo_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMDecor *decor; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + cairo_t *cr; + int xi, yi, wi, hi; + double x, y, w, h; + cairo_font_extents_t font_extents; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMClientType c_type; + MBWMThemeCairo *c_theme = MB_WM_THEME_CAIRO (theme); + MBWMXmlClient *c = NULL; + MBWMXmlDecor *d = NULL; + MBWMXmlButton *b = NULL; + struct DecorData * dd; + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.0; + clr_bg.g = 0.0; + clr_bg.b = 0.0; + + decor = button->decor; + client = mb_wm_decor_get_parent (decor); + xwin = decor->xwin; + dd = mb_wm_decor_get_theme_data (decor); + + if (client == NULL || xwin == None || dd->xpix == None || !dd->surface) + return; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, button->type))) + { + clr_fg.r = b->clr_fg.r; + clr_fg.g = b->clr_fg.g; + clr_fg.b = b->clr_fg.b; + + clr_bg.r = b->clr_bg.r; + clr_bg.g = b->clr_bg.g; + clr_bg.b = b->clr_bg.b; + } + + wi = button->geom.width; + hi = button->geom.height; + xi = button->geom.x; + yi = button->geom.y; + + w = wi; + h = hi; + x = xi; + y = yi; + + cr = cairo_create (dd->surface); + + cairo_set_line_width (cr, 0.04); + cairo_rectangle( cr, x, y, w, h); + + if (button->state == MBWMDecorButtonStateInactive) + { + cairo_set_source_rgb (cr, clr_bg.r, clr_bg.g, clr_bg.b); + } + else + { + if (d && d->clr_bg.set) + { + cairo_set_source_rgba (cr, d->clr_bg.r, d->clr_bg.g, d->clr_bg.b, + 0.25); + } + else + { + cairo_set_source_rgba (cr, 0.7, 0.7, 0.7, 0.25); + } + + cairo_fill (cr); + cairo_set_source_rgba (cr, clr_bg.r, clr_bg.g, clr_bg.b, 0.5); + } + + cairo_fill (cr); + + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr, 3.0); + cairo_set_source_rgb (cr, clr_fg.r, clr_fg.g, clr_fg.b); + + if (button->type == MBWMDecorButtonClose) + { + cairo_move_to (cr, x + 3.0, y + 3.0); + cairo_line_to (cr, x + w-3.0, y + h-3.0); + cairo_stroke (cr); + + cairo_move_to (cr, x + 3.0, y + h-3.0); + cairo_line_to (cr, x + w-3.0, y + 3.0); + cairo_stroke (cr); + } + else if (button->type == MBWMDecorButtonFullscreen) + { + cairo_move_to (cr, x + 3.0, y + 3.0); + cairo_line_to (cr, x + 3.0, y + h-3.0); + cairo_line_to (cr, x + w-3.0, y + h-3.0); + cairo_line_to (cr, x + w-3.0, y + 3.0); + cairo_line_to (cr, x + 3.0, y + 3.0); + cairo_stroke (cr); + } + else if (button->type == MBWMDecorButtonMinimize) + { + cairo_move_to (cr, x + 3.0, y + h-5.0); + cairo_line_to (cr, x + w-3.0, y + h-5.0); + cairo_stroke (cr); + } + else if (button->type == MBWMDecorButtonHelp) + { + cairo_select_font_face (cr, + "Sans Serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + + cairo_set_font_size (cr, h); + cairo_font_extents (cr, &font_extents); + + + cairo_move_to (cr, + x + 4.0, + y + (h - (font_extents.ascent + font_extents.descent)) / 2 + + font_extents.ascent); + + cairo_show_text (cr, "?"); + } + else if (button->type == MBWMDecorButtonMenu) + { + cairo_move_to (cr, x + 3.0, y + 5.0); + cairo_line_to (cr, x + w/2.0, y +h-7.0); + cairo_stroke (cr); + + cairo_move_to (cr, x + w/2.0, y + h-7.0); + cairo_line_to (cr, x + w-3.0, y + 5.0); + cairo_stroke (cr); + } + else if (button->type == MBWMDecorButtonAccept) + { + cairo_arc (cr, x + w/2.0, y + h/2.0, (w-8.0)/2.0, 0.0, 2.0 * M_PI); + cairo_stroke (cr); + } + + cairo_destroy (cr); + + XClearWindow (wm->xdpy, xwin); +} |