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"
32 #include "wine/server.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(keyboard
);
35 WINE_DECLARE_DEBUG_CHANNEL(key
);
38 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
48 cmdKey
= 1 << cmdKeyBit
,
49 shiftKey
= 1 << shiftKeyBit
,
50 alphaLock
= 1 << alphaLockBit
,
51 optionKey
= 1 << optionKeyBit
,
52 controlKey
= 1 << controlKeyBit
,
56 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
68 kVK_ISO_Section
= 0x0A,
82 kVK_ANSI_Equal
= 0x18,
85 kVK_ANSI_Minus
= 0x1B,
88 kVK_ANSI_RightBracket
= 0x1E,
91 kVK_ANSI_LeftBracket
= 0x21,
97 kVK_ANSI_Quote
= 0x27,
99 kVK_ANSI_Semicolon
= 0x29,
100 kVK_ANSI_Backslash
= 0x2A,
101 kVK_ANSI_Comma
= 0x2B,
102 kVK_ANSI_Slash
= 0x2C,
105 kVK_ANSI_Period
= 0x2F,
108 kVK_ANSI_Grave
= 0x32,
111 kVK_RightCommand
= 0x36, /* invented for Wine; co-opt unused key code */
117 kVK_RightShift
= 0x3C,
118 kVK_RightOption
= 0x3D,
119 kVK_RightControl
= 0x3E,
122 kVK_ANSI_KeypadDecimal
= 0x41,
123 kVK_ANSI_KeypadMultiply
= 0x43,
124 kVK_ANSI_KeypadPlus
= 0x45,
125 kVK_ANSI_KeypadClear
= 0x47,
127 kVK_VolumeDown
= 0x49,
129 kVK_ANSI_KeypadDivide
= 0x4B,
130 kVK_ANSI_KeypadEnter
= 0x4C,
131 kVK_ANSI_KeypadMinus
= 0x4E,
134 kVK_ANSI_KeypadEquals
= 0x51,
135 kVK_ANSI_Keypad0
= 0x52,
136 kVK_ANSI_Keypad1
= 0x53,
137 kVK_ANSI_Keypad2
= 0x54,
138 kVK_ANSI_Keypad3
= 0x55,
139 kVK_ANSI_Keypad4
= 0x56,
140 kVK_ANSI_Keypad5
= 0x57,
141 kVK_ANSI_Keypad6
= 0x58,
142 kVK_ANSI_Keypad7
= 0x59,
144 kVK_ANSI_Keypad8
= 0x5B,
145 kVK_ANSI_Keypad9
= 0x5C,
147 kVK_JIS_Underscore
= 0x5E,
148 kVK_JIS_KeypadComma
= 0x5F,
167 kVK_ForwardDelete
= 0x75,
173 kVK_LeftArrow
= 0x7B,
174 kVK_RightArrow
= 0x7C,
175 kVK_DownArrow
= 0x7D,
179 extern const CFStringRef kTISTypeKeyboardLayout
;
181 /* Indexed by Mac virtual keycode values defined above. */
182 static const struct {
186 } default_map
[128] = {
187 { 'A', 0x1E, FALSE
}, /* kVK_ANSI_A */
188 { 'S', 0x1F, FALSE
}, /* kVK_ANSI_S */
189 { 'D', 0x20, FALSE
}, /* kVK_ANSI_D */
190 { 'F', 0x21, FALSE
}, /* kVK_ANSI_F */
191 { 'H', 0x23, FALSE
}, /* kVK_ANSI_H */
192 { 'G', 0x22, FALSE
}, /* kVK_ANSI_G */
193 { 'Z', 0x2C, FALSE
}, /* kVK_ANSI_Z */
194 { 'X', 0x2D, FALSE
}, /* kVK_ANSI_X */
195 { 'C', 0x2E, FALSE
}, /* kVK_ANSI_C */
196 { 'V', 0x2F, FALSE
}, /* kVK_ANSI_V */
197 { VK_OEM_102
, 0x56, TRUE
}, /* kVK_ISO_Section */
198 { 'B', 0x30, FALSE
}, /* kVK_ANSI_B */
199 { 'Q', 0x10, FALSE
}, /* kVK_ANSI_Q */
200 { 'W', 0x11, FALSE
}, /* kVK_ANSI_W */
201 { 'E', 0x12, FALSE
}, /* kVK_ANSI_E */
202 { 'R', 0x13, FALSE
}, /* kVK_ANSI_R */
203 { 'Y', 0x15, FALSE
}, /* kVK_ANSI_Y */
204 { 'T', 0x14, FALSE
}, /* kVK_ANSI_T */
205 { '1', 0x02, FALSE
}, /* kVK_ANSI_1 */
206 { '2', 0x03, FALSE
}, /* kVK_ANSI_2 */
207 { '3', 0x04, FALSE
}, /* kVK_ANSI_3 */
208 { '4', 0x05, FALSE
}, /* kVK_ANSI_4 */
209 { '6', 0x07, FALSE
}, /* kVK_ANSI_6 */
210 { '5', 0x06, FALSE
}, /* kVK_ANSI_5 */
211 { VK_OEM_PLUS
, 0x0D, FALSE
}, /* kVK_ANSI_Equal */
212 { '9', 0x0A, FALSE
}, /* kVK_ANSI_9 */
213 { '7', 0x08, FALSE
}, /* kVK_ANSI_7 */
214 { VK_OEM_MINUS
, 0x0C, FALSE
}, /* kVK_ANSI_Minus */
215 { '8', 0x09, FALSE
}, /* kVK_ANSI_8 */
216 { '0', 0x0B, FALSE
}, /* kVK_ANSI_0 */
217 { VK_OEM_6
, 0x1B, FALSE
}, /* kVK_ANSI_RightBracket */
218 { 'O', 0x18, FALSE
}, /* kVK_ANSI_O */
219 { 'U', 0x16, FALSE
}, /* kVK_ANSI_U */
220 { VK_OEM_4
, 0x1A, FALSE
}, /* kVK_ANSI_LeftBracket */
221 { 'I', 0x17, FALSE
}, /* kVK_ANSI_I */
222 { 'P', 0x19, FALSE
}, /* kVK_ANSI_P */
223 { VK_RETURN
, 0x1C, TRUE
}, /* kVK_Return */
224 { 'L', 0x26, FALSE
}, /* kVK_ANSI_L */
225 { 'J', 0x24, FALSE
}, /* kVK_ANSI_J */
226 { VK_OEM_7
, 0x28, FALSE
}, /* kVK_ANSI_Quote */
227 { 'K', 0x25, FALSE
}, /* kVK_ANSI_K */
228 { VK_OEM_1
, 0x27, FALSE
}, /* kVK_ANSI_Semicolon */
229 { VK_OEM_5
, 0x2B, FALSE
}, /* kVK_ANSI_Backslash */
230 { VK_OEM_COMMA
, 0x33, FALSE
}, /* kVK_ANSI_Comma */
231 { VK_OEM_2
, 0x35, FALSE
}, /* kVK_ANSI_Slash */
232 { 'N', 0x31, FALSE
}, /* kVK_ANSI_N */
233 { 'M', 0x32, FALSE
}, /* kVK_ANSI_M */
234 { VK_OEM_PERIOD
, 0x34, FALSE
}, /* kVK_ANSI_Period */
235 { VK_TAB
, 0x0F, TRUE
}, /* kVK_Tab */
236 { VK_SPACE
, 0x39, TRUE
}, /* kVK_Space */
237 { VK_OEM_3
, 0x29, FALSE
}, /* kVK_ANSI_Grave */
238 { VK_BACK
, 0x0E, TRUE
}, /* kVK_Delete */
239 { 0, 0, FALSE
}, /* 0x34 unused */
240 { VK_ESCAPE
, 0x01, TRUE
}, /* kVK_Escape */
241 { VK_RMENU
, 0x38 | 0x100, TRUE
}, /* kVK_RightCommand */
242 { VK_LMENU
, 0x38, TRUE
}, /* kVK_Command */
243 { VK_LSHIFT
, 0x2A, TRUE
}, /* kVK_Shift */
244 { VK_CAPITAL
, 0x3A, TRUE
}, /* kVK_CapsLock */
245 { 0, 0, FALSE
}, /* kVK_Option */
246 { VK_LCONTROL
, 0x1D, TRUE
}, /* kVK_Control */
247 { VK_RSHIFT
, 0x36, TRUE
}, /* kVK_RightShift */
248 { 0, 0, FALSE
}, /* kVK_RightOption */
249 { VK_RCONTROL
, 0x1D | 0x100, TRUE
}, /* kVK_RightControl */
250 { 0, 0, FALSE
}, /* kVK_Function */
251 { VK_F17
, 0x68, TRUE
}, /* kVK_F17 */
252 { VK_DECIMAL
, 0x53, TRUE
}, /* kVK_ANSI_KeypadDecimal */
253 { 0, 0, FALSE
}, /* 0x42 unused */
254 { VK_MULTIPLY
, 0x37, TRUE
}, /* kVK_ANSI_KeypadMultiply */
255 { 0, 0, FALSE
}, /* 0x44 unused */
256 { VK_ADD
, 0x4E, TRUE
}, /* kVK_ANSI_KeypadPlus */
257 { 0, 0, FALSE
}, /* 0x46 unused */
258 { VK_OEM_CLEAR
, 0x59, TRUE
}, /* kVK_ANSI_KeypadClear */
259 { VK_VOLUME_UP
, 0 | 0x100, TRUE
}, /* kVK_VolumeUp */
260 { VK_VOLUME_DOWN
, 0 | 0x100, TRUE
}, /* kVK_VolumeDown */
261 { VK_VOLUME_MUTE
, 0 | 0x100, TRUE
}, /* kVK_Mute */
262 { VK_DIVIDE
, 0x35 | 0x100, TRUE
}, /* kVK_ANSI_KeypadDivide */
263 { VK_RETURN
, 0x1C | 0x100, TRUE
}, /* kVK_ANSI_KeypadEnter */
264 { 0, 0, FALSE
}, /* 0x4D unused */
265 { VK_SUBTRACT
, 0x4A, TRUE
}, /* kVK_ANSI_KeypadMinus */
266 { VK_F18
, 0x69, TRUE
}, /* kVK_F18 */
267 { VK_F19
, 0x6A, TRUE
}, /* kVK_F19 */
268 { VK_OEM_NEC_EQUAL
, 0x0D | 0x100, TRUE
}, /* kVK_ANSI_KeypadEquals */
269 { VK_NUMPAD0
, 0x52, TRUE
}, /* kVK_ANSI_Keypad0 */
270 { VK_NUMPAD1
, 0x4F, TRUE
}, /* kVK_ANSI_Keypad1 */
271 { VK_NUMPAD2
, 0x50, TRUE
}, /* kVK_ANSI_Keypad2 */
272 { VK_NUMPAD3
, 0x51, TRUE
}, /* kVK_ANSI_Keypad3 */
273 { VK_NUMPAD4
, 0x4B, TRUE
}, /* kVK_ANSI_Keypad4 */
274 { VK_NUMPAD5
, 0x4C, TRUE
}, /* kVK_ANSI_Keypad5 */
275 { VK_NUMPAD6
, 0x4D, TRUE
}, /* kVK_ANSI_Keypad6 */
276 { VK_NUMPAD7
, 0x47, TRUE
}, /* kVK_ANSI_Keypad7 */
277 { VK_F20
, 0x6B, TRUE
}, /* kVK_F20 */
278 { VK_NUMPAD8
, 0x48, TRUE
}, /* kVK_ANSI_Keypad8 */
279 { VK_NUMPAD9
, 0x49, TRUE
}, /* kVK_ANSI_Keypad9 */
280 { 0xFF, 0x7D, TRUE
}, /* kVK_JIS_Yen */
281 { 0xC1, 0x73, TRUE
}, /* kVK_JIS_Underscore */
282 { VK_SEPARATOR
, 0x7E, TRUE
}, /* kVK_JIS_KeypadComma */
283 { VK_F5
, 0x3F, TRUE
}, /* kVK_F5 */
284 { VK_F6
, 0x40, TRUE
}, /* kVK_F6 */
285 { VK_F7
, 0x41, TRUE
}, /* kVK_F7 */
286 { VK_F3
, 0x3D, TRUE
}, /* kVK_F3 */
287 { VK_F8
, 0x42, TRUE
}, /* kVK_F8 */
288 { VK_F9
, 0x43, TRUE
}, /* kVK_F9 */
289 { 0xFF, 0x72, TRUE
}, /* kVK_JIS_Eisu */
290 { VK_F11
, 0x57, TRUE
}, /* kVK_F11 */
291 { VK_OEM_RESET
, 0x71, TRUE
}, /* kVK_JIS_Kana */
292 { VK_F13
, 0x64, TRUE
}, /* kVK_F13 */
293 { VK_F16
, 0x67, TRUE
}, /* kVK_F16 */
294 { VK_F14
, 0x65, TRUE
}, /* kVK_F14 */
295 { 0, 0, FALSE
}, /* 0x6C unused */
296 { VK_F10
, 0x44, TRUE
}, /* kVK_F10 */
297 { 0, 0, FALSE
}, /* 0x6E unused */
298 { VK_F12
, 0x58, TRUE
}, /* kVK_F12 */
299 { 0, 0, FALSE
}, /* 0x70 unused */
300 { VK_F15
, 0x66, TRUE
}, /* kVK_F15 */
301 { VK_INSERT
, 0x52 | 0x100, TRUE
}, /* kVK_Help */ /* map to Insert */
302 { VK_HOME
, 0x47 | 0x100, TRUE
}, /* kVK_Home */
303 { VK_PRIOR
, 0x49 | 0x100, TRUE
}, /* kVK_PageUp */
304 { VK_DELETE
, 0x53 | 0x100, TRUE
}, /* kVK_ForwardDelete */
305 { VK_F4
, 0x3E, TRUE
}, /* kVK_F4 */
306 { VK_END
, 0x4F | 0x100, TRUE
}, /* kVK_End */
307 { VK_F2
, 0x3C, TRUE
}, /* kVK_F2 */
308 { VK_NEXT
, 0x51 | 0x100, TRUE
}, /* kVK_PageDown */
309 { VK_F1
, 0x3B, TRUE
}, /* kVK_F1 */
310 { VK_LEFT
, 0x4B | 0x100, TRUE
}, /* kVK_LeftArrow */
311 { VK_RIGHT
, 0x4D | 0x100, TRUE
}, /* kVK_RightArrow */
312 { VK_DOWN
, 0x50 | 0x100, TRUE
}, /* kVK_DownArrow */
313 { VK_UP
, 0x48 | 0x100, TRUE
}, /* kVK_UpArrow */
317 static const struct {
322 { VK_BACK
, "Backspace" },
323 { VK_CAPITAL
, "Caps Lock" },
324 { VK_CONTROL
, "Ctrl" },
325 { VK_DECIMAL
, "Num Del" },
326 { VK_DELETE
| 0x100, "Delete" },
327 { VK_DIVIDE
| 0x100, "Num /" },
328 { VK_DOWN
| 0x100, "Down" },
329 { VK_END
| 0x100, "End" },
330 { VK_ESCAPE
, "Esc" },
355 { VK_HELP
| 0x100, "Help" },
356 { VK_HOME
| 0x100, "Home" },
357 { VK_INSERT
| 0x100, "Insert" },
358 { VK_LCONTROL
, "Ctrl" },
359 { VK_LEFT
| 0x100, "Left" },
361 { VK_LSHIFT
, "Shift" },
362 { VK_LWIN
| 0x100, "Win" },
364 { VK_MULTIPLY
, "Num *" },
365 { VK_NEXT
| 0x100, "Page Down" },
366 { VK_NUMLOCK
| 0x100, "Num Lock" },
367 { VK_NUMPAD0
, "Num 0" },
368 { VK_NUMPAD1
, "Num 1" },
369 { VK_NUMPAD2
, "Num 2" },
370 { VK_NUMPAD3
, "Num 3" },
371 { VK_NUMPAD4
, "Num 4" },
372 { VK_NUMPAD5
, "Num 5" },
373 { VK_NUMPAD6
, "Num 6" },
374 { VK_NUMPAD7
, "Num 7" },
375 { VK_NUMPAD8
, "Num 8" },
376 { VK_NUMPAD9
, "Num 9" },
377 { VK_OEM_CLEAR
, "Num Clear" },
378 { VK_OEM_NEC_EQUAL
| 0x100, "Num =" },
379 { VK_PRIOR
| 0x100, "Page Up" },
380 { VK_RCONTROL
| 0x100, "Right Ctrl" },
381 { VK_RETURN
, "Return" },
382 { VK_RETURN
| 0x100, "Num Enter" },
383 { VK_RIGHT
| 0x100, "Right" },
384 { VK_RMENU
| 0x100, "Right Alt" },
385 { VK_RSHIFT
, "Right Shift" },
386 { VK_RWIN
| 0x100, "Right Win" },
387 { VK_SEPARATOR
, "Num ," },
388 { VK_SHIFT
, "Shift" },
389 { VK_SPACE
, "Space" },
390 { VK_SUBTRACT
, "Num -" },
392 { VK_UP
| 0x100, "Up" },
393 { VK_VOLUME_DOWN
| 0x100, "Volume Down" },
394 { VK_VOLUME_MUTE
| 0x100, "Mute" },
395 { VK_VOLUME_UP
| 0x100, "Volume Up" },
398 HKL CDECL
macdrv_GetKeyboardLayout(DWORD
);
400 static BOOL
char_matches_string(WCHAR wchar
, UniChar
*string
, BOOL ignore_diacritics
)
403 CFStringRef s1
= CFStringCreateWithCharactersNoCopy(NULL
, (UniChar
*)&wchar
, 1, kCFAllocatorNull
);
404 CFStringRef s2
= CFStringCreateWithCharactersNoCopy(NULL
, string
, strlenW(string
), kCFAllocatorNull
);
405 CFStringCompareFlags flags
= kCFCompareCaseInsensitive
| kCFCompareNonliteral
| kCFCompareWidthInsensitive
;
406 if (ignore_diacritics
)
407 flags
|= kCFCompareDiacriticInsensitive
;
408 ret
= (CFStringCompare(s1
, s2
, flags
) == kCFCompareEqualTo
);
415 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
416 * string. Returns the length of the string after stripping. */
417 static int strip_apple_private_chars(LPWSTR bufW
, int len
)
420 for (i
= 0; i
< len
; )
422 if (0xF700 <= bufW
[i
] && bufW
[i
] <= 0xF8FF)
424 memmove(&bufW
[i
], &bufW
[i
+1], (len
- i
- 1) * sizeof(bufW
[0]));
433 static struct list layout_list
= LIST_INIT( layout_list
);
438 TISInputSourceRef input_source
;
439 BOOL enabled
; /* is the input source enabled - ie displayed in the input source selector UI */
442 static CRITICAL_SECTION layout_list_section
;
443 static CRITICAL_SECTION_DEBUG critsect_debug
=
445 0, 0, &layout_list_section
,
446 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
447 0, 0, { (DWORD_PTR
)(__FILE__
": layout_list_section") }
449 static CRITICAL_SECTION layout_list_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
451 int macdrv_layout_list_needs_update
= TRUE
;
453 static DWORD
get_lcid(CFStringRef lang
)
459 range
.length
= min(CFStringGetLength(lang
), sizeof(str
) / sizeof(str
[0]) - 1);
460 CFStringGetCharacters(lang
, range
, str
);
461 str
[range
.length
] = 0;
462 return LocaleNameToLCID(str
, 0);
465 static HKL
get_hkl(CFStringRef lang
, CFStringRef type
)
467 ULONG_PTR lcid
= get_lcid(lang
);
468 struct layout
*layout
;
470 /* Look for the last occurrence of this lcid in the list and if
471 present use that value + 0x10000 */
472 LIST_FOR_EACH_ENTRY_REV(layout
, &layout_list
, struct layout
, entry
)
474 ULONG_PTR hkl
= HandleToUlong(layout
->hkl
);
476 if (LOWORD(hkl
) == lcid
)
478 lcid
= (hkl
& ~0xe0000000) + 0x10000;
483 if (!CFEqual(type
, kTISTypeKeyboardLayout
)) lcid
|= 0xe0000000;
488 /******************************************************************
489 * get_layout_from_source
491 * Must be called while holding the layout_list_section.
492 * Note, returned layout may not currently be enabled.
494 static struct layout
*get_layout_from_source(TISInputSourceRef input
)
496 struct layout
*ret
= NULL
, *layout
;
498 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
500 if (CFEqual(input
, layout
->input_source
))
509 /***********************************************************************
512 * Must be called while holding the layout_list_section
514 * If an input source has been disabled (ie. removed from the UI) its
515 * entry remains in the layout list but is marked as such and is not
516 * enumerated by GetKeyboardLayoutList. This is to ensure the
517 * HKL <-> input source mapping is unique.
519 static void update_layout_list(void)
522 struct layout
*layout
;
525 if (!InterlockedExchange(&macdrv_layout_list_needs_update
, FALSE
)) return;
527 sources
= macdrv_create_input_source_list();
529 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
530 layout
->enabled
= FALSE
;
532 for (i
= 0; i
< CFArrayGetCount(sources
); i
++)
534 CFDictionaryRef dict
= CFArrayGetValueAtIndex(sources
, i
);
535 TISInputSourceRef input
= (TISInputSourceRef
)CFDictionaryGetValue(dict
, macdrv_input_source_input_key
);
536 layout
= get_layout_from_source(input
);
539 CFStringRef type
= CFDictionaryGetValue(dict
, macdrv_input_source_type_key
);
540 CFStringRef lang
= CFDictionaryGetValue(dict
, macdrv_input_source_lang_key
);
542 layout
= HeapAlloc(GetProcessHeap(), 0, sizeof(*layout
));
543 layout
->input_source
= (TISInputSourceRef
)CFRetain(input
);
544 layout
->hkl
= get_hkl(lang
, type
);
546 list_add_tail(&layout_list
, &layout
->entry
);
547 TRACE("adding new layout %p\n", layout
->hkl
);
550 TRACE("enabling already existing layout %p\n", layout
->hkl
);
552 layout
->enabled
= TRUE
;
558 /***********************************************************************
559 * macdrv_get_hkl_from_source
561 * Find the HKL associated with a given input source.
563 HKL
macdrv_get_hkl_from_source(TISInputSourceRef input
)
565 struct layout
*layout
;
568 EnterCriticalSection(&layout_list_section
);
570 update_layout_list();
571 layout
= get_layout_from_source(input
);
572 if (layout
) ret
= layout
->hkl
;
574 LeaveCriticalSection(&layout_list_section
);
580 /***********************************************************************
581 * macdrv_compute_keyboard_layout
583 void macdrv_compute_keyboard_layout(struct macdrv_thread_data
*thread_data
)
587 const UCKeyboardLayout
*uchr
;
588 const UInt32 modifier_combos
[] = {
592 (shiftKey
| cmdKey
) >> 8,
594 (shiftKey
| optionKey
) >> 8,
596 UniChar map
[128][sizeof(modifier_combos
) / sizeof(modifier_combos
[0])][4 + 1];
599 int ignore_diacritics
;
600 static const struct {
604 { '-', VK_OEM_MINUS
},
605 { '+', VK_OEM_PLUS
},
606 { '_', VK_OEM_MINUS
},
607 { ',', VK_OEM_COMMA
},
608 { '.', VK_OEM_PERIOD
},
609 { '=', VK_OEM_PLUS
},
610 { '>', VK_OEM_PERIOD
},
611 { '<', VK_OEM_COMMA
},
625 { ':', VK_OEM_PERIOD
},
626 { ';', VK_OEM_COMMA
},
628 { 0x00B4, VK_OEM_4
}, /* 0x00B4 is ACUTE ACCENT */
630 { 0x00A7, VK_OEM_5
}, /* 0x00A7 is SECTION SIGN */
631 { '*', VK_OEM_PLUS
},
632 { 0x00B4, VK_OEM_7
},
640 { '?', VK_OEM_PLUS
},
642 { 0x00B4, VK_OEM_3
},
643 { '?', VK_OEM_COMMA
},
644 { '~', VK_OEM_PLUS
},
647 { 0x00A7, VK_OEM_7
},
651 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
652 contiguous ranges. */
653 static const struct {
658 { VK_OEM_1
, VK_OEM_3
},
659 { VK_OEM_4
, VK_ICO_CLEAR
},
661 { VK_OEM_NEC_EQUAL
, VK_OEM_NEC_EQUAL
},
667 if (!thread_data
->keyboard_layout_uchr
)
669 ERR("no keyboard layout UCHR data\n");
673 memset(thread_data
->keyc2vkey
, 0, sizeof(thread_data
->keyc2vkey
));
674 memset(vkey_used
, 0, sizeof(vkey_used
));
676 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
678 thread_data
->keyc2scan
[keyc
] = default_map
[keyc
].scan
;
679 if (default_map
[keyc
].fixed
)
681 vkey
= default_map
[keyc
].vkey
;
682 thread_data
->keyc2vkey
[keyc
] = vkey
;
684 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc
, vkey
);
688 if (thread_data
->iso_keyboard
)
690 /* In almost all cases, the Mac key codes indicate a physical key position
691 and this corresponds nicely to Win32 scan codes. However, the Mac key
692 codes differ in one case between ANSI and ISO keyboards. For ANSI
693 keyboards, the key to the left of the digits and above the Tab key
694 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
695 some position produces kVK_ISO_Section. The additional key on ISO
696 keyboards, the one to the right of the left Shift key, produces
697 kVK_ANSI_Grave, which is just weird.
699 Since we want the key in that upper left corner to always produce the
700 same scan code (0x29), we need to swap the scan codes of those two
701 Mac key codes for ISO keyboards. */
702 DWORD temp
= thread_data
->keyc2scan
[kVK_ANSI_Grave
];
703 thread_data
->keyc2scan
[kVK_ANSI_Grave
] = thread_data
->keyc2scan
[kVK_ISO_Section
];
704 thread_data
->keyc2scan
[kVK_ISO_Section
] = temp
;
707 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
709 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
710 memset(map
, 0, sizeof(map
));
711 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
713 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
714 if (thread_data
->keyc2vkey
[keyc
]) continue; /* assigned a fixed vkey */
716 TRACE("keyc 0x%04x: ", keyc
);
718 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
725 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifier_combos
[combo
],
726 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
727 &deadKeyState
, sizeof(map
[keyc
][combo
])/sizeof(map
[keyc
][combo
][0]) - 1,
728 &len
, map
[keyc
][combo
]);
730 map
[keyc
][combo
][0] = 0;
732 TRACE("%s%s", (combo
? ", " : ""), debugstr_w(map
[keyc
][combo
]));
738 /* First try to match key codes to the vkeys for the letters A through Z.
739 Try unmodified first, then with various modifier combinations in succession.
740 On the first pass, try to get a match lacking diacritical marks. On the
741 second pass, accept matches with diacritical marks. */
742 for (ignore_diacritics
= 0; ignore_diacritics
<= 1; ignore_diacritics
++)
744 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
746 for (vkey
= 'A'; vkey
<= 'Z'; vkey
++)
751 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
753 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
756 if (char_matches_string(vkey
, map
[keyc
][combo
], ignore_diacritics
))
758 thread_data
->keyc2vkey
[keyc
] = vkey
;
760 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
761 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
769 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
770 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
772 for (vkey
= '0'; vkey
<= '9'; vkey
++)
777 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
779 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
782 if (char_matches_string(vkey
, map
[keyc
][combo
], FALSE
))
784 thread_data
->keyc2vkey
[keyc
] = vkey
;
786 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
787 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
794 /* Now try to match key codes for certain common punctuation characters to
795 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
796 for (i
= 0; i
< sizeof(symbol_vkeys
) / sizeof(symbol_vkeys
[0]); i
++)
798 vkey
= symbol_vkeys
[i
].vkey
;
803 for (combo
= 0; combo
< sizeof(modifier_combos
) / sizeof(modifier_combos
[0]); combo
++)
805 for (keyc
= 0; keyc
< sizeof(map
) / sizeof(map
[0]); keyc
++)
807 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
808 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
811 if (char_matches_string(symbol_vkeys
[i
].wchar
, map
[keyc
][combo
], FALSE
))
813 thread_data
->keyc2vkey
[keyc
] = vkey
;
815 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
816 debugstr_wn(&symbol_vkeys
[i
].wchar
, 1), debugstr_w(map
[keyc
][combo
]));
826 /* For those key codes still without a vkey, try to use the default vkey
827 from the default map, if it's still available. */
828 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
830 DWORD vkey
= default_map
[keyc
].vkey
;
832 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
833 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
835 if (!vkey_used
[vkey
])
837 thread_data
->keyc2vkey
[keyc
] = vkey
;
839 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc
, vkey
);
843 /* For any unassigned key codes which would map to a letter in the default
844 map, but whose normal letter vkey wasn't available, try to find a
847 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
849 if (default_map
[keyc
].vkey
< 'A' || 'Z' < default_map
[keyc
].vkey
)
850 continue; /* not a letter in ANSI layout */
851 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
852 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
854 while (vkey
<= 'Z' && vkey_used
[vkey
]) vkey
++;
857 thread_data
->keyc2vkey
[keyc
] = vkey
;
859 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc
, vkey
);
862 break; /* no more unused letter vkeys, so stop trying */
865 /* Same thing but with the digits. */
867 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
869 if (default_map
[keyc
].vkey
< '0' || '9' < default_map
[keyc
].vkey
)
870 continue; /* not a digit in ANSI layout */
871 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
872 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
874 while (vkey
<= '9' && vkey_used
[vkey
]) vkey
++;
877 thread_data
->keyc2vkey
[keyc
] = vkey
;
879 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc
, vkey
);
882 break; /* no more unused digit vkeys, so stop trying */
885 /* Last chance. Assign any available vkey. */
887 vkey
= vkey_ranges
[vkey_range
].first
;
888 for (keyc
= 0; keyc
< sizeof(default_map
) / sizeof(default_map
[0]); keyc
++)
890 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
891 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
893 while (vkey
&& vkey_used
[vkey
])
895 if (vkey
== vkey_ranges
[vkey_range
].last
)
898 vkey
= vkey_ranges
[vkey_range
].first
;
906 WARN("No more vkeys available!\n");
910 thread_data
->keyc2vkey
[keyc
] = vkey
;
912 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc
, vkey
);
917 /***********************************************************************
918 * macdrv_send_keyboard_input
920 static void macdrv_send_keyboard_input(HWND hwnd
, WORD vkey
, WORD scan
, DWORD flags
, DWORD time
)
924 TRACE_(key
)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd
, vkey
, scan
, flags
);
926 input
.type
= INPUT_KEYBOARD
;
928 input
.ki
.wScan
= scan
;
929 input
.ki
.dwFlags
= flags
;
930 input
.ki
.time
= time
;
931 input
.ki
.dwExtraInfo
= 0;
933 __wine_send_input(hwnd
, &input
);
937 /***********************************************************************
938 * get_async_key_state
940 static BOOL
get_async_key_state(BYTE state
[256])
944 SERVER_START_REQ(get_key_state
)
948 wine_server_set_reply(req
, state
, 256);
949 ret
= !wine_server_call(req
);
956 /***********************************************************************
957 * update_modifier_state
959 static void update_modifier_state(unsigned int modifier
, unsigned int modifiers
, const BYTE
*keystate
,
960 WORD vkey
, WORD alt_vkey
, WORD scan
, WORD alt_scan
,
961 DWORD event_time
, BOOL restore
)
963 int key_pressed
= (modifiers
& modifier
) != 0;
964 int vkey_pressed
= (keystate
[vkey
] & 0x80) || (keystate
[alt_vkey
] & 0x80);
967 if (key_pressed
!= vkey_pressed
)
971 flags
= (scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0;
973 flags
|= KEYEVENTF_KEYUP
;
975 macdrv_send_keyboard_input(NULL
, vkey
, scan
& 0xff, flags
, event_time
);
979 flags
= restore
? 0 : KEYEVENTF_KEYUP
;
981 if (keystate
[vkey
] & 0x80)
983 macdrv_send_keyboard_input(NULL
, vkey
, scan
& 0xff,
984 flags
| ((scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0),
987 if (keystate
[alt_vkey
] & 0x80)
989 macdrv_send_keyboard_input(NULL
, alt_vkey
, alt_scan
& 0xff,
990 flags
| ((alt_scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0),
998 /***********************************************************************
1001 * Handler for KEY_PRESS and KEY_RELEASE events.
1003 void macdrv_key_event(HWND hwnd
, const macdrv_event
*event
)
1005 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1009 TRACE_(key
)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1010 hwnd
, event
->window
, (event
->type
== KEY_PRESS
? "press" : "release"),
1011 event
->key
.keycode
, event
->key
.modifiers
);
1013 thread_data
->last_modifiers
= event
->key
.modifiers
;
1015 if (event
->key
.keycode
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1017 vkey
= thread_data
->keyc2vkey
[event
->key
.keycode
];
1018 scan
= thread_data
->keyc2scan
[event
->key
.keycode
];
1023 TRACE_(key
)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1024 event
->key
.keycode
, vkey
, scan
);
1029 if (event
->type
== KEY_RELEASE
) flags
|= KEYEVENTF_KEYUP
;
1030 if (scan
& 0x100) flags
|= KEYEVENTF_EXTENDEDKEY
;
1032 macdrv_send_keyboard_input(hwnd
, vkey
, scan
& 0xff, flags
, event
->key
.time_ms
);
1036 /***********************************************************************
1037 * macdrv_keyboard_changed
1039 * Handler for KEYBOARD_CHANGED events.
1041 void macdrv_keyboard_changed(const macdrv_event
*event
)
1043 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1045 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event
->keyboard_changed
.uchr
,
1046 event
->keyboard_changed
.keyboard_type
, event
->keyboard_changed
.iso_keyboard
);
1048 if (thread_data
->keyboard_layout_uchr
)
1049 CFRelease(thread_data
->keyboard_layout_uchr
);
1050 thread_data
->keyboard_layout_uchr
= CFDataCreateCopy(NULL
, event
->keyboard_changed
.uchr
);
1051 thread_data
->keyboard_type
= event
->keyboard_changed
.keyboard_type
;
1052 thread_data
->iso_keyboard
= event
->keyboard_changed
.iso_keyboard
;
1053 thread_data
->active_keyboard_layout
= macdrv_get_hkl_from_source(event
->keyboard_changed
.input_source
);
1054 thread_data
->dead_key_state
= 0;
1056 macdrv_compute_keyboard_layout(thread_data
);
1058 SendMessageW(GetActiveWindow(), WM_CANCELMODE
, 0, 0);
1062 /***********************************************************************
1063 * macdrv_hotkey_press
1065 * Handler for HOTKEY_PRESS events.
1067 void macdrv_hotkey_press(const macdrv_event
*event
)
1069 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1071 TRACE_(key
)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
1072 event
->hotkey_press
.vkey
, event
->hotkey_press
.mod_flags
, event
->hotkey_press
.keycode
,
1073 event
->hotkey_press
.time_ms
);
1075 if (event
->hotkey_press
.keycode
< sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]))
1077 WORD scan
= thread_data
->keyc2scan
[event
->hotkey_press
.keycode
];
1082 if ((got_keystate
= get_async_key_state(keystate
)))
1084 update_modifier_state(MOD_ALT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LMENU
, VK_RMENU
,
1085 0x38, 0x138, event
->hotkey_press
.time_ms
, FALSE
);
1086 update_modifier_state(MOD_CONTROL
, event
->hotkey_press
.mod_flags
, keystate
, VK_LCONTROL
, VK_RCONTROL
,
1087 0x1D, 0x11D, event
->hotkey_press
.time_ms
, FALSE
);
1088 update_modifier_state(MOD_SHIFT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LSHIFT
, VK_RSHIFT
,
1089 0x2A, 0x36, event
->hotkey_press
.time_ms
, FALSE
);
1090 update_modifier_state(MOD_WIN
, event
->hotkey_press
.mod_flags
, keystate
, VK_LWIN
, VK_RWIN
,
1091 0x15B, 0x15C, event
->hotkey_press
.time_ms
, FALSE
);
1094 activate_on_following_focus();
1096 flags
= (scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0;
1097 macdrv_send_keyboard_input(NULL
, event
->hotkey_press
.vkey
, scan
& 0xff,
1098 flags
, event
->key
.time_ms
);
1099 macdrv_send_keyboard_input(NULL
, event
->hotkey_press
.vkey
, scan
& 0xff,
1100 flags
| KEYEVENTF_KEYUP
, event
->key
.time_ms
);
1104 update_modifier_state(MOD_ALT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LMENU
, VK_RMENU
,
1105 0x38, 0x138, event
->hotkey_press
.time_ms
, TRUE
);
1106 update_modifier_state(MOD_CONTROL
, event
->hotkey_press
.mod_flags
, keystate
, VK_LCONTROL
, VK_RCONTROL
,
1107 0x1D, 0x11D, event
->hotkey_press
.time_ms
, TRUE
);
1108 update_modifier_state(MOD_SHIFT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LSHIFT
, VK_RSHIFT
,
1109 0x2A, 0x36, event
->hotkey_press
.time_ms
, TRUE
);
1110 update_modifier_state(MOD_WIN
, event
->hotkey_press
.mod_flags
, keystate
, VK_LWIN
, VK_RWIN
,
1111 0x15B, 0x15C, event
->hotkey_press
.time_ms
, TRUE
);
1117 /***********************************************************************
1118 * macdrv_process_text_input
1120 BOOL
macdrv_process_text_input(UINT vkey
, UINT scan
, UINT repeat
, const BYTE
*key_state
, void *himc
)
1122 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1127 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", vkey
, scan
, repeat
, himc
);
1129 flags
= thread_data
->last_modifiers
;
1130 if (key_state
[VK_SHIFT
] & 0x80)
1131 flags
|= NX_SHIFTMASK
;
1133 flags
&= ~(NX_SHIFTMASK
| NX_DEVICELSHIFTKEYMASK
| NX_DEVICERSHIFTKEYMASK
);
1134 if (key_state
[VK_CAPITAL
] & 0x01)
1135 flags
|= NX_ALPHASHIFTMASK
;
1137 flags
&= ~NX_ALPHASHIFTMASK
;
1138 if (key_state
[VK_CONTROL
] & 0x80)
1139 flags
|= NX_CONTROLMASK
;
1141 flags
&= ~(NX_CONTROLMASK
| NX_DEVICELCTLKEYMASK
| NX_DEVICERCTLKEYMASK
);
1142 if (key_state
[VK_MENU
] & 0x80)
1143 flags
|= NX_COMMANDMASK
;
1145 flags
&= ~(NX_COMMANDMASK
| NX_DEVICELCMDKEYMASK
| NX_DEVICERCMDKEYMASK
);
1147 /* Find the Mac keycode corresponding to the scan code */
1148 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1149 if (thread_data
->keyc2vkey
[keyc
] == vkey
) break;
1151 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1154 TRACE("flags 0x%08x keyc 0x%04x\n", flags
, keyc
);
1156 ret
= macdrv_send_text_input_event(((scan
& 0x8000) == 0), flags
, repeat
, keyc
, himc
);
1159 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");
1164 /***********************************************************************
1165 * ActivateKeyboardLayout (MACDRV.@)
1167 HKL CDECL
macdrv_ActivateKeyboardLayout(HKL hkl
, UINT flags
)
1170 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1171 struct layout
*layout
;
1173 TRACE("hkl %p flags %04x\n", hkl
, flags
);
1175 if (flags
) FIXME("flags %x not supported\n",flags
);
1177 if (hkl
== (HKL
)HKL_NEXT
|| hkl
== (HKL
)HKL_PREV
)
1179 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1180 FIXME("HKL_NEXT and HKL_PREV not supported\n");
1184 EnterCriticalSection(&layout_list_section
);
1185 update_layout_list();
1187 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
1189 if (layout
->hkl
== hkl
)
1191 if (macdrv_select_input_source(layout
->input_source
))
1193 oldHkl
= thread_data
->active_keyboard_layout
;
1194 thread_data
->active_keyboard_layout
= hkl
;
1199 LeaveCriticalSection(&layout_list_section
);
1205 /***********************************************************************
1208 void CDECL
macdrv_Beep(void)
1214 /***********************************************************************
1215 * GetKeyNameText (MACDRV.@)
1217 INT CDECL
macdrv_GetKeyNameText(LONG lparam
, LPWSTR buffer
, INT size
)
1219 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1222 scan
= (lparam
>> 16) & 0x1FF;
1223 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2scan
)/sizeof(thread_data
->keyc2scan
[0]); keyc
++)
1225 if (thread_data
->keyc2scan
[keyc
] == scan
)
1227 static const WCHAR dead
[] = {' ','d','e','a','d',0};
1228 const UCKeyboardLayout
*uchr
;
1229 UInt32 deadKeyState
= 0;
1235 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1236 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDisplay
, 0, thread_data
->keyboard_type
,
1237 0, &deadKeyState
, size
- 1, &len
, (UniChar
*)buffer
);
1238 if (status
!= noErr
)
1240 if (len
&& isgraphW(buffer
[0]))
1243 vkey
= thread_data
->keyc2vkey
[keyc
];
1244 if (lparam
& (1 << 25))
1246 /* Caller doesn't care about distinctions between left and
1252 vkey
= VK_SHIFT
; break;
1255 vkey
= VK_CONTROL
; break;
1258 vkey
= VK_MENU
; break;
1262 if (scan
& 0x100) vkey
|= 0x100;
1264 for (i
= 0; i
< sizeof(vkey_names
) / sizeof(vkey_names
[0]); i
++)
1266 if (vkey_names
[i
].vkey
== vkey
)
1268 len
= MultiByteToWideChar(CP_UTF8
, 0, vkey_names
[i
].name
, -1, buffer
, size
);
1276 static const WCHAR format
[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1277 snprintfW(buffer
, size
, format
, vkey
);
1278 len
= strlenW(buffer
);
1284 if (status
== noErr
&& deadKeyState
)
1286 lstrcpynW(buffer
+ len
, dead
, size
- len
);
1287 len
= strlenW(buffer
);
1290 TRACE("lparam 0x%08x -> %s\n", lparam
, debugstr_w(buffer
));
1295 WARN("found no name for lparam 0x%08x\n", lparam
);
1300 /***********************************************************************
1301 * GetKeyboardLayout (MACDRV.@)
1303 HKL CDECL
macdrv_GetKeyboardLayout(DWORD thread_id
)
1305 if (thread_id
&& thread_id
!= GetCurrentThreadId())
1306 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id
);
1308 return macdrv_init_thread_data()->active_keyboard_layout
;
1312 /***********************************************************************
1313 * GetKeyboardLayoutList (MACDRV.@)
1315 UINT CDECL
macdrv_GetKeyboardLayoutList(INT size
, HKL
*list
)
1318 struct layout
*layout
;
1320 TRACE("%d, %p\n", size
, list
);
1322 EnterCriticalSection(&layout_list_section
);
1324 update_layout_list();
1326 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
1328 if (!layout
->enabled
) continue;
1331 if (count
>= size
) break;
1332 list
[count
] = layout
->hkl
;
1333 TRACE("\t%d: %p\n", count
, list
[count
]);
1337 LeaveCriticalSection(&layout_list_section
);
1339 TRACE("returning %d\n", count
);
1343 /***********************************************************************
1344 * GetKeyboardLayoutName (MACDRV.@)
1346 BOOL CDECL
macdrv_GetKeyboardLayoutName(LPWSTR name
)
1348 static const WCHAR formatW
[] = {'%','0','8','x',0};
1351 layout
= HandleToUlong(macdrv_GetKeyboardLayout(0));
1352 if (HIWORD(layout
) == LOWORD(layout
)) layout
= LOWORD(layout
);
1353 sprintfW(name
, formatW
, layout
);
1354 TRACE("returning %s\n", debugstr_w(name
));
1359 /***********************************************************************
1360 * MapVirtualKeyEx (MACDRV.@)
1362 UINT CDECL
macdrv_MapVirtualKeyEx(UINT wCode
, UINT wMapType
, HKL hkl
)
1364 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1368 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode
, wMapType
, hkl
);
1372 case MAPVK_VK_TO_VSC
: /* vkey-code to scan-code */
1373 case MAPVK_VK_TO_VSC_EX
:
1376 case VK_SHIFT
: wCode
= VK_LSHIFT
; break;
1377 case VK_CONTROL
: wCode
= VK_LCONTROL
; break;
1378 case VK_MENU
: wCode
= VK_LMENU
; break;
1381 /* vkey -> keycode -> scan */
1382 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1384 if (thread_data
->keyc2vkey
[keyc
] == wCode
)
1386 ret
= thread_data
->keyc2scan
[keyc
] & 0xFF;
1392 case MAPVK_VSC_TO_VK
: /* scan-code to vkey-code */
1393 case MAPVK_VSC_TO_VK_EX
:
1394 /* scan -> keycode -> vkey */
1395 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1396 if ((thread_data
->keyc2scan
[keyc
] & 0xFF) == (wCode
& 0xFF))
1398 ret
= thread_data
->keyc2vkey
[keyc
];
1399 /* Only stop if it's not a numpad vkey; otherwise keep
1400 looking for a potential better vkey. */
1401 if (ret
&& (ret
< VK_NUMPAD0
|| VK_DIVIDE
< ret
))
1405 if (wMapType
== MAPVK_VSC_TO_VK
)
1410 ret
= VK_SHIFT
; break;
1413 ret
= VK_CONTROL
; break;
1416 ret
= VK_MENU
; break;
1421 case MAPVK_VK_TO_CHAR
: /* vkey-code to character */
1423 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1424 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1425 const UCKeyboardLayout
*uchr
;
1428 UInt32 deadKeyState
;
1430 BOOL deadKey
= FALSE
;
1432 if ((VK_PRIOR
<= wCode
&& wCode
<= VK_HELP
) ||
1433 (VK_F1
<= wCode
&& wCode
<= VK_F24
))
1436 if (!thread_data
|| !thread_data
->keyboard_layout_uchr
)
1438 WARN("No keyboard layout uchr data\n");
1442 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1444 /* Find the Mac keycode corresponding to the vkey */
1445 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1446 if (thread_data
->keyc2vkey
[keyc
] == wCode
) break;
1448 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1450 WARN("Unknown virtual key %X\n", wCode
);
1454 TRACE("Found keycode %u\n", keyc
);
1457 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1458 thread_data
->keyboard_type
, 0, &deadKeyState
,
1459 sizeof(s
)/sizeof(s
[0]), &len
, s
);
1460 if (status
== noErr
&& !len
&& deadKeyState
)
1464 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1465 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1466 &deadKeyState
, sizeof(s
)/sizeof(s
[0]), &len
, s
);
1469 if (status
== noErr
&& len
)
1470 ret
= toupperW(s
[0]) | (deadKey
? 0x80000000 : 0);
1474 default: /* reserved */
1475 FIXME("Unknown wMapType %d\n", wMapType
);
1479 TRACE("returning 0x%04x\n", ret
);
1484 /***********************************************************************
1485 * RegisterHotKey (MACDRV.@)
1487 BOOL CDECL
macdrv_RegisterHotKey(HWND hwnd
, UINT mod_flags
, UINT vkey
)
1489 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1490 unsigned int keyc
, modifiers
= 0;
1493 TRACE_(key
)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd
, mod_flags
, vkey
);
1495 /* Find the Mac keycode corresponding to the vkey */
1496 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1497 if (thread_data
->keyc2vkey
[keyc
] == vkey
) break;
1499 if (keyc
>= sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]))
1501 WARN_(key
)("ignoring unknown virtual key 0x%04x\n", vkey
);
1505 if (mod_flags
& MOD_ALT
) modifiers
|= cmdKey
;
1506 if (mod_flags
& MOD_CONTROL
) modifiers
|= controlKey
;
1507 if (mod_flags
& MOD_SHIFT
) modifiers
|= shiftKey
;
1508 if (mod_flags
& MOD_WIN
)
1510 WARN_(key
)("MOD_WIN not supported; ignoring\n");
1514 ret
= macdrv_register_hot_key(thread_data
->queue
, vkey
, mod_flags
, keyc
, modifiers
);
1515 TRACE_(key
)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc
, modifiers
, ret
);
1517 if (ret
== MACDRV_HOTKEY_ALREADY_REGISTERED
)
1518 SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
1519 else if (ret
!= MACDRV_HOTKEY_SUCCESS
)
1520 SetLastError(ERROR_GEN_FAILURE
);
1522 return ret
== MACDRV_HOTKEY_SUCCESS
;
1526 /***********************************************************************
1527 * ToUnicodeEx (MACDRV.@)
1529 * The ToUnicode function translates the specified virtual-key code and keyboard
1530 * state to the corresponding Windows character or characters.
1532 * If the specified key is a dead key, the return value is negative. Otherwise,
1533 * it is one of the following values:
1535 * -1 The specified virtual key is a dead-key. If possible, the
1536 * non-combining form of the dead character is written to bufW.
1537 * 0 The specified virtual key has no translation for the current
1538 * state of the keyboard.
1539 * 1 One Windows character was copied to the buffer.
1540 * 2 or more Multiple characters were copied to the buffer. This usually
1541 * happens when a dead-key character (accent or diacritic) stored
1542 * in the keyboard layout cannot be composed with the specified
1543 * virtual key to form a single character.
1546 INT CDECL
macdrv_ToUnicodeEx(UINT virtKey
, UINT scanCode
, const BYTE
*lpKeyState
,
1547 LPWSTR bufW
, int bufW_size
, UINT flags
, HKL hkl
)
1549 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1552 BOOL is_menu
= (flags
& 0x1);
1554 const UCKeyboardLayout
*uchr
;
1556 UInt32 modifierKeyState
;
1558 UInt32 deadKeyState
, savedDeadKeyState
;
1562 TRACE_(key
)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1563 virtKey
, scanCode
, lpKeyState
, bufW
, bufW_size
, flags
, hkl
);
1568 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1569 modifier key press. We want it to effectively ignore modifier key
1570 presses. I think that one isn't supposed to call it at all for modifier
1571 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1572 are different event types than key up/down events. */
1588 /* There are a number of key combinations for which Windows does not
1589 produce characters, but Mac keyboard layouts may. Eat them. Do this
1590 here to avoid the expense of UCKeyTranslate() but also because these
1591 keys shouldn't terminate dead key sequences. */
1592 if ((VK_PRIOR
<= virtKey
&& virtKey
<= VK_HELP
) || (VK_F1
<= virtKey
&& virtKey
<= VK_F24
))
1595 /* Shift + <non-digit keypad keys>. */
1596 if ((lpKeyState
[VK_SHIFT
] & 0x80) && VK_MULTIPLY
<= virtKey
&& virtKey
<= VK_DIVIDE
)
1599 if (lpKeyState
[VK_CONTROL
] & 0x80)
1601 /* Control-Tab, with or without other modifiers. */
1602 if (virtKey
== VK_TAB
)
1605 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1607 if ((lpKeyState
[VK_SHIFT
] & 0x80) || (lpKeyState
[VK_MENU
] & 0x80))
1621 if (thread_data
->keyboard_layout_uchr
)
1622 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1626 keyAction
= (scanCode
& 0x8000) ? kUCKeyActionUp
: kUCKeyActionDown
;
1628 modifierKeyState
= 0;
1629 if (lpKeyState
[VK_SHIFT
] & 0x80)
1630 modifierKeyState
|= (shiftKey
>> 8);
1631 if (lpKeyState
[VK_CAPITAL
] & 0x01)
1632 modifierKeyState
|= (alphaLock
>> 8);
1633 if (lpKeyState
[VK_CONTROL
] & 0x80)
1634 modifierKeyState
|= (controlKey
>> 8);
1635 if (lpKeyState
[VK_MENU
] & 0x80)
1636 modifierKeyState
|= (cmdKey
>> 8);
1637 if (thread_data
->last_modifiers
& (NX_ALTERNATEMASK
| NX_DEVICELALTKEYMASK
| NX_DEVICERALTKEYMASK
))
1638 modifierKeyState
|= (optionKey
>> 8);
1640 /* Find the Mac keycode corresponding to the vkey */
1641 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1642 if (thread_data
->keyc2vkey
[keyc
] == virtKey
) break;
1644 if (keyc
>= sizeof(thread_data
->keyc2vkey
)/sizeof(thread_data
->keyc2vkey
[0]))
1646 WARN_(key
)("Unknown virtual key 0x%04x\n", virtKey
);
1650 TRACE_(key
)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc
,
1651 (keyAction
== kUCKeyActionDown
) ? "pressed" : "released", (unsigned)modifierKeyState
);
1655 if (keyAction
== kUCKeyActionUp
)
1658 options
= kUCKeyTranslateNoDeadKeysMask
;
1664 deadKeyState
= thread_data
->dead_key_state
;
1666 savedDeadKeyState
= deadKeyState
;
1667 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1668 thread_data
->keyboard_type
, options
, &deadKeyState
, bufW_size
,
1670 if (status
!= noErr
)
1672 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1677 if (keyAction
!= kUCKeyActionUp
&& len
> 0 && deadKeyState
== thread_data
->dead_key_state
)
1678 thread_data
->dead_key_state
= 0;
1680 thread_data
->dead_key_state
= deadKeyState
;
1682 if (keyAction
== kUCKeyActionUp
)
1686 if (len
== 0 && deadKeyState
)
1688 /* Repeat the translation, but disabling dead-key generation to
1689 learn which dead key it was. */
1690 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1691 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1692 &savedDeadKeyState
, bufW_size
, &len
, bufW
);
1693 if (status
!= noErr
)
1695 ERR_(key
)("Couldn't translate keycode 0x%04x, status %ld\n", keyc
, status
);
1703 len
= strip_apple_private_chars(bufW
, len
);
1705 if (dead
&& len
> 0) ret
= -1;
1708 /* Control-Return produces line feed instead of carriage return. */
1709 if (ret
> 0 && (lpKeyState
[VK_CONTROL
] & 0x80) && virtKey
== VK_RETURN
)
1712 for (i
= 0; i
< len
; i
++)
1713 if (bufW
[i
] == '\r')
1718 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1719 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1720 if (1 <= ret
&& ret
< bufW_size
)
1723 TRACE_(key
)("returning %d / %s\n", ret
, debugstr_wn(bufW
, abs(ret
)));
1728 /***********************************************************************
1729 * UnregisterHotKey (MACDRV.@)
1731 void CDECL
macdrv_UnregisterHotKey(HWND hwnd
, UINT modifiers
, UINT vkey
)
1733 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1735 TRACE_(key
)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd
, modifiers
, vkey
);
1738 macdrv_unregister_hot_key(thread_data
->queue
, vkey
, modifiers
);
1742 /***********************************************************************
1743 * VkKeyScanEx (MACDRV.@)
1745 * Note: Windows ignores HKL parameter and uses current active layout instead
1747 SHORT CDECL
macdrv_VkKeyScanEx(WCHAR wChar
, HKL hkl
)
1749 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1752 const UCKeyboardLayout
*uchr
;
1754 TRACE("%04x, %p\n", wChar
, hkl
);
1756 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1759 TRACE("no keyboard layout UCHR data; returning -1\n");
1763 for (state
= 0; state
< 8; state
++)
1765 UInt32 modifierKeyState
= 0;
1769 modifierKeyState
|= (shiftKey
>> 8);
1770 if ((state
& 6) == 6)
1771 modifierKeyState
|= (optionKey
>> 8);
1775 modifierKeyState
|= (controlKey
>> 8);
1777 modifierKeyState
|= (cmdKey
>> 8);
1780 for (keyc
= 0; keyc
< sizeof(thread_data
->keyc2vkey
) / sizeof(thread_data
->keyc2vkey
[0]); keyc
++)
1782 UInt32 deadKeyState
= 0;
1787 if (!thread_data
->keyc2vkey
[keyc
]) continue;
1789 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifierKeyState
,
1790 thread_data
->keyboard_type
, 0, &deadKeyState
,
1792 if (status
== noErr
&& len
== 1 && uchar
== wChar
)
1794 WORD vkey
= thread_data
->keyc2vkey
[keyc
];
1796 ret
= vkey
| (state
<< 8);
1797 if ((VK_NUMPAD0
<= vkey
&& vkey
<= VK_DIVIDE
) ||
1798 keyc
== kVK_ANSI_KeypadClear
|| keyc
== kVK_ANSI_KeypadEnter
||
1799 keyc
== kVK_ANSI_KeypadEquals
)
1801 /* Keep searching for a non-numpad match, which is preferred. */
1810 TRACE(" -> 0x%04x\n", ret
);