Add experimental GtkDrawingArea based UI.

This commit is contained in:
Daiki Ueno
2010-06-15 19:17:05 +09:00
parent 231186ae71
commit 63b9fea27b
17 changed files with 1429 additions and 303 deletions

View File

@ -78,6 +78,7 @@ EXTRA_HFILES=
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
IGNORE_HFILES= eek-private.h \
eek-keysym.h \
config.h \
$(NULL)
# Images to copy into HTML directory.
@ -101,6 +102,7 @@ expand_content_files=
GTKDOC_CFLAGS = $(GOBJECT2_CFLAGS)
GTKDOC_LIBS = $(top_srcdir)/eek/libeek.la \
$(top_srcdir)/eek/libeek-clutter.la \
$(top_srcdir)/eek/libeek-gtk.la \
$(top_srcdir)/eek/libeek-xkb.la \
$(top_srcdir)/eek/libeek-xkl.la \
$(GOBJECT2_LIBS) \

View File

@ -16,7 +16,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
lib_LTLIBRARIES = libeek.la libeek-clutter.la libeek-xkb.la libeek-xkl.la
lib_LTLIBRARIES = \
libeek.la \
libeek-clutter.la \
libeek-gtk.la \
libeek-xkb.la \
libeek-xkl.la
libeek_la_SOURCES = \
eek-layout.c \
@ -37,8 +42,7 @@ libeek_la_SOURCES = \
eek-keysym.c \
eek-special-keysym-labels.h \
eek-unicode-keysym-labels.h \
eek-keyname-keysym-labels.h \
$(NULL)
eek-keyname-keysym-labels.h
libeek_la_CFLAGS = $(GOBJECT2_CFLAGS) $(CAIRO_CFLAGS) $(PANGO_CFLAGS)
libeek_la_LIBADD = $(GOBJECT2_LIBS) $(CAIRO_LIBS) $(PANGO_LIBS)
@ -52,24 +56,33 @@ libeek_clutter_la_SOURCES = \
eek-clutter-key.h \
eek-clutter-key-actor.c \
eek-clutter-key-actor.h \
eek-clutter.h \
$(NULL)
eek-drawing.h \
eek-drawing.c \
eek-clutter.h
libeek_clutter_la_CFLAGS = $(CLUTTER_CFLAGS)
libeek_clutter_la_LIBADD = libeek.la $(CLUTTER_LIBS)
libeek_gtk_la_SOURCES = \
eek-gtk-keyboard.c \
eek-gtk-keyboard.h \
eek-drawing.h \
eek-drawing.c \
eek-gtk.h
libeek_gtk_la_CFLAGS = $(GTK2_CFLAGS)
libeek_gtk_la_LIBADD = libeek.la $(GTK2_LIBS)
libeek_xkb_la_SOURCES = \
eek-xkb-layout.h \
eek-xkb-layout.c \
$(NULL)
eek-xkb-layout.c
libeek_xkb_la_CFLAGS = $(GTK2_CFLAGS) $(XKB_CFLAGS)
libeek_xkb_la_LIBADD = libeek.la $(GTK2_LIBS) $(XKB_LIBS)
libeek_xkl_la_SOURCES = \
eek-xkl-layout.h \
eek-xkl-layout.c \
$(NULL)
eek-xkl-layout.c
libeek_xkl_la_CFLAGS = $(GTK2_CFLAGS) $(LIBXKLAVIER_CFLAGS)
libeek_xkl_la_LIBADD = libeek-xkb.la $(GTK2_LIBS) $(LIBXKLAVIER_LIBS)

View File

@ -26,13 +26,15 @@
#include <cogl/cogl.h>
#include <cogl/cogl-pango.h>
#include <math.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "eek-clutter-key-actor.h"
#include "eek-keysym.h"
#include "eek-drawing.h"
#include "eek-section.h"
#include "eek-keyboard.h"
#define noKBDRAW_DEBUG
@ -54,6 +56,7 @@ struct _EekClutterKeyActorPrivate
{
EekKey *key;
ClutterActor *texture;
PangoFontDescription *font;
};
static struct {
@ -61,20 +64,24 @@ static struct {
GHashTable *outline_textures;
/* manually maintain the ref-count of outline_textures to set it
to NULL on destroy */
to NULL when all the EekClutterKeyActor are destroyed */
gint outline_textures_ref_count;
} texture_cache;
static struct {
/* keysym category -> PangoFontDescription * */
PangoFontDescription *category_fonts[EEK_KEYSYM_CATEGORY_LAST];
/* manually maintain the ref-count of category_fonts to set it to
NULL when all the EekClutterKeyActor are destroyed */
gint category_fonts_ref_count;
} font_cache;
static ClutterActor *get_texture (EekClutterKeyActor *actor);
static void draw_key_on_layout (EekKey *key,
static void draw_key_on_layout (EekClutterKeyActor *actor,
PangoLayout *layout);
static void key_enlarge (ClutterActor *actor);
static void key_shrink (ClutterActor *actor);
static void draw_rounded_polygon (cairo_t *cr,
gboolean filled,
gdouble radius,
EekPoint *points,
gint num_points);
static void
eek_clutter_key_actor_real_paint (ClutterActor *self)
@ -104,7 +111,7 @@ eek_clutter_key_actor_real_paint (ClutterActor *self)
/* Draw the label on the key. */
layout = clutter_actor_create_pango_layout (self, NULL);
draw_key_on_layout (priv->key, layout);
draw_key_on_layout (EEK_CLUTTER_KEY_ACTOR(self), layout);
pango_layout_get_extents (layout, NULL, &logical_rect);
/* FIXME: Color should be configurable through a property. */
@ -129,12 +136,11 @@ eek_clutter_key_actor_real_get_preferred_width (ClutterActor *self,
gfloat *min_width_p,
gfloat *natural_width_p)
{
EekClutterKeyActorPrivate *priv = EEK_CLUTTER_KEY_ACTOR_GET_PRIVATE (self);
PangoLayout *layout;
/* Draw the label on the key - just to validate the glyph cache. */
layout = clutter_actor_create_pango_layout (self, NULL);
draw_key_on_layout (priv->key, layout);
draw_key_on_layout (EEK_CLUTTER_KEY_ACTOR(self), layout);
cogl_pango_ensure_glyph_cache_for_layout (layout);
g_object_unref (layout);
@ -189,6 +195,18 @@ eek_clutter_key_actor_dispose (GObject *object)
texture_cache.outline_textures = NULL;
}
}
if (priv->font) {
/* no need to pango_font_description_free (priv->font) */
priv->font = NULL;
if (--font_cache.category_fonts_ref_count == 0) {
gint i;
for (i = 0; i < EEK_KEYSYM_CATEGORY_LAST; i++) {
pango_font_description_free (font_cache.category_fonts[i]);
font_cache.category_fonts[i] = NULL;
}
}
}
G_OBJECT_CLASS (eek_clutter_key_actor_parent_class)->dispose (object);
}
@ -268,6 +286,8 @@ eek_clutter_key_actor_init (EekClutterKeyActor *self)
priv = self->priv = EEK_CLUTTER_KEY_ACTOR_GET_PRIVATE(self);
priv->key = NULL;
priv->texture = NULL;
priv->font = NULL;
clutter_actor_set_reactive (CLUTTER_ACTOR(self), TRUE);
g_signal_connect (self, "button-press-event",
G_CALLBACK (on_button_press_event), NULL);
@ -343,20 +363,20 @@ create_texture_for_key (EekKey *key)
cairo_set_source (cr, pat);
draw_rounded_polygon (cr,
TRUE,
outline->corner_radius,
outline->points,
outline->num_points);
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);
draw_rounded_polygon (cr,
FALSE,
outline->corner_radius,
outline->points,
outline->num_points);
eek_draw_rounded_polygon (cr,
FALSE,
outline->corner_radius,
outline->points,
outline->num_points);
cairo_destroy (cr);
return texture;
}
@ -382,272 +402,57 @@ get_texture (EekClutterKeyActor *actor)
}
static void
draw_text_on_layout (PangoLayout *layout,
const gchar *text,
gdouble scale)
{
PangoFontDescription *font_desc;
#define FONT_SIZE (720 * 50)
/* FIXME: Font should be configurable through a property. */
font_desc = pango_font_description_from_string ("Sans");
pango_font_description_set_size (font_desc, FONT_SIZE * scale);
pango_layout_set_font_description (layout, font_desc);
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
pango_layout_set_text (layout, text, -1);
pango_font_description_free (font_desc);
}
static void
draw_key_on_layout (EekKey *key,
draw_key_on_layout (EekClutterKeyActor *self,
PangoLayout *layout)
{
PangoLayout *buffer;
PangoRectangle logical_rect = { 0, };
EekBounds bounds;
EekClutterKeyActorPrivate *priv = EEK_CLUTTER_KEY_ACTOR_GET_PRIVATE (self);
guint keysym;
const gchar *label, *empty_label = "";
gdouble scale_x, scale_y;
EekKeysymCategory category;
EekBounds bounds;
eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
keysym = eek_key_get_keysym (key);
if (font_cache.category_fonts_ref_count == 0) {
EekContainer *keyboard, *section;
PangoFontDescription *font;
PangoLayout *copy;
section = EEK_ELEMENT_GET_CLASS(priv->key)->
get_parent (EEK_ELEMENT(priv->key));
g_return_if_fail (EEK_IS_SECTION(section));
keyboard = EEK_ELEMENT_GET_CLASS(section)->
get_parent (EEK_ELEMENT(section));
g_return_if_fail (EEK_IS_KEYBOARD(keyboard));
copy = pango_layout_copy (layout);
font = pango_font_description_from_string ("Sans");
pango_layout_set_font_description (copy, font);
eek_get_fonts (EEK_KEYBOARD(keyboard), copy,
font_cache.category_fonts);
g_object_unref (G_OBJECT(copy));
}
keysym = eek_key_get_keysym (priv->key);
if (keysym == EEK_INVALID_KEYSYM)
return;
category = eek_keysym_get_category (keysym);
if (category == EEK_KEYSYM_CATEGORY_UNKNOWN)
return;
if (!priv->font) {
priv->font = font_cache.category_fonts[category];
font_cache.category_fonts_ref_count++;
}
pango_layout_set_font_description (layout, priv->font);
eek_element_get_bounds (EEK_ELEMENT(priv->key), &bounds);
pango_layout_set_width (layout, PANGO_SCALE * bounds.width);
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
label = eek_keysym_to_string (keysym);
if (!label)
label = empty_label;
/* Compute the layout extents. */
buffer = pango_layout_copy (layout);
draw_text_on_layout (buffer, label, 1.0);
pango_layout_get_extents (buffer, NULL, &logical_rect);
scale_x = scale_y = 1.0;
if (PANGO_PIXELS(logical_rect.width) > bounds.width)
scale_x = bounds.width / PANGO_PIXELS(logical_rect.width);
if (PANGO_PIXELS(logical_rect.height) > bounds.height)
scale_y = bounds.height / PANGO_PIXELS(logical_rect.height);
g_object_unref (buffer);
/* Actually draw on the layout */
draw_text_on_layout (layout,
label,
(scale_x < scale_y ? scale_x : scale_y) * 0.8);
eek_draw_text_on_layout (layout, label);
if (label != empty_label)
g_free ((gpointer)label);
}
/*
* The functions below are borrowed from
* libgnomekbd/gkbd-keyboard-drawing.c.
* Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
*
* 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);
}
static void
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);
}

View File

@ -59,7 +59,9 @@ eek_container_real_add_child (EekContainer *self,
g_return_if_fail (EEK_IS_ELEMENT(child));
g_object_ref_sink (child);
priv->children = g_slist_prepend (priv->children, child);
EEK_ELEMENT_GET_CLASS(child)->set_parent (child, self);
}
static void
@ -74,6 +76,7 @@ eek_container_real_remove_child (EekContainer *self,
g_return_if_fail (head);
g_object_unref (child);
priv->children = g_slist_remove_link (priv->children, head);
EEK_ELEMENT_GET_CLASS(child)->set_parent (child, NULL);
}
static void
@ -121,7 +124,6 @@ static void
eek_container_finalize (GObject *object)
{
EekContainerPrivate *priv = EEK_CONTAINER_GET_PRIVATE(object);
GSList *head;
g_slist_free (priv->children);
G_OBJECT_CLASS(eek_container_parent_class)->finalize (object);

View File

@ -31,7 +31,6 @@ G_BEGIN_DECLS
#define EEK_IS_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_CONTAINER))
#define EEK_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_CONTAINER, EekContainerClass))
typedef struct _EekContainer EekContainer;
typedef struct _EekContainerClass EekContainerClass;
typedef struct _EekContainerPrivate EekContainerPrivate;

399
eek/eek-drawing.c Normal file
View File

@ -0,0 +1,399 @@
/*
* Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
* Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
* 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 <math.h>
#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;
guint *keysyms;
gint num_groups, num_levels, i;
gdouble font_size;
eek_key_get_keysyms (key, &keysyms, &num_groups, &num_levels);
for (i = 0; i < num_groups * num_levels; i++) {
guint keysym = keysyms[i];
EekBounds bounds;
const gchar *label;
if (keysym == EEK_INVALID_KEYSYM ||
eek_keysym_get_category (keysym) != data->category)
continue;
eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
label = eek_keysym_to_string (keysym);
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;
}
}
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;
}
/*
* The functions below are borrowed from
* libgnomekbd/gkbd-keyboard-drawing.c.
* Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
*
* 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);
}

26
eek/eek-drawing.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef EEK_DRAWING_H
#define EEK_DRAWING_H 1
#include <cairo/cairo.h>
#include <pango/pango.h>
#include "eek-keyboard.h"
#include "eek-keysym.h"
#include "eek-types.h"
G_BEGIN_DECLS
void eek_draw_text_on_layout (PangoLayout *layout,
const gchar *text);
void eek_get_fonts (EekKeyboard *keyboard,
PangoLayout *layout,
PangoFontDescription **fonts);
void eek_draw_rounded_polygon (cairo_t *cr,
gboolean filled,
gdouble radius,
EekPoint *points,
gint num_points);
G_END_DECLS
#endif /* EEK_DRAWING_H */

View File

@ -33,6 +33,7 @@
#endif /* HAVE_CONFIG_H */
#include "eek-element.h"
#include "eek-container.h"
enum {
PROP_0,
@ -51,14 +52,37 @@ struct _EekElementPrivate
{
gchar *name;
EekBounds bounds;
EekContainer *parent;
};
static void
eek_element_real_set_name (EekElement *self,
const gchar *name)
eek_element_real_set_parent (EekElement *self,
EekContainer *parent)
{
EekElementPrivate *priv = EEK_ELEMENT_GET_PRIVATE(self);
if (parent) {
g_return_if_fail (EEK_IS_CONTAINER(parent));
g_object_ref_sink (G_OBJECT(parent));
} else if (priv->parent)
g_object_unref (G_OBJECT (priv->parent));
priv->parent = parent;
}
static EekContainer *
eek_element_real_get_parent (EekElement *self)
{
EekElementPrivate *priv = EEK_ELEMENT_GET_PRIVATE(self);
return priv->parent;
}
static void
eek_element_real_set_name (EekElement *self,
const gchar *name)
{
EekElementPrivate *priv = EEK_ELEMENT_GET_PRIVATE(self);
g_free (priv->name);
priv->name = g_strdup (name);
g_object_notify (G_OBJECT(self), "name");
@ -93,6 +117,17 @@ eek_element_real_get_bounds (EekElement *self,
g_object_notify (G_OBJECT(self), "bounds");
}
static void
eek_element_dispose (GObject *object)
{
EekElementPrivate *priv = EEK_ELEMENT_GET_PRIVATE(object);
if (priv->parent) {
g_object_unref (G_OBJECT(priv->parent));
priv->parent = NULL;
}
}
static void
eek_element_finalize (GObject *object)
{
@ -155,6 +190,8 @@ eek_element_class_init (EekElementClass *klass)
g_type_class_add_private (gobject_class,
sizeof (EekElementPrivate));
klass->set_parent = eek_element_real_set_parent;
klass->get_parent = eek_element_real_get_parent;
klass->set_name = eek_element_real_set_name;
klass->get_name = eek_element_real_get_name;
klass->set_bounds = eek_element_real_set_bounds;
@ -163,6 +200,7 @@ eek_element_class_init (EekElementClass *klass)
gobject_class->set_property = eek_element_set_property;
gobject_class->get_property = eek_element_get_property;
gobject_class->finalize = eek_element_finalize;
gobject_class->dispose = eek_element_dispose;
/**
* EekElement:name:

View File

@ -31,7 +31,6 @@ G_BEGIN_DECLS
#define EEK_IS_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_ELEMENT))
#define EEK_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_ELEMENT, EekElementClass))
typedef struct _EekElement EekElement;
typedef struct _EekElementClass EekElementClass;
typedef struct _EekElementPrivate EekElementPrivate;
@ -48,16 +47,19 @@ struct _EekElementClass
/*< private >*/
GInitiallyUnownedClass parent_class;
void (* set_name) (EekElement *element,
const gchar *name);
void (* set_parent) (EekElement *self,
EekContainer *parent);
EekContainer *(* get_parent) (EekElement *self);
void (* set_name) (EekElement *self,
const gchar *name);
G_CONST_RETURN gchar *(* get_name) (EekElement *element);
G_CONST_RETURN gchar *(* get_name) (EekElement *self);
void (* set_bounds) (EekElement *element,
EekBounds *bounds);
void (* set_bounds) (EekElement *self,
EekBounds *bounds);
void (* get_bounds) (EekElement *element,
EekBounds *bounds);
void (* get_bounds) (EekElement *self,
EekBounds *bounds);
};
GType eek_element_get_type (void) G_GNUC_CONST;

259
eek/eek-gtk-keyboard.c Normal file
View File

@ -0,0 +1,259 @@
/*
* Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
* 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
*/
/**
* SECTION:eek-gtk-keyboard
* @short_description: #EekKeyboard embedding a #GtkActor
*/
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "eek-gtk-keyboard.h"
#include "eek-drawing.h"
#include "eek-keyboard.h"
#include "eek-section.h"
#include "eek-key.h"
#include "eek-keysym.h"
G_DEFINE_TYPE (EekGtkKeyboard, eek_gtk_keyboard, EEK_TYPE_KEYBOARD);
#define EEK_GTK_KEYBOARD_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_GTK_KEYBOARD, EekGtkKeyboardPrivate))
struct _EekGtkKeyboardPrivate
{
GtkWidget *widget; /* GtkDrawingArea */
GdkPixmap *pixmap;
GdkColor *dark_color;
cairo_t *cr;
PangoLayout *layout;
PangoFontDescription *fonts[EEK_KEYSYM_CATEGORY_LAST];
};
static void
eek_gtk_keyboard_dispose (GObject *object)
{
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(object);
if (priv->widget) {
g_object_unref (priv->widget);
priv->widget = NULL;
}
G_OBJECT_CLASS (eek_gtk_keyboard_parent_class)->dispose (object);
}
static void
eek_gtk_keyboard_finalize (GObject *object)
{
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(object);
gint i;
for (i = 0; i < EEK_KEYSYM_CATEGORY_LAST; i++)
pango_font_description_free (priv->fonts[i]);
}
static void
eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (gobject_class,
sizeof (EekGtkKeyboardPrivate));
gobject_class->dispose = eek_gtk_keyboard_dispose;
gobject_class->finalize = eek_gtk_keyboard_finalize;
}
static void
eek_gtk_keyboard_init (EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv;
priv = self->priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
priv->widget = NULL;
priv->pixmap = NULL;
memset (priv->fonts, 0, sizeof priv->fonts);
}
/**
* eek_gtk_keyboard_new:
*
* Create a new #EekGtkKeyboard.
*/
EekKeyboard*
eek_gtk_keyboard_new (void)
{
return g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL);
}
static void
draw_key (EekElement *element, gpointer user_data)
{
EekKeyboard *keyboard = user_data;
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(keyboard);
EekKey *key = EEK_KEY(element);
EekOutline *outline;
EekBounds bounds;
guint keysym;
gdk_cairo_set_source_color (priv->cr, priv->dark_color);
cairo_set_line_width (priv->cr, 1);
cairo_set_line_join (priv->cr, CAIRO_LINE_JOIN_ROUND);
eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
cairo_save (priv->cr);
cairo_translate (priv->cr, bounds.x, bounds.y);
outline = eek_key_get_outline (key);
eek_draw_rounded_polygon (priv->cr,
FALSE,
outline->corner_radius,
outline->points,
outline->num_points);
cairo_stroke (priv->cr);
keysym = eek_key_get_keysym (key);
if (keysym != EEK_INVALID_KEYSYM) {
const gchar *label = eek_keysym_to_string (keysym);
PangoRectangle logical_rect = { 0, };
EekKeysymCategory category = eek_keysym_get_category (keysym);
if (category != EEK_KEYSYM_CATEGORY_UNKNOWN && label) {
pango_layout_set_font_description (priv->layout,
priv->fonts[category]);
pango_layout_set_text (priv->layout, label, -1);
pango_layout_get_extents (priv->layout, NULL, &logical_rect);
cairo_move_to
(priv->cr,
(bounds.width - logical_rect.width / PANGO_SCALE) / 2,
(bounds.height - logical_rect.height / PANGO_SCALE) / 2);
pango_layout_set_width (priv->layout, PANGO_SCALE * bounds.width);
pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END);
pango_cairo_show_layout (priv->cr, priv->layout);
}
}
cairo_restore (priv->cr);
}
static void
draw_section (EekElement *element, gpointer user_data)
{
EekKeyboard *keyboard = user_data;
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(keyboard);
EekBounds bounds;
gdk_cairo_set_source_color (priv->cr, priv->dark_color);
eek_element_get_bounds (element, &bounds);
cairo_save (priv->cr);
cairo_translate (priv->cr, bounds.x, bounds.y);
#if 0
cairo_rectangle (priv->cr, 0, 0, bounds.width, bounds.height);
cairo_stroke (priv->cr);
#endif
eek_container_foreach_child (EEK_CONTAINER(element), draw_key,
keyboard);
cairo_restore (priv->cr);
}
static gboolean
on_gtk_expose_event (GtkWidget *widget,
GdkEventExpose *event,
gpointer user_data)
{
EekGtkKeyboard *keyboard = user_data;
EekGtkKeyboardPrivate *priv =
EEK_GTK_KEYBOARD_GET_PRIVATE(keyboard);
GtkStateType state = gtk_widget_get_state (GTK_WIDGET (widget));
if (!priv->pixmap) {
GtkStateType state = gtk_widget_get_state (GTK_WIDGET (priv->widget));
GtkAllocation allocation;
PangoContext *context;
PangoFontDescription *default_font_desc;
context = gtk_widget_get_pango_context (GTK_WIDGET (priv->widget));
priv->layout = pango_layout_new (context);
/* compute font sizes */
default_font_desc =
gtk_widget_get_style (GTK_WIDGET(priv->widget))->font_desc;
pango_layout_set_font_description (priv->layout, default_font_desc);
eek_get_fonts (EEK_KEYBOARD(keyboard), priv->layout, priv->fonts);
/* create priv->pixmap */
gtk_widget_set_double_buffered (GTK_WIDGET (priv->widget), FALSE);
gtk_widget_get_allocation (GTK_WIDGET (priv->widget), &allocation);
priv->pixmap =
gdk_pixmap_new (gtk_widget_get_window (GTK_WIDGET (priv->widget)),
allocation.width, allocation.height, -1);
/* blank background */
gdk_draw_rectangle
(priv->pixmap,
gtk_widget_get_style (GTK_WIDGET(priv->widget))->base_gc[state],
TRUE,
0, 0, allocation.width, allocation.height);
/* draw sections on the canvas */
priv->cr = gdk_cairo_create (GDK_DRAWABLE (priv->pixmap));
priv->dark_color =
&gtk_widget_get_style (GTK_WIDGET (priv->widget))->dark[state];
eek_container_foreach_child (EEK_CONTAINER(keyboard), draw_section,
keyboard);
cairo_destroy (priv->cr);
priv->cr = NULL;
priv->dark_color = NULL;
}
gdk_draw_drawable (gtk_widget_get_window (widget),
gtk_widget_get_style (widget)->fg_gc[state],
priv->pixmap,
event->area.x, event->area.y,
event->area.x, event->area.y,
event->area.width, event->area.height);
return TRUE;
}
GtkWidget *
eek_gtk_keyboard_get_widget (EekGtkKeyboard *keyboard)
{
EekGtkKeyboardPrivate *priv =
EEK_GTK_KEYBOARD_GET_PRIVATE(keyboard);
if (!priv->widget) {
priv->widget = gtk_drawing_area_new ();
g_object_ref_sink (priv->widget);
g_signal_connect (priv->widget, "expose_event",
G_CALLBACK (on_gtk_expose_event), keyboard);
eek_keyboard_realize (EEK_KEYBOARD(keyboard));
}
return priv->widget;
}

57
eek/eek-gtk-keyboard.h Normal file
View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
* 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
*/
#ifndef EEK_GTK_KEYBOARD_H
#define EEK_GTK_KEYBOARD_H 1
#include <gtk/gtk.h>
#include "eek-keyboard.h"
G_BEGIN_DECLS
#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
#define EEK_GTK_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_GTK_KEYBOARD, EekGtkKeyboard))
#define EEK_GTK_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_GTK_KEYBOARD, EekGtkKeyboardClass))
#define EEK_IS_GTK_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_GTK_KEYBOARD))
#define EEK_IS_GTK_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_GTK_KEYBOARD))
#define EEK_GTK_KEYBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_GTK_KEYBOARD, EekGtkKeyboardClass))
typedef struct _EekGtkKeyboard EekGtkKeyboard;
typedef struct _EekGtkKeyboardClass EekGtkKeyboardClass;
typedef struct _EekGtkKeyboardPrivate EekGtkKeyboardPrivate;
struct _EekGtkKeyboard
{
/*< private >*/
EekKeyboard parent;
EekGtkKeyboardPrivate *priv;
};
struct _EekGtkKeyboardClass
{
/*< private >*/
EekKeyboardClass parent_class;
};
GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
EekKeyboard *eek_gtk_keyboard_new (void);
GtkWidget *eek_gtk_keyboard_get_widget (EekGtkKeyboard *keyboard);
G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */

26
eek/eek-gtk.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
* 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
*/
#ifndef EEK_GTK_H
#define EEK_GTK_H 1
#include "eek.h"
#include "eek-gtk-keyboard.h"
#endif /* EEK_GTK_H */

View File

@ -17,8 +17,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef EEK_KEYSYMS_H
#define EEK_KEYSYMS_H 1
#ifndef EEK_KEYSYM_H
#define EEK_KEYSYM_H 1
#define EEK_INVALID_KEYSYM ((guint)(-1))
#define EEK_INVALID_KEYCODE ((guint)(-1))
@ -27,10 +27,11 @@ typedef enum {
EEK_KEYSYM_CATEGORY_LETTER,
EEK_KEYSYM_CATEGORY_FUNCTION,
EEK_KEYSYM_CATEGORY_KEYNAME,
EEK_KEYSYM_CATEGORY_UNKNOWN
EEK_KEYSYM_CATEGORY_UNKNOWN,
EEK_KEYSYM_CATEGORY_LAST = EEK_KEYSYM_CATEGORY_UNKNOWN
} EekKeysymCategory;
G_CONST_RETURN gchar *eek_keysym_to_string (guint keysym);
EekKeysymCategory eek_keysym_get_category (guint keysym);
#endif /* EEK_KEYSYMS_H */
#endif /* EEK_KEYSYM_H */

View File

@ -30,6 +30,8 @@ typedef enum {
EEK_ORIENTATION_INVALID = -1
} EekOrientation;
typedef struct _EekElement EekElement;
typedef struct _EekContainer EekContainer;
typedef struct _EekKey EekKey;
typedef struct _EekSection EekSection;
typedef struct _EekKeyboard EekKeyboard;
@ -91,6 +93,12 @@ typedef struct _EekBounds EekBounds;
#define EEK_TYPE_BOUNDS (eek_bounds_get_type ())
GType eek_bounds_get_type (void) G_GNUC_CONST;
G_INLINE_FUNC gdouble
eek_bounds_long_side (EekBounds *bounds)
{
return bounds->width > bounds->height ? bounds->width : bounds->height;
}
/**
* EekOutline:
* @corner_radius: radius of corners of rounded polygon

View File

@ -21,6 +21,8 @@
#define EEK_H 1
#include "eek-keyboard.h"
#include "eek-section.h"
#include "eek-key.h"
#include "eek-layout.h"
#include "eek-keysym.h"

View File

@ -16,6 +16,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
bin_PROGRAMS = eekboard
bin_PROGRAMS = eekboard eekboard-gtk
eekboard_CFLAGS = -I$(top_srcdir) $(GOBJECT2_CFLAGS) $(CLUTTER_CFLAGS) $(CLUTTER_GTK_CFLAGS) $(GTK2_CFLAGS) $(XKB_CFLAGS) $(LIBXKLAVIER_CFLAGS) $(LIBFAKEKEY_CFLAGS)
eekboard_LDFLAGS = $(top_builddir)/eek/libeek.la $(top_builddir)/eek/libeek-xkl.la $(top_builddir)/eek/libeek-clutter.la $(GOBJECT2_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(GTK2_CFLAGS) $(XKB_LIBS) $(LIBXKLAVIER_LIBS) $(LIBFAKEKEY_LIBS)
eekboard_gtk_CFLAGS = -I$(top_srcdir) $(GOBJECT2_CFLAGS) $(GTK2_CFLAGS) $(XKB_CFLAGS) $(LIBXKLAVIER_CFLAGS) $(LIBFAKEKEY_CFLAGS)
eekboard_gtk_LDFLAGS = $(top_builddir)/eek/libeek.la $(top_builddir)/eek/libeek-xkl.la $(top_builddir)/eek/libeek-gtk.la $(GOBJECT2_LIBS) $(GTK2_CFLAGS) $(XKB_LIBS) $(LIBXKLAVIER_LIBS) $(LIBFAKEKEY_LIBS)

484
src/eekboard-gtk.c Normal file
View File

@ -0,0 +1,484 @@
/*
* Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fakekey/fakekey.h>
#include <glib/gi18n.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <libxklavier/xklavier.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "eek/eek-gtk.h"
#include "eek/eek-xkl.h"
#define CSW 640
#define CSH 480
#define LICENSE \
"This program is free software: you can redistribute it and/or modify " \
"it under the terms of the GNU General Public License as published by " \
"the Free Software Foundation, either version 3 of the License, or " \
"(at your option) any later version." \
"\n\n" \
"This program 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 General Public License for more details." \
"\n\n" \
"You should have received a copy of the GNU General Public License " \
"along with this program. If not, see <http://www.gnu.org/licenses/>. " \
struct _EekBoard {
EekKeyboard *keyboard;
EekLayout *layout; /* FIXME: eek_keyboard_get_layout() */
Display *display;
FakeKey *fakekey;
guint modifiers;
gfloat width, height;
};
typedef struct _EekBoard EekBoard;
static void on_about (GtkAction * action, GtkWidget *window);
static void on_monitor_key_event_toggled (GtkToggleAction *action,
GtkWidget *window);
static const char ui_description[] =
"<ui>"
" <menubar name='MainMenu'>"
" <menu action='FileMenu'>"
" <menuitem action='Quit'/>"
" </menu>"
" <menu action='KeyboardMenu'>"
" <menuitem action='MonitorKeyEvent'/>"
" <menu action='SetLayout'>"
" <placeholder name='LayoutsPH'/>"
" </menu>"
" </menu>"
" <menu action='HelpMenu'>"
" <menuitem action='About'/>"
" </menu>"
" </menubar>"
"</ui>";
#define SET_LAYOUT_UI_PATH "/MainMenu/KeyboardMenu/SetLayout/LayoutsPH"
struct _ConfigCallbackData {
EekBoard *eekboard;
XklConfigRec *config;
};
typedef struct _ConfigCallbackData ConfigCallbackData;
struct _LayoutCallbackData {
EekBoard *eekboard;
GtkUIManager *ui_manager;
GtkActionGroup *action_group;
guint merge_id;
};
typedef struct _LayoutCallbackData LayoutCallbackData;
static const GtkActionEntry action_entry[] = {
{"FileMenu", NULL, N_("_File")},
{"KeyboardMenu", NULL, N_("_Keyboard")},
{"HelpMenu", NULL, N_("_Help")},
{"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (gtk_main_quit)},
{"SetLayout", NULL, N_("Set Layout"), NULL, NULL, NULL},
{"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK (on_about)}
};
static const GtkToggleActionEntry toggle_action_entry[] = {
{"MonitorKeyEvent", NULL, N_("Monitor Key Typing"), NULL, NULL,
G_CALLBACK(on_monitor_key_event_toggled), FALSE}
};
static void
on_about (GtkAction * action, GtkWidget *window)
{
const gchar *authors[] = { "Daiki Ueno", NULL };
gtk_show_about_dialog (GTK_WINDOW (window),
"version", VERSION,
"copyright",
"Copyright \xc2\xa9 2010 Daiki Ueno\n"
"Copyright \xc2\xa9 2010 Red Hat, Inc.",
"license", LICENSE,
"comments",
_("A virtual keyboard for GNOME"),
"authors", authors,
"website",
"http://github.com/ueno/eek/",
"website-label", _("EekBoard web site"),
"wrap-license", TRUE, NULL);
}
static void
on_monitor_key_event_toggled (GtkToggleAction *action,
GtkWidget *window)
{
g_warning ("not implemented");
}
static void
on_key_pressed (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data)
{
EekBoard *eekboard = user_data;
guint keysym;
keysym = eek_key_get_keysym (key);
#if DEBUG
g_debug ("%s %X", eek_keysym_to_string (keysym), eekboard->modifiers);
#endif
fakekey_press_keysym (eekboard->fakekey, keysym, eekboard->modifiers);
if (keysym == XK_Shift_L || keysym == XK_Shift_R) {
gint group, level;
eekboard->modifiers ^= ShiftMask;
eek_keyboard_get_keysym_index (keyboard, &group, &level);
eek_keyboard_set_keysym_index (keyboard, group,
eekboard->modifiers & ShiftMask ? 1 : 0);
} else if (keysym == XK_Control_L || keysym == XK_Control_R) {
eekboard->modifiers ^= ControlMask;
} else if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
eekboard->modifiers ^= Mod1Mask;
}
}
static void
on_key_released (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data)
{
EekBoard *eekboard = user_data;
fakekey_release (eekboard->fakekey);
}
static void
on_activate (GtkAction *action, gpointer user_data)
{
ConfigCallbackData *data = user_data;
eek_xkl_layout_set_config (EEK_XKL_LAYOUT(data->eekboard->layout),
data->config);
g_object_unref (data->config);
}
#if 0
static void
create_keyboard (EekBoard *eekboard,
ClutterActor *stage,
EekLayout *layout,
gfloat initial_width,
gfloat initial_height)
{
ClutterActor *actor;
eekboard->keyboard = eek_clutter_keyboard_new (initial_width,
initial_height);
g_signal_connect (eekboard->keyboard, "key-pressed",
G_CALLBACK(on_key_pressed), eekboard);
g_signal_connect (eekboard->keyboard, "key-released",
G_CALLBACK(on_key_released), eekboard);
eek_keyboard_set_layout (eekboard->keyboard, layout);
actor = eek_clutter_keyboard_get_actor
(EEK_CLUTTER_KEYBOARD(eekboard->keyboard));
clutter_actor_get_size (actor,
&eekboard->width,
&eekboard->height);
clutter_container_add_actor (CLUTTER_CONTAINER(stage), actor);
clutter_actor_set_size (stage,
eekboard->width,
eekboard->height);
}
/* FIXME: EekKeyboard should handle relayout by itself. */
static void
on_changed (EekLayout *layout, gpointer user_data)
{
EekBoard *eekboard = user_data;
ClutterActor *stage, *actor;
gfloat width, height;
actor = eek_clutter_keyboard_get_actor
(EEK_CLUTTER_KEYBOARD(eekboard->keyboard));
stage = clutter_actor_get_stage (actor);
clutter_actor_get_size (stage, &width, &height);
clutter_container_remove_actor (CLUTTER_CONTAINER(stage), actor);
g_object_unref (eekboard->keyboard);
create_keyboard (eekboard, stage, layout, width, height);
clutter_actor_get_size (stage, &eekboard->width, &eekboard->height);
}
#endif
static void
variant_callback (XklConfigRegistry *registry,
const XklConfigItem *item,
gpointer user_data)
{
GSList **r_variants = user_data;
XklConfigItem *_item;
_item = g_slice_dup (XklConfigItem, item);
*r_variants = g_slist_prepend (*r_variants, _item);
}
static void
layout_callback (XklConfigRegistry *registry,
const XklConfigItem *item,
gpointer user_data)
{
LayoutCallbackData *data = user_data;
GtkAction *action;
GSList *variants = NULL;
char layout_action_name[128], variant_action_name[128];
ConfigCallbackData *config;
g_snprintf (layout_action_name, sizeof (layout_action_name),
"SetLayout%s", item->name);
action = gtk_action_new (layout_action_name, item->description, NULL, NULL);
gtk_action_group_add_action (data->action_group, action);
xkl_config_registry_foreach_layout_variant (registry,
item->name,
variant_callback,
&variants);
if (!variants) {
config = g_slice_new (ConfigCallbackData);
config->eekboard = data->eekboard;
config->config = xkl_config_rec_new ();
config->config->layouts = g_new0 (char *, 2);
config->config->layouts[0] = g_strdup (item->name);
config->config->layouts[1] = NULL;
config->config->variants = NULL;
g_signal_connect (action, "activate", G_CALLBACK (on_activate),
config);
g_object_unref (action);
gtk_ui_manager_add_ui (data->ui_manager, data->merge_id,
SET_LAYOUT_UI_PATH,
layout_action_name, layout_action_name,
GTK_UI_MANAGER_MENUITEM, FALSE);
} else {
char layout_path[128];
GSList *head;
g_object_unref (action);
gtk_ui_manager_add_ui (data->ui_manager, data->merge_id,
SET_LAYOUT_UI_PATH,
layout_action_name, layout_action_name,
GTK_UI_MANAGER_MENU, FALSE);
g_snprintf (layout_path, sizeof (layout_path),
SET_LAYOUT_UI_PATH "/%s", layout_action_name);
for (head = variants; head; head = head->next) {
XklConfigItem *_item = head->data;
g_snprintf (variant_action_name, sizeof (variant_action_name),
"SetVariant%s%s", item->name, _item->name);
action = gtk_action_new (variant_action_name,
_item->description,
NULL,
NULL);
config = g_slice_new (ConfigCallbackData);
config->eekboard = data->eekboard;
config->config = xkl_config_rec_new ();
config->config->layouts = g_new0 (char *, 2);
config->config->layouts[0] = g_strdup (item->name);
config->config->layouts[1] = NULL;
config->config->variants = g_new0 (char *, 2);
config->config->variants[0] = g_strdup (_item->name);
config->config->variants[1] = NULL;
g_signal_connect (action, "activate", G_CALLBACK (on_activate),
config);
gtk_action_group_add_action (data->action_group, action);
g_object_unref (action);
gtk_ui_manager_add_ui (data->ui_manager, data->merge_id,
layout_path,
variant_action_name, variant_action_name,
GTK_UI_MANAGER_MENUITEM, FALSE);
g_slice_free (XklConfigItem, _item);
}
g_slist_free (variants);
}
}
static void
create_layouts_menu (EekBoard *eekboard, GtkUIManager *ui_manager)
{
XklEngine *engine;
XklConfigRegistry *registry;
LayoutCallbackData data;
data.eekboard = eekboard;
data.ui_manager = ui_manager;
data.action_group = gtk_action_group_new ("Layouts");
gtk_ui_manager_insert_action_group (data.ui_manager, data.action_group, -1);
data.merge_id = gtk_ui_manager_new_merge_id (ui_manager);
engine = xkl_engine_get_instance (eekboard->display);
registry = xkl_config_registry_get_instance (engine);
xkl_config_registry_load (registry, FALSE);
xkl_config_registry_foreach_layout (registry, layout_callback, &data);
}
static void
create_menus (EekBoard *eekboard,
GtkWidget *window,
GtkUIManager * ui_manager)
{
GtkActionGroup *action_group;
action_group = gtk_action_group_new ("MenuActions");
gtk_action_group_add_actions (action_group, action_entry,
G_N_ELEMENTS (action_entry), window);
gtk_action_group_add_toggle_actions (action_group, toggle_action_entry,
G_N_ELEMENTS (toggle_action_entry),
window);
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, NULL);
create_layouts_menu (eekboard, ui_manager);
}
#if 0
static void
on_resize (GObject *object,
GParamSpec *param_spec,
gpointer user_data)
{
EekBoard *eekboard = user_data;
GValue value = {0};
gfloat width, height, scale;
ClutterActor *stage, *actor;
actor = eek_clutter_keyboard_get_actor
(EEK_CLUTTER_KEYBOARD(eekboard->keyboard));
stage = clutter_actor_get_stage (actor);
g_object_get (G_OBJECT(stage), "width", &width, NULL);
g_object_get (G_OBJECT(stage), "height", &height, NULL);
g_value_init (&value, G_TYPE_DOUBLE);
scale = width > height ? width / eekboard->width :
width / eekboard->height;
g_value_set_double (&value, scale);
g_object_set_property (G_OBJECT (stage),
"scale-x",
&value);
g_value_set_double (&value, scale);
g_object_set_property (G_OBJECT (stage),
"scale-y",
&value);
}
#endif
int
main (int argc, char *argv[])
{
EekBoard eekboard;
GtkWidget *menubar, *embed, *vbox, *window;
GtkUIManager *ui_manager;
EekBounds bounds;
if (!gtk_init_check (&argc, &argv)) {
fprintf (stderr, "Can't init Clutter-Gtk\n");
exit (1);
}
memset (&eekboard, 0, sizeof eekboard);
eekboard.display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
if (!eekboard.display) {
fprintf (stderr, "Can't open display\n");
exit (1);
}
eekboard.fakekey = fakekey_init (eekboard.display);
if (!eekboard.fakekey) {
fprintf (stderr, "Can't init fakekey\n");
exit (1);
}
eekboard.layout = eek_xkl_layout_new ();
if (!eekboard.layout) {
fprintf (stderr, "Failed to create layout\n");
exit (1);
}
#if 0
g_signal_connect (eekboard.layout,
"changed",
G_CALLBACK(on_changed),
&eekboard);
g_signal_connect (stage,
"notify::width",
G_CALLBACK (on_resize),
&eekboard);
g_signal_connect (stage,
"notify::height",
G_CALLBACK (on_resize),
&eekboard);
#endif
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_can_focus (window, FALSE);
g_object_set (G_OBJECT(window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(window), "Keyboard");
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
bounds.x = bounds.y = 0;
bounds.width = CSW;
bounds.height = CSH;
eekboard.keyboard = eek_gtk_keyboard_new ();
eek_element_set_bounds (eekboard.keyboard, &bounds);
eek_keyboard_set_layout (eekboard.keyboard, eekboard.layout);
embed = eek_gtk_keyboard_get_widget (EEK_GTK_KEYBOARD (eekboard.keyboard));
vbox = gtk_vbox_new (FALSE, 0);
ui_manager = gtk_ui_manager_new ();
create_menus (&eekboard, window, ui_manager);
menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER(vbox), embed);
gtk_container_add (GTK_CONTAINER(window), vbox);
gtk_widget_set_size_request (embed, CSW, CSH);
gtk_widget_show_all (window);
//gtk_widget_set_size_request (embed, -1, -1);
gtk_main ();
return 0;
}