ncrypt: Add NCryptIsKeyHandle stub.
[wine.git] / dlls / winemac.drv / keyboard.c
blobbb408cb20c56e41568c13e147ed52f03825d376a
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 HKL CDECL macdrv_GetKeyboardLayout(DWORD);
400 static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
402 BOOL ret;
403 CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
404 CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, strlenW(string), kCFAllocatorNull);
405 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
406 if (ignore_diacritics)
407 flags |= kCFCompareDiacriticInsensitive;
408 ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
409 CFRelease(s1);
410 CFRelease(s2);
411 return ret;
415 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
416 * string. Returns the length of the string after stripping. */
417 static int strip_apple_private_chars(LPWSTR bufW, int len)
419 int i;
420 for (i = 0; i < len; )
422 if (0xF700 <= bufW[i] && bufW[i] <= 0xF8FF)
424 memmove(&bufW[i], &bufW[i+1], (len - i - 1) * sizeof(bufW[0]));
425 len--;
427 else
428 i++;
430 return len;
433 static struct list layout_list = LIST_INIT( layout_list );
434 struct layout
436 struct list entry;
437 HKL hkl;
438 TISInputSourceRef input_source;
439 BOOL enabled; /* is the input source enabled - ie displayed in the input source selector UI */
442 static CRITICAL_SECTION layout_list_section;
443 static CRITICAL_SECTION_DEBUG critsect_debug =
445 0, 0, &layout_list_section,
446 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
447 0, 0, { (DWORD_PTR)(__FILE__ ": layout_list_section") }
449 static CRITICAL_SECTION layout_list_section = { &critsect_debug, -1, 0, 0, 0, 0 };
451 int macdrv_layout_list_needs_update = TRUE;
453 static DWORD get_lcid(CFStringRef lang)
455 CFRange range;
456 WCHAR str[10];
458 range.location = 0;
459 range.length = min(CFStringGetLength(lang), ARRAY_SIZE(str) - 1);
460 CFStringGetCharacters(lang, range, str);
461 str[range.length] = 0;
462 return LocaleNameToLCID(str, 0);
465 static HKL get_hkl(CFStringRef lang, CFStringRef type)
467 ULONG_PTR lcid = get_lcid(lang);
468 struct layout *layout;
470 /* Look for the last occurrence of this lcid in the list and if
471 present use that value + 0x10000 */
472 LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry)
474 ULONG_PTR hkl = HandleToUlong(layout->hkl);
476 if (LOWORD(hkl) == lcid)
478 lcid = (hkl & ~0xe0000000) + 0x10000;
479 break;
483 if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000;
485 return (HKL)lcid;
488 /******************************************************************
489 * get_layout_from_source
491 * Must be called while holding the layout_list_section.
492 * Note, returned layout may not currently be enabled.
494 static struct layout *get_layout_from_source(TISInputSourceRef input)
496 struct layout *ret = NULL, *layout;
498 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
500 if (CFEqual(input, layout->input_source))
502 ret = layout;
503 break;
506 return ret;
509 /***********************************************************************
510 * update_layout_list
512 * Must be called while holding the layout_list_section
514 * If an input source has been disabled (ie. removed from the UI) its
515 * entry remains in the layout list but is marked as such and is not
516 * enumerated by GetKeyboardLayoutList. This is to ensure the
517 * HKL <-> input source mapping is unique.
519 static void update_layout_list(void)
521 CFArrayRef sources;
522 struct layout *layout;
523 int i;
525 if (!InterlockedExchange(&macdrv_layout_list_needs_update, FALSE)) return;
527 sources = macdrv_create_input_source_list();
529 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
530 layout->enabled = FALSE;
532 for (i = 0; i < CFArrayGetCount(sources); i++)
534 CFDictionaryRef dict = CFArrayGetValueAtIndex(sources, i);
535 TISInputSourceRef input = (TISInputSourceRef)CFDictionaryGetValue(dict, macdrv_input_source_input_key);
536 layout = get_layout_from_source(input);
537 if (!layout)
539 CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key);
540 CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key);
542 layout = HeapAlloc(GetProcessHeap(), 0, sizeof(*layout));
543 layout->input_source = (TISInputSourceRef)CFRetain(input);
544 layout->hkl = get_hkl(lang, type);
546 list_add_tail(&layout_list, &layout->entry);
547 TRACE("adding new layout %p\n", layout->hkl);
549 else
550 TRACE("enabling already existing layout %p\n", layout->hkl);
552 layout->enabled = TRUE;
555 CFRelease(sources);
558 /***********************************************************************
559 * macdrv_get_hkl_from_source
561 * Find the HKL associated with a given input source.
563 HKL macdrv_get_hkl_from_source(TISInputSourceRef input)
565 struct layout *layout;
566 HKL ret = 0;
568 EnterCriticalSection(&layout_list_section);
570 update_layout_list();
571 layout = get_layout_from_source(input);
572 if (layout) ret = layout->hkl;
574 LeaveCriticalSection(&layout_list_section);
576 return ret;
580 /***********************************************************************
581 * macdrv_compute_keyboard_layout
583 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
585 int keyc;
586 WCHAR vkey;
587 const UCKeyboardLayout *uchr;
588 const UInt32 modifier_combos[] = {
590 shiftKey >> 8,
591 cmdKey >> 8,
592 (shiftKey | cmdKey) >> 8,
593 optionKey >> 8,
594 (shiftKey | optionKey) >> 8,
596 UniChar map[128][ARRAY_SIZE(modifier_combos)][4 + 1];
597 int combo;
598 BYTE vkey_used[256];
599 int ignore_diacritics;
600 static const struct {
601 WCHAR wchar;
602 DWORD vkey;
603 } symbol_vkeys[] = {
604 { '-', VK_OEM_MINUS },
605 { '+', VK_OEM_PLUS },
606 { '_', VK_OEM_MINUS },
607 { ',', VK_OEM_COMMA },
608 { '.', VK_OEM_PERIOD },
609 { '=', VK_OEM_PLUS },
610 { '>', VK_OEM_PERIOD },
611 { '<', VK_OEM_COMMA },
612 { '|', VK_OEM_5 },
613 { '\\', VK_OEM_5 },
614 { '`', VK_OEM_3 },
615 { '[', VK_OEM_4 },
616 { '~', VK_OEM_3 },
617 { '?', VK_OEM_2 },
618 { ']', VK_OEM_6 },
619 { '/', VK_OEM_2 },
620 { ':', VK_OEM_1 },
621 { '}', VK_OEM_6 },
622 { '{', VK_OEM_4 },
623 { ';', VK_OEM_1 },
624 { '\'', VK_OEM_7 },
625 { ':', VK_OEM_PERIOD },
626 { ';', VK_OEM_COMMA },
627 { '"', VK_OEM_7 },
628 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
629 { '\'', VK_OEM_2 },
630 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
631 { '*', VK_OEM_PLUS },
632 { 0x00B4, VK_OEM_7 },
633 { '`', VK_OEM_4 },
634 { '[', VK_OEM_6 },
635 { '/', VK_OEM_5 },
636 { '^', VK_OEM_6 },
637 { '*', VK_OEM_2 },
638 { '{', VK_OEM_6 },
639 { '~', VK_OEM_1 },
640 { '?', VK_OEM_PLUS },
641 { '?', VK_OEM_4 },
642 { 0x00B4, VK_OEM_3 },
643 { '?', VK_OEM_COMMA },
644 { '~', VK_OEM_PLUS },
645 { ']', VK_OEM_4 },
646 { '\'', VK_OEM_3 },
647 { 0x00A7, VK_OEM_7 },
649 int i;
651 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
652 contiguous ranges. */
653 static const struct {
654 WORD first, last;
655 } vkey_ranges[] = {
656 { 'A', 'Z' },
657 { '0', '9' },
658 { VK_OEM_1, VK_OEM_3 },
659 { VK_OEM_4, VK_ICO_CLEAR },
660 { 0xe9, 0xf5 },
661 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
662 { VK_F1, VK_F24 },
663 { 0, 0 }
665 int vkey_range;
667 if (!thread_data->keyboard_layout_uchr)
669 ERR("no keyboard layout UCHR data\n");
670 return;
673 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
674 memset(vkey_used, 0, sizeof(vkey_used));
676 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
678 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
679 if (default_map[keyc].fixed)
681 vkey = default_map[keyc].vkey;
682 thread_data->keyc2vkey[keyc] = vkey;
683 vkey_used[vkey] = 1;
684 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
688 if (thread_data->iso_keyboard)
690 /* In almost all cases, the Mac key codes indicate a physical key position
691 and this corresponds nicely to Win32 scan codes. However, the Mac key
692 codes differ in one case between ANSI and ISO keyboards. For ANSI
693 keyboards, the key to the left of the digits and above the Tab key
694 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
695 some position produces kVK_ISO_Section. The additional key on ISO
696 keyboards, the one to the right of the left Shift key, produces
697 kVK_ANSI_Grave, which is just weird.
699 Since we want the key in that upper left corner to always produce the
700 same scan code (0x29), we need to swap the scan codes of those two
701 Mac key codes for ISO keyboards. */
702 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
703 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
704 thread_data->keyc2scan[kVK_ISO_Section] = temp;
707 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
709 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
710 memset(map, 0, sizeof(map));
711 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
713 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
714 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
716 TRACE("keyc 0x%04x: ", keyc);
718 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
720 UInt32 deadKeyState;
721 UniCharCount len;
722 OSStatus status;
724 deadKeyState = 0;
725 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
726 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
727 &deadKeyState, ARRAY_SIZE(map[keyc][combo]) - 1, &len, map[keyc][combo]);
728 if (status != noErr)
729 map[keyc][combo][0] = 0;
731 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
734 TRACE("\n");
737 /* First try to match key codes to the vkeys for the letters A through Z.
738 Try unmodified first, then with various modifier combinations in succession.
739 On the first pass, try to get a match lacking diacritical marks. On the
740 second pass, accept matches with diacritical marks. */
741 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
743 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
745 for (vkey = 'A'; vkey <= 'Z'; vkey++)
747 if (vkey_used[vkey])
748 continue;
750 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
752 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
753 continue;
755 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
757 thread_data->keyc2vkey[keyc] = vkey;
758 vkey_used[vkey] = 1;
759 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
760 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
761 break;
768 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
769 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
771 for (vkey = '0'; vkey <= '9'; vkey++)
773 if (vkey_used[vkey])
774 continue;
776 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
778 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
779 continue;
781 if (char_matches_string(vkey, map[keyc][combo], FALSE))
783 thread_data->keyc2vkey[keyc] = vkey;
784 vkey_used[vkey] = 1;
785 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
786 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
787 break;
793 /* Now try to match key codes for certain common punctuation characters to
794 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
795 for (i = 0; i < ARRAY_SIZE(symbol_vkeys); i++)
797 vkey = symbol_vkeys[i].vkey;
799 if (vkey_used[vkey])
800 continue;
802 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
804 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
806 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
807 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
808 continue;
810 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
812 thread_data->keyc2vkey[keyc] = vkey;
813 vkey_used[vkey] = 1;
814 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
815 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
816 break;
820 if (vkey_used[vkey])
821 break;
825 /* For those key codes still without a vkey, try to use the default vkey
826 from the default map, if it's still available. */
827 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
829 DWORD vkey = default_map[keyc].vkey;
831 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
832 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
834 if (!vkey_used[vkey])
836 thread_data->keyc2vkey[keyc] = vkey;
837 vkey_used[vkey] = 1;
838 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
842 /* For any unassigned key codes which would map to a letter in the default
843 map, but whose normal letter vkey wasn't available, try to find a
844 different letter. */
845 vkey = 'A';
846 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
848 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
849 continue; /* not a letter in ANSI layout */
850 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
851 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
853 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
854 if (vkey <= 'Z')
856 thread_data->keyc2vkey[keyc] = vkey;
857 vkey_used[vkey] = 1;
858 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
860 else
861 break; /* no more unused letter vkeys, so stop trying */
864 /* Same thing but with the digits. */
865 vkey = '0';
866 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
868 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
869 continue; /* not a digit in ANSI layout */
870 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
871 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
873 while (vkey <= '9' && vkey_used[vkey]) vkey++;
874 if (vkey <= '9')
876 thread_data->keyc2vkey[keyc] = vkey;
877 vkey_used[vkey] = 1;
878 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
880 else
881 break; /* no more unused digit vkeys, so stop trying */
884 /* Last chance. Assign any available vkey. */
885 vkey_range = 0;
886 vkey = vkey_ranges[vkey_range].first;
887 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
889 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
890 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
892 while (vkey && vkey_used[vkey])
894 if (vkey == vkey_ranges[vkey_range].last)
896 vkey_range++;
897 vkey = vkey_ranges[vkey_range].first;
899 else
900 vkey++;
903 if (!vkey)
905 WARN("No more vkeys available!\n");
906 break;
909 thread_data->keyc2vkey[keyc] = vkey;
910 vkey_used[vkey] = 1;
911 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
916 /***********************************************************************
917 * macdrv_send_keyboard_input
919 static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD flags, DWORD time)
921 INPUT input;
923 TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
925 input.type = INPUT_KEYBOARD;
926 input.ki.wVk = vkey;
927 input.ki.wScan = scan;
928 input.ki.dwFlags = flags;
929 input.ki.time = time;
930 input.ki.dwExtraInfo = 0;
932 __wine_send_input(hwnd, &input);
936 /***********************************************************************
937 * get_async_key_state
939 static BOOL get_async_key_state(BYTE state[256])
941 BOOL ret;
943 SERVER_START_REQ(get_key_state)
945 req->tid = 0;
946 req->key = -1;
947 wine_server_set_reply(req, state, 256);
948 ret = !wine_server_call(req);
950 SERVER_END_REQ;
951 return ret;
955 /***********************************************************************
956 * update_modifier_state
958 static void update_modifier_state(unsigned int modifier, unsigned int modifiers, const BYTE *keystate,
959 WORD vkey, WORD alt_vkey, WORD scan, WORD alt_scan,
960 DWORD event_time, BOOL restore)
962 int key_pressed = (modifiers & modifier) != 0;
963 int vkey_pressed = (keystate[vkey] & 0x80) || (keystate[alt_vkey] & 0x80);
964 DWORD flags;
966 if (key_pressed != vkey_pressed)
968 if (key_pressed)
970 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
971 if (restore)
972 flags |= KEYEVENTF_KEYUP;
974 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff, flags, event_time);
976 else
978 flags = restore ? 0 : KEYEVENTF_KEYUP;
980 if (keystate[vkey] & 0x80)
982 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff,
983 flags | ((scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
984 event_time);
986 if (keystate[alt_vkey] & 0x80)
988 macdrv_send_keyboard_input(NULL, alt_vkey, alt_scan & 0xff,
989 flags | ((alt_scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
990 event_time);
997 /***********************************************************************
998 * macdrv_key_event
1000 * Handler for KEY_PRESS and KEY_RELEASE events.
1002 void macdrv_key_event(HWND hwnd, const macdrv_event *event)
1004 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1005 WORD vkey, scan;
1006 DWORD flags;
1008 TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1009 hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
1010 event->key.keycode, event->key.modifiers);
1012 thread_data->last_modifiers = event->key.modifiers;
1014 if (event->key.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1016 vkey = thread_data->keyc2vkey[event->key.keycode];
1017 scan = thread_data->keyc2scan[event->key.keycode];
1019 else
1020 vkey = scan = 0;
1022 TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1023 event->key.keycode, vkey, scan);
1025 if (!vkey) return;
1027 flags = 0;
1028 if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
1029 if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
1031 macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
1035 /***********************************************************************
1036 * macdrv_keyboard_changed
1038 * Handler for KEYBOARD_CHANGED events.
1040 void macdrv_keyboard_changed(const macdrv_event *event)
1042 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1044 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
1045 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
1047 if (thread_data->keyboard_layout_uchr)
1048 CFRelease(thread_data->keyboard_layout_uchr);
1049 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
1050 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
1051 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
1052 thread_data->active_keyboard_layout = macdrv_get_hkl_from_source(event->keyboard_changed.input_source);
1053 thread_data->dead_key_state = 0;
1055 macdrv_compute_keyboard_layout(thread_data);
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 HKL CDECL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
1166 HKL oldHkl = 0;
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 (flags) FIXME("flags %x not supported\n",flags);
1174 if (hkl == (HKL)HKL_NEXT || hkl == (HKL)HKL_PREV)
1176 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1177 FIXME("HKL_NEXT and HKL_PREV not supported\n");
1178 return 0;
1181 EnterCriticalSection(&layout_list_section);
1182 update_layout_list();
1184 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1186 if (layout->hkl == hkl)
1188 if (macdrv_select_input_source(layout->input_source))
1190 oldHkl = thread_data->active_keyboard_layout;
1191 if (thread_data->keyboard_layout_uchr)
1192 CFRelease(thread_data->keyboard_layout_uchr);
1194 macdrv_get_input_source_info(&thread_data->keyboard_layout_uchr, &thread_data->keyboard_type,
1195 &thread_data->iso_keyboard, NULL);
1196 thread_data->active_keyboard_layout = hkl;
1197 thread_data->dead_key_state = 0;
1199 macdrv_compute_keyboard_layout(thread_data);
1201 break;
1204 LeaveCriticalSection(&layout_list_section);
1206 return oldHkl;
1210 /***********************************************************************
1211 * Beep (MACDRV.@)
1213 void CDECL macdrv_Beep(void)
1215 macdrv_beep();
1219 /***********************************************************************
1220 * GetKeyNameText (MACDRV.@)
1222 INT CDECL macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
1224 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1225 int scan, keyc;
1227 scan = (lparam >> 16) & 0x1FF;
1228 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2scan); keyc++)
1230 if (thread_data->keyc2scan[keyc] == scan)
1232 static const WCHAR dead[] = {' ','d','e','a','d',0};
1233 const UCKeyboardLayout *uchr;
1234 UInt32 deadKeyState = 0;
1235 UniCharCount len;
1236 OSStatus status;
1237 DWORD vkey;
1238 int i;
1240 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1241 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
1242 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
1243 if (status != noErr)
1244 len = 0;
1245 if (len && isgraphW(buffer[0]))
1246 buffer[len] = 0;
1248 vkey = thread_data->keyc2vkey[keyc];
1249 if (lparam & (1 << 25))
1251 /* Caller doesn't care about distinctions between left and
1252 right keys. */
1253 switch (vkey)
1255 case VK_LSHIFT:
1256 case VK_RSHIFT:
1257 vkey = VK_SHIFT; break;
1258 case VK_LCONTROL:
1259 case VK_RCONTROL:
1260 vkey = VK_CONTROL; break;
1261 case VK_LMENU:
1262 case VK_RMENU:
1263 vkey = VK_MENU; break;
1267 if (scan & 0x100) vkey |= 0x100;
1269 for (i = 0; i < ARRAY_SIZE(vkey_names); i++)
1271 if (vkey_names[i].vkey == vkey)
1273 len = MultiByteToWideChar(CP_UTF8, 0, vkey_names[i].name, -1, buffer, size);
1274 if (len) len--;
1275 break;
1279 if (!len)
1281 static const WCHAR format[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1282 snprintfW(buffer, size, format, vkey);
1283 len = strlenW(buffer);
1286 if (!len)
1287 break;
1289 if (status == noErr && deadKeyState)
1291 lstrcpynW(buffer + len, dead, size - len);
1292 len = strlenW(buffer);
1295 TRACE("lparam 0x%08x -> %s\n", lparam, debugstr_w(buffer));
1296 return len;
1300 WARN("found no name for lparam 0x%08x\n", lparam);
1301 return 0;
1305 /***********************************************************************
1306 * GetKeyboardLayout (MACDRV.@)
1308 HKL CDECL macdrv_GetKeyboardLayout(DWORD thread_id)
1310 if (thread_id && thread_id != GetCurrentThreadId())
1311 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id);
1313 return macdrv_init_thread_data()->active_keyboard_layout;
1317 /***********************************************************************
1318 * GetKeyboardLayoutList (MACDRV.@)
1320 UINT CDECL macdrv_GetKeyboardLayoutList(INT size, HKL *list)
1322 int count = 0;
1323 struct layout *layout;
1325 TRACE("%d, %p\n", size, list);
1327 EnterCriticalSection(&layout_list_section);
1329 update_layout_list();
1331 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1333 if (!layout->enabled) continue;
1334 if (list)
1336 if (count >= size) break;
1337 list[count] = layout->hkl;
1338 TRACE("\t%d: %p\n", count, list[count]);
1340 count++;
1342 LeaveCriticalSection(&layout_list_section);
1344 TRACE("returning %d\n", count);
1345 return count;
1348 /***********************************************************************
1349 * GetKeyboardLayoutName (MACDRV.@)
1351 BOOL CDECL macdrv_GetKeyboardLayoutName(LPWSTR name)
1353 static const WCHAR formatW[] = {'%','0','8','x',0};
1354 DWORD layout;
1356 layout = HandleToUlong(macdrv_GetKeyboardLayout(0));
1357 if (HIWORD(layout) == LOWORD(layout)) layout = LOWORD(layout);
1358 sprintfW(name, formatW, layout);
1359 TRACE("returning %s\n", debugstr_w(name));
1360 return TRUE;
1364 /***********************************************************************
1365 * MapVirtualKeyEx (MACDRV.@)
1367 UINT CDECL macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1369 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1370 UINT ret = 0;
1371 int keyc;
1373 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1375 switch (wMapType)
1377 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1378 case MAPVK_VK_TO_VSC_EX:
1379 switch (wCode)
1381 case VK_SHIFT: wCode = VK_LSHIFT; break;
1382 case VK_CONTROL: wCode = VK_LCONTROL; break;
1383 case VK_MENU: wCode = VK_LMENU; break;
1386 /* vkey -> keycode -> scan */
1387 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1389 if (thread_data->keyc2vkey[keyc] == wCode)
1391 ret = thread_data->keyc2scan[keyc] & 0xFF;
1392 break;
1396 /* set scan code prefix */
1397 if (wMapType == MAPVK_VK_TO_VSC_EX &&
1398 (wCode == VK_RCONTROL || wCode == VK_RMENU))
1399 ret |= 0xe000;
1400 break;
1402 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1403 case MAPVK_VSC_TO_VK_EX:
1404 /* scan -> keycode -> vkey */
1405 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1406 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1408 ret = thread_data->keyc2vkey[keyc];
1409 /* Only stop if it's not a numpad vkey; otherwise keep
1410 looking for a potential better vkey. */
1411 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1412 break;
1415 if (wMapType == MAPVK_VSC_TO_VK)
1416 switch (ret)
1418 case VK_LSHIFT:
1419 case VK_RSHIFT:
1420 ret = VK_SHIFT; break;
1421 case VK_LCONTROL:
1422 case VK_RCONTROL:
1423 ret = VK_CONTROL; break;
1424 case VK_LMENU:
1425 case VK_RMENU:
1426 ret = VK_MENU; break;
1429 break;
1431 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1433 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1434 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1435 const UCKeyboardLayout *uchr;
1436 UniChar s[10];
1437 OSStatus status;
1438 UInt32 deadKeyState;
1439 UniCharCount len;
1440 BOOL deadKey = FALSE;
1442 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1443 (VK_F1 <= wCode && wCode <= VK_F24))
1444 break;
1446 if (!thread_data || !thread_data->keyboard_layout_uchr)
1448 WARN("No keyboard layout uchr data\n");
1449 break;
1452 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1454 /* Find the Mac keycode corresponding to the vkey */
1455 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1456 if (thread_data->keyc2vkey[keyc] == wCode) break;
1458 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1460 WARN("Unknown virtual key %X\n", wCode);
1461 break;
1464 TRACE("Found keycode %u\n", keyc);
1466 deadKeyState = 0;
1467 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1468 thread_data->keyboard_type, 0, &deadKeyState, ARRAY_SIZE(s), &len, s);
1469 if (status == noErr && !len && deadKeyState)
1471 deadKey = TRUE;
1472 deadKeyState = 0;
1473 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1474 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1475 &deadKeyState, ARRAY_SIZE(s), &len, s);
1478 if (status == noErr && len)
1479 ret = toupperW(s[0]) | (deadKey ? 0x80000000 : 0);
1481 break;
1483 default: /* reserved */
1484 FIXME("Unknown wMapType %d\n", wMapType);
1485 break;
1488 TRACE("returning 0x%04x\n", ret);
1489 return ret;
1493 /***********************************************************************
1494 * RegisterHotKey (MACDRV.@)
1496 BOOL CDECL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey)
1498 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1499 unsigned int keyc, modifiers = 0;
1500 int ret;
1502 TRACE_(key)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd, mod_flags, vkey);
1504 /* Find the Mac keycode corresponding to the vkey */
1505 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1506 if (thread_data->keyc2vkey[keyc] == vkey) break;
1508 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1510 WARN_(key)("ignoring unknown virtual key 0x%04x\n", vkey);
1511 return TRUE;
1514 if (mod_flags & MOD_ALT) modifiers |= cmdKey;
1515 if (mod_flags & MOD_CONTROL) modifiers |= controlKey;
1516 if (mod_flags & MOD_SHIFT) modifiers |= shiftKey;
1517 if (mod_flags & MOD_WIN)
1519 WARN_(key)("MOD_WIN not supported; ignoring\n");
1520 return TRUE;
1523 ret = macdrv_register_hot_key(thread_data->queue, vkey, mod_flags, keyc, modifiers);
1524 TRACE_(key)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc, modifiers, ret);
1526 if (ret == MACDRV_HOTKEY_ALREADY_REGISTERED)
1527 SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
1528 else if (ret != MACDRV_HOTKEY_SUCCESS)
1529 SetLastError(ERROR_GEN_FAILURE);
1531 return ret == MACDRV_HOTKEY_SUCCESS;
1535 /***********************************************************************
1536 * ToUnicodeEx (MACDRV.@)
1538 * The ToUnicode function translates the specified virtual-key code and keyboard
1539 * state to the corresponding Windows character or characters.
1541 * If the specified key is a dead key, the return value is negative. Otherwise,
1542 * it is one of the following values:
1543 * Value Meaning
1544 * -1 The specified virtual key is a dead-key. If possible, the
1545 * non-combining form of the dead character is written to bufW.
1546 * 0 The specified virtual key has no translation for the current
1547 * state of the keyboard.
1548 * 1 One Windows character was copied to the buffer.
1549 * 2 or more Multiple characters were copied to the buffer. This usually
1550 * happens when a dead-key character (accent or diacritic) stored
1551 * in the keyboard layout cannot be composed with the specified
1552 * virtual key to form a single character.
1555 INT CDECL macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1556 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1558 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1559 INT ret = 0;
1560 int keyc;
1561 BOOL is_menu = (flags & 0x1);
1562 int status;
1563 const UCKeyboardLayout *uchr;
1564 UInt16 keyAction;
1565 UInt32 modifierKeyState;
1566 OptionBits options;
1567 UInt32 deadKeyState, savedDeadKeyState;
1568 UniCharCount len;
1569 BOOL dead = FALSE;
1571 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1572 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1574 if (!virtKey)
1575 goto done;
1577 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1578 modifier key press. We want it to effectively ignore modifier key
1579 presses. I think that one isn't supposed to call it at all for modifier
1580 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1581 are different event types than key up/down events. */
1582 switch (virtKey)
1584 case VK_SHIFT:
1585 case VK_CONTROL:
1586 case VK_MENU:
1587 case VK_CAPITAL:
1588 case VK_LSHIFT:
1589 case VK_RSHIFT:
1590 case VK_LCONTROL:
1591 case VK_RCONTROL:
1592 case VK_LMENU:
1593 case VK_RMENU:
1594 goto done;
1597 /* There are a number of key combinations for which Windows does not
1598 produce characters, but Mac keyboard layouts may. Eat them. Do this
1599 here to avoid the expense of UCKeyTranslate() but also because these
1600 keys shouldn't terminate dead key sequences. */
1601 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1602 goto done;
1604 /* Shift + <non-digit keypad keys>. */
1605 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1606 goto done;
1608 if (lpKeyState[VK_CONTROL] & 0x80)
1610 /* Control-Tab, with or without other modifiers. */
1611 if (virtKey == VK_TAB)
1612 goto done;
1614 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1615 for these keys. */
1616 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1618 switch (virtKey)
1620 case VK_CANCEL:
1621 case VK_BACK:
1622 case VK_ESCAPE:
1623 case VK_SPACE:
1624 case VK_RETURN:
1625 goto done;
1630 if (thread_data->keyboard_layout_uchr)
1631 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1632 else
1633 uchr = NULL;
1635 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1637 modifierKeyState = 0;
1638 if (lpKeyState[VK_SHIFT] & 0x80)
1639 modifierKeyState |= (shiftKey >> 8);
1640 if (lpKeyState[VK_CAPITAL] & 0x01)
1641 modifierKeyState |= (alphaLock >> 8);
1642 if (lpKeyState[VK_CONTROL] & 0x80)
1643 modifierKeyState |= (controlKey >> 8);
1644 if (lpKeyState[VK_MENU] & 0x80)
1645 modifierKeyState |= (cmdKey >> 8);
1646 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1647 modifierKeyState |= (optionKey >> 8);
1649 /* Find the Mac keycode corresponding to the vkey */
1650 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1651 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1653 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1655 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1656 goto done;
1659 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1660 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1662 if (is_menu)
1664 if (keyAction == kUCKeyActionUp)
1665 goto done;
1667 options = kUCKeyTranslateNoDeadKeysMask;
1668 deadKeyState = 0;
1670 else
1672 options = 0;
1673 deadKeyState = thread_data->dead_key_state;
1675 savedDeadKeyState = deadKeyState;
1676 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1677 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1678 &len, bufW);
1679 if (status != noErr)
1681 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1682 goto done;
1684 if (!is_menu)
1686 if (keyAction != kUCKeyActionUp && len > 0 && deadKeyState == thread_data->dead_key_state)
1687 thread_data->dead_key_state = 0;
1688 else
1689 thread_data->dead_key_state = deadKeyState;
1691 if (keyAction == kUCKeyActionUp)
1692 goto done;
1695 if (len == 0 && deadKeyState)
1697 /* Repeat the translation, but disabling dead-key generation to
1698 learn which dead key it was. */
1699 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1700 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1701 &savedDeadKeyState, bufW_size, &len, bufW);
1702 if (status != noErr)
1704 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1705 goto done;
1708 dead = TRUE;
1711 if (len > 0)
1712 len = strip_apple_private_chars(bufW, len);
1714 if (dead && len > 0) ret = -1;
1715 else ret = len;
1717 /* Control-Return produces line feed instead of carriage return. */
1718 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1720 int i;
1721 for (i = 0; i < len; i++)
1722 if (bufW[i] == '\r')
1723 bufW[i] = '\n';
1726 done:
1727 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1728 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1729 if (1 <= ret && ret < bufW_size)
1730 bufW[ret] = 0;
1732 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, abs(ret)));
1733 return ret;
1737 /***********************************************************************
1738 * UnregisterHotKey (MACDRV.@)
1740 void CDECL macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey)
1742 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1744 TRACE_(key)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd, modifiers, vkey);
1746 if (thread_data)
1747 macdrv_unregister_hot_key(thread_data->queue, vkey, modifiers);
1751 /***********************************************************************
1752 * VkKeyScanEx (MACDRV.@)
1754 * Note: Windows ignores HKL parameter and uses current active layout instead
1756 SHORT CDECL macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1758 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1759 SHORT ret = -1;
1760 int state;
1761 const UCKeyboardLayout *uchr;
1763 TRACE("%04x, %p\n", wChar, hkl);
1765 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1766 if (!uchr)
1768 TRACE("no keyboard layout UCHR data; returning -1\n");
1769 return -1;
1772 for (state = 0; state < 8; state++)
1774 UInt32 modifierKeyState = 0;
1775 int keyc;
1777 if (state & 1)
1778 modifierKeyState |= (shiftKey >> 8);
1779 if ((state & 6) == 6)
1780 modifierKeyState |= (optionKey >> 8);
1781 else
1783 if (state & 2)
1784 modifierKeyState |= (controlKey >> 8);
1785 if (state & 4)
1786 modifierKeyState |= (cmdKey >> 8);
1789 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1791 UInt32 deadKeyState = 0;
1792 UniChar uchar;
1793 UniCharCount len;
1794 OSStatus status;
1796 if (!thread_data->keyc2vkey[keyc]) continue;
1798 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1799 thread_data->keyboard_type, 0, &deadKeyState,
1800 1, &len, &uchar);
1801 if (status == noErr && len == 1 && uchar == wChar)
1803 WORD vkey = thread_data->keyc2vkey[keyc];
1805 ret = vkey | (state << 8);
1806 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1807 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1808 keyc == kVK_ANSI_KeypadEquals)
1810 /* Keep searching for a non-numpad match, which is preferred. */
1812 else
1813 goto done;
1818 done:
1819 TRACE(" -> 0x%04x\n", ret);
1820 return ret;