]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkkeys-quartz.c
Fix a compatibility problem
[~andy/gtk] / gdk / quartz / gdkkeys-quartz.c
1 /* gdkkeys-quartz.c
2  *
3  * Copyright (C) 2000 Red Hat, Inc.
4  * Copyright (C) 2005 Imendio AB
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /* Some parts of this code come from quartzKeyboard.c,
22  * from the Apple X11 Server.
23  *
24  * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
25  *
26  *  Permission is hereby granted, free of charge, to any person
27  *  obtaining a copy of this software and associated documentation files
28  *  (the "Software"), to deal in the Software without restriction,
29  *  including without limitation the rights to use, copy, modify, merge,
30  *  publish, distribute, sublicense, and/or sell copies of the Software,
31  *  and to permit persons to whom the Software is furnished to do so,
32  *  subject to the following conditions:
33  *
34  *  The above copyright notice and this permission notice shall be
35  *  included in all copies or substantial portions of the Software.
36  *
37  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40  *  NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
41  *  HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
42  *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
44  *  DEALINGS IN THE SOFTWARE.
45  *
46  *  Except as contained in this notice, the name(s) of the above
47  *  copyright holders shall not be used in advertising or otherwise to
48  *  promote the sale, use or other dealings in this Software without
49  *  prior written authorization.
50  */
51
52 #include "config.h"
53
54 #include <Carbon/Carbon.h>
55 #include <AppKit/NSEvent.h>
56 #include "gdk.h"
57 #include "gdkkeysyms.h"
58
59 #define NUM_KEYCODES 128
60 #define KEYVALS_PER_KEYCODE 4
61
62 static GdkKeymap *default_keymap = NULL;
63
64 /* Note: we could check only if building against the 10.5 SDK instead, but
65  * that would make non-xml layouts not work in 32-bit which would be a quite
66  * bad regression. This way, old unsupported layouts will just not work in
67  * 64-bit.
68  */
69 #ifdef __LP64__
70 static TISInputSourceRef current_layout = NULL;
71 #else
72 static KeyboardLayoutRef current_layout = NULL;
73 #endif
74
75 /* This is a table of all keyvals. Each keycode gets KEYVALS_PER_KEYCODE entries.
76  * TThere is 1 keyval per modifier (Nothing, Shift, Alt, Shift+Alt);
77  */
78 static guint *keyval_array = NULL;
79
80 static inline UniChar
81 macroman2ucs (unsigned char c)
82 {
83   /* Precalculated table mapping MacRoman-128 to Unicode. Generated
84      by creating single element CFStringRefs then extracting the
85      first character. */
86   
87   static const unsigned short table[128] = {
88     0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1,
89     0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8,
90     0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3,
91     0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc,
92     0x2020, 0xb0, 0xa2, 0xa3, 0xa7, 0x2022, 0xb6, 0xdf,
93     0xae, 0xa9, 0x2122, 0xb4, 0xa8, 0x2260, 0xc6, 0xd8,
94     0x221e, 0xb1, 0x2264, 0x2265, 0xa5, 0xb5, 0x2202, 0x2211,
95     0x220f, 0x3c0, 0x222b, 0xaa, 0xba, 0x3a9, 0xe6, 0xf8,
96     0xbf, 0xa1, 0xac, 0x221a, 0x192, 0x2248, 0x2206, 0xab,
97     0xbb, 0x2026, 0xa0, 0xc0, 0xc3, 0xd5, 0x152, 0x153,
98     0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0xf7, 0x25ca,
99     0xff, 0x178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02,
100     0x2021, 0xb7, 0x201a, 0x201e, 0x2030, 0xc2, 0xca, 0xc1,
101     0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4,
102     0xf8ff, 0xd2, 0xda, 0xdb, 0xd9, 0x131, 0x2c6, 0x2dc,
103     0xaf, 0x2d8, 0x2d9, 0x2da, 0xb8, 0x2dd, 0x2db, 0x2c7
104   };
105
106   if (c < 128)
107     return c;
108   else
109     return table[c - 128];
110 }
111
112 const static struct {
113   guint keycode;
114   guint keyval;
115   unsigned int modmask; /* So we can tell when a mod key is pressed/released */
116 } known_keys[] = {
117   {  54, GDK_Meta_R,    NSCommandKeyMask },
118   {  55, GDK_Meta_L,    NSCommandKeyMask },
119   {  56, GDK_Shift_L,   NSShiftKeyMask },
120   {  57, GDK_Caps_Lock, NSAlphaShiftKeyMask },
121   {  58, GDK_Alt_L,     NSAlternateKeyMask },
122   {  59, GDK_Control_L, NSControlKeyMask },
123   {  60, GDK_Shift_R,   NSShiftKeyMask },
124   {  61, GDK_Alt_R,     NSAlternateKeyMask },
125   {  62, GDK_Control_R, NSControlKeyMask },
126   { 122, GDK_F1, 0 },
127   { 120, GDK_F2, 0 },
128   {  99, GDK_F3, 0 },
129   { 118, GDK_F4, 0 },
130   {  96, GDK_F5, 0 },
131   {  97, GDK_F6, 0 },
132   {  98, GDK_F7, 0 },
133   { 100, GDK_F8, 0 },
134   { 101, GDK_F9, 0 },
135   { 109, GDK_F10, 0 },
136   { 103, GDK_F11, 0 },
137   { 111, GDK_F12, 0 },
138   { 105, GDK_F13, 0 },
139   { 107, GDK_F14, 0 },
140   { 113, GDK_F15, 0 },
141   { 106, GDK_F16, 0 }
142 };
143
144 const static struct {
145   guint keycode;
146   guint normal_keyval, keypad_keyval;
147 } known_numeric_keys[] = {
148   { 65, GDK_period, GDK_KP_Decimal },
149   { 67, GDK_asterisk, GDK_KP_Multiply },
150   { 69, GDK_plus, GDK_KP_Add },
151   { 75, GDK_slash, GDK_KP_Divide },
152   { 76, 0x01000003, GDK_KP_Enter },
153   { 78, GDK_minus, GDK_KP_Subtract },
154   { 81, GDK_equal, GDK_KP_Equal },
155   { 82, GDK_0, GDK_KP_0 },
156   { 83, GDK_1, GDK_KP_1 },
157   { 84, GDK_2, GDK_KP_2 },
158   { 85, GDK_3, GDK_KP_3 },
159   { 86, GDK_4, GDK_KP_4 },
160   { 87, GDK_5, GDK_KP_5 },
161   { 88, GDK_6, GDK_KP_6 },
162   { 89, GDK_7, GDK_KP_7 },
163   { 91, GDK_8, GDK_KP_8 },
164   { 92, GDK_9, GDK_KP_9 }
165 };
166
167 /* These values aren't covered by gdk_unicode_to_keyval */
168 const static struct {
169   gunichar ucs_value;
170   guint keyval;
171 } special_ucs_table [] = {
172   { 0x0001, GDK_Home },
173   { 0x0003, GDK_Return },
174   { 0x0004, GDK_End },
175   { 0x0008, GDK_BackSpace },
176   { 0x0009, GDK_Tab },
177   { 0x000b, GDK_Page_Up },
178   { 0x000c, GDK_Page_Down },
179   { 0x000d, GDK_Return },
180   { 0x001b, GDK_Escape },
181   { 0x001c, GDK_Left },
182   { 0x001d, GDK_Right },
183   { 0x001e, GDK_Up },
184   { 0x001f, GDK_Down },
185   { 0x007f, GDK_Delete }
186 };
187
188 static void
189 maybe_update_keymap (void)
190 {
191   const void *chr_data = NULL;
192
193 #ifdef __LP64__
194   TISInputSourceRef new_layout = TISCopyCurrentKeyboardLayoutInputSource ();
195   CFDataRef layout_data_ref;
196
197 #else
198   KeyboardLayoutRef new_layout;
199   KeyboardLayoutKind layout_kind;
200
201   KLGetCurrentKeyboardLayout (&new_layout);
202 #endif
203
204   if (new_layout != current_layout)
205     {
206       guint *p;
207       int i;
208
209       g_free (keyval_array);
210       keyval_array = g_new0 (guint, NUM_KEYCODES * KEYVALS_PER_KEYCODE);
211
212 #ifdef __LP64__
213       layout_data_ref = (CFDataRef) TISGetInputSourceProperty
214         (new_layout, kTISPropertyUnicodeKeyLayoutData);
215
216       if (layout_data_ref)
217         chr_data = CFDataGetBytePtr (layout_data_ref);
218
219       if (chr_data == NULL)
220         {
221           g_error ("cannot get keyboard layout data");
222           return;
223         }
224 #else
225       /* Get the layout kind */
226       KLGetKeyboardLayoutProperty (new_layout, kKLKind, (const void **)&layout_kind);
227
228       /* 8-bit-only keyabord layout */
229       if (layout_kind == kKLKCHRKind)
230         { 
231           /* Get chr data */
232           KLGetKeyboardLayoutProperty (new_layout, kKLKCHRData, (const void **)&chr_data);
233           
234           for (i = 0; i < NUM_KEYCODES; i++) 
235             {
236               int j;
237               UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
238
239               p = keyval_array + i * KEYVALS_PER_KEYCODE;
240               
241               for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
242                 {
243                   UInt32 c, state = 0;
244                   UInt16 key_code;
245                   UniChar uc;
246                   
247                   key_code = modifiers[j] | i;
248                   c = KeyTranslate (chr_data, key_code, &state);
249
250                   if (state != 0)
251                     {
252                       UInt32 state2 = 0;
253                       c = KeyTranslate (chr_data, key_code | 128, &state2);
254                     }
255
256                   if (c != 0 && c != 0x10)
257                     {
258                       int k;
259                       gboolean found = FALSE;
260
261                       /* FIXME: some keyboard layouts (e.g. Russian) use
262                        * a different 8-bit character set. We should
263                        * check for this. Not a serious problem, because
264                        * most (all?) of these layouts also have a
265                        * uchr version. 
266                        */
267                       uc = macroman2ucs (c);
268
269                       for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
270                         {
271                           if (special_ucs_table[k].ucs_value == uc)
272                             {
273                               p[j] = special_ucs_table[k].keyval;
274                               found = TRUE;
275                               break;
276                             }
277                         }
278                       
279                       /* Special-case shift-tab since GTK+ expects
280                        * GDK_ISO_Left_Tab for that. 
281                        */
282                       if (found && p[j] == GDK_Tab && modifiers[j] == shiftKey) 
283                         p[j] = GDK_ISO_Left_Tab;
284
285                       if (!found)
286                         {
287                           guint tmp;
288                           
289                           tmp = gdk_unicode_to_keyval (uc);
290                           if (tmp != (uc | 0x01000000))
291                             p[j] = tmp;
292                           else
293                             p[j] = 0;
294                         }
295                     }
296                 }
297               
298               if (p[3] == p[2])
299                 p[3] = 0;
300               if (p[2] == p[1])
301                 p[2] = 0;
302               if (p[1] == p[0])
303                 p[1] = 0;
304               if (p[0] == p[2] && 
305                   p[1] == p[3])
306                 p[2] = p[3] = 0;
307             }
308         }
309       /* unicode keyboard layout */
310       else if (layout_kind == kKLKCHRuchrKind || layout_kind == kKLuchrKind)
311         { 
312           /* Get chr data */
313           KLGetKeyboardLayoutProperty (new_layout, kKLuchrData, (const void **)&chr_data);
314 #endif
315           
316           for (i = 0; i < NUM_KEYCODES; i++) 
317             {
318               int j;
319               UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
320               UniChar chars[4];
321               UniCharCount nChars;
322
323               p = keyval_array + i * KEYVALS_PER_KEYCODE;
324
325               for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
326                 {
327                   UInt32 state = 0;
328                   OSStatus err;
329                   UInt16 key_code;
330                   UniChar uc;
331                   
332                   key_code = modifiers[j] | i;
333                   err = UCKeyTranslate (chr_data, i, kUCKeyActionDown,
334                                         (modifiers[j] >> 8) & 0xFF,
335                                         LMGetKbdType(),
336                                         kUCKeyTranslateNoDeadKeysMask,
337                                         &state, 4, &nChars, chars);
338
339
340                   /* FIXME: Theoretically, we can get multiple UTF-16 values;
341                    * we should convert them to proper unicode and figure
342                    * out whether there are really keyboard layouts that
343                    * give us more than one character for one keypress. */
344                   if (err == noErr && nChars == 1)
345                     {
346                       int k;
347                       gboolean found = FALSE;
348                       
349                       uc = chars[0];
350
351                       for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
352                         {
353                           if (special_ucs_table[k].ucs_value == uc)
354                             {
355                               p[j] = special_ucs_table[k].keyval;
356                               found = TRUE;
357                               break;
358                             }
359                         }
360
361                       /* Special-case shift-tab since GTK+ expects
362                        * GDK_ISO_Left_Tab for that.
363                        */
364                       if (found && p[j] == GDK_Tab && modifiers[j] == shiftKey)
365                         p[j] = GDK_ISO_Left_Tab;
366                       
367                       if (!found)
368                         {
369                           guint tmp;
370                           
371                           tmp = gdk_unicode_to_keyval (uc);
372                           if (tmp != (uc | 0x01000000))
373                             p[j] = tmp;
374                           else
375                             p[j] = 0;
376                         }
377                     }
378                 }
379               
380               if (p[3] == p[2])
381                 p[3] = 0;
382               if (p[2] == p[1])
383                 p[2] = 0;
384               if (p[1] == p[0])
385                 p[1] = 0;
386               if (p[0] == p[2] && 
387                   p[1] == p[3])
388                 p[2] = p[3] = 0;
389             }
390 #ifndef __LP64__
391         }
392       else
393         {
394           g_error ("unknown type of keyboard layout (neither KCHR nor uchr)"
395                    " - not supported right now");
396         }
397 #endif
398
399       for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
400         {
401           p = keyval_array + known_keys[i].keycode * KEYVALS_PER_KEYCODE;
402
403           if (p[0] == 0 && p[1] == 0 && 
404               p[2] == 0 && p[3] == 0)
405             p[0] = known_keys[i].keyval;
406         }
407
408       for (i = 0; i < G_N_ELEMENTS (known_numeric_keys); i++)
409         {
410           p = keyval_array + known_numeric_keys[i].keycode * KEYVALS_PER_KEYCODE;
411
412           if (p[0] == known_numeric_keys[i].normal_keyval)
413             p[0] = known_numeric_keys[i].keypad_keyval;
414         }
415       
416       if (current_layout)
417         g_signal_emit_by_name (default_keymap, "keys_changed");
418
419       current_layout = new_layout;
420     }
421 }
422
423 GdkKeymap *
424 gdk_keymap_get_for_display (GdkDisplay *display)
425 {
426   g_return_val_if_fail (display == gdk_display_get_default (), NULL);
427
428   if (default_keymap == NULL)
429     default_keymap = g_object_new (gdk_keymap_get_type (), NULL);
430
431   return default_keymap;
432 }
433
434 PangoDirection
435 gdk_keymap_get_direction (GdkKeymap *keymap)
436 {
437   return PANGO_DIRECTION_NEUTRAL;
438 }
439
440 gboolean
441 gdk_keymap_have_bidi_layouts (GdkKeymap *keymap)
442 {
443   /* FIXME: Can we implement this? */
444   return FALSE;
445 }
446
447 gboolean
448 gdk_keymap_get_caps_lock_state (GdkKeymap *keymap)
449 {
450   /* FIXME: Implement this. */
451   return FALSE;
452 }
453
454 gboolean
455 gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
456                                    guint          keyval,
457                                    GdkKeymapKey **keys,
458                                    gint          *n_keys)
459 {
460   GArray *keys_array;
461   int i;
462
463   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
464   g_return_val_if_fail (keys != NULL, FALSE);
465   g_return_val_if_fail (n_keys != NULL, FALSE);
466   g_return_val_if_fail (keyval != 0, FALSE);
467
468   maybe_update_keymap ();
469
470   *n_keys = 0;
471   keys_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
472
473   for (i = 0; i < NUM_KEYCODES * KEYVALS_PER_KEYCODE; i++)
474     {
475       GdkKeymapKey key;
476
477       if (keyval_array[i] != keyval)
478         continue;
479
480       (*n_keys)++;
481
482       key.keycode = i / KEYVALS_PER_KEYCODE;
483       key.group = 0;
484       key.level = i % KEYVALS_PER_KEYCODE;
485
486       g_array_append_val (keys_array, key);
487     }
488
489   *keys = (GdkKeymapKey *)g_array_free (keys_array, FALSE);
490   
491   return *n_keys > 0;;
492 }
493
494 gboolean
495 gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
496                                     guint          hardware_keycode,
497                                     GdkKeymapKey **keys,
498                                     guint        **keyvals,
499                                     gint          *n_entries)
500 {
501   GArray *keys_array, *keyvals_array;
502   int i;
503   guint *p;
504
505   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
506   g_return_val_if_fail (n_entries != NULL, FALSE);
507
508   maybe_update_keymap ();
509
510   *n_entries = 0;
511
512   if (hardware_keycode > NUM_KEYCODES)
513     return FALSE;
514
515   if (keys)
516     keys_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
517   else
518     keys_array = NULL;
519
520   if (keyvals)
521     keyvals_array = g_array_new (FALSE, FALSE, sizeof (guint));
522   else
523     keyvals_array = NULL;
524
525   p = keyval_array + hardware_keycode * KEYVALS_PER_KEYCODE;
526   
527   for (i = 0; i < KEYVALS_PER_KEYCODE; i++)
528     {
529       if (!p[i])
530         continue;
531
532       (*n_entries)++;
533       
534       if (keyvals_array)
535         g_array_append_val (keyvals_array, p[i]);
536
537       if (keys_array)
538         {
539           GdkKeymapKey key;
540
541           key.keycode = hardware_keycode;
542           key.group = i / 2;
543           key.level = i % 2;
544
545           g_array_append_val (keys_array, key);
546         }
547     }
548   
549   if (keys)
550     *keys = (GdkKeymapKey *)g_array_free (keys_array, FALSE);
551
552   if (keyvals)
553     *keyvals = (guint *)g_array_free (keyvals_array, FALSE);
554
555   return *n_entries > 0;
556 }
557
558 guint
559 gdk_keymap_lookup_key (GdkKeymap          *keymap,
560                        const GdkKeymapKey *key)
561 {
562   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0);
563   g_return_val_if_fail (key != NULL, 0);
564   g_return_val_if_fail (key->group < 4, 0);
565
566   /* FIXME: Implement */
567
568   return 0;
569 }
570
571 #define GET_KEYVAL(keycode, group, level) (keyval_array[(keycode * KEYVALS_PER_KEYCODE + group * 2 + level)])
572
573 static guint
574 translate_keysym (guint           hardware_keycode,
575                   gint            group,
576                   GdkModifierType state,
577                   gint           *effective_group,
578                   gint           *effective_level)
579 {
580   gint level;
581   guint tmp_keyval;
582
583   level = (state & GDK_SHIFT_MASK) ? 1 : 0;
584
585   if (!(GET_KEYVAL (hardware_keycode, group, 0) || GET_KEYVAL (hardware_keycode, group, 1)) &&
586       (GET_KEYVAL (hardware_keycode, 0, 0) || GET_KEYVAL (hardware_keycode, 0, 1)))
587     group = 0;
588
589   if (!GET_KEYVAL (hardware_keycode, group, level) &&
590       GET_KEYVAL (hardware_keycode, group, 0))
591     level = 0;
592
593   tmp_keyval = GET_KEYVAL (hardware_keycode, group, level);
594
595   if (state & GDK_LOCK_MASK)
596     {
597       guint upper = gdk_keyval_to_upper (tmp_keyval);
598       if (upper != tmp_keyval)
599         tmp_keyval = upper;
600     }
601
602   return tmp_keyval;
603 }
604
605 gboolean
606 gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
607                                      guint            hardware_keycode,
608                                      GdkModifierType  state,
609                                      gint             group,
610                                      guint           *keyval,
611                                      gint            *effective_group,
612                                      gint            *level,
613                                      GdkModifierType *consumed_modifiers)
614 {
615   guint tmp_keyval;
616   GdkModifierType bit;
617   guint tmp_modifiers = 0;
618
619   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
620   g_return_val_if_fail (group >= 0 && group <= 1, FALSE);
621   
622   maybe_update_keymap ();
623
624   if (keyval)
625     *keyval = 0;
626   if (effective_group)
627     *effective_group = 0;
628   if (level)
629     *level = 0;
630   if (consumed_modifiers)
631     *consumed_modifiers = 0;
632
633   if (hardware_keycode < 0 || hardware_keycode >= NUM_KEYCODES)
634     return FALSE;
635   
636   /* Check if shift or capslock modify the keyval */
637   for (bit = GDK_SHIFT_MASK; bit < GDK_CONTROL_MASK; bit <<= 1)
638     {
639       if (translate_keysym (hardware_keycode, group, state & ~bit, NULL, NULL) !=
640           translate_keysym (hardware_keycode, group, state | bit, NULL, NULL))
641         tmp_modifiers |= bit;
642     }
643
644   tmp_keyval = translate_keysym (hardware_keycode, group, state, level, effective_group);
645
646   if (consumed_modifiers)
647     *consumed_modifiers = tmp_modifiers;
648
649   if (keyval)
650     *keyval = tmp_keyval; 
651
652   return TRUE;
653 }
654
655 void
656 gdk_keymap_add_virtual_modifiers (GdkKeymap       *keymap,
657                                   GdkModifierType *state)
658 {
659   /* FIXME: For now, we've mimiced the Windows backend. */
660 }
661
662 gboolean
663 gdk_keymap_map_virtual_modifiers (GdkKeymap       *keymap,
664                                   GdkModifierType *state)
665 {
666   /* FIXME: For now, we've mimiced the Windows backend. */
667   return TRUE;
668 }
669
670 /* What sort of key event is this? Returns one of
671  * GDK_KEY_PRESS, GDK_KEY_RELEASE, GDK_NOTHING (should be ignored)
672  */
673 GdkEventType
674 _gdk_quartz_keys_event_type (NSEvent *event)
675 {
676   unsigned short keycode;
677   unsigned int flags;
678   int i;
679   
680   switch ([event type])
681     {
682     case NSKeyDown:
683       return GDK_KEY_PRESS;
684     case NSKeyUp:
685       return GDK_KEY_RELEASE;
686     case NSFlagsChanged:
687       break;
688     default:
689       g_assert_not_reached ();
690     }
691   
692   /* For flags-changed events, we have to find the special key that caused the
693    * event, and see if it's in the modifier mask. */
694   keycode = [event keyCode];
695   flags = [event modifierFlags];
696   
697   for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
698     {
699       if (known_keys[i].keycode == keycode)
700         {
701           if (flags & known_keys[i].modmask)
702             return GDK_KEY_PRESS;
703           else
704             return GDK_KEY_RELEASE;
705         }
706     }
707   
708   /* Some keypresses (eg: Expose' activations) seem to trigger flags-changed
709    * events for no good reason. Ignore them! */
710   return GDK_NOTHING;
711 }
712
713 gboolean
714 _gdk_quartz_keys_is_modifier (guint keycode)
715 {
716   gint i;
717   
718   for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
719     {
720       if (known_keys[i].modmask == 0)
721         break;
722
723       if (known_keys[i].keycode == keycode)
724         return TRUE;
725     }
726
727   return FALSE;
728 }