diff --git a/eek/eek-drawing.c b/eek/eek-drawing.c deleted file mode 100644 index 675f6dc7..00000000 --- a/eek/eek-drawing.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2006 Sergey V. Udaltsov - * Copyright (C) 2010 Daiki Ueno - * Copyright (C) 2010 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - - */ - -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "eek-keyboard.h" -#include "eek-key.h" -#include "eek-drawing.h" -#include "eek-keysym.h" - - -void -eek_draw_text_on_layout (PangoLayout *layout, - const gchar *text) -{ - /* pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); */ - pango_layout_set_text (layout, text, -1); -} - -struct _GetFontSizeCallbackData -{ - PangoLayout *layout; - EekKeysymCategory category; - gint minimum_font_size; - gint font_size; -}; -typedef struct _GetFontSizeCallbackData GetFontSizeCallbackData; - -static gint -get_font_size (const gchar *text, - EekBounds *bounds, - PangoLayout *layout) -{ - gdouble scale_x, scale_y; - const PangoFontDescription *base_font_desc; - PangoFontDescription *font_desc; - PangoRectangle logical_rect = { 0, }; - gint font_size; - - layout = pango_layout_copy (layout); - base_font_desc = pango_layout_get_font_description (layout); - font_desc = pango_font_description_copy (base_font_desc); - - font_size = eek_bounds_long_side (bounds) * PANGO_SCALE; - pango_font_description_set_size (font_desc, font_size); - pango_layout_set_font_description (layout, font_desc); - pango_font_description_free (font_desc); - - eek_draw_text_on_layout (layout, text); - pango_layout_get_extents (layout, NULL, &logical_rect); - - scale_x = scale_y = 1.0; - if (logical_rect.width > bounds->width * PANGO_SCALE) - scale_x = bounds->width * PANGO_SCALE / logical_rect.width; - if (logical_rect.height > bounds->height * PANGO_SCALE) - scale_y = bounds->height * PANGO_SCALE / logical_rect.height; - g_object_unref (layout); - return font_size * (scale_x < scale_y ? scale_x : scale_y); -} - -static void -egf_key_callback (EekElement *element, - gpointer user_data) -{ - EekKey *key = EEK_KEY(element); - GetFontSizeCallbackData *data = user_data; - gdouble font_size; - guint keysym; - EekBounds bounds; - gchar *label; - - keysym = eek_key_get_keysym (key); - if (keysym == EEK_INVALID_KEYSYM || - eek_keysym_get_category (keysym) != data->category) - return; - - eek_element_get_bounds (EEK_ELEMENT(key), &bounds); - label = eek_keysym_to_string (keysym); - if (!label) - return; - font_size = get_font_size (label, &bounds, data->layout); - if (font_size < data->font_size && font_size >= data->minimum_font_size) - data->font_size = font_size; - g_free (label); -} - -static void -egf_section_callback (EekElement *element, - gpointer user_data) -{ - eek_container_foreach_child (EEK_CONTAINER(element), - egf_key_callback, - user_data); -} - -static PangoFontDescription * -get_font_for_category (EekKeyboard *keyboard, - EekKeysymCategory category, - PangoLayout *layout, - gdouble minimum_font_size, - gdouble maximum_font_size) -{ - GetFontSizeCallbackData data; - PangoFontDescription *font_desc; - const PangoFontDescription *base_font_desc; - - data.layout = layout; - data.category = category; - data.minimum_font_size = minimum_font_size; - data.font_size = maximum_font_size; - - eek_container_foreach_child (EEK_CONTAINER(keyboard), - egf_section_callback, - &data); - - base_font_desc = pango_layout_get_font_description (layout); - font_desc = pango_font_description_copy (base_font_desc); - pango_font_description_set_size (font_desc, data.font_size); - - return font_desc; -} - -void -eek_get_fonts (EekKeyboard *keyboard, - PangoLayout *layout, - PangoFontDescription **fonts) -{ - EekBounds bounds; - PangoFontDescription *font_desc; - gint font_size; - - /* font for EEK_KEYSYM_CATEGORY_LETTER */ - eek_element_get_bounds (EEK_ELEMENT(keyboard), &bounds); - font_desc = get_font_for_category (keyboard, - EEK_KEYSYM_CATEGORY_LETTER, - layout, - 0, - eek_bounds_long_side (&bounds) * - PANGO_SCALE); - font_size = pango_font_description_get_size (font_desc); - fonts[EEK_KEYSYM_CATEGORY_LETTER] = font_desc; - - /* font for EEK_KEYSYM_CATEGORY_FUNCTION */ - font_desc = get_font_for_category (keyboard, - EEK_KEYSYM_CATEGORY_FUNCTION, - layout, - font_size * 0.3, - font_size); - fonts[EEK_KEYSYM_CATEGORY_FUNCTION] = font_desc; - - /* font for EEK_KEYSYM_CATEGORY_KEYNAME */ - font_desc = get_font_for_category (keyboard, - EEK_KEYSYM_CATEGORY_KEYNAME, - layout, - font_size * 0.3, - font_size); - fonts[EEK_KEYSYM_CATEGORY_KEYNAME] = font_desc; -} - -void -eek_draw_outline (cairo_t *cr, EekOutline *outline) -{ - cairo_pattern_t *pat; - - cairo_set_line_width (cr, 1); - cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); - - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0); - cairo_pattern_add_color_stop_rgba (pat, 1, 0.5, 0.5, 0.5, 1); - cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1); - - cairo_set_source (cr, pat); - - eek_draw_rounded_polygon (cr, - TRUE, - outline->corner_radius, - outline->points, - outline->num_points); - - cairo_pattern_destroy (pat); - - cairo_set_source_rgba (cr, 0.3, 0.3, 0.3, 0.5); - eek_draw_rounded_polygon (cr, - FALSE, - outline->corner_radius, - outline->points, - outline->num_points); -} - -void -eek_draw_key_label (cairo_t *cr, - EekKey *key, - PangoFontDescription **fonts) -{ - guint keysym; - EekKeysymCategory category; - gchar *label; - PangoLayout *layout; - PangoRectangle logical_rect = { 0, }; - EekBounds bounds; - - keysym = eek_key_get_keysym (key); - if (keysym == EEK_INVALID_KEYSYM) - return; - - category = eek_keysym_get_category (keysym); - if (category == EEK_KEYSYM_CATEGORY_UNKNOWN) - return; - - label = eek_keysym_to_string (keysym); - if (!label) - return; - - eek_element_get_bounds (EEK_ELEMENT(key), &bounds); - layout = pango_cairo_create_layout (cr); - pango_layout_set_font_description (layout, fonts[category]); - pango_layout_set_width (layout, PANGO_SCALE * bounds.width); - pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); - pango_layout_set_text (layout, label, -1); - pango_layout_get_extents (layout, NULL, &logical_rect); - cairo_rel_move_to (cr, - (bounds.width - logical_rect.width / PANGO_SCALE) / 2, - (bounds.height - logical_rect.height / PANGO_SCALE) / 2); - pango_cairo_show_layout (cr, layout); - g_free (label); - g_object_unref (layout); -} - -/* - * The functions below are borrowed from - * libgnomekbd/gkbd-keyboard-drawing.c. - * Copyright (C) 2006 Sergey V. Udaltsov - * - * length(), point_line_distance(), normal_form(), inverse(), multiply(), - * intersect(), rounded_corner(), draw_rounded_polygon() - */ - -static gdouble -length (gdouble x, gdouble y) -{ - return sqrt (x * x + y * y); -} - -static gdouble -point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny) -{ - return ax * nx + ay * ny; -} - -static void -normal_form (gdouble ax, gdouble ay, - gdouble bx, gdouble by, - gdouble * nx, gdouble * ny, gdouble * d) -{ - gdouble l; - - *nx = by - ay; - *ny = ax - bx; - - l = length (*nx, *ny); - - *nx /= l; - *ny /= l; - - *d = point_line_distance (ax, ay, *nx, *ny); -} - -static void -inverse (gdouble a, gdouble b, gdouble c, gdouble d, - gdouble * e, gdouble * f, gdouble * g, gdouble * h) -{ - gdouble det; - - det = a * d - b * c; - - *e = d / det; - *f = -b / det; - *g = -c / det; - *h = a / det; -} - -static void -multiply (gdouble a, gdouble b, gdouble c, gdouble d, - gdouble e, gdouble f, gdouble * x, gdouble * y) -{ - *x = a * e + b * f; - *y = c * e + d * f; -} - -static void -intersect (gdouble n1x, gdouble n1y, gdouble d1, - gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y) -{ - gdouble e, f, g, h; - - inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h); - multiply (e, f, g, h, d1, d2, x, y); -} - - -/* draw an angle from the current point to b and then to c, - * with a rounded corner of the given radius. - */ -static void -rounded_corner (cairo_t * cr, - gdouble bx, gdouble by, - gdouble cx, gdouble cy, gdouble radius) -{ - gdouble ax, ay; - gdouble n1x, n1y, d1; - gdouble n2x, n2y, d2; - gdouble pd1, pd2; - gdouble ix, iy; - gdouble dist1, dist2; - gdouble nx, ny, d; - gdouble a1x, a1y, c1x, c1y; - gdouble phi1, phi2; - - cairo_get_current_point (cr, &ax, &ay); -#ifdef KBDRAW_DEBUG - printf (" current point: (%f, %f), radius %f:\n", ax, ay, - radius); -#endif - - /* make sure radius is not too large */ - dist1 = length (bx - ax, by - ay); - dist2 = length (cx - bx, cy - by); - - radius = MIN (radius, MIN (dist1, dist2)); - - /* construct normal forms of the lines */ - normal_form (ax, ay, bx, by, &n1x, &n1y, &d1); - normal_form (bx, by, cx, cy, &n2x, &n2y, &d2); - - /* find which side of the line a,b the point c is on */ - if (point_line_distance (cx, cy, n1x, n1y) < d1) - pd1 = d1 - radius; - else - pd1 = d1 + radius; - - /* find which side of the line b,c the point a is on */ - if (point_line_distance (ax, ay, n2x, n2y) < d2) - pd2 = d2 - radius; - else - pd2 = d2 + radius; - - /* intersect the parallels to find the center of the arc */ - intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy); - - nx = (bx - ax) / dist1; - ny = (by - ay) / dist1; - d = point_line_distance (ix, iy, nx, ny); - - /* a1 is the point on the line a-b where the arc starts */ - intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y); - - nx = (cx - bx) / dist2; - ny = (cy - by) / dist2; - d = point_line_distance (ix, iy, nx, ny); - - /* c1 is the point on the line b-c where the arc ends */ - intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y); - - /* determine the first angle */ - if (a1x - ix == 0) - phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; - else if (a1x - ix > 0) - phi1 = atan ((a1y - iy) / (a1x - ix)); - else - phi1 = M_PI + atan ((a1y - iy) / (a1x - ix)); - - /* determine the second angle */ - if (c1x - ix == 0) - phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; - else if (c1x - ix > 0) - phi2 = atan ((c1y - iy) / (c1x - ix)); - else - phi2 = M_PI + atan ((c1y - iy) / (c1x - ix)); - - /* compute the difference between phi2 and phi1 mod 2pi */ - d = phi2 - phi1; - while (d < 0) - d += 2 * M_PI; - while (d > 2 * M_PI) - d -= 2 * M_PI; - -#ifdef KBDRAW_DEBUG - printf (" line 1 to: (%f, %f):\n", a1x, a1y); -#endif - if (!(isnan (a1x) || isnan (a1y))) - cairo_line_to (cr, a1x, a1y); - - /* pick the short arc from phi1 to phi2 */ - if (d < M_PI) - cairo_arc (cr, ix, iy, radius, phi1, phi2); - else - cairo_arc_negative (cr, ix, iy, radius, phi1, phi2); - -#ifdef KBDRAW_DEBUG - printf (" line 2 to: (%f, %f):\n", cx, cy); -#endif - cairo_line_to (cr, cx, cy); -} - -void -eek_draw_rounded_polygon (cairo_t *cr, - gboolean filled, - gdouble radius, - EekPoint *points, - gint num_points) -{ - gint i, j; - - cairo_move_to (cr, - (gdouble) (points[num_points - 1].x + - points[0].x) / 2, - (gdouble) (points[num_points - 1].y + - points[0].y) / 2); - - -#ifdef KBDRAW_DEBUG - printf (" rounded polygon of radius %f:\n", radius); -#endif - for (i = 0; i < num_points; i++) { - j = (i + 1) % num_points; - rounded_corner (cr, (gdouble) points[i].x, - (gdouble) points[i].y, - (gdouble) (points[i].x + points[j].x) / 2, - (gdouble) (points[i].y + points[j].y) / 2, - radius); -#ifdef KBDRAW_DEBUG - printf (" corner (%d, %d) -> (%d, %d):\n", - points[i].x, points[i].y, points[j].x, - points[j].y); -#endif - }; - cairo_close_path (cr); - - if (filled) - cairo_fill (cr); - else - cairo_stroke (cr); -} diff --git a/eek/eek-keyboard-drawing.c b/eek/eek-keyboard-drawing.c new file mode 100644 index 00000000..9a23179b --- /dev/null +++ b/eek/eek-keyboard-drawing.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2006 Sergey V. Udaltsov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "eek-types.h" + +static gdouble +length (gdouble x, gdouble y) +{ + return sqrt (x * x + y * y); +} + +static gdouble +point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny) +{ + return ax * nx + ay * ny; +} + +static void +normal_form (gdouble ax, gdouble ay, + gdouble bx, gdouble by, + gdouble * nx, gdouble * ny, gdouble * d) +{ + gdouble l; + + *nx = by - ay; + *ny = ax - bx; + + l = length (*nx, *ny); + + *nx /= l; + *ny /= l; + + *d = point_line_distance (ax, ay, *nx, *ny); +} + +static void +inverse (gdouble a, gdouble b, gdouble c, gdouble d, + gdouble * e, gdouble * f, gdouble * g, gdouble * h) +{ + gdouble det; + + det = a * d - b * c; + + *e = d / det; + *f = -b / det; + *g = -c / det; + *h = a / det; +} + +static void +multiply (gdouble a, gdouble b, gdouble c, gdouble d, + gdouble e, gdouble f, gdouble * x, gdouble * y) +{ + *x = a * e + b * f; + *y = c * e + d * f; +} + +static void +intersect (gdouble n1x, gdouble n1y, gdouble d1, + gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y) +{ + gdouble e, f, g, h; + + inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h); + multiply (e, f, g, h, d1, d2, x, y); +} + + +/* draw an angle from the current point to b and then to c, + * with a rounded corner of the given radius. + */ +static void +rounded_corner (cairo_t * cr, + gdouble bx, gdouble by, + gdouble cx, gdouble cy, gdouble radius) +{ + gdouble ax, ay; + gdouble n1x, n1y, d1; + gdouble n2x, n2y, d2; + gdouble pd1, pd2; + gdouble ix, iy; + gdouble dist1, dist2; + gdouble nx, ny, d; + gdouble a1x, a1y, c1x, c1y; + gdouble phi1, phi2; + + cairo_get_current_point (cr, &ax, &ay); +#ifdef KBDRAW_DEBUG + printf (" current point: (%f, %f), radius %f:\n", ax, ay, + radius); +#endif + + /* make sure radius is not too large */ + dist1 = length (bx - ax, by - ay); + dist2 = length (cx - bx, cy - by); + + radius = MIN (radius, MIN (dist1, dist2)); + + /* construct normal forms of the lines */ + normal_form (ax, ay, bx, by, &n1x, &n1y, &d1); + normal_form (bx, by, cx, cy, &n2x, &n2y, &d2); + + /* find which side of the line a,b the point c is on */ + if (point_line_distance (cx, cy, n1x, n1y) < d1) + pd1 = d1 - radius; + else + pd1 = d1 + radius; + + /* find which side of the line b,c the point a is on */ + if (point_line_distance (ax, ay, n2x, n2y) < d2) + pd2 = d2 - radius; + else + pd2 = d2 + radius; + + /* intersect the parallels to find the center of the arc */ + intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy); + + nx = (bx - ax) / dist1; + ny = (by - ay) / dist1; + d = point_line_distance (ix, iy, nx, ny); + + /* a1 is the point on the line a-b where the arc starts */ + intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y); + + nx = (cx - bx) / dist2; + ny = (cy - by) / dist2; + d = point_line_distance (ix, iy, nx, ny); + + /* c1 is the point on the line b-c where the arc ends */ + intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y); + + /* determine the first angle */ + if (a1x - ix == 0) + phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; + else if (a1x - ix > 0) + phi1 = atan ((a1y - iy) / (a1x - ix)); + else + phi1 = M_PI + atan ((a1y - iy) / (a1x - ix)); + + /* determine the second angle */ + if (c1x - ix == 0) + phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; + else if (c1x - ix > 0) + phi2 = atan ((c1y - iy) / (c1x - ix)); + else + phi2 = M_PI + atan ((c1y - iy) / (c1x - ix)); + + /* compute the difference between phi2 and phi1 mod 2pi */ + d = phi2 - phi1; + while (d < 0) + d += 2 * M_PI; + while (d > 2 * M_PI) + d -= 2 * M_PI; + +#ifdef KBDRAW_DEBUG + printf (" line 1 to: (%f, %f):\n", a1x, a1y); +#endif + if (!(isnan (a1x) || isnan (a1y))) + cairo_line_to (cr, a1x, a1y); + + /* pick the short arc from phi1 to phi2 */ + if (d < M_PI) + cairo_arc (cr, ix, iy, radius, phi1, phi2); + else + cairo_arc_negative (cr, ix, iy, radius, phi1, phi2); + +#ifdef KBDRAW_DEBUG + printf (" line 2 to: (%f, %f):\n", cx, cy); +#endif + cairo_line_to (cr, cx, cy); +} + +/* renamed from rounded_polygon, use EekPoint instead of GdkPoint not + to depend on GTK+, and exported */ +void +_eek_rounded_polygon (cairo_t *cr, + gdouble radius, + EekPoint *points, + gint num_points) +{ + gint i, j; + + cairo_move_to (cr, + (gdouble) (points[num_points - 1].x + + points[0].x) / 2, + (gdouble) (points[num_points - 1].y + + points[0].y) / 2); + + +#ifdef KBDRAW_DEBUG + printf (" rounded polygon of radius %f:\n", radius); +#endif + for (i = 0; i < num_points; i++) { + j = (i + 1) % num_points; + rounded_corner (cr, (gdouble) points[i].x, + (gdouble) points[i].y, + (gdouble) (points[i].x + points[j].x) / 2, + (gdouble) (points[i].y + points[j].y) / 2, + radius); +#ifdef KBDRAW_DEBUG + printf (" corner (%d, %d) -> (%d, %d):\n", + points[i].x, points[i].y, points[j].x, + points[j].y); +#endif + }; + cairo_close_path (cr); +} diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c new file mode 100644 index 00000000..88c22c45 --- /dev/null +++ b/eek/eek-renderer.c @@ -0,0 +1,886 @@ +#include + +#include "eek-key.h" +#include "eek-section.h" +#include "eek-renderer.h" + +G_DEFINE_TYPE (EekRenderer, eek_renderer, G_TYPE_OBJECT); + +#define EEK_RENDERER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_RENDERER, EekRendererPrivate)) + +struct _EekRendererPrivate +{ + EekKeyboard *keyboard; + PangoContext *pcontext; + + EekColor *foreground; + EekColor *background; + gdouble border_width; + + gdouble scale; + PangoFontDescription *font; + GHashTable *outline_surface_cache; + cairo_surface_t *keyboard_surface; +}; + +struct { + gint category; + gdouble scale; +} keysym_category_scale_factors[EEK_KEYSYM_CATEGORY_LAST] = { + { EEK_KEYSYM_CATEGORY_LETTER, 1.0 }, + { EEK_KEYSYM_CATEGORY_FUNCTION, 0.5 }, + { EEK_KEYSYM_CATEGORY_KEYNAME, 0.5 } +}; + +/* eek-keyboard-drawing.c */ +extern void _eek_rounded_polygon (cairo_t *cr, + gdouble radius, + EekPoint *points, + gint num_points); + +static void invalidate (EekRenderer *renderer); +static void render_key (EekRenderer *self, + cairo_t *cr, + EekKey *key); + +struct _CreateKeyboardSurfaceCallbackData { + cairo_t *cr; + EekRenderer *renderer; +}; +typedef struct _CreateKeyboardSurfaceCallbackData CreateKeyboardSurfaceCallbackData; + +static void +create_keyboard_surface_key_callback (EekElement *element, + gpointer user_data) +{ + CreateKeyboardSurfaceCallbackData *data = user_data; + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer); + EekBounds bounds; + + cairo_save (data->cr); + + eek_element_get_bounds (element, &bounds); + cairo_translate (data->cr, bounds.x * priv->scale, bounds.y * priv->scale); + cairo_rectangle (data->cr, + 0.0, + 0.0, + bounds.width * priv->scale, + bounds.height * priv->scale); + cairo_clip (data->cr); + render_key (data->renderer, data->cr, EEK_KEY(element)); + + cairo_restore (data->cr); +} + +static void +create_keyboard_surface_section_callback (EekElement *element, + gpointer user_data) +{ + CreateKeyboardSurfaceCallbackData *data = user_data; + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer); + EekBounds bounds; + gint angle; + + cairo_save (data->cr); + + eek_element_get_bounds (element, &bounds); + cairo_translate (data->cr, bounds.x * priv->scale, bounds.y * priv->scale); + + angle = eek_section_get_angle (EEK_SECTION(element)); + cairo_rotate (data->cr, angle * G_PI / 180); + + eek_container_foreach_child (EEK_CONTAINER(element), + create_keyboard_surface_key_callback, + data); + + cairo_restore (data->cr); +} + +static cairo_surface_t * +create_keyboard_surface (EekRenderer *renderer) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer); + EekBounds bounds; + cairo_surface_t *keyboard_surface; + CreateKeyboardSurfaceCallbackData data; + + eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds); + keyboard_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + bounds.width * priv->scale, + bounds.height * priv->scale); + data.cr = cairo_create (keyboard_surface); + data.renderer = renderer; + + cairo_translate (data.cr, bounds.x * priv->scale, bounds.y * priv->scale); + + /* blank background */ + cairo_set_source_rgba (data.cr, 0, 0, 0, 0); + cairo_rectangle (data.cr, + 0.0, + 0.0, + cairo_image_surface_get_width (keyboard_surface), + cairo_image_surface_get_height (keyboard_surface)); + cairo_fill (data.cr); + + /* draw sections */ + eek_container_foreach_child (EEK_CONTAINER(priv->keyboard), + create_keyboard_surface_section_callback, + &data); + cairo_destroy (data.cr); + return keyboard_surface; +} + +static void +render_key_outline (EekRenderer *renderer, + cairo_t *cr, + EekKey *key) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer); + EekOutline *outline; + EekBounds bounds; + cairo_pattern_t *pat; + EekPoint *points; + gdouble scale; + gint i; + + /* need to rescale so that the border fit inside the clipping + region */ + eek_element_get_bounds (EEK_ELEMENT(key), &bounds); + scale = MIN((bounds.width - priv->border_width) / bounds.width, + (bounds.height - priv->border_width) / bounds.height); + + outline = eek_key_get_outline (key); + points = g_slice_copy (sizeof (EekPoint) * outline->num_points, + outline->points); + for (i = 0; i < outline->num_points; i++) { + points[i].x *= priv->scale * scale; + points[i].y *= priv->scale * scale; + } + + cairo_translate (cr, + priv->border_width / 2 * priv->scale, + priv->border_width / 2 * priv->scale); + + /* paint the background with gradient */ + pat = cairo_pattern_create_linear (0.0, + 0.0, + 0.0, + bounds.height * priv->scale * 5.0); + cairo_pattern_add_color_stop_rgba (pat, + 1, + priv->background->red * 0.5, + priv->background->green * 0.5, + priv->background->blue * 0.5, + priv->background->alpha); + cairo_pattern_add_color_stop_rgba (pat, + 0, + priv->background->red, + priv->background->green, + priv->background->blue, + priv->background->alpha); + + cairo_set_source (cr, pat); + _eek_rounded_polygon (cr, + outline->corner_radius, + points, + outline->num_points); + cairo_fill (cr); + + cairo_pattern_destroy (pat); + + /* paint the border */ + cairo_set_line_width (cr, priv->border_width * priv->scale); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + cairo_set_source_rgba + (cr, + ABS(priv->background->red - priv->foreground->red) * 0.7, + ABS(priv->background->green - priv->foreground->green) * 0.7, + ABS(priv->background->blue - priv->foreground->blue) * 0.7, + priv->foreground->alpha); + + _eek_rounded_polygon (cr, + outline->corner_radius, + points, + outline->num_points); + cairo_stroke (cr); + + g_slice_free1 (sizeof (EekPoint) * outline->num_points, points); +} + +struct _CalculateFontSizeCallbackData { + gdouble size; + EekRenderer *renderer; +}; +typedef struct _CalculateFontSizeCallbackData CalculateFontSizeCallbackData; + +static void +calculate_font_size_key_callback (EekElement *element, gpointer user_data) +{ + CalculateFontSizeCallbackData *data = user_data; + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer); + gdouble sx, sy; + PangoFontDescription *font; + const PangoFontDescription *base_font; + PangoRectangle extents = { 0, }; + PangoLayout *layout; + gdouble size; + guint keysym; + EekBounds bounds; + gchar *label; + + keysym = eek_key_get_keysym (EEK_KEY(element)); + if (keysym == EEK_INVALID_KEYSYM || + eek_keysym_get_category (keysym) != EEK_KEYSYM_CATEGORY_LETTER) + return; + + label = eek_keysym_to_string (keysym); + if (!label) + return; + + base_font = pango_context_get_font_description (priv->pcontext); + font = pango_font_description_copy (base_font); + + eek_element_get_bounds (element, &bounds); + size = eek_bounds_long_side (&bounds) * PANGO_SCALE; + pango_font_description_set_size (font, size); + layout = pango_layout_new (priv->pcontext); + pango_layout_set_font_description (layout, font); + pango_font_description_free (font); + + pango_layout_set_text (layout, label, -1); + g_free (label); + + pango_layout_get_extents (layout, NULL, &extents); + + sx = sy = 1.0; + if (extents.width > bounds.width * PANGO_SCALE) + sx = bounds.width * PANGO_SCALE / extents.width; + if (extents.height > bounds.height * PANGO_SCALE) + sy = bounds.height * PANGO_SCALE / extents.height; + g_object_unref (layout); + + size *= MIN(sx, sy); + if (size < data->size && + size >= pango_font_description_get_size (base_font)) + data->size = size; +} + +static void +calculate_font_size_section_callback (EekElement *element, gpointer user_data) +{ + eek_container_foreach_child (EEK_CONTAINER(element), + calculate_font_size_key_callback, + user_data); +} + +static gdouble +calculate_font_size (EekRenderer *renderer) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer); + CalculateFontSizeCallbackData data; + PangoFontDescription *base_font; + + base_font = pango_context_get_font_description (priv->pcontext); + data.size = pango_font_description_get_size (base_font); + data.renderer = renderer; + eek_container_foreach_child (EEK_CONTAINER(priv->keyboard), + calculate_font_size_section_callback, + &data); + return data.size; +} + +static void +render_key_label (EekRenderer *renderer, + cairo_t *cr, + EekKey *key) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer); + guint keysym; + EekKeysymCategory category; + gchar *label; + PangoLayout *layout; + PangoRectangle extents = { 0, }; + EekBounds bounds; + PangoFontDescription *font; + gdouble size, scale; + gint i; + + keysym = eek_key_get_keysym (key); + if (keysym == EEK_INVALID_KEYSYM) + return; + + category = eek_keysym_get_category (keysym); + if (category == EEK_KEYSYM_CATEGORY_UNKNOWN) + return; + + label = eek_keysym_to_string (keysym); + if (!label) + return; + + eek_element_get_bounds (EEK_ELEMENT(key), &bounds); + scale = MIN((bounds.width - priv->border_width) / bounds.width, + (bounds.height - priv->border_width) / bounds.height); + + layout = pango_cairo_create_layout (cr); + font = pango_font_description_copy (priv->font); + size = pango_font_description_get_size (font); + for (i = 0; i < G_N_ELEMENTS(keysym_category_scale_factors); i++) + if (keysym_category_scale_factors[i].category == category) { + size *= keysym_category_scale_factors[i].scale; + break; + } + pango_font_description_set_size (font, size * priv->scale * scale); + pango_layout_set_font_description (layout, font); + pango_layout_set_text (layout, label, -1); + g_free (label); + pango_layout_set_width (layout, + PANGO_SCALE * bounds.width * priv->scale * scale); + pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); + pango_layout_get_extents (layout, NULL, &extents); + + cairo_save (cr); + cairo_move_to + (cr, + (bounds.width * priv->scale - extents.width / PANGO_SCALE) / 2, + (bounds.height * priv->scale - extents.height / PANGO_SCALE) / 2); + cairo_set_source_rgba (cr, + priv->foreground->red, + priv->foreground->green, + priv->foreground->blue, + priv->foreground->alpha); + pango_cairo_show_layout (cr, layout); + cairo_restore (cr); + g_object_unref (layout); +} + +static void +render_key (EekRenderer *self, + cairo_t *cr, + EekKey *key) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(self); + EekOutline *outline; + cairo_surface_t *outline_surface; + EekBounds bounds; + + outline = eek_key_get_outline (key); + + outline_surface = g_hash_table_lookup (priv->outline_surface_cache, + outline); + if (!outline_surface) { + cairo_t *cr; + + eek_element_get_bounds (EEK_ELEMENT(key), &bounds); + outline_surface = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + bounds.width * priv->scale, + bounds.height * priv->scale); + cr = cairo_create (outline_surface); + + /* blank background */ + eek_element_get_bounds (EEK_ELEMENT(key), &bounds); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); + cairo_rectangle (cr, + 0.0, + 0.0, + bounds.width * priv->scale, + bounds.height * priv->scale); + cairo_fill (cr); + + render_key_outline (self, cr, key); + + g_hash_table_insert (priv->outline_surface_cache, + outline, + outline_surface); + } + + if (!priv->font) { + PangoFontDescription *base_font; + gdouble size; + + size = calculate_font_size (self); + base_font = pango_context_get_font_description (priv->pcontext); + priv->font = pango_font_description_copy (base_font); + pango_font_description_set_size (priv->font, size); + } + + cairo_set_source_surface (cr, outline_surface, 0.0, 0.0); + cairo_paint (cr); + + render_key_label (self, cr, key); +} + +static void +eek_renderer_real_render_key (EekRenderer *self, + cairo_t *cr, + EekKey *key, + gdouble scale) +{ + EekElement *section; + EekBounds bounds, rotated_bounds; + gint angle; + gdouble s; + + eek_renderer_get_key_bounds (self, key, &bounds, FALSE); + eek_renderer_get_key_bounds (self, key, &rotated_bounds, TRUE); + + section = eek_element_get_parent (EEK_ELEMENT(key)); + angle = eek_section_get_angle (EEK_SECTION(section)); + + cairo_save (cr); + cairo_scale (cr, scale, scale); +#if DEBUG + cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0); + cairo_rectangle (cr, 0, 0, + rotated_bounds.width, rotated_bounds.height); + cairo_stroke (cr); + cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0); +#endif /* DEBUG */ + s = sin (angle * G_PI / 180); + if (s < 0) + cairo_translate (cr, 0, - bounds.width * s); + else + cairo_translate (cr, bounds.height * s, 0); + cairo_rotate (cr, angle * G_PI / 180); +#if DEBUG + cairo_rectangle (cr, + 0, + 0, + bounds.width, bounds.height); + cairo_stroke (cr); +#endif /* DEBUG */ + render_key (self, cr, key); + cairo_restore (cr); +} + +static void +eek_renderer_real_render_keyboard (EekRenderer *self, + cairo_t *cr) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(self); + + if (!priv->keyboard_surface) + priv->keyboard_surface = create_keyboard_surface (self); + + cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0); + cairo_paint (cr); +} + +static void +eek_renderer_dispose (GObject *object) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(object); + + if (priv->keyboard) { + if (g_object_is_floating (priv->keyboard)) + g_object_unref (priv->keyboard); + priv->keyboard = NULL; + } + if (priv->pcontext) { + g_object_unref (priv->pcontext); + priv->pcontext = NULL; + } + + /* this will release all allocated surfaces and font if any */ + invalidate (EEK_RENDERER(object)); + + G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object); +} + +static void +eek_renderer_finalize (GObject *object) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(object); + + g_hash_table_destroy (priv->outline_surface_cache); + G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object); +} + +static void +eek_renderer_class_init (EekRendererClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (gobject_class, + sizeof (EekRendererPrivate)); + + klass->render_key = eek_renderer_real_render_key; + klass->render_keyboard = eek_renderer_real_render_keyboard; + + gobject_class->dispose = eek_renderer_dispose; + gobject_class->finalize = eek_renderer_finalize; +} + +static void +free_surface (gpointer data) +{ + cairo_surface_destroy (data); +} + +static void +eek_renderer_init (EekRenderer *self) +{ + EekRendererPrivate *priv; + + priv = self->priv = EEK_RENDERER_GET_PRIVATE(self); + priv->keyboard = NULL; + priv->pcontext = NULL; + priv->foreground = eek_color_new (0.3, 0.3, 0.3, 1.0); + priv->background = eek_color_new (1.0, 1.0, 1.0, 1.0); + priv->border_width = 3.0; + priv->scale = 1.0; + priv->font = NULL; + priv->outline_surface_cache = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + free_surface); + priv->keyboard_surface = NULL; +} + +static void +invalidate (EekRenderer *renderer) +{ + EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer); + + if (priv->outline_surface_cache) + g_hash_table_remove_all (priv->outline_surface_cache); + + if (priv->keyboard_surface) { + cairo_surface_destroy (priv->keyboard_surface); + priv->keyboard_surface = NULL; + } +} + +static void +on_keysym_index_changed (EekKeyboard *keyboard, + gint group, + gint level, + gpointer user_data) +{ + EekRenderer *renderer = user_data; + invalidate (renderer); +} + +EekRenderer * +eek_renderer_new (EekKeyboard *keyboard, + PangoContext *pcontext) +{ + EekRenderer *renderer; + EekRendererPrivate *priv; + + renderer = g_object_new (EEK_TYPE_RENDERER, NULL); + priv = EEK_RENDERER_GET_PRIVATE(renderer); + priv->keyboard = g_object_ref_sink (keyboard); + priv->pcontext = g_object_ref (pcontext); + + g_signal_connect (keyboard, "keysym-index-changed", + G_CALLBACK(on_keysym_index_changed), + renderer); + + return renderer; +} + +void +eek_renderer_set_preferred_size (EekRenderer *renderer, + gdouble width, + gdouble height) +{ + EekRendererPrivate *priv; + EekBounds bounds; + gdouble scale; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (width > 0.0 && height > 0.0); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + + eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds); + scale = width > height ? height / bounds.height : + width / bounds.width; + + if (scale != priv->scale) { + priv->scale = scale; + invalidate (renderer); + } +} + +void +eek_renderer_get_key_bounds (EekRenderer *renderer, + EekKey *key, + EekBounds *bounds, + gboolean rotate) +{ + EekRendererPrivate *priv; + EekElement *section; + EekBounds section_bounds, keyboard_bounds; + gint angle = 0; + EekPoint points[4], min, max; + gint i; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (EEK_IS_KEY(key)); + g_return_if_fail (bounds != NULL); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + + section = eek_element_get_parent (EEK_ELEMENT(key)); + + eek_element_get_bounds (EEK_ELEMENT(key), bounds); + eek_element_get_bounds (section, §ion_bounds); + eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &keyboard_bounds); + + if (!rotate) { + bounds->x += keyboard_bounds.x + section_bounds.x; + bounds->y += keyboard_bounds.y + section_bounds.y; + bounds->x *= priv->scale; + bounds->y *= priv->scale; + bounds->width *= priv->scale; + bounds->height *= priv->scale; + return; + } + points[0].x = bounds->x; + points[0].y = bounds->y; + points[1].x = points[0].x + bounds->width; + points[1].y = points[0].y; + points[2].x = points[1].x; + points[2].y = points[1].y + bounds->height; + points[3].x = points[0].x; + points[3].y = points[2].y; + + if (rotate) + angle = eek_section_get_angle (EEK_SECTION(section)); + + min = points[2]; + max = points[0]; + for (i = 0; i < G_N_ELEMENTS(points); i++) { + eek_point_rotate (&points[i], angle); + if (points[i].x < min.x) + min.x = points[i].x; + if (points[i].x > max.x) + max.x = points[i].x; + if (points[i].y < min.y) + min.y = points[i].y; + if (points[i].y > max.y) + max.y = points[i].y; + } + bounds->x = keyboard_bounds.x + section_bounds.x + min.x; + bounds->y = keyboard_bounds.y + section_bounds.y + min.y; + bounds->width = (max.x - min.x); + bounds->height = (max.y - min.y); + bounds->x *= priv->scale; + bounds->y *= priv->scale; + bounds->width *= priv->scale; + bounds->height *= priv->scale; +} + +gdouble +eek_renderer_get_scale (EekRenderer *renderer) +{ + EekRendererPrivate *priv; + + g_assert (EEK_IS_RENDERER(renderer)); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + + return priv->scale; +} + +void +eek_renderer_render_key (EekRenderer *renderer, + cairo_t *cr, + EekKey *key, + gdouble scale) +{ + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (scale >= 0.0); + + EEK_RENDERER_GET_CLASS(renderer)->render_key (renderer, cr, key, scale); +} + +void +eek_renderer_render_keyboard (EekRenderer *renderer, + cairo_t *cr) +{ + g_return_if_fail (EEK_IS_RENDERER(renderer)); + EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr); +} + +void +eek_renderer_set_foreground (EekRenderer *renderer, + EekColor *foreground) +{ + EekRendererPrivate *priv; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (foreground); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + priv->foreground = g_boxed_copy (EEK_TYPE_COLOR, foreground); +} + +void +eek_renderer_set_background (EekRenderer *renderer, + EekColor *background) +{ + EekRendererPrivate *priv; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (background); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + priv->background = g_boxed_copy (EEK_TYPE_COLOR, background); +} + +void +eek_renderer_get_foreground (EekRenderer *renderer, + EekColor *foreground) +{ + EekRendererPrivate *priv; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (foreground); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + *foreground = *priv->foreground; +} + +void +eek_renderer_get_background (EekRenderer *renderer, + EekColor *background) +{ + EekRendererPrivate *priv; + + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (background); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + *background = *priv->background; +} + +struct _FindKeyByPositionCallbackData { + EekPoint point; + EekPoint origin; + gint angle; + EekKey *key; + EekRenderer *renderer; +}; +typedef struct _FindKeyByPositionCallbackData FindKeyByPositionCallbackData; + +static gboolean +sign (EekPoint *p1, EekPoint *p2, EekPoint *p3) +{ + return (p1->x - p3->x) * (p2->y - p3->y) - + (p2->x - p3->x) * (p1->y - p3->y); +} + +static gint +find_key_by_position_key_callback (EekElement *element, + gpointer user_data) +{ + FindKeyByPositionCallbackData *data = user_data; + EekBounds bounds; + EekRendererPrivate *priv; + EekPoint points[4]; + gint i; + gboolean b1, b2, b3; + + priv = EEK_RENDERER_GET_PRIVATE(data->renderer); + + eek_element_get_bounds (element, &bounds); + + points[0].x = bounds.x; + points[0].y = bounds.y; + points[1].x = points[0].x + bounds.width; + points[1].y = points[0].y; + points[2].x = points[1].x; + points[2].y = points[1].y + bounds.height; + points[3].x = points[0].x; + points[3].y = points[2].y; + + for (i = 0; i < G_N_ELEMENTS(points); i++) { + eek_point_rotate (&points[i], data->angle); + points[i].x += data->origin.x; + points[i].y += data->origin.y; + points[i].x *= priv->scale; + points[i].y *= priv->scale; + } + + b1 = sign (&data->point, &points[0], &points[1]) < 0.0; + b2 = sign (&data->point, &points[1], &points[2]) < 0.0; + b3 = sign (&data->point, &points[2], &points[0]) < 0.0; + + if (b1 == b2 && b2 == b3) { + data->key = EEK_KEY(element); + return 0; + } + + b1 = sign (&data->point, &points[2], &points[3]) < 0.0; + b2 = sign (&data->point, &points[3], &points[0]) < 0.0; + b3 = sign (&data->point, &points[0], &points[2]) < 0.0; + + if (b1 == b2 && b2 == b3) { + data->key = EEK_KEY(element); + return 0; + } + + return -1; +} + +static gint +find_key_by_position_section_callback (EekElement *element, + gpointer user_data) +{ + FindKeyByPositionCallbackData *data = user_data; + EekBounds bounds; + EekRendererPrivate *priv; + EekPoint origin; + + priv = EEK_RENDERER_GET_PRIVATE(data->renderer); + + origin = data->origin; + eek_element_get_bounds (element, &bounds); + data->origin.x += bounds.x; + data->origin.y += bounds.y; + data->angle = eek_section_get_angle (EEK_SECTION(element)); + + eek_container_find (EEK_CONTAINER(element), + find_key_by_position_key_callback, + data); + data->origin = origin; + return data->key ? 0 : -1; +} + +EekKey * +eek_renderer_find_key_by_position (EekRenderer *renderer, + gdouble x, + gdouble y) +{ + EekRendererPrivate *priv; + EekBounds bounds; + FindKeyByPositionCallbackData data; + + g_assert (EEK_IS_RENDERER(renderer)); + + priv = EEK_RENDERER_GET_PRIVATE(renderer); + eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds); + + g_assert (x >= bounds.x * priv->scale); + g_assert (y >= bounds.y * priv->scale); + g_assert (x <= bounds.width * priv->scale); + g_assert (y <= bounds.height * priv->scale); + + data.point.x = x; + data.point.y = y; + data.origin.x = bounds.x; + data.origin.y = bounds.y; + data.key = NULL; + data.renderer = renderer; + + eek_container_find (EEK_CONTAINER(priv->keyboard), + find_key_by_position_section_callback, + &data); + return data.key; +} diff --git a/eek/eek-renderer.h b/eek/eek-renderer.h new file mode 100644 index 00000000..6615fa5b --- /dev/null +++ b/eek/eek-renderer.h @@ -0,0 +1,83 @@ +#ifndef EEK_RENDERER_H +#define EEK_RENDERER_H 1 + +#include +#include "eek-keyboard.h" +#include "eek-keysym.h" +#include "eek-types.h" + +G_BEGIN_DECLS + +#define EEK_TYPE_RENDERER (eek_renderer_get_type()) +#define EEK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_RENDERER, EekRenderer)) +#define EEK_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_RENDERER, EekRendererClass)) +#define EEK_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_RENDERER)) +#define EEK_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_RENDERER)) +#define EEK_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_RENDERER, EekRendererClass)) + +typedef struct _EekRenderer EekRenderer; +typedef struct _EekRendererClass EekRendererClass; +typedef struct _EekRendererPrivate EekRendererPrivate; + +struct _EekRenderer { + GObject parent; + + EekRendererPrivate *priv; +}; + +struct _EekRendererClass +{ + GObjectClass parent_class; + + void (* render_key) (EekRenderer *self, + cairo_t *cr, + EekKey *key, + gdouble scale); + + void (* render_keyboard) (EekRenderer *self, + cairo_t *cr); + + /*< private >*/ + /* padding */ + gpointer pdummy[24]; +}; + +GType eek_renderer_get_type (void) G_GNUC_CONST; +EekRenderer *eek_renderer_new (EekKeyboard *keyboard, + PangoContext *pcontext); +void eek_renderer_set_preferred_size (EekRenderer *renderer, + gdouble width, + gdouble height); +void eek_renderer_get_size (EekRenderer *renderer, + gdouble *width, + gdouble *height); +void eek_renderer_get_key_bounds (EekRenderer *renderer, + EekKey *key, + EekBounds *bounds, + gboolean rotate); +gdouble eek_renderer_get_scale (EekRenderer *renderer); + +void eek_renderer_render_key (EekRenderer *renderer, + cairo_t *cr, + EekKey *key, + gdouble scale); + +void eek_renderer_render_keyboard (EekRenderer *renderer, + cairo_t *cr); + +void eek_renderer_set_foreground (EekRenderer *renderer, + EekColor *foreground); +void eek_renderer_set_background (EekRenderer *renderer, + EekColor *background); +void eek_renderer_get_foreground (EekRenderer *renderer, + EekColor *foreground); +void eek_renderer_get_background (EekRenderer *renderer, + EekColor *background); +void eek_renderer_set_border_width (EekRenderer *renderer, + gdouble border_width); +EekKey *eek_renderer_find_key_by_position (EekRenderer *renderer, + gdouble x, + gdouble y); + +G_END_DECLS +#endif /* EEK_RENDERER_H */