]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkkeys-quartz.c
Check if gdk_unicode_to_keyval() worked before using the result. Makes
[~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 static KeyboardLayoutRef current_layout = NULL;
65
66 /* This is a table of all keyvals. Each keycode gets KEYVALS_PER_KEYCODE entries.
67  * TThere is 1 keyval per modifier (Nothing, Shift, Alt, Shift+Alt);
68  */
69 static guint *keyval_array = NULL;
70
71 static inline UniChar
72 macroman2ucs (unsigned char c)
73 {
74   /* Precalculated table mapping MacRoman-128 to Unicode. Generated
75      by creating single element CFStringRefs then extracting the
76      first character. */
77   
78   static const unsigned short table[128] = {
79     0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1,
80     0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8,
81     0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3,
82     0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc,
83     0x2020, 0xb0, 0xa2, 0xa3, 0xa7, 0x2022, 0xb6, 0xdf,
84     0xae, 0xa9, 0x2122, 0xb4, 0xa8, 0x2260, 0xc6, 0xd8,
85     0x221e, 0xb1, 0x2264, 0x2265, 0xa5, 0xb5, 0x2202, 0x2211,
86     0x220f, 0x3c0, 0x222b, 0xaa, 0xba, 0x3a9, 0xe6, 0xf8,
87     0xbf, 0xa1, 0xac, 0x221a, 0x192, 0x2248, 0x2206, 0xab,
88     0xbb, 0x2026, 0xa0, 0xc0, 0xc3, 0xd5, 0x152, 0x153,
89     0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0xf7, 0x25ca,
90     0xff, 0x178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02,
91     0x2021, 0xb7, 0x201a, 0x201e, 0x2030, 0xc2, 0xca, 0xc1,
92     0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4,
93     0xf8ff, 0xd2, 0xda, 0xdb, 0xd9, 0x131, 0x2c6, 0x2dc,
94     0xaf, 0x2d8, 0x2d9, 0x2da, 0xb8, 0x2dd, 0x2db, 0x2c7
95   };
96
97   if (c < 128)
98     return c;
99   else
100     return table[c - 128];
101 }
102
103 const static struct {
104   guint keycode;
105   guint keyval;
106   unsigned int modmask; /* So we can tell when a mod key is pressed/released */
107 } known_keys[] = {
108   {  54, GDK_Meta_R,    NSCommandKeyMask },
109   {  55, GDK_Meta_L,    NSCommandKeyMask },
110   {  56, GDK_Shift_L,   NSShiftKeyMask },
111   {  57, GDK_Caps_Lock, NSAlphaShiftKeyMask },
112   {  58, GDK_Alt_L,     NSAlternateKeyMask },
113   {  59, GDK_Control_L, NSControlKeyMask },
114   {  60, GDK_Shift_R,   NSShiftKeyMask },
115   {  61, GDK_Alt_R,     NSAlternateKeyMask },
116   {  62, GDK_Control_R, NSControlKeyMask },
117   { 122, GDK_F1, 0 },
118   { 120, GDK_F2, 0 },
119   {  99, GDK_F3, 0 },
120   { 118, GDK_F4, 0 },
121   {  96, GDK_F5, 0 },
122   {  97, GDK_F6, 0 },
123   {  98, GDK_F7, 0 },
124   { 100, GDK_F8, 0 },
125   { 101, GDK_F9, 0 },
126   { 109, GDK_F10, 0 },
127   { 103, GDK_F11, 0 },
128   { 111, GDK_F12, 0 },
129   { 105, GDK_F13, 0 },
130   { 107, GDK_F14, 0 },
131   { 113, GDK_F15, 0 },
132   { 106, GDK_F16, 0 }
133 };
134
135 const static struct {
136   guint keycode;
137   guint normal_keyval, keypad_keyval;
138 } known_numeric_keys[] = {
139   { 65, GDK_period, GDK_KP_Decimal },
140   { 67, GDK_asterisk, GDK_KP_Multiply },
141   { 69, GDK_plus, GDK_KP_Add },
142   { 75, GDK_slash, GDK_KP_Divide },
143   { 76, 0x01000003, GDK_KP_Enter },
144   { 78, GDK_minus, GDK_KP_Subtract },
145   { 81, GDK_equal, GDK_KP_Equal },
146   { 82, GDK_0, GDK_KP_0 },
147   { 83, GDK_1, GDK_KP_1 },
148   { 84, GDK_2, GDK_KP_2 },
149   { 85, GDK_3, GDK_KP_3 },
150   { 86, GDK_4, GDK_KP_4 },
151   { 87, GDK_5, GDK_KP_5 },
152   { 88, GDK_6, GDK_KP_6 },
153   { 89, GDK_7, GDK_KP_7 },
154   { 91, GDK_8, GDK_KP_8 },
155   { 92, GDK_9, GDK_KP_9 }
156 };
157
158 /* These values aren't covered by gdk_unicode_to_keyval */
159 const static struct {
160   gunichar ucs_value;
161   guint keyval;
162 } special_ucs_table [] = {
163   { 0x0001, GDK_Home },
164   { 0x0003, GDK_Return },
165   { 0x0004, GDK_End },
166   { 0x0008, GDK_BackSpace },
167   { 0x0009, GDK_Tab },
168   { 0x000b, GDK_Page_Up },
169   { 0x000c, GDK_Page_Down },
170   { 0x000d, GDK_Return },
171   { 0x001b, GDK_Escape },
172   { 0x001c, GDK_Left },
173   { 0x001d, GDK_Right },
174   { 0x001e, GDK_Up },
175   { 0x001f, GDK_Down },
176   { 0x007f, GDK_Delete }
177 };
178
179 static void
180 maybe_update_keymap (void)
181 {
182   KeyboardLayoutRef new_layout;
183
184   KLGetCurrentKeyboardLayout (&new_layout);
185
186   if (new_layout != current_layout)
187     {
188       guint *p;
189       int i;
190
191       KeyboardLayoutKind layout_kind;
192       
193       g_free (keyval_array);
194       keyval_array = g_new0 (guint, NUM_KEYCODES * KEYVALS_PER_KEYCODE);
195
196       /* Get the layout kind */
197       KLGetKeyboardLayoutProperty (new_layout, kKLKind, (const void **)&layout_kind);
198
199       /* 8-bit-only keyabord layout */
200       if (layout_kind == kKLKCHRKind)
201         { 
202           const void *chr_data;
203           
204           /* Get chr data */
205           KLGetKeyboardLayoutProperty (new_layout, kKLKCHRData, (const void **)&chr_data);
206           
207           for (i = 0; i < NUM_KEYCODES; i++) 
208             {
209               int j;
210               UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
211
212               p = keyval_array + i * KEYVALS_PER_KEYCODE;
213               
214               for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
215                 {
216                   UInt32 c, state = 0;
217                   UInt16 key_code;
218                   UniChar uc;
219                   
220                   key_code = modifiers[j] | i;
221                   c = KeyTranslate (chr_data, key_code, &state);
222
223                   if (state != 0)
224                     {
225                       UInt32 state2 = 0;
226                       c = KeyTranslate (chr_data, key_code | 128, &state2);
227                     }
228
229                   if (c != 0 && c != 0x10)
230                     {
231                       int k;
232                       gboolean found = FALSE;
233
234                       /* FIXME: some keyboard layouts (e.g. Russian) use
235                        * a different 8-bit character set. We should
236                        * check for this. Not a serious problem, because
237                        * most (all?) of these layouts also have a
238                        * uchr version. 
239                        */
240                       uc = macroman2ucs (c);
241
242                       for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
243                         {
244                           if (special_ucs_table[k].ucs_value == uc)
245                             {
246                               p[j] = special_ucs_table[k].keyval;
247                               found = TRUE;
248                               break;
249                             }
250                         }
251                       
252                       /* Special-case shift-tab since GTK+ expects
253                        * GDK_ISO_Left_Tab for that. 
254                        */
255                       if (found && p[j] == GDK_Tab && modifiers[j] == shiftKey) 
256                         p[j] = GDK_ISO_Left_Tab;
257
258                       if (!found)
259                         {
260                           guint tmp;
261                           
262                           tmp = gdk_unicode_to_keyval (uc);
263                           if (tmp != (uc | 0x01000000))
264                             p[j] = tmp;
265                           else
266                             p[j] = 0;
267                         }
268                     }
269                 }
270               
271               if (p[3] == p[2])
272                 p[3] = 0;
273               if (p[2] == p[1])
274                 p[2] = 0;
275               if (p[1] == p[0])
276                 p[1] = 0;
277               if (p[0] == p[2] && 
278                   p[1] == p[3])
279                 p[2] = p[3] = 0;
280             }
281         }
282       /* unicode keyboard layout */
283       else if (layout_kind == kKLKCHRuchrKind || layout_kind == kKLuchrKind)
284         { 
285           const void *chr_data;
286           
287           /* Get chr data */
288           KLGetKeyboardLayoutProperty (new_layout, kKLuchrData, (const void **)&chr_data);
289           
290           for (i = 0; i < NUM_KEYCODES; i++) 
291             {
292               int j;
293               UInt32 modifiers[] = {0, shiftKey, optionKey, shiftKey | optionKey};
294               UniChar chars[4];
295               UniCharCount nChars;
296
297               p = keyval_array + i * KEYVALS_PER_KEYCODE;
298
299               for (j = 0; j < KEYVALS_PER_KEYCODE; j++)
300                 {
301                   UInt32 state = 0;
302                   OSStatus err;
303                   UInt16 key_code;
304                   UniChar uc;
305                   
306                   key_code = modifiers[j] | i;
307                   err = UCKeyTranslate (chr_data, i, kUCKeyActionDown,
308                                         (modifiers[j] >> 8) & 0xFF,
309                                         LMGetKbdType(),
310                                         kUCKeyTranslateNoDeadKeysMask,
311                                         &state, 4, &nChars, chars);
312
313
314                   /* FIXME: Theoretically, we can get multiple UTF-16 values;
315                    * we should convert them to proper unicode and figure
316                    * out whether there are really keyboard layouts that
317                    * give us more than one character for one keypress. */
318                   if (err == noErr && nChars == 1)
319                     {
320                       int k;
321                       gboolean found = FALSE;
322                       
323                       uc = chars[0];
324
325                       for (k = 0; k < G_N_ELEMENTS (special_ucs_table); k++) 
326                         {
327                           if (special_ucs_table[k].ucs_value == uc)
328                             {
329                               p[j] = special_ucs_table[k].keyval;
330                               found = TRUE;
331                               break;
332                             }
333                         }
334
335                       /* Special-case shift-tab since GTK+ expects
336                        * GDK_ISO_Left_Tab for that.
337                        */
338                       if (found && p[j] == GDK_Tab && modifiers[j] == shiftKey)
339                         p[j] = GDK_ISO_Left_Tab;
340                       
341                       if (!found)
342                         {
343                           guint tmp;
344                           
345                           tmp = gdk_unicode_to_keyval (uc);
346                           if (tmp != (uc | 0x01000000))
347                             p[j] = tmp;
348                           else
349                             p[j] = 0;
350                         }
351                     }
352                 }
353               
354               if (p[3] == p[2])
355                 p[3] = 0;
356               if (p[2] == p[1])
357                 p[2] = 0;
358               if (p[1] == p[0])
359                 p[1] = 0;
360               if (p[0] == p[2] && 
361                   p[1] == p[3])
362                 p[2] = p[3] = 0;
363             }
364         }
365       else
366         {
367           g_error ("unknown type of keyboard layout (neither KCHR nor uchr)"
368                    " - not supported right now");
369         }
370
371       for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
372         {
373           p = keyval_array + known_keys[i].keycode * KEYVALS_PER_KEYCODE;
374
375           if (p[0] == 0 && p[1] == 0 && 
376               p[2] == 0 && p[3] == 0)
377             p[0] = known_keys[i].keyval;
378         }
379
380       for (i = 0; i < G_N_ELEMENTS (known_numeric_keys); i++)
381         {
382           p = keyval_array + known_numeric_keys[i].keycode * KEYVALS_PER_KEYCODE;
383
384           if (p[0] == known_numeric_keys[i].normal_keyval)
385             p[0] = known_numeric_keys[i].keypad_keyval;
386         }
387       
388       if (current_layout)
389         g_signal_emit_by_name (default_keymap, "keys_changed");
390
391       current_layout = new_layout;
392     }
393 }
394
395 GdkKeymap *
396 gdk_keymap_get_for_display (GdkDisplay *display)
397 {
398   g_return_val_if_fail (display == gdk_display_get_default (), NULL);
399
400   if (default_keymap == NULL)
401     default_keymap = g_object_new (gdk_keymap_get_type (), NULL);
402
403   return default_keymap;
404 }
405
406 PangoDirection
407 gdk_keymap_get_direction (GdkKeymap *keymap)
408 {
409   return PANGO_DIRECTION_NEUTRAL;
410 }
411
412 gboolean
413 gdk_keymap_have_bidi_layouts (GdkKeymap *keymap)
414 {
415   /* FIXME: Can we implement this? */
416   return FALSE;
417 }
418
419 gboolean
420 gdk_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
421                                    guint          keyval,
422                                    GdkKeymapKey **keys,
423                                    gint          *n_keys)
424 {
425   GArray *keys_array;
426   int i;
427
428   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
429   g_return_val_if_fail (keys != NULL, FALSE);
430   g_return_val_if_fail (n_keys != NULL, FALSE);
431   g_return_val_if_fail (keyval != 0, FALSE);
432
433   maybe_update_keymap ();
434
435   *n_keys = 0;
436   keys_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
437
438   for (i = 0; i < NUM_KEYCODES * KEYVALS_PER_KEYCODE; i++)
439     {
440       GdkKeymapKey key;
441
442       if (keyval_array[i] != keyval)
443         continue;
444
445       (*n_keys)++;
446
447       key.keycode = i / KEYVALS_PER_KEYCODE;
448       key.group = 0;
449       key.level = i % KEYVALS_PER_KEYCODE;
450
451       g_array_append_val (keys_array, key);
452     }
453
454   *keys = (GdkKeymapKey *)g_array_free (keys_array, FALSE);
455   
456   return *n_keys > 0;;
457 }
458
459 gboolean
460 gdk_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
461                                     guint          hardware_keycode,
462                                     GdkKeymapKey **keys,
463                                     guint        **keyvals,
464                                     gint          *n_entries)
465 {
466   GArray *keys_array, *keyvals_array;
467   int i;
468   guint *p;
469
470   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
471   g_return_val_if_fail (n_entries != NULL, FALSE);
472
473   maybe_update_keymap ();
474
475   *n_entries = 0;
476
477   if (hardware_keycode > NUM_KEYCODES)
478     return FALSE;
479
480   if (keys)
481     keys_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
482   else
483     keys_array = NULL;
484
485   if (keyvals)
486     keyvals_array = g_array_new (FALSE, FALSE, sizeof (guint));
487   else
488     keyvals_array = NULL;
489
490   p = keyval_array + hardware_keycode * KEYVALS_PER_KEYCODE;
491   
492   for (i = 0; i < KEYVALS_PER_KEYCODE; i++)
493     {
494       if (!p[i])
495         continue;
496
497       (*n_entries)++;
498       
499       if (keyvals_array)
500         g_array_append_val (keyvals_array, p[i]);
501
502       if (keys_array)
503         {
504           GdkKeymapKey key;
505
506           key.keycode = hardware_keycode;
507           key.group = i / 2;
508           key.level = i % 2;
509
510           g_array_append_val (keys_array, key);
511         }
512     }
513   
514   if (keys)
515     *keys = (GdkKeymapKey *)g_array_free (keys_array, FALSE);
516
517   if (keyvals)
518     *keyvals = (guint *)g_array_free (keyvals_array, FALSE);
519
520   return *n_entries > 0;
521 }
522
523 guint
524 gdk_keymap_lookup_key (GdkKeymap          *keymap,
525                        const GdkKeymapKey *key)
526 {
527   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0);
528   g_return_val_if_fail (key != NULL, 0);
529   g_return_val_if_fail (key->group < 4, 0);
530
531   /* FIXME: Implement */
532
533   return 0;
534 }
535
536 #define GET_KEYVAL(keycode, group, level) (keyval_array[(keycode * KEYVALS_PER_KEYCODE + group * 2 + level)])
537
538 static guint
539 translate_keysym (guint           hardware_keycode,
540                   gint            group,
541                   GdkModifierType state,
542                   gint           *effective_group,
543                   gint           *effective_level)
544 {
545   gint level;
546   guint tmp_keyval;
547
548   level = (state & GDK_SHIFT_MASK) ? 1 : 0;
549
550   if (!(GET_KEYVAL (hardware_keycode, group, 0) || GET_KEYVAL (hardware_keycode, group, 1)) &&
551       (GET_KEYVAL (hardware_keycode, 0, 0) || GET_KEYVAL (hardware_keycode, 0, 1)))
552     group = 0;
553
554   if (!GET_KEYVAL (hardware_keycode, group, level) &&
555       GET_KEYVAL (hardware_keycode, group, 0))
556     level = 0;
557
558   tmp_keyval = GET_KEYVAL (hardware_keycode, group, level);
559
560   if (state & GDK_LOCK_MASK)
561     {
562       guint upper = gdk_keyval_to_upper (tmp_keyval);
563       if (upper != tmp_keyval)
564         tmp_keyval = upper;
565     }
566
567   return tmp_keyval;
568 }
569
570 gboolean
571 gdk_keymap_translate_keyboard_state (GdkKeymap       *keymap,
572                                      guint            hardware_keycode,
573                                      GdkModifierType  state,
574                                      gint             group,
575                                      guint           *keyval,
576                                      gint            *effective_group,
577                                      gint            *level,
578                                      GdkModifierType *consumed_modifiers)
579 {
580   guint tmp_keyval;
581   GdkModifierType bit;
582   guint tmp_modifiers = 0;
583
584   g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
585   g_return_val_if_fail (group >= 0 && group <= 1, FALSE);
586   
587   maybe_update_keymap ();
588
589   if (keyval)
590     *keyval = 0;
591   if (effective_group)
592     *effective_group = 0;
593   if (level)
594     *level = 0;
595   if (consumed_modifiers)
596     *consumed_modifiers = 0;
597
598   if (hardware_keycode < 0 || hardware_keycode >= NUM_KEYCODES)
599     return FALSE;
600   
601   /* Check if shift or capslock modify the keyval */
602   for (bit = GDK_SHIFT_MASK; bit < GDK_CONTROL_MASK; bit <<= 1)
603     {
604       if (translate_keysym (hardware_keycode, group, state & ~bit, NULL, NULL) !=
605           translate_keysym (hardware_keycode, group, state | bit, NULL, NULL))
606         tmp_modifiers |= bit;
607     }
608
609   tmp_keyval = translate_keysym (hardware_keycode, group, state, level, effective_group);
610
611   if (consumed_modifiers)
612     *consumed_modifiers = tmp_modifiers;
613
614   if (keyval)
615     *keyval = tmp_keyval; 
616
617   return TRUE;
618 }
619
620 /* What sort of key event is this? Returns one of
621  * GDK_KEY_PRESS, GDK_KEY_RELEASE, GDK_NOTHING (should be ignored)
622  */
623 GdkEventType
624 _gdk_quartz_keys_event_type (NSEvent *event)
625 {
626   unsigned short keycode;
627   unsigned int flags;
628   int i;
629   
630   switch ([event type])
631     {
632     case NSKeyDown:
633       return GDK_KEY_PRESS;
634     case NSKeyUp:
635       return GDK_KEY_RELEASE;
636     case NSFlagsChanged:
637       break;
638     default:
639       g_assert_not_reached ();
640     }
641   
642   /* For flags-changed events, we have to find the special key that caused the
643    * event, and see if it's in the modifier mask. */
644   keycode = [event keyCode];
645   flags = [event modifierFlags];
646   
647   for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
648     {
649       if (known_keys[i].keycode == keycode)
650         {
651           if (flags & known_keys[i].modmask)
652             return GDK_KEY_PRESS;
653           else
654             return GDK_KEY_RELEASE;
655         }
656     }
657   
658   /* Some keypresses (eg: Expose' activations) seem to trigger flags-changed
659    * events for no good reason. Ignore them! */
660   return GDK_NOTHING;
661 }
662
663 gboolean
664 _gdk_quartz_keys_is_modifier (guint keycode)
665 {
666   gint i;
667   
668   for (i = 0; i < G_N_ELEMENTS (known_keys); i++)
669     {
670       if (known_keys[i].modmask == 0)
671         break;
672
673       if (known_keys[i].keycode == keycode)
674         return TRUE;
675     }
676
677   return FALSE;
678 }