]> Pileus Git - ~andy/gtk/blobdiff - gdk/quartz/gdkkeys-quartz.c
Change FSF Address
[~andy/gtk] / gdk / quartz / gdkkeys-quartz.c
index 55ff3d40709e5867fcb8a2db3c51dcd85b4b3c66..1e8c46b6fdaa2d96dd25414d3216deddf940c727 100644 (file)
@@ -14,9 +14,7 @@
  * 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 /* Some parts of this code come from quartzKeyboard.c,
  * from the Apple X11 Server.
  *  prior written authorization.
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <Carbon/Carbon.h>
+#include <AppKit/NSEvent.h>
 #include "gdk.h"
+#include "gdkquartzkeys.h"
+#include "gdkkeysprivate.h"
 #include "gdkkeysyms.h"
 
 #define NUM_KEYCODES 128
 
 static GdkKeymap *default_keymap = NULL;
 
+struct _GdkQuartzKeymap
+{
+  GdkKeymap keymap;
+};
+
+struct _GdkQuartzKeymapClass
+{
+  GdkKeymapClass keymap_class;
+};
+
+G_DEFINE_TYPE (GdkQuartzKeymap, gdk_quartz_keymap, GDK_TYPE_KEYMAP)
+
+GdkKeymap *
+_gdk_quartz_display_get_keymap (GdkDisplay *display)
+{
+  if (default_keymap == NULL)
+    default_keymap = g_object_new (gdk_quartz_keymap_get_type (), NULL);
+
+  return default_keymap;
+}
+
+/* Note: we could check only if building against the 10.5 SDK instead, but
+ * that would make non-xml layouts not work in 32-bit which would be a quite
+ * bad regression. This way, old unsupported layouts will just not work in
+ * 64-bit.
+ */
+#ifdef __LP64__
+static TISInputSourceRef current_layout = NULL;
+#else
 static KeyboardLayoutRef current_layout = NULL;
+#endif
 
 /* This is a table of all keyvals. Each keycode gets KEYVALS_PER_KEYCODE entries.
  * TThere is 1 keyval per modifier (Nothing, Shift, Alt, Shift+Alt);
@@ -90,7 +121,7 @@ macroman2ucs (unsigned char c)
     0x2021, 0xb7, 0x201a, 0x201e, 0x2030, 0xc2, 0xca, 0xc1,
     0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4,
     0xf8ff, 0xd2, 0xda, 0xdb, 0xd9, 0x131, 0x2c6, 0x2dc,
-    0xaf, 0x2d8, 0x2d9, 0x2da, 0xb8, 0x2dd, 0x2db, 0x2c7,
+    0xaf, 0x2d8, 0x2d9, 0x2da, 0xb8, 0x2dd, 0x2db, 0x2c7
   };
 
   if (c < 128)
@@ -102,53 +133,62 @@ macroman2ucs (unsigned char c)
 const static struct {
   guint keycode;
   guint keyval;
-} known_keys[] = {
-  {  55, GDK_Meta_L },
-  {  56, GDK_Shift_L },
-  {  57, GDK_Caps_Lock },
-  {  58, GDK_Alt_L },
-  {  59, GDK_Control_L },
-  {  60, GDK_Shift_R },
-  {  61, GDK_Alt_R },
-  {  62, GDK_Control_R },
-  { 122, GDK_F1 },
-  { 120, GDK_F2 },
-  {  99, GDK_F3 },
-  { 118, GDK_F4 },
-  {  96, GDK_F5 },
-  {  97, GDK_F6 },
-  {  98, GDK_F7 },
-  { 100, GDK_F8 },
-  { 101, GDK_F9 },
-  { 109, GDK_F10 },
-  { 103, GDK_F11 },
-  { 111, GDK_F12 },
-  { 105, GDK_F13 },
-  { 107, GDK_F14 },
-  { 113, GDK_F15 },
+  unsigned int modmask; /* So we can tell when a mod key is pressed/released */
+} modifier_keys[] = {
+  {  54, GDK_KEY_Meta_R,    NSCommandKeyMask },
+  {  55, GDK_KEY_Meta_L,    NSCommandKeyMask },
+  {  56, GDK_KEY_Shift_L,   NSShiftKeyMask },
+  {  57, GDK_KEY_Caps_Lock, NSAlphaShiftKeyMask },
+  {  58, GDK_KEY_Alt_L,     NSAlternateKeyMask },
+  {  59, GDK_KEY_Control_L, NSControlKeyMask },
+  {  60, GDK_KEY_Shift_R,   NSShiftKeyMask },
+  {  61, GDK_KEY_Alt_R,     NSAlternateKeyMask },
+  {  62, GDK_KEY_Control_R, NSControlKeyMask }
+};
+
+const static struct {
+  guint keycode;
+  guint keyval;
+} function_keys[] = {
+  { 122, GDK_KEY_F1 },
+  { 120, GDK_KEY_F2 },
+  {  99, GDK_KEY_F3 },
+  { 118, GDK_KEY_F4 },
+  {  96, GDK_KEY_F5 },
+  {  97, GDK_KEY_F6 },
+  {  98, GDK_KEY_F7 },
+  { 100, GDK_KEY_F8 },
+  { 101, GDK_KEY_F9 },
+  { 109, GDK_KEY_F10 },
+  { 103, GDK_KEY_F11 },
+  { 111, GDK_KEY_F12 },
+  { 105, GDK_KEY_F13 },
+  { 107, GDK_KEY_F14 },
+  { 113, GDK_KEY_F15 },
+  { 106, GDK_KEY_F16 }
 };
 
 const static struct {
   guint keycode;
   guint normal_keyval, keypad_keyval;
 } known_numeric_keys[] = {
-  { 65, GDK_period, GDK_KP_Decimal },
-  { 67, GDK_asterisk, GDK_KP_Multiply },
-  { 69, GDK_plus, GDK_KP_Add },
-  { 75, GDK_slash, GDK_KP_Divide },
-  { 76, 0x01000003, GDK_KP_Enter },
-  { 78, GDK_minus, GDK_KP_Subtract },
-  { 81, GDK_equal, GDK_KP_Equal },
-  { 82, GDK_0, GDK_KP_0 },
-  { 83, GDK_1, GDK_KP_1 },
-  { 84, GDK_2, GDK_KP_2 },
-  { 85, GDK_3, GDK_KP_3 },
-  { 86, GDK_4, GDK_KP_4 },
-  { 87, GDK_5, GDK_KP_5 },
-  { 88, GDK_6, GDK_KP_6 },
-  { 89, GDK_7, GDK_KP_7 },
-  { 91, GDK_8, GDK_KP_8 },
-  { 92, GDK_9, GDK_KP_9 },
+  { 65, GDK_KEY_period, GDK_KEY_KP_Decimal },
+  { 67, GDK_KEY_asterisk, GDK_KEY_KP_Multiply },
+  { 69, GDK_KEY_plus, GDK_KEY_KP_Add },
+  { 75, GDK_KEY_slash, GDK_KEY_KP_Divide },
+  { 76, 0x01000003, GDK_KEY_KP_Enter },
+  { 78, GDK_KEY_minus, GDK_KEY_KP_Subtract },
+  { 81, GDK_KEY_equal, GDK_KEY_KP_Equal },
+  { 82, GDK_KEY_0, GDK_KEY_KP_0 },
+  { 83, GDK_KEY_1, GDK_KEY_KP_1 },
+  { 84, GDK_KEY_2, GDK_KEY_KP_2 },
+  { 85, GDK_KEY_3, GDK_KEY_KP_3 },
+  { 86, GDK_KEY_4, GDK_KEY_KP_4 },
+  { 87, GDK_KEY_5, GDK_KEY_KP_5 },
+  { 88, GDK_KEY_6, GDK_KEY_KP_6 },
+  { 89, GDK_KEY_7, GDK_KEY_KP_7 },
+  { 91, GDK_KEY_8, GDK_KEY_KP_8 },
+  { 92, GDK_KEY_9, GDK_KEY_KP_9 }
 };
 
 /* These values aren't covered by gdk_unicode_to_keyval */
@@ -156,60 +196,135 @@ const static struct {
   gunichar ucs_value;
   guint keyval;
 } special_ucs_table [] = {
-  { 0x0001, GDK_Home },
-  { 0x0008, GDK_BackSpace },
-  { 0x0009, GDK_Tab },
-  { 0x000d, GDK_Return },
-  { 0x001c, GDK_Left },
-  { 0x001d, GDK_Right },
-  { 0x001e, GDK_Up },
-  { 0x001f, GDK_Down },
+  { 0x0001, GDK_KEY_Home },
+  { 0x0003, GDK_KEY_Return },
+  { 0x0004, GDK_KEY_End },
+  { 0x0008, GDK_KEY_BackSpace },
+  { 0x0009, GDK_KEY_Tab },
+  { 0x000b, GDK_KEY_Page_Up },
+  { 0x000c, GDK_KEY_Page_Down },
+  { 0x000d, GDK_KEY_Return },
+  { 0x001b, GDK_KEY_Escape },
+  { 0x001c, GDK_KEY_Left },
+  { 0x001d, GDK_KEY_Right },
+  { 0x001e, GDK_KEY_Up },
+  { 0x001f, GDK_KEY_Down },
+  { 0x007f, GDK_KEY_Delete },
+  { 0xf027, GDK_KEY_dead_acute },
+  { 0xf060, GDK_KEY_dead_grave },
+  { 0xf300, GDK_KEY_dead_grave },
+  { 0xf0b4, GDK_KEY_dead_acute },
+  { 0xf301, GDK_KEY_dead_acute },
+  { 0xf385, GDK_KEY_dead_acute },
+  { 0xf05e, GDK_KEY_dead_circumflex },
+  { 0xf2c6, GDK_KEY_dead_circumflex },
+  { 0xf302, GDK_KEY_dead_circumflex },
+  { 0xf07e, GDK_KEY_dead_tilde },
+  { 0xf303, GDK_KEY_dead_tilde },
+  { 0xf342, GDK_KEY_dead_perispomeni },
+  { 0xf0af, GDK_KEY_dead_macron },
+  { 0xf304, GDK_KEY_dead_macron },
+  { 0xf2d8, GDK_KEY_dead_breve },
+  { 0xf306, GDK_KEY_dead_breve },
+  { 0xf2d9, GDK_KEY_dead_abovedot },
+  { 0xf307, GDK_KEY_dead_abovedot },
+  { 0xf0a8, GDK_KEY_dead_diaeresis },
+  { 0xf308, GDK_KEY_dead_diaeresis },
+  { 0xf2da, GDK_KEY_dead_abovering },
+  { 0xf30A, GDK_KEY_dead_abovering },
+  { 0xf022, GDK_KEY_dead_doubleacute },
+  { 0xf2dd, GDK_KEY_dead_doubleacute },
+  { 0xf30B, GDK_KEY_dead_doubleacute },
+  { 0xf2c7, GDK_KEY_dead_caron },
+  { 0xf30C, GDK_KEY_dead_caron },
+  { 0xf0be, GDK_KEY_dead_cedilla },
+  { 0xf327, GDK_KEY_dead_cedilla },
+  { 0xf2db, GDK_KEY_dead_ogonek },
+  { 0xf328, GDK_KEY_dead_ogonek },
+  { 0xfe5d, GDK_KEY_dead_iota },
+  { 0xf323, GDK_KEY_dead_belowdot },
+  { 0xf309, GDK_KEY_dead_hook },
+  { 0xf31B, GDK_KEY_dead_horn },
+  { 0xf02d, GDK_KEY_dead_stroke },
+  { 0xf335, GDK_KEY_dead_stroke },
+  { 0xf336, GDK_KEY_dead_stroke },
+  { 0xf313, GDK_KEY_dead_abovecomma },
+  /*  { 0xf313, GDK_KEY_dead_psili }, */
+  { 0xf314, GDK_KEY_dead_abovereversedcomma },
+  /*  { 0xf314, GDK_KEY_dead_dasia }, */
+  { 0xf30F, GDK_KEY_dead_doublegrave },
+  { 0xf325, GDK_KEY_dead_belowring },
+  { 0xf2cd, GDK_KEY_dead_belowmacron },
+  { 0xf331, GDK_KEY_dead_belowmacron },
+  { 0xf32D, GDK_KEY_dead_belowcircumflex },
+  { 0xf330, GDK_KEY_dead_belowtilde },
+  { 0xf32E, GDK_KEY_dead_belowbreve },
+  { 0xf324, GDK_KEY_dead_belowdiaeresis },
+  { 0xf311, GDK_KEY_dead_invertedbreve },
+  { 0xf02c, GDK_KEY_dead_belowcomma },
+  { 0xf326, GDK_KEY_dead_belowcomma }
 };
 
 static void
 maybe_update_keymap (void)
 {
+  const void *chr_data = NULL;
+
+#ifdef __LP64__
+  TISInputSourceRef new_layout = TISCopyCurrentKeyboardLayoutInputSource ();
+  CFDataRef layout_data_ref;
+
+#else
   KeyboardLayoutRef new_layout;
+  KeyboardLayoutKind layout_kind;
 
   KLGetCurrentKeyboardLayout (&new_layout);
+#endif
 
   if (new_layout != current_layout)
     {
       guint *p;
       int i;
 
-      KeyboardLayoutKind layout_kind;
-      
       g_free (keyval_array);
       keyval_array = g_new0 (guint, NUM_KEYCODES * KEYVALS_PER_KEYCODE);
 
-      /* Get the layout kind */
-      KLGetKeyboardLayoutProperty (new_layout, kKLKind, &layout_kind);
+#ifdef __LP64__
+      layout_data_ref = (CFDataRef) TISGetInputSourceProperty
+       (new_layout, kTISPropertyUnicodeKeyLayoutData);
+
+      if (layout_data_ref)
+       chr_data = CFDataGetBytePtr (layout_data_ref);
 
-      /* FIXME: When we have UCHR support, I think that should be preffered
-       * if a keymap has both.
-       */
-      if (layout_kind == kKLKCHRuchrKind || layout_kind == kKLKCHRKind)
+      if (chr_data == NULL)
        {
-         const void *chr_data;
+         g_error ("cannot get keyboard layout data");
+         return;
+       }
+#else
+      /* Get the layout kind */
+      KLGetKeyboardLayoutProperty (new_layout, kKLKind, (const void **)&layout_kind);
 
+      /* 8-bit-only keyabord layout */
+      if (layout_kind == kKLKCHRKind)
+       { 
          /* Get chr data */
-         KLGetKeyboardLayoutProperty (new_layout, kKLKCHRData, (void **)&chr_data);
+         KLGetKeyboardLayoutProperty (new_layout, kKLKCHRData, (const void **)&chr_data);
          
          for (i = 0; i < NUM_KEYCODES; i++) 
            {
              int j;
-             UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey|optionKey};
+             UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
 
              p = keyval_array + i * KEYVALS_PER_KEYCODE;
-
+             
              for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
                {
                  UInt32 c, state = 0;
                  UInt16 key_code;
                  UniChar uc;
                  
-                 key_code = modifiers[j]|i;
+                 key_code = modifiers[j] | i;
                  c = KeyTranslate (chr_data, key_code, &state);
 
                  if (state != 0)
@@ -220,12 +335,17 @@ maybe_update_keymap (void)
 
                  if (c != 0 && c != 0x10)
                    {
-                     guint keyval;
                      int k;
                      gboolean found = FALSE;
 
+                     /* FIXME: some keyboard layouts (e.g. Russian) use
+                       * a different 8-bit character set. We should
+                       * check for this. Not a serious problem, because
+                      * most (all?) of these layouts also have a
+                      * uchr version. 
+                      */
                      uc = macroman2ucs (c);
-                     
+
                      for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
                        {
                          if (special_ucs_table[k].ucs_value == uc)
@@ -236,8 +356,101 @@ maybe_update_keymap (void)
                            }
                        }
                      
+                     /* Special-case shift-tab since GTK+ expects
+                      * GDK_KEY_ISO_Left_Tab for that.
+                      */
+                     if (found && p[j] == GDK_KEY_Tab && modifiers[j] == shiftKey)
+                       p[j] = GDK_KEY_ISO_Left_Tab;
+
+                     if (!found)
+                        p[j] = gdk_unicode_to_keyval (uc);
+                   }
+               }
+
+             if (p[3] == p[2])
+               p[3] = 0;
+             if (p[2] == p[1])
+               p[2] = 0;
+             if (p[1] == p[0])
+               p[1] = 0;
+             if (p[0] == p[2] && 
+                 p[1] == p[3])
+               p[2] = p[3] = 0;
+           }
+       }
+      /* unicode keyboard layout */
+      else if (layout_kind == kKLKCHRuchrKind || layout_kind == kKLuchrKind)
+       { 
+         /* Get chr data */
+         KLGetKeyboardLayoutProperty (new_layout, kKLuchrData, (const void **)&chr_data);
+#endif
+         
+         for (i = 0; i < NUM_KEYCODES; i++) 
+           {
+             int j;
+             UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
+              UniChar chars[4];
+              UniCharCount nChars;
+
+             p = keyval_array + i * KEYVALS_PER_KEYCODE;
+
+             for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
+               {
+                 UInt32 state = 0;
+                 OSStatus err;
+                 UInt16 key_code;
+                 UniChar uc;
+                 
+                 key_code = modifiers[j] | i;
+                 err = UCKeyTranslate (chr_data, i, kUCKeyActionDisplay,
+                                       (modifiers[j] >> 8) & 0xFF,
+                                       LMGetKbdType(),
+                                       0,
+                                       &state, 4, &nChars, chars);
+
+                  /* FIXME: Theoretically, we can get multiple UTF-16 values;
+                  * we should convert them to proper unicode and figure
+                  * out whether there are really keyboard layouts that
+                  * give us more than one character for one keypress. */
+                 if (err == noErr && nChars == 1)
+                   {
+                     int k;
+                     gboolean found = FALSE;
+
+                     /* A few <Shift><Option>keys return two
+                      * characters, the first of which is U+00a0,
+                      * which isn't interesting; so we return the
+                      * second. More sophisticated handling is the
+                      * job of a GtkIMContext.
+                      *
+                      * If state isn't zero, it means that it's a
+                      * dead key of some sort. Some of those are
+                      * enumerated in the special_ucs_table with the
+                      * high nibble set to f to push it into the
+                      * private use range. Here we do the same.
+                      */
+                     if (state != 0)
+                       chars[nChars - 1] |= 0xf000;
+                     uc = chars[nChars - 1];
+
+                     for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
+                       {
+                         if (special_ucs_table[k].ucs_value == uc)
+                           {
+                             p[j] = special_ucs_table[k].keyval;
+                             found = TRUE;
+                             break;
+                           }
+                       }
+
+                     /* Special-case shift-tab since GTK+ expects
+                      * GDK_KEY_ISO_Left_Tab for that.
+                      */
+                     if (found && p[j] == GDK_KEY_Tab && modifiers[j] == shiftKey)
+                       p[j] = GDK_KEY_ISO_Left_Tab;
+
                      if (!found)
-                       p[j] = gdk_unicode_to_keyval (uc);
+                        p[j] = gdk_unicode_to_keyval (uc);
                    }
                }
 
@@ -251,27 +464,38 @@ maybe_update_keymap (void)
                  p[1] == p[3])
                p[2] = p[3] = 0;
            }
+#ifndef __LP64__
        }
       else
        {
-         g_error ("uchr type layouts aren't supported right now");
+         g_error ("unknown type of keyboard layout (neither KCHR nor uchr)"
+                  " - not supported right now");
        }
+#endif
 
-      for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
+      for (i = 0; i < G_N_ELEMENTS (modifier_keys); i++)
        {
-         p = keyval_array + known_keys[i].keycode * KEYVALS_PER_KEYCODE;
+         p = keyval_array + modifier_keys[i].keycode * KEYVALS_PER_KEYCODE;
 
          if (p[0] == 0 && p[1] == 0 && 
              p[2] == 0 && p[3] == 0)
-           p[0] = known_keys[i].keyval;
+           p[0] = modifier_keys[i].keyval;
+       }
+
+      for (i = 0; i < G_N_ELEMENTS (function_keys); i++)
+       {
+         p = keyval_array + function_keys[i].keycode * KEYVALS_PER_KEYCODE;
+
+          p[0] = function_keys[i].keyval;
+          p[1] = p[2] = p[3] = 0;
        }
 
       for (i = 0; i < G_N_ELEMENTS (known_numeric_keys); i++)
        {
          p = keyval_array + known_numeric_keys[i].keycode * KEYVALS_PER_KEYCODE;
 
-         if (p[0] == known_numeric_keys[i].normal_keyval);
-             p[0] = known_numeric_keys[i].keypad_keyval;
+         if (p[0] == known_numeric_keys[i].normal_keyval)
+            p[0] = known_numeric_keys[i].keypad_keyval;
        }
       
       if (current_layout)
@@ -281,37 +505,42 @@ maybe_update_keymap (void)
     }
 }
 
-GdkKeymap *
-gdk_keymap_get_for_display (GdkDisplay *display)
+static PangoDirection
+gdk_quartz_keymap_get_direction (GdkKeymap *keymap)
 {
-  g_return_val_if_fail (display == gdk_display_get_default (), NULL);
+  return PANGO_DIRECTION_NEUTRAL;
+}
 
-  if (default_keymap == NULL)
-    default_keymap = g_object_new (gdk_keymap_get_type (), NULL);
+static gboolean
+gdk_quartz_keymap_have_bidi_layouts (GdkKeymap *keymap)
+{
+  /* FIXME: Can we implement this? */
+  return FALSE;
+}
 
-  return default_keymap;
+static gboolean
+gdk_quartz_keymap_get_caps_lock_state (GdkKeymap *keymap)
+{
+  /* FIXME: Implement this. */
+  return FALSE;
 }
 
-PangoDirection
-gdk_keymap_get_direction (GdkKeymap *keymap)
+static gboolean
+gdk_quartz_keymap_get_num_lock_state (GdkKeymap *keymap)
 {
-  return PANGO_DIRECTION_NEUTRAL;
+  /* FIXME: Implement this. */
+  return FALSE;
 }
 
-gboolean
-gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
-                                   guint          keyval,
-                                   GdkKeymapKey **keys,
-                                   gint          *n_keys)
+static gboolean
+gdk_quartz_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
+                                          guint          keyval,
+                                          GdkKeymapKey **keys,
+                                          gint          *n_keys)
 {
   GArray *keys_array;
   int i;
 
-  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);
-
   maybe_update_keymap ();
 
   *n_keys = 0;
@@ -327,8 +556,8 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
       (*n_keys)++;
 
       key.keycode = i / KEYVALS_PER_KEYCODE;
-      key.group = 0;
-      key.level = i % KEYVALS_PER_KEYCODE;
+      key.group = (i % KEYVALS_PER_KEYCODE) >= 2;
+      key.level = i % 2;
 
       g_array_append_val (keys_array, key);
     }
@@ -338,20 +567,17 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
   return *n_keys > 0;;
 }
 
-gboolean
-gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
-                                    guint          hardware_keycode,
-                                    GdkKeymapKey **keys,
-                                    guint        **keyvals,
-                                    gint          *n_entries)
+static gboolean
+gdk_quartz_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
+                                           guint          hardware_keycode,
+                                           GdkKeymapKey **keys,
+                                           guint        **keyvals,
+                                           gint          *n_entries)
 {
   GArray *keys_array, *keyvals_array;
   int i;
   guint *p;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
-  g_return_val_if_fail (n_entries != NULL, FALSE);
-
   maybe_update_keymap ();
 
   *n_entries = 0;
@@ -386,7 +612,7 @@ gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
          GdkKeymapKey key;
 
          key.keycode = hardware_keycode;
-         key.group = i / 2;
+         key.group = i >= 2;
          key.level = i % 2;
 
          g_array_append_val (keys_array, key);
@@ -402,14 +628,23 @@ gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
   return *n_entries > 0;
 }
 
+static guint
+gdk_quartz_keymap_lookup_key (GdkKeymap          *keymap,
+                              const GdkKeymapKey *key)
+{
+  /* FIXME: Implement */
+
+  return 0;
+}
+
 #define GET_KEYVAL(keycode, group, level) (keyval_array[(keycode * KEYVALS_PER_KEYCODE + group * 2 + level)])
 
 static guint
 translate_keysym (guint           hardware_keycode,
                  gint            group,
                  GdkModifierType state,
-                 guint          *effective_group,
-                 guint          *effective_level)
+                 gint           *effective_group,
+                 gint           *effective_level)
 {
   gint level;
   guint tmp_keyval;
@@ -429,30 +664,32 @@ translate_keysym (guint           hardware_keycode,
   if (state & GDK_LOCK_MASK)
     {
       guint upper = gdk_keyval_to_upper (tmp_keyval);
-       if (upper != tmp_keyval)
-         tmp_keyval = upper;
+      if (upper != tmp_keyval)
+        tmp_keyval = upper;
     }
 
+  if (effective_group)
+    *effective_group = group;
+  if (effective_level)
+    *effective_level = level;
+
   return tmp_keyval;
 }
 
-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)
+static gboolean
+gdk_quartz_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;
   GdkModifierType bit;
   guint tmp_modifiers = 0;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
-  g_return_val_if_fail (group >= 0 && group <= 1, FALSE);
-  
   maybe_update_keymap ();
 
   if (keyval)
@@ -466,12 +703,18 @@ gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
 
   if (hardware_keycode < 0 || hardware_keycode >= NUM_KEYCODES)
     return FALSE;
-  
-  /* Check if shift or capslock modify the keyval */
-  for (bit = GDK_SHIFT_MASK; bit < GDK_CONTROL_MASK; bit <<= 1)
+
+  /* Check if modifiers modify the keyval */
+  for (bit = GDK_SHIFT_MASK; bit < GDK_BUTTON1_MASK; bit <<= 1)
     {
-      if (translate_keysym (hardware_keycode, group, state & ~bit, NULL, NULL) !=
-         translate_keysym (hardware_keycode, group, state | bit, NULL, NULL))
+      if (translate_keysym (hardware_keycode,
+                            (bit == GDK_MOD1_MASK) ? 0 : group,
+                            state & ~bit,
+                            NULL, NULL) !=
+         translate_keysym (hardware_keycode,
+                            (bit == GDK_MOD1_MASK) ? 1 : group,
+                            state | bit,
+                            NULL, NULL))
        tmp_modifiers |= bit;
     }
 
@@ -485,3 +728,142 @@ gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
 
   return TRUE;
 }
+
+static void
+gdk_quartz_keymap_add_virtual_modifiers (GdkKeymap       *keymap,
+                                         GdkModifierType *state)
+{
+  if (*state & GDK_MOD2_MASK)
+    *state |= GDK_META_MASK;
+}
+
+static gboolean
+gdk_quartz_keymap_map_virtual_modifiers (GdkKeymap       *keymap,
+                                         GdkModifierType *state)
+{
+  if (*state & GDK_META_MASK)
+    *state |= GDK_MOD2_MASK;
+
+  return TRUE;
+}
+
+static GdkModifierType
+gdk_quartz_keymap_get_modifier_mask (GdkKeymap         *keymap,
+                                     GdkModifierIntent  intent)
+{
+  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;
+
+    case GDK_MODIFIER_INTENT_SHIFT_GROUP:
+      return GDK_MOD1_MASK;
+
+    default:
+      g_return_val_if_reached (0);
+    }
+}
+
+/* What sort of key event is this? Returns one of
+ * GDK_KEY_PRESS, GDK_KEY_RELEASE, GDK_NOTHING (should be ignored)
+ */
+GdkEventType
+_gdk_quartz_keys_event_type (NSEvent *event)
+{
+  unsigned short keycode;
+  unsigned int flags;
+  int i;
+  
+  switch ([event type])
+    {
+    case NSKeyDown:
+      return GDK_KEY_PRESS;
+    case NSKeyUp:
+      return GDK_KEY_RELEASE;
+    case NSFlagsChanged:
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+  
+  /* For flags-changed events, we have to find the special key that caused the
+   * event, and see if it's in the modifier mask. */
+  keycode = [event keyCode];
+  flags = [event modifierFlags];
+  
+  for (i = 0; i < G_N_ELEMENTS (modifier_keys); i++)
+    {
+      if (modifier_keys[i].keycode == keycode)
+       {
+         if (flags & modifier_keys[i].modmask)
+           return GDK_KEY_PRESS;
+         else
+           return GDK_KEY_RELEASE;
+       }
+    }
+  
+  /* Some keypresses (eg: Expose' activations) seem to trigger flags-changed
+   * events for no good reason. Ignore them! */
+  return GDK_NOTHING;
+}
+
+gboolean
+_gdk_quartz_keys_is_modifier (guint keycode)
+{
+  gint i;
+  
+  for (i = 0; i < G_N_ELEMENTS (modifier_keys); i++)
+    {
+      if (modifier_keys[i].modmask == 0)
+       break;
+
+      if (modifier_keys[i].keycode == keycode)
+       return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gdk_quartz_keymap_init (GdkQuartzKeymap *keymap)
+{
+}
+
+static void
+gdk_quartz_keymap_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gdk_quartz_keymap_parent_class)->finalize (object);
+}
+
+static void
+gdk_quartz_keymap_class_init (GdkQuartzKeymapClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkKeymapClass *keymap_class = GDK_KEYMAP_CLASS (klass);
+
+  object_class->finalize = gdk_quartz_keymap_finalize;
+
+  keymap_class->get_direction = gdk_quartz_keymap_get_direction;
+  keymap_class->have_bidi_layouts = gdk_quartz_keymap_have_bidi_layouts;
+  keymap_class->get_caps_lock_state = gdk_quartz_keymap_get_caps_lock_state;
+  keymap_class->get_num_lock_state = gdk_quartz_keymap_get_num_lock_state;
+  keymap_class->get_entries_for_keyval = gdk_quartz_keymap_get_entries_for_keyval;
+  keymap_class->get_entries_for_keycode = gdk_quartz_keymap_get_entries_for_keycode;
+  keymap_class->lookup_key = gdk_quartz_keymap_lookup_key;
+  keymap_class->translate_keyboard_state = gdk_quartz_keymap_translate_keyboard_state;
+  keymap_class->add_virtual_modifiers = gdk_quartz_keymap_add_virtual_modifiers;
+  keymap_class->map_virtual_modifiers = gdk_quartz_keymap_map_virtual_modifiers;
+  keymap_class->get_modifier_mask = gdk_quartz_keymap_get_modifier_mask;
+}