dwrite: Check for allocation failures of glyph buffers.
[wine.git] / dlls / winemac.drv / keyboard.c
blobf4b955cd46a9ec3d5182527c1ba0e3ae4f2785e5
1 /*
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
27 #include "config.h"
29 #include "macdrv.h"
30 #include "winuser.h"
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>. */
39 enum {
40 cmdKeyBit = 8,
41 shiftKeyBit = 9,
42 alphaLockBit = 10,
43 optionKeyBit = 11,
44 controlKeyBit = 12,
47 enum {
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>. */
57 enum {
58 kVK_ANSI_A = 0x00,
59 kVK_ANSI_S = 0x01,
60 kVK_ANSI_D = 0x02,
61 kVK_ANSI_F = 0x03,
62 kVK_ANSI_H = 0x04,
63 kVK_ANSI_G = 0x05,
64 kVK_ANSI_Z = 0x06,
65 kVK_ANSI_X = 0x07,
66 kVK_ANSI_C = 0x08,
67 kVK_ANSI_V = 0x09,
68 kVK_ISO_Section = 0x0A,
69 kVK_ANSI_B = 0x0B,
70 kVK_ANSI_Q = 0x0C,
71 kVK_ANSI_W = 0x0D,
72 kVK_ANSI_E = 0x0E,
73 kVK_ANSI_R = 0x0F,
74 kVK_ANSI_Y = 0x10,
75 kVK_ANSI_T = 0x11,
76 kVK_ANSI_1 = 0x12,
77 kVK_ANSI_2 = 0x13,
78 kVK_ANSI_3 = 0x14,
79 kVK_ANSI_4 = 0x15,
80 kVK_ANSI_6 = 0x16,
81 kVK_ANSI_5 = 0x17,
82 kVK_ANSI_Equal = 0x18,
83 kVK_ANSI_9 = 0x19,
84 kVK_ANSI_7 = 0x1A,
85 kVK_ANSI_Minus = 0x1B,
86 kVK_ANSI_8 = 0x1C,
87 kVK_ANSI_0 = 0x1D,
88 kVK_ANSI_RightBracket = 0x1E,
89 kVK_ANSI_O = 0x1F,
90 kVK_ANSI_U = 0x20,
91 kVK_ANSI_LeftBracket = 0x21,
92 kVK_ANSI_I = 0x22,
93 kVK_ANSI_P = 0x23,
94 kVK_Return = 0x24,
95 kVK_ANSI_L = 0x25,
96 kVK_ANSI_J = 0x26,
97 kVK_ANSI_Quote = 0x27,
98 kVK_ANSI_K = 0x28,
99 kVK_ANSI_Semicolon = 0x29,
100 kVK_ANSI_Backslash = 0x2A,
101 kVK_ANSI_Comma = 0x2B,
102 kVK_ANSI_Slash = 0x2C,
103 kVK_ANSI_N = 0x2D,
104 kVK_ANSI_M = 0x2E,
105 kVK_ANSI_Period = 0x2F,
106 kVK_Tab = 0x30,
107 kVK_Space = 0x31,
108 kVK_ANSI_Grave = 0x32,
109 kVK_Delete = 0x33,
110 kVK_Escape = 0x35,
111 kVK_RightCommand = 0x36, /* invented for Wine; co-opt unused key code */
112 kVK_Command = 0x37,
113 kVK_Shift = 0x38,
114 kVK_CapsLock = 0x39,
115 kVK_Option = 0x3A,
116 kVK_Control = 0x3B,
117 kVK_RightShift = 0x3C,
118 kVK_RightOption = 0x3D,
119 kVK_RightControl = 0x3E,
120 kVK_Function = 0x3F,
121 kVK_F17 = 0x40,
122 kVK_ANSI_KeypadDecimal = 0x41,
123 kVK_ANSI_KeypadMultiply = 0x43,
124 kVK_ANSI_KeypadPlus = 0x45,
125 kVK_ANSI_KeypadClear = 0x47,
126 kVK_VolumeUp = 0x48,
127 kVK_VolumeDown = 0x49,
128 kVK_Mute = 0x4A,
129 kVK_ANSI_KeypadDivide = 0x4B,
130 kVK_ANSI_KeypadEnter = 0x4C,
131 kVK_ANSI_KeypadMinus = 0x4E,
132 kVK_F18 = 0x4F,
133 kVK_F19 = 0x50,
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,
143 kVK_F20 = 0x5A,
144 kVK_ANSI_Keypad8 = 0x5B,
145 kVK_ANSI_Keypad9 = 0x5C,
146 kVK_JIS_Yen = 0x5D,
147 kVK_JIS_Underscore = 0x5E,
148 kVK_JIS_KeypadComma = 0x5F,
149 kVK_F5 = 0x60,
150 kVK_F6 = 0x61,
151 kVK_F7 = 0x62,
152 kVK_F3 = 0x63,
153 kVK_F8 = 0x64,
154 kVK_F9 = 0x65,
155 kVK_JIS_Eisu = 0x66,
156 kVK_F11 = 0x67,
157 kVK_JIS_Kana = 0x68,
158 kVK_F13 = 0x69,
159 kVK_F16 = 0x6A,
160 kVK_F14 = 0x6B,
161 kVK_F10 = 0x6D,
162 kVK_F12 = 0x6F,
163 kVK_F15 = 0x71,
164 kVK_Help = 0x72,
165 kVK_Home = 0x73,
166 kVK_PageUp = 0x74,
167 kVK_ForwardDelete = 0x75,
168 kVK_F4 = 0x76,
169 kVK_End = 0x77,
170 kVK_F2 = 0x78,
171 kVK_PageDown = 0x79,
172 kVK_F1 = 0x7A,
173 kVK_LeftArrow = 0x7B,
174 kVK_RightArrow = 0x7C,
175 kVK_DownArrow = 0x7D,
176 kVK_UpArrow = 0x7E,
179 extern const CFStringRef kTISTypeKeyboardLayout;
181 /* Indexed by Mac virtual keycode values defined above. */
182 static const struct {
183 WORD vkey;
184 WORD scan;
185 BOOL fixed;
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 {
318 DWORD vkey;
319 const char *name;
320 } vkey_names[] = {
321 { VK_ADD, "Num +" },
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" },
331 { VK_F1, "F1" },
332 { VK_F2, "F2" },
333 { VK_F3, "F3" },
334 { VK_F4, "F4" },
335 { VK_F5, "F5" },
336 { VK_F6, "F6" },
337 { VK_F7, "F7" },
338 { VK_F8, "F8" },
339 { VK_F9, "F9" },
340 { VK_F10, "F10" },
341 { VK_F11, "F11" },
342 { VK_F12, "F12" },
343 { VK_F13, "F13" },
344 { VK_F14, "F14" },
345 { VK_F15, "F15" },
346 { VK_F16, "F16" },
347 { VK_F17, "F17" },
348 { VK_F18, "F18" },
349 { VK_F19, "F19" },
350 { VK_F20, "F20" },
351 { VK_F21, "F21" },
352 { VK_F22, "F22" },
353 { VK_F23, "F23" },
354 { VK_F24, "F24" },
355 { VK_HELP | 0x100, "Help" },
356 { VK_HOME | 0x100, "Home" },
357 { VK_INSERT | 0x100, "Insert" },
358 { VK_LCONTROL, "Ctrl" },
359 { VK_LEFT | 0x100, "Left" },
360 { VK_LMENU, "Alt" },
361 { VK_LSHIFT, "Shift" },
362 { VK_LWIN | 0x100, "Win" },
363 { VK_MENU, "Alt" },
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 -" },
391 { VK_TAB, "Tab" },
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 static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
400 BOOL ret;
401 CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
402 CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, strlenW(string), kCFAllocatorNull);
403 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
404 if (ignore_diacritics)
405 flags |= kCFCompareDiacriticInsensitive;
406 ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
407 CFRelease(s1);
408 CFRelease(s2);
409 return ret;
413 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
414 * string. Returns the length of the string after stripping. */
415 static int strip_apple_private_chars(LPWSTR bufW, int len)
417 int i;
418 for (i = 0; i < len; )
420 if (0xF700 <= bufW[i] && bufW[i] <= 0xF8FF)
422 memmove(&bufW[i], &bufW[i+1], (len - i - 1) * sizeof(bufW[0]));
423 len--;
425 else
426 i++;
428 return len;
431 static struct list layout_list = LIST_INIT( layout_list );
432 struct layout
434 struct list entry;
435 HKL hkl;
436 TISInputSourceRef input_source;
437 BOOL enabled; /* is the input source enabled - ie displayed in the input source selector UI */
440 static CRITICAL_SECTION layout_list_section;
441 static CRITICAL_SECTION_DEBUG critsect_debug =
443 0, 0, &layout_list_section,
444 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
445 0, 0, { (DWORD_PTR)(__FILE__ ": layout_list_section") }
447 static CRITICAL_SECTION layout_list_section = { &critsect_debug, -1, 0, 0, 0, 0 };
449 int macdrv_layout_list_needs_update = TRUE;
451 static DWORD get_lcid(CFStringRef lang)
453 CFRange range;
454 WCHAR str[10];
456 range.location = 0;
457 range.length = min(CFStringGetLength(lang), ARRAY_SIZE(str) - 1);
458 CFStringGetCharacters(lang, range, str);
459 str[range.length] = 0;
460 return LocaleNameToLCID(str, 0);
463 static HKL get_hkl(CFStringRef lang, CFStringRef type)
465 ULONG_PTR lcid = get_lcid(lang);
466 struct layout *layout;
468 /* Look for the last occurrence of this lcid in the list and if
469 present use that value + 0x10000 */
470 LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry)
472 ULONG_PTR hkl = HandleToUlong(layout->hkl);
474 if (LOWORD(hkl) == lcid)
476 lcid = (hkl & ~0xe0000000) + 0x10000;
477 break;
481 if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000;
483 return (HKL)lcid;
486 /******************************************************************
487 * get_layout_from_source
489 * Must be called while holding the layout_list_section.
490 * Note, returned layout may not currently be enabled.
492 static struct layout *get_layout_from_source(TISInputSourceRef input)
494 struct layout *ret = NULL, *layout;
496 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
498 if (CFEqual(input, layout->input_source))
500 ret = layout;
501 break;
504 return ret;
507 /***********************************************************************
508 * update_layout_list
510 * Must be called while holding the layout_list_section
512 * If an input source has been disabled (ie. removed from the UI) its
513 * entry remains in the layout list but is marked as such and is not
514 * enumerated by GetKeyboardLayoutList. This is to ensure the
515 * HKL <-> input source mapping is unique.
517 static void update_layout_list(void)
519 CFArrayRef sources;
520 struct layout *layout;
521 int i;
523 if (!InterlockedExchange(&macdrv_layout_list_needs_update, FALSE)) return;
525 sources = macdrv_create_input_source_list();
527 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
528 layout->enabled = FALSE;
530 for (i = 0; i < CFArrayGetCount(sources); i++)
532 CFDictionaryRef dict = CFArrayGetValueAtIndex(sources, i);
533 TISInputSourceRef input = (TISInputSourceRef)CFDictionaryGetValue(dict, macdrv_input_source_input_key);
534 layout = get_layout_from_source(input);
535 if (!layout)
537 CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key);
538 CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key);
540 layout = HeapAlloc(GetProcessHeap(), 0, sizeof(*layout));
541 layout->input_source = (TISInputSourceRef)CFRetain(input);
542 layout->hkl = get_hkl(lang, type);
544 list_add_tail(&layout_list, &layout->entry);
545 TRACE("adding new layout %p\n", layout->hkl);
547 else
548 TRACE("enabling already existing layout %p\n", layout->hkl);
550 layout->enabled = TRUE;
553 CFRelease(sources);
556 /***********************************************************************
557 * macdrv_get_hkl_from_source
559 * Find the HKL associated with a given input source.
561 HKL macdrv_get_hkl_from_source(TISInputSourceRef input)
563 struct layout *layout;
564 HKL ret = 0;
566 EnterCriticalSection(&layout_list_section);
568 update_layout_list();
569 layout = get_layout_from_source(input);
570 if (layout) ret = layout->hkl;
572 LeaveCriticalSection(&layout_list_section);
574 return ret;
578 /***********************************************************************
579 * macdrv_compute_keyboard_layout
581 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
583 int keyc;
584 WCHAR vkey;
585 const UCKeyboardLayout *uchr;
586 const UInt32 modifier_combos[] = {
588 shiftKey >> 8,
589 cmdKey >> 8,
590 (shiftKey | cmdKey) >> 8,
591 optionKey >> 8,
592 (shiftKey | optionKey) >> 8,
594 UniChar map[128][ARRAY_SIZE(modifier_combos)][4 + 1];
595 int combo;
596 BYTE vkey_used[256];
597 int ignore_diacritics;
598 static const struct {
599 WCHAR wchar;
600 DWORD vkey;
601 } symbol_vkeys[] = {
602 { '-', VK_OEM_MINUS },
603 { '+', VK_OEM_PLUS },
604 { '_', VK_OEM_MINUS },
605 { ',', VK_OEM_COMMA },
606 { '.', VK_OEM_PERIOD },
607 { '=', VK_OEM_PLUS },
608 { '>', VK_OEM_PERIOD },
609 { '<', VK_OEM_COMMA },
610 { '|', VK_OEM_5 },
611 { '\\', VK_OEM_5 },
612 { '`', VK_OEM_3 },
613 { '[', VK_OEM_4 },
614 { '~', VK_OEM_3 },
615 { '?', VK_OEM_2 },
616 { ']', VK_OEM_6 },
617 { '/', VK_OEM_2 },
618 { ':', VK_OEM_1 },
619 { '}', VK_OEM_6 },
620 { '{', VK_OEM_4 },
621 { ';', VK_OEM_1 },
622 { '\'', VK_OEM_7 },
623 { ':', VK_OEM_PERIOD },
624 { ';', VK_OEM_COMMA },
625 { '"', VK_OEM_7 },
626 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
627 { '\'', VK_OEM_2 },
628 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
629 { '*', VK_OEM_PLUS },
630 { 0x00B4, VK_OEM_7 },
631 { '`', VK_OEM_4 },
632 { '[', VK_OEM_6 },
633 { '/', VK_OEM_5 },
634 { '^', VK_OEM_6 },
635 { '*', VK_OEM_2 },
636 { '{', VK_OEM_6 },
637 { '~', VK_OEM_1 },
638 { '?', VK_OEM_PLUS },
639 { '?', VK_OEM_4 },
640 { 0x00B4, VK_OEM_3 },
641 { '?', VK_OEM_COMMA },
642 { '~', VK_OEM_PLUS },
643 { ']', VK_OEM_4 },
644 { '\'', VK_OEM_3 },
645 { 0x00A7, VK_OEM_7 },
647 int i;
649 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
650 contiguous ranges. */
651 static const struct {
652 WORD first, last;
653 } vkey_ranges[] = {
654 { 'A', 'Z' },
655 { '0', '9' },
656 { VK_OEM_1, VK_OEM_3 },
657 { VK_OEM_4, VK_ICO_CLEAR },
658 { 0xe9, 0xf5 },
659 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
660 { VK_F1, VK_F24 },
661 { 0, 0 }
663 int vkey_range;
665 if (!thread_data->keyboard_layout_uchr)
667 ERR("no keyboard layout UCHR data\n");
668 return;
671 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
672 memset(vkey_used, 0, sizeof(vkey_used));
674 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
676 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
677 if (default_map[keyc].fixed)
679 vkey = default_map[keyc].vkey;
680 thread_data->keyc2vkey[keyc] = vkey;
681 vkey_used[vkey] = 1;
682 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
686 if (thread_data->iso_keyboard)
688 /* In almost all cases, the Mac key codes indicate a physical key position
689 and this corresponds nicely to Win32 scan codes. However, the Mac key
690 codes differ in one case between ANSI and ISO keyboards. For ANSI
691 keyboards, the key to the left of the digits and above the Tab key
692 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
693 some position produces kVK_ISO_Section. The additional key on ISO
694 keyboards, the one to the right of the left Shift key, produces
695 kVK_ANSI_Grave, which is just weird.
697 Since we want the key in that upper left corner to always produce the
698 same scan code (0x29), we need to swap the scan codes of those two
699 Mac key codes for ISO keyboards. */
700 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
701 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
702 thread_data->keyc2scan[kVK_ISO_Section] = temp;
705 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
707 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
708 memset(map, 0, sizeof(map));
709 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
711 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
712 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
714 TRACE("keyc 0x%04x: ", keyc);
716 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
718 UInt32 deadKeyState;
719 UniCharCount len;
720 OSStatus status;
722 deadKeyState = 0;
723 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
724 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
725 &deadKeyState, ARRAY_SIZE(map[keyc][combo]) - 1, &len, map[keyc][combo]);
726 if (status != noErr)
727 map[keyc][combo][0] = 0;
729 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
732 TRACE("\n");
735 /* First try to match key codes to the vkeys for the letters A through Z.
736 Try unmodified first, then with various modifier combinations in succession.
737 On the first pass, try to get a match lacking diacritical marks. On the
738 second pass, accept matches with diacritical marks. */
739 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
741 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
743 for (vkey = 'A'; vkey <= 'Z'; vkey++)
745 if (vkey_used[vkey])
746 continue;
748 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
750 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
751 continue;
753 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
755 thread_data->keyc2vkey[keyc] = vkey;
756 vkey_used[vkey] = 1;
757 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
758 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
759 break;
766 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
767 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
769 for (vkey = '0'; vkey <= '9'; vkey++)
771 if (vkey_used[vkey])
772 continue;
774 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
776 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
777 continue;
779 if (char_matches_string(vkey, map[keyc][combo], FALSE))
781 thread_data->keyc2vkey[keyc] = vkey;
782 vkey_used[vkey] = 1;
783 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
784 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
785 break;
791 /* Now try to match key codes for certain common punctuation characters to
792 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
793 for (i = 0; i < ARRAY_SIZE(symbol_vkeys); i++)
795 vkey = symbol_vkeys[i].vkey;
797 if (vkey_used[vkey])
798 continue;
800 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
802 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
804 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
805 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
806 continue;
808 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
810 thread_data->keyc2vkey[keyc] = vkey;
811 vkey_used[vkey] = 1;
812 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
813 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
814 break;
818 if (vkey_used[vkey])
819 break;
823 /* For those key codes still without a vkey, try to use the default vkey
824 from the default map, if it's still available. */
825 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
827 DWORD vkey = default_map[keyc].vkey;
829 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
830 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
832 if (!vkey_used[vkey])
834 thread_data->keyc2vkey[keyc] = vkey;
835 vkey_used[vkey] = 1;
836 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
840 /* For any unassigned key codes which would map to a letter in the default
841 map, but whose normal letter vkey wasn't available, try to find a
842 different letter. */
843 vkey = 'A';
844 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
846 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
847 continue; /* not a letter in ANSI layout */
848 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
849 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
851 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
852 if (vkey <= 'Z')
854 thread_data->keyc2vkey[keyc] = vkey;
855 vkey_used[vkey] = 1;
856 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
858 else
859 break; /* no more unused letter vkeys, so stop trying */
862 /* Same thing but with the digits. */
863 vkey = '0';
864 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
866 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
867 continue; /* not a digit in ANSI layout */
868 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
869 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
871 while (vkey <= '9' && vkey_used[vkey]) vkey++;
872 if (vkey <= '9')
874 thread_data->keyc2vkey[keyc] = vkey;
875 vkey_used[vkey] = 1;
876 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
878 else
879 break; /* no more unused digit vkeys, so stop trying */
882 /* Last chance. Assign any available vkey. */
883 vkey_range = 0;
884 vkey = vkey_ranges[vkey_range].first;
885 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
887 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
888 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
890 while (vkey && vkey_used[vkey])
892 if (vkey == vkey_ranges[vkey_range].last)
894 vkey_range++;
895 vkey = vkey_ranges[vkey_range].first;
897 else
898 vkey++;
901 if (!vkey)
903 WARN("No more vkeys available!\n");
904 break;
907 thread_data->keyc2vkey[keyc] = vkey;
908 vkey_used[vkey] = 1;
909 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
914 /***********************************************************************
915 * macdrv_send_keyboard_input
917 static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD flags, DWORD time)
919 INPUT input;
921 TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
923 input.type = INPUT_KEYBOARD;
924 input.ki.wVk = vkey;
925 input.ki.wScan = scan;
926 input.ki.dwFlags = flags;
927 input.ki.time = time;
928 input.ki.dwExtraInfo = 0;
930 __wine_send_input(hwnd, &input, NULL);
934 /***********************************************************************
935 * get_async_key_state
937 static BOOL get_async_key_state(BYTE state[256])
939 BOOL ret;
941 SERVER_START_REQ(get_key_state)
943 req->async = 1;
944 req->key = -1;
945 wine_server_set_reply(req, state, 256);
946 ret = !wine_server_call(req);
948 SERVER_END_REQ;
949 return ret;
953 /***********************************************************************
954 * update_modifier_state
956 static void update_modifier_state(unsigned int modifier, unsigned int modifiers, const BYTE *keystate,
957 WORD vkey, WORD alt_vkey, WORD scan, WORD alt_scan,
958 DWORD event_time, BOOL restore)
960 int key_pressed = (modifiers & modifier) != 0;
961 int vkey_pressed = (keystate[vkey] & 0x80) || (keystate[alt_vkey] & 0x80);
962 DWORD flags;
964 if (key_pressed != vkey_pressed)
966 if (key_pressed)
968 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
969 if (restore)
970 flags |= KEYEVENTF_KEYUP;
972 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff, flags, event_time);
974 else
976 flags = restore ? 0 : KEYEVENTF_KEYUP;
978 if (keystate[vkey] & 0x80)
980 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff,
981 flags | ((scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
982 event_time);
984 if (keystate[alt_vkey] & 0x80)
986 macdrv_send_keyboard_input(NULL, alt_vkey, alt_scan & 0xff,
987 flags | ((alt_scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
988 event_time);
995 /***********************************************************************
996 * macdrv_key_event
998 * Handler for KEY_PRESS and KEY_RELEASE events.
1000 void macdrv_key_event(HWND hwnd, const macdrv_event *event)
1002 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1003 WORD vkey, scan;
1004 DWORD flags;
1006 TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1007 hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
1008 event->key.keycode, event->key.modifiers);
1010 thread_data->last_modifiers = event->key.modifiers;
1012 if (event->key.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1014 vkey = thread_data->keyc2vkey[event->key.keycode];
1015 scan = thread_data->keyc2scan[event->key.keycode];
1017 else
1018 vkey = scan = 0;
1020 TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1021 event->key.keycode, vkey, scan);
1023 if (!vkey) return;
1025 flags = 0;
1026 if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
1027 if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
1029 macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
1033 /***********************************************************************
1034 * macdrv_keyboard_changed
1036 * Handler for KEYBOARD_CHANGED events.
1038 void macdrv_keyboard_changed(const macdrv_event *event)
1040 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1042 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
1043 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
1045 if (thread_data->keyboard_layout_uchr)
1046 CFRelease(thread_data->keyboard_layout_uchr);
1047 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
1048 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
1049 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
1050 thread_data->active_keyboard_layout = macdrv_get_hkl_from_source(event->keyboard_changed.input_source);
1051 thread_data->dead_key_state = 0;
1053 macdrv_compute_keyboard_layout(thread_data);
1055 ActivateKeyboardLayout(thread_data->active_keyboard_layout, 0);
1057 SendMessageW(GetActiveWindow(), WM_CANCELMODE, 0, 0);
1061 /***********************************************************************
1062 * macdrv_hotkey_press
1064 * Handler for HOTKEY_PRESS events.
1066 void macdrv_hotkey_press(const macdrv_event *event)
1068 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1070 TRACE_(key)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
1071 event->hotkey_press.vkey, event->hotkey_press.mod_flags, event->hotkey_press.keycode,
1072 event->hotkey_press.time_ms);
1074 if (event->hotkey_press.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1076 WORD scan = thread_data->keyc2scan[event->hotkey_press.keycode];
1077 BYTE keystate[256];
1078 BOOL got_keystate;
1079 DWORD flags;
1081 if ((got_keystate = get_async_key_state(keystate)))
1083 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1084 0x38, 0x138, event->hotkey_press.time_ms, FALSE);
1085 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1086 0x1D, 0x11D, event->hotkey_press.time_ms, FALSE);
1087 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1088 0x2A, 0x36, event->hotkey_press.time_ms, FALSE);
1089 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1090 0x15B, 0x15C, event->hotkey_press.time_ms, FALSE);
1093 activate_on_following_focus();
1095 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
1096 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1097 flags, event->key.time_ms);
1098 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1099 flags | KEYEVENTF_KEYUP, event->key.time_ms);
1101 if (got_keystate)
1103 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1104 0x38, 0x138, event->hotkey_press.time_ms, TRUE);
1105 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1106 0x1D, 0x11D, event->hotkey_press.time_ms, TRUE);
1107 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1108 0x2A, 0x36, event->hotkey_press.time_ms, TRUE);
1109 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1110 0x15B, 0x15C, event->hotkey_press.time_ms, TRUE);
1116 /***********************************************************************
1117 * macdrv_process_text_input
1119 void macdrv_process_text_input(UINT vkey, UINT scan, UINT repeat, const BYTE *key_state, void *himc, int* done)
1121 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1122 unsigned int flags;
1123 int keyc;
1125 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", vkey, scan, repeat, himc);
1127 flags = thread_data->last_modifiers;
1128 if (key_state[VK_SHIFT] & 0x80)
1129 flags |= NX_SHIFTMASK;
1130 else
1131 flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK);
1132 if (key_state[VK_CAPITAL] & 0x01)
1133 flags |= NX_ALPHASHIFTMASK;
1134 else
1135 flags &= ~NX_ALPHASHIFTMASK;
1136 if (key_state[VK_CONTROL] & 0x80)
1137 flags |= NX_CONTROLMASK;
1138 else
1139 flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK);
1140 if (key_state[VK_MENU] & 0x80)
1141 flags |= NX_COMMANDMASK;
1142 else
1143 flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
1145 /* Find the Mac keycode corresponding to the scan code */
1146 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1147 if (thread_data->keyc2vkey[keyc] == vkey) break;
1149 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1151 *done = -1;
1152 return;
1155 TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc);
1157 macdrv_send_text_input_event(((scan & 0x8000) == 0), flags, repeat, keyc, himc, done);
1161 /***********************************************************************
1162 * ActivateKeyboardLayout (MACDRV.@)
1164 BOOL CDECL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
1166 BOOL ret = FALSE;
1167 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1168 struct layout *layout;
1170 TRACE("hkl %p flags %04x\n", hkl, flags);
1172 if (hkl == thread_data->active_keyboard_layout)
1173 return TRUE;
1175 EnterCriticalSection(&layout_list_section);
1176 update_layout_list();
1178 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1180 if (layout->hkl == hkl)
1182 if (macdrv_select_input_source(layout->input_source))
1184 ret = TRUE;
1185 if (thread_data->keyboard_layout_uchr)
1186 CFRelease(thread_data->keyboard_layout_uchr);
1188 macdrv_get_input_source_info(&thread_data->keyboard_layout_uchr, &thread_data->keyboard_type,
1189 &thread_data->iso_keyboard, NULL);
1190 thread_data->active_keyboard_layout = hkl;
1191 thread_data->dead_key_state = 0;
1193 macdrv_compute_keyboard_layout(thread_data);
1195 break;
1198 LeaveCriticalSection(&layout_list_section);
1200 return ret;
1204 /***********************************************************************
1205 * Beep (MACDRV.@)
1207 void CDECL macdrv_Beep(void)
1209 macdrv_beep();
1213 /***********************************************************************
1214 * GetKeyNameText (MACDRV.@)
1216 INT CDECL macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
1218 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1219 int scan, keyc;
1221 scan = (lparam >> 16) & 0x1FF;
1222 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2scan); keyc++)
1224 if (thread_data->keyc2scan[keyc] == scan)
1226 static const WCHAR dead[] = {' ','d','e','a','d',0};
1227 const UCKeyboardLayout *uchr;
1228 UInt32 deadKeyState = 0;
1229 UniCharCount len;
1230 OSStatus status;
1231 DWORD vkey;
1232 int i;
1234 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1235 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
1236 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
1237 if (status != noErr)
1238 len = 0;
1239 if (len && buffer[0] > 32)
1240 buffer[len] = 0;
1242 vkey = thread_data->keyc2vkey[keyc];
1243 if (lparam & (1 << 25))
1245 /* Caller doesn't care about distinctions between left and
1246 right keys. */
1247 switch (vkey)
1249 case VK_LSHIFT:
1250 case VK_RSHIFT:
1251 vkey = VK_SHIFT; break;
1252 case VK_LCONTROL:
1253 case VK_RCONTROL:
1254 vkey = VK_CONTROL; break;
1255 case VK_LMENU:
1256 case VK_RMENU:
1257 vkey = VK_MENU; break;
1261 if (scan & 0x100) vkey |= 0x100;
1263 for (i = 0; i < ARRAY_SIZE(vkey_names); i++)
1265 if (vkey_names[i].vkey == vkey)
1267 len = MultiByteToWideChar(CP_UTF8, 0, vkey_names[i].name, -1, buffer, size);
1268 if (len) len--;
1269 break;
1273 if (!len)
1275 static const WCHAR format[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1276 snprintfW(buffer, size, format, vkey);
1277 len = strlenW(buffer);
1280 if (!len)
1281 break;
1283 if (status == noErr && deadKeyState)
1285 lstrcpynW(buffer + len, dead, size - len);
1286 len = strlenW(buffer);
1289 TRACE("lparam 0x%08x -> %s\n", lparam, debugstr_w(buffer));
1290 return len;
1294 WARN("found no name for lparam 0x%08x\n", lparam);
1295 return 0;
1299 /***********************************************************************
1300 * GetKeyboardLayoutList (MACDRV.@)
1302 UINT CDECL macdrv_GetKeyboardLayoutList(INT size, HKL *list)
1304 int count = 0;
1305 struct layout *layout;
1307 TRACE("%d, %p\n", size, list);
1309 EnterCriticalSection(&layout_list_section);
1311 update_layout_list();
1313 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1315 if (!layout->enabled) continue;
1316 if (list)
1318 if (count >= size) break;
1319 list[count] = layout->hkl;
1320 TRACE("\t%d: %p\n", count, list[count]);
1322 count++;
1324 LeaveCriticalSection(&layout_list_section);
1326 TRACE("returning %d\n", count);
1327 return count;
1331 /***********************************************************************
1332 * MapVirtualKeyEx (MACDRV.@)
1334 UINT CDECL macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1336 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1337 UINT ret = 0;
1338 int keyc;
1340 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1342 switch (wMapType)
1344 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1345 case MAPVK_VK_TO_VSC_EX:
1346 switch (wCode)
1348 case VK_SHIFT: wCode = VK_LSHIFT; break;
1349 case VK_CONTROL: wCode = VK_LCONTROL; break;
1350 case VK_MENU: wCode = VK_LMENU; break;
1353 /* vkey -> keycode -> scan */
1354 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1356 if (thread_data->keyc2vkey[keyc] == wCode)
1358 ret = thread_data->keyc2scan[keyc] & 0xFF;
1359 break;
1363 /* set scan code prefix */
1364 if (wMapType == MAPVK_VK_TO_VSC_EX &&
1365 (wCode == VK_RCONTROL || wCode == VK_RMENU))
1366 ret |= 0xe000;
1367 break;
1369 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1370 case MAPVK_VSC_TO_VK_EX:
1371 /* scan -> keycode -> vkey */
1372 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1373 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1375 ret = thread_data->keyc2vkey[keyc];
1376 /* Only stop if it's not a numpad vkey; otherwise keep
1377 looking for a potential better vkey. */
1378 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1379 break;
1382 if (wMapType == MAPVK_VSC_TO_VK)
1383 switch (ret)
1385 case VK_LSHIFT:
1386 case VK_RSHIFT:
1387 ret = VK_SHIFT; break;
1388 case VK_LCONTROL:
1389 case VK_RCONTROL:
1390 ret = VK_CONTROL; break;
1391 case VK_LMENU:
1392 case VK_RMENU:
1393 ret = VK_MENU; break;
1396 break;
1398 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1400 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1401 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1402 const UCKeyboardLayout *uchr;
1403 UniChar s[10];
1404 OSStatus status;
1405 UInt32 deadKeyState;
1406 UniCharCount len;
1407 BOOL deadKey = FALSE;
1409 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1410 (VK_F1 <= wCode && wCode <= VK_F24))
1411 break;
1413 if (!thread_data || !thread_data->keyboard_layout_uchr)
1415 WARN("No keyboard layout uchr data\n");
1416 break;
1419 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1421 /* Find the Mac keycode corresponding to the vkey */
1422 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1423 if (thread_data->keyc2vkey[keyc] == wCode) break;
1425 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1427 WARN("Unknown virtual key %X\n", wCode);
1428 break;
1431 TRACE("Found keycode %u\n", keyc);
1433 deadKeyState = 0;
1434 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1435 thread_data->keyboard_type, 0, &deadKeyState, ARRAY_SIZE(s), &len, s);
1436 if (status == noErr && !len && deadKeyState)
1438 deadKey = TRUE;
1439 deadKeyState = 0;
1440 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1441 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1442 &deadKeyState, ARRAY_SIZE(s), &len, s);
1445 if (status == noErr && len)
1446 ret = toupperW(s[0]) | (deadKey ? 0x80000000 : 0);
1448 break;
1450 default: /* reserved */
1451 FIXME("Unknown wMapType %d\n", wMapType);
1452 break;
1455 TRACE("returning 0x%04x\n", ret);
1456 return ret;
1460 /***********************************************************************
1461 * RegisterHotKey (MACDRV.@)
1463 BOOL CDECL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey)
1465 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1466 unsigned int keyc, modifiers = 0;
1467 int ret;
1469 TRACE_(key)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd, mod_flags, vkey);
1471 /* Find the Mac keycode corresponding to the vkey */
1472 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1473 if (thread_data->keyc2vkey[keyc] == vkey) break;
1475 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1477 WARN_(key)("ignoring unknown virtual key 0x%04x\n", vkey);
1478 return TRUE;
1481 if (mod_flags & MOD_ALT) modifiers |= cmdKey;
1482 if (mod_flags & MOD_CONTROL) modifiers |= controlKey;
1483 if (mod_flags & MOD_SHIFT) modifiers |= shiftKey;
1484 if (mod_flags & MOD_WIN)
1486 WARN_(key)("MOD_WIN not supported; ignoring\n");
1487 return TRUE;
1490 ret = macdrv_register_hot_key(thread_data->queue, vkey, mod_flags, keyc, modifiers);
1491 TRACE_(key)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc, modifiers, ret);
1493 if (ret == MACDRV_HOTKEY_ALREADY_REGISTERED)
1494 SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
1495 else if (ret != MACDRV_HOTKEY_SUCCESS)
1496 SetLastError(ERROR_GEN_FAILURE);
1498 return ret == MACDRV_HOTKEY_SUCCESS;
1502 /***********************************************************************
1503 * ToUnicodeEx (MACDRV.@)
1505 * The ToUnicode function translates the specified virtual-key code and keyboard
1506 * state to the corresponding Windows character or characters.
1508 * If the specified key is a dead key, the return value is negative. Otherwise,
1509 * it is one of the following values:
1510 * Value Meaning
1511 * -1 The specified virtual key is a dead-key. If possible, the
1512 * non-combining form of the dead character is written to bufW.
1513 * 0 The specified virtual key has no translation for the current
1514 * state of the keyboard.
1515 * 1 One Windows character was copied to the buffer.
1516 * 2 or more Multiple characters were copied to the buffer. This usually
1517 * happens when a dead-key character (accent or diacritic) stored
1518 * in the keyboard layout cannot be composed with the specified
1519 * virtual key to form a single character.
1522 INT CDECL macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1523 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1525 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1526 INT ret = 0;
1527 int keyc;
1528 BOOL is_menu = (flags & 0x1);
1529 int status;
1530 const UCKeyboardLayout *uchr;
1531 UInt16 keyAction;
1532 UInt32 modifierKeyState;
1533 OptionBits options;
1534 UInt32 deadKeyState, savedDeadKeyState;
1535 UniCharCount len;
1536 BOOL dead = FALSE;
1538 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1539 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1541 if (!virtKey)
1542 goto done;
1544 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1545 modifier key press. We want it to effectively ignore modifier key
1546 presses. I think that one isn't supposed to call it at all for modifier
1547 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1548 are different event types than key up/down events. */
1549 switch (virtKey)
1551 case VK_SHIFT:
1552 case VK_CONTROL:
1553 case VK_MENU:
1554 case VK_CAPITAL:
1555 case VK_LSHIFT:
1556 case VK_RSHIFT:
1557 case VK_LCONTROL:
1558 case VK_RCONTROL:
1559 case VK_LMENU:
1560 case VK_RMENU:
1561 goto done;
1564 /* There are a number of key combinations for which Windows does not
1565 produce characters, but Mac keyboard layouts may. Eat them. Do this
1566 here to avoid the expense of UCKeyTranslate() but also because these
1567 keys shouldn't terminate dead key sequences. */
1568 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1569 goto done;
1571 /* Shift + <non-digit keypad keys>. */
1572 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1573 goto done;
1575 if (lpKeyState[VK_CONTROL] & 0x80)
1577 /* Control-Tab, with or without other modifiers. */
1578 if (virtKey == VK_TAB)
1579 goto done;
1581 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1582 for these keys. */
1583 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1585 switch (virtKey)
1587 case VK_CANCEL:
1588 case VK_BACK:
1589 case VK_ESCAPE:
1590 case VK_SPACE:
1591 case VK_RETURN:
1592 goto done;
1597 if (thread_data->keyboard_layout_uchr)
1598 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1599 else
1600 uchr = NULL;
1602 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1604 modifierKeyState = 0;
1605 if (lpKeyState[VK_SHIFT] & 0x80)
1606 modifierKeyState |= (shiftKey >> 8);
1607 if (lpKeyState[VK_CAPITAL] & 0x01)
1608 modifierKeyState |= (alphaLock >> 8);
1609 if (lpKeyState[VK_CONTROL] & 0x80)
1610 modifierKeyState |= (controlKey >> 8);
1611 if (lpKeyState[VK_MENU] & 0x80)
1612 modifierKeyState |= (cmdKey >> 8);
1613 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1614 modifierKeyState |= (optionKey >> 8);
1616 /* Find the Mac keycode corresponding to the vkey */
1617 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1618 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1620 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1622 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1623 goto done;
1626 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1627 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1629 if (is_menu)
1631 if (keyAction == kUCKeyActionUp)
1632 goto done;
1634 options = kUCKeyTranslateNoDeadKeysMask;
1635 deadKeyState = 0;
1637 else
1639 options = 0;
1640 deadKeyState = thread_data->dead_key_state;
1642 savedDeadKeyState = deadKeyState;
1643 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1644 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1645 &len, bufW);
1646 if (status != noErr)
1648 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1649 goto done;
1651 if (!is_menu)
1653 if (keyAction != kUCKeyActionUp && len > 0 && deadKeyState == thread_data->dead_key_state)
1654 thread_data->dead_key_state = 0;
1655 else
1656 thread_data->dead_key_state = deadKeyState;
1658 if (keyAction == kUCKeyActionUp)
1659 goto done;
1662 if (len == 0 && deadKeyState)
1664 /* Repeat the translation, but disabling dead-key generation to
1665 learn which dead key it was. */
1666 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1667 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1668 &savedDeadKeyState, bufW_size, &len, bufW);
1669 if (status != noErr)
1671 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1672 goto done;
1675 dead = TRUE;
1678 if (len > 0)
1679 len = strip_apple_private_chars(bufW, len);
1681 if (dead && len > 0) ret = -1;
1682 else ret = len;
1684 /* Control-Return produces line feed instead of carriage return. */
1685 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1687 int i;
1688 for (i = 0; i < len; i++)
1689 if (bufW[i] == '\r')
1690 bufW[i] = '\n';
1693 done:
1694 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1695 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1696 if (1 <= ret && ret < bufW_size)
1697 bufW[ret] = 0;
1699 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, abs(ret)));
1700 return ret;
1704 /***********************************************************************
1705 * UnregisterHotKey (MACDRV.@)
1707 void CDECL macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey)
1709 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1711 TRACE_(key)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd, modifiers, vkey);
1713 if (thread_data)
1714 macdrv_unregister_hot_key(thread_data->queue, vkey, modifiers);
1718 /***********************************************************************
1719 * VkKeyScanEx (MACDRV.@)
1721 * Note: Windows ignores HKL parameter and uses current active layout instead
1723 SHORT CDECL macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1725 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1726 SHORT ret = -1;
1727 int state;
1728 const UCKeyboardLayout *uchr;
1730 TRACE("%04x, %p\n", wChar, hkl);
1732 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1733 if (!uchr)
1735 TRACE("no keyboard layout UCHR data; returning -1\n");
1736 return -1;
1739 for (state = 0; state < 8; state++)
1741 UInt32 modifierKeyState = 0;
1742 int keyc;
1744 if (state & 1)
1745 modifierKeyState |= (shiftKey >> 8);
1746 if ((state & 6) == 6)
1747 modifierKeyState |= (optionKey >> 8);
1748 else
1750 if (state & 2)
1751 modifierKeyState |= (controlKey >> 8);
1752 if (state & 4)
1753 modifierKeyState |= (cmdKey >> 8);
1756 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1758 UInt32 deadKeyState = 0;
1759 UniChar uchar;
1760 UniCharCount len;
1761 OSStatus status;
1763 if (!thread_data->keyc2vkey[keyc]) continue;
1765 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1766 thread_data->keyboard_type, 0, &deadKeyState,
1767 1, &len, &uchar);
1768 if (status == noErr && len == 1 && uchar == wChar)
1770 WORD vkey = thread_data->keyc2vkey[keyc];
1772 ret = vkey | (state << 8);
1773 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1774 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1775 keyc == kVK_ANSI_KeypadEquals)
1777 /* Keep searching for a non-numpad match, which is preferred. */
1779 else
1780 goto done;
1785 done:
1786 TRACE(" -> 0x%04x\n", ret);
1787 return ret;