2 * MACDRV keyboard driver
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1996 Albrecht Kleine
6 * Copyright 1997 David Faure
7 * Copyright 1998 Morten Welinder
8 * Copyright 1998 Ulrich Weigand
9 * Copyright 1999 Ove Kåven
10 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
36 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
46 cmdKey = 1 << cmdKeyBit,
47 shiftKey = 1 << shiftKeyBit,
48 alphaLock = 1 << alphaLockBit,
49 optionKey = 1 << optionKeyBit,
50 controlKey = 1 << controlKeyBit,
54 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
66 kVK_ISO_Section = 0x0A,
80 kVK_ANSI_Equal = 0x18,
83 kVK_ANSI_Minus = 0x1B,
86 kVK_ANSI_RightBracket = 0x1E,
89 kVK_ANSI_LeftBracket = 0x21,
95 kVK_ANSI_Quote = 0x27,
97 kVK_ANSI_Semicolon = 0x29,
98 kVK_ANSI_Backslash = 0x2A,
99 kVK_ANSI_Comma = 0x2B,
100 kVK_ANSI_Slash = 0x2C,
103 kVK_ANSI_Period = 0x2F,
106 kVK_ANSI_Grave = 0x32,
109 kVK_RightCommand = 0x36, /* invented for Wine; co-opt unused key code */
115 kVK_RightShift = 0x3C,
116 kVK_RightOption = 0x3D,
117 kVK_RightControl = 0x3E,
120 kVK_ANSI_KeypadDecimal = 0x41,
121 kVK_ANSI_KeypadMultiply = 0x43,
122 kVK_ANSI_KeypadPlus = 0x45,
123 kVK_ANSI_KeypadClear = 0x47,
125 kVK_VolumeDown = 0x49,
127 kVK_ANSI_KeypadDivide = 0x4B,
128 kVK_ANSI_KeypadEnter = 0x4C,
129 kVK_ANSI_KeypadMinus = 0x4E,
132 kVK_ANSI_KeypadEquals = 0x51,
133 kVK_ANSI_Keypad0 = 0x52,
134 kVK_ANSI_Keypad1 = 0x53,
135 kVK_ANSI_Keypad2 = 0x54,
136 kVK_ANSI_Keypad3 = 0x55,
137 kVK_ANSI_Keypad4 = 0x56,
138 kVK_ANSI_Keypad5 = 0x57,
139 kVK_ANSI_Keypad6 = 0x58,
140 kVK_ANSI_Keypad7 = 0x59,
142 kVK_ANSI_Keypad8 = 0x5B,
143 kVK_ANSI_Keypad9 = 0x5C,
145 kVK_JIS_Underscore = 0x5E,
146 kVK_JIS_KeypadComma = 0x5F,
165 kVK_ForwardDelete = 0x75,
171 kVK_LeftArrow = 0x7B,
172 kVK_RightArrow = 0x7C,
173 kVK_DownArrow = 0x7D,
178 /* Indexed by Mac virtual keycode values defined above. */
179 static const struct {
183 } default_map[128] = {
184 { 'A', 0x1E, FALSE }, /* kVK_ANSI_A */
185 { 'S', 0x1F, FALSE }, /* kVK_ANSI_S */
186 { 'D', 0x20, FALSE }, /* kVK_ANSI_D */
187 { 'F', 0x21, FALSE }, /* kVK_ANSI_F */
188 { 'H', 0x23, FALSE }, /* kVK_ANSI_H */
189 { 'G', 0x22, FALSE }, /* kVK_ANSI_G */
190 { 'Z', 0x2C, FALSE }, /* kVK_ANSI_Z */
191 { 'X', 0x2D, FALSE }, /* kVK_ANSI_X */
192 { 'C', 0x2E, FALSE }, /* kVK_ANSI_C */
193 { 'V', 0x2F, FALSE }, /* kVK_ANSI_V */
194 { VK_OEM_102, 0x56, TRUE }, /* kVK_ISO_Section */
195 { 'B', 0x30, FALSE }, /* kVK_ANSI_B */
196 { 'Q', 0x10, FALSE }, /* kVK_ANSI_Q */
197 { 'W', 0x11, FALSE }, /* kVK_ANSI_W */
198 { 'E', 0x12, FALSE }, /* kVK_ANSI_E */
199 { 'R', 0x13, FALSE }, /* kVK_ANSI_R */
200 { 'Y', 0x15, FALSE }, /* kVK_ANSI_Y */
201 { 'T', 0x14, FALSE }, /* kVK_ANSI_T */
202 { '1', 0x02, FALSE }, /* kVK_ANSI_1 */
203 { '2', 0x03, FALSE }, /* kVK_ANSI_2 */
204 { '3', 0x04, FALSE }, /* kVK_ANSI_3 */
205 { '4', 0x05, FALSE }, /* kVK_ANSI_4 */
206 { '6', 0x07, FALSE }, /* kVK_ANSI_6 */
207 { '5', 0x06, FALSE }, /* kVK_ANSI_5 */
208 { VK_OEM_PLUS, 0x0D, FALSE }, /* kVK_ANSI_Equal */
209 { '9', 0x0A, FALSE }, /* kVK_ANSI_9 */
210 { '7', 0x08, FALSE }, /* kVK_ANSI_7 */
211 { VK_OEM_MINUS, 0x0C, FALSE }, /* kVK_ANSI_Minus */
212 { '8', 0x09, FALSE }, /* kVK_ANSI_8 */
213 { '0', 0x0B, FALSE }, /* kVK_ANSI_0 */
214 { VK_OEM_6, 0x1B, FALSE }, /* kVK_ANSI_RightBracket */
215 { 'O', 0x18, FALSE }, /* kVK_ANSI_O */
216 { 'U', 0x16, FALSE }, /* kVK_ANSI_U */
217 { VK_OEM_4, 0x1A, FALSE }, /* kVK_ANSI_LeftBracket */
218 { 'I', 0x17, FALSE }, /* kVK_ANSI_I */
219 { 'P', 0x19, FALSE }, /* kVK_ANSI_P */
220 { VK_RETURN, 0x1C, TRUE }, /* kVK_Return */
221 { 'L', 0x26, FALSE }, /* kVK_ANSI_L */
222 { 'J', 0x24, FALSE }, /* kVK_ANSI_J */
223 { VK_OEM_7, 0x28, FALSE }, /* kVK_ANSI_Quote */
224 { 'K', 0x25, FALSE }, /* kVK_ANSI_K */
225 { VK_OEM_1, 0x27, FALSE }, /* kVK_ANSI_Semicolon */
226 { VK_OEM_5, 0x2B, FALSE }, /* kVK_ANSI_Backslash */
227 { VK_OEM_COMMA, 0x33, FALSE }, /* kVK_ANSI_Comma */
228 { VK_OEM_2, 0x35, FALSE }, /* kVK_ANSI_Slash */
229 { 'N', 0x31, FALSE }, /* kVK_ANSI_N */
230 { 'M', 0x32, FALSE }, /* kVK_ANSI_M */
231 { VK_OEM_PERIOD, 0x34, FALSE }, /* kVK_ANSI_Period */
232 { VK_TAB, 0x0F, TRUE }, /* kVK_Tab */
233 { VK_SPACE, 0x39, TRUE }, /* kVK_Space */
234 { VK_OEM_3, 0x29, FALSE }, /* kVK_ANSI_Grave */
235 { VK_BACK, 0x0E, TRUE }, /* kVK_Delete */
236 { 0, 0, FALSE }, /* 0x34 unused */
237 { VK_ESCAPE, 0x01, TRUE }, /* kVK_Escape */
238 { VK_RMENU, 0x38 | 0x100, TRUE }, /* kVK_RightCommand */
239 { VK_LMENU, 0x38, TRUE }, /* kVK_Command */
240 { VK_LSHIFT, 0x2A, TRUE }, /* kVK_Shift */
241 { VK_CAPITAL, 0x3A, TRUE }, /* kVK_CapsLock */
242 { 0, 0, FALSE }, /* kVK_Option */
243 { VK_LCONTROL, 0x1D, TRUE }, /* kVK_Control */
244 { VK_RSHIFT, 0x36, TRUE }, /* kVK_RightShift */
245 { 0, 0, FALSE }, /* kVK_RightOption */
246 { VK_RCONTROL, 0x1D | 0x100, TRUE }, /* kVK_RightControl */
247 { 0, 0, FALSE }, /* kVK_Function */
248 { VK_F17, 0x68, TRUE }, /* kVK_F17 */
249 { VK_DECIMAL, 0x53, TRUE }, /* kVK_ANSI_KeypadDecimal */
250 { 0, 0, FALSE }, /* 0x42 unused */
251 { VK_MULTIPLY, 0x37, TRUE }, /* kVK_ANSI_KeypadMultiply */
252 { 0, 0, FALSE }, /* 0x44 unused */
253 { VK_ADD, 0x4E, TRUE }, /* kVK_ANSI_KeypadPlus */
254 { 0, 0, FALSE }, /* 0x46 unused */
255 { VK_OEM_CLEAR, 0x59, TRUE }, /* kVK_ANSI_KeypadClear */
256 { VK_VOLUME_UP, 0 | 0x100, TRUE }, /* kVK_VolumeUp */
257 { VK_VOLUME_DOWN, 0 | 0x100, TRUE }, /* kVK_VolumeDown */
258 { VK_VOLUME_MUTE, 0 | 0x100, TRUE }, /* kVK_Mute */
259 { VK_DIVIDE, 0x35 | 0x100, TRUE }, /* kVK_ANSI_KeypadDivide */
260 { VK_RETURN, 0x1C | 0x100, TRUE }, /* kVK_ANSI_KeypadEnter */
261 { 0, 0, FALSE }, /* 0x4D unused */
262 { VK_SUBTRACT, 0x4A, TRUE }, /* kVK_ANSI_KeypadMinus */
263 { VK_F18, 0x69, TRUE }, /* kVK_F18 */
264 { VK_F19, 0x6A, TRUE }, /* kVK_F19 */
265 { VK_OEM_NEC_EQUAL, 0x0D | 0x100, TRUE }, /* kVK_ANSI_KeypadEquals */
266 { VK_NUMPAD0, 0x52, TRUE }, /* kVK_ANSI_Keypad0 */
267 { VK_NUMPAD1, 0x4F, TRUE }, /* kVK_ANSI_Keypad1 */
268 { VK_NUMPAD2, 0x50, TRUE }, /* kVK_ANSI_Keypad2 */
269 { VK_NUMPAD3, 0x51, TRUE }, /* kVK_ANSI_Keypad3 */
270 { VK_NUMPAD4, 0x4B, TRUE }, /* kVK_ANSI_Keypad4 */
271 { VK_NUMPAD5, 0x4C, TRUE }, /* kVK_ANSI_Keypad5 */
272 { VK_NUMPAD6, 0x4D, TRUE }, /* kVK_ANSI_Keypad6 */
273 { VK_NUMPAD7, 0x47, TRUE }, /* kVK_ANSI_Keypad7 */
274 { VK_F20, 0x6B, TRUE }, /* kVK_F20 */
275 { VK_NUMPAD8, 0x48, TRUE }, /* kVK_ANSI_Keypad8 */
276 { VK_NUMPAD9, 0x49, TRUE }, /* kVK_ANSI_Keypad9 */
277 { 0xFF, 0x7D, TRUE }, /* kVK_JIS_Yen */
278 { 0xC1, 0x73, TRUE }, /* kVK_JIS_Underscore */
279 { VK_SEPARATOR, 0x7E, TRUE }, /* kVK_JIS_KeypadComma */
280 { VK_F5, 0x3F, TRUE }, /* kVK_F5 */
281 { VK_F6, 0x40, TRUE }, /* kVK_F6 */
282 { VK_F7, 0x41, TRUE }, /* kVK_F7 */
283 { VK_F3, 0x3D, TRUE }, /* kVK_F3 */
284 { VK_F8, 0x42, TRUE }, /* kVK_F8 */
285 { VK_F9, 0x43, TRUE }, /* kVK_F9 */
286 { 0xFF, 0x72, TRUE }, /* kVK_JIS_Eisu */
287 { VK_F11, 0x57, TRUE }, /* kVK_F11 */
288 { VK_OEM_RESET, 0x71, TRUE }, /* kVK_JIS_Kana */
289 { VK_F13, 0x64, TRUE }, /* kVK_F13 */
290 { VK_F16, 0x67, TRUE }, /* kVK_F16 */
291 { VK_F14, 0x65, TRUE }, /* kVK_F14 */
292 { 0, 0, FALSE }, /* 0x6C unused */
293 { VK_F10, 0x44, TRUE }, /* kVK_F10 */
294 { 0, 0, FALSE }, /* 0x6E unused */
295 { VK_F12, 0x58, TRUE }, /* kVK_F12 */
296 { 0, 0, FALSE }, /* 0x70 unused */
297 { VK_F15, 0x66, TRUE }, /* kVK_F15 */
298 { VK_INSERT, 0x52 | 0x100, TRUE }, /* kVK_Help */ /* map to Insert */
299 { VK_HOME, 0x47 | 0x100, TRUE }, /* kVK_Home */
300 { VK_PRIOR, 0x49 | 0x100, TRUE }, /* kVK_PageUp */
301 { VK_DELETE, 0x53 | 0x100, TRUE }, /* kVK_ForwardDelete */
302 { VK_F4, 0x3E, TRUE }, /* kVK_F4 */
303 { VK_END, 0x4F | 0x100, TRUE }, /* kVK_End */
304 { VK_F2, 0x3C, TRUE }, /* kVK_F2 */
305 { VK_NEXT, 0x51 | 0x100, TRUE }, /* kVK_PageDown */
306 { VK_F1, 0x3B, TRUE }, /* kVK_F1 */
307 { VK_LEFT, 0x4B | 0x100, TRUE }, /* kVK_LeftArrow */
308 { VK_RIGHT, 0x4D | 0x100, TRUE }, /* kVK_RightArrow */
309 { VK_DOWN, 0x50 | 0x100, TRUE }, /* kVK_DownArrow */
310 { VK_UP, 0x48 | 0x100, TRUE }, /* kVK_UpArrow */
314 static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
317 CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
318 CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, strlenW(string), kCFAllocatorNull);
319 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
320 if (ignore_diacritics)
321 flags |= kCFCompareDiacriticInsensitive;
322 ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
329 /***********************************************************************
330 * macdrv_compute_keyboard_layout
332 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
336 const UCKeyboardLayout *uchr;
337 const UInt32 modifier_combos[] = {
341 (shiftKey | cmdKey) >> 8,
343 (shiftKey | optionKey) >> 8,
345 UniChar map[128][sizeof(modifier_combos) / sizeof(modifier_combos[0])][4 + 1];
348 int ignore_diacritics;
349 static const struct {
353 { '-', VK_OEM_MINUS },
354 { '+', VK_OEM_PLUS },
355 { '_', VK_OEM_MINUS },
356 { ',', VK_OEM_COMMA },
357 { '.', VK_OEM_PERIOD },
358 { '=', VK_OEM_PLUS },
359 { '>', VK_OEM_PERIOD },
360 { '<', VK_OEM_COMMA },
374 { ':', VK_OEM_PERIOD },
375 { ';', VK_OEM_COMMA },
377 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
379 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
380 { '*', VK_OEM_PLUS },
381 { 0x00B4, VK_OEM_7 },
389 { '?', VK_OEM_PLUS },
391 { 0x00B4, VK_OEM_3 },
392 { '?', VK_OEM_COMMA },
393 { '~', VK_OEM_PLUS },
396 { 0x00A7, VK_OEM_7 },
400 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
401 contiguous ranges. */
402 static const struct {
407 { VK_OEM_1, VK_OEM_3 },
408 { VK_OEM_4, VK_ICO_CLEAR },
410 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
416 if (!thread_data->keyboard_layout_uchr)
418 ERR("no keyboard layout UCHR data\n");
422 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
423 memset(vkey_used, 0, sizeof(vkey_used));
425 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
427 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
428 if (default_map[keyc].fixed)
430 vkey = default_map[keyc].vkey;
431 thread_data->keyc2vkey[keyc] = vkey;
433 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
437 if (thread_data->iso_keyboard)
439 /* In almost all cases, the Mac key codes indicate a physical key position
440 and this corresponds nicely to Win32 scan codes. However, the Mac key
441 codes differ in one case between ANSI and ISO keyboards. For ANSI
442 keyboards, the key to the left of the digits and above the Tab key
443 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
444 some position produces kVK_ISO_Section. The additional key on ISO
445 keyboards, the one to the right of the left Shift key, produces
446 kVK_ANSI_Grave, which is just weird.
448 Since we want the key in that upper left corner to always produce the
449 same scan code (0x29), we need to swap the scan codes of those two
450 Mac key codes for ISO keyboards. */
451 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
452 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
453 thread_data->keyc2scan[kVK_ISO_Section] = temp;
456 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
458 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
459 memset(map, 0, sizeof(map));
460 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
462 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
463 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
465 TRACE("keyc 0x%04x: ", keyc);
467 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
474 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
475 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
476 &deadKeyState, sizeof(map[keyc][combo])/sizeof(map[keyc][combo][0]) - 1,
477 &len, map[keyc][combo]);
479 map[keyc][combo][0] = 0;
481 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
487 /* First try to match key codes to the vkeys for the letters A through Z.
488 Try unmodified first, then with various modifier combinations in succession.
489 On the first pass, try to get a match lacking diacritical marks. On the
490 second pass, accept matches with diacritical marks. */
491 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
493 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
495 for (vkey = 'A'; vkey <= 'Z'; vkey++)
500 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
502 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
505 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
507 thread_data->keyc2vkey[keyc] = vkey;
509 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
510 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
518 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
519 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
521 for (vkey = '0'; vkey <= '9'; vkey++)
526 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
528 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
531 if (char_matches_string(vkey, map[keyc][combo], FALSE))
533 thread_data->keyc2vkey[keyc] = vkey;
535 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
536 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
543 /* Now try to match key codes for certain common punctuation characters to
544 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
545 for (i = 0; i < sizeof(symbol_vkeys) / sizeof(symbol_vkeys[0]); i++)
547 vkey = symbol_vkeys[i].vkey;
552 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
554 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
556 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
557 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
560 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
562 thread_data->keyc2vkey[keyc] = vkey;
564 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
565 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
575 /* For those key codes still without a vkey, try to use the default vkey
576 from the default map, if it's still available. */
577 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
579 DWORD vkey = default_map[keyc].vkey;
581 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
582 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
584 if (!vkey_used[vkey])
586 thread_data->keyc2vkey[keyc] = vkey;
588 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
592 /* For any unassigned key codes which would map to a letter in the default
593 map, but whose normal letter vkey wasn't available, try to find a
596 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
598 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
599 continue; /* not a letter in ANSI layout */
600 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
601 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
603 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
606 thread_data->keyc2vkey[keyc] = vkey;
608 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
611 break; /* no more unused letter vkeys, so stop trying */
614 /* Same thing but with the digits. */
616 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
618 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
619 continue; /* not a digit in ANSI layout */
620 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
621 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
623 while (vkey <= '9' && vkey_used[vkey]) vkey++;
626 thread_data->keyc2vkey[keyc] = vkey;
628 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
631 break; /* no more unused digit vkeys, so stop trying */
634 /* Last chance. Assign any available vkey. */
636 vkey = vkey_ranges[vkey_range].first;
637 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
639 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
640 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
642 while (vkey && vkey_used[vkey])
644 if (vkey == vkey_ranges[vkey_range].last)
647 vkey = vkey_ranges[vkey_range].first;
655 WARN("No more vkeys available!\n");
659 thread_data->keyc2vkey[keyc] = vkey;
661 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
666 /***********************************************************************
667 * macdrv_keyboard_changed
669 * Handler for KEYBOARD_CHANGED events.
671 void macdrv_keyboard_changed(const macdrv_event *event)
673 struct macdrv_thread_data *thread_data = macdrv_thread_data();
675 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
676 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
678 if (thread_data->keyboard_layout_uchr)
679 CFRelease(thread_data->keyboard_layout_uchr);
680 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
681 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
682 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
684 macdrv_compute_keyboard_layout(thread_data);