/* * Copyright (C) 2006 Sergey V. Udaltsov * Copyright (C) 2010-2011 Daiki Ueno * 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 // #include // #include // #include // #include // #include // // #include "eek-xkb-layout.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); // // G_DEFINE_TYPE_WITH_CODE (EekXkbLayout, eek_xkb_layout, EEK_TYPE_LAYOUT, // G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, // initable_iface_init)); // // #define EEK_XKB_LAYOUT_GET_PRIVATE(obj) \ // (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_XKB_LAYOUT, EekXkbLayoutPrivate)) // // enum { // PROP_0, // PROP_DISPLAY, // 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 orefs by shape address. */ // GHashTable *shape_oref_hash; // // gint scale_numerator; // gint scale_denominator; // }; // // 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 = 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, // EekKeyboard *keyboard, // 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; // 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; // 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, 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 = 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 EekKeyboard * // eek_xkb_layout_real_create_keyboard (EekLayout *self, // gdouble initial_width, // gdouble initial_height) // { // EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self); // EekBounds bounds; // EekKeyboard *keyboard; // // keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", self, NULL); // bounds.x = bounds.y = 0.0; // bounds.width = initial_width; // bounds.height = initial_height; // eek_element_set_bounds (EEK_ELEMENT(keyboard), &bounds); // // /* resolve modifiers dynamically assigned at run time */ // 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_PRIVATE (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); // // switch (prop_id) { // case PROP_DISPLAY: // layout->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); // // switch (prop_id) { // case PROP_DISPLAY: // g_value_set_pointer (value, layout->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; // // g_type_class_add_private (gobject_class, sizeof (EekXkbLayoutPrivate)); // // 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) // { // self->priv = EEK_XKB_LAYOUT_GET_PRIVATE (self); // } // // static gboolean // get_names_from_server (EekXkbLayout *layout, // GError **error) // { // 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); // } // } // // 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) // { // if (g_strcmp0 (names->keycodes, layout->priv->names.keycodes)) { // g_free (layout->priv->names.keycodes); // layout->priv->names.keycodes = g_strdup (names->keycodes); // } // // if (g_strcmp0 (names->geometry, layout->priv->names.geometry)) { // g_free (layout->priv->names.geometry); // layout->priv->names.geometry = g_strdup (names->geometry); // } // // if (g_strcmp0 (names->symbols, layout->priv->names.symbols)) { // g_free (layout->priv->names.symbols); // layout->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 = layout->priv; // // 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 = layout->priv; // // 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 = 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; // } // } // // 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; // }