- use G_DECLARE_ and G_DEFINE_ macros - move all data into ClassNamePrivate - use _get_instance_private() This should not introduce any functional changes or breakage. Skipped two classes (EekKeyboard and EekboardContextService) for now in order not to break the build. These two classes are used in some very funky WIP code that tries to circumvent encapsulation. (Funky code is in eekboard/key-emitter.c and eekboard/eekboard-context-service.c)
		
			
				
	
	
		
			685 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			685 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 
 | 
						|
 * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
 | 
						|
 * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
 | 
						|
 * Copyright (C) 2010-2011 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.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include "config.h"
 | 
						|
#endif  /* HAVE_CONFIG_H */
 | 
						|
 | 
						|
#include "eek-xkb-layout.h"
 | 
						|
 | 
						|
#include <X11/keysym.h>
 | 
						|
#include <X11/extensions/XKBgeom.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <gio/gio.h>
 | 
						|
 | 
						|
#include "eek-keyboard.h"
 | 
						|
#include "eek-section.h"
 | 
						|
#include "eek-key.h"
 | 
						|
#include "eek-keysym.h"
 | 
						|
 | 
						|
#define XKB_COMPONENT_MASK (XkbGBN_GeometryMask |       \
 | 
						|
                            XkbGBN_KeyNamesMask |       \
 | 
						|
                            XkbGBN_OtherNamesMask |     \
 | 
						|
                            XkbGBN_SymbolsMask |        \
 | 
						|
                            XkbGBN_IndicatorMapMask)
 | 
						|
 | 
						|
static void initable_iface_init (GInitableIface *initable_iface);
 | 
						|
 | 
						|
enum {
 | 
						|
    PROP_0,
 | 
						|
    PROP_DISPLAY,
 | 
						|
    PROP_LAST
 | 
						|
};
 | 
						|
 | 
						|
typedef 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 orefs by shape address. */
 | 
						|
    GHashTable *shape_oref_hash;
 | 
						|
 | 
						|
    gint scale_numerator;
 | 
						|
    gint scale_denominator;
 | 
						|
} EekXkbLayoutPrivate;
 | 
						|
 | 
						|
G_DEFINE_TYPE_EXTENDED (EekXkbLayout, eek_xkb_layout, EEK_TYPE_LAYOUT,
 | 
						|
			0, /* GTypeFlags */
 | 
						|
			G_ADD_PRIVATE(EekXkbLayout)
 | 
						|
                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
 | 
						|
                                               initable_iface_init))
 | 
						|
 | 
						|
static guint    find_keycode             (EekXkbLayout *layout,
 | 
						|
                                          gchar        *key_name);
 | 
						|
 | 
						|
static gboolean get_keyboard_from_server (EekXkbLayout *layout,
 | 
						|
                                          GError      **error);
 | 
						|
 | 
						|
static gboolean get_names_from_server    (EekXkbLayout *layout,
 | 
						|
                                          GError      **error);
 | 
						|
 | 
						|
static void     setup_scaling            (EekXkbLayout *layout,
 | 
						|
                                          gdouble       width,
 | 
						|
                                          gdouble       height);
 | 
						|
 | 
						|
G_INLINE_FUNC gint
 | 
						|
xkb_to_pixmap_coord (EekXkbLayout *layout,
 | 
						|
                     gint          n)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    return n * priv->scale_numerator / priv->scale_denominator;
 | 
						|
}
 | 
						|
 | 
						|
G_INLINE_FUNC gdouble
 | 
						|
xkb_to_pixmap_double (EekXkbLayout *layout,
 | 
						|
                     gdouble       d)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    return d * priv->scale_numerator / priv->scale_denominator;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_key (EekXkbLayout *layout,
 | 
						|
            EekKeyboard  *keyboard,
 | 
						|
            EekSection   *section,
 | 
						|
            gint          column,
 | 
						|
            gint          row,
 | 
						|
            gdouble       x,
 | 
						|
            gdouble       y,
 | 
						|
            XkbKeyRec    *xkbkey)
 | 
						|
{
 | 
						|
    XkbGeometryRec *xkbgeometry;
 | 
						|
    XkbBoundsRec *xkbbounds;
 | 
						|
    XkbShapeRec *xkbshape;
 | 
						|
    XkbOutlineRec *xkboutline;
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    EekKey *key;
 | 
						|
    EekBounds bounds;
 | 
						|
    EekSymbolMatrix *matrix = NULL;
 | 
						|
    gchar name[XkbKeyNameLength + 1];
 | 
						|
    KeyCode keycode;
 | 
						|
    gint num_groups, num_levels;
 | 
						|
    guint oref;
 | 
						|
    gpointer v;
 | 
						|
 | 
						|
    xkbgeometry = priv->xkb->geom;
 | 
						|
    xkbshape = &xkbgeometry->shapes[xkbkey->shape_ndx];
 | 
						|
    if (g_hash_table_lookup_extended (priv->shape_oref_hash, xkbshape,
 | 
						|
                                      NULL, &v)) {
 | 
						|
        oref = GPOINTER_TO_UINT(v);
 | 
						|
    } else {
 | 
						|
        EekOutline *outline;
 | 
						|
 | 
						|
        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);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        oref = eek_keyboard_add_outline (keyboard, outline);
 | 
						|
        eek_outline_free (outline);
 | 
						|
        g_hash_table_insert (priv->shape_oref_hash, xkbshape,
 | 
						|
                             GUINT_TO_POINTER(oref));
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
        matrix = eek_symbol_matrix_new (0, 0);
 | 
						|
    } else {
 | 
						|
        KeySym keysym;
 | 
						|
        gint i, j;
 | 
						|
 | 
						|
        num_groups = XkbKeyNumGroups (priv->xkb, keycode);
 | 
						|
        num_levels = XkbKeyGroupsWidth (priv->xkb, keycode);
 | 
						|
        matrix = eek_symbol_matrix_new (num_groups, num_levels);
 | 
						|
        for (i = 0; i < num_groups; i++)
 | 
						|
            for (j = 0; j < num_levels; j++) {
 | 
						|
                EekModifierType modifier;
 | 
						|
 | 
						|
                keysym = XkbKeySymEntry (priv->xkb, keycode, j, i);
 | 
						|
                modifier = XkbKeysymToModifiers (priv->display, keysym);
 | 
						|
                matrix->data[i * num_levels + j] =
 | 
						|
                    EEK_SYMBOL(eek_keysym_new_with_modifier (keysym,
 | 
						|
                                                             modifier));
 | 
						|
            }
 | 
						|
    }
 | 
						|
 | 
						|
    key = eek_section_create_key (section, keycode, column, row);
 | 
						|
    eek_element_set_name (EEK_ELEMENT(key), name);
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(key), &bounds);
 | 
						|
    eek_key_set_symbol_matrix (key, matrix);
 | 
						|
    eek_symbol_matrix_free (matrix);
 | 
						|
    eek_key_set_oref (key, oref);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_section (EekXkbLayout  *layout,
 | 
						|
                EekKeyboard   *keyboard,
 | 
						|
                XkbSectionRec *xkbsection)
 | 
						|
{
 | 
						|
    XkbGeometryRec *xkbgeometry;
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    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);
 | 
						|
 | 
						|
    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, keyboard, 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 = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    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 EekKeyboard *
 | 
						|
eek_xkb_layout_real_create_keyboard (EekboardContextService *manager,
 | 
						|
                                     EekLayout *self,
 | 
						|
                                     gdouble    initial_width,
 | 
						|
                                     gdouble    initial_height)
 | 
						|
{
 | 
						|
    EekKeyboard *keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", self, NULL);
 | 
						|
    keyboard->manager = manager;
 | 
						|
 | 
						|
    EekBounds bounds = {
 | 
						|
        .x = 0.0,
 | 
						|
        .y = 0.0,
 | 
						|
        .width = initial_width,
 | 
						|
        .height = initial_height
 | 
						|
    };
 | 
						|
    eek_element_set_bounds (EEK_ELEMENT(keyboard), &bounds);
 | 
						|
 | 
						|
    /* resolve modifiers dynamically assigned at run time */
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (
 | 
						|
		    EEK_XKB_LAYOUT(self));
 | 
						|
    eek_keyboard_set_num_lock_mask (keyboard,
 | 
						|
                                    XkbKeysymToModifiers (priv->display,
 | 
						|
                                                          XK_Num_Lock));
 | 
						|
    eek_keyboard_set_alt_gr_mask (keyboard,
 | 
						|
                                  XkbKeysymToModifiers (priv->display,
 | 
						|
                                                        XK_ISO_Level3_Shift));
 | 
						|
 | 
						|
    if (priv->shape_oref_hash)
 | 
						|
        g_hash_table_destroy (priv->shape_oref_hash);
 | 
						|
 | 
						|
    priv->shape_oref_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
 | 
						|
    create_keyboard (EEK_XKB_LAYOUT(self), keyboard);
 | 
						|
    g_hash_table_destroy (priv->shape_oref_hash);
 | 
						|
 | 
						|
    return keyboard;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_finalize (GObject *object)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (
 | 
						|
		    EEK_XKB_LAYOUT (object));
 | 
						|
 | 
						|
    g_free (priv->names.keycodes);
 | 
						|
    g_free (priv->names.geometry);
 | 
						|
    g_free (priv->names.symbols);
 | 
						|
    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)
 | 
						|
{
 | 
						|
    EekXkbLayout *layout = EEK_XKB_LAYOUT (object);
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    switch (prop_id) {
 | 
						|
    case PROP_DISPLAY:
 | 
						|
        priv->display = g_value_get_pointer (value);
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void 
 | 
						|
eek_xkb_layout_get_property (GObject    *object,
 | 
						|
                             guint       prop_id,
 | 
						|
                             GValue     *value,
 | 
						|
                             GParamSpec *pspec)
 | 
						|
{
 | 
						|
    EekXkbLayout *layout = EEK_XKB_LAYOUT (object);
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    switch (prop_id) {
 | 
						|
    case PROP_DISPLAY:
 | 
						|
        g_value_set_pointer (value, priv->display);
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_class_init (EekXkbLayoutClass *klass)
 | 
						|
{
 | 
						|
    EekLayoutClass *layout_class = EEK_LAYOUT_CLASS (klass);
 | 
						|
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
						|
    GParamSpec *pspec;
 | 
						|
 | 
						|
    layout_class->create_keyboard = eek_xkb_layout_real_create_keyboard;
 | 
						|
 | 
						|
    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_pointer ("display",
 | 
						|
                                  "Display",
 | 
						|
                                  "X Display",
 | 
						|
                                  G_PARAM_READWRITE |
 | 
						|
                                  G_PARAM_CONSTRUCT_ONLY);
 | 
						|
    g_object_class_install_property (gobject_class, PROP_DISPLAY, pspec);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
eek_xkb_layout_init (EekXkbLayout *self)
 | 
						|
{
 | 
						|
    /* void */
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
get_names_from_server (EekXkbLayout *layout,
 | 
						|
                       GError      **error)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
    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);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_new:
 | 
						|
 *
 | 
						|
 * Create a new #EekXkbLayout.
 | 
						|
 */
 | 
						|
EekLayout *
 | 
						|
eek_xkb_layout_new (Display *display,
 | 
						|
                    GError **error)
 | 
						|
{
 | 
						|
    return (EekLayout *) g_initable_new (EEK_TYPE_XKB_LAYOUT,
 | 
						|
                                         NULL,
 | 
						|
                                         error,
 | 
						|
                                         "display", display,
 | 
						|
                                         NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * eek_xkb_layout_set_names: (skip)
 | 
						|
 * @layout: an #EekXkbLayout
 | 
						|
 * @names: XKB component names
 | 
						|
 * @error: a #GError
 | 
						|
 *
 | 
						|
 * Set the XKB component names to @layout.
 | 
						|
 * Returns: %TRUE if the component names are successfully set, %FALSE otherwise
 | 
						|
 */
 | 
						|
gboolean
 | 
						|
eek_xkb_layout_set_names (EekXkbLayout         *layout,
 | 
						|
                          XkbComponentNamesRec *names,
 | 
						|
                          GError              **error)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    if (g_strcmp0 (names->keycodes, priv->names.keycodes)) {
 | 
						|
        g_free (priv->names.keycodes);
 | 
						|
        priv->names.keycodes = g_strdup (names->keycodes);
 | 
						|
    }
 | 
						|
 | 
						|
    if (g_strcmp0 (names->geometry, priv->names.geometry)) {
 | 
						|
        g_free (priv->names.geometry);
 | 
						|
        priv->names.geometry = g_strdup (names->geometry);
 | 
						|
    }
 | 
						|
 | 
						|
    if (g_strcmp0 (names->symbols, priv->names.symbols)) {
 | 
						|
        g_free (priv->names.symbols);
 | 
						|
        priv->names.symbols = g_strdup (names->symbols);
 | 
						|
    }
 | 
						|
 | 
						|
    return get_keyboard_from_server (layout, error);
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
get_keyboard_from_server (EekXkbLayout *layout,
 | 
						|
                          GError      **error)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    if (priv->xkb) {
 | 
						|
        XkbFreeKeyboard (priv->xkb, 0, True);
 | 
						|
        priv->xkb = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (priv->names.keycodes && priv->names.geometry && priv->names.symbols) {
 | 
						|
        priv->xkb = XkbGetKeyboardByName (priv->display,
 | 
						|
                                          XkbUseCoreKbd,
 | 
						|
                                          &priv->names,
 | 
						|
                                          0,
 | 
						|
                                          XKB_COMPONENT_MASK,
 | 
						|
                                          False);
 | 
						|
    } else {
 | 
						|
        priv->xkb = XkbGetKeyboard (priv->display,
 | 
						|
                                    XKB_COMPONENT_MASK,
 | 
						|
                                    XkbUseCoreKbd);
 | 
						|
        if (!get_names_from_server (layout, error)) {
 | 
						|
            XkbFreeKeyboard (priv->xkb, 0, True);
 | 
						|
            priv->xkb = NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (priv->xkb == NULL) {
 | 
						|
        g_set_error (error,
 | 
						|
                     EEK_ERROR,
 | 
						|
                     EEK_ERROR_LAYOUT_ERROR,
 | 
						|
                     "can't get keyboard from server");
 | 
						|
        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;
 | 
						|
        return FALSE;
 | 
						|
    }
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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 = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    if (!priv->xkb)
 | 
						|
        return EEK_INVALID_KEYCODE;
 | 
						|
 | 
						|
    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)
 | 
						|
            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);
 | 
						|
            return keycode;
 | 
						|
        }
 | 
						|
        palias++;
 | 
						|
    }
 | 
						|
 | 
						|
    return EEK_INVALID_KEYCODE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setup_scaling (EekXkbLayout *layout,
 | 
						|
               gdouble       width,
 | 
						|
               gdouble       height)
 | 
						|
{
 | 
						|
    EekXkbLayoutPrivate *priv = eek_xkb_layout_get_instance_private (layout);
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
initable_init (GInitable    *initable,
 | 
						|
               GCancellable *cancellable,
 | 
						|
               GError      **error)
 | 
						|
{
 | 
						|
    EekXkbLayout *layout = EEK_XKB_LAYOUT (initable);
 | 
						|
 | 
						|
    if (!get_keyboard_from_server (layout, error))
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    if (!get_names_from_server (layout, error))
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
initable_iface_init (GInitableIface *initable_iface)
 | 
						|
{
 | 
						|
  initable_iface->init = initable_init;
 | 
						|
}
 |