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
35 #include "wine/server.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(keyboard
);
38 WINE_DECLARE_DEBUG_CHANNEL(key
);
41 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
51 cmdKey
= 1 << cmdKeyBit
,
52 shiftKey
= 1 << shiftKeyBit
,
53 alphaLock
= 1 << alphaLockBit
,
54 optionKey
= 1 << optionKeyBit
,
55 controlKey
= 1 << controlKeyBit
,
59 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
71 kVK_ISO_Section
= 0x0A,
85 kVK_ANSI_Equal
= 0x18,
88 kVK_ANSI_Minus
= 0x1B,
91 kVK_ANSI_RightBracket
= 0x1E,
94 kVK_ANSI_LeftBracket
= 0x21,
100 kVK_ANSI_Quote
= 0x27,
102 kVK_ANSI_Semicolon
= 0x29,
103 kVK_ANSI_Backslash
= 0x2A,
104 kVK_ANSI_Comma
= 0x2B,
105 kVK_ANSI_Slash
= 0x2C,
108 kVK_ANSI_Period
= 0x2F,
111 kVK_ANSI_Grave
= 0x32,
114 kVK_RightCommand
= 0x36, /* invented for Wine; co-opt unused key code */
120 kVK_RightShift
= 0x3C,
121 kVK_RightOption
= 0x3D,
122 kVK_RightControl
= 0x3E,
125 kVK_ANSI_KeypadDecimal
= 0x41,
126 kVK_ANSI_KeypadMultiply
= 0x43,
127 kVK_ANSI_KeypadPlus
= 0x45,
128 kVK_ANSI_KeypadClear
= 0x47,
130 kVK_VolumeDown
= 0x49,
132 kVK_ANSI_KeypadDivide
= 0x4B,
133 kVK_ANSI_KeypadEnter
= 0x4C,
134 kVK_ANSI_KeypadMinus
= 0x4E,
137 kVK_ANSI_KeypadEquals
= 0x51,
138 kVK_ANSI_Keypad0
= 0x52,
139 kVK_ANSI_Keypad1
= 0x53,
140 kVK_ANSI_Keypad2
= 0x54,
141 kVK_ANSI_Keypad3
= 0x55,
142 kVK_ANSI_Keypad4
= 0x56,
143 kVK_ANSI_Keypad5
= 0x57,
144 kVK_ANSI_Keypad6
= 0x58,
145 kVK_ANSI_Keypad7
= 0x59,
147 kVK_ANSI_Keypad8
= 0x5B,
148 kVK_ANSI_Keypad9
= 0x5C,
150 kVK_JIS_Underscore
= 0x5E,
151 kVK_JIS_KeypadComma
= 0x5F,
170 kVK_ForwardDelete
= 0x75,
176 kVK_LeftArrow
= 0x7B,
177 kVK_RightArrow
= 0x7C,
178 kVK_DownArrow
= 0x7D,
182 extern const CFStringRef kTISTypeKeyboardLayout
;
184 /* Indexed by Mac virtual keycode values defined above. */
185 static const struct {
189 } default_map
[128] = {
190 { 'A', 0x1E, FALSE
}, /* kVK_ANSI_A */
191 { 'S', 0x1F, FALSE
}, /* kVK_ANSI_S */
192 { 'D', 0x20, FALSE
}, /* kVK_ANSI_D */
193 { 'F', 0x21, FALSE
}, /* kVK_ANSI_F */
194 { 'H', 0x23, FALSE
}, /* kVK_ANSI_H */
195 { 'G', 0x22, FALSE
}, /* kVK_ANSI_G */
196 { 'Z', 0x2C, FALSE
}, /* kVK_ANSI_Z */
197 { 'X', 0x2D, FALSE
}, /* kVK_ANSI_X */
198 { 'C', 0x2E, FALSE
}, /* kVK_ANSI_C */
199 { 'V', 0x2F, FALSE
}, /* kVK_ANSI_V */
200 { VK_OEM_102
, 0x56, TRUE
}, /* kVK_ISO_Section */
201 { 'B', 0x30, FALSE
}, /* kVK_ANSI_B */
202 { 'Q', 0x10, FALSE
}, /* kVK_ANSI_Q */
203 { 'W', 0x11, FALSE
}, /* kVK_ANSI_W */
204 { 'E', 0x12, FALSE
}, /* kVK_ANSI_E */
205 { 'R', 0x13, FALSE
}, /* kVK_ANSI_R */
206 { 'Y', 0x15, FALSE
}, /* kVK_ANSI_Y */
207 { 'T', 0x14, FALSE
}, /* kVK_ANSI_T */
208 { '1', 0x02, FALSE
}, /* kVK_ANSI_1 */
209 { '2', 0x03, FALSE
}, /* kVK_ANSI_2 */
210 { '3', 0x04, FALSE
}, /* kVK_ANSI_3 */
211 { '4', 0x05, FALSE
}, /* kVK_ANSI_4 */
212 { '6', 0x07, FALSE
}, /* kVK_ANSI_6 */
213 { '5', 0x06, FALSE
}, /* kVK_ANSI_5 */
214 { VK_OEM_PLUS
, 0x0D, FALSE
}, /* kVK_ANSI_Equal */
215 { '9', 0x0A, FALSE
}, /* kVK_ANSI_9 */
216 { '7', 0x08, FALSE
}, /* kVK_ANSI_7 */
217 { VK_OEM_MINUS
, 0x0C, FALSE
}, /* kVK_ANSI_Minus */
218 { '8', 0x09, FALSE
}, /* kVK_ANSI_8 */
219 { '0', 0x0B, FALSE
}, /* kVK_ANSI_0 */
220 { VK_OEM_6
, 0x1B, FALSE
}, /* kVK_ANSI_RightBracket */
221 { 'O', 0x18, FALSE
}, /* kVK_ANSI_O */
222 { 'U', 0x16, FALSE
}, /* kVK_ANSI_U */
223 { VK_OEM_4
, 0x1A, FALSE
}, /* kVK_ANSI_LeftBracket */
224 { 'I', 0x17, FALSE
}, /* kVK_ANSI_I */
225 { 'P', 0x19, FALSE
}, /* kVK_ANSI_P */
226 { VK_RETURN
, 0x1C, TRUE
}, /* kVK_Return */
227 { 'L', 0x26, FALSE
}, /* kVK_ANSI_L */
228 { 'J', 0x24, FALSE
}, /* kVK_ANSI_J */
229 { VK_OEM_7
, 0x28, FALSE
}, /* kVK_ANSI_Quote */
230 { 'K', 0x25, FALSE
}, /* kVK_ANSI_K */
231 { VK_OEM_1
, 0x27, FALSE
}, /* kVK_ANSI_Semicolon */
232 { VK_OEM_5
, 0x2B, FALSE
}, /* kVK_ANSI_Backslash */
233 { VK_OEM_COMMA
, 0x33, FALSE
}, /* kVK_ANSI_Comma */
234 { VK_OEM_2
, 0x35, FALSE
}, /* kVK_ANSI_Slash */
235 { 'N', 0x31, FALSE
}, /* kVK_ANSI_N */
236 { 'M', 0x32, FALSE
}, /* kVK_ANSI_M */
237 { VK_OEM_PERIOD
, 0x34, FALSE
}, /* kVK_ANSI_Period */
238 { VK_TAB
, 0x0F, TRUE
}, /* kVK_Tab */
239 { VK_SPACE
, 0x39, TRUE
}, /* kVK_Space */
240 { VK_OEM_3
, 0x29, FALSE
}, /* kVK_ANSI_Grave */
241 { VK_BACK
, 0x0E, TRUE
}, /* kVK_Delete */
242 { 0, 0, FALSE
}, /* 0x34 unused */
243 { VK_ESCAPE
, 0x01, TRUE
}, /* kVK_Escape */
244 { VK_RMENU
, 0x38 | 0x100, TRUE
}, /* kVK_RightCommand */
245 { VK_LMENU
, 0x38, TRUE
}, /* kVK_Command */
246 { VK_LSHIFT
, 0x2A, TRUE
}, /* kVK_Shift */
247 { VK_CAPITAL
, 0x3A, TRUE
}, /* kVK_CapsLock */
248 { 0, 0, FALSE
}, /* kVK_Option */
249 { VK_LCONTROL
, 0x1D, TRUE
}, /* kVK_Control */
250 { VK_RSHIFT
, 0x36, TRUE
}, /* kVK_RightShift */
251 { 0, 0, FALSE
}, /* kVK_RightOption */
252 { VK_RCONTROL
, 0x1D | 0x100, TRUE
}, /* kVK_RightControl */
253 { 0, 0, FALSE
}, /* kVK_Function */
254 { VK_F17
, 0x68, TRUE
}, /* kVK_F17 */
255 { VK_DECIMAL
, 0x53, TRUE
}, /* kVK_ANSI_KeypadDecimal */
256 { 0, 0, FALSE
}, /* 0x42 unused */
257 { VK_MULTIPLY
, 0x37, TRUE
}, /* kVK_ANSI_KeypadMultiply */
258 { 0, 0, FALSE
}, /* 0x44 unused */
259 { VK_ADD
, 0x4E, TRUE
}, /* kVK_ANSI_KeypadPlus */
260 { 0, 0, FALSE
}, /* 0x46 unused */
261 { VK_OEM_CLEAR
, 0x59, TRUE
}, /* kVK_ANSI_KeypadClear */
262 { VK_VOLUME_UP
, 0 | 0x100, TRUE
}, /* kVK_VolumeUp */
263 { VK_VOLUME_DOWN
, 0 | 0x100, TRUE
}, /* kVK_VolumeDown */
264 { VK_VOLUME_MUTE
, 0 | 0x100, TRUE
}, /* kVK_Mute */
265 { VK_DIVIDE
, 0x35 | 0x100, TRUE
}, /* kVK_ANSI_KeypadDivide */
266 { VK_RETURN
, 0x1C | 0x100, TRUE
}, /* kVK_ANSI_KeypadEnter */
267 { 0, 0, FALSE
}, /* 0x4D unused */
268 { VK_SUBTRACT
, 0x4A, TRUE
}, /* kVK_ANSI_KeypadMinus */
269 { VK_F18
, 0x69, TRUE
}, /* kVK_F18 */
270 { VK_F19
, 0x6A, TRUE
}, /* kVK_F19 */
271 { VK_OEM_NEC_EQUAL
, 0x0D | 0x100, TRUE
}, /* kVK_ANSI_KeypadEquals */
272 { VK_NUMPAD0
, 0x52, TRUE
}, /* kVK_ANSI_Keypad0 */
273 { VK_NUMPAD1
, 0x4F, TRUE
}, /* kVK_ANSI_Keypad1 */
274 { VK_NUMPAD2
, 0x50, TRUE
}, /* kVK_ANSI_Keypad2 */
275 { VK_NUMPAD3
, 0x51, TRUE
}, /* kVK_ANSI_Keypad3 */
276 { VK_NUMPAD4
, 0x4B, TRUE
}, /* kVK_ANSI_Keypad4 */
277 { VK_NUMPAD5
, 0x4C, TRUE
}, /* kVK_ANSI_Keypad5 */
278 { VK_NUMPAD6
, 0x4D, TRUE
}, /* kVK_ANSI_Keypad6 */
279 { VK_NUMPAD7
, 0x47, TRUE
}, /* kVK_ANSI_Keypad7 */
280 { VK_F20
, 0x6B, TRUE
}, /* kVK_F20 */
281 { VK_NUMPAD8
, 0x48, TRUE
}, /* kVK_ANSI_Keypad8 */
282 { VK_NUMPAD9
, 0x49, TRUE
}, /* kVK_ANSI_Keypad9 */
283 { 0xFF, 0x7D, TRUE
}, /* kVK_JIS_Yen */
284 { 0xC1, 0x73, TRUE
}, /* kVK_JIS_Underscore */
285 { VK_SEPARATOR
, 0x7E, TRUE
}, /* kVK_JIS_KeypadComma */
286 { VK_F5
, 0x3F, TRUE
}, /* kVK_F5 */
287 { VK_F6
, 0x40, TRUE
}, /* kVK_F6 */
288 { VK_F7
, 0x41, TRUE
}, /* kVK_F7 */
289 { VK_F3
, 0x3D, TRUE
}, /* kVK_F3 */
290 { VK_F8
, 0x42, TRUE
}, /* kVK_F8 */
291 { VK_F9
, 0x43, TRUE
}, /* kVK_F9 */
292 { 0xFF, 0x72, TRUE
}, /* kVK_JIS_Eisu */
293 { VK_F11
, 0x57, TRUE
}, /* kVK_F11 */
294 { VK_OEM_RESET
, 0x71, TRUE
}, /* kVK_JIS_Kana */
295 { VK_F13
, 0x64, TRUE
}, /* kVK_F13 */
296 { VK_F16
, 0x67, TRUE
}, /* kVK_F16 */
297 { VK_F14
, 0x65, TRUE
}, /* kVK_F14 */
298 { 0, 0, FALSE
}, /* 0x6C unused */
299 { VK_F10
, 0x44, TRUE
}, /* kVK_F10 */
300 { 0, 0, FALSE
}, /* 0x6E unused */
301 { VK_F12
, 0x58, TRUE
}, /* kVK_F12 */
302 { 0, 0, FALSE
}, /* 0x70 unused */
303 { VK_F15
, 0x66, TRUE
}, /* kVK_F15 */
304 { VK_INSERT
, 0x52 | 0x100, TRUE
}, /* kVK_Help */ /* map to Insert */
305 { VK_HOME
, 0x47 | 0x100, TRUE
}, /* kVK_Home */
306 { VK_PRIOR
, 0x49 | 0x100, TRUE
}, /* kVK_PageUp */
307 { VK_DELETE
, 0x53 | 0x100, TRUE
}, /* kVK_ForwardDelete */
308 { VK_F4
, 0x3E, TRUE
}, /* kVK_F4 */
309 { VK_END
, 0x4F | 0x100, TRUE
}, /* kVK_End */
310 { VK_F2
, 0x3C, TRUE
}, /* kVK_F2 */
311 { VK_NEXT
, 0x51 | 0x100, TRUE
}, /* kVK_PageDown */
312 { VK_F1
, 0x3B, TRUE
}, /* kVK_F1 */
313 { VK_LEFT
, 0x4B | 0x100, TRUE
}, /* kVK_LeftArrow */
314 { VK_RIGHT
, 0x4D | 0x100, TRUE
}, /* kVK_RightArrow */
315 { VK_DOWN
, 0x50 | 0x100, TRUE
}, /* kVK_DownArrow */
316 { VK_UP
, 0x48 | 0x100, TRUE
}, /* kVK_UpArrow */
320 static const struct {
325 { VK_BACK
, "Backspace" },
326 { VK_CAPITAL
, "Caps Lock" },
327 { VK_CONTROL
, "Ctrl" },
328 { VK_DECIMAL
, "Num Del" },
329 { VK_DELETE
| 0x100, "Delete" },
330 { VK_DIVIDE
| 0x100, "Num /" },
331 { VK_DOWN
| 0x100, "Down" },
332 { VK_END
| 0x100, "End" },
333 { VK_ESCAPE
, "Esc" },
358 { VK_HELP
| 0x100, "Help" },
359 { VK_HOME
| 0x100, "Home" },
360 { VK_INSERT
| 0x100, "Insert" },
361 { VK_LCONTROL
, "Ctrl" },
362 { VK_LEFT
| 0x100, "Left" },
364 { VK_LSHIFT
, "Shift" },
365 { VK_LWIN
| 0x100, "Win" },
367 { VK_MULTIPLY
, "Num *" },
368 { VK_NEXT
| 0x100, "Page Down" },
369 { VK_NUMLOCK
| 0x100, "Num Lock" },
370 { VK_NUMPAD0
, "Num 0" },
371 { VK_NUMPAD1
, "Num 1" },
372 { VK_NUMPAD2
, "Num 2" },
373 { VK_NUMPAD3
, "Num 3" },
374 { VK_NUMPAD4
, "Num 4" },
375 { VK_NUMPAD5
, "Num 5" },
376 { VK_NUMPAD6
, "Num 6" },
377 { VK_NUMPAD7
, "Num 7" },
378 { VK_NUMPAD8
, "Num 8" },
379 { VK_NUMPAD9
, "Num 9" },
380 { VK_OEM_CLEAR
, "Num Clear" },
381 { VK_OEM_NEC_EQUAL
| 0x100, "Num =" },
382 { VK_PRIOR
| 0x100, "Page Up" },
383 { VK_RCONTROL
| 0x100, "Right Ctrl" },
384 { VK_RETURN
, "Return" },
385 { VK_RETURN
| 0x100, "Num Enter" },
386 { VK_RIGHT
| 0x100, "Right" },
387 { VK_RMENU
| 0x100, "Right Alt" },
388 { VK_RSHIFT
, "Right Shift" },
389 { VK_RWIN
| 0x100, "Right Win" },
390 { VK_SEPARATOR
, "Num ," },
391 { VK_SHIFT
, "Shift" },
392 { VK_SPACE
, "Space" },
393 { VK_SUBTRACT
, "Num -" },
395 { VK_UP
| 0x100, "Up" },
396 { VK_VOLUME_DOWN
| 0x100, "Volume Down" },
397 { VK_VOLUME_MUTE
| 0x100, "Mute" },
398 { VK_VOLUME_UP
| 0x100, "Volume Up" },
401 static BOOL
char_matches_string(WCHAR wchar
, UniChar
*string
, BOOL ignore_diacritics
)
404 CFStringRef s1
= CFStringCreateWithCharactersNoCopy(NULL
, (UniChar
*)&wchar
, 1, kCFAllocatorNull
);
405 CFStringRef s2
= CFStringCreateWithCharactersNoCopy(NULL
, string
, wcslen(string
), kCFAllocatorNull
);
406 CFStringCompareFlags flags
= kCFCompareCaseInsensitive
| kCFCompareNonliteral
| kCFCompareWidthInsensitive
;
407 if (ignore_diacritics
)
408 flags
|= kCFCompareDiacriticInsensitive
;
409 ret
= (CFStringCompare(s1
, s2
, flags
) == kCFCompareEqualTo
);
416 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
417 * string. Returns the length of the string after stripping. */
418 static int strip_apple_private_chars(LPWSTR bufW
, int len
)
421 for (i
= 0; i
< len
; )
423 if (0xF700 <= bufW
[i
] && bufW
[i
] <= 0xF8FF)
425 memmove(&bufW
[i
], &bufW
[i
+1], (len
- i
- 1) * sizeof(bufW
[0]));
434 static struct list layout_list
= LIST_INIT( layout_list
);
439 TISInputSourceRef input_source
;
440 BOOL enabled
; /* is the input source enabled - ie displayed in the input source selector UI */
443 static pthread_mutex_t layout_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
445 int macdrv_layout_list_needs_update
= TRUE
;
447 static const NLS_LOCALE_HEADER
*locale_table
;
449 static int compare_locale_names(const WCHAR
*n1
, const WCHAR
*n2
)
455 if (ch1
>= 'a' && ch1
<= 'z') ch1
-= 'a' - 'A';
456 else if (ch1
== '_') ch1
= '-';
457 if (ch2
>= 'a' && ch2
<= 'z') ch2
-= 'a' - 'A';
458 else if (ch2
== '_') ch2
= '-';
459 if (!ch1
|| ch1
!= ch2
) return ch1
- ch2
;
464 static const NLS_LOCALE_LCNAME_INDEX
*find_lcname_entry(const WCHAR
*name
)
466 const NLS_LOCALE_LCNAME_INDEX
*lcnames_index
;
467 const WCHAR
*locale_strings
;
468 int min
= 0, max
= locale_table
->nb_lcnames
- 1;
470 locale_strings
= (const WCHAR
*)((char *)locale_table
+ locale_table
->strings_offset
);
471 lcnames_index
= (const NLS_LOCALE_LCNAME_INDEX
*)((char *)locale_table
+ locale_table
->lcnames_offset
);
475 int res
, pos
= (min
+ max
) / 2;
476 const WCHAR
*str
= locale_strings
+ lcnames_index
[pos
].name
;
477 res
= compare_locale_names(name
, str
+ 1);
478 if (res
< 0) max
= pos
- 1;
479 else if (res
> 0) min
= pos
+ 1;
480 else return &lcnames_index
[pos
];
486 static DWORD
get_lcid(CFStringRef lang
)
488 const NLS_LOCALE_LCNAME_INDEX
*entry
;
489 const NLS_LOCALE_DATA
*locale
;
510 if (NtInitializeNlsFiles((void **)&header
, &system_lcid
, &size
))
512 ERR("NtInitializeNlsFiles failed\n");
516 if (InterlockedCompareExchangePointer((void **)&locale_table
,
517 (char *)header
+ header
->locales
, NULL
))
518 NtUnmapViewOfSection(GetCurrentProcess(), header
);
522 range
.length
= min(CFStringGetLength(lang
), ARRAY_SIZE(str
) - 1);
523 CFStringGetCharacters(lang
, range
, str
);
524 str
[range
.length
] = 0;
526 if (!(entry
= find_lcname_entry(str
)))
528 ERR("%s not found\n", debugstr_w(str
));
532 offset
= locale_table
->locales_offset
+ entry
->idx
* locale_table
->locale_size
;
533 locale
= (const NLS_LOCALE_DATA
*)((const char *)locale_table
+ offset
);
534 return locale
->inotneutral
? entry
->id
: locale
->idefaultlanguage
;
537 static HKL
get_hkl(CFStringRef lang
, CFStringRef type
)
539 ULONG_PTR lcid
= get_lcid(lang
);
540 struct layout
*layout
;
542 /* Look for the last occurrence of this lcid in the list and if
543 present use that value + 0x10000 */
544 LIST_FOR_EACH_ENTRY_REV(layout
, &layout_list
, struct layout
, entry
)
546 ULONG_PTR hkl
= HandleToUlong(layout
->hkl
);
548 if (LOWORD(hkl
) == lcid
)
550 lcid
= (hkl
& ~0xe0000000) + 0x10000;
555 if (!CFEqual(type
, kTISTypeKeyboardLayout
)) lcid
|= 0xe0000000;
560 /******************************************************************
561 * get_layout_from_source
563 * Must be called while holding the layout_list_mutex.
564 * Note, returned layout may not currently be enabled.
566 static struct layout
*get_layout_from_source(TISInputSourceRef input
)
568 struct layout
*ret
= NULL
, *layout
;
570 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
572 if (CFEqual(input
, layout
->input_source
))
581 /***********************************************************************
584 * Must be called while holding the layout_list_mutex
586 * If an input source has been disabled (ie. removed from the UI) its
587 * entry remains in the layout list but is marked as such and is not
588 * enumerated by GetKeyboardLayoutList. This is to ensure the
589 * HKL <-> input source mapping is unique.
591 static void update_layout_list(void)
594 struct layout
*layout
;
597 if (!InterlockedExchange((LONG
*)&macdrv_layout_list_needs_update
, FALSE
)) return;
599 sources
= macdrv_create_input_source_list();
601 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
602 layout
->enabled
= FALSE
;
604 for (i
= 0; i
< CFArrayGetCount(sources
); i
++)
606 CFDictionaryRef dict
= CFArrayGetValueAtIndex(sources
, i
);
607 TISInputSourceRef input
= (TISInputSourceRef
)CFDictionaryGetValue(dict
, macdrv_input_source_input_key
);
608 layout
= get_layout_from_source(input
);
611 CFStringRef type
= CFDictionaryGetValue(dict
, macdrv_input_source_type_key
);
612 CFStringRef lang
= CFDictionaryGetValue(dict
, macdrv_input_source_lang_key
);
614 layout
= malloc(sizeof(*layout
));
615 layout
->input_source
= (TISInputSourceRef
)CFRetain(input
);
616 layout
->hkl
= get_hkl(lang
, type
);
618 list_add_tail(&layout_list
, &layout
->entry
);
619 TRACE("adding new layout %p\n", layout
->hkl
);
622 TRACE("enabling already existing layout %p\n", layout
->hkl
);
624 layout
->enabled
= TRUE
;
630 /***********************************************************************
631 * macdrv_get_hkl_from_source
633 * Find the HKL associated with a given input source.
635 HKL
macdrv_get_hkl_from_source(TISInputSourceRef input
)
637 struct layout
*layout
;
640 pthread_mutex_lock(&layout_list_mutex
);
642 update_layout_list();
643 layout
= get_layout_from_source(input
);
644 if (layout
) ret
= layout
->hkl
;
646 pthread_mutex_unlock(&layout_list_mutex
);
652 /***********************************************************************
653 * macdrv_compute_keyboard_layout
655 void macdrv_compute_keyboard_layout(struct macdrv_thread_data
*thread_data
)
659 const UCKeyboardLayout
*uchr
;
660 const UInt32 modifier_combos
[] = {
664 (shiftKey
| cmdKey
) >> 8,
666 (shiftKey
| optionKey
) >> 8,
668 UniChar map
[128][ARRAY_SIZE(modifier_combos
)][4 + 1];
671 int ignore_diacritics
;
672 static const struct {
676 { '-', VK_OEM_MINUS
},
677 { '+', VK_OEM_PLUS
},
678 { '_', VK_OEM_MINUS
},
679 { ',', VK_OEM_COMMA
},
680 { '.', VK_OEM_PERIOD
},
681 { '=', VK_OEM_PLUS
},
682 { '>', VK_OEM_PERIOD
},
683 { '<', VK_OEM_COMMA
},
697 { ':', VK_OEM_PERIOD
},
698 { ';', VK_OEM_COMMA
},
700 { 0x00B4, VK_OEM_4
}, /* 0x00B4 is ACUTE ACCENT */
702 { 0x00A7, VK_OEM_5
}, /* 0x00A7 is SECTION SIGN */
703 { '*', VK_OEM_PLUS
},
704 { 0x00B4, VK_OEM_7
},
712 { '?', VK_OEM_PLUS
},
714 { 0x00B4, VK_OEM_3
},
715 { '?', VK_OEM_COMMA
},
716 { '~', VK_OEM_PLUS
},
719 { 0x00A7, VK_OEM_7
},
723 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
724 contiguous ranges. */
725 static const struct {
730 { VK_OEM_1
, VK_OEM_3
},
731 { VK_OEM_4
, VK_ICO_CLEAR
},
733 { VK_OEM_NEC_EQUAL
, VK_OEM_NEC_EQUAL
},
739 if (!thread_data
->keyboard_layout_uchr
)
741 ERR("no keyboard layout UCHR data\n");
745 memset(thread_data
->keyc2vkey
, 0, sizeof(thread_data
->keyc2vkey
));
746 memset(vkey_used
, 0, sizeof(vkey_used
));
748 for (keyc
= 0; keyc
< ARRAY_SIZE(default_map
); keyc
++)
750 thread_data
->keyc2scan
[keyc
] = default_map
[keyc
].scan
;
751 if (default_map
[keyc
].fixed
)
753 vkey
= default_map
[keyc
].vkey
;
754 thread_data
->keyc2vkey
[keyc
] = vkey
;
756 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc
, vkey
);
760 if (thread_data
->iso_keyboard
)
762 /* In almost all cases, the Mac key codes indicate a physical key position
763 and this corresponds nicely to Win32 scan codes. However, the Mac key
764 codes differ in one case between ANSI and ISO keyboards. For ANSI
765 keyboards, the key to the left of the digits and above the Tab key
766 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
767 some position produces kVK_ISO_Section. The additional key on ISO
768 keyboards, the one to the right of the left Shift key, produces
769 kVK_ANSI_Grave, which is just weird.
771 Since we want the key in that upper left corner to always produce the
772 same scan code (0x29), we need to swap the scan codes of those two
773 Mac key codes for ISO keyboards. */
774 DWORD temp
= thread_data
->keyc2scan
[kVK_ANSI_Grave
];
775 thread_data
->keyc2scan
[kVK_ANSI_Grave
] = thread_data
->keyc2scan
[kVK_ISO_Section
];
776 thread_data
->keyc2scan
[kVK_ISO_Section
] = temp
;
779 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
781 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
782 memset(map
, 0, sizeof(map
));
783 for (keyc
= 0; keyc
< ARRAY_SIZE(map
); keyc
++)
785 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
786 if (thread_data
->keyc2vkey
[keyc
]) continue; /* assigned a fixed vkey */
788 TRACE("keyc 0x%04x: ", keyc
);
790 for (combo
= 0; combo
< ARRAY_SIZE(modifier_combos
); combo
++)
797 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifier_combos
[combo
],
798 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
799 &deadKeyState
, ARRAY_SIZE(map
[keyc
][combo
]) - 1, &len
, map
[keyc
][combo
]);
801 map
[keyc
][combo
][0] = 0;
803 TRACE("%s%s", (combo
? ", " : ""), debugstr_w(map
[keyc
][combo
]));
809 /* First try to match key codes to the vkeys for the letters A through Z.
810 Try unmodified first, then with various modifier combinations in succession.
811 On the first pass, try to get a match lacking diacritical marks. On the
812 second pass, accept matches with diacritical marks. */
813 for (ignore_diacritics
= 0; ignore_diacritics
<= 1; ignore_diacritics
++)
815 for (combo
= 0; combo
< ARRAY_SIZE(modifier_combos
); combo
++)
817 for (vkey
= 'A'; vkey
<= 'Z'; vkey
++)
822 for (keyc
= 0; keyc
< ARRAY_SIZE(map
); keyc
++)
824 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
827 if (char_matches_string(vkey
, map
[keyc
][combo
], ignore_diacritics
))
829 thread_data
->keyc2vkey
[keyc
] = vkey
;
831 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
832 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
840 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
841 for (combo
= 0; combo
< ARRAY_SIZE(modifier_combos
); combo
++)
843 for (vkey
= '0'; vkey
<= '9'; vkey
++)
848 for (keyc
= 0; keyc
< ARRAY_SIZE(map
); keyc
++)
850 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
853 if (char_matches_string(vkey
, map
[keyc
][combo
], FALSE
))
855 thread_data
->keyc2vkey
[keyc
] = vkey
;
857 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
858 debugstr_wn(&vkey
, 1), debugstr_w(map
[keyc
][combo
]));
865 /* Now try to match key codes for certain common punctuation characters to
866 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
867 for (i
= 0; i
< ARRAY_SIZE(symbol_vkeys
); i
++)
869 vkey
= symbol_vkeys
[i
].vkey
;
874 for (combo
= 0; combo
< ARRAY_SIZE(modifier_combos
); combo
++)
876 for (keyc
= 0; keyc
< ARRAY_SIZE(map
); keyc
++)
878 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
879 if (thread_data
->keyc2vkey
[keyc
] || !map
[keyc
][combo
][0])
882 if (char_matches_string(symbol_vkeys
[i
].wchar
, map
[keyc
][combo
], FALSE
))
884 thread_data
->keyc2vkey
[keyc
] = vkey
;
886 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc
, vkey
,
887 debugstr_wn(&symbol_vkeys
[i
].wchar
, 1), debugstr_w(map
[keyc
][combo
]));
897 /* For those key codes still without a vkey, try to use the default vkey
898 from the default map, if it's still available. */
899 for (keyc
= 0; keyc
< ARRAY_SIZE(default_map
); keyc
++)
901 unsigned int vkey
= default_map
[keyc
].vkey
;
903 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
904 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
906 if (!vkey_used
[vkey
])
908 thread_data
->keyc2vkey
[keyc
] = vkey
;
910 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc
, vkey
);
914 /* For any unassigned key codes which would map to a letter in the default
915 map, but whose normal letter vkey wasn't available, try to find a
918 for (keyc
= 0; keyc
< ARRAY_SIZE(default_map
); keyc
++)
920 if (default_map
[keyc
].vkey
< 'A' || 'Z' < default_map
[keyc
].vkey
)
921 continue; /* not a letter in ANSI layout */
922 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
923 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
925 while (vkey
<= 'Z' && vkey_used
[vkey
]) vkey
++;
928 thread_data
->keyc2vkey
[keyc
] = vkey
;
930 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc
, vkey
);
933 break; /* no more unused letter vkeys, so stop trying */
936 /* Same thing but with the digits. */
938 for (keyc
= 0; keyc
< ARRAY_SIZE(default_map
); keyc
++)
940 if (default_map
[keyc
].vkey
< '0' || '9' < default_map
[keyc
].vkey
)
941 continue; /* not a digit in ANSI layout */
942 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
943 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
945 while (vkey
<= '9' && vkey_used
[vkey
]) vkey
++;
948 thread_data
->keyc2vkey
[keyc
] = vkey
;
950 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc
, vkey
);
953 break; /* no more unused digit vkeys, so stop trying */
956 /* Last chance. Assign any available vkey. */
958 vkey
= vkey_ranges
[vkey_range
].first
;
959 for (keyc
= 0; keyc
< ARRAY_SIZE(default_map
); keyc
++)
961 if (!thread_data
->keyc2scan
[keyc
]) continue; /* not a known Mac key code */
962 if (thread_data
->keyc2vkey
[keyc
]) continue; /* already assigned */
964 while (vkey
&& vkey_used
[vkey
])
966 if (vkey
== vkey_ranges
[vkey_range
].last
)
969 vkey
= vkey_ranges
[vkey_range
].first
;
977 WARN("No more vkeys available!\n");
981 thread_data
->keyc2vkey
[keyc
] = vkey
;
983 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc
, vkey
);
988 /***********************************************************************
989 * macdrv_send_keyboard_input
991 static void macdrv_send_keyboard_input(HWND hwnd
, WORD vkey
, WORD scan
, unsigned int flags
, unsigned int time
)
995 TRACE_(key
)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd
, vkey
, scan
, flags
);
997 input
.type
= INPUT_KEYBOARD
;
999 input
.ki
.wScan
= scan
;
1000 input
.ki
.dwFlags
= flags
;
1001 input
.ki
.time
= time
;
1002 input
.ki
.dwExtraInfo
= 0;
1004 __wine_send_input(hwnd
, &input
, NULL
);
1008 /***********************************************************************
1009 * get_async_key_state
1011 static BOOL
get_async_key_state(BYTE state
[256])
1015 SERVER_START_REQ(get_key_state
)
1019 wine_server_set_reply(req
, state
, 256);
1020 ret
= !wine_server_call(req
);
1027 /***********************************************************************
1028 * update_modifier_state
1030 static void update_modifier_state(unsigned int modifier
, unsigned int modifiers
, const BYTE
*keystate
,
1031 WORD vkey
, WORD alt_vkey
, WORD scan
, WORD alt_scan
,
1032 DWORD event_time
, BOOL restore
)
1034 int key_pressed
= (modifiers
& modifier
) != 0;
1035 int vkey_pressed
= (keystate
[vkey
] & 0x80) || (keystate
[alt_vkey
] & 0x80);
1038 if (key_pressed
!= vkey_pressed
)
1042 flags
= (scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0;
1044 flags
|= KEYEVENTF_KEYUP
;
1046 macdrv_send_keyboard_input(NULL
, vkey
, scan
& 0xff, flags
, event_time
);
1050 flags
= restore
? 0 : KEYEVENTF_KEYUP
;
1052 if (keystate
[vkey
] & 0x80)
1054 macdrv_send_keyboard_input(NULL
, vkey
, scan
& 0xff,
1055 flags
| ((scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0),
1058 if (keystate
[alt_vkey
] & 0x80)
1060 macdrv_send_keyboard_input(NULL
, alt_vkey
, alt_scan
& 0xff,
1061 flags
| ((alt_scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0),
1069 /***********************************************************************
1072 * Handler for KEY_PRESS and KEY_RELEASE events.
1074 void macdrv_key_event(HWND hwnd
, const macdrv_event
*event
)
1076 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1080 TRACE_(key
)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1081 hwnd
, event
->window
, (event
->type
== KEY_PRESS
? "press" : "release"),
1082 event
->key
.keycode
, event
->key
.modifiers
);
1084 thread_data
->last_modifiers
= event
->key
.modifiers
;
1086 if (event
->key
.keycode
< ARRAY_SIZE(thread_data
->keyc2vkey
))
1088 vkey
= thread_data
->keyc2vkey
[event
->key
.keycode
];
1089 scan
= thread_data
->keyc2scan
[event
->key
.keycode
];
1094 TRACE_(key
)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1095 event
->key
.keycode
, vkey
, scan
);
1100 if (event
->type
== KEY_RELEASE
) flags
|= KEYEVENTF_KEYUP
;
1101 if (scan
& 0x100) flags
|= KEYEVENTF_EXTENDEDKEY
;
1103 macdrv_send_keyboard_input(hwnd
, vkey
, scan
& 0xff, flags
, event
->key
.time_ms
);
1107 /***********************************************************************
1108 * macdrv_keyboard_changed
1110 * Handler for KEYBOARD_CHANGED events.
1112 void macdrv_keyboard_changed(const macdrv_event
*event
)
1114 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1116 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event
->keyboard_changed
.uchr
,
1117 event
->keyboard_changed
.keyboard_type
, event
->keyboard_changed
.iso_keyboard
);
1119 if (thread_data
->keyboard_layout_uchr
)
1120 CFRelease(thread_data
->keyboard_layout_uchr
);
1121 thread_data
->keyboard_layout_uchr
= CFDataCreateCopy(NULL
, event
->keyboard_changed
.uchr
);
1122 thread_data
->keyboard_type
= event
->keyboard_changed
.keyboard_type
;
1123 thread_data
->iso_keyboard
= event
->keyboard_changed
.iso_keyboard
;
1124 thread_data
->active_keyboard_layout
= macdrv_get_hkl_from_source(event
->keyboard_changed
.input_source
);
1125 thread_data
->dead_key_state
= 0;
1127 macdrv_compute_keyboard_layout(thread_data
);
1129 NtUserActivateKeyboardLayout(thread_data
->active_keyboard_layout
, 0);
1131 send_message(get_active_window(), WM_CANCELMODE
, 0, 0);
1135 /***********************************************************************
1136 * macdrv_hotkey_press
1138 * Handler for HOTKEY_PRESS events.
1140 void macdrv_hotkey_press(const macdrv_event
*event
)
1142 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1144 TRACE_(key
)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
1145 event
->hotkey_press
.vkey
, event
->hotkey_press
.mod_flags
, event
->hotkey_press
.keycode
,
1146 event
->hotkey_press
.time_ms
);
1148 if (event
->hotkey_press
.keycode
< ARRAY_SIZE(thread_data
->keyc2vkey
))
1150 WORD scan
= thread_data
->keyc2scan
[event
->hotkey_press
.keycode
];
1155 if ((got_keystate
= get_async_key_state(keystate
)))
1157 update_modifier_state(MOD_ALT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LMENU
, VK_RMENU
,
1158 0x38, 0x138, event
->hotkey_press
.time_ms
, FALSE
);
1159 update_modifier_state(MOD_CONTROL
, event
->hotkey_press
.mod_flags
, keystate
, VK_LCONTROL
, VK_RCONTROL
,
1160 0x1D, 0x11D, event
->hotkey_press
.time_ms
, FALSE
);
1161 update_modifier_state(MOD_SHIFT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LSHIFT
, VK_RSHIFT
,
1162 0x2A, 0x36, event
->hotkey_press
.time_ms
, FALSE
);
1163 update_modifier_state(MOD_WIN
, event
->hotkey_press
.mod_flags
, keystate
, VK_LWIN
, VK_RWIN
,
1164 0x15B, 0x15C, event
->hotkey_press
.time_ms
, FALSE
);
1167 activate_on_following_focus();
1169 flags
= (scan
& 0x100) ? KEYEVENTF_EXTENDEDKEY
: 0;
1170 macdrv_send_keyboard_input(NULL
, event
->hotkey_press
.vkey
, scan
& 0xff,
1171 flags
, event
->key
.time_ms
);
1172 macdrv_send_keyboard_input(NULL
, event
->hotkey_press
.vkey
, scan
& 0xff,
1173 flags
| KEYEVENTF_KEYUP
, event
->key
.time_ms
);
1177 update_modifier_state(MOD_ALT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LMENU
, VK_RMENU
,
1178 0x38, 0x138, event
->hotkey_press
.time_ms
, TRUE
);
1179 update_modifier_state(MOD_CONTROL
, event
->hotkey_press
.mod_flags
, keystate
, VK_LCONTROL
, VK_RCONTROL
,
1180 0x1D, 0x11D, event
->hotkey_press
.time_ms
, TRUE
);
1181 update_modifier_state(MOD_SHIFT
, event
->hotkey_press
.mod_flags
, keystate
, VK_LSHIFT
, VK_RSHIFT
,
1182 0x2A, 0x36, event
->hotkey_press
.time_ms
, TRUE
);
1183 update_modifier_state(MOD_WIN
, event
->hotkey_press
.mod_flags
, keystate
, VK_LWIN
, VK_RWIN
,
1184 0x15B, 0x15C, event
->hotkey_press
.time_ms
, TRUE
);
1190 /***********************************************************************
1191 * macdrv_process_text_input
1193 NTSTATUS
macdrv_ime_process_text_input(void *arg
)
1195 struct process_text_input_params
*params
= arg
;
1196 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1197 const BYTE
*key_state
= params
->key_state
;
1201 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", params
->vkey
, params
->scan
,
1202 params
->repeat
, params
->himc
);
1204 flags
= thread_data
->last_modifiers
;
1205 if (key_state
[VK_SHIFT
] & 0x80)
1206 flags
|= NX_SHIFTMASK
;
1208 flags
&= ~(NX_SHIFTMASK
| NX_DEVICELSHIFTKEYMASK
| NX_DEVICERSHIFTKEYMASK
);
1209 if (key_state
[VK_CAPITAL
] & 0x01)
1210 flags
|= NX_ALPHASHIFTMASK
;
1212 flags
&= ~NX_ALPHASHIFTMASK
;
1213 if (key_state
[VK_CONTROL
] & 0x80)
1214 flags
|= NX_CONTROLMASK
;
1216 flags
&= ~(NX_CONTROLMASK
| NX_DEVICELCTLKEYMASK
| NX_DEVICERCTLKEYMASK
);
1217 if (key_state
[VK_MENU
] & 0x80)
1218 flags
|= NX_COMMANDMASK
;
1220 flags
&= ~(NX_COMMANDMASK
| NX_DEVICELCMDKEYMASK
| NX_DEVICERCMDKEYMASK
);
1222 /* Find the Mac keycode corresponding to the scan code */
1223 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1224 if (thread_data
->keyc2vkey
[keyc
] == params
->vkey
) break;
1226 if (keyc
>= ARRAY_SIZE(thread_data
->keyc2vkey
))
1232 TRACE("flags 0x%08x keyc 0x%04x\n", flags
, keyc
);
1234 macdrv_send_text_input_event(((params
->scan
& 0x8000) == 0), flags
, params
->repeat
, keyc
,
1235 params
->himc
, params
->done
);
1240 /***********************************************************************
1241 * ActivateKeyboardLayout (MACDRV.@)
1243 BOOL
macdrv_ActivateKeyboardLayout(HKL hkl
, UINT flags
)
1246 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1247 struct layout
*layout
;
1249 TRACE("hkl %p flags %04x\n", hkl
, flags
);
1251 if (hkl
== thread_data
->active_keyboard_layout
)
1254 pthread_mutex_lock(&layout_list_mutex
);
1255 update_layout_list();
1257 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
1259 if (layout
->hkl
== hkl
)
1261 if (macdrv_select_input_source(layout
->input_source
))
1264 if (thread_data
->keyboard_layout_uchr
)
1265 CFRelease(thread_data
->keyboard_layout_uchr
);
1267 macdrv_get_input_source_info(&thread_data
->keyboard_layout_uchr
, &thread_data
->keyboard_type
,
1268 &thread_data
->iso_keyboard
, NULL
);
1269 thread_data
->active_keyboard_layout
= hkl
;
1270 thread_data
->dead_key_state
= 0;
1272 macdrv_compute_keyboard_layout(thread_data
);
1277 pthread_mutex_unlock(&layout_list_mutex
);
1283 /***********************************************************************
1286 void macdrv_Beep(void)
1292 /***********************************************************************
1293 * GetKeyNameText (MACDRV.@)
1295 INT
macdrv_GetKeyNameText(LONG lparam
, LPWSTR buffer
, INT size
)
1297 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1300 scan
= (lparam
>> 16) & 0x1FF;
1301 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2scan
); keyc
++)
1303 if (thread_data
->keyc2scan
[keyc
] == scan
)
1305 static const WCHAR dead
[] = {' ','d','e','a','d',0};
1306 const UCKeyboardLayout
*uchr
;
1307 UInt32 deadKeyState
= 0;
1313 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1314 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDisplay
, 0, thread_data
->keyboard_type
,
1315 0, &deadKeyState
, size
- 1, &len
, (UniChar
*)buffer
);
1316 if (status
!= noErr
)
1318 if (len
&& buffer
[0] > 32)
1321 vkey
= thread_data
->keyc2vkey
[keyc
];
1322 if (lparam
& (1 << 25))
1324 /* Caller doesn't care about distinctions between left and
1330 vkey
= VK_SHIFT
; break;
1333 vkey
= VK_CONTROL
; break;
1336 vkey
= VK_MENU
; break;
1340 if (scan
& 0x100) vkey
|= 0x100;
1342 for (i
= 0; i
< ARRAY_SIZE(vkey_names
); i
++)
1344 if (vkey_names
[i
].vkey
== vkey
)
1346 len
= min(strlen(vkey_names
[i
].name
) + 1, size
);
1347 ascii_to_unicode(buffer
, vkey_names
[i
].name
, len
);
1348 if (len
) buffer
[--len
] = 0;
1356 len
= sprintf(name
, "Key 0x%02x", vkey
);
1357 len
= min(len
+ 1, size
);
1358 ascii_to_unicode(buffer
, name
, len
);
1359 if (len
) buffer
[--len
] = 0;
1365 if (status
== noErr
&& deadKeyState
)
1367 lstrcpynW(buffer
+ len
, dead
, size
- len
);
1368 len
= wcslen(buffer
);
1371 TRACE("lparam 0x%08x -> %s\n", (unsigned int)lparam
, debugstr_w(buffer
));
1376 WARN("found no name for lparam 0x%08x\n", (unsigned int)lparam
);
1381 /***********************************************************************
1382 * GetKeyboardLayoutList (MACDRV.@)
1384 UINT
macdrv_GetKeyboardLayoutList(INT size
, HKL
*list
)
1387 struct layout
*layout
;
1389 TRACE("%d, %p\n", size
, list
);
1391 pthread_mutex_lock(&layout_list_mutex
);
1393 update_layout_list();
1395 LIST_FOR_EACH_ENTRY(layout
, &layout_list
, struct layout
, entry
)
1397 if (!layout
->enabled
) continue;
1400 if (count
>= size
) break;
1401 list
[count
] = layout
->hkl
;
1402 TRACE("\t%d: %p\n", count
, list
[count
]);
1406 pthread_mutex_unlock(&layout_list_mutex
);
1408 TRACE("returning %d\n", count
);
1413 /***********************************************************************
1414 * MapVirtualKeyEx (MACDRV.@)
1416 UINT
macdrv_MapVirtualKeyEx(UINT wCode
, UINT wMapType
, HKL hkl
)
1418 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1422 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode
, wMapType
, hkl
);
1426 case MAPVK_VK_TO_VSC
: /* vkey-code to scan-code */
1427 case MAPVK_VK_TO_VSC_EX
:
1430 case VK_SHIFT
: wCode
= VK_LSHIFT
; break;
1431 case VK_CONTROL
: wCode
= VK_LCONTROL
; break;
1432 case VK_MENU
: wCode
= VK_LMENU
; break;
1435 /* vkey -> keycode -> scan */
1436 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1438 if (thread_data
->keyc2vkey
[keyc
] == wCode
)
1440 ret
= thread_data
->keyc2scan
[keyc
] & 0xFF;
1445 /* set scan code prefix */
1446 if (wMapType
== MAPVK_VK_TO_VSC_EX
&&
1447 (wCode
== VK_RCONTROL
|| wCode
== VK_RMENU
))
1451 case MAPVK_VSC_TO_VK
: /* scan-code to vkey-code */
1452 case MAPVK_VSC_TO_VK_EX
:
1453 /* scan -> keycode -> vkey */
1454 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1455 if ((thread_data
->keyc2scan
[keyc
] & 0xFF) == (wCode
& 0xFF))
1457 ret
= thread_data
->keyc2vkey
[keyc
];
1458 /* Only stop if it's not a numpad vkey; otherwise keep
1459 looking for a potential better vkey. */
1460 if (ret
&& (ret
< VK_NUMPAD0
|| VK_DIVIDE
< ret
))
1464 if (wMapType
== MAPVK_VSC_TO_VK
)
1469 ret
= VK_SHIFT
; break;
1472 ret
= VK_CONTROL
; break;
1475 ret
= VK_MENU
; break;
1480 case MAPVK_VK_TO_CHAR
: /* vkey-code to character */
1482 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1483 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1484 const UCKeyboardLayout
*uchr
;
1487 UInt32 deadKeyState
;
1489 BOOL deadKey
= FALSE
;
1491 if ((VK_PRIOR
<= wCode
&& wCode
<= VK_HELP
) ||
1492 (VK_F1
<= wCode
&& wCode
<= VK_F24
))
1495 if (!thread_data
|| !thread_data
->keyboard_layout_uchr
)
1497 WARN("No keyboard layout uchr data\n");
1501 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1503 /* Find the Mac keycode corresponding to the vkey */
1504 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1505 if (thread_data
->keyc2vkey
[keyc
] == wCode
) break;
1507 if (keyc
>= ARRAY_SIZE(thread_data
->keyc2vkey
))
1509 WARN("Unknown virtual key %X\n", wCode
);
1513 TRACE("Found keycode %u\n", keyc
);
1516 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1517 thread_data
->keyboard_type
, 0, &deadKeyState
, ARRAY_SIZE(s
), &len
, s
);
1518 if (status
== noErr
&& !len
&& deadKeyState
)
1522 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, 0,
1523 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1524 &deadKeyState
, ARRAY_SIZE(s
), &len
, s
);
1527 if (status
== noErr
&& len
)
1528 ret
= RtlUpcaseUnicodeChar(s
[0]) | (deadKey
? 0x80000000 : 0);
1532 default: /* reserved */
1533 FIXME("Unknown wMapType %d\n", wMapType
);
1537 TRACE("returning 0x%04x\n", ret
);
1542 /***********************************************************************
1543 * RegisterHotKey (MACDRV.@)
1545 BOOL
macdrv_RegisterHotKey(HWND hwnd
, UINT mod_flags
, UINT vkey
)
1547 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1548 unsigned int keyc
, modifiers
= 0;
1551 TRACE_(key
)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd
, mod_flags
, vkey
);
1553 /* Find the Mac keycode corresponding to the vkey */
1554 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1555 if (thread_data
->keyc2vkey
[keyc
] == vkey
) break;
1557 if (keyc
>= ARRAY_SIZE(thread_data
->keyc2vkey
))
1559 WARN_(key
)("ignoring unknown virtual key 0x%04x\n", vkey
);
1563 if (mod_flags
& MOD_ALT
) modifiers
|= cmdKey
;
1564 if (mod_flags
& MOD_CONTROL
) modifiers
|= controlKey
;
1565 if (mod_flags
& MOD_SHIFT
) modifiers
|= shiftKey
;
1566 if (mod_flags
& MOD_WIN
)
1568 WARN_(key
)("MOD_WIN not supported; ignoring\n");
1572 ret
= macdrv_register_hot_key(thread_data
->queue
, vkey
, mod_flags
, keyc
, modifiers
);
1573 TRACE_(key
)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc
, modifiers
, ret
);
1575 if (ret
== MACDRV_HOTKEY_ALREADY_REGISTERED
)
1576 RtlSetLastWin32Error(ERROR_HOTKEY_ALREADY_REGISTERED
);
1577 else if (ret
!= MACDRV_HOTKEY_SUCCESS
)
1578 RtlSetLastWin32Error(ERROR_GEN_FAILURE
);
1580 return ret
== MACDRV_HOTKEY_SUCCESS
;
1584 /***********************************************************************
1585 * ToUnicodeEx (MACDRV.@)
1587 * The ToUnicode function translates the specified virtual-key code and keyboard
1588 * state to the corresponding Windows character or characters.
1590 * If the specified key is a dead key, the return value is negative. Otherwise,
1591 * it is one of the following values:
1593 * -1 The specified virtual key is a dead-key. If possible, the
1594 * non-combining form of the dead character is written to bufW.
1595 * 0 The specified virtual key has no translation for the current
1596 * state of the keyboard.
1597 * 1 One Windows character was copied to the buffer.
1598 * 2 or more Multiple characters were copied to the buffer. This usually
1599 * happens when a dead-key character (accent or diacritic) stored
1600 * in the keyboard layout cannot be composed with the specified
1601 * virtual key to form a single character.
1604 INT
macdrv_ToUnicodeEx(UINT virtKey
, UINT scanCode
, const BYTE
*lpKeyState
,
1605 LPWSTR bufW
, int bufW_size
, UINT flags
, HKL hkl
)
1607 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1610 BOOL is_menu
= (flags
& 0x1);
1612 const UCKeyboardLayout
*uchr
;
1614 UInt32 modifierKeyState
;
1616 UInt32 deadKeyState
, savedDeadKeyState
;
1620 TRACE_(key
)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1621 virtKey
, scanCode
, lpKeyState
, bufW
, bufW_size
, flags
, hkl
);
1626 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1627 modifier key press. We want it to effectively ignore modifier key
1628 presses. I think that one isn't supposed to call it at all for modifier
1629 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1630 are different event types than key up/down events. */
1646 /* There are a number of key combinations for which Windows does not
1647 produce characters, but Mac keyboard layouts may. Eat them. Do this
1648 here to avoid the expense of UCKeyTranslate() but also because these
1649 keys shouldn't terminate dead key sequences. */
1650 if ((VK_PRIOR
<= virtKey
&& virtKey
<= VK_HELP
) || (VK_F1
<= virtKey
&& virtKey
<= VK_F24
))
1653 /* Shift + <non-digit keypad keys>. */
1654 if ((lpKeyState
[VK_SHIFT
] & 0x80) && VK_MULTIPLY
<= virtKey
&& virtKey
<= VK_DIVIDE
)
1657 if (lpKeyState
[VK_CONTROL
] & 0x80)
1659 /* Control-Tab, with or without other modifiers. */
1660 if (virtKey
== VK_TAB
)
1663 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1665 if ((lpKeyState
[VK_SHIFT
] & 0x80) || (lpKeyState
[VK_MENU
] & 0x80))
1679 if (thread_data
->keyboard_layout_uchr
)
1680 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1684 keyAction
= (scanCode
& 0x8000) ? kUCKeyActionUp
: kUCKeyActionDown
;
1686 modifierKeyState
= 0;
1687 if (lpKeyState
[VK_SHIFT
] & 0x80)
1688 modifierKeyState
|= (shiftKey
>> 8);
1689 if (lpKeyState
[VK_CAPITAL
] & 0x01)
1690 modifierKeyState
|= (alphaLock
>> 8);
1691 if (lpKeyState
[VK_CONTROL
] & 0x80)
1692 modifierKeyState
|= (controlKey
>> 8);
1693 if (lpKeyState
[VK_MENU
] & 0x80)
1694 modifierKeyState
|= (cmdKey
>> 8);
1695 if (thread_data
->last_modifiers
& (NX_ALTERNATEMASK
| NX_DEVICELALTKEYMASK
| NX_DEVICERALTKEYMASK
))
1696 modifierKeyState
|= (optionKey
>> 8);
1698 /* Find the Mac keycode corresponding to the vkey */
1699 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1700 if (thread_data
->keyc2vkey
[keyc
] == virtKey
) break;
1702 if (keyc
>= ARRAY_SIZE(thread_data
->keyc2vkey
))
1704 WARN_(key
)("Unknown virtual key 0x%04x\n", virtKey
);
1708 TRACE_(key
)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc
,
1709 (keyAction
== kUCKeyActionDown
) ? "pressed" : "released", (unsigned)modifierKeyState
);
1713 if (keyAction
== kUCKeyActionUp
)
1716 options
= kUCKeyTranslateNoDeadKeysMask
;
1722 deadKeyState
= thread_data
->dead_key_state
;
1724 savedDeadKeyState
= deadKeyState
;
1725 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1726 thread_data
->keyboard_type
, options
, &deadKeyState
, bufW_size
,
1728 if (status
!= noErr
)
1730 ERR_(key
)("Couldn't translate keycode 0x%04x, status %d\n", keyc
, status
);
1735 if (keyAction
!= kUCKeyActionUp
&& len
> 0 && deadKeyState
== thread_data
->dead_key_state
)
1736 thread_data
->dead_key_state
= 0;
1738 thread_data
->dead_key_state
= deadKeyState
;
1740 if (keyAction
== kUCKeyActionUp
)
1744 if (len
== 0 && deadKeyState
)
1746 /* Repeat the translation, but disabling dead-key generation to
1747 learn which dead key it was. */
1748 status
= UCKeyTranslate(uchr
, keyc
, keyAction
, modifierKeyState
,
1749 thread_data
->keyboard_type
, kUCKeyTranslateNoDeadKeysMask
,
1750 &savedDeadKeyState
, bufW_size
, &len
, bufW
);
1751 if (status
!= noErr
)
1753 ERR_(key
)("Couldn't translate keycode 0x%04x, status %d\n", keyc
, status
);
1761 len
= strip_apple_private_chars(bufW
, len
);
1763 if (dead
&& len
> 0) ret
= -1;
1766 /* Control-Return produces line feed instead of carriage return. */
1767 if (ret
> 0 && (lpKeyState
[VK_CONTROL
] & 0x80) && virtKey
== VK_RETURN
)
1770 for (i
= 0; i
< len
; i
++)
1771 if (bufW
[i
] == '\r')
1776 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1777 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1778 if (1 <= ret
&& ret
< bufW_size
)
1781 TRACE_(key
)("returning %d / %s\n", ret
, debugstr_wn(bufW
, abs(ret
)));
1786 /***********************************************************************
1787 * UnregisterHotKey (MACDRV.@)
1789 void macdrv_UnregisterHotKey(HWND hwnd
, UINT modifiers
, UINT vkey
)
1791 struct macdrv_thread_data
*thread_data
= macdrv_thread_data();
1793 TRACE_(key
)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd
, modifiers
, vkey
);
1796 macdrv_unregister_hot_key(thread_data
->queue
, vkey
, modifiers
);
1800 /***********************************************************************
1801 * VkKeyScanEx (MACDRV.@)
1803 * Note: Windows ignores HKL parameter and uses current active layout instead
1805 SHORT
macdrv_VkKeyScanEx(WCHAR wChar
, HKL hkl
)
1807 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
1810 const UCKeyboardLayout
*uchr
;
1812 TRACE("%04x, %p\n", wChar
, hkl
);
1814 uchr
= (const UCKeyboardLayout
*)CFDataGetBytePtr(thread_data
->keyboard_layout_uchr
);
1817 TRACE("no keyboard layout UCHR data; returning -1\n");
1821 for (state
= 0; state
< 8; state
++)
1823 UInt32 modifierKeyState
= 0;
1827 modifierKeyState
|= (shiftKey
>> 8);
1828 if ((state
& 6) == 6)
1829 modifierKeyState
|= (optionKey
>> 8);
1833 modifierKeyState
|= (controlKey
>> 8);
1835 modifierKeyState
|= (cmdKey
>> 8);
1838 for (keyc
= 0; keyc
< ARRAY_SIZE(thread_data
->keyc2vkey
); keyc
++)
1840 UInt32 deadKeyState
= 0;
1845 if (!thread_data
->keyc2vkey
[keyc
]) continue;
1847 status
= UCKeyTranslate(uchr
, keyc
, kUCKeyActionDown
, modifierKeyState
,
1848 thread_data
->keyboard_type
, 0, &deadKeyState
,
1850 if (status
== noErr
&& len
== 1 && uchar
== wChar
)
1852 WORD vkey
= thread_data
->keyc2vkey
[keyc
];
1854 ret
= vkey
| (state
<< 8);
1855 if ((VK_NUMPAD0
<= vkey
&& vkey
<= VK_DIVIDE
) ||
1856 keyc
== kVK_ANSI_KeypadClear
|| keyc
== kVK_ANSI_KeypadEnter
||
1857 keyc
== kVK_ANSI_KeypadEquals
)
1859 /* Keep searching for a non-numpad match, which is preferred. */
1868 TRACE(" -> 0x%04x\n", ret
);