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
);
34 WINE_DECLARE_DEBUG_CHANNEL(key
);
37 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
47 cmdKey
= 1 << cmdKeyBit
,
48 shiftKey
= 1 << shiftKeyBit
,
49 alphaLock
= 1 << alphaLockBit
,
50 optionKey
= 1 << optionKeyBit
,
51 controlKey
= 1 << controlKeyBit
,
55 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
67 kVK_ISO_Section
= 0x0A,
81 kVK_ANSI_Equal
= 0x18,
84 kVK_ANSI_Minus
= 0x1B,
87 kVK_ANSI_RightBracket
= 0x1E,
90 kVK_ANSI_LeftBracket
= 0x21,
96 kVK_ANSI_Quote
= 0x27,
98 kVK_ANSI_Semicolon
= 0x29,
99 kVK_ANSI_Backslash
= 0x2A,
100 kVK_ANSI_Comma
= 0x2B,
101 kVK_ANSI_Slash
= 0x2C,
104 kVK_ANSI_Period
= 0x2F,
107 kVK_ANSI_Grave
= 0x32,
110 kVK_RightCommand
= 0x36, /* invented for Wine; co-opt unused key code */
116 kVK_RightShift
= 0x3C,
117 kVK_RightOption
= 0x3D,
118 kVK_RightControl
= 0x3E,
121 kVK_ANSI_KeypadDecimal
= 0x41,
122 kVK_ANSI_KeypadMultiply
= 0x43,
123 kVK_ANSI_KeypadPlus
= 0x45,
124 kVK_ANSI_KeypadClear
= 0x47,
126 kVK_VolumeDown
= 0x49,
128 kVK_ANSI_KeypadDivide
= 0x4B,
129 kVK_ANSI_KeypadEnter
= 0x4C,
130 kVK_ANSI_KeypadMinus
= 0x4E,
133 kVK_ANSI_KeypadEquals
= 0x51,
134 kVK_ANSI_Keypad0
= 0x52,
135 kVK_ANSI_Keypad1
= 0x53,
136 kVK_ANSI_Keypad2
= 0x54,
137 kVK_ANSI_Keypad3
= 0x55,
138 kVK_ANSI_Keypad4
= 0x56,
139 kVK_ANSI_Keypad5
= 0x57,
140 kVK_ANSI_Keypad6
= 0x58,
141 kVK_ANSI_Keypad7
= 0x59,
143 kVK_ANSI_Keypad8
= 0x5B,
144 kVK_ANSI_Keypad9
= 0x5C,
146 kVK_JIS_Underscore
= 0x5E,
147 kVK_JIS_KeypadComma
= 0x5F,
166 kVK_ForwardDelete
= 0x75,
172 kVK_LeftArrow
= 0x7B,
173 kVK_RightArrow
= 0x7C,
174 kVK_DownArrow
= 0x7D,
179 /* Indexed by Mac virtual keycode values defined above. */
180 static const struct {
184 } default_map
[128] = {
185 { 'A', 0x1E, FALSE
}, /* kVK_ANSI_A */
186 { 'S', 0x1F, FALSE
}, /* kVK_ANSI_S */
187 { 'D', 0x20, FALSE
}, /* kVK_ANSI_D */
188 { 'F', 0x21, FALSE
}, /* kVK_ANSI_F */
189 { 'H', 0x23, FALSE
}, /* kVK_ANSI_H */
190 { 'G', 0x22, FALSE
}, /* kVK_ANSI_G */
191 { 'Z', 0x2C, FALSE
}, /* kVK_ANSI_Z */
192 { 'X', 0x2D, FALSE
}, /* kVK_ANSI_X */
193 { 'C', 0x2E, FALSE
}, /* kVK_ANSI_C */
194 { 'V', 0x2F, FALSE
}, /* kVK_ANSI_V */
195 { VK_OEM_102
, 0x56, TRUE
}, /* kVK_ISO_Section */
196 { 'B', 0x30, FALSE
}, /* kVK_ANSI_B */
197 { 'Q', 0x10, FALSE
}, /* kVK_ANSI_Q */
198 { 'W', 0x11, FALSE
}, /* kVK_ANSI_W */
199 { 'E', 0x12, FALSE
}, /* kVK_ANSI_E */
200 { 'R', 0x13, FALSE
}, /* kVK_ANSI_R */
201 { 'Y', 0x15, FALSE
}, /* kVK_ANSI_Y */
202 { 'T', 0x14, FALSE
}, /* kVK_ANSI_T */
203 { '1', 0x02, FALSE
}, /* kVK_ANSI_1 */
204 { '2', 0x03, FALSE
}, /* kVK_ANSI_2 */
205 { '3', 0x04, FALSE
}, /* kVK_ANSI_3 */
206 { '4', 0x05, FALSE
}, /* kVK_ANSI_4 */
207 { '6', 0x07, FALSE
}, /* kVK_ANSI_6 */
208 { '5', 0x06, FALSE
}, /* kVK_ANSI_5 */
209 { VK_OEM_PLUS
, 0x0D, FALSE
}, /* kVK_ANSI_Equal */
210 { '9', 0x0A, FALSE
}, /* kVK_ANSI_9 */
211 { '7', 0x08, FALSE
}, /* kVK_ANSI_7 */
212 { VK_OEM_MINUS
, 0x0C, FALSE
}, /* kVK_ANSI_Minus */
213 { '8', 0x09, FALSE
}, /* kVK_ANSI_8 */
214 { '0', 0x0B, FALSE
}, /* kVK_ANSI_0 */
215 { VK_OEM_6
, 0x1B, FALSE
}, /* kVK_ANSI_RightBracket */
216 { 'O', 0x18, FALSE
}, /* kVK_ANSI_O */
217 { 'U', 0x16, FALSE
}, /* kVK_ANSI_U */
218 { VK_OEM_4
, 0x1A, FALSE
}, /* kVK_ANSI_LeftBracket */
219 { 'I', 0x17, FALSE
}, /* kVK_ANSI_I */
220 { 'P', 0x19, FALSE
}, /* kVK_ANSI_P */
221 { VK_RETURN
, 0x1C, TRUE
}, /* kVK_Return */
222 { 'L', 0x26, FALSE
}, /* kVK_ANSI_L */
223 { 'J', 0x24, FALSE
}, /* kVK_ANSI_J */
224 { VK_OEM_7
, 0x28, FALSE
}, /* kVK_ANSI_Quote */
225 { 'K', 0x25, FALSE
}, /* kVK_ANSI_K */
226 { VK_OEM_1
, 0x27, FALSE
}, /* kVK_ANSI_Semicolon */
227 { VK_OEM_5
, 0x2B, FALSE
}, /* kVK_ANSI_Backslash */
228 { VK_OEM_COMMA
, 0x33, FALSE
}, /* kVK_ANSI_Comma */
229 { VK_OEM_2
, 0x35, FALSE
}, /* kVK_ANSI_Slash */
230 { 'N', 0x31, FALSE
}, /* kVK_ANSI_N */
231 { 'M', 0x32, FALSE
}, /* kVK_ANSI_M */
232 { VK_OEM_PERIOD
, 0x34, FALSE
}, /* kVK_ANSI_Period */
233 { VK_TAB
, 0x0F, TRUE
}, /* kVK_Tab */
234 { VK_SPACE
, 0x39, TRUE
}, /* kVK_Space */
235 { VK_OEM_3
, 0x29, FALSE
}, /* kVK_ANSI_Grave */
236 { VK_BACK
, 0x0E, TRUE
}, /* kVK_Delete */
237 { 0, 0, FALSE
}, /* 0x34 unused */
238 { VK_ESCAPE
, 0x01, TRUE
}, /* kVK_Escape */
239 { VK_RMENU
, 0x38 | 0x100, TRUE
}, /* kVK_RightCommand */
240 { VK_LMENU
, 0x38, TRUE
}, /* kVK_Command */
241 { VK_LSHIFT
, 0x2A, TRUE
}, /* kVK_Shift */
242 { VK_CAPITAL
, 0x3A, TRUE
}, /* kVK_CapsLock */
243 { 0, 0, FALSE
}, /* kVK_Option */
244 { VK_LCONTROL
, 0x1D, TRUE
}, /* kVK_Control */
245 { VK_RSHIFT
, 0x36, TRUE
}, /* kVK_RightShift */
246 { 0, 0, FALSE
}, /* kVK_RightOption */
247 { VK_RCONTROL
, 0x1D | 0x100, TRUE
}, /* kVK_RightControl */
248 { 0, 0, FALSE
}, /* kVK_Function */
249 { VK_F17
, 0x68, TRUE
}, /* kVK_F17 */
250 { VK_DECIMAL
, 0x53, TRUE
}, /* kVK_ANSI_KeypadDecimal */
251 { 0, 0, FALSE
}, /* 0x42 unused */
252 { VK_MULTIPLY
, 0x37, TRUE
}, /* kVK_ANSI_KeypadMultiply */
253 { 0, 0, FALSE
}, /* 0x44 unused */
254 { VK_ADD
, 0x4E, TRUE
}, /* kVK_ANSI_KeypadPlus */
255 { 0, 0, FALSE
}, /* 0x46 unused */
256 { VK_OEM_CLEAR
, 0x59, TRUE
}, /* kVK_ANSI_KeypadClear */
257 { VK_VOLUME_UP
, 0 | 0x100, TRUE
}, /* kVK_VolumeUp */
258 { VK_VOLUME_DOWN
, 0 | 0x100, TRUE
}, /* kVK_VolumeDown */
259 { VK_VOLUME_MUTE
, 0 | 0x100, TRUE
}, /* kVK_Mute */
260 { VK_DIVIDE
, 0x35 | 0x100, TRUE
}, /* kVK_ANSI_KeypadDivide */
261 { VK_RETURN
, 0x1C | 0x100, TRUE
}, /* kVK_ANSI_KeypadEnter */
262 { 0, 0, FALSE
}, /* 0x4D unused */
263 { VK_SUBTRACT
, 0x4A, TRUE
}, /* kVK_ANSI_KeypadMinus */
264 { VK_F18
, 0x69, TRUE
}, /* kVK_F18 */
265 { VK_F19
, 0x6A, TRUE
}, /* kVK_F19 */
266 { VK_OEM_NEC_EQUAL
, 0x0D | 0x100, TRUE
}, /* kVK_ANSI_KeypadEquals */
267 { VK_NUMPAD0
, 0x52, TRUE
}, /* kVK_ANSI_Keypad0 */
268 { VK_NUMPAD1
, 0x4F, TRUE
}, /* kVK_ANSI_Keypad1 */
269 { VK_NUMPAD2
, 0x50, TRUE
}, /* kVK_ANSI_Keypad2 */
270 { VK_NUMPAD3
, 0x51, TRUE
}, /* kVK_ANSI_Keypad3 */
271 { VK_NUMPAD4
, 0x4B, TRUE
}, /* kVK_ANSI_Keypad4 */
272 { VK_NUMPAD5
, 0x4C, TRUE
}, /* kVK_ANSI_Keypad5 */
273 { VK_NUMPAD6
, 0x4D, TRUE
}, /* kVK_ANSI_Keypad6 */
274 { VK_NUMPAD7
, 0x47, TRUE
}, /* kVK_ANSI_Keypad7 */
275 { VK_F20
, 0x6B, TRUE
}, /* kVK_F20 */
276 { VK_NUMPAD8
, 0x48, TRUE
}, /* kVK_ANSI_Keypad8 */
277 { VK_NUMPAD9
, 0x49, TRUE
}, /* kVK_ANSI_Keypad9 */
278 { 0xFF, 0x7D, TRUE
}, /* kVK_JIS_Yen */
279 { 0xC1, 0x73, TRUE
}, /* kVK_JIS_Underscore */
280 { VK_SEPARATOR
, 0x7E, TRUE
}, /* kVK_JIS_KeypadComma */
281 { VK_F5
, 0x3F, TRUE
}, /* kVK_F5 */
282 { VK_F6
, 0x40, TRUE
}, /* kVK_F6 */
283 { VK_F7
, 0x41, TRUE
}, /* kVK_F7 */
284 { VK_F3
, 0x3D, TRUE
}, /* kVK_F3 */
285 { VK_F8
, 0x42, TRUE
}, /* kVK_F8 */
286 { VK_F9
, 0x43, TRUE
}, /* kVK_F9 */
287 { 0xFF, 0x72, TRUE
}, /* kVK_JIS_Eisu */
288 { VK_F11
, 0x57, TRUE
}, /* kVK_F11 */
289 { VK_OEM_RESET
, 0x71, TRUE
}, /* kVK_JIS_Kana */
290 { VK_F13
, 0x64, TRUE
}, /* kVK_F13 */
291 { VK_F16
, 0x67, TRUE
}, /* kVK_F16 */
292 { VK_F14
, 0x65, TRUE
}, /* kVK_F14 */
293 { 0, 0, FALSE
}, /* 0x6C unused */
294 { VK_F10
, 0x44, TRUE
}, /* kVK_F10 */
295 { 0, 0, FALSE
}, /* 0x6E unused */
296 { VK_F12
, 0x58, TRUE
}, /* kVK_F12 */
297 { 0, 0, FALSE
}, /* 0x70 unused */
298 { VK_F15
, 0x66, TRUE
}, /* kVK_F15 */
299 { VK_INSERT
, 0x52 | 0x100, TRUE
}, /* kVK_Help */ /* map to Insert */
300 { VK_HOME
, 0x47 | 0x100, TRUE
}, /* kVK_Home */
301 { VK_PRIOR
, 0x49 | 0x100, TRUE
}, /* kVK_PageUp */
302 { VK_DELETE
, 0x53 | 0x100, TRUE
}, /* kVK_ForwardDelete */
303 { VK_F4
, 0x3E, TRUE
}, /* kVK_F4 */
304 { VK_END
, 0x4F | 0x100, TRUE
}, /* kVK_End */
305 { VK_F2
, 0x3C, TRUE
}, /* kVK_F2 */
306 { VK_NEXT
, 0x51 | 0x100, TRUE
}, /* kVK_PageDown */
307 { VK_F1
, 0x3B, TRUE
}, /* kVK_F1 */
308 { VK_LEFT
, 0x4B | 0x100, TRUE
}, /* kVK_LeftArrow */
309 { VK_RIGHT
, 0x4D | 0x100, TRUE
}, /* kVK_RightArrow */
310 { VK_DOWN
, 0x50 | 0x100, TRUE
}, /* kVK_DownArrow */
311 { VK_UP
, 0x48 | 0x100, TRUE
}, /* kVK_UpArrow */
315 static const struct {
320 { VK_BACK
, "Backspace" },
321 { VK_CAPITAL
, "Caps Lock" },
322 { VK_CONTROL
, "Ctrl" },
323 { VK_DECIMAL
, "Num Del" },
324 { VK_DELETE
| 0x100, "Delete" },
325 { VK_DIVIDE
| 0x100, "Num /" },
326 { VK_DOWN
| 0x100, "Down" },
327 { VK_END
| 0x100, "End" },
328 { VK_ESCAPE
, "Esc" },
353 { VK_HELP
| 0x100, "Help" },
354 { VK_HOME
| 0x100, "Home" },
355 { VK_INSERT
| 0x100, "Insert" },
356 { VK_LCONTROL
, "Ctrl" },
357 { VK_LEFT
| 0x100, "Left" },
359 { VK_LSHIFT
, "Shift" },
360 { VK_LWIN
| 0x100, "Win" },
362 { VK_MULTIPLY
, "Num *" },
363 { VK_NEXT
| 0x100, "Page Down" },
364 { VK_NUMLOCK
| 0x100, "Num Lock" },
365 { VK_NUMPAD0
, "Num 0" },
366 { VK_NUMPAD1
, "Num 1" },
367 { VK_NUMPAD2
, "Num 2" },
368 { VK_NUMPAD3
, "Num 3" },
369 { VK_NUMPAD4
, "Num 4" },
370 { VK_NUMPAD5
, "Num 5" },
371 { VK_NUMPAD6
, "Num 6" },
372 { VK_NUMPAD7
, "Num 7" },
373 { VK_NUMPAD8
, "Num 8" },
374 { VK_NUMPAD9
, "Num 9" },
375 { VK_OEM_CLEAR
, "Num Clear" },
376 { VK_OEM_NEC_EQUAL
| 0x100, "Num =" },
377 { VK_PRIOR
| 0x100, "Page Up" },
378 { VK_RCONTROL
| 0x100, "Right Ctrl" },
379 { VK_RETURN
, "Return" },
380 { VK_RETURN
| 0x100, "Num Enter" },
381 { VK_RIGHT
| 0x100, "Right" },
382 { VK_RMENU
| 0x100, "Right Alt" },
383 { VK_RSHIFT
, "Right Shift" },
384 { VK_RWIN
| 0x100, "Right Win" },
385 { VK_SEPARATOR
, "Num ," },
386 { VK_SHIFT
, "Shift" },
387 { VK_SPACE
, "Space" },
388 { VK_SUBTRACT
, "Num -" },
390 { VK_UP
| 0x100, "Up" },
391 { VK_VOLUME_DOWN
| 0x100, "Volume Down" },
392 { VK_VOLUME_MUTE
| 0x100, "Mute" },
393 { VK_VOLUME_UP
| 0x100, "Volume Up" },
397 static BOOL
char_matches_string(WCHAR wchar
, UniChar
*string
, BOOL ignore_diacritics
)
400 CFStringRef s1
= CFStringCreateWithCharactersNoCopy(NULL
, (UniChar
*)&wchar
, 1, kCFAllocatorNull
);
401 CFStringRef s2
= CFStringCreateWithCharactersNoCopy(NULL
, string
, strlenW(string
), kCFAllocatorNull
);
402 CFStringCompareFlags flags
= kCFCompareCaseInsensitive
| kCFCompareNonliteral
| kCFCompareWidthInsensitive
;
403 if (ignore_diacritics
)
404 flags
|= kCFCompareDiacriticInsensitive
;
405 ret
= (CFStringCompare(s1
, s2
, flags
) == kCFCompareEqualTo
);
412 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
413 * string. Returns the length of the string after stripping. */
414 static int strip_apple_private_chars(LPWSTR bufW
, int len
)
417 for (i
= 0; i
< len
; )
419 if (0xF700 <= bufW
[i
] && bufW
[i
] <= 0xF8FF)
421 memmove(&bufW
[i
], &bufW
[i
+1], (len
- i
- 1) * sizeof(bufW
[0]));
431 /***********************************************************************
432 * macdrv_compute_keyboard_layout
434 void macdrv_compute_keyboard_layout(struct macdrv_thread_data
*thread_data
)
438 const UCKeyboardLayout
*uchr
;
439 const UInt32 modifier_combos
[] = {
443 (shiftKey
| cmdKey
) >> 8,
445 (shiftKey
| optionKey
) >> 8,
447 UniChar map
[128][sizeof(modifier_combos
) / sizeof(modifier_combos
[0])][4 + 1];
450 int ignore_diacritics
;
451 static const struct {
455 { '-', VK_OEM_MINUS
},
456 { '+', VK_OEM_PLUS
},
457 { '_', VK_OEM_MINUS
},
458 { ',', VK_OEM_COMMA
},
459 { '.', VK_OEM_PERIOD
},
460 { '=', VK_OEM_PLUS
},
461 { '>', VK_OEM_PERIOD
},
462 { '<', VK_OEM_COMMA
},
476 { ':', VK_OEM_PERIOD
},
477 { ';', VK_OEM_COMMA
},
479 { 0x00B4, VK_OEM_4
}, /* 0x00B4 is ACUTE ACCENT */
481 { 0x00A7, VK_OEM_5
}, /* 0x00A7 is SECTION SIGN */
482 { '*', VK_OEM_PLUS
},
483 { 0x00B4, VK_OEM_7
},
491 { '?', VK_OEM_PLUS
},
493 { 0x00B4, VK_OEM_3
},
494 { '?', VK_OEM_COMMA
},
495 { '~', VK_OEM_PLUS
},
498 { 0x00A7, VK_OEM_7
},
502 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
503 contiguous ranges. */
504 static const struct {
509 { VK_OEM_1
, VK_OEM_3
},
510 { VK_OEM_4
, VK_ICO_CLEAR
},
512 { VK_OEM_NEC_EQUAL
, VK_OEM_NEC_EQUAL
},
518 if (!thread_data
->keyboard_layout_uchr
)
520 ERR("no keyboard layout UCHR data\n");
524 memset(thread_data
->keyc2vkey
, 0, sizeof(thread_data
->keyc2vkey
));
525 memset(vkey_used
, 0, sizeof(vkey_used
));
527 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
529 thread_data
->keyc2scan
[keyc
] = default_map
[keyc
].scan
;
530 if (default_map
[keyc
].fixed
)
532 vkey
= default_map
[keyc
].vkey
;
533 thread_data
->keyc2vkey
[keyc
] = vkey
;
535 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc
, vkey
);
539 if (thread_data
->iso_keyboard
)
541 /* In almost all cases, the Mac key codes indicate a physical key position
542 and this corresponds nicely to Win32 scan codes. However, the Mac key
543 codes differ in one case between ANSI and ISO keyboards. For ANSI
544 keyboards, the key to the left of the digits and above the Tab key
545 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
546 some position produces kVK_ISO_Section. The additional key on ISO
547 keyboards, the one to the right of the left Shift key, produces
548 kVK_ANSI_Grave, which is just weird.
550 Since we want the key in that upper left corner to always produce the
551 same scan code (0x29), we need to swap the scan codes of those two
552 Mac key codes for ISO keyboards. */
553 DWORD temp
= thread_data
->keyc2scan
[kVK_ANSI_Grave
];
554 thread_data
->keyc2scan
[kVK_ANSI_Grave
] = thread_data
->keyc2scan
[kVK_ISO_Section
];
555 thread_data
->keyc2scan
[kVK_ISO_Section
] = temp
;
558 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
560 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
561 memset(map
, 0, sizeof(map
));
562 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
564 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
565 if (thread_data
->keyc2vkey
[keyc
]) continue; /* assigned a fixed vkey */
567 TRACE("keyc 0x%04x: ", keyc
);
569 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
576 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifier_combos
[combo
],
577 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
578 &deadKeyState
, sizeof(map
[keyc
][combo
])/sizeof(map
[keyc
][combo
][0]) - 1,
579 &len
, map
[keyc
][combo
]);
581 map
[keyc
][combo
][0] = 0;
583 TRACE("%s%s", (combo
? ", " : ""), debugstr_w(map
[keyc
][combo
]));
589 /* First try to match key codes to the vkeys for the letters A through Z.
590 Try unmodified first, then with various modifier combinations in succession.
591 On the first pass, try to get a match lacking diacritical marks. On the
592 second pass, accept matches with diacritical marks. */
593 for (ignore_diacritics
= 0; ignore_diacritics
<= 1; ignore_diacritics
++)
595 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
597 for (vkey
= 'A'; vkey
<= 'Z'; vkey
++)
602 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
604 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
607 if (char_matches_string(vkey
, map
[keyc
][combo
], ignore_diacritics
))
609 thread_data
->keyc2vkey
[keyc
] = vkey
;
611 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
612 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
620 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
621 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
623 for (vkey
= '0'; vkey
<= '9'; vkey
++)
628 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
630 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
633 if (char_matches_string(vkey
, map
[keyc
][combo
], FALSE
))
635 thread_data
->keyc2vkey
[keyc
] = vkey
;
637 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
638 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
645 /* Now try to match key codes for certain common punctuation characters to
646 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
647 for (i
= 0; i
< sizeof(symbol_vkeys
) / sizeof(symbol_vkeys
[0]); i
++)
649 vkey
= symbol_vkeys
[i
].vkey
;
654 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
656 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
658 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
659 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
662 if (char_matches_string(symbol_vkeys
[i
].wchar
, map
[keyc
][combo
], FALSE
))
664 thread_data
->keyc2vkey
[keyc
] = vkey
;
666 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
667 debugstr_wn(&symbol_vkeys
[i
].wchar
, 1), debugstr_w(map
[keyc
][combo
]));
677 /* For those key codes still without a vkey, try to use the default vkey
678 from the default map, if it's still available. */
679 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
681 DWORD vkey
= default_map
[keyc
].vkey
;
683 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
684 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
686 if (!vkey_used
[vkey
])
688 thread_data
->keyc2vkey
[keyc
] = vkey
;
690 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc
, vkey
);
694 /* For any unassigned key codes which would map to a letter in the default
695 map, but whose normal letter vkey wasn't available, try to find a
698 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
700 if (default_map
[keyc
].vkey
< 'A' || 'Z' < default_map
[keyc
].vkey
)
701 continue; /* not a letter in ANSI layout */
702 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
703 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
705 while (vkey
<= 'Z' && vkey_used
[vkey
]) vkey
++;
708 thread_data
->keyc2vkey
[keyc
] = vkey
;
710 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc
, vkey
);
713 break; /* no more unused letter vkeys, so stop trying */
716 /* Same thing but with the digits. */
718 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
720 if (default_map
[keyc
].vkey
< '0' || '9' < default_map
[keyc
].vkey
)
721 continue; /* not a digit in ANSI layout */
722 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
723 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
725 while (vkey
<= '9' && vkey_used
[vkey
]) vkey
++;
728 thread_data
->keyc2vkey
[keyc
] = vkey
;
730 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc
, vkey
);
733 break; /* no more unused digit vkeys, so stop trying */
736 /* Last chance. Assign any available vkey. */
738 vkey
= vkey_ranges
[vkey_range
].first
;
739 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
741 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
742 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
744 while (vkey
&& vkey_used
[vkey
])
746 if (vkey
== vkey_ranges
[vkey_range
].last
)
749 vkey
= vkey_ranges
[vkey_range
].first
;
757 WARN("No more vkeys available!\n");
761 thread_data
->keyc2vkey
[keyc
] = vkey
;
763 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc
, vkey
);
768 /***********************************************************************
769 * macdrv_send_keyboard_input
771 static void macdrv_send_keyboard_input(HWND hwnd
, WORD vkey
, WORD scan
, DWORD flags
, DWORD time
)
775 TRACE_(key
)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd
, vkey
, scan
, flags
);
777 input
.type
= INPUT_KEYBOARD
;
779 input
.ki
.wScan
= scan
;
780 input
.ki
.dwFlags
= flags
;
781 input
.ki
.time
= time
;
782 input
.ki
.dwExtraInfo
= 0;
784 __wine_send_input(hwnd
, &input
);
788 /***********************************************************************
791 * Handler for KEY_PRESS and KEY_RELEASE events.
793 void macdrv_key_event(HWND hwnd
, const macdrv_event
*event
)
795 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
799 TRACE_(key
)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
800 hwnd
, event
->window
, (event
->type
== KEY_PRESS
? "press" : "release"),
801 event
->key
.keycode
, event
->key
.modifiers
);
803 thread_data
->last_modifiers
= event
->key
.modifiers
;
805 if (event
->key
.keycode
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
807 vkey
= thread_data
->keyc2vkey
[event
->key
.keycode
];
808 scan
= thread_data
->keyc2scan
[event
->key
.keycode
];
813 TRACE_(key
)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
814 event
->key
.keycode
, vkey
, scan
);
819 if (event
->type
== KEY_RELEASE
) flags
|= KEYEVENTF_KEYUP
;
820 if (scan
& 0x100) flags
|= KEYEVENTF_EXTENDEDKEY
;
822 macdrv_send_keyboard_input(hwnd
, vkey
, scan
& 0xff, flags
, event
->key
.time_ms
);
826 /***********************************************************************
827 * macdrv_keyboard_changed
829 * Handler for KEYBOARD_CHANGED events.
831 void macdrv_keyboard_changed(const macdrv_event
*event
)
833 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
835 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event
->keyboard_changed
.uchr
,
836 event
->keyboard_changed
.keyboard_type
, event
->keyboard_changed
.iso_keyboard
);
838 if (thread_data
->keyboard_layout_uchr
)
839 CFRelease(thread_data
->keyboard_layout_uchr
);
840 thread_data
->keyboard_layout_uchr
= CFDataCreateCopy(NULL
, event
->keyboard_changed
.uchr
);
841 thread_data
->keyboard_type
= event
->keyboard_changed
.keyboard_type
;
842 thread_data
->iso_keyboard
= event
->keyboard_changed
.iso_keyboard
;
843 thread_data
->dead_key_state
= 0;
845 macdrv_compute_keyboard_layout(thread_data
);
849 /***********************************************************************
850 * get_locale_keyboard_layout
852 static HKL
get_locale_keyboard_layout(void)
857 layout
= GetUserDefaultLCID();
860 * Microsoft Office expects this value to be something specific
861 * for Japanese and Korean Windows with an IME the value is 0xe001
862 * We should probably check to see if an IME exists and if so then
863 * set this word properly.
865 langid
= PRIMARYLANGID(LANGIDFROMLCID(layout
));
866 if (langid
== LANG_CHINESE
|| langid
== LANG_JAPANESE
|| langid
== LANG_KOREAN
)
867 layout
|= 0xe001 << 16; /* IME */
869 layout
|= layout
<< 16;
875 /***********************************************************************
876 * match_keyboard_layout
878 static BOOL
match_keyboard_layout(HKL hkl
)
880 const DWORD isIME
= 0xE0000000;
881 HKL current_hkl
= get_locale_keyboard_layout();
883 /* if the layout is an IME, only match the low word (LCID) */
884 if (((ULONG_PTR
)hkl
& isIME
) == isIME
)
885 return (LOWORD(hkl
) == LOWORD(current_hkl
));
887 return (hkl
== current_hkl
);
891 /***********************************************************************
892 * ActivateKeyboardLayout (MACDRV.@)
894 HKL CDECL
macdrv_ActivateKeyboardLayout(HKL hkl
, UINT flags
)
897 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
899 /* FIXME: Use Text Input Services or NSTextInputContext to actually
900 change the Mac keyboard input source. */
902 FIXME("hkl %p flags %04x: semi-stub!\n", hkl
, flags
);
903 if (flags
& KLF_SETFORPROCESS
)
905 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
906 FIXME("KLF_SETFORPROCESS not supported\n");
911 FIXME("flags %x not supported\n",flags
);
913 if (hkl
== (HKL
)HKL_NEXT
|| hkl
== (HKL
)HKL_PREV
)
915 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
916 FIXME("HKL_NEXT and HKL_PREV not supported\n");
920 if (!match_keyboard_layout(hkl
))
922 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
923 FIXME("setting keyboard of different locales not supported\n");
927 oldHkl
= thread_data
->active_keyboard_layout
;
928 if (!oldHkl
) oldHkl
= get_locale_keyboard_layout();
930 thread_data
->active_keyboard_layout
= hkl
;
936 /***********************************************************************
939 void CDECL
macdrv_Beep(void)
945 /***********************************************************************
946 * GetKeyNameText (MACDRV.@)
948 INT CDECL
macdrv_GetKeyNameText(LONG lparam
, LPWSTR buffer
, INT size
)
950 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
953 scan
= (lparam
>> 16) & 0x1FF;
954 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2scan
)/sizeof(thread_data
->keyc2scan
[0]); keyc
++)
956 if (thread_data
->keyc2scan
[keyc
] == scan
)
958 static const WCHAR dead
[] = {' ','d','e','a','d',0};
959 const UCKeyboardLayout
*uchr
;
960 UInt32 deadKeyState
= 0;
964 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
965 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDisplay
, 0, thread_data
->keyboard_type
,
966 0, &deadKeyState
, size
- 1, &len
, (UniChar
*)buffer
);
969 if (len
&& isgraphW(buffer
[0]))
973 DWORD vkey
= thread_data
->keyc2vkey
[keyc
];
976 if (scan
& 0x100) vkey
|= 0x100;
978 if (lparam
& (1 << 25))
980 /* Caller doesn't care about distinctions between left and
986 vkey
= VK_SHIFT
; break;
989 vkey
= VK_CONTROL
; break;
992 vkey
= VK_MENU
; break;
997 for (i
= 0; i
< sizeof(vkey_names
) / sizeof(vkey_names
[0]); i
++)
999 if (vkey_names
[i
].vkey
== vkey
)
1001 len
= MultiByteToWideChar(CP_UTF8
, 0, vkey_names
[i
].name
, -1, buffer
, size
);
1009 static const WCHAR format
[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1010 snprintfW(buffer
, size
, format
, vkey
);
1011 len
= strlenW(buffer
);
1018 if (status
== noErr
&& deadKeyState
)
1020 lstrcpynW(buffer
+ len
, dead
, size
- len
);
1021 len
= strlenW(buffer
);
1024 TRACE("lparam 0x%08x -> %s\n", lparam
, debugstr_w(buffer
));
1029 WARN("found no name for lparam 0x%08x\n", lparam
);
1034 /***********************************************************************
1035 * GetKeyboardLayout (MACDRV.@)
1037 HKL CDECL
macdrv_GetKeyboardLayout(DWORD thread_id
)
1039 if (!thread_id
|| thread_id
== GetCurrentThreadId())
1041 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1042 if (thread_data
&& thread_data
->active_keyboard_layout
)
1043 return thread_data
->active_keyboard_layout
;
1046 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id
);
1048 /* FIXME: Use TISGetInputSourceProperty() and kTISPropertyInputSourceLanguages
1049 * to get input source language ID string. Use
1050 * CFLocaleGetWindowsLocaleCodeFromLocaleIdentifier() to convert that
1051 * to a Windows locale ID and from there to a layout handle.
1054 return get_locale_keyboard_layout();
1058 /***********************************************************************
1059 * GetKeyboardLayoutName (MACDRV.@)
1061 BOOL CDECL
macdrv_GetKeyboardLayoutName(LPWSTR name
)
1063 static const WCHAR formatW
[] = {'%','0','8','x',0};
1066 layout
= HandleToUlong(get_locale_keyboard_layout());
1067 if (HIWORD(layout
) == LOWORD(layout
)) layout
= LOWORD(layout
);
1068 sprintfW(name
, formatW
, layout
);
1069 TRACE("returning %s\n", debugstr_w(name
));
1074 /***********************************************************************
1075 * MapVirtualKeyEx (MACDRV.@)
1077 UINT CDECL
macdrv_MapVirtualKeyEx(UINT wCode
, UINT wMapType
, HKL hkl
)
1079 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1083 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode
, wMapType
, hkl
);
1087 case MAPVK_VK_TO_VSC
: /* vkey-code to scan-code */
1088 case MAPVK_VK_TO_VSC_EX
:
1091 case VK_SHIFT
: wCode
= VK_LSHIFT
; break;
1092 case VK_CONTROL
: wCode
= VK_LCONTROL
; break;
1093 case VK_MENU
: wCode
= VK_LMENU
; break;
1096 /* vkey -> keycode -> scan */
1097 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1099 if (thread_data
->keyc2vkey
[keyc
] == wCode
)
1101 ret
= thread_data
->keyc2scan
[keyc
] & 0xFF;
1107 case MAPVK_VSC_TO_VK
: /* scan-code to vkey-code */
1108 case MAPVK_VSC_TO_VK_EX
:
1109 /* scan -> keycode -> vkey */
1110 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1111 if ((thread_data
->keyc2scan
[keyc
] & 0xFF) == (wCode
& 0xFF))
1113 ret
= thread_data
->keyc2vkey
[keyc
];
1114 /* Only stop if it's not a numpad vkey; otherwise keep
1115 looking for a potential better vkey. */
1116 if (ret
&& (ret
< VK_NUMPAD0
|| VK_DIVIDE
< ret
))
1120 if (wMapType
== MAPVK_VSC_TO_VK
)
1125 ret
= VK_SHIFT
; break;
1128 ret
= VK_CONTROL
; break;
1131 ret
= VK_MENU
; break;
1136 case MAPVK_VK_TO_CHAR
: /* vkey-code to character */
1138 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1139 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1140 const UCKeyboardLayout
*uchr
;
1143 UInt32 deadKeyState
;
1145 BOOL deadKey
= FALSE
;
1147 if ((VK_PRIOR
<= wCode
&& wCode
<= VK_HELP
) ||
1148 (VK_F1
<= wCode
&& wCode
<= VK_F24
))
1151 if (!thread_data
|| !thread_data
->keyboard_layout_uchr
)
1153 WARN("No keyboard layout uchr data\n");
1157 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1159 /* Find the Mac keycode corresponding to the vkey */
1160 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1161 if (thread_data
->keyc2vkey
[keyc
] == wCode
) break;
1163 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1165 WARN("Unknown virtual key %X\n", wCode
);
1169 TRACE("Found keycode %u\n", keyc
);
1172 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1173 thread_data
->keyboard_type
, 0, &deadKeyState
,
1174 sizeof(s
)/sizeof(s
[0]), &len
, s
);
1175 if (status
== noErr
&& !len
&& deadKeyState
)
1179 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1180 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1181 &deadKeyState
, sizeof(s
)/sizeof(s
[0]), &len
, s
);
1184 if (status
== noErr
&& len
)
1185 ret
= toupperW(s
[0]) | (deadKey
? 0x80000000 : 0);
1189 default: /* reserved */
1190 FIXME("Unknown wMapType %d\n", wMapType
);
1194 TRACE("returning 0x%04x\n", ret
);
1199 /***********************************************************************
1200 * ToUnicodeEx (MACDRV.@)
1202 * The ToUnicode function translates the specified virtual-key code and keyboard
1203 * state to the corresponding Windows character or characters.
1205 * If the specified key is a dead key, the return value is negative. Otherwise,
1206 * it is one of the following values:
1208 * -1 The specified virtual key is a dead-key. If possible, the
1209 * non-combining form of the dead character is written to bufW.
1210 * 0 The specified virtual key has no translation for the current
1211 * state of the keyboard.
1212 * 1 One Windows character was copied to the buffer.
1213 * 2 or more Multiple characters were copied to the buffer. This usually
1214 * happens when a dead-key character (accent or diacritic) stored
1215 * in the keyboard layout cannot be composed with the specified
1216 * virtual key to form a single character.
1219 INT CDECL
macdrv_ToUnicodeEx(UINT virtKey
, UINT scanCode
, const BYTE
*lpKeyState
,
1220 LPWSTR bufW
, int bufW_size
, UINT flags
, HKL hkl
)
1222 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1225 BOOL is_menu
= (flags
& 0x1);
1227 const UCKeyboardLayout
*uchr
;
1229 UInt32 modifierKeyState
;
1231 UInt32 deadKeyState
, savedDeadKeyState
;
1235 TRACE_(key
)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1236 virtKey
, scanCode
, lpKeyState
, bufW
, bufW_size
, flags
, hkl
);
1241 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1242 modifier key press. We want it to effectively ignore modifier key
1243 presses. I think that one isn't supposed to call it at all for modifier
1244 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1245 are different event types than key up/down events. */
1261 /* There are a number of key combinations for which Windows does not
1262 produce characters, but Mac keyboard layouts may. Eat them. Do this
1263 here to avoid the expense of UCKeyTranslate() but also because these
1264 keys shouldn't terminate dead key sequences. */
1265 if ((VK_PRIOR
<= virtKey
&& virtKey
<= VK_HELP
) || (VK_F1
<= virtKey
&& virtKey
<= VK_F24
))
1268 /* Shift + <non-digit keypad keys>. */
1269 if ((lpKeyState
[VK_SHIFT
] & 0x80) && VK_MULTIPLY
<= virtKey
&& virtKey
<= VK_DIVIDE
)
1272 if (lpKeyState
[VK_CONTROL
] & 0x80)
1274 /* Control-Tab, with or without other modifiers. */
1275 if (virtKey
== VK_TAB
)
1278 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1280 if ((lpKeyState
[VK_SHIFT
] & 0x80) || (lpKeyState
[VK_MENU
] & 0x80))
1294 if (thread_data
->keyboard_layout_uchr
)
1295 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1299 keyAction
= (scanCode
& 0x8000) ? kUCKeyActionUp
: kUCKeyActionDown
;
1301 modifierKeyState
= 0;
1302 if (lpKeyState
[VK_SHIFT
] & 0x80)
1303 modifierKeyState
|= (shiftKey
>> 8);
1304 if (lpKeyState
[VK_CAPITAL
] & 0x01)
1305 modifierKeyState
|= (alphaLock
>> 8);
1306 if (lpKeyState
[VK_CONTROL
] & 0x80)
1307 modifierKeyState
|= (controlKey
>> 8);
1308 if (lpKeyState
[VK_MENU
] & 0x80)
1309 modifierKeyState
|= (cmdKey
>> 8);
1310 if (thread_data
->last_modifiers
& (NX_ALTERNATEMASK
| NX_DEVICELALTKEYMASK
| NX_DEVICERALTKEYMASK
))
1311 modifierKeyState
|= (optionKey
>> 8);
1313 /* Find the Mac keycode corresponding to the vkey */
1314 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1315 if (thread_data
->keyc2vkey
[keyc
] == virtKey
) break;
1317 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1319 WARN_(key
)("Unknown virtual key 0x%04x\n", virtKey
);
1323 TRACE_(key
)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc
,
1324 (keyAction
== kUCKeyActionDown
) ? "pressed" : "released", (unsigned)modifierKeyState
);
1328 options
= kUCKeyTranslateNoDeadKeysMask
;
1334 deadKeyState
= thread_data
->dead_key_state
;
1336 savedDeadKeyState
= deadKeyState
;
1337 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1338 thread_data
->keyboard_type
, options
, &deadKeyState
, bufW_size
,
1340 if (status
!= noErr
)
1342 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1346 thread_data
->dead_key_state
= deadKeyState
;
1348 if (len
== 0 && deadKeyState
)
1350 /* Repeat the translation, but disabling dead-key generation to
1351 learn which dead key it was. */
1352 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1353 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1354 &savedDeadKeyState
, bufW_size
, &len
, bufW
);
1355 if (status
!= noErr
)
1357 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1365 len
= strip_apple_private_chars(bufW
, len
);
1367 if (dead
&& len
> 0) ret
= -1;
1370 /* Control-Return produces line feed instead of carriage return. */
1371 if (ret
> 0 && (lpKeyState
[VK_CONTROL
] & 0x80) && virtKey
== VK_RETURN
)
1374 for (i
= 0; i
< len
; i
++)
1375 if (bufW
[i
] == '\r')
1380 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1381 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1382 if (1 <= ret
&& ret
< bufW_size
)
1385 TRACE_(key
)("returning %d / %s\n", ret
, debugstr_wn(bufW
, abs(ret
)));
1390 /***********************************************************************
1391 * VkKeyScanEx (MACDRV.@)
1393 * Note: Windows ignores HKL parameter and uses current active layout instead
1395 SHORT CDECL
macdrv_VkKeyScanEx(WCHAR wChar
, HKL hkl
)
1397 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1400 const UCKeyboardLayout
*uchr
;
1402 TRACE("%04x, %p\n", wChar
, hkl
);
1404 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1407 TRACE("no keyboard layout UCHR data; returning -1\n");
1411 for (state
= 0; state
< 8; state
++)
1413 UInt32 modifierKeyState
= 0;
1417 modifierKeyState
|= (shiftKey
>> 8);
1418 if ((state
& 6) == 6)
1419 modifierKeyState
|= (optionKey
>> 8);
1423 modifierKeyState
|= (controlKey
>> 8);
1425 modifierKeyState
|= (cmdKey
>> 8);
1428 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1430 UInt32 deadKeyState
= 0;
1435 if (!thread_data
->keyc2vkey
[keyc
]) continue;
1437 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifierKeyState
,
1438 thread_data
->keyboard_type
, 0, &deadKeyState
,
1440 if (status
== noErr
&& len
== 1 && uchar
== wChar
)
1442 WORD vkey
= thread_data
->keyc2vkey
[keyc
];
1444 ret
= vkey
| (state
<< 8);
1445 if ((VK_NUMPAD0
<= vkey
&& vkey
<= VK_DIVIDE
) ||
1446 keyc
== kVK_ANSI_KeypadClear
|| keyc
== kVK_ANSI_KeypadEnter
||
1447 keyc
== kVK_ANSI_KeypadEquals
)
1449 /* Keep searching for a non-numpad match, which is preferred. */
1458 TRACE(" -> 0x%04x\n", ret
);