856 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			856 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 
 | 
						|
 * 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
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * SECTION:eek-xkb-layout
 | 
						|
 * @short_description: Layout engine using XKB configuration
 | 
						|
 *
 | 
						|
 * The #EekXkbLayout inherits #EekLayout class and arranges keyboard
 | 
						|
 * elements using XKB.
 | 
						|
 */
 | 
						|
 | 
						|
#include <gdk/gdkx.h>
 | 
						|
#include <X11/XKBlib.h>
 | 
						|
#include <X11/extensions/XKBgeom.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include "config.h"
 | 
						|
#endif  /* HAVE_CONFIG_H */
 | 
						|
 | 
						|
#include "eek-xkb-layout.h"
 | 
						|
#include "eek-keyboard.h"
 | 
						|
#include "eek-section.h"
 | 
						|
#include "eek-key.h"
 | 
						|
#include "eek-keysym.h"
 | 
						|
 | 
						|
#define noKBDRAW_DEBUG
 | 
						|
 | 
						|
static void eek_layout_iface_init (EekLayoutIface *iface);
 | 
						|
 | 
						|
G_DEFINE_TYPE_WITH_CODE (EekXkbLayout, eek_xkb_layout, G_TYPE_INITIALLY_UNOWNED,
 | 
						|
                         G_IMPLEMENT_INTERFACE (EEK_TYPE_LAYOUT,
 | 
						|
                                                eek_layout_iface_init));
 | 
						|
 | 
						|
#define EEK_XKB_LAYOUT_GET_PRIVATE(obj)                                  \
 | 
						|
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_XKB_LAYOUT, EekXkbLayoutPrivate))
 | 
						|
 | 
						|
enum {
 | 
						|
    PROP_0,
 | 
						|
    PROP_KEYCODES,
 | 
						|
    PROP_GEOMETRY,
 | 
						|
    PROP_SYMBOLS,
 | 
						|
    PROP_LAST
 | 
						|
};
 | 
						|
 | 
						|
struct _EekXkbLayoutPrivate
 | 
						|
{
 | 
						|
    /* Configuration names that should synch'ed to the symbolic names
 | 
						|
       in priv->xkb->names.  Since we use GLib's memory allocator,
 | 
						|
       don't store any address returned from the X server here. */
 | 
						|
    XkbComponentNamesRec names;
 | 
						|
 | 
						|
    Display *display;
 | 
						|
 | 
						|
    /* Actual XKB configuration of DISPLAY. */
 | 
						|
    XkbDescRec *xkb;
 | 
						|
 | 
						|
    /* Hash table to cache outlines by shape address. */
 | 
						|
    GHashTable *outline_hash;
 | 
						|
 | 
						|
    gint scale_numerator;
 | 
						|
    gint scale_denominator;
 | 
						|
};
 | 
						|
 | 
						|
static guint
 | 
						|
find_keycode (EekXkbLayout *layout, gchar *key_name);
 | 
						|
 | 
						|
static void
 | 
						|
get_keyboard (EekXkbLayout *layout);
 | 
						|
 | 
						|
static void
 | 
						|
get_names (EekXkbLayout *layout);
 | 
						|
 | 
						|
static void
 | 
						|
setup_scaling (EekXkbLayout *layout,
 | 
						|
               gdouble       width,
 | 
						|
               gdouble       height);
 | 
						|
 | 
						|
G_INLINE_FUNC gint
 | 
						|
xkb_to_pixmap_coord (EekXkbLayout *layout,
 | 
						|
                     gint          n)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
    return n * priv->scale_numerator / priv->scale_denominator;
 | 
						|
}
 | 
						|
 | 
						|
G_INLINE_FUNC gdouble
 | 
						|
xkb_to_pixmap_double (EekXkbLayout *layout,
 | 
						|
                     gdouble       d)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
    return d * priv->scale_numerator / priv->scale_denominator;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_key (EekXkbLayout *layout,
 | 
						|
            EekSection   *section,
 | 
						|
            gint          column,
 | 
						|
            gint          row,
 | 
						|
            gdouble       x,
 | 
						|
            gdouble       y,
 | 
						|
            XkbKeyRec    *xkbkey)
 | 
						|
{
 | 
						|
    XkbGeometryRec *xkbgeometry;
 | 
						|
    XkbBoundsRec *xkbbounds;
 | 
						|
    XkbShapeRec *xkbshape;
 | 
						|
    XkbOutlineRec *xkboutline;
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
    EekKey *key;
 | 
						|
    EekBounds bounds;
 | 
						|
    guint *keysyms = NULL;
 | 
						|
    gchar name[XkbKeyNameLength + 1];
 | 
						|
    EekOutline *outline;
 | 
						|
    KeyCode keycode;
 | 
						|
    gint num_groups, num_levels, num_keysyms;
 | 
						|
 | 
						|
    xkbgeometry = priv->xkb->geom;
 | 
						|
    xkbshape = &xkbgeometry->shapes[xkbkey->shape_ndx];
 | 
						|
    outline = g_hash_table_lookup (priv->outline_hash, xkbshape);
 | 
						|
    if (outline == NULL) {
 | 
						|
        xkboutline = xkbshape->primary == NULL ? &xkbshape->outlines[0] :
 | 
						|
            xkbshape->primary;
 | 
						|
 | 
						|
        outline = g_slice_new (EekOutline);
 | 
						|
        outline->corner_radius = xkb_to_pixmap_coord(layout, xkboutline->corner_radius);
 | 
						|
 | 
						|
        if (xkboutline->num_points <= 2) { /* rectangular */
 | 
						|
            gdouble x1, y1, x2, y2;
 | 
						|
 | 
						|
            outline->num_points = 4;
 | 
						|
            outline->points = g_slice_alloc0 (sizeof (EekPoint) *
 | 
						|
                                              outline->num_points);
 | 
						|
            if (xkboutline->num_points == 1) {
 | 
						|
                x1 = xkb_to_pixmap_coord(layout, xkbshape->bounds.x1);
 | 
						|
                y1 = xkb_to_pixmap_coord(layout, xkbshape->bounds.y1);
 | 
						|
                x2 = xkb_to_pixmap_coord(layout, xkboutline->points[0].x);
 | 
						|
                y2 = xkb_to_pixmap_coord(layout, xkboutline->points[0].y);
 | 
						|
            } else {
 | 
						|
                x1 = xkb_to_pixmap_coord(layout, xkboutline->points[0].x);
 | 
						|
                y1 = xkb_to_pixmap_coord(layout, xkboutline->points[0].y);
 | 
						|
                x2 = xkb_to_pixmap_coord(layout, xkboutline->points[1].x);
 | 
						|
                y2 = xkb_to_pixmap_coord(layout, xkboutline->points[1].y);
 | 
						|
            }
 | 
						|
            outline->points[0].x = outline->points[3].x = x1;
 | 
						|
            outline->points[0].y = outline->points[1].y = y1;
 | 
						|
            outline->points[1].x = outline->points[2].x = x2;
 | 
						|
            outline->points[2].y = outline->points[3].y = y2;
 | 
						|
        } else {                /* polygon */
 | 
						|
            gint i;
 | 
						|
 | 
						|
            outline->num_points = xkboutline->num_points;
 | 
						|
            outline->points = g_new0 (EekPoint, outline->num_points);
 | 
						|
            for (i = 0; i < xkboutline->num_points; i++) {
 | 
						|
                outline->points[i].x = xkb_to_pixmap_coord(layout, xkboutline->points[i].x);
 | 
						|
                outline->points[i].y = xkb_to_pixmap_coord(layout, xkboutline->points[i].y);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        g_hash_table_insert (priv->outline_hash, xkbshape, outline);
 | 
						|
    }
 | 
						|
 | 
						|
    memset (name, 0, sizeof name);
 | 
						|
    memcpy (name, xkbkey->name.name, sizeof name - 1);
 | 
						|
 | 
						|
    xkbbounds = &xkbgeometry->shapes[xkbkey->shape_ndx].bounds;
 | 
						|
    bounds.x = xkb_to_pixmap_coord(layout, xkbbounds->x1 + x);
 | 
						|
    bounds.y = xkb_to_pixmap_coord(layout, xkbbounds->y1 + y);
 | 
						|
    bounds.width = xkb_to_pixmap_coord(layout, xkbbounds->x2 - xkbbounds->x1);
 | 
						|
    bounds.height = xkb_to_pixmap_coord(layout, xkbbounds->y2 - xkbbounds->y1);
 | 
						|
 | 
						|
    keycode = find_keycode (layout, name);
 | 
						|
    if (keycode == EEK_INVALID_KEYCODE)
 | 
						|
        num_groups = num_levels = 0;
 | 
						|
    else {
 | 
						|
        KeySym keysym;
 | 
						|
        gint i, j;
 | 
						|
 | 
						|
        num_groups = XkbKeyNumGroups (priv->xkb, keycode);
 | 
						|
        num_levels = XkbKeyGroupsWidth (priv->xkb, keycode);
 | 
						|
        num_keysyms = num_groups * num_levels;
 | 
						|
        keysyms = g_slice_alloc0 (num_keysyms * sizeof(guint));
 | 
						|
        for (i = 0; i < num_groups; i++)
 | 
						|
            for (j = 0; j < num_levels; j++) {
 | 
						|
                keysym = XkbKeySymEntry (priv->xkb, keycode, j, i);
 | 
						|
                keysyms[i * num_levels + j] = keysym;
 | 
						|
            }
 | 
						|
    }
 | 
						|
 | 
						|
    key = eek_section_create_key (section, column, row);
 | 
						|
    eek_element_set_name (EEK_ELEMENT(key), name);
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(key), &bounds);
 | 
						|
    eek_key_set_keycode (key, keycode);
 | 
						|
    eek_key_set_keysyms (key, keysyms, num_groups, num_levels);
 | 
						|
    if (keysyms)
 | 
						|
        g_slice_free1 (num_keysyms * sizeof(guint), keysyms);
 | 
						|
    eek_key_set_keysym_index (key, 0, 0);
 | 
						|
    eek_key_set_outline (key, outline);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_section (EekXkbLayout  *layout,
 | 
						|
                EekKeyboard   *keyboard,
 | 
						|
                XkbSectionRec *xkbsection)
 | 
						|
{
 | 
						|
    XkbGeometryRec *xkbgeometry;
 | 
						|
    EekXkbLayoutPrivate *priv;
 | 
						|
    EekSection *section;
 | 
						|
    EekBounds bounds;
 | 
						|
    gchar *name;
 | 
						|
    gfloat left, top;
 | 
						|
    gint i, j;
 | 
						|
 | 
						|
    bounds.x = xkb_to_pixmap_coord(layout, xkbsection->left);
 | 
						|
    bounds.y = xkb_to_pixmap_coord(layout, xkbsection->top);
 | 
						|
    bounds.width = xkb_to_pixmap_coord(layout, xkbsection->width);
 | 
						|
    bounds.height = xkb_to_pixmap_coord(layout, xkbsection->height);
 | 
						|
 | 
						|
    priv = layout->priv;
 | 
						|
    xkbgeometry = priv->xkb->geom;
 | 
						|
    section = eek_keyboard_create_section (keyboard);
 | 
						|
    name = XGetAtomName (priv->display, xkbsection->name);
 | 
						|
    eek_element_set_name (EEK_ELEMENT(section), name);
 | 
						|
    XFree (name);
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(section), &bounds);
 | 
						|
    eek_section_set_angle (section,
 | 
						|
                           /* angle is in tenth of degree */
 | 
						|
                           xkbsection->angle / 10);
 | 
						|
 | 
						|
    for (i = 0; i < xkbsection->num_rows; i++) {
 | 
						|
        XkbRowRec *xkbrow;
 | 
						|
 | 
						|
        xkbrow = &xkbsection->rows[i];
 | 
						|
        left = xkbrow->left;
 | 
						|
        top = xkbrow->top;
 | 
						|
        eek_section_add_row (section,
 | 
						|
                             xkbrow->num_keys,
 | 
						|
                             xkbrow->vertical ?
 | 
						|
                             EEK_ORIENTATION_VERTICAL :
 | 
						|
                             EEK_ORIENTATION_HORIZONTAL);
 | 
						|
        for (j = 0; j < xkbrow->num_keys; j++) {
 | 
						|
            XkbKeyRec *xkbkey;
 | 
						|
            XkbBoundsRec *xkbbounds;
 | 
						|
 | 
						|
            xkbkey = &xkbrow->keys[j];
 | 
						|
            if (xkbrow->vertical)
 | 
						|
                top += xkbkey->gap;
 | 
						|
            else
 | 
						|
                left += xkbkey->gap;
 | 
						|
            create_key (layout, section, j, i, left, top, xkbkey);
 | 
						|
            xkbbounds = &xkbgeometry->shapes[xkbkey->shape_ndx].bounds;
 | 
						|
            if (xkbrow->vertical)
 | 
						|
                top += xkbbounds->y2 - xkbbounds->y1;
 | 
						|
            else
 | 
						|
                left += xkbbounds->x2 - xkbbounds->x1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_keyboard (EekXkbLayout *layout, EekKeyboard *keyboard)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
    XkbGeometryRec *xkbgeometry;
 | 
						|
    EekBounds bounds;
 | 
						|
    gint i;
 | 
						|
 | 
						|
    g_return_if_fail (priv->xkb);
 | 
						|
    g_return_if_fail (priv->xkb->geom);
 | 
						|
 | 
						|
    xkbgeometry = priv->xkb->geom;
 | 
						|
 | 
						|
    eek_element_get_bounds (EEK_ELEMENT(keyboard), &bounds);
 | 
						|
    setup_scaling (EEK_XKB_LAYOUT(layout), bounds.width, bounds.height);
 | 
						|
 | 
						|
    bounds.x = bounds.y = 0;
 | 
						|
    bounds.width = xkb_to_pixmap_coord(layout, xkbgeometry->width_mm);
 | 
						|
    bounds.height = xkb_to_pixmap_coord(layout, xkbgeometry->height_mm);
 | 
						|
 | 
						|
    for (i = 0; i < xkbgeometry->num_sections; i++) {
 | 
						|
        XkbSectionRec *xkbsection;
 | 
						|
 | 
						|
        xkbsection = &xkbgeometry->sections[i];
 | 
						|
        create_section (layout, keyboard, xkbsection);
 | 
						|
    }
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(keyboard), &bounds);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
outline_free (gpointer data)
 | 
						|
{
 | 
						|
    EekOutline *outline = data;
 | 
						|
    g_slice_free1 (sizeof (EekPoint) * outline->num_points, outline->points);
 | 
						|
    g_boxed_free (EEK_TYPE_OUTLINE, outline);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_real_apply (EekLayout *layout, EekKeyboard *keyboard)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
 | 
						|
    g_return_if_fail (priv);
 | 
						|
    if (priv->outline_hash)
 | 
						|
        g_hash_table_unref (priv->outline_hash);
 | 
						|
    priv->outline_hash = g_hash_table_new_full (g_direct_hash,
 | 
						|
                                                g_direct_equal,
 | 
						|
                                                NULL,
 | 
						|
                                                outline_free);
 | 
						|
    create_keyboard (EEK_XKB_LAYOUT(layout), keyboard);
 | 
						|
}
 | 
						|
 | 
						|
static gint
 | 
						|
compare_component_names (gchar *name0, gchar *name1)
 | 
						|
{
 | 
						|
    if (name0 && name1)
 | 
						|
        return g_strcmp0 (name0, name1);
 | 
						|
    else if (!name0 && name1)
 | 
						|
        return -1;
 | 
						|
    else if (name0 && !name1)
 | 
						|
        return 1;
 | 
						|
    else
 | 
						|
        return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_real_set_names (EekXkbLayout *self, XkbComponentNamesRec *names)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self);
 | 
						|
    gboolean is_changed;
 | 
						|
 | 
						|
    g_return_if_fail (priv);
 | 
						|
    is_changed =
 | 
						|
        compare_component_names (names->keycodes, priv->names.keycodes) != 0;
 | 
						|
    g_free (priv->names.keycodes);
 | 
						|
    priv->names.keycodes = g_strdup (names->keycodes);
 | 
						|
    is_changed =
 | 
						|
        compare_component_names (names->geometry, priv->names.geometry) != 0;
 | 
						|
    g_free (priv->names.geometry);
 | 
						|
    priv->names.geometry = g_strdup (names->geometry);
 | 
						|
    is_changed =
 | 
						|
        compare_component_names (names->symbols, priv->names.symbols) != 0;
 | 
						|
    g_free (priv->names.symbols);
 | 
						|
    priv->names.symbols = g_strdup (names->symbols);
 | 
						|
    get_keyboard (self);
 | 
						|
    if (is_changed)
 | 
						|
        g_signal_emit_by_name (self, "changed");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_finalize (GObject *object)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (object);
 | 
						|
 | 
						|
    g_free (priv->names.keycodes);
 | 
						|
    g_free (priv->names.geometry);
 | 
						|
    g_free (priv->names.symbols);
 | 
						|
    if (priv->outline_hash)
 | 
						|
        g_hash_table_unref (priv->outline_hash);
 | 
						|
    XkbFreeKeyboard (priv->xkb, 0, TRUE);	/* free_all = TRUE */
 | 
						|
    G_OBJECT_CLASS (eek_xkb_layout_parent_class)->finalize (object);
 | 
						|
}
 | 
						|
 | 
						|
static void 
 | 
						|
eek_xkb_layout_set_property (GObject      *object, 
 | 
						|
                               guint         prop_id,
 | 
						|
                               const GValue *value, 
 | 
						|
                               GParamSpec   *pspec)
 | 
						|
{
 | 
						|
    const gchar *name;
 | 
						|
 | 
						|
    switch (prop_id) 
 | 
						|
        {
 | 
						|
        case PROP_KEYCODES:
 | 
						|
            name = g_value_get_string (value);
 | 
						|
            eek_xkb_layout_set_keycodes (EEK_XKB_LAYOUT(object), name);
 | 
						|
            break;
 | 
						|
        case PROP_GEOMETRY:
 | 
						|
            name = g_value_get_string (value);
 | 
						|
            eek_xkb_layout_set_geometry (EEK_XKB_LAYOUT(object), name);
 | 
						|
            break;
 | 
						|
        case PROP_SYMBOLS:
 | 
						|
            name = g_value_get_string (value);
 | 
						|
            eek_xkb_layout_set_symbols (EEK_XKB_LAYOUT(object), name);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            g_object_set_property (object,
 | 
						|
                                   g_param_spec_get_name (pspec),
 | 
						|
                                   value);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
}
 | 
						|
 | 
						|
static void 
 | 
						|
eek_xkb_layout_get_property (GObject    *object,
 | 
						|
                             guint       prop_id,
 | 
						|
                             GValue     *value,
 | 
						|
                             GParamSpec *pspec)
 | 
						|
{
 | 
						|
    const gchar *name;
 | 
						|
 | 
						|
    switch (prop_id) 
 | 
						|
        {
 | 
						|
        case PROP_KEYCODES:
 | 
						|
            name = eek_xkb_layout_get_keycodes (EEK_XKB_LAYOUT(object));
 | 
						|
            g_value_set_string (value, name);
 | 
						|
            break;
 | 
						|
        case PROP_GEOMETRY:
 | 
						|
            name = eek_xkb_layout_get_geometry (EEK_XKB_LAYOUT(object));
 | 
						|
            g_value_set_string (value, name);
 | 
						|
            break;
 | 
						|
        case PROP_SYMBOLS:
 | 
						|
            name = eek_xkb_layout_get_symbols (EEK_XKB_LAYOUT(object));
 | 
						|
            g_value_set_string (value, name);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            g_object_get_property (object,
 | 
						|
                                   g_param_spec_get_name (pspec),
 | 
						|
                                   value);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
}
 | 
						|
 | 
						|
static gint
 | 
						|
eek_xkb_layout_real_get_group (EekLayout *self)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self);
 | 
						|
    XkbStateRec state;
 | 
						|
 | 
						|
    g_return_val_if_fail (XkbGetState (priv->display, XkbUseCoreKbd, &state), -1);
 | 
						|
    return state.group;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_layout_iface_init (EekLayoutIface *iface)
 | 
						|
{
 | 
						|
    iface->apply = eek_xkb_layout_real_apply;
 | 
						|
    iface->get_group = eek_xkb_layout_real_get_group;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_class_init (EekXkbLayoutClass *klass)
 | 
						|
{
 | 
						|
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
						|
    GParamSpec *pspec;
 | 
						|
 | 
						|
    g_type_class_add_private (gobject_class, sizeof (EekXkbLayoutPrivate));
 | 
						|
 | 
						|
    klass->set_names = eek_xkb_layout_real_set_names;
 | 
						|
    gobject_class->finalize = eek_xkb_layout_finalize;
 | 
						|
    gobject_class->set_property = eek_xkb_layout_set_property;
 | 
						|
    gobject_class->get_property = eek_xkb_layout_get_property;
 | 
						|
 | 
						|
    pspec = g_param_spec_string ("keycodes",
 | 
						|
				 "Keycodes",
 | 
						|
				 "XKB keycodes component name",
 | 
						|
				 NULL,
 | 
						|
				 G_PARAM_READWRITE);
 | 
						|
    g_object_class_install_property (gobject_class, PROP_KEYCODES, pspec);
 | 
						|
 | 
						|
    pspec = g_param_spec_string ("geometry",
 | 
						|
                                 "Geometry",
 | 
						|
                                 "XKB geometry component name",
 | 
						|
                                 NULL,
 | 
						|
                                 G_PARAM_READWRITE);
 | 
						|
    g_object_class_install_property (gobject_class, PROP_GEOMETRY, pspec);
 | 
						|
 | 
						|
    pspec = g_param_spec_string ("symbols",
 | 
						|
                                 "Symbols",
 | 
						|
                                 "XKB symbols component name",
 | 
						|
                                 NULL,
 | 
						|
                                 G_PARAM_READWRITE);
 | 
						|
    g_object_class_install_property (gobject_class, PROP_SYMBOLS, pspec);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_init (EekXkbLayout *self)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv;
 | 
						|
 | 
						|
    priv = self->priv = EEK_XKB_LAYOUT_GET_PRIVATE (self);
 | 
						|
    memset (&priv->names, 0, sizeof priv->names);
 | 
						|
 | 
						|
    priv->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 | 
						|
    g_return_if_fail (priv->display);
 | 
						|
 | 
						|
    /* XXX: XkbClientMapMask | XkbIndicatorMapMask | XkbNamesMask |
 | 
						|
       XkbGeometryMask */
 | 
						|
    priv->xkb = XkbGetKeyboard (priv->display,
 | 
						|
                                XkbGBN_GeometryMask |
 | 
						|
                                XkbGBN_KeyNamesMask |
 | 
						|
                                XkbGBN_OtherNamesMask |
 | 
						|
                                XkbGBN_SymbolsMask |
 | 
						|
                                XkbGBN_IndicatorMapMask,
 | 
						|
                                XkbUseCoreKbd);
 | 
						|
 | 
						|
    if (priv->xkb == NULL) {
 | 
						|
        g_critical ("XkbGetKeyboard failed to get keyboard from the server!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    get_names (self);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
get_names (EekXkbLayout *layout)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
    gchar *name;
 | 
						|
 | 
						|
    XkbGetNames (priv->display, XkbAllNamesMask, priv->xkb);
 | 
						|
 | 
						|
    if (priv->xkb->names->keycodes <= 0)
 | 
						|
        g_warning ("XKB keycodes setting is not loaded properly");
 | 
						|
    else {
 | 
						|
        name = XGetAtomName (priv->display, priv->xkb->names->keycodes);
 | 
						|
        if (!name)
 | 
						|
            g_warning ("Can't get the name of keycodes");
 | 
						|
        else if (!priv->names.keycodes ||
 | 
						|
                 g_strcmp0 (name, priv->names.keycodes)) {
 | 
						|
            g_free (priv->names.keycodes);
 | 
						|
            priv->names.keycodes = g_strdup (name);
 | 
						|
            XFree (name);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (priv->xkb->names->geometry <= 0)
 | 
						|
        g_warning ("XKB geometry setting is not loaded");
 | 
						|
    else {
 | 
						|
        name = XGetAtomName (priv->display, priv->xkb->names->geometry);
 | 
						|
        if (!name)
 | 
						|
            g_warning ("Can't get the name of geometry");
 | 
						|
        else if (!priv->names.geometry ||
 | 
						|
                 g_strcmp0 (name, priv->names.geometry)) {
 | 
						|
            g_free (priv->names.geometry);
 | 
						|
            priv->names.geometry = g_strdup (name);
 | 
						|
            XFree (name);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (priv->xkb->names->symbols <= 0)
 | 
						|
        g_warning ("XKB symbols setting is not loaded");
 | 
						|
    else {
 | 
						|
        name = XGetAtomName (priv->display, priv->xkb->names->symbols);
 | 
						|
        if (!name)
 | 
						|
            g_warning ("Can't get the name of symbols");
 | 
						|
        else if (!priv->names.symbols ||
 | 
						|
                 g_strcmp0 (name, priv->names.symbols)) {
 | 
						|
            g_free (priv->names.symbols);
 | 
						|
            priv->names.symbols = g_strdup (name);
 | 
						|
            XFree (name);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_new:
 | 
						|
 * @keycodes: component name for keycodes
 | 
						|
 * @geometry: component name for geometry
 | 
						|
 * @symbols: component name for symbols
 | 
						|
 *
 | 
						|
 * Create a new #EekXkbLayout.
 | 
						|
 */
 | 
						|
EekLayout *
 | 
						|
eek_xkb_layout_new (const gchar *keycodes,
 | 
						|
                    const gchar *geometry,
 | 
						|
                    const gchar *symbols)
 | 
						|
{
 | 
						|
    EekXkbLayout *layout;
 | 
						|
    EekXkbLayoutPrivate *priv;
 | 
						|
 | 
						|
    layout = g_object_new (EEK_TYPE_XKB_LAYOUT, NULL);
 | 
						|
    g_return_val_if_fail (layout, NULL);
 | 
						|
    priv = layout->priv;
 | 
						|
    if (keycodes)
 | 
						|
        priv->names.keycodes = g_strdup (keycodes);
 | 
						|
    if (geometry)
 | 
						|
        priv->names.geometry = g_strdup (geometry);
 | 
						|
    if (symbols)
 | 
						|
        priv->names.symbols = g_strdup (symbols);
 | 
						|
 | 
						|
    get_keyboard (layout);
 | 
						|
    if (priv->xkb == NULL) {
 | 
						|
        g_object_unref (layout);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return EEK_LAYOUT(layout);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_set_keycodes:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 * @keycodes: component name for keycodes
 | 
						|
 *
 | 
						|
 * Set the keycodes component (in the XKB terminology).
 | 
						|
 */
 | 
						|
void
 | 
						|
eek_xkb_layout_set_keycodes (EekXkbLayout *layout, const gchar *keycodes)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
    gboolean is_changed = TRUE;
 | 
						|
 | 
						|
    g_return_if_fail (priv);
 | 
						|
    if (keycodes && priv->names.keycodes)
 | 
						|
        is_changed = g_strcmp0 (keycodes, priv->names.keycodes) != 0;
 | 
						|
    else if (keycodes == NULL && priv->names.keycodes == NULL)
 | 
						|
        is_changed = FALSE;
 | 
						|
    g_free (priv->names.keycodes);
 | 
						|
    priv->names.keycodes = g_strdup (keycodes);
 | 
						|
    get_keyboard (layout);
 | 
						|
    if (is_changed)
 | 
						|
        g_signal_emit_by_name (layout, "changed");
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_set_geometry:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 * @geometry: component name for geometry
 | 
						|
 *
 | 
						|
 * Set the keycodes component (in the XKB terminology).
 | 
						|
 */
 | 
						|
void
 | 
						|
eek_xkb_layout_set_geometry (EekXkbLayout *layout, const gchar *geometry)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
    gboolean is_changed = TRUE;
 | 
						|
 | 
						|
    g_return_if_fail (priv);
 | 
						|
    if (geometry && priv->names.geometry)
 | 
						|
        is_changed = g_strcmp0 (geometry, priv->names.geometry) != 0;
 | 
						|
    else if (geometry == NULL && priv->names.geometry == NULL)
 | 
						|
        is_changed = FALSE;
 | 
						|
    g_free (priv->names.geometry);
 | 
						|
    priv->names.geometry = g_strdup (geometry);
 | 
						|
    get_keyboard (layout);
 | 
						|
    if (is_changed)
 | 
						|
        g_signal_emit_by_name (layout, "changed");
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_set_symbols:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 * @symbols: component name for symbols
 | 
						|
 *
 | 
						|
 * Set the symbols component (in the XKB terminology).
 | 
						|
 */
 | 
						|
void
 | 
						|
eek_xkb_layout_set_symbols (EekXkbLayout *layout, const gchar *symbols)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
    gboolean is_changed = TRUE;
 | 
						|
 | 
						|
    g_return_if_fail (priv);
 | 
						|
    if (symbols && priv->names.symbols)
 | 
						|
        is_changed = g_strcmp0 (symbols, priv->names.symbols) != 0;
 | 
						|
    else if (symbols == NULL && priv->names.symbols == NULL)
 | 
						|
        is_changed = FALSE;
 | 
						|
    g_free (priv->names.symbols);
 | 
						|
    priv->names.symbols = g_strdup (symbols);
 | 
						|
    get_keyboard (layout);
 | 
						|
    if (is_changed)
 | 
						|
        g_signal_emit_by_name (layout, "changed");
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_get_keycodes:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 *
 | 
						|
 * Get the keycodes component name (in the XKB terminology).
 | 
						|
 */
 | 
						|
G_CONST_RETURN gchar *
 | 
						|
eek_xkb_layout_get_keycodes (EekXkbLayout *layout)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
 | 
						|
    g_return_val_if_fail (priv, NULL);
 | 
						|
    return priv->names.keycodes;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_get_geometry:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 *
 | 
						|
 * Get the geometry component name (in the XKB terminology).
 | 
						|
 */
 | 
						|
G_CONST_RETURN gchar *
 | 
						|
eek_xkb_layout_get_geometry (EekXkbLayout *layout)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
 | 
						|
    g_return_val_if_fail (priv, NULL);
 | 
						|
    return priv->names.geometry;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_get_symbols:
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 *
 | 
						|
 * Get the symbols component name (in the XKB terminology).
 | 
						|
 */
 | 
						|
G_CONST_RETURN gchar *
 | 
						|
eek_xkb_layout_get_symbols (EekXkbLayout *layout)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (layout);
 | 
						|
 | 
						|
    g_return_val_if_fail (priv, NULL);
 | 
						|
    return priv->names.symbols;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
get_keyboard (EekXkbLayout *layout)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
 | 
						|
    if (priv->xkb)
 | 
						|
        XkbFreeKeyboard (priv->xkb, 0, TRUE);	/* free_all = TRUE */
 | 
						|
    priv->xkb = NULL;
 | 
						|
 | 
						|
    if (priv->names.keycodes &&
 | 
						|
        priv->names.geometry &&
 | 
						|
        priv->names.symbols) {
 | 
						|
        priv->xkb = XkbGetKeyboardByName (priv->display, XkbUseCoreKbd,
 | 
						|
                                          &priv->names, 0,
 | 
						|
                                          XkbGBN_GeometryMask |
 | 
						|
                                          XkbGBN_KeyNamesMask |
 | 
						|
                                          XkbGBN_OtherNamesMask |
 | 
						|
                                          XkbGBN_ClientSymbolsMask |
 | 
						|
                                          XkbGBN_IndicatorMapMask, FALSE);
 | 
						|
    } else {
 | 
						|
        priv->xkb = XkbGetKeyboard (priv->display,
 | 
						|
                                    XkbGBN_GeometryMask |
 | 
						|
                                    XkbGBN_KeyNamesMask |
 | 
						|
                                    XkbGBN_OtherNamesMask |
 | 
						|
                                    XkbGBN_SymbolsMask |
 | 
						|
                                    XkbGBN_IndicatorMapMask,
 | 
						|
                                    XkbUseCoreKbd);
 | 
						|
        get_names (layout);
 | 
						|
    }
 | 
						|
 | 
						|
    if (priv->xkb == NULL) {
 | 
						|
        g_free (priv->names.keycodes);
 | 
						|
        priv->names.keycodes = NULL;
 | 
						|
        g_free (priv->names.geometry);
 | 
						|
        priv->names.geometry = NULL;
 | 
						|
        g_free (priv->names.symbols);
 | 
						|
        priv->names.symbols = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static guint
 | 
						|
find_keycode (EekXkbLayout *layout, gchar *key_name)
 | 
						|
{
 | 
						|
#define KEYSYM_NAME_MAX_LENGTH 4
 | 
						|
    guint keycode;
 | 
						|
    gint i, j;
 | 
						|
    XkbKeyNamePtr pkey;
 | 
						|
    XkbKeyAliasPtr palias;
 | 
						|
    guint is_name_matched;
 | 
						|
    gchar *src, *dst;
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
 | 
						|
    if (!priv->xkb)
 | 
						|
        return EEK_INVALID_KEYCODE;
 | 
						|
 | 
						|
#ifdef KBDRAW_DEBUG
 | 
						|
    printf ("    looking for keycode for (%c%c%c%c)\n",
 | 
						|
            key_name[0], key_name[1], key_name[2], key_name[3]);
 | 
						|
#endif
 | 
						|
 | 
						|
    pkey = priv->xkb->names->keys + priv->xkb->min_key_code;
 | 
						|
    for (keycode = priv->xkb->min_key_code;
 | 
						|
         keycode <= priv->xkb->max_key_code; keycode++) {
 | 
						|
        is_name_matched = 1;
 | 
						|
        src = key_name;
 | 
						|
        dst = pkey->name;
 | 
						|
        for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) {
 | 
						|
            if ('\0' == *src)
 | 
						|
                break;
 | 
						|
            if (*src++ != *dst++) {
 | 
						|
                is_name_matched = 0;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (is_name_matched) {
 | 
						|
#ifdef KBDRAW_DEBUG
 | 
						|
            printf ("      found keycode %u\n", keycode);
 | 
						|
#endif
 | 
						|
            return keycode;
 | 
						|
        }
 | 
						|
        pkey++;
 | 
						|
    }
 | 
						|
 | 
						|
    palias = priv->xkb->names->key_aliases;
 | 
						|
    for (j = priv->xkb->names->num_key_aliases; --j >= 0;) {
 | 
						|
        is_name_matched = 1;
 | 
						|
        src = key_name;
 | 
						|
        dst = palias->alias;
 | 
						|
        for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) {
 | 
						|
            if ('\0' == *src)
 | 
						|
                break;
 | 
						|
            if (*src++ != *dst++) {
 | 
						|
                is_name_matched = 0;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (is_name_matched) {
 | 
						|
            keycode = find_keycode (layout, palias->real);
 | 
						|
#ifdef KBDRAW_DEBUG
 | 
						|
            printf ("found alias keycode %u\n", keycode);
 | 
						|
#endif
 | 
						|
            return keycode;
 | 
						|
        }
 | 
						|
        palias++;
 | 
						|
    }
 | 
						|
 | 
						|
    return EEK_INVALID_KEYCODE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setup_scaling (EekXkbLayout *layout,
 | 
						|
               gdouble       width,
 | 
						|
               gdouble       height)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = layout->priv;
 | 
						|
 | 
						|
    g_return_if_fail (priv->xkb);
 | 
						|
 | 
						|
    g_return_if_fail (priv->xkb->geom->width_mm > 0);
 | 
						|
    g_return_if_fail (priv->xkb->geom->height_mm > 0);
 | 
						|
 | 
						|
    if (width * priv->xkb->geom->height_mm <
 | 
						|
        height * priv->xkb->geom->width_mm) {
 | 
						|
        priv->scale_numerator = width;
 | 
						|
        priv->scale_denominator = priv->xkb->geom->width_mm;
 | 
						|
    } else {
 | 
						|
        priv->scale_numerator = height;
 | 
						|
        priv->scale_denominator = priv->xkb->geom->height_mm;
 | 
						|
    }
 | 
						|
}
 |