/* gdkkeys-quartz.c * * Copyright (C) 2000 Red Hat, Inc. * Copyright (C) 2005 Imendio AB * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* Some parts of this code come from quartzKeyboard.c, * from the Apple X11 Server. * * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name(s) of the above * copyright holders shall not be used in advertising or otherwise to * promote the sale, use or other dealings in this Software without * prior written authorization. */ #include "config.h" #include #include #include "gdk.h" #include "gdkquartzkeys.h" #include "gdkkeysprivate.h" #include "gdkkeysyms.h" #include "gdkkeys-quartz.h" #include "gdkinternal-quartz.h" #define NUM_KEYCODES 128 #define KEYVALS_PER_KEYCODE 4 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; } /* 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); */ static guint *keyval_array = NULL; const static struct { guint keycode; guint keyval; unsigned int modmask; /* So we can tell when a mod key is pressed/released */ } modifier_keys[] = { { 54, GDK_KEY_Meta_R, GDK_QUARTZ_COMMAND_KEY_MASK }, { 55, GDK_KEY_Meta_L, GDK_QUARTZ_COMMAND_KEY_MASK }, { 56, GDK_KEY_Shift_L, GDK_QUARTZ_SHIFT_KEY_MASK }, { 57, GDK_KEY_Caps_Lock, GDK_QUARTZ_ALPHA_SHIFT_KEY_MASK }, { 58, GDK_KEY_Alt_L, GDK_QUARTZ_ALTERNATE_KEY_MASK }, { 59, GDK_KEY_Control_L, GDK_QUARTZ_CONTROL_KEY_MASK }, { 60, GDK_KEY_Shift_R, GDK_QUARTZ_SHIFT_KEY_MASK }, { 61, GDK_KEY_Alt_R, GDK_QUARTZ_ALTERNATE_KEY_MASK }, { 62, GDK_KEY_Control_R, GDK_QUARTZ_CONTROL_KEY_MASK } }; 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_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, GDK_KEY_Return, 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 */ const static struct { gunichar ucs_value; guint keyval; } special_ucs_table [] = { { 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 }, { 0xf2dc, 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 update_keymap (void) { const void *chr_data = NULL; guint *p; int i; /* 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__ TISInputSourceRef new_layout = TISCopyCurrentKeyboardLayoutInputSource (); CFDataRef layout_data_ref; #else KeyboardLayoutRef new_layout; KeyboardLayoutKind layout_kind; KLGetCurrentKeyboardLayout (&new_layout); #endif g_free (keyval_array); keyval_array = g_new0 (guint, NUM_KEYCODES * KEYVALS_PER_KEYCODE); #ifdef __LP64__ layout_data_ref = (CFDataRef) TISGetInputSourceProperty (new_layout, kTISPropertyUnicodeKeyLayoutData); if (layout_data_ref) chr_data = CFDataGetBytePtr (layout_data_ref); if (chr_data == NULL) { 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, (const void **)&chr_data); for (i = 0; i < NUM_KEYCODES; i++) { int j; 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; c = KeyTranslate (chr_data, key_code, &state); if (state != 0) { UInt32 state2 = 0; c = KeyTranslate (chr_data, key_code | 128, &state2); } if (c != 0 && c != 0x10) { 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) { 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); } } 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