include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winemac.drv / keyboard.c
blob0431cc22150be73f04822a396e2d30b53e7dba08
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" },
402 static const struct {
403 WCHAR wchar;
404 const char *name;
405 } dead_key_names[] = {
406 { '^', "CIRCUMFLEX ACCENT" },
407 { '`', "GRAVE ACCENT" },
408 { 0x00B4, "ACUTE ACCENT" },
409 { '~', "TILDE" },
410 { 0x00A8, "DIAERESIS" },
411 { 0x00B8, "CEDILLA" },
412 { 0x02D8, "BREVE" },
413 { 0x02D9, "DOT ABOVE" },
414 { 0x00AF, "MACRON" },
415 { 0x02DA, "RING ABOVE" },
416 { 0x02DB, "OGONEK" },
417 { 0x02DC, "SMALL TILDE" },
418 { 0x02DD, "DOUBLE ACUTE ACCENT" },
422 static Boolean char_matches_string(WCHAR wchar, UniChar *string, CollatorRef collatorRef)
424 Boolean equivalent;
425 OSStatus status;
427 status = UCCompareText(collatorRef, (UniChar*)&wchar, 1, string, wcslen(string), &equivalent, NULL);
428 if (status != noErr)
430 WARN("Failed to compare %s to %s\n", debugstr_wn(&wchar, 1), debugstr_w(string));
431 return FALSE;
433 return equivalent;
437 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
438 * string. Returns the length of the string after stripping. */
439 static int strip_apple_private_chars(LPWSTR bufW, int len)
441 int i;
442 for (i = 0; i < len; )
444 if (0xF700 <= bufW[i] && bufW[i] <= 0xF8FF)
446 memmove(&bufW[i], &bufW[i+1], (len - i - 1) * sizeof(bufW[0]));
447 len--;
449 else
450 i++;
452 return len;
455 static struct list layout_list = LIST_INIT( layout_list );
456 struct layout
458 struct list entry;
459 HKL hkl;
460 TISInputSourceRef input_source;
461 BOOL enabled; /* is the input source enabled - ie displayed in the input source selector UI */
464 static pthread_mutex_t layout_list_mutex = PTHREAD_MUTEX_INITIALIZER;
466 int macdrv_layout_list_needs_update = TRUE;
468 static const NLS_LOCALE_HEADER *locale_table;
470 static int compare_locale_names(const WCHAR *n1, const WCHAR *n2)
472 for (;;)
474 WCHAR ch1 = *n1++;
475 WCHAR ch2 = *n2++;
476 if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A';
477 else if (ch1 == '_') ch1 = '-';
478 if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A';
479 else if (ch2 == '_') ch2 = '-';
480 if (!ch1 || ch1 != ch2) return ch1 - ch2;
485 static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry(const WCHAR *name)
487 const NLS_LOCALE_LCNAME_INDEX *lcnames_index;
488 const WCHAR *locale_strings;
489 int min = 0, max = locale_table->nb_lcnames - 1;
491 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
492 lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset);
494 while (min <= max)
496 int res, pos = (min + max) / 2;
497 const WCHAR *str = locale_strings + lcnames_index[pos].name;
498 res = compare_locale_names(name, str + 1);
499 if (res < 0) max = pos - 1;
500 else if (res > 0) min = pos + 1;
501 else return &lcnames_index[pos];
503 return NULL;
507 static DWORD get_lcid(CFStringRef lang)
509 const NLS_LOCALE_LCNAME_INDEX *entry;
510 const NLS_LOCALE_DATA *locale;
511 CFRange range;
512 WCHAR str[10];
513 ULONG offset;
515 if (!locale_table)
517 struct
519 UINT ctypes;
520 UINT unknown1;
521 UINT unknown2;
522 UINT unknown3;
523 UINT locales;
524 UINT charmaps;
525 UINT geoids;
526 UINT scripts;
527 } *header;
528 LCID system_lcid;
529 LARGE_INTEGER size;
531 if (NtInitializeNlsFiles((void **)&header, &system_lcid, &size))
533 ERR("NtInitializeNlsFiles failed\n");
534 return 0;
537 if (InterlockedCompareExchangePointer((void **)&locale_table,
538 (char *)header + header->locales, NULL))
539 NtUnmapViewOfSection(GetCurrentProcess(), header);
542 range.location = 0;
543 range.length = min(CFStringGetLength(lang), ARRAY_SIZE(str) - 1);
544 CFStringGetCharacters(lang, range, str);
545 str[range.length] = 0;
547 if (!(entry = find_lcname_entry(str)))
549 ERR("%s not found\n", debugstr_w(str));
550 return 0;
553 offset = locale_table->locales_offset + entry->idx * locale_table->locale_size;
554 locale = (const NLS_LOCALE_DATA *)((const char *)locale_table + offset);
555 return locale->inotneutral ? entry->id : locale->idefaultlanguage;
558 static HKL get_hkl(CFStringRef lang, CFStringRef type)
560 ULONG_PTR lcid = get_lcid(lang);
561 struct layout *layout;
563 /* Look for the last occurrence of this lcid in the list and if
564 present use that value + 0x10000 */
565 LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry)
567 ULONG_PTR hkl = HandleToUlong(layout->hkl);
569 if (LOWORD(hkl) == lcid)
571 lcid = (hkl & ~0xe0000000) + 0x10000;
572 break;
576 if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000;
578 return (HKL)lcid;
581 /******************************************************************
582 * get_layout_from_source
584 * Must be called while holding the layout_list_mutex.
585 * Note, returned layout may not currently be enabled.
587 static struct layout *get_layout_from_source(TISInputSourceRef input)
589 struct layout *ret = NULL, *layout;
591 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
593 if (CFEqual(input, layout->input_source))
595 ret = layout;
596 break;
599 return ret;
602 /***********************************************************************
603 * update_layout_list
605 * Must be called while holding the layout_list_mutex
607 * If an input source has been disabled (ie. removed from the UI) its
608 * entry remains in the layout list but is marked as such and is not
609 * enumerated by GetKeyboardLayoutList. This is to ensure the
610 * HKL <-> input source mapping is unique.
612 static void update_layout_list(void)
614 CFArrayRef sources;
615 struct layout *layout;
616 int i;
618 if (!InterlockedExchange((LONG *)&macdrv_layout_list_needs_update, FALSE)) return;
620 sources = macdrv_create_input_source_list();
622 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
623 layout->enabled = FALSE;
625 for (i = 0; i < CFArrayGetCount(sources); i++)
627 CFDictionaryRef dict = CFArrayGetValueAtIndex(sources, i);
628 TISInputSourceRef input = (TISInputSourceRef)CFDictionaryGetValue(dict, macdrv_input_source_input_key);
629 layout = get_layout_from_source(input);
630 if (!layout)
632 CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key);
633 CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key);
635 layout = malloc(sizeof(*layout));
636 layout->input_source = (TISInputSourceRef)CFRetain(input);
637 layout->hkl = get_hkl(lang, type);
639 list_add_tail(&layout_list, &layout->entry);
640 TRACE("adding new layout %p\n", layout->hkl);
642 else
643 TRACE("enabling already existing layout %p\n", layout->hkl);
645 layout->enabled = TRUE;
648 CFRelease(sources);
651 /***********************************************************************
652 * macdrv_get_hkl_from_source
654 * Find the HKL associated with a given input source.
656 HKL macdrv_get_hkl_from_source(TISInputSourceRef input)
658 struct layout *layout;
659 HKL ret = 0;
661 pthread_mutex_lock(&layout_list_mutex);
663 update_layout_list();
664 layout = get_layout_from_source(input);
665 if (layout) ret = layout->hkl;
667 pthread_mutex_unlock(&layout_list_mutex);
669 return ret;
673 /***********************************************************************
674 * macdrv_compute_keyboard_layout
676 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
678 int keyc;
679 WCHAR vkey;
680 const UCKeyboardLayout *uchr;
681 LocaleRef localeRef;
682 CollatorRef collatorRef, caseInsensitiveCollatorRef, diacriticInsensitiveCollatorRef;
683 UCCollateOptions collateOptions = 0;
684 const UInt32 modifier_combos[] = {
686 shiftKey >> 8,
687 cmdKey >> 8,
688 (shiftKey | cmdKey) >> 8,
689 optionKey >> 8,
690 (shiftKey | optionKey) >> 8,
692 UniChar map[128][ARRAY_SIZE(modifier_combos)][4 + 1];
693 int combo;
694 BYTE vkey_used[256];
695 int ignore_diacritics;
696 static const struct {
697 WCHAR wchar;
698 DWORD vkey;
699 } symbol_vkeys[] = {
700 { '-', VK_OEM_MINUS },
701 { '+', VK_OEM_PLUS },
702 { '_', VK_OEM_MINUS },
703 { ',', VK_OEM_COMMA },
704 { '.', VK_OEM_PERIOD },
705 { '=', VK_OEM_PLUS },
706 { '>', VK_OEM_PERIOD },
707 { '<', VK_OEM_COMMA },
708 { '|', VK_OEM_5 },
709 { '\\', VK_OEM_5 },
710 { '`', VK_OEM_3 },
711 { '[', VK_OEM_4 },
712 { '~', VK_OEM_3 },
713 { 0x00DF, VK_OEM_4 }, /* 0x00DF is ESZETT */
714 { 0x00FC, VK_OEM_1 }, /* 0x00FC is German U Umlaut */
715 { 0x00F6, VK_OEM_3 }, /* 0x00F6 is German O Umlaut */
716 { 0x00E4, VK_OEM_7 }, /* 0x00B4 is German A Umlaut */
717 { '?', VK_OEM_2 },
718 { ']', VK_OEM_6 },
719 { '/', VK_OEM_2 },
720 { ':', VK_OEM_1 },
721 { '}', VK_OEM_6 },
722 { '{', VK_OEM_4 },
723 { ';', VK_OEM_1 },
724 { '\'', VK_OEM_7 },
725 { ':', VK_OEM_PERIOD },
726 { ';', VK_OEM_COMMA },
727 { '"', VK_OEM_7 },
728 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
729 { '\'', VK_OEM_2 },
730 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
731 { '*', VK_OEM_PLUS },
732 { 0x00B4, VK_OEM_7 },
733 { '`', VK_OEM_4 },
734 { '[', VK_OEM_6 },
735 { '/', VK_OEM_5 },
736 { '^', VK_OEM_6 },
737 { '*', VK_OEM_2 },
738 { '{', VK_OEM_6 },
739 { 0x00B4, VK_OEM_6 },
740 { '~', VK_OEM_1 },
741 { '?', VK_OEM_PLUS },
742 { '?', VK_OEM_4 },
743 { 0x00B4, VK_OEM_3 },
744 { '?', VK_OEM_COMMA },
745 { '~', VK_OEM_PLUS },
746 { ']', VK_OEM_4 },
747 { '\'', VK_OEM_3 },
748 { 0x00A7, VK_OEM_7 },
749 { '<', VK_OEM_102 },
751 int i;
753 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
754 contiguous ranges. */
755 static const struct {
756 WORD first, last;
757 } vkey_ranges[] = {
758 { 'A', 'Z' },
759 { '0', '9' },
760 { VK_OEM_1, VK_OEM_3 },
761 { VK_OEM_4, VK_ICO_CLEAR },
762 { 0xe9, 0xf5 },
763 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
764 { VK_F1, VK_F24 },
765 { 0, 0 }
767 int vkey_range;
769 if (!thread_data->keyboard_layout_uchr)
771 ERR("no keyboard layout UCHR data\n");
772 return;
775 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
776 memset(vkey_used, 0, sizeof(vkey_used));
778 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
780 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
781 if (default_map[keyc].fixed)
783 vkey = default_map[keyc].vkey;
784 thread_data->keyc2vkey[keyc] = vkey;
785 vkey_used[vkey] = 1;
786 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
790 if (thread_data->iso_keyboard)
792 /* In almost all cases, the Mac key codes indicate a physical key position
793 and this corresponds nicely to Win32 scan codes. However, the Mac key
794 codes differ in one case between ANSI and ISO keyboards. For ANSI
795 keyboards, the key to the left of the digits and above the Tab key
796 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
797 some position produces kVK_ISO_Section. The additional key on ISO
798 keyboards, the one to the right of the left Shift key, produces
799 kVK_ANSI_Grave, which is just weird.
801 Since we want the key in that upper left corner to always produce the
802 same scan code (0x29), we need to swap the scan codes of those two
803 Mac key codes for ISO keyboards. */
804 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
805 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
806 thread_data->keyc2scan[kVK_ISO_Section] = temp;
809 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
811 LocaleRefFromLocaleString("POSIX", &localeRef);
812 UCCreateCollator(localeRef, 0, collateOptions, &collatorRef);
813 collateOptions |= kUCCollateComposeInsensitiveMask | kUCCollateWidthInsensitiveMask | kUCCollateCaseInsensitiveMask;
814 UCCreateCollator(localeRef, 0, collateOptions, &caseInsensitiveCollatorRef);
815 collateOptions |= kUCCollateDiacritInsensitiveMask;
816 UCCreateCollator(localeRef, 0, collateOptions, &diacriticInsensitiveCollatorRef);
818 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
819 memset(map, 0, sizeof(map));
820 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
822 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
823 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
825 TRACE("keyc 0x%04x: ", keyc);
827 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
829 UInt32 deadKeyState;
830 UniCharCount len;
831 OSStatus status;
833 deadKeyState = 0;
834 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
835 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
836 &deadKeyState, ARRAY_SIZE(map[keyc][combo]) - 1, &len, map[keyc][combo]);
837 if (status != noErr)
838 map[keyc][combo][0] = 0;
840 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
843 TRACE("\n");
846 /* First try to match key codes to the vkeys for the letters A through Z.
847 Try unmodified first, then with various modifier combinations in succession.
848 On the first pass, try to get a match lacking diacritical marks. On the
849 second pass, accept matches with diacritical marks. */
850 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
852 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
854 for (vkey = 'A'; vkey <= 'Z'; vkey++)
856 if (vkey_used[vkey])
857 continue;
859 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
861 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
862 continue;
864 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics ? diacriticInsensitiveCollatorRef : caseInsensitiveCollatorRef))
866 thread_data->keyc2vkey[keyc] = vkey;
867 vkey_used[vkey] = 1;
868 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
869 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
870 break;
877 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
878 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
880 for (vkey = '0'; vkey <= '9'; vkey++)
882 if (vkey_used[vkey])
883 continue;
885 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
887 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
888 continue;
890 if (char_matches_string(vkey, map[keyc][combo], collatorRef))
892 thread_data->keyc2vkey[keyc] = vkey;
893 vkey_used[vkey] = 1;
894 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
895 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
896 break;
902 /* Now try to match key codes for certain common punctuation characters to
903 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
904 for (combo = 0; combo < ARRAY_SIZE(modifier_combos); combo++)
906 for (i = 0; i < ARRAY_SIZE(symbol_vkeys); i++)
908 vkey = symbol_vkeys[i].vkey;
910 if (vkey_used[vkey])
911 continue;
913 for (keyc = 0; keyc < ARRAY_SIZE(map); keyc++)
915 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
916 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
917 continue;
919 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], collatorRef))
921 thread_data->keyc2vkey[keyc] = vkey;
922 vkey_used[vkey] = 1;
923 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
924 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
925 break;
931 /* For those key codes still without a vkey, try to use the default vkey
932 from the default map, if it's still available. */
933 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
935 unsigned int vkey = default_map[keyc].vkey;
937 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
938 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
940 if (!vkey_used[vkey])
942 thread_data->keyc2vkey[keyc] = vkey;
943 vkey_used[vkey] = 1;
944 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
948 /* For any unassigned key codes which would map to a letter in the default
949 map, but whose normal letter vkey wasn't available, try to find a
950 different letter. */
951 vkey = 'A';
952 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
954 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
955 continue; /* not a letter in ANSI layout */
956 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
957 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
959 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
960 if (vkey <= 'Z')
962 thread_data->keyc2vkey[keyc] = vkey;
963 vkey_used[vkey] = 1;
964 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
966 else
967 break; /* no more unused letter vkeys, so stop trying */
970 /* Same thing but with the digits. */
971 vkey = '0';
972 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
974 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
975 continue; /* not a digit in ANSI layout */
976 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
977 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
979 while (vkey <= '9' && vkey_used[vkey]) vkey++;
980 if (vkey <= '9')
982 thread_data->keyc2vkey[keyc] = vkey;
983 vkey_used[vkey] = 1;
984 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
986 else
987 break; /* no more unused digit vkeys, so stop trying */
990 /* Last chance. Assign any available vkey. */
991 vkey_range = 0;
992 vkey = vkey_ranges[vkey_range].first;
993 for (keyc = 0; keyc < ARRAY_SIZE(default_map); keyc++)
995 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
996 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
998 while (vkey && vkey_used[vkey])
1000 if (vkey == vkey_ranges[vkey_range].last)
1002 vkey_range++;
1003 vkey = vkey_ranges[vkey_range].first;
1005 else
1006 vkey++;
1009 if (!vkey)
1011 WARN("No more vkeys available!\n");
1012 break;
1015 thread_data->keyc2vkey[keyc] = vkey;
1016 vkey_used[vkey] = 1;
1017 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
1020 UCDisposeCollator(&collatorRef);
1021 UCDisposeCollator(&caseInsensitiveCollatorRef);
1022 UCDisposeCollator(&diacriticInsensitiveCollatorRef);
1026 /***********************************************************************
1027 * macdrv_send_keyboard_input
1029 static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, unsigned int flags, unsigned int time)
1031 INPUT input;
1033 TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
1035 input.type = INPUT_KEYBOARD;
1036 input.ki.wVk = vkey;
1037 input.ki.wScan = scan;
1038 input.ki.dwFlags = flags;
1039 input.ki.time = time;
1040 input.ki.dwExtraInfo = 0;
1042 NtUserSendHardwareInput(hwnd, 0, &input, 0);
1046 /***********************************************************************
1047 * get_async_key_state
1049 static BOOL get_async_key_state(BYTE state[256])
1051 BOOL ret;
1053 SERVER_START_REQ(get_key_state)
1055 req->async = 1;
1056 req->key = -1;
1057 wine_server_set_reply(req, state, 256);
1058 ret = !wine_server_call(req);
1060 SERVER_END_REQ;
1061 return ret;
1065 /***********************************************************************
1066 * update_modifier_state
1068 static void update_modifier_state(unsigned int modifier, unsigned int modifiers, const BYTE *keystate,
1069 WORD vkey, WORD alt_vkey, WORD scan, WORD alt_scan,
1070 DWORD event_time, BOOL restore)
1072 int key_pressed = (modifiers & modifier) != 0;
1073 int vkey_pressed = (keystate[vkey] & 0x80) || (keystate[alt_vkey] & 0x80);
1074 DWORD flags;
1076 if (key_pressed != vkey_pressed)
1078 if (key_pressed)
1080 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
1081 if (restore)
1082 flags |= KEYEVENTF_KEYUP;
1084 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff, flags, event_time);
1086 else
1088 flags = restore ? 0 : KEYEVENTF_KEYUP;
1090 if (keystate[vkey] & 0x80)
1092 macdrv_send_keyboard_input(NULL, vkey, scan & 0xff,
1093 flags | ((scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
1094 event_time);
1096 if (keystate[alt_vkey] & 0x80)
1098 macdrv_send_keyboard_input(NULL, alt_vkey, alt_scan & 0xff,
1099 flags | ((alt_scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
1100 event_time);
1107 /***********************************************************************
1108 * macdrv_key_event
1110 * Handler for KEY_PRESS and KEY_RELEASE events.
1112 void macdrv_key_event(HWND hwnd, const macdrv_event *event)
1114 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1115 WORD vkey, scan;
1116 DWORD flags;
1118 TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
1119 hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
1120 event->key.keycode, event->key.modifiers);
1122 thread_data->last_modifiers = event->key.modifiers;
1124 if (event->key.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1126 vkey = thread_data->keyc2vkey[event->key.keycode];
1127 scan = thread_data->keyc2scan[event->key.keycode];
1129 else
1130 vkey = scan = 0;
1132 TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
1133 event->key.keycode, vkey, scan);
1135 if (!vkey) return;
1137 flags = 0;
1138 if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
1139 if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
1141 macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
1145 /***********************************************************************
1146 * macdrv_keyboard_changed
1148 * Handler for KEYBOARD_CHANGED events.
1150 void macdrv_keyboard_changed(const macdrv_event *event)
1152 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1154 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
1155 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
1157 if (thread_data->keyboard_layout_uchr)
1158 CFRelease(thread_data->keyboard_layout_uchr);
1159 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
1160 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
1161 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
1162 thread_data->active_keyboard_layout = macdrv_get_hkl_from_source(event->keyboard_changed.input_source);
1163 thread_data->dead_key_state = 0;
1165 macdrv_compute_keyboard_layout(thread_data);
1167 NtUserActivateKeyboardLayout(thread_data->active_keyboard_layout, 0);
1169 send_message(get_active_window(), WM_CANCELMODE, 0, 0);
1173 /***********************************************************************
1174 * macdrv_hotkey_press
1176 * Handler for HOTKEY_PRESS events.
1178 void macdrv_hotkey_press(const macdrv_event *event)
1180 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1182 TRACE_(key)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
1183 event->hotkey_press.vkey, event->hotkey_press.mod_flags, event->hotkey_press.keycode,
1184 event->hotkey_press.time_ms);
1186 if (event->hotkey_press.keycode < ARRAY_SIZE(thread_data->keyc2vkey))
1188 WORD scan = thread_data->keyc2scan[event->hotkey_press.keycode];
1189 BYTE keystate[256];
1190 BOOL got_keystate;
1191 DWORD flags;
1193 if ((got_keystate = get_async_key_state(keystate)))
1195 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1196 0x38, 0x138, event->hotkey_press.time_ms, FALSE);
1197 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1198 0x1D, 0x11D, event->hotkey_press.time_ms, FALSE);
1199 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1200 0x2A, 0x36, event->hotkey_press.time_ms, FALSE);
1201 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1202 0x15B, 0x15C, event->hotkey_press.time_ms, FALSE);
1205 activate_on_following_focus();
1207 flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
1208 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1209 flags, event->key.time_ms);
1210 macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
1211 flags | KEYEVENTF_KEYUP, event->key.time_ms);
1213 if (got_keystate)
1215 update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
1216 0x38, 0x138, event->hotkey_press.time_ms, TRUE);
1217 update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
1218 0x1D, 0x11D, event->hotkey_press.time_ms, TRUE);
1219 update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
1220 0x2A, 0x36, event->hotkey_press.time_ms, TRUE);
1221 update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
1222 0x15B, 0x15C, event->hotkey_press.time_ms, TRUE);
1228 /***********************************************************************
1229 * ImeProcessKey (MACDRV.@)
1231 UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_state)
1233 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1234 WORD scan = HIWORD(lparam) & 0x1ff, vkey = LOWORD(wparam);
1235 BOOL repeat = !!(lparam >> 30), pressed = !(lparam >> 31);
1236 unsigned int flags;
1237 int keyc, done = 0;
1239 TRACE("himc %p, scan %#x, vkey %#x, repeat %u, pressed %u\n",
1240 himc, scan, vkey, repeat, pressed);
1242 if (!macdrv_using_input_method()) return 0;
1244 switch (vkey)
1246 case VK_SHIFT:
1247 case VK_CONTROL:
1248 case VK_CAPITAL:
1249 case VK_MENU:
1250 return 0;
1253 flags = thread_data->last_modifiers;
1254 if (key_state[VK_SHIFT] & 0x80)
1255 flags |= NX_SHIFTMASK;
1256 else
1257 flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK);
1258 if (key_state[VK_CAPITAL] & 0x01)
1259 flags |= NX_ALPHASHIFTMASK;
1260 else
1261 flags &= ~NX_ALPHASHIFTMASK;
1262 if (key_state[VK_CONTROL] & 0x80)
1263 flags |= NX_CONTROLMASK;
1264 else
1265 flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK);
1266 if (key_state[VK_MENU] & 0x80)
1267 flags |= NX_COMMANDMASK;
1268 else
1269 flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
1271 /* Find the Mac keycode corresponding to the scan code */
1272 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1273 if (thread_data->keyc2vkey[keyc] == vkey) break;
1275 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey)) return 0;
1277 TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc);
1279 macdrv_send_text_input_event(pressed, flags, repeat, keyc, himc, &done);
1280 while (!done) NtUserMsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0);
1282 return done > 0;
1286 /***********************************************************************
1287 * ActivateKeyboardLayout (MACDRV.@)
1289 BOOL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
1291 BOOL ret = FALSE;
1292 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1293 struct layout *layout;
1295 TRACE("hkl %p flags %04x\n", hkl, flags);
1297 if (hkl == thread_data->active_keyboard_layout)
1298 return TRUE;
1300 pthread_mutex_lock(&layout_list_mutex);
1301 update_layout_list();
1303 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1305 if (layout->hkl == hkl)
1307 if (macdrv_select_input_source(layout->input_source))
1309 ret = TRUE;
1310 if (thread_data->keyboard_layout_uchr)
1311 CFRelease(thread_data->keyboard_layout_uchr);
1313 macdrv_get_input_source_info(&thread_data->keyboard_layout_uchr, &thread_data->keyboard_type,
1314 &thread_data->iso_keyboard, NULL);
1315 thread_data->active_keyboard_layout = hkl;
1316 thread_data->dead_key_state = 0;
1318 macdrv_compute_keyboard_layout(thread_data);
1320 break;
1323 pthread_mutex_unlock(&layout_list_mutex);
1325 return ret;
1329 /***********************************************************************
1330 * Beep (MACDRV.@)
1332 void macdrv_Beep(void)
1334 macdrv_beep();
1338 /***********************************************************************
1339 * GetKeyNameText (MACDRV.@)
1341 INT macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
1343 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1344 int scan, keyc;
1346 scan = (lparam >> 16) & 0x1FF;
1347 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2scan); keyc++)
1349 if (thread_data->keyc2scan[keyc] == scan)
1351 const UCKeyboardLayout *uchr;
1352 UInt32 deadKeyState = 0;
1353 UniCharCount len;
1354 OSStatus status;
1355 unsigned int vkey;
1356 int i;
1358 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1359 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
1360 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
1361 if (status != noErr)
1362 len = 0;
1363 if (len && buffer[0] > 32)
1364 buffer[len] = 0;
1366 vkey = thread_data->keyc2vkey[keyc];
1367 if (lparam & (1 << 25))
1369 /* Caller doesn't care about distinctions between left and
1370 right keys. */
1371 switch (vkey)
1373 case VK_LSHIFT:
1374 case VK_RSHIFT:
1375 vkey = VK_SHIFT; break;
1376 case VK_LCONTROL:
1377 case VK_RCONTROL:
1378 vkey = VK_CONTROL; break;
1379 case VK_LMENU:
1380 case VK_RMENU:
1381 vkey = VK_MENU; break;
1385 if (scan & 0x100) vkey |= 0x100;
1387 for (i = 0; i < ARRAY_SIZE(vkey_names); i++)
1389 if (vkey_names[i].vkey == vkey)
1391 len = min(strlen(vkey_names[i].name) + 1, size);
1392 ascii_to_unicode(buffer, vkey_names[i].name, len);
1393 if (len) buffer[--len] = 0;
1394 break;
1398 if (!len)
1400 char name[16];
1401 len = snprintf(name, sizeof(name), "Key 0x%02x", vkey);
1402 len = min(len + 1, size);
1403 ascii_to_unicode(buffer, name, len);
1404 if (len) buffer[--len] = 0;
1407 if (!len)
1408 break;
1410 if (status == noErr && deadKeyState)
1412 for (i = 0; i < ARRAY_SIZE(dead_key_names); i++)
1414 if (dead_key_names[i].wchar == buffer[0])
1416 len = min(strlen(dead_key_names[i].name) + 1, size);
1417 ascii_to_unicode(buffer, dead_key_names[i].name, len);
1418 if (len) buffer[--len] = 0;
1419 break;
1424 if (status == noErr && len == 1 && buffer[0] >= 'a' && buffer[0] <= 'z')
1425 buffer[0] += 'A' - 'a';
1427 TRACE("lparam 0x%08x -> %s\n", (unsigned int)lparam, debugstr_w(buffer));
1428 return len;
1432 WARN("found no name for lparam 0x%08x\n", (unsigned int)lparam);
1433 return 0;
1437 /***********************************************************************
1438 * GetKeyboardLayoutList (MACDRV.@)
1440 UINT macdrv_GetKeyboardLayoutList(INT size, HKL *list)
1442 int count = 0;
1443 struct layout *layout;
1445 TRACE("%d, %p\n", size, list);
1447 pthread_mutex_lock(&layout_list_mutex);
1449 update_layout_list();
1451 LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry)
1453 if (!layout->enabled) continue;
1454 if (list)
1456 if (count >= size) break;
1457 list[count] = layout->hkl;
1458 TRACE("\t%d: %p\n", count, list[count]);
1460 count++;
1462 pthread_mutex_unlock(&layout_list_mutex);
1464 TRACE("returning %d\n", count);
1465 return count;
1469 /***********************************************************************
1470 * MapVirtualKeyEx (MACDRV.@)
1472 UINT macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1474 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1475 UINT ret = 0;
1476 int keyc;
1478 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1480 switch (wMapType)
1482 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1483 case MAPVK_VK_TO_VSC_EX:
1484 switch (wCode)
1486 case VK_SHIFT: wCode = VK_LSHIFT; break;
1487 case VK_CONTROL: wCode = VK_LCONTROL; break;
1488 case VK_MENU: wCode = VK_LMENU; break;
1491 /* vkey -> keycode -> scan */
1492 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1494 if (thread_data->keyc2vkey[keyc] == wCode)
1496 ret = thread_data->keyc2scan[keyc] & 0xFF;
1497 break;
1501 /* set scan code prefix */
1502 if (wMapType == MAPVK_VK_TO_VSC_EX &&
1503 (wCode == VK_RCONTROL || wCode == VK_RMENU))
1504 ret |= 0xe000;
1505 break;
1507 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1508 case MAPVK_VSC_TO_VK_EX:
1509 /* scan -> keycode -> vkey */
1510 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1511 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1513 ret = thread_data->keyc2vkey[keyc];
1514 /* Only stop if it's not a numpad vkey; otherwise keep
1515 looking for a potential better vkey. */
1516 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1517 break;
1520 if (wMapType == MAPVK_VSC_TO_VK)
1521 switch (ret)
1523 case VK_LSHIFT:
1524 case VK_RSHIFT:
1525 ret = VK_SHIFT; break;
1526 case VK_LCONTROL:
1527 case VK_RCONTROL:
1528 ret = VK_CONTROL; break;
1529 case VK_LMENU:
1530 case VK_RMENU:
1531 ret = VK_MENU; break;
1534 break;
1536 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1538 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1539 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1540 const UCKeyboardLayout *uchr;
1541 UniChar s[10];
1542 OSStatus status;
1543 UInt32 deadKeyState;
1544 UniCharCount len;
1545 BOOL deadKey = FALSE;
1547 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1548 (VK_F1 <= wCode && wCode <= VK_F24))
1549 break;
1551 if (!thread_data || !thread_data->keyboard_layout_uchr)
1553 WARN("No keyboard layout uchr data\n");
1554 break;
1557 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1559 /* Find the Mac keycode corresponding to the vkey */
1560 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1561 if (thread_data->keyc2vkey[keyc] == wCode) break;
1563 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1565 WARN("Unknown virtual key %X\n", wCode);
1566 break;
1569 TRACE("Found keycode %u\n", keyc);
1571 deadKeyState = 0;
1572 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1573 thread_data->keyboard_type, 0, &deadKeyState, ARRAY_SIZE(s), &len, s);
1574 if (status == noErr && !len && deadKeyState)
1576 deadKey = TRUE;
1577 deadKeyState = 0;
1578 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1579 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1580 &deadKeyState, ARRAY_SIZE(s), &len, s);
1583 if (status == noErr && len)
1584 ret = RtlUpcaseUnicodeChar(s[0]) | (deadKey ? 0x80000000 : 0);
1586 break;
1588 default: /* reserved */
1589 FIXME("Unknown wMapType %d\n", wMapType);
1590 break;
1593 TRACE("returning 0x%04x\n", ret);
1594 return ret;
1598 /***********************************************************************
1599 * RegisterHotKey (MACDRV.@)
1601 BOOL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey)
1603 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1604 unsigned int keyc, modifiers = 0;
1605 int ret;
1607 TRACE_(key)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd, mod_flags, vkey);
1609 /* Find the Mac keycode corresponding to the vkey */
1610 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1611 if (thread_data->keyc2vkey[keyc] == vkey) break;
1613 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1615 WARN_(key)("ignoring unknown virtual key 0x%04x\n", vkey);
1616 return TRUE;
1619 if (mod_flags & MOD_ALT) modifiers |= cmdKey;
1620 if (mod_flags & MOD_CONTROL) modifiers |= controlKey;
1621 if (mod_flags & MOD_SHIFT) modifiers |= shiftKey;
1622 if (mod_flags & MOD_WIN)
1624 WARN_(key)("MOD_WIN not supported; ignoring\n");
1625 return TRUE;
1628 ret = macdrv_register_hot_key(thread_data->queue, vkey, mod_flags, keyc, modifiers);
1629 TRACE_(key)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc, modifiers, ret);
1631 if (ret == MACDRV_HOTKEY_ALREADY_REGISTERED)
1632 RtlSetLastWin32Error(ERROR_HOTKEY_ALREADY_REGISTERED);
1633 else if (ret != MACDRV_HOTKEY_SUCCESS)
1634 RtlSetLastWin32Error(ERROR_GEN_FAILURE);
1636 return ret == MACDRV_HOTKEY_SUCCESS;
1640 /***********************************************************************
1641 * ToUnicodeEx (MACDRV.@)
1643 * The ToUnicode function translates the specified virtual-key code and keyboard
1644 * state to the corresponding Windows character or characters.
1646 * If the specified key is a dead key, the return value is negative. Otherwise,
1647 * it is one of the following values:
1648 * Value Meaning
1649 * -1 The specified virtual key is a dead-key. If possible, the
1650 * non-combining form of the dead character is written to bufW.
1651 * 0 The specified virtual key has no translation for the current
1652 * state of the keyboard.
1653 * 1 One Windows character was copied to the buffer.
1654 * 2 or more Multiple characters were copied to the buffer. This usually
1655 * happens when a dead-key character (accent or diacritic) stored
1656 * in the keyboard layout cannot be composed with the specified
1657 * virtual key to form a single character.
1660 INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1661 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1663 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1664 INT ret = 0;
1665 int keyc;
1666 BOOL is_menu = (flags & 0x1);
1667 int status;
1668 const UCKeyboardLayout *uchr;
1669 UInt16 keyAction;
1670 UInt32 modifierKeyState;
1671 OptionBits options;
1672 UInt32 deadKeyState, savedDeadKeyState;
1673 UniCharCount len = 0;
1674 BOOL dead = FALSE;
1676 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1677 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1679 if (!virtKey)
1680 goto done;
1682 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1683 modifier key press. We want it to effectively ignore modifier key
1684 presses. I think that one isn't supposed to call it at all for modifier
1685 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1686 are different event types than key up/down events. */
1687 switch (virtKey)
1689 case VK_SHIFT:
1690 case VK_CONTROL:
1691 case VK_MENU:
1692 case VK_CAPITAL:
1693 case VK_LSHIFT:
1694 case VK_RSHIFT:
1695 case VK_LCONTROL:
1696 case VK_RCONTROL:
1697 case VK_LMENU:
1698 case VK_RMENU:
1699 goto done;
1702 /* There are a number of key combinations for which Windows does not
1703 produce characters, but Mac keyboard layouts may. Eat them. Do this
1704 here to avoid the expense of UCKeyTranslate() but also because these
1705 keys shouldn't terminate dead key sequences. */
1706 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1707 goto done;
1709 /* Shift + <non-digit keypad keys>. */
1710 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1711 goto done;
1713 if (lpKeyState[VK_CONTROL] & 0x80)
1715 /* Control-Tab, with or without other modifiers. */
1716 if (virtKey == VK_TAB)
1717 goto done;
1719 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1720 for these keys. */
1721 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1723 switch (virtKey)
1725 case VK_CANCEL:
1726 case VK_BACK:
1727 case VK_ESCAPE:
1728 case VK_SPACE:
1729 case VK_RETURN:
1730 goto done;
1735 if (thread_data->keyboard_layout_uchr)
1736 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1737 else
1738 uchr = NULL;
1740 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1742 modifierKeyState = 0;
1743 if (lpKeyState[VK_SHIFT] & 0x80)
1744 modifierKeyState |= (shiftKey >> 8);
1745 if (lpKeyState[VK_CAPITAL] & 0x01)
1746 modifierKeyState |= (alphaLock >> 8);
1747 if (lpKeyState[VK_CONTROL] & 0x80)
1748 modifierKeyState |= (controlKey >> 8);
1749 if (lpKeyState[VK_MENU] & 0x80)
1750 modifierKeyState |= (cmdKey >> 8);
1751 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1752 modifierKeyState |= (optionKey >> 8);
1754 /* Find the Mac keycode corresponding to the vkey */
1755 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1756 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1758 if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey))
1760 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1761 goto done;
1764 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1765 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1767 if (is_menu)
1769 if (keyAction == kUCKeyActionUp)
1770 goto done;
1772 options = kUCKeyTranslateNoDeadKeysMask;
1773 deadKeyState = 0;
1775 else
1777 options = 0;
1778 deadKeyState = thread_data->dead_key_state;
1780 savedDeadKeyState = deadKeyState;
1781 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1782 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1783 &len, bufW);
1784 if (status != noErr)
1786 ERR_(key)("Couldn't translate keycode 0x%04x, status %d\n", keyc, status);
1787 goto done;
1789 if (!is_menu)
1791 if (keyAction != kUCKeyActionUp && len > 0 && deadKeyState == thread_data->dead_key_state)
1792 thread_data->dead_key_state = 0;
1793 else
1794 thread_data->dead_key_state = deadKeyState;
1796 if (keyAction == kUCKeyActionUp)
1797 goto done;
1800 if (len == 0 && deadKeyState)
1802 /* Repeat the translation, but disabling dead-key generation to
1803 learn which dead key it was. */
1804 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1805 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1806 &savedDeadKeyState, bufW_size, &len, bufW);
1807 if (status != noErr)
1809 ERR_(key)("Couldn't translate dead keycode 0x%04x, status %d\n", keyc, status);
1810 goto done;
1813 dead = TRUE;
1816 if (len > 0)
1817 len = strip_apple_private_chars(bufW, len);
1819 ret = dead ? -len : len;
1821 /* Control-Return produces line feed instead of carriage return. */
1822 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1824 int i;
1825 for (i = 0; i < len; i++)
1826 if (bufW[i] == '\r')
1827 bufW[i] = '\n';
1830 done:
1831 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1832 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1833 if (len < bufW_size)
1834 bufW[len] = 0;
1836 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, len));
1837 return ret;
1841 /***********************************************************************
1842 * UnregisterHotKey (MACDRV.@)
1844 void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey)
1846 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1848 TRACE_(key)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd, modifiers, vkey);
1850 if (thread_data)
1851 macdrv_unregister_hot_key(thread_data->queue, vkey, modifiers);
1855 /***********************************************************************
1856 * VkKeyScanEx (MACDRV.@)
1858 * Note: Windows ignores HKL parameter and uses current active layout instead
1860 SHORT macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1862 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1863 SHORT ret = -1;
1864 int state;
1865 const UCKeyboardLayout *uchr;
1867 TRACE("%04x, %p\n", wChar, hkl);
1869 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1870 if (!uchr)
1872 TRACE("no keyboard layout UCHR data; returning -1\n");
1873 return -1;
1876 for (state = 0; state < 8; state++)
1878 UInt32 modifierKeyState = 0;
1879 int keyc;
1881 if (state & 1)
1882 modifierKeyState |= (shiftKey >> 8);
1883 if ((state & 6) == 6)
1884 modifierKeyState |= (optionKey >> 8);
1885 else
1887 if (state & 2)
1888 modifierKeyState |= (controlKey >> 8);
1889 if (state & 4)
1890 modifierKeyState |= (cmdKey >> 8);
1893 for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++)
1895 UInt32 deadKeyState = 0;
1896 UniChar uchar;
1897 UniCharCount len;
1898 OSStatus status;
1900 if (!thread_data->keyc2vkey[keyc]) continue;
1902 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1903 thread_data->keyboard_type, 0, &deadKeyState,
1904 1, &len, &uchar);
1905 if (status == noErr && len == 1 && uchar == wChar)
1907 WORD vkey = thread_data->keyc2vkey[keyc];
1909 ret = vkey | (state << 8);
1910 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1911 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1912 keyc == kVK_ANSI_KeypadEquals)
1914 /* Keep searching for a non-numpad match, which is preferred. */
1916 else
1917 goto done;
1922 done:
1923 TRACE(" -> 0x%04x\n", ret);
1924 return ret;