kernelbase: Let GetModuleBaseName succeed on 64bit modules in wow64.
[wine.git] / dlls / winemac.drv / keyboard.c
blob76c038caf0275b3b83c7607ad4544cf7c58e8a30
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 #if 0
28 #pragma makedep unix
29 #endif
31 #include "config.h"
33 #include "macdrv.h"
34 #include "winuser.h"
35 #include "wine/server.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
38 WINE_DECLARE_DEBUG_CHANNEL(key);
41 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
42 enum {
43 cmdKeyBit = 8,
44 shiftKeyBit = 9,
45 alphaLockBit = 10,
46 optionKeyBit = 11,
47 controlKeyBit = 12,
50 enum {
51 cmdKey = 1 << cmdKeyBit,
52 shiftKey = 1 << shiftKeyBit,
53 alphaLock = 1 << alphaLockBit,
54 optionKey = 1 << optionKeyBit,
55 controlKey = 1 << controlKeyBit,
59 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
60 enum {
61 kVK_ANSI_A = 0x00,
62 kVK_ANSI_S = 0x01,
63 kVK_ANSI_D = 0x02,
64 kVK_ANSI_F = 0x03,
65 kVK_ANSI_H = 0x04,
66 kVK_ANSI_G = 0x05,
67 kVK_ANSI_Z = 0x06,
68 kVK_ANSI_X = 0x07,
69 kVK_ANSI_C = 0x08,
70 kVK_ANSI_V = 0x09,
71 kVK_ISO_Section = 0x0A,
72 kVK_ANSI_B = 0x0B,
73 kVK_ANSI_Q = 0x0C,
74 kVK_ANSI_W = 0x0D,
75 kVK_ANSI_E = 0x0E,
76 kVK_ANSI_R = 0x0F,
77 kVK_ANSI_Y = 0x10,
78 kVK_ANSI_T = 0x11,
79 kVK_ANSI_1 = 0x12,
80 kVK_ANSI_2 = 0x13,
81 kVK_ANSI_3 = 0x14,
82 kVK_ANSI_4 = 0x15,
83 kVK_ANSI_6 = 0x16,
84 kVK_ANSI_5 = 0x17,
85 kVK_ANSI_Equal = 0x18,
86 kVK_ANSI_9 = 0x19,
87 kVK_ANSI_7 = 0x1A,
88 kVK_ANSI_Minus = 0x1B,
89 kVK_ANSI_8 = 0x1C,
90 kVK_ANSI_0 = 0x1D,
91 kVK_ANSI_RightBracket = 0x1E,
92 kVK_ANSI_O = 0x1F,
93 kVK_ANSI_U = 0x20,
94 kVK_ANSI_LeftBracket = 0x21,
95 kVK_ANSI_I = 0x22,
96 kVK_ANSI_P = 0x23,
97 kVK_Return = 0x24,
98 kVK_ANSI_L = 0x25,
99 kVK_ANSI_J = 0x26,
100 kVK_ANSI_Quote = 0x27,
101 kVK_ANSI_K = 0x28,
102 kVK_ANSI_Semicolon = 0x29,
103 kVK_ANSI_Backslash = 0x2A,
104 kVK_ANSI_Comma = 0x2B,
105 kVK_ANSI_Slash = 0x2C,
106 kVK_ANSI_N = 0x2D,
107 kVK_ANSI_M = 0x2E,
108 kVK_ANSI_Period = 0x2F,
109 kVK_Tab = 0x30,
110 kVK_Space = 0x31,
111 kVK_ANSI_Grave = 0x32,
112 kVK_Delete = 0x33,
113 kVK_Escape = 0x35,
114 kVK_RightCommand = 0x36, /* invented for Wine; co-opt unused key code */
115 kVK_Command = 0x37,
116 kVK_Shift = 0x38,
117 kVK_CapsLock = 0x39,
118 kVK_Option = 0x3A,
119 kVK_Control = 0x3B,
120 kVK_RightShift = 0x3C,
121 kVK_RightOption = 0x3D,
122 kVK_RightControl = 0x3E,
123 kVK_Function = 0x3F,
124 kVK_F17 = 0x40,
125 kVK_ANSI_KeypadDecimal = 0x41,
126 kVK_ANSI_KeypadMultiply = 0x43,
127 kVK_ANSI_KeypadPlus = 0x45,
128 kVK_ANSI_KeypadClear = 0x47,
129 kVK_VolumeUp = 0x48,
130 kVK_VolumeDown = 0x49,
131 kVK_Mute = 0x4A,
132 kVK_ANSI_KeypadDivide = 0x4B,
133 kVK_ANSI_KeypadEnter = 0x4C,
134 kVK_ANSI_KeypadMinus = 0x4E,
135 kVK_F18 = 0x4F,
136 kVK_F19 = 0x50,
137 kVK_ANSI_KeypadEquals = 0x51,
138 kVK_ANSI_Keypad0 = 0x52,
139 kVK_ANSI_Keypad1 = 0x53,
140 kVK_ANSI_Keypad2 = 0x54,
141 kVK_ANSI_Keypad3 = 0x55,
142 kVK_ANSI_Keypad4 = 0x56,
143 kVK_ANSI_Keypad5 = 0x57,
144 kVK_ANSI_Keypad6 = 0x58,
145 kVK_ANSI_Keypad7 = 0x59,
146 kVK_F20 = 0x5A,
147 kVK_ANSI_Keypad8 = 0x5B,
148 kVK_ANSI_Keypad9 = 0x5C,
149 kVK_JIS_Yen = 0x5D,
150 kVK_JIS_Underscore = 0x5E,
151 kVK_JIS_KeypadComma = 0x5F,
152 kVK_F5 = 0x60,
153 kVK_F6 = 0x61,
154 kVK_F7 = 0x62,
155 kVK_F3 = 0x63,
156 kVK_F8 = 0x64,
157 kVK_F9 = 0x65,
158 kVK_JIS_Eisu = 0x66,
159 kVK_F11 = 0x67,
160 kVK_JIS_Kana = 0x68,
161 kVK_F13 = 0x69,
162 kVK_F16 = 0x6A,
163 kVK_F14 = 0x6B,
164 kVK_F10 = 0x6D,
165 kVK_F12 = 0x6F,
166 kVK_F15 = 0x71,
167 kVK_Help = 0x72,
168 kVK_Home = 0x73,
169 kVK_PageUp = 0x74,
170 kVK_ForwardDelete = 0x75,
171 kVK_F4 = 0x76,
172 kVK_End = 0x77,
173 kVK_F2 = 0x78,
174 kVK_PageDown = 0x79,
175 kVK_F1 = 0x7A,
176 kVK_LeftArrow = 0x7B,
177 kVK_RightArrow = 0x7C,
178 kVK_DownArrow = 0x7D,
179 kVK_UpArrow = 0x7E,
182 extern const CFStringRef kTISTypeKeyboardLayout;
184 /* Indexed by Mac virtual keycode values defined above. */
185 static const struct {
186 WORD vkey;
187 WORD scan;
188 BOOL fixed;
189 } default_map[128] = {
190 { 'A', 0x1E, FALSE }, /* kVK_ANSI_A */
191 { 'S', 0x1F, FALSE }, /* kVK_ANSI_S */
192 { 'D', 0x20, FALSE }, /* kVK_ANSI_D */
193 { 'F', 0x21, FALSE }, /* kVK_ANSI_F */
194 { 'H', 0x23, FALSE }, /* kVK_ANSI_H */
195 { 'G', 0x22, FALSE }, /* kVK_ANSI_G */
196 { 'Z', 0x2C, FALSE }, /* kVK_ANSI_Z */
197 { 'X', 0x2D, FALSE }, /* kVK_ANSI_X */
198 { 'C', 0x2E, FALSE }, /* kVK_ANSI_C */
199 { 'V', 0x2F, FALSE }, /* kVK_ANSI_V */
200 { VK_OEM_102, 0x56, TRUE }, /* kVK_ISO_Section */
201 { 'B', 0x30, FALSE }, /* kVK_ANSI_B */
202 { 'Q', 0x10, FALSE }, /* kVK_ANSI_Q */
203 { 'W', 0x11, FALSE }, /* kVK_ANSI_W */
204 { 'E', 0x12, FALSE }, /* kVK_ANSI_E */
205 { 'R', 0x13, FALSE }, /* kVK_ANSI_R */
206 { 'Y', 0x15, FALSE }, /* kVK_ANSI_Y */
207 { 'T', 0x14, FALSE }, /* kVK_ANSI_T */
208 { '1', 0x02, FALSE }, /* kVK_ANSI_1 */
209 { '2', 0x03, FALSE }, /* kVK_ANSI_2 */
210 { '3', 0x04, FALSE }, /* kVK_ANSI_3 */
211 { '4', 0x05, FALSE }, /* kVK_ANSI_4 */
212 { '6', 0x07, FALSE }, /* kVK_ANSI_6 */
213 { '5', 0x06, FALSE }, /* kVK_ANSI_5 */
214 { VK_OEM_PLUS, 0x0D, FALSE }, /* kVK_ANSI_Equal */
215 { '9', 0x0A, FALSE }, /* kVK_ANSI_9 */
216 { '7', 0x08, FALSE }, /* kVK_ANSI_7 */
217 { VK_OEM_MINUS, 0x0C, FALSE }, /* kVK_ANSI_Minus */
218 { '8', 0x09, FALSE }, /* kVK_ANSI_8 */
219 { '0', 0x0B, FALSE }, /* kVK_ANSI_0 */
220 { VK_OEM_6, 0x1B, FALSE }, /* kVK_ANSI_RightBracket */
221 { 'O', 0x18, FALSE }, /* kVK_ANSI_O */
222 { 'U', 0x16, FALSE }, /* kVK_ANSI_U */
223 { VK_OEM_4, 0x1A, FALSE }, /* kVK_ANSI_LeftBracket */
224 { 'I', 0x17, FALSE }, /* kVK_ANSI_I */
225 { 'P', 0x19, FALSE }, /* kVK_ANSI_P */
226 { VK_RETURN, 0x1C, TRUE }, /* kVK_Return */
227 { 'L', 0x26, FALSE }, /* kVK_ANSI_L */
228 { 'J', 0x24, FALSE }, /* kVK_ANSI_J */
229 { VK_OEM_7, 0x28, FALSE }, /* kVK_ANSI_Quote */
230 { 'K', 0x25, FALSE }, /* kVK_ANSI_K */
231 { VK_OEM_1, 0x27, FALSE }, /* kVK_ANSI_Semicolon */
232 { VK_OEM_5, 0x2B, FALSE }, /* kVK_ANSI_Backslash */
233 { VK_OEM_COMMA, 0x33, FALSE }, /* kVK_ANSI_Comma */
234 { VK_OEM_2, 0x35, FALSE }, /* kVK_ANSI_Slash */
235 { 'N', 0x31, FALSE }, /* kVK_ANSI_N */
236 { 'M', 0x32, FALSE }, /* kVK_ANSI_M */
237 { VK_OEM_PERIOD, 0x34, FALSE }, /* kVK_ANSI_Period */
238 { VK_TAB, 0x0F, TRUE }, /* kVK_Tab */
239 { VK_SPACE, 0x39, TRUE }, /* kVK_Space */
240 { VK_OEM_3, 0x29, FALSE }, /* kVK_ANSI_Grave */
241 { VK_BACK, 0x0E, TRUE }, /* kVK_Delete */
242 { 0, 0, FALSE }, /* 0x34 unused */
243 { VK_ESCAPE, 0x01, TRUE }, /* kVK_Escape */
244 { VK_RMENU, 0x38 | 0x100, TRUE }, /* kVK_RightCommand */
245 { VK_LMENU, 0x38, TRUE }, /* kVK_Command */
246 { VK_LSHIFT, 0x2A, TRUE }, /* kVK_Shift */
247 { VK_CAPITAL, 0x3A, TRUE }, /* kVK_CapsLock */
248 { 0, 0, FALSE }, /* kVK_Option */
249 { VK_LCONTROL, 0x1D, TRUE }, /* kVK_Control */
250 { VK_RSHIFT, 0x36, TRUE }, /* kVK_RightShift */
251 { 0, 0, FALSE }, /* kVK_RightOption */
252 { VK_RCONTROL, 0x1D | 0x100, TRUE }, /* kVK_RightControl */
253 { 0, 0, FALSE }, /* kVK_Function */
254 { VK_F17, 0x68, TRUE }, /* kVK_F17 */
255 { VK_DECIMAL, 0x53, TRUE }, /* kVK_ANSI_KeypadDecimal */
256 { 0, 0, FALSE }, /* 0x42 unused */
257 { VK_MULTIPLY, 0x37, TRUE }, /* kVK_ANSI_KeypadMultiply */
258 { 0, 0, FALSE }, /* 0x44 unused */
259 { VK_ADD, 0x4E, TRUE }, /* kVK_ANSI_KeypadPlus */
260 { 0, 0, FALSE }, /* 0x46 unused */
261 { VK_OEM_CLEAR, 0x59, TRUE }, /* kVK_ANSI_KeypadClear */
262 { VK_VOLUME_UP, 0 | 0x100, TRUE }, /* kVK_VolumeUp */
263 { VK_VOLUME_DOWN, 0 | 0x100, TRUE }, /* kVK_VolumeDown */
264 { VK_VOLUME_MUTE, 0 | 0x100, TRUE }, /* kVK_Mute */
265 { VK_DIVIDE, 0x35 | 0x100, TRUE }, /* kVK_ANSI_KeypadDivide */
266 { VK_RETURN, 0x1C | 0x100, TRUE }, /* kVK_ANSI_KeypadEnter */
267 { 0, 0, FALSE }, /* 0x4D unused */
268 { VK_SUBTRACT, 0x4A, TRUE }, /* kVK_ANSI_KeypadMinus */
269 { VK_F18, 0x69, TRUE }, /* kVK_F18 */
270 { VK_F19, 0x6A, TRUE }, /* kVK_F19 */
271 { VK_OEM_NEC_EQUAL, 0x0D | 0x100, TRUE }, /* kVK_ANSI_KeypadEquals */
272 { VK_NUMPAD0, 0x52, TRUE }, /* kVK_ANSI_Keypad0 */
273 { VK_NUMPAD1, 0x4F, TRUE }, /* kVK_ANSI_Keypad1 */
274 { VK_NUMPAD2, 0x50, TRUE }, /* kVK_ANSI_Keypad2 */
275 { VK_NUMPAD3, 0x51, TRUE }, /* kVK_ANSI_Keypad3 */
276 { VK_NUMPAD4, 0x4B, TRUE }, /* kVK_ANSI_Keypad4 */
277 { VK_NUMPAD5, 0x4C, TRUE }, /* kVK_ANSI_Keypad5 */
278 { VK_NUMPAD6, 0x4D, TRUE }, /* kVK_ANSI_Keypad6 */
279 { VK_NUMPAD7, 0x47, TRUE }, /* kVK_ANSI_Keypad7 */
280 { VK_F20, 0x6B, TRUE }, /* kVK_F20 */
281 { VK_NUMPAD8, 0x48, TRUE }, /* kVK_ANSI_Keypad8 */
282 { VK_NUMPAD9, 0x49, TRUE }, /* kVK_ANSI_Keypad9 */
283 { 0xFF, 0x7D, TRUE }, /* kVK_JIS_Yen */
284 { 0xC1, 0x73, TRUE }, /* kVK_JIS_Underscore */
285 { VK_SEPARATOR, 0x7E, TRUE }, /* kVK_JIS_KeypadComma */
286 { VK_F5, 0x3F, TRUE }, /* kVK_F5 */
287 { VK_F6, 0x40, TRUE }, /* kVK_F6 */
288 { VK_F7, 0x41, TRUE }, /* kVK_F7 */
289 { VK_F3, 0x3D, TRUE }, /* kVK_F3 */
290 { VK_F8, 0x42, TRUE }, /* kVK_F8 */
291 { VK_F9, 0x43, TRUE }, /* kVK_F9 */
292 { 0xFF, 0x72, TRUE }, /* kVK_JIS_Eisu */
293 { VK_F11, 0x57, TRUE }, /* kVK_F11 */
294 { VK_OEM_RESET, 0x71, TRUE }, /* kVK_JIS_Kana */
295 { VK_F13, 0x64, TRUE }, /* kVK_F13 */
296 { VK_F16, 0x67, TRUE }, /* kVK_F16 */
297 { VK_F14, 0x65, TRUE }, /* kVK_F14 */
298 { 0, 0, FALSE }, /* 0x6C unused */
299 { VK_F10, 0x44, TRUE }, /* kVK_F10 */
300 { 0, 0, FALSE }, /* 0x6E unused */
301 { VK_F12, 0x58, TRUE }, /* kVK_F12 */
302 { 0, 0, FALSE }, /* 0x70 unused */
303 { VK_F15, 0x66, TRUE }, /* kVK_F15 */
304 { VK_INSERT, 0x52 | 0x100, TRUE }, /* kVK_Help */ /* map to Insert */
305 { VK_HOME, 0x47 | 0x100, TRUE }, /* kVK_Home */
306 { VK_PRIOR, 0x49 | 0x100, TRUE }, /* kVK_PageUp */
307 { VK_DELETE, 0x53 | 0x100, TRUE }, /* kVK_ForwardDelete */
308 { VK_F4, 0x3E, TRUE }, /* kVK_F4 */
309 { VK_END, 0x4F | 0x100, TRUE }, /* kVK_End */
310 { VK_F2, 0x3C, TRUE }, /* kVK_F2 */
311 { VK_NEXT, 0x51 | 0x100, TRUE }, /* kVK_PageDown */
312 { VK_F1, 0x3B, TRUE }, /* kVK_F1 */
313 { VK_LEFT, 0x4B | 0x100, TRUE }, /* kVK_LeftArrow */
314 { VK_RIGHT, 0x4D | 0x100, TRUE }, /* kVK_RightArrow */
315 { VK_DOWN, 0x50 | 0x100, TRUE }, /* kVK_DownArrow */
316 { VK_UP, 0x48 | 0x100, TRUE }, /* kVK_UpArrow */
320 static const struct {
321 DWORD vkey;
322 const char *name;
323 } vkey_names[] = {
324 { VK_ADD, "Num +" },
325 { VK_BACK, "Backspace" },
326 { VK_CAPITAL, "Caps Lock" },
327 { VK_CONTROL, "Ctrl" },
328 { VK_DECIMAL, "Num Del" },
329 { VK_DELETE | 0x100, "Delete" },
330 { VK_DIVIDE | 0x100, "Num /" },
331 { VK_DOWN | 0x100, "Down" },
332 { VK_END | 0x100, "End" },
333 { VK_ESCAPE, "Esc" },
334 { VK_F1, "F1" },
335 { VK_F2, "F2" },
336 { VK_F3, "F3" },
337 { VK_F4, "F4" },
338 { VK_F5, "F5" },
339 { VK_F6, "F6" },
340 { VK_F7, "F7" },
341 { VK_F8, "F8" },
342 { VK_F9, "F9" },
343 { VK_F10, "F10" },
344 { VK_F11, "F11" },
345 { VK_F12, "F12" },
346 { VK_F13, "F13" },
347 { VK_F14, "F14" },
348 { VK_F15, "F15" },
349 { VK_F16, "F16" },
350 { VK_F17, "F17" },
351 { VK_F18, "F18" },
352 { VK_F19, "F19" },
353 { VK_F20, "F20" },
354 { VK_F21, "F21" },
355 { VK_F22, "F22" },
356 { VK_F23, "F23" },
357 { VK_F24, "F24" },
358 { VK_HELP | 0x100, "Help" },
359 { VK_HOME | 0x100, "Home" },
360 { VK_INSERT | 0x100, "Insert" },
361 { VK_LCONTROL, "Ctrl" },
362 { VK_LEFT | 0x100, "Left" },
363 { VK_LMENU, "Alt" },
364 { VK_LSHIFT, "Shift" },
365 { VK_LWIN | 0x100, "Win" },
366 { VK_MENU, "Alt" },
367 { VK_MULTIPLY, "Num *" },
368 { VK_NEXT | 0x100, "Page Down" },
369 { VK_NUMLOCK | 0x100, "Num Lock" },
370 { VK_NUMPAD0, "Num 0" },
371 { VK_NUMPAD1, "Num 1" },
372 { VK_NUMPAD2, "Num 2" },
373 { VK_NUMPAD3, "Num 3" },
374 { VK_NUMPAD4, "Num 4" },
375 { VK_NUMPAD5, "Num 5" },
376 { VK_NUMPAD6, "Num 6" },
377 { VK_NUMPAD7, "Num 7" },
378 { VK_NUMPAD8, "Num 8" },
379 { VK_NUMPAD9, "Num 9" },
380 { VK_OEM_CLEAR, "Num Clear" },
381 { VK_OEM_NEC_EQUAL | 0x100, "Num =" },
382 { VK_PRIOR | 0x100, "Page Up" },
383 { VK_RCONTROL | 0x100, "Right Ctrl" },
384 { VK_RETURN, "Return" },
385 { VK_RETURN | 0x100, "Num Enter" },
386 { VK_RIGHT | 0x100, "Right" },
387 { VK_RMENU | 0x100, "Right Alt" },
388 { VK_RSHIFT, "Right Shift" },
389 { VK_RWIN | 0x100, "Right Win" },
390 { VK_SEPARATOR, "Num ," },
391 { VK_SHIFT, "Shift" },
392 { VK_SPACE, "Space" },
393 { VK_SUBTRACT, "Num -" },
394 { VK_TAB, "Tab" },
395 { VK_UP | 0x100, "Up" },
396 { VK_VOLUME_DOWN | 0x100, "Volume Down" },
397 { VK_VOLUME_MUTE | 0x100, "Mute" },
398 { VK_VOLUME_UP | 0x100, "Volume Up" },
401 static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
403 BOOL ret;
404 CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
405 CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, wcslen(string), kCFAllocatorNull);
406 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
407 if (ignore_diacritics)
408 flags |= kCFCompareDiacriticInsensitive;
409 ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
410 CFRelease(s1);
411 CFRelease(s2);
412 return ret;
416 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
417 * string. Returns the length of the string after stripping. */
418 static int strip_apple_private_chars(LPWSTR bufW, int len)
420 int i;
421 for (i = 0; i < len; )
423 if (0xF700 <= bufW[i] && bufW[i] <= 0xF8FF)
425 memmove(&bufW[i], &bufW[i+1], (len - i - 1) * sizeof(bufW[0]));
426 len--;
428 else
429 i++;
431 return len;
434 static struct list layout_list = LIST_INIT( layout_list );
435 struct layout
437 struct list entry;
438 HKL hkl;
439 TISInputSourceRef input_source;
440 BOOL enabled; /* is the input source enabled - ie displayed in the input source selector UI */
443 static pthread_mutex_t layout_list_mutex = PTHREAD_MUTEX_INITIALIZER;
445 int macdrv_layout_list_needs_update = TRUE;
447 static const NLS_LOCALE_HEADER *locale_table;
449 static int compare_locale_names(const WCHAR *n1, const WCHAR *n2)
451 for (;;)
453 WCHAR ch1 = *n1++;
454 WCHAR ch2 = *n2++;
455 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
456 else if (ch1 == '_') ch1 = '-';
457 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
458 else if (ch2 == '_') ch2 = '-';
459 if (!ch1 || ch1 != ch2) return ch1 - ch2;
464 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry(const WCHAR *name)
466 const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
467 const WCHAR *locale_strings;
468 int min = 0, max = locale_table->nb_lcnames - 1;
470 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
471 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
473 while (min <= max)
475 int res, pos = (min + max) / 2;
476 const WCHAR *str = locale_strings + lcnames_index[pos].name;
477 res = compare_locale_names(name, str + 1);
478 if (res < 0) max = pos - 1;
479 else if (res > 0) min = pos + 1;
480 else return &lcnames_index[pos];
482 return NULL;
486 static DWORD get_lcid(CFStringRef lang)
488 const NLS_LOCALE_LCNAME_INDEX *entry;
489 const NLS_LOCALE_DATA *locale;
490 CFRange range;
491 WCHAR str[10];
492 ULONG offset;
494 if (!locale_table)
496 struct
498 UINT ctypes;
499 UINT unknown1;
500 UINT unknown2;
501 UINT unknown3;
502 UINT locales;
503 UINT charmaps;
504 UINT geoids;
505 UINT scripts;
506 } *header;
507 LCID system_lcid;
508 LARGE_INTEGER size;
510 if (NtInitializeNlsFiles((void **)&header, &system_lcid, &size))
512 ERR("NtInitializeNlsFiles failed\n");
513 return 0;
516 if (InterlockedCompareExchangePointer((void **)&locale_table,
517 (char *)header + header->locales, NULL))
518 NtUnmapViewOfSection(GetCurrentProcess(), header);
521 range.location = 0;
522 range.length = min(CFStringGetLength(lang), ARRAY_SIZE(str) - 1);
523 CFStringGetCharacters(lang, range, str);
524 str[range.length] = 0;
526 if (!(entry = find_lcname_entry(str)))
528 ERR("%s not found\n", debugstr_w(str));
529 return 0;
532 offset = locale_table->locales_offset + entry->idx * locale_table->locale_size;
533 locale = (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
534 return locale->inotneutral ? entry->id : locale->idefaultlanguage;
537 static HKL get_hkl(CFStringRef lang, CFStringRef type)
539 ULONG_PTR lcid = get_lcid(lang);
540 struct layout *layout;
542 /* Look for the last occurrence of this lcid in the list and if
543 present use that value + 0x10000 */
544 LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry)
546 ULONG_PTR hkl = HandleToUlong(layout->hkl);
548 if (LOWORD(hkl) == lcid)
550 lcid = (hkl & ~0xe0000000) + 0x10000;
551 break;
555 if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000;
557 return (HKL)lcid;
560 /******************************************************************
561 * get_layout_from_source
563 * Must be called while holding the layout_list_mutex.
564 * Note, returned layout may not currently be enabled.
566 static struct layout *get_layout_from_source(TISInputSourceRef input)
568 struct layout *ret = NULL, *layout;
570 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
572 if (CFEqual(input, layout->input_source))
574 ret = layout;
575 break;
578 return ret;
581 /***********************************************************************
582 * update_layout_list
584 * Must be called while holding the layout_list_mutex
586 * If an input source has been disabled (ie. removed from the UI) its
587 * entry remains in the layout list but is marked as such and is not
588 * enumerated by GetKeyboardLayoutList. This is to ensure the
589 * HKL <-> input source mapping is unique.
591 static void update_layout_list(void)
593 CFArrayRef sources;
594 struct layout *layout;
595 int i;
597 if (!InterlockedExchange((LONG *)&macdrv_layout_list_needs_update, FALSE)) return;
599 sources = macdrv_create_input_source_list();
601 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
602 layout->enabled = FALSE;
604 for (i = 0; i < CFArrayGetCount(sources); i++)
606 CFDictionaryRef dict = CFArrayGetValueAtIndex(sources, i);
607 TISInputSourceRef input = (TISInputSourceRef)CFDictionaryGetValue(dict, macdrv_input_source_input_key);
608 layout = get_layout_from_source(input);
609 if (!layout)
611 CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key);
612 CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key);
614 layout = malloc(sizeof(*layout));
615 layout->input_source = (TISInputSourceRef)CFRetain(input);
616 layout->hkl = get_hkl(lang, type);
618 list_add_tail(&layout_list, &layout->entry);
619 TRACE("adding new layout %p\n", layout->hkl);
621 else
622 TRACE("enabling already existing layout %p\n", layout->hkl);
624 layout->enabled = TRUE;
627 CFRelease(sources);
630 /***********************************************************************
631 * macdrv_get_hkl_from_source
633 * Find the HKL associated with a given input source.
635 HKL macdrv_get_hkl_from_source(TISInputSourceRef input)
637 struct layout *layout;
638 HKL ret = 0;
640 pthread_mutex_lock(&layout_list_mutex);
642 update_layout_list();
643 layout = get_layout_from_source(input);
644 if (layout) ret = layout->hkl;
646 pthread_mutex_unlock(&layout_list_mutex);
648 return ret;
652 /***********************************************************************
653 * macdrv_compute_keyboard_layout
655 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
657 int keyc;
658 WCHAR vkey;
659 const UCKeyboardLayout *uchr;
660 const UInt32 modifier_combos[] = {
662 shiftKey >> 8,
663 cmdKey >> 8,
664 (shiftKey | cmdKey) >> 8,
665 optionKey >> 8,
666 (shiftKey | optionKey) >> 8,
668 UniChar map[128][ARRAY_SIZE(modifier_combos)][4 + 1];
669 int combo;
670 BYTE vkey_used[256];
671 int ignore_diacritics;
672 static const struct {
673 WCHAR wchar;
674 DWORD vkey;
675 } symbol_vkeys[] = {
676 { '-', VK_OEM_MINUS },
677 { '+', VK_OEM_PLUS },
678 { '_', VK_OEM_MINUS },
679 { ',', VK_OEM_COMMA },
680 { '.', VK_OEM_PERIOD },
681 { '=', VK_OEM_PLUS },
682 { '>', VK_OEM_PERIOD },
683 { '<', VK_OEM_COMMA },
684 { '|', VK_OEM_5 },
685 { '\\', VK_OEM_5 },
686 { '`', VK_OEM_3 },
687 { '[', VK_OEM_4 },
688 { '~', VK_OEM_3 },
689 { '?', VK_OEM_2 },
690 { ']', VK_OEM_6 },
691 { '/', VK_OEM_2 },
692 { ':', VK_OEM_1 },
693 { '}', VK_OEM_6 },
694 { '{', VK_OEM_4 },
695 { ';', VK_OEM_1 },
696 { '\'', VK_OEM_7 },
697 { ':', VK_OEM_PERIOD },
698 { ';', VK_OEM_COMMA },
699 { '"', VK_OEM_7 },
700 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
701 { '\'', VK_OEM_2 },
702 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
703 { '*', VK_OEM_PLUS },
704 { 0x00B4, VK_OEM_7 },
705 { '`', VK_OEM_4 },
706 { '[', VK_OEM_6 },
707 { '/', VK_OEM_5 },
708 { '^', VK_OEM_6 },
709 { '*', VK_OEM_2 },
710 { '{', VK_OEM_6 },
711 { '~', VK_OEM_1 },
712 { '?', VK_OEM_PLUS },
713 { '?', VK_OEM_4 },
714 { 0x00B4, VK_OEM_3 },
715 { '?', VK_OEM_COMMA },
716 { '~', VK_OEM_PLUS },
717 { ']', VK_OEM_4 },
718 { '\'', VK_OEM_3 },
719 { 0x00A7, VK_OEM_7 },
721 int i;
723 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
724 contiguous ranges. */
725 static const struct {
726 WORD first, last;
727 } vkey_ranges[] = {
728 { 'A', 'Z' },
729 { '0', '9' },
730 { VK_OEM_1, VK_OEM_3 },
731 { VK_OEM_4, VK_ICO_CLEAR },
732 { 0xe9, 0xf5 },
733 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
734 { VK_F1, VK_F24 },
735 { 0, 0 }
737 int vkey_range;
739 if (!thread_data->keyboard_layout_uchr)
741 ERR("no keyboard layout UCHR data\n");
742 return;
745 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
746 memset(vkey_used, 0, sizeof(vkey_used));
748 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
750 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
751 if (default_map[keyc].fixed)
753 vkey = default_map[keyc].vkey;
754 thread_data->keyc2vkey[keyc] = vkey;
755 vkey_used[vkey] = 1;
756 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
760 if (thread_data->iso_keyboard)
762 /* In almost all cases, the Mac key codes indicate a physical key position
763 and this corresponds nicely to Win32 scan codes. However, the Mac key
764 codes differ in one case between ANSI and ISO keyboards. For ANSI
765 keyboards, the key to the left of the digits and above the Tab key
766 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
767 some position produces kVK_ISO_Section. The additional key on ISO
768 keyboards, the one to the right of the left Shift key, produces
769 kVK_ANSI_Grave, which is just weird.
771 Since we want the key in that upper left corner to always produce the
772 same scan code (0x29), we need to swap the scan codes of those two
773 Mac key codes for ISO keyboards. */
774 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
775 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
776 thread_data->keyc2scan[kVK_ISO_Section] = temp;
779 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
781 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
782 memset(map, 0, sizeof(map));
783 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
785 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
786 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
788 TRACE("keyc 0x%04x: ", keyc);
790 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
792 UInt32 deadKeyState;
793 UniCharCount len;
794 OSStatus status;
796 deadKeyState = 0;
797 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
798 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
799 &deadKeyState, ARRAY_SIZE(map[keyc][combo]) - 1, &len, map[keyc][combo]);
800 if (status != noErr)
801 map[keyc][combo][0] = 0;
803 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
806 TRACE("\n");
809 /* First try to match key codes to the vkeys for the letters A through Z.
810 Try unmodified first, then with various modifier combinations in succession.
811 On the first pass, try to get a match lacking diacritical marks. On the
812 second pass, accept matches with diacritical marks. */
813 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
815 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
817 for (vkey = 'A'; vkey <= 'Z'; vkey++)
819 if (vkey_used[vkey])
820 continue;
822 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
824 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
825 continue;
827 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
829 thread_data->keyc2vkey[keyc] = vkey;
830 vkey_used[vkey] = 1;
831 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
832 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
833 break;
840 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
841 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
843 for (vkey = '0'; vkey <= '9'; vkey++)
845 if (vkey_used[vkey])
846 continue;
848 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
850 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
851 continue;
853 if (char_matches_string(vkey, map[keyc][combo], FALSE))
855 thread_data->keyc2vkey[keyc] = vkey;
856 vkey_used[vkey] = 1;
857 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
858 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
859 break;
865 /* Now try to match key codes for certain common punctuation characters to
866 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
867 for (i = 0; i < ARRAY_SIZE(symbol_vkeys); i++)
869 vkey = symbol_vkeys[i].vkey;
871 if (vkey_used[vkey])
872 continue;
874 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
876 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
878 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
879 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
880 continue;
882 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
884 thread_data->keyc2vkey[keyc] = vkey;
885 vkey_used[vkey] = 1;
886 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
887 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
888 break;
892 if (vkey_used[vkey])
893 break;
897 /* For those key codes still without a vkey, try to use the default vkey
898 from the default map, if it's still available. */
899 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
901 unsigned int vkey = default_map[keyc].vkey;
903 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
904 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
906 if (!vkey_used[vkey])
908 thread_data->keyc2vkey[keyc] = vkey;
909 vkey_used[vkey] = 1;
910 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
914 /* For any unassigned key codes which would map to a letter in the default
915 map, but whose normal letter vkey wasn't available, try to find a
916 different letter. */
917 vkey = 'A';
918 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
920 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
921 continue; /* not a letter in ANSI layout */
922 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
923 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
925 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
926 if (vkey <= 'Z')
928 thread_data->keyc2vkey[keyc] = vkey;
929 vkey_used[vkey] = 1;
930 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
932 else
933 break; /* no more unused letter vkeys, so stop trying */
936 /* Same thing but with the digits. */
937 vkey = '0';
938 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
940 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
941 continue; /* not a digit in ANSI layout */
942 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
943 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
945 while (vkey <= '9' && vkey_used[vkey]) vkey++;
946 if (vkey <= '9')
948 thread_data->keyc2vkey[keyc] = vkey;
949 vkey_used[vkey] = 1;
950 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
952 else
953 break; /* no more unused digit vkeys, so stop trying */
956 /* Last chance. Assign any available vkey. */
957 vkey_range = 0;
958 vkey = vkey_ranges[vkey_range].first;
959 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
961 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
962 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
964 while (vkey && vkey_used[vkey])
966 if (vkey == vkey_ranges[vkey_range].last)
968 vkey_range++;
969 vkey = vkey_ranges[vkey_range].first;
971 else
972 vkey++;
975 if (!vkey)
977 WARN("No more vkeys available!\n");
978 break;
981 thread_data->keyc2vkey[keyc] = vkey;
982 vkey_used[vkey] = 1;
983 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
988 /***********************************************************************
989 * macdrv_send_keyboard_input
991 static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, unsigned int flags, unsigned int time)
993 INPUT input;
995 TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
997 input.type = INPUT_KEYBOARD;
998 input.ki.wVk = vkey;
999 input.ki.wScan = scan;
1000 input.ki.dwFlags = flags;
1001 input.ki.time = time;
1002 input.ki.dwExtraInfo = 0;
1004 __wine_send_input(hwnd, &input, NULL);
1008 /***********************************************************************
1009 * get_async_key_state
1011 static BOOL get_async_key_state(BYTE state[256])
1013 BOOL ret;
1015 SERVER_START_REQ(get_key_state)
1017 req->async = 1;
1018 req->key = -1;
1019 wine_server_set_reply(req, state, 256);
1020 ret = !wine_server_call(req);
1022 SERVER_END_REQ;
1023 return ret;
1027 /***********************************************************************
1028 * update_modifier_state
1030 static void update_modifier_state(unsigned int modifier, unsigned int modifiers, const BYTE *keystate,
1031 WORD vkey, WORD alt_vkey, WORD scan, WORD alt_scan,
1032 DWORD event_time, BOOL restore)
1034 int key_pressed = (modifiers & modifier) != 0;
1035 int vkey_pressed = (keystate[vkey] & 0x80) || (keystate[alt_vkey] & 0x80);
1036 DWORD flags;
1038 if (key_pressed != vkey_pressed)
1040 if (key_pressed)
1042 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
1043 if (restore)
1044 flags |= KEYEVENTF_KEYUP;
1046 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff, flags, event_time);
1048 else
1050 flags = restore ? 0 : KEYEVENTF_KEYUP;
1052 if (keystate[vkey] & 0x80)
1054 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff,
1055 flags | ((scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
1056 event_time);
1058 if (keystate[alt_vkey] & 0x80)
1060 macdrv_send_keyboard_input(NULL, alt_vkey, alt_scan & 0xff,
1061 flags | ((alt_scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
1062 event_time);
1069 /***********************************************************************
1070 * macdrv_key_event
1072 * Handler for KEY_PRESS and KEY_RELEASE events.
1074 void macdrv_key_event(HWND hwnd, const macdrv_event *event)
1076 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1077 WORD vkey, scan;
1078 DWORD flags;
1080 TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1081 hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
1082 event->key.keycode, event->key.modifiers);
1084 thread_data->last_modifiers = event->key.modifiers;
1086 if (event->key.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1088 vkey = thread_data->keyc2vkey[event->key.keycode];
1089 scan = thread_data->keyc2scan[event->key.keycode];
1091 else
1092 vkey = scan = 0;
1094 TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1095 event->key.keycode, vkey, scan);
1097 if (!vkey) return;
1099 flags = 0;
1100 if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
1101 if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
1103 macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
1107 /***********************************************************************
1108 * macdrv_keyboard_changed
1110 * Handler for KEYBOARD_CHANGED events.
1112 void macdrv_keyboard_changed(const macdrv_event *event)
1114 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1116 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
1117 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
1119 if (thread_data->keyboard_layout_uchr)
1120 CFRelease(thread_data->keyboard_layout_uchr);
1121 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
1122 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
1123 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
1124 thread_data->active_keyboard_layout = macdrv_get_hkl_from_source(event->keyboard_changed.input_source);
1125 thread_data->dead_key_state = 0;
1127 macdrv_compute_keyboard_layout(thread_data);
1129 NtUserActivateKeyboardLayout(thread_data->active_keyboard_layout, 0);
1131 send_message(get_active_window(), WM_CANCELMODE, 0, 0);
1135 /***********************************************************************
1136 * macdrv_hotkey_press
1138 * Handler for HOTKEY_PRESS events.
1140 void macdrv_hotkey_press(const macdrv_event *event)
1142 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1144 TRACE_(key)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
1145 event->hotkey_press.vkey, event->hotkey_press.mod_flags, event->hotkey_press.keycode,
1146 event->hotkey_press.time_ms);
1148 if (event->hotkey_press.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1150 WORD scan = thread_data->keyc2scan[event->hotkey_press.keycode];
1151 BYTE keystate[256];
1152 BOOL got_keystate;
1153 DWORD flags;
1155 if ((got_keystate = get_async_key_state(keystate)))
1157 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1158 0x38, 0x138, event->hotkey_press.time_ms, FALSE);
1159 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1160 0x1D, 0x11D, event->hotkey_press.time_ms, FALSE);
1161 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1162 0x2A, 0x36, event->hotkey_press.time_ms, FALSE);
1163 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1164 0x15B, 0x15C, event->hotkey_press.time_ms, FALSE);
1167 activate_on_following_focus();
1169 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
1170 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1171 flags, event->key.time_ms);
1172 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1173 flags | KEYEVENTF_KEYUP, event->key.time_ms);
1175 if (got_keystate)
1177 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1178 0x38, 0x138, event->hotkey_press.time_ms, TRUE);
1179 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1180 0x1D, 0x11D, event->hotkey_press.time_ms, TRUE);
1181 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1182 0x2A, 0x36, event->hotkey_press.time_ms, TRUE);
1183 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1184 0x15B, 0x15C, event->hotkey_press.time_ms, TRUE);
1190 /***********************************************************************
1191 * macdrv_process_text_input
1193 NTSTATUS macdrv_ime_process_text_input(void *arg)
1195 struct process_text_input_params *params = arg;
1196 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1197 const BYTE *key_state = params->key_state;
1198 unsigned int flags;
1199 int keyc;
1201 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", params->vkey, params->scan,
1202 params->repeat, params->himc);
1204 flags = thread_data->last_modifiers;
1205 if (key_state[VK_SHIFT] & 0x80)
1206 flags |= NX_SHIFTMASK;
1207 else
1208 flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK);
1209 if (key_state[VK_CAPITAL] & 0x01)
1210 flags |= NX_ALPHASHIFTMASK;
1211 else
1212 flags &= ~NX_ALPHASHIFTMASK;
1213 if (key_state[VK_CONTROL] & 0x80)
1214 flags |= NX_CONTROLMASK;
1215 else
1216 flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK);
1217 if (key_state[VK_MENU] & 0x80)
1218 flags |= NX_COMMANDMASK;
1219 else
1220 flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
1222 /* Find the Mac keycode corresponding to the scan code */
1223 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1224 if (thread_data->keyc2vkey[keyc] == params->vkey) break;
1226 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1228 *params->done = -1;
1229 return 0;
1232 TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc);
1234 macdrv_send_text_input_event(((params->scan & 0x8000) == 0), flags, params->repeat, keyc,
1235 params->himc, params->done);
1236 return 0;
1240 /***********************************************************************
1241 * ActivateKeyboardLayout (MACDRV.@)
1243 BOOL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
1245 BOOL ret = FALSE;
1246 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1247 struct layout *layout;
1249 TRACE("hkl %p flags %04x\n", hkl, flags);
1251 if (hkl == thread_data->active_keyboard_layout)
1252 return TRUE;
1254 pthread_mutex_lock(&layout_list_mutex);
1255 update_layout_list();
1257 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1259 if (layout->hkl == hkl)
1261 if (macdrv_select_input_source(layout->input_source))
1263 ret = TRUE;
1264 if (thread_data->keyboard_layout_uchr)
1265 CFRelease(thread_data->keyboard_layout_uchr);
1267 macdrv_get_input_source_info(&thread_data->keyboard_layout_uchr, &thread_data->keyboard_type,
1268 &thread_data->iso_keyboard, NULL);
1269 thread_data->active_keyboard_layout = hkl;
1270 thread_data->dead_key_state = 0;
1272 macdrv_compute_keyboard_layout(thread_data);
1274 break;
1277 pthread_mutex_unlock(&layout_list_mutex);
1279 return ret;
1283 /***********************************************************************
1284 * Beep (MACDRV.@)
1286 void macdrv_Beep(void)
1288 macdrv_beep();
1292 /***********************************************************************
1293 * GetKeyNameText (MACDRV.@)
1295 INT macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
1297 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1298 int scan, keyc;
1300 scan = (lparam >> 16) & 0x1FF;
1301 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2scan); keyc++)
1303 if (thread_data->keyc2scan[keyc] == scan)
1305 static const WCHAR dead[] = {' ','d','e','a','d',0};
1306 const UCKeyboardLayout *uchr;
1307 UInt32 deadKeyState = 0;
1308 UniCharCount len;
1309 OSStatus status;
1310 unsigned int vkey;
1311 int i;
1313 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1314 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
1315 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
1316 if (status != noErr)
1317 len = 0;
1318 if (len && buffer[0] > 32)
1319 buffer[len] = 0;
1321 vkey = thread_data->keyc2vkey[keyc];
1322 if (lparam & (1 << 25))
1324 /* Caller doesn't care about distinctions between left and
1325 right keys. */
1326 switch (vkey)
1328 case VK_LSHIFT:
1329 case VK_RSHIFT:
1330 vkey = VK_SHIFT; break;
1331 case VK_LCONTROL:
1332 case VK_RCONTROL:
1333 vkey = VK_CONTROL; break;
1334 case VK_LMENU:
1335 case VK_RMENU:
1336 vkey = VK_MENU; break;
1340 if (scan & 0x100) vkey |= 0x100;
1342 for (i = 0; i < ARRAY_SIZE(vkey_names); i++)
1344 if (vkey_names[i].vkey == vkey)
1346 len = min(strlen(vkey_names[i].name) + 1, size);
1347 ascii_to_unicode(buffer, vkey_names[i].name, len);
1348 if (len) buffer[--len] = 0;
1349 break;
1353 if (!len)
1355 char name[16];
1356 len = sprintf(name, "Key 0x%02x", vkey);
1357 len = min(len + 1, size);
1358 ascii_to_unicode(buffer, name, len);
1359 if (len) buffer[--len] = 0;
1362 if (!len)
1363 break;
1365 if (status == noErr && deadKeyState)
1367 lstrcpynW(buffer + len, dead, size - len);
1368 len = wcslen(buffer);
1371 TRACE("lparam 0x%08x -> %s\n", (unsigned int)lparam, debugstr_w(buffer));
1372 return len;
1376 WARN("found no name for lparam 0x%08x\n", (unsigned int)lparam);
1377 return 0;
1381 /***********************************************************************
1382 * GetKeyboardLayoutList (MACDRV.@)
1384 UINT macdrv_GetKeyboardLayoutList(INT size, HKL *list)
1386 int count = 0;
1387 struct layout *layout;
1389 TRACE("%d, %p\n", size, list);
1391 pthread_mutex_lock(&layout_list_mutex);
1393 update_layout_list();
1395 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1397 if (!layout->enabled) continue;
1398 if (list)
1400 if (count >= size) break;
1401 list[count] = layout->hkl;
1402 TRACE("\t%d: %p\n", count, list[count]);
1404 count++;
1406 pthread_mutex_unlock(&layout_list_mutex);
1408 TRACE("returning %d\n", count);
1409 return count;
1413 /***********************************************************************
1414 * MapVirtualKeyEx (MACDRV.@)
1416 UINT macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1418 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1419 UINT ret = 0;
1420 int keyc;
1422 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1424 switch (wMapType)
1426 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1427 case MAPVK_VK_TO_VSC_EX:
1428 switch (wCode)
1430 case VK_SHIFT: wCode = VK_LSHIFT; break;
1431 case VK_CONTROL: wCode = VK_LCONTROL; break;
1432 case VK_MENU: wCode = VK_LMENU; break;
1435 /* vkey -> keycode -> scan */
1436 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1438 if (thread_data->keyc2vkey[keyc] == wCode)
1440 ret = thread_data->keyc2scan[keyc] & 0xFF;
1441 break;
1445 /* set scan code prefix */
1446 if (wMapType == MAPVK_VK_TO_VSC_EX &&
1447 (wCode == VK_RCONTROL || wCode == VK_RMENU))
1448 ret |= 0xe000;
1449 break;
1451 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1452 case MAPVK_VSC_TO_VK_EX:
1453 /* scan -> keycode -> vkey */
1454 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1455 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1457 ret = thread_data->keyc2vkey[keyc];
1458 /* Only stop if it's not a numpad vkey; otherwise keep
1459 looking for a potential better vkey. */
1460 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1461 break;
1464 if (wMapType == MAPVK_VSC_TO_VK)
1465 switch (ret)
1467 case VK_LSHIFT:
1468 case VK_RSHIFT:
1469 ret = VK_SHIFT; break;
1470 case VK_LCONTROL:
1471 case VK_RCONTROL:
1472 ret = VK_CONTROL; break;
1473 case VK_LMENU:
1474 case VK_RMENU:
1475 ret = VK_MENU; break;
1478 break;
1480 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1482 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1483 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1484 const UCKeyboardLayout *uchr;
1485 UniChar s[10];
1486 OSStatus status;
1487 UInt32 deadKeyState;
1488 UniCharCount len;
1489 BOOL deadKey = FALSE;
1491 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1492 (VK_F1 <= wCode && wCode <= VK_F24))
1493 break;
1495 if (!thread_data || !thread_data->keyboard_layout_uchr)
1497 WARN("No keyboard layout uchr data\n");
1498 break;
1501 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1503 /* Find the Mac keycode corresponding to the vkey */
1504 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1505 if (thread_data->keyc2vkey[keyc] == wCode) break;
1507 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1509 WARN("Unknown virtual key %X\n", wCode);
1510 break;
1513 TRACE("Found keycode %u\n", keyc);
1515 deadKeyState = 0;
1516 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1517 thread_data->keyboard_type, 0, &deadKeyState, ARRAY_SIZE(s), &len, s);
1518 if (status == noErr && !len && deadKeyState)
1520 deadKey = TRUE;
1521 deadKeyState = 0;
1522 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1523 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1524 &deadKeyState, ARRAY_SIZE(s), &len, s);
1527 if (status == noErr && len)
1528 ret = RtlUpcaseUnicodeChar(s[0]) | (deadKey ? 0x80000000 : 0);
1530 break;
1532 default: /* reserved */
1533 FIXME("Unknown wMapType %d\n", wMapType);
1534 break;
1537 TRACE("returning 0x%04x\n", ret);
1538 return ret;
1542 /***********************************************************************
1543 * RegisterHotKey (MACDRV.@)
1545 BOOL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey)
1547 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1548 unsigned int keyc, modifiers = 0;
1549 int ret;
1551 TRACE_(key)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd, mod_flags, vkey);
1553 /* Find the Mac keycode corresponding to the vkey */
1554 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1555 if (thread_data->keyc2vkey[keyc] == vkey) break;
1557 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1559 WARN_(key)("ignoring unknown virtual key 0x%04x\n", vkey);
1560 return TRUE;
1563 if (mod_flags & MOD_ALT) modifiers |= cmdKey;
1564 if (mod_flags & MOD_CONTROL) modifiers |= controlKey;
1565 if (mod_flags & MOD_SHIFT) modifiers |= shiftKey;
1566 if (mod_flags & MOD_WIN)
1568 WARN_(key)("MOD_WIN not supported; ignoring\n");
1569 return TRUE;
1572 ret = macdrv_register_hot_key(thread_data->queue, vkey, mod_flags, keyc, modifiers);
1573 TRACE_(key)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc, modifiers, ret);
1575 if (ret == MACDRV_HOTKEY_ALREADY_REGISTERED)
1576 RtlSetLastWin32Error(ERROR_HOTKEY_ALREADY_REGISTERED);
1577 else if (ret != MACDRV_HOTKEY_SUCCESS)
1578 RtlSetLastWin32Error(ERROR_GEN_FAILURE);
1580 return ret == MACDRV_HOTKEY_SUCCESS;
1584 /***********************************************************************
1585 * ToUnicodeEx (MACDRV.@)
1587 * The ToUnicode function translates the specified virtual-key code and keyboard
1588 * state to the corresponding Windows character or characters.
1590 * If the specified key is a dead key, the return value is negative. Otherwise,
1591 * it is one of the following values:
1592 * Value Meaning
1593 * -1 The specified virtual key is a dead-key. If possible, the
1594 * non-combining form of the dead character is written to bufW.
1595 * 0 The specified virtual key has no translation for the current
1596 * state of the keyboard.
1597 * 1 One Windows character was copied to the buffer.
1598 * 2 or more Multiple characters were copied to the buffer. This usually
1599 * happens when a dead-key character (accent or diacritic) stored
1600 * in the keyboard layout cannot be composed with the specified
1601 * virtual key to form a single character.
1604 INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1605 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1607 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1608 INT ret = 0;
1609 int keyc;
1610 BOOL is_menu = (flags & 0x1);
1611 int status;
1612 const UCKeyboardLayout *uchr;
1613 UInt16 keyAction;
1614 UInt32 modifierKeyState;
1615 OptionBits options;
1616 UInt32 deadKeyState, savedDeadKeyState;
1617 UniCharCount len;
1618 BOOL dead = FALSE;
1620 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1621 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1623 if (!virtKey)
1624 goto done;
1626 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1627 modifier key press. We want it to effectively ignore modifier key
1628 presses. I think that one isn't supposed to call it at all for modifier
1629 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1630 are different event types than key up/down events. */
1631 switch (virtKey)
1633 case VK_SHIFT:
1634 case VK_CONTROL:
1635 case VK_MENU:
1636 case VK_CAPITAL:
1637 case VK_LSHIFT:
1638 case VK_RSHIFT:
1639 case VK_LCONTROL:
1640 case VK_RCONTROL:
1641 case VK_LMENU:
1642 case VK_RMENU:
1643 goto done;
1646 /* There are a number of key combinations for which Windows does not
1647 produce characters, but Mac keyboard layouts may. Eat them. Do this
1648 here to avoid the expense of UCKeyTranslate() but also because these
1649 keys shouldn't terminate dead key sequences. */
1650 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1651 goto done;
1653 /* Shift + <non-digit keypad keys>. */
1654 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1655 goto done;
1657 if (lpKeyState[VK_CONTROL] & 0x80)
1659 /* Control-Tab, with or without other modifiers. */
1660 if (virtKey == VK_TAB)
1661 goto done;
1663 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1664 for these keys. */
1665 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1667 switch (virtKey)
1669 case VK_CANCEL:
1670 case VK_BACK:
1671 case VK_ESCAPE:
1672 case VK_SPACE:
1673 case VK_RETURN:
1674 goto done;
1679 if (thread_data->keyboard_layout_uchr)
1680 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1681 else
1682 uchr = NULL;
1684 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1686 modifierKeyState = 0;
1687 if (lpKeyState[VK_SHIFT] & 0x80)
1688 modifierKeyState |= (shiftKey >> 8);
1689 if (lpKeyState[VK_CAPITAL] & 0x01)
1690 modifierKeyState |= (alphaLock >> 8);
1691 if (lpKeyState[VK_CONTROL] & 0x80)
1692 modifierKeyState |= (controlKey >> 8);
1693 if (lpKeyState[VK_MENU] & 0x80)
1694 modifierKeyState |= (cmdKey >> 8);
1695 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1696 modifierKeyState |= (optionKey >> 8);
1698 /* Find the Mac keycode corresponding to the vkey */
1699 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1700 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1702 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1704 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1705 goto done;
1708 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1709 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1711 if (is_menu)
1713 if (keyAction == kUCKeyActionUp)
1714 goto done;
1716 options = kUCKeyTranslateNoDeadKeysMask;
1717 deadKeyState = 0;
1719 else
1721 options = 0;
1722 deadKeyState = thread_data->dead_key_state;
1724 savedDeadKeyState = deadKeyState;
1725 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1726 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1727 &len, bufW);
1728 if (status != noErr)
1730 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1731 goto done;
1733 if (!is_menu)
1735 if (keyAction != kUCKeyActionUp && len > 0 && deadKeyState == thread_data->dead_key_state)
1736 thread_data->dead_key_state = 0;
1737 else
1738 thread_data->dead_key_state = deadKeyState;
1740 if (keyAction == kUCKeyActionUp)
1741 goto done;
1744 if (len == 0 && deadKeyState)
1746 /* Repeat the translation, but disabling dead-key generation to
1747 learn which dead key it was. */
1748 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1749 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1750 &savedDeadKeyState, bufW_size, &len, bufW);
1751 if (status != noErr)
1753 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1754 goto done;
1757 dead = TRUE;
1760 if (len > 0)
1761 len = strip_apple_private_chars(bufW, len);
1763 if (dead && len > 0) ret = -1;
1764 else ret = len;
1766 /* Control-Return produces line feed instead of carriage return. */
1767 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1769 int i;
1770 for (i = 0; i < len; i++)
1771 if (bufW[i] == '\r')
1772 bufW[i] = '\n';
1775 done:
1776 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1777 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1778 if (1 <= ret && ret < bufW_size)
1779 bufW[ret] = 0;
1781 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, abs(ret)));
1782 return ret;
1786 /***********************************************************************
1787 * UnregisterHotKey (MACDRV.@)
1789 void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey)
1791 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1793 TRACE_(key)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd, modifiers, vkey);
1795 if (thread_data)
1796 macdrv_unregister_hot_key(thread_data->queue, vkey, modifiers);
1800 /***********************************************************************
1801 * VkKeyScanEx (MACDRV.@)
1803 * Note: Windows ignores HKL parameter and uses current active layout instead
1805 SHORT macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1807 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1808 SHORT ret = -1;
1809 int state;
1810 const UCKeyboardLayout *uchr;
1812 TRACE("%04x, %p\n", wChar, hkl);
1814 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1815 if (!uchr)
1817 TRACE("no keyboard layout UCHR data; returning -1\n");
1818 return -1;
1821 for (state = 0; state < 8; state++)
1823 UInt32 modifierKeyState = 0;
1824 int keyc;
1826 if (state & 1)
1827 modifierKeyState |= (shiftKey >> 8);
1828 if ((state & 6) == 6)
1829 modifierKeyState |= (optionKey >> 8);
1830 else
1832 if (state & 2)
1833 modifierKeyState |= (controlKey >> 8);
1834 if (state & 4)
1835 modifierKeyState |= (cmdKey >> 8);
1838 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1840 UInt32 deadKeyState = 0;
1841 UniChar uchar;
1842 UniCharCount len;
1843 OSStatus status;
1845 if (!thread_data->keyc2vkey[keyc]) continue;
1847 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1848 thread_data->keyboard_type, 0, &deadKeyState,
1849 1, &len, &uchar);
1850 if (status == noErr && len == 1 && uchar == wChar)
1852 WORD vkey = thread_data->keyc2vkey[keyc];
1854 ret = vkey | (state << 8);
1855 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1856 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1857 keyc == kVK_ANSI_KeypadEquals)
1859 /* Keep searching for a non-numpad match, which is preferred. */
1861 else
1862 goto done;
1867 done:
1868 TRACE(" -> 0x%04x\n", ret);
1869 return ret;