Exemple #1
0
/**
 * gdk_keymap_get_scroll_lock_state:
 * @keymap: a #GdkKeymap
 *
 * Returns whether the Scroll Lock modifer is locked.
 *
 * Returns: %TRUE if Scroll Lock is on
 */
gboolean
gdk_keymap_get_scroll_lock_state (GdkKeymap *keymap)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);

  return GDK_KEYMAP_GET_CLASS (keymap)->get_scroll_lock_state (keymap);
}
Exemple #2
0
void
egg_keymap_resolve_virtual_modifiers (GdkKeymap              *keymap,
                                      EggVirtualModifierType  virtual_mods,
                                      GdkModifierType        *concrete_mods)
{
  GdkModifierType concrete;
  int i;
  const EggModmap *modmap;

  g_return_if_fail (GDK_IS_KEYMAP (keymap));
  g_return_if_fail (concrete_mods != NULL);
  
  modmap = egg_keymap_get_modmap (keymap);
  
  /* Not so sure about this algorithm. */
  
  concrete = 0;
  i = 0;
  while (i < EGG_MODMAP_ENTRY_LAST)
    {
      if (modmap->mapping[i] & virtual_mods)
        concrete |= (1 << i);

      ++i;
    }

  *concrete_mods = concrete;
}
Exemple #3
0
/**
 * gdk_keymap_get_direction:
 * @keymap: a #GdkKeymap
 *
 * Returns the direction of effective layout of the keymap.
 * The direction of a layout is the direction of the majority of its
 * symbols. See pango_unichar_direction().
 *
 * Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL
 *   if it can determine the direction. %PANGO_DIRECTION_NEUTRAL
 *   otherwise.
 **/
PangoDirection
gdk_keymap_get_direction (GdkKeymap *keymap)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), PANGO_DIRECTION_LTR);

  return GDK_KEYMAP_GET_CLASS (keymap)->get_direction (keymap);
}
Exemple #4
0
guint
gdk_keymap_lookup_key (GdkKeymap          *keymap,
                       const GdkKeymapKey *key)
{
  guint sym;

  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0);
  g_return_val_if_fail (key != NULL, 0);
  g_return_val_if_fail (key->group < 4, 0);
  
  /* Accept only the default keymap */
  if (keymap != NULL && keymap != gdk_keymap_get_default ())
    return 0;

  update_keymap ();
  
  if (key->keycode >= 256 ||
      key->group < 0 || key->group >= 2 ||
      key->level < 0 || key->level >= 2)
    return 0;
  
  sym = keysym_tab[key->keycode*4 + key->group*2 + key->level];
  
  if (sym == GDK_VoidSymbol)
    return 0;
  else
    return sym;
}
Exemple #5
0
/**
 * gdk_keymap_have_bidi_layouts:
 * @keymap: a #GdkKeymap
 *
 * Determines if keyboard layouts for both right-to-left and left-to-right
 * languages are in use.
 *
 * Returns: %TRUE if there are layouts in both directions, %FALSE otherwise
 **/
gboolean
gdk_keymap_have_bidi_layouts (GdkKeymap *keymap)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);

  return GDK_KEYMAP_GET_CLASS (keymap)->have_bidi_layouts (keymap);
}
Exemple #6
0
/**
 * gdk_keymap_get_display:
 * @keymap: a #GdkKeymap
 *
 * Retrieves the #GdkDisplay associated to the @keymap.
 *
 * Returns: (transfer none): a #GdkDisplay
 */
GdkDisplay *
gdk_keymap_get_display (GdkKeymap *keymap)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), NULL);

  return keymap->display;
}
Exemple #7
0
void
egg_keymap_virtualize_modifiers (GdkKeymap              *keymap,
                                 GdkModifierType         concrete_mods,
                                 EggVirtualModifierType *virtual_mods)
{
    GdkModifierType virtual;
    int i;
    const EggModmap *modmap;

    g_return_if_fail (GDK_IS_KEYMAP (keymap));
    g_return_if_fail (virtual_mods != NULL);

    modmap = egg_keymap_get_modmap (keymap);

    /* Not so sure about this algorithm. */
    virtual = 0;
    i = 0;
    while (i < EGG_MODMAP_ENTRY_LAST) {
        if ((1 << i) & concrete_mods) {
            EggVirtualModifierType cleaned;
            cleaned = modmap->mapping[i] & ~(EGG_VIRTUAL_MOD2_MASK |
                                             EGG_VIRTUAL_MOD3_MASK |
                                             EGG_VIRTUAL_MOD4_MASK |
                                             EGG_VIRTUAL_MOD5_MASK);
            if (cleaned != 0) {
                virtual |= cleaned;
            } else {
                /* Rather than dropping mod2->mod5 if not bound,
                 * go ahead and use the concrete names
                 */
				virtual |= modmap->mapping[i];
            }
        }
Exemple #8
0
/**
 * gdk_keymap_map_virtual_modifiers:
 * @keymap: a #GdkKeymap
 * @state: (inout): pointer to the modifier state to map
 *
 * Maps the virtual modifiers (i.e. Super, Hyper and Meta) which
 * are set in @state to their non-virtual counterparts (i.e. Mod2,
 * Mod3,...) and set the corresponding bits in @state.
 *
 * This function is useful when matching key events against
 * accelerators.
 *
 * Returns: %FALSE if two virtual modifiers were mapped to the
 *     same non-virtual modifier. Note that %FALSE is also returned
 *     if a virtual modifier is mapped to a non-virtual modifier that
 *     was already set in @state.
 */
gboolean
gdk_keymap_map_virtual_modifiers (GdkKeymap       *keymap,
                                  GdkModifierType *state)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);

  return GDK_KEYMAP_GET_CLASS(keymap)->map_virtual_modifiers (keymap, state);
}
Exemple #9
0
/**
 * gdk_keymap_get_modifier_mask:
 * @keymap: a #GdkKeymap
 * @intent: the use case for the modifier mask
 *
 * Returns the modifier mask the @keymap’s windowing system backend
 * uses for a particular purpose.
 *
 * Note that this function always returns real hardware modifiers, not
 * virtual ones (e.g. it will return #GDK_MOD1_MASK rather than
 * #GDK_META_MASK if the backend maps MOD1 to META), so there are use
 * cases where the return value of this function has to be transformed
 * by gdk_keymap_add_virtual_modifiers() in order to contain the
 * expected result.
 *
 * Returns: the modifier mask used for @intent.
 **/
GdkModifierType
gdk_keymap_get_modifier_mask (GdkKeymap         *keymap,
                              GdkModifierIntent  intent)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), 0);

  return GDK_KEYMAP_GET_CLASS (keymap)->get_modifier_mask (keymap, intent);
}
Exemple #10
0
/**
 * gdk_keymap_add_virtual_modifiers:
 * @keymap: a #GdkKeymap
 * @state: (inout): pointer to the modifier mask to change
 *
 * Maps the non-virtual modifiers (i.e Mod2, Mod3, ...) which are set
 * in @state to the virtual modifiers (i.e. Super, Hyper and Meta) and
 * set the corresponding bits in @state.
 *
 * GDK already does this before delivering key events, but for
 * compatibility reasons, it only sets the first virtual modifier
 * it finds, whereas this function sets all matching virtual modifiers.
 *
 * This function is useful when matching key events against
 * accelerators.
 */
void
gdk_keymap_add_virtual_modifiers (GdkKeymap       *keymap,
			          GdkModifierType *state)
{
  g_return_if_fail (GDK_IS_KEYMAP (keymap));

  GDK_KEYMAP_GET_CLASS (keymap)->add_virtual_modifiers (keymap, state);
}
Exemple #11
0
/**
 * gdk_keymap_lookup_key:
 * @keymap: a #GdkKeymap
 * @key: a #GdkKeymapKey with keycode, group, and level initialized
 *
 * Looks up the keyval mapped to a keycode/group/level triplet.
 * If no keyval is bound to @key, returns 0. For normal user input,
 * you want to use gdk_keymap_translate_keyboard_state() instead of
 * this function, since the effective group/level may not be
 * the same as the current keyboard state.
 *
 * Returns: a keyval, or 0 if none was mapped to the given @key
 **/
guint
gdk_keymap_lookup_key (GdkKeymap          *keymap,
                       const GdkKeymapKey *key)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), 0);
  g_return_val_if_fail (key != NULL, 0);

  return GDK_KEYMAP_GET_CLASS (keymap)->lookup_key (keymap, key);
}
Exemple #12
0
/**
 * gdk_keymap_get_modifier_state:
 * @keymap: a #GdkKeymap
 *
 * Returns the current modifier state.
 *
 * Returns: the current modifier state.
 */
guint
gdk_keymap_get_modifier_state (GdkKeymap *keymap)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);

  if (GDK_KEYMAP_GET_CLASS (keymap)->get_modifier_state)
    return GDK_KEYMAP_GET_CLASS (keymap)->get_modifier_state (keymap);

  return 0;
}
Exemple #13
0
/**
 * gdk_keymap_get_entries_for_keycode:
 * @keymap: a #GdkKeymap
 * @hardware_keycode: a keycode
 * @keys: (out) (array length=n_entries) (transfer full) (optional): return
 *     location for array of #GdkKeymapKey, or %NULL
 * @keyvals: (out) (array length=n_entries) (transfer full) (optional): return
 *     location for array of keyvals, or %NULL
 * @n_entries: length of @keys and @keyvals
 *
 * Returns the keyvals bound to @hardware_keycode.
 * The Nth #GdkKeymapKey in @keys is bound to the Nth
 * keyval in @keyvals. Free the returned arrays with g_free().
 * When a keycode is pressed by the user, the keyval from
 * this list of entries is selected by considering the effective
 * keyboard group and level. See gdk_keymap_translate_keyboard_state().
 *
 * Returns: %TRUE if there were any entries
 **/
gboolean
gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
                                    guint          hardware_keycode,
                                    GdkKeymapKey **keys,
                                    guint        **keyvals,
                                    gint          *n_entries)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
  g_return_val_if_fail (n_entries != NULL, FALSE);

  return GDK_KEYMAP_GET_CLASS (keymap)->get_entries_for_keycode (keymap, hardware_keycode,
                                                                 keys, keyvals, n_entries);
}
Exemple #14
0
/**
 * gdk_keymap_get_entries_for_keyval:
 * @keymap: a #GdkKeymap
 * @keyval: a keyval, such as %GDK_KEY_a, %GDK_KEY_Up, %GDK_KEY_Return, etc.
 * @keys: (out) (array length=n_keys) (transfer full): return location
 *     for an array of #GdkKeymapKey
 * @n_keys: return location for number of elements in returned array
 *
 * Obtains a list of keycode/group/level combinations that will
 * generate @keyval. Groups and levels are two kinds of keyboard mode;
 * in general, the level determines whether the top or bottom symbol
 * on a key is used, and the group determines whether the left or
 * right symbol is used. On US keyboards, the shift key changes the
 * keyboard level, and there are no groups. A group switch key might
 * convert a keyboard between Hebrew to English modes, for example.
 * #GdkEventKey contains a %group field that indicates the active
 * keyboard group. The level is computed from the modifier mask.
 * The returned array should be freed
 * with g_free().
 *
 * Returns: %TRUE if keys were found and returned
 **/
gboolean
gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
                                   guint          keyval,
                                   GdkKeymapKey **keys,
                                   gint          *n_keys)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
  g_return_val_if_fail (keys != NULL, FALSE);
  g_return_val_if_fail (n_keys != NULL, FALSE);
  g_return_val_if_fail (keyval != 0, FALSE);

  return GDK_KEYMAP_GET_CLASS (keymap)->get_entries_for_keyval (keymap, keyval,
                                                                keys, n_keys);
}
Exemple #15
0
GdkModifierType
gdk_keymap_get_modifier_mask (GdkKeymap         *keymap,
                              GdkModifierIntent  intent)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), 0);

#ifdef GDK_WINDOWING_QUARTZ
  switch (intent)
    {
    case GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR:
      return GDK_MOD2_MASK;

    case GDK_MODIFIER_INTENT_CONTEXT_MENU:
      return GDK_CONTROL_MASK;

    case GDK_MODIFIER_INTENT_EXTEND_SELECTION:
      return GDK_SHIFT_MASK;

    case GDK_MODIFIER_INTENT_MODIFY_SELECTION:
      return GDK_MOD2_MASK;

    case GDK_MODIFIER_INTENT_NO_TEXT_INPUT:
      return GDK_MOD2_MASK | GDK_CONTROL_MASK;

    default:
      g_return_val_if_reached (0);
    }
#else
  switch (intent)
    {
    case GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR:
      return GDK_CONTROL_MASK;

    case GDK_MODIFIER_INTENT_CONTEXT_MENU:
      return 0;

    case GDK_MODIFIER_INTENT_EXTEND_SELECTION:
      return GDK_SHIFT_MASK;

    case GDK_MODIFIER_INTENT_MODIFY_SELECTION:
      return GDK_CONTROL_MASK;

    case GDK_MODIFIER_INTENT_NO_TEXT_INPUT:
      return GDK_MOD1_MASK | GDK_CONTROL_MASK;

    default:
      g_return_val_if_reached (0);
    }
#endif
}
Exemple #16
0
/**
 * gdk_keymap_translate_keyboard_state:
 * @keymap: a #GdkKeymap
 * @hardware_keycode: a keycode
 * @state: a modifier state
 * @group: active keyboard group
 * @keyval: (out) (allow-none): return location for keyval, or %NULL
 * @effective_group: (out) (allow-none): return location for effective
 *     group, or %NULL
 * @level: (out) (allow-none): return location for level, or %NULL
 * @consumed_modifiers: (out) (allow-none): return location for modifiers
 *     that were used to determine the group or level, or %NULL
 *
 * Translates the contents of a #GdkEventKey into a keyval, effective
 * group, and level. Modifiers that affected the translation and
 * are thus unavailable for application use are returned in
 * @consumed_modifiers.
 * See [Groups][key-group-explanation] for an explanation of
 * groups and levels. The @effective_group is the group that was
 * actually used for the translation; some keys such as Enter are not
 * affected by the active keyboard group. The @level is derived from
 * @state. For convenience, #GdkEventKey already contains the translated
 * keyval, so this function isn’t as useful as you might think.
 *
 * @consumed_modifiers gives modifiers that should be masked outfrom @state
 * when comparing this key press to a hot key. For instance, on a US keyboard,
 * the `plus` symbol is shifted, so when comparing a key press to a
 * `<Control>plus` accelerator `<Shift>` should be masked out.
 *
 * |[<!-- language="C" -->
 * // We want to ignore irrelevant modifiers like ScrollLock
 * #define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)
 * gdk_keymap_translate_keyboard_state (keymap, event->hardware_keycode,
 *                                      event->state, event->group,
 *                                      &keyval, NULL, NULL, &consumed);
 * if (keyval == GDK_PLUS &&
 *     (event->state & ~consumed & ALL_ACCELS_MASK) == GDK_CONTROL_MASK)
 *   // Control was pressed
 * ]|
 * 
 * An older interpretation @consumed_modifiers was that it contained
 * all modifiers that might affect the translation of the key;
 * this allowed accelerators to be stored with irrelevant consumed
 * modifiers, by doing:
 * |[<!-- language="C" -->
 * // XXX Don’t do this XXX
 * if (keyval == accel_keyval &&
 *     (event->state & ~consumed & ALL_ACCELS_MASK) == (accel_mods & ~consumed))
 *   // Accelerator was pressed
 * ]|
 *
 * However, this did not work if multi-modifier combinations were
 * used in the keymap, since, for instance, `<Control>` would be
 * masked out even if only `<Control><Alt>` was used in the keymap.
 * To support this usage as well as well as possible, all single
 * modifier combinations that could affect the key for any combination
 * of modifiers will be returned in @consumed_modifiers; multi-modifier
 * combinations are returned only when actually found in @state. When
 * you store accelerators, you should always store them with consumed
 * modifiers removed. Store `<Control>plus`, not `<Control><Shift>plus`,
 *
 * Returns: %TRUE if there was a keyval bound to the keycode/state/group
 **/
gboolean
gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
                                     guint            hardware_keycode,
                                     GdkModifierType  state,
                                     gint             group,
                                     guint           *keyval,
                                     gint            *effective_group,
                                     gint            *level,
                                     GdkModifierType *consumed_modifiers)
{
  g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);

  return GDK_KEYMAP_GET_CLASS (keymap)->translate_keyboard_state (keymap,
								  hardware_keycode,
								  state,
								  group,
								  keyval,
								  effective_group,
								  level,
								  consumed_modifiers);
}
Exemple #17
0
gboolean
gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
                                    guint          hardware_keycode,
                                    GdkKeymapKey **keys,
                                    guint        **keyvals,
                                    gint          *n_entries)
{
  GArray *key_array;
  GArray *keyval_array;

  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
  g_return_val_if_fail (n_entries != NULL, FALSE);

  if (hardware_keycode <= 0 ||
      hardware_keycode >= 256)
    {
      if (keys)
        *keys = NULL;
      if (keyvals)
        *keyvals = NULL;

      *n_entries = 0;
      return FALSE;
    }
  
  if (keys)
    key_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
  else
    key_array = NULL;
  
  if (keyvals)
    keyval_array = g_array_new (FALSE, FALSE, sizeof (guint));
  else
    keyval_array = NULL;
  
  /* Accept only the default keymap */
  if (keymap == NULL || keymap == gdk_keymap_get_default ())
    {
      gint i;

      update_keymap ();

      for (i = 0; i < 4; i++)
	{
	  if (key_array)
            {
              GdkKeymapKey key;
	      
              key.keycode = hardware_keycode;
              
              key.group = i / 2;
              key.level = i % 2;
              
              g_array_append_val (key_array, key);
            }

          if (keyval_array)
            g_array_append_val (keyval_array, keysym_tab[hardware_keycode*4+i]);
	}
    }

  if ((key_array && key_array->len > 0) ||
      (keyval_array && keyval_array->len > 0))
    {
      if (keys)
        *keys = (GdkKeymapKey*) key_array->data;

      if (keyvals)
        *keyvals = (guint*) keyval_array->data;

      if (key_array)
        *n_entries = key_array->len;
      else
        *n_entries = keyval_array->len;
    }
  else
    {
      if (keys)
        *keys = NULL;

      if (keyvals)
        *keyvals = NULL;
      
      *n_entries = 0;
    }

  if (key_array)
    g_array_free (key_array, key_array->len > 0 ? FALSE : TRUE);
  if (keyval_array)
    g_array_free (keyval_array, keyval_array->len > 0 ? FALSE : TRUE);

  return *n_entries > 0;
}
Exemple #18
0
gboolean
gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
                                     guint            hardware_keycode,
                                     GdkModifierType  state,
                                     gint             group,
                                     guint           *keyval,
                                     gint            *effective_group,
                                     gint            *level,
                                     GdkModifierType *consumed_modifiers)
{
  guint tmp_keyval;
  guint *keyvals;
  gint shift_level;
  gboolean ignore_shift = FALSE;
  gboolean ignore_group = FALSE;
      
  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
  g_return_val_if_fail (group < 4, FALSE);
  
#if 0
  GDK_NOTE (EVENTS, g_print ("gdk_keymap_translate_keyboard_state: keycode=%#x state=%#x group=%d\n",
			     hardware_keycode, state, group));
#endif
  if (keyval)
    *keyval = 0;
  if (effective_group)
    *effective_group = 0;
  if (level)
    *level = 0;
  if (consumed_modifiers)
    *consumed_modifiers = 0;

  /* Accept only the default keymap */
  if (keymap != NULL && keymap != gdk_keymap_get_default ())
    return FALSE;

  if (hardware_keycode >= 256)
    return FALSE;

  if (group < 0 || group >= 2)
    return FALSE;

  update_keymap ();

  keyvals = keysym_tab + hardware_keycode*4;

  if ((state & GDK_LOCK_MASK) &&
      (state & GDK_SHIFT_MASK) &&
      ((gdk_shift_modifiers & GDK_LOCK_MASK) ||
       (keyvals[group*2 + 1] == gdk_keyval_to_upper (keyvals[group*2 + 0]))))
    /* Shift always disables ShiftLock. Shift disables CapsLock for
     * keys with lowercase/uppercase letter pairs.
     */
    shift_level = 0;
  else if (state & gdk_shift_modifiers)
    shift_level = 1;
  else
    shift_level = 0;

  /* Drop group and shift if there are no keysymbols on
   * the key for those.
   */
  if (shift_level == 1 &&
      keyvals[group*2 + 1] == GDK_VoidSymbol &&
      keyvals[group*2] != GDK_VoidSymbol)
    {
      shift_level = 0;
      ignore_shift = TRUE;
    }

  if (group == 1 &&
      keyvals[2 + shift_level] == GDK_VoidSymbol &&
      keyvals[0 + shift_level] != GDK_VoidSymbol)
    {
      group = 0;
      ignore_group = TRUE;
    }

  if (keyvals[group *2 + shift_level] == GDK_VoidSymbol &&
      keyvals[0 + 0] != GDK_VoidSymbol)
    {
      shift_level = 0;
      group = 0;
      ignore_group = TRUE;
      ignore_shift = TRUE;
    }

  /* See whether the group and shift level actually mattered
   * to know what to put in consumed_modifiers
   */
  if (keyvals[group*2 + 1] == GDK_VoidSymbol ||
      keyvals[group*2 + 0] == keyvals[group*2 + 1])
    ignore_shift = TRUE;

  if (keyvals[2 + shift_level] == GDK_VoidSymbol ||
      keyvals[0 + shift_level] == keyvals[2 + shift_level])
    ignore_group = TRUE;

  tmp_keyval = keyvals[group*2 + shift_level];

  /* If a true CapsLock is toggled, and Shift is not down,
   * and the shifted keysym is the uppercase of the unshifted,
   * use it.
   */
  if (!(gdk_shift_modifiers & GDK_LOCK_MASK) &&
      !(state & GDK_SHIFT_MASK) &&
      (state & GDK_LOCK_MASK))
    {
      guint upper = gdk_keyval_to_upper (tmp_keyval);
      if (upper == keyvals[group*2 + 1])
	tmp_keyval = upper;
    }

  if (keyval)
    *keyval = tmp_keyval;

  if (effective_group)
    *effective_group = group;

  if (level)
    *level = shift_level;

  if (consumed_modifiers)
    {
      *consumed_modifiers =
	(ignore_group ? 0 : GDK_MOD2_MASK) |
	(ignore_shift ? 0 : (GDK_SHIFT_MASK|GDK_LOCK_MASK));
    }
				
#if 0
  GDK_NOTE (EVENTS, g_print ("... group=%d level=%d cmods=%#x keyval=%s\n",
			     group, shift_level, tmp_modifiers, gdk_keyval_name (tmp_keyval)));
#endif

  return tmp_keyval != GDK_VoidSymbol;
}
Exemple #19
0
gboolean
gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
                                   guint          keyval,
                                   GdkKeymapKey **keys,
                                   gint          *n_keys)
{
  GArray *retval;

  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
  g_return_val_if_fail (keys != NULL, FALSE);
  g_return_val_if_fail (n_keys != NULL, FALSE);
  g_return_val_if_fail (keyval != 0, FALSE);
  
  retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));

  /* Accept only the default keymap */
  if (keymap == NULL || keymap == gdk_keymap_get_default ())
    {
      gint vk;
      
      update_keymap ();

      for (vk = 0; vk < 256; vk++)
	{
	  gint i;

	  for (i = 0; i < 4; i++)
	    {
	      if (keysym_tab[vk*4+i] == keyval)
		{
		  GdkKeymapKey key;
		  
		  key.keycode = vk;
		  
		  /* 2 levels (normal, shift), two groups (normal, AltGr) */
		  key.group = i / 2;
		  key.level = i % 2;
		  
		  g_array_append_val (retval, key);
		}
	    }
	}
    }

#ifdef G_ENABLE_DEBUG
  if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
    {
      gint i;

      g_print ("gdk_keymap_get_entries_for_keyval: %#.04x (%s):",
	       keyval, gdk_keyval_name (keyval));
      for (i = 0; i < retval->len; i++)
	{
	  GdkKeymapKey *entry = (GdkKeymapKey *) retval->data + i;
	  g_print ("  %#.02x %d %d", entry->keycode, entry->group, entry->level);
	}
      g_print ("\n");
    }
#endif

  if (retval->len > 0)
    {
      *keys = (GdkKeymapKey*) retval->data;
      *n_keys = retval->len;
    }
  else
    {
      *keys = NULL;
      *n_keys = 0;
    }
      
  g_array_free (retval, retval->len > 0 ? FALSE : TRUE);

  return *n_keys > 0;
}