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 * macdrv_process_text_input
894 BOOL
macdrv_process_text_input(UINT vkey
, UINT scan
, UINT repeat
, const BYTE
*key_state
, void *himc
)
896 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
901 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", vkey
, scan
, repeat
, himc
);
903 flags
= thread_data
->last_modifiers
;
904 if (key_state
[VK_SHIFT
] & 0x80)
905 flags
|= NX_SHIFTMASK
;
907 flags
&= ~(NX_SHIFTMASK
| NX_DEVICELSHIFTKEYMASK
| NX_DEVICERSHIFTKEYMASK
);
908 if (key_state
[VK_CAPITAL
] & 0x01)
909 flags
|= NX_ALPHASHIFTMASK
;
911 flags
&= ~NX_ALPHASHIFTMASK
;
912 if (key_state
[VK_CONTROL
] & 0x80)
913 flags
|= NX_CONTROLMASK
;
915 flags
&= ~(NX_CONTROLMASK
| NX_DEVICELCTLKEYMASK
| NX_DEVICERCTLKEYMASK
);
916 if (key_state
[VK_MENU
] & 0x80)
917 flags
|= NX_COMMANDMASK
;
919 flags
&= ~(NX_COMMANDMASK
| NX_DEVICELCMDKEYMASK
| NX_DEVICERCMDKEYMASK
);
921 /* Find the Mac keycode corresponding to the scan code */
922 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
923 if (thread_data
->keyc2vkey
[keyc
] == vkey
) break;
925 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
928 TRACE("flags 0x%08x keyc 0x%04x\n", flags
, keyc
);
930 ret
= macdrv_send_text_input_event(((scan
& 0x8000) == 0), flags
, repeat
, keyc
, himc
);
933 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");
938 /***********************************************************************
939 * ActivateKeyboardLayout (MACDRV.@)
941 HKL CDECL
macdrv_ActivateKeyboardLayout(HKL hkl
, UINT flags
)
944 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
946 /* FIXME: Use Text Input Services or NSTextInputContext to actually
947 change the Mac keyboard input source. */
949 FIXME("hkl %p flags %04x: semi-stub!\n", hkl
, flags
);
950 if (flags
& KLF_SETFORPROCESS
)
952 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
953 FIXME("KLF_SETFORPROCESS not supported\n");
958 FIXME("flags %x not supported\n",flags
);
960 if (hkl
== (HKL
)HKL_NEXT
|| hkl
== (HKL
)HKL_PREV
)
962 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
963 FIXME("HKL_NEXT and HKL_PREV not supported\n");
967 if (!match_keyboard_layout(hkl
))
969 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
970 FIXME("setting keyboard of different locales not supported\n");
974 oldHkl
= thread_data
->active_keyboard_layout
;
975 if (!oldHkl
) oldHkl
= get_locale_keyboard_layout();
977 thread_data
->active_keyboard_layout
= hkl
;
983 /***********************************************************************
986 void CDECL
macdrv_Beep(void)
992 /***********************************************************************
993 * GetKeyNameText (MACDRV.@)
995 INT CDECL
macdrv_GetKeyNameText(LONG lparam
, LPWSTR buffer
, INT size
)
997 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1000 scan
= (lparam
>> 16) & 0x1FF;
1001 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2scan
)/sizeof(thread_data
->keyc2scan
[0]); keyc
++)
1003 if (thread_data
->keyc2scan
[keyc
] == scan
)
1005 static const WCHAR dead
[] = {' ','d','e','a','d',0};
1006 const UCKeyboardLayout
*uchr
;
1007 UInt32 deadKeyState
= 0;
1011 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1012 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDisplay
, 0, thread_data
->keyboard_type
,
1013 0, &deadKeyState
, size
- 1, &len
, (UniChar
*)buffer
);
1014 if (status
!= noErr
)
1016 if (len
&& isgraphW(buffer
[0]))
1020 DWORD vkey
= thread_data
->keyc2vkey
[keyc
];
1023 if (scan
& 0x100) vkey
|= 0x100;
1025 if (lparam
& (1 << 25))
1027 /* Caller doesn't care about distinctions between left and
1033 vkey
= VK_SHIFT
; break;
1036 vkey
= VK_CONTROL
; break;
1039 vkey
= VK_MENU
; break;
1044 for (i
= 0; i
< sizeof(vkey_names
) / sizeof(vkey_names
[0]); i
++)
1046 if (vkey_names
[i
].vkey
== vkey
)
1048 len
= MultiByteToWideChar(CP_UTF8
, 0, vkey_names
[i
].name
, -1, buffer
, size
);
1056 static const WCHAR format
[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1057 snprintfW(buffer
, size
, format
, vkey
);
1058 len
= strlenW(buffer
);
1065 if (status
== noErr
&& deadKeyState
)
1067 lstrcpynW(buffer
+ len
, dead
, size
- len
);
1068 len
= strlenW(buffer
);
1071 TRACE("lparam 0x%08x -> %s\n", lparam
, debugstr_w(buffer
));
1076 WARN("found no name for lparam 0x%08x\n", lparam
);
1081 /***********************************************************************
1082 * GetKeyboardLayout (MACDRV.@)
1084 HKL CDECL
macdrv_GetKeyboardLayout(DWORD thread_id
)
1086 if (!thread_id
|| thread_id
== GetCurrentThreadId())
1088 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1089 if (thread_data
&& thread_data
->active_keyboard_layout
)
1090 return thread_data
->active_keyboard_layout
;
1093 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id
);
1095 /* FIXME: Use TISGetInputSourceProperty() and kTISPropertyInputSourceLanguages
1096 * to get input source language ID string. Use
1097 * CFLocaleGetWindowsLocaleCodeFromLocaleIdentifier() to convert that
1098 * to a Windows locale ID and from there to a layout handle.
1101 return get_locale_keyboard_layout();
1105 /***********************************************************************
1106 * GetKeyboardLayoutName (MACDRV.@)
1108 BOOL CDECL
macdrv_GetKeyboardLayoutName(LPWSTR name
)
1110 static const WCHAR formatW
[] = {'%','0','8','x',0};
1113 layout
= HandleToUlong(get_locale_keyboard_layout());
1114 if (HIWORD(layout
) == LOWORD(layout
)) layout
= LOWORD(layout
);
1115 sprintfW(name
, formatW
, layout
);
1116 TRACE("returning %s\n", debugstr_w(name
));
1121 /***********************************************************************
1122 * MapVirtualKeyEx (MACDRV.@)
1124 UINT CDECL
macdrv_MapVirtualKeyEx(UINT wCode
, UINT wMapType
, HKL hkl
)
1126 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1130 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode
, wMapType
, hkl
);
1134 case MAPVK_VK_TO_VSC
: /* vkey-code to scan-code */
1135 case MAPVK_VK_TO_VSC_EX
:
1138 case VK_SHIFT
: wCode
= VK_LSHIFT
; break;
1139 case VK_CONTROL
: wCode
= VK_LCONTROL
; break;
1140 case VK_MENU
: wCode
= VK_LMENU
; break;
1143 /* vkey -> keycode -> scan */
1144 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1146 if (thread_data
->keyc2vkey
[keyc
] == wCode
)
1148 ret
= thread_data
->keyc2scan
[keyc
] & 0xFF;
1154 case MAPVK_VSC_TO_VK
: /* scan-code to vkey-code */
1155 case MAPVK_VSC_TO_VK_EX
:
1156 /* scan -> keycode -> vkey */
1157 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1158 if ((thread_data
->keyc2scan
[keyc
] & 0xFF) == (wCode
& 0xFF))
1160 ret
= thread_data
->keyc2vkey
[keyc
];
1161 /* Only stop if it's not a numpad vkey; otherwise keep
1162 looking for a potential better vkey. */
1163 if (ret
&& (ret
< VK_NUMPAD0
|| VK_DIVIDE
< ret
))
1167 if (wMapType
== MAPVK_VSC_TO_VK
)
1172 ret
= VK_SHIFT
; break;
1175 ret
= VK_CONTROL
; break;
1178 ret
= VK_MENU
; break;
1183 case MAPVK_VK_TO_CHAR
: /* vkey-code to character */
1185 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1186 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1187 const UCKeyboardLayout
*uchr
;
1190 UInt32 deadKeyState
;
1192 BOOL deadKey
= FALSE
;
1194 if ((VK_PRIOR
<= wCode
&& wCode
<= VK_HELP
) ||
1195 (VK_F1
<= wCode
&& wCode
<= VK_F24
))
1198 if (!thread_data
|| !thread_data
->keyboard_layout_uchr
)
1200 WARN("No keyboard layout uchr data\n");
1204 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1206 /* Find the Mac keycode corresponding to the vkey */
1207 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1208 if (thread_data
->keyc2vkey
[keyc
] == wCode
) break;
1210 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1212 WARN("Unknown virtual key %X\n", wCode
);
1216 TRACE("Found keycode %u\n", keyc
);
1219 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1220 thread_data
->keyboard_type
, 0, &deadKeyState
,
1221 sizeof(s
)/sizeof(s
[0]), &len
, s
);
1222 if (status
== noErr
&& !len
&& deadKeyState
)
1226 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1227 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1228 &deadKeyState
, sizeof(s
)/sizeof(s
[0]), &len
, s
);
1231 if (status
== noErr
&& len
)
1232 ret
= toupperW(s
[0]) | (deadKey
? 0x80000000 : 0);
1236 default: /* reserved */
1237 FIXME("Unknown wMapType %d\n", wMapType
);
1241 TRACE("returning 0x%04x\n", ret
);
1246 /***********************************************************************
1247 * ToUnicodeEx (MACDRV.@)
1249 * The ToUnicode function translates the specified virtual-key code and keyboard
1250 * state to the corresponding Windows character or characters.
1252 * If the specified key is a dead key, the return value is negative. Otherwise,
1253 * it is one of the following values:
1255 * -1 The specified virtual key is a dead-key. If possible, the
1256 * non-combining form of the dead character is written to bufW.
1257 * 0 The specified virtual key has no translation for the current
1258 * state of the keyboard.
1259 * 1 One Windows character was copied to the buffer.
1260 * 2 or more Multiple characters were copied to the buffer. This usually
1261 * happens when a dead-key character (accent or diacritic) stored
1262 * in the keyboard layout cannot be composed with the specified
1263 * virtual key to form a single character.
1266 INT CDECL
macdrv_ToUnicodeEx(UINT virtKey
, UINT scanCode
, const BYTE
*lpKeyState
,
1267 LPWSTR bufW
, int bufW_size
, UINT flags
, HKL hkl
)
1269 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1272 BOOL is_menu
= (flags
& 0x1);
1274 const UCKeyboardLayout
*uchr
;
1276 UInt32 modifierKeyState
;
1278 UInt32 deadKeyState
, savedDeadKeyState
;
1282 TRACE_(key
)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1283 virtKey
, scanCode
, lpKeyState
, bufW
, bufW_size
, flags
, hkl
);
1288 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1289 modifier key press. We want it to effectively ignore modifier key
1290 presses. I think that one isn't supposed to call it at all for modifier
1291 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1292 are different event types than key up/down events. */
1308 /* There are a number of key combinations for which Windows does not
1309 produce characters, but Mac keyboard layouts may. Eat them. Do this
1310 here to avoid the expense of UCKeyTranslate() but also because these
1311 keys shouldn't terminate dead key sequences. */
1312 if ((VK_PRIOR
<= virtKey
&& virtKey
<= VK_HELP
) || (VK_F1
<= virtKey
&& virtKey
<= VK_F24
))
1315 /* Shift + <non-digit keypad keys>. */
1316 if ((lpKeyState
[VK_SHIFT
] & 0x80) && VK_MULTIPLY
<= virtKey
&& virtKey
<= VK_DIVIDE
)
1319 if (lpKeyState
[VK_CONTROL
] & 0x80)
1321 /* Control-Tab, with or without other modifiers. */
1322 if (virtKey
== VK_TAB
)
1325 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1327 if ((lpKeyState
[VK_SHIFT
] & 0x80) || (lpKeyState
[VK_MENU
] & 0x80))
1341 if (thread_data
->keyboard_layout_uchr
)
1342 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1346 keyAction
= (scanCode
& 0x8000) ? kUCKeyActionUp
: kUCKeyActionDown
;
1348 modifierKeyState
= 0;
1349 if (lpKeyState
[VK_SHIFT
] & 0x80)
1350 modifierKeyState
|= (shiftKey
>> 8);
1351 if (lpKeyState
[VK_CAPITAL
] & 0x01)
1352 modifierKeyState
|= (alphaLock
>> 8);
1353 if (lpKeyState
[VK_CONTROL
] & 0x80)
1354 modifierKeyState
|= (controlKey
>> 8);
1355 if (lpKeyState
[VK_MENU
] & 0x80)
1356 modifierKeyState
|= (cmdKey
>> 8);
1357 if (thread_data
->last_modifiers
& (NX_ALTERNATEMASK
| NX_DEVICELALTKEYMASK
| NX_DEVICERALTKEYMASK
))
1358 modifierKeyState
|= (optionKey
>> 8);
1360 /* Find the Mac keycode corresponding to the vkey */
1361 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1362 if (thread_data
->keyc2vkey
[keyc
] == virtKey
) break;
1364 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1366 WARN_(key
)("Unknown virtual key 0x%04x\n", virtKey
);
1370 TRACE_(key
)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc
,
1371 (keyAction
== kUCKeyActionDown
) ? "pressed" : "released", (unsigned)modifierKeyState
);
1375 if (keyAction
== kUCKeyActionUp
)
1378 options
= kUCKeyTranslateNoDeadKeysMask
;
1384 deadKeyState
= thread_data
->dead_key_state
;
1386 savedDeadKeyState
= deadKeyState
;
1387 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1388 thread_data
->keyboard_type
, options
, &deadKeyState
, bufW_size
,
1390 if (status
!= noErr
)
1392 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1397 thread_data
->dead_key_state
= deadKeyState
;
1399 if (keyAction
== kUCKeyActionUp
)
1403 if (len
== 0 && deadKeyState
)
1405 /* Repeat the translation, but disabling dead-key generation to
1406 learn which dead key it was. */
1407 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1408 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1409 &savedDeadKeyState
, bufW_size
, &len
, bufW
);
1410 if (status
!= noErr
)
1412 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1420 len
= strip_apple_private_chars(bufW
, len
);
1422 if (dead
&& len
> 0) ret
= -1;
1425 /* Control-Return produces line feed instead of carriage return. */
1426 if (ret
> 0 && (lpKeyState
[VK_CONTROL
] & 0x80) && virtKey
== VK_RETURN
)
1429 for (i
= 0; i
< len
; i
++)
1430 if (bufW
[i
] == '\r')
1435 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1436 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1437 if (1 <= ret
&& ret
< bufW_size
)
1440 TRACE_(key
)("returning %d / %s\n", ret
, debugstr_wn(bufW
, abs(ret
)));
1445 /***********************************************************************
1446 * VkKeyScanEx (MACDRV.@)
1448 * Note: Windows ignores HKL parameter and uses current active layout instead
1450 SHORT CDECL
macdrv_VkKeyScanEx(WCHAR wChar
, HKL hkl
)
1452 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1455 const UCKeyboardLayout
*uchr
;
1457 TRACE("%04x, %p\n", wChar
, hkl
);
1459 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1462 TRACE("no keyboard layout UCHR data; returning -1\n");
1466 for (state
= 0; state
< 8; state
++)
1468 UInt32 modifierKeyState
= 0;
1472 modifierKeyState
|= (shiftKey
>> 8);
1473 if ((state
& 6) == 6)
1474 modifierKeyState
|= (optionKey
>> 8);
1478 modifierKeyState
|= (controlKey
>> 8);
1480 modifierKeyState
|= (cmdKey
>> 8);
1483 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1485 UInt32 deadKeyState
= 0;
1490 if (!thread_data
->keyc2vkey
[keyc
]) continue;
1492 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifierKeyState
,
1493 thread_data
->keyboard_type
, 0, &deadKeyState
,
1495 if (status
== noErr
&& len
== 1 && uchar
== wChar
)
1497 WORD vkey
= thread_data
->keyc2vkey
[keyc
];
1499 ret
= vkey
| (state
<< 8);
1500 if ((VK_NUMPAD0
<= vkey
&& vkey
<= VK_DIVIDE
) ||
1501 keyc
== kVK_ANSI_KeypadClear
|| keyc
== kVK_ANSI_KeypadEnter
||
1502 keyc
== kVK_ANSI_KeypadEquals
)
1504 /* Keep searching for a non-numpad match, which is preferred. */
1513 TRACE(" -> 0x%04x\n", ret
);