Release 1.6-rc2.
[wine/testsucceed.git] / dlls / winemac.drv / keyboard.c
blob98d17ba429ff1a23c8cefcaf10a2be9f3be0cc7d
1 /*
2 * MACDRV keyboard driver
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1996 Albrecht Kleine
6 * Copyright 1997 David Faure
7 * Copyright 1998 Morten Welinder
8 * Copyright 1998 Ulrich Weigand
9 * Copyright 1999 Ove Kåven
10 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
29 #include "macdrv.h"
30 #include "winuser.h"
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
34 WINE_DECLARE_DEBUG_CHANNEL(key);
37 /* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
38 enum {
39 cmdKeyBit = 8,
40 shiftKeyBit = 9,
41 alphaLockBit = 10,
42 optionKeyBit = 11,
43 controlKeyBit = 12,
46 enum {
47 cmdKey = 1 << cmdKeyBit,
48 shiftKey = 1 << shiftKeyBit,
49 alphaLock = 1 << alphaLockBit,
50 optionKey = 1 << optionKeyBit,
51 controlKey = 1 << controlKeyBit,
55 /* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
56 enum {
57 kVK_ANSI_A = 0x00,
58 kVK_ANSI_S = 0x01,
59 kVK_ANSI_D = 0x02,
60 kVK_ANSI_F = 0x03,
61 kVK_ANSI_H = 0x04,
62 kVK_ANSI_G = 0x05,
63 kVK_ANSI_Z = 0x06,
64 kVK_ANSI_X = 0x07,
65 kVK_ANSI_C = 0x08,
66 kVK_ANSI_V = 0x09,
67 kVK_ISO_Section = 0x0A,
68 kVK_ANSI_B = 0x0B,
69 kVK_ANSI_Q = 0x0C,
70 kVK_ANSI_W = 0x0D,
71 kVK_ANSI_E = 0x0E,
72 kVK_ANSI_R = 0x0F,
73 kVK_ANSI_Y = 0x10,
74 kVK_ANSI_T = 0x11,
75 kVK_ANSI_1 = 0x12,
76 kVK_ANSI_2 = 0x13,
77 kVK_ANSI_3 = 0x14,
78 kVK_ANSI_4 = 0x15,
79 kVK_ANSI_6 = 0x16,
80 kVK_ANSI_5 = 0x17,
81 kVK_ANSI_Equal = 0x18,
82 kVK_ANSI_9 = 0x19,
83 kVK_ANSI_7 = 0x1A,
84 kVK_ANSI_Minus = 0x1B,
85 kVK_ANSI_8 = 0x1C,
86 kVK_ANSI_0 = 0x1D,
87 kVK_ANSI_RightBracket = 0x1E,
88 kVK_ANSI_O = 0x1F,
89 kVK_ANSI_U = 0x20,
90 kVK_ANSI_LeftBracket = 0x21,
91 kVK_ANSI_I = 0x22,
92 kVK_ANSI_P = 0x23,
93 kVK_Return = 0x24,
94 kVK_ANSI_L = 0x25,
95 kVK_ANSI_J = 0x26,
96 kVK_ANSI_Quote = 0x27,
97 kVK_ANSI_K = 0x28,
98 kVK_ANSI_Semicolon = 0x29,
99 kVK_ANSI_Backslash = 0x2A,
100 kVK_ANSI_Comma = 0x2B,
101 kVK_ANSI_Slash = 0x2C,
102 kVK_ANSI_N = 0x2D,
103 kVK_ANSI_M = 0x2E,
104 kVK_ANSI_Period = 0x2F,
105 kVK_Tab = 0x30,
106 kVK_Space = 0x31,
107 kVK_ANSI_Grave = 0x32,
108 kVK_Delete = 0x33,
109 kVK_Escape = 0x35,
110 kVK_RightCommand = 0x36, /* invented for Wine; co-opt unused key code */
111 kVK_Command = 0x37,
112 kVK_Shift = 0x38,
113 kVK_CapsLock = 0x39,
114 kVK_Option = 0x3A,
115 kVK_Control = 0x3B,
116 kVK_RightShift = 0x3C,
117 kVK_RightOption = 0x3D,
118 kVK_RightControl = 0x3E,
119 kVK_Function = 0x3F,
120 kVK_F17 = 0x40,
121 kVK_ANSI_KeypadDecimal = 0x41,
122 kVK_ANSI_KeypadMultiply = 0x43,
123 kVK_ANSI_KeypadPlus = 0x45,
124 kVK_ANSI_KeypadClear = 0x47,
125 kVK_VolumeUp = 0x48,
126 kVK_VolumeDown = 0x49,
127 kVK_Mute = 0x4A,
128 kVK_ANSI_KeypadDivide = 0x4B,
129 kVK_ANSI_KeypadEnter = 0x4C,
130 kVK_ANSI_KeypadMinus = 0x4E,
131 kVK_F18 = 0x4F,
132 kVK_F19 = 0x50,
133 kVK_ANSI_KeypadEquals = 0x51,
134 kVK_ANSI_Keypad0 = 0x52,
135 kVK_ANSI_Keypad1 = 0x53,
136 kVK_ANSI_Keypad2 = 0x54,
137 kVK_ANSI_Keypad3 = 0x55,
138 kVK_ANSI_Keypad4 = 0x56,
139 kVK_ANSI_Keypad5 = 0x57,
140 kVK_ANSI_Keypad6 = 0x58,
141 kVK_ANSI_Keypad7 = 0x59,
142 kVK_F20 = 0x5A,
143 kVK_ANSI_Keypad8 = 0x5B,
144 kVK_ANSI_Keypad9 = 0x5C,
145 kVK_JIS_Yen = 0x5D,
146 kVK_JIS_Underscore = 0x5E,
147 kVK_JIS_KeypadComma = 0x5F,
148 kVK_F5 = 0x60,
149 kVK_F6 = 0x61,
150 kVK_F7 = 0x62,
151 kVK_F3 = 0x63,
152 kVK_F8 = 0x64,
153 kVK_F9 = 0x65,
154 kVK_JIS_Eisu = 0x66,
155 kVK_F11 = 0x67,
156 kVK_JIS_Kana = 0x68,
157 kVK_F13 = 0x69,
158 kVK_F16 = 0x6A,
159 kVK_F14 = 0x6B,
160 kVK_F10 = 0x6D,
161 kVK_F12 = 0x6F,
162 kVK_F15 = 0x71,
163 kVK_Help = 0x72,
164 kVK_Home = 0x73,
165 kVK_PageUp = 0x74,
166 kVK_ForwardDelete = 0x75,
167 kVK_F4 = 0x76,
168 kVK_End = 0x77,
169 kVK_F2 = 0x78,
170 kVK_PageDown = 0x79,
171 kVK_F1 = 0x7A,
172 kVK_LeftArrow = 0x7B,
173 kVK_RightArrow = 0x7C,
174 kVK_DownArrow = 0x7D,
175 kVK_UpArrow = 0x7E,
179 /* Indexed by Mac virtual keycode values defined above. */
180 static const struct {
181 WORD vkey;
182 WORD scan;
183 BOOL fixed;
184 } default_map[128] = {
185 { 'A', 0x1E, FALSE }, /* kVK_ANSI_A */
186 { 'S', 0x1F, FALSE }, /* kVK_ANSI_S */
187 { 'D', 0x20, FALSE }, /* kVK_ANSI_D */
188 { 'F', 0x21, FALSE }, /* kVK_ANSI_F */
189 { 'H', 0x23, FALSE }, /* kVK_ANSI_H */
190 { 'G', 0x22, FALSE }, /* kVK_ANSI_G */
191 { 'Z', 0x2C, FALSE }, /* kVK_ANSI_Z */
192 { 'X', 0x2D, FALSE }, /* kVK_ANSI_X */
193 { 'C', 0x2E, FALSE }, /* kVK_ANSI_C */
194 { 'V', 0x2F, FALSE }, /* kVK_ANSI_V */
195 { VK_OEM_102, 0x56, TRUE }, /* kVK_ISO_Section */
196 { 'B', 0x30, FALSE }, /* kVK_ANSI_B */
197 { 'Q', 0x10, FALSE }, /* kVK_ANSI_Q */
198 { 'W', 0x11, FALSE }, /* kVK_ANSI_W */
199 { 'E', 0x12, FALSE }, /* kVK_ANSI_E */
200 { 'R', 0x13, FALSE }, /* kVK_ANSI_R */
201 { 'Y', 0x15, FALSE }, /* kVK_ANSI_Y */
202 { 'T', 0x14, FALSE }, /* kVK_ANSI_T */
203 { '1', 0x02, FALSE }, /* kVK_ANSI_1 */
204 { '2', 0x03, FALSE }, /* kVK_ANSI_2 */
205 { '3', 0x04, FALSE }, /* kVK_ANSI_3 */
206 { '4', 0x05, FALSE }, /* kVK_ANSI_4 */
207 { '6', 0x07, FALSE }, /* kVK_ANSI_6 */
208 { '5', 0x06, FALSE }, /* kVK_ANSI_5 */
209 { VK_OEM_PLUS, 0x0D, FALSE }, /* kVK_ANSI_Equal */
210 { '9', 0x0A, FALSE }, /* kVK_ANSI_9 */
211 { '7', 0x08, FALSE }, /* kVK_ANSI_7 */
212 { VK_OEM_MINUS, 0x0C, FALSE }, /* kVK_ANSI_Minus */
213 { '8', 0x09, FALSE }, /* kVK_ANSI_8 */
214 { '0', 0x0B, FALSE }, /* kVK_ANSI_0 */
215 { VK_OEM_6, 0x1B, FALSE }, /* kVK_ANSI_RightBracket */
216 { 'O', 0x18, FALSE }, /* kVK_ANSI_O */
217 { 'U', 0x16, FALSE }, /* kVK_ANSI_U */
218 { VK_OEM_4, 0x1A, FALSE }, /* kVK_ANSI_LeftBracket */
219 { 'I', 0x17, FALSE }, /* kVK_ANSI_I */
220 { 'P', 0x19, FALSE }, /* kVK_ANSI_P */
221 { VK_RETURN, 0x1C, TRUE }, /* kVK_Return */
222 { 'L', 0x26, FALSE }, /* kVK_ANSI_L */
223 { 'J', 0x24, FALSE }, /* kVK_ANSI_J */
224 { VK_OEM_7, 0x28, FALSE }, /* kVK_ANSI_Quote */
225 { 'K', 0x25, FALSE }, /* kVK_ANSI_K */
226 { VK_OEM_1, 0x27, FALSE }, /* kVK_ANSI_Semicolon */
227 { VK_OEM_5, 0x2B, FALSE }, /* kVK_ANSI_Backslash */
228 { VK_OEM_COMMA, 0x33, FALSE }, /* kVK_ANSI_Comma */
229 { VK_OEM_2, 0x35, FALSE }, /* kVK_ANSI_Slash */
230 { 'N', 0x31, FALSE }, /* kVK_ANSI_N */
231 { 'M', 0x32, FALSE }, /* kVK_ANSI_M */
232 { VK_OEM_PERIOD, 0x34, FALSE }, /* kVK_ANSI_Period */
233 { VK_TAB, 0x0F, TRUE }, /* kVK_Tab */
234 { VK_SPACE, 0x39, TRUE }, /* kVK_Space */
235 { VK_OEM_3, 0x29, FALSE }, /* kVK_ANSI_Grave */
236 { VK_BACK, 0x0E, TRUE }, /* kVK_Delete */
237 { 0, 0, FALSE }, /* 0x34 unused */
238 { VK_ESCAPE, 0x01, TRUE }, /* kVK_Escape */
239 { VK_RMENU, 0x38 | 0x100, TRUE }, /* kVK_RightCommand */
240 { VK_LMENU, 0x38, TRUE }, /* kVK_Command */
241 { VK_LSHIFT, 0x2A, TRUE }, /* kVK_Shift */
242 { VK_CAPITAL, 0x3A, TRUE }, /* kVK_CapsLock */
243 { 0, 0, FALSE }, /* kVK_Option */
244 { VK_LCONTROL, 0x1D, TRUE }, /* kVK_Control */
245 { VK_RSHIFT, 0x36, TRUE }, /* kVK_RightShift */
246 { 0, 0, FALSE }, /* kVK_RightOption */
247 { VK_RCONTROL, 0x1D | 0x100, TRUE }, /* kVK_RightControl */
248 { 0, 0, FALSE }, /* kVK_Function */
249 { VK_F17, 0x68, TRUE }, /* kVK_F17 */
250 { VK_DECIMAL, 0x53, TRUE }, /* kVK_ANSI_KeypadDecimal */
251 { 0, 0, FALSE }, /* 0x42 unused */
252 { VK_MULTIPLY, 0x37, TRUE }, /* kVK_ANSI_KeypadMultiply */
253 { 0, 0, FALSE }, /* 0x44 unused */
254 { VK_ADD, 0x4E, TRUE }, /* kVK_ANSI_KeypadPlus */
255 { 0, 0, FALSE }, /* 0x46 unused */
256 { VK_OEM_CLEAR, 0x59, TRUE }, /* kVK_ANSI_KeypadClear */
257 { VK_VOLUME_UP, 0 | 0x100, TRUE }, /* kVK_VolumeUp */
258 { VK_VOLUME_DOWN, 0 | 0x100, TRUE }, /* kVK_VolumeDown */
259 { VK_VOLUME_MUTE, 0 | 0x100, TRUE }, /* kVK_Mute */
260 { VK_DIVIDE, 0x35 | 0x100, TRUE }, /* kVK_ANSI_KeypadDivide */
261 { VK_RETURN, 0x1C | 0x100, TRUE }, /* kVK_ANSI_KeypadEnter */
262 { 0, 0, FALSE }, /* 0x4D unused */
263 { VK_SUBTRACT, 0x4A, TRUE }, /* kVK_ANSI_KeypadMinus */
264 { VK_F18, 0x69, TRUE }, /* kVK_F18 */
265 { VK_F19, 0x6A, TRUE }, /* kVK_F19 */
266 { VK_OEM_NEC_EQUAL, 0x0D | 0x100, TRUE }, /* kVK_ANSI_KeypadEquals */
267 { VK_NUMPAD0, 0x52, TRUE }, /* kVK_ANSI_Keypad0 */
268 { VK_NUMPAD1, 0x4F, TRUE }, /* kVK_ANSI_Keypad1 */
269 { VK_NUMPAD2, 0x50, TRUE }, /* kVK_ANSI_Keypad2 */
270 { VK_NUMPAD3, 0x51, TRUE }, /* kVK_ANSI_Keypad3 */
271 { VK_NUMPAD4, 0x4B, TRUE }, /* kVK_ANSI_Keypad4 */
272 { VK_NUMPAD5, 0x4C, TRUE }, /* kVK_ANSI_Keypad5 */
273 { VK_NUMPAD6, 0x4D, TRUE }, /* kVK_ANSI_Keypad6 */
274 { VK_NUMPAD7, 0x47, TRUE }, /* kVK_ANSI_Keypad7 */
275 { VK_F20, 0x6B, TRUE }, /* kVK_F20 */
276 { VK_NUMPAD8, 0x48, TRUE }, /* kVK_ANSI_Keypad8 */
277 { VK_NUMPAD9, 0x49, TRUE }, /* kVK_ANSI_Keypad9 */
278 { 0xFF, 0x7D, TRUE }, /* kVK_JIS_Yen */
279 { 0xC1, 0x73, TRUE }, /* kVK_JIS_Underscore */
280 { VK_SEPARATOR, 0x7E, TRUE }, /* kVK_JIS_KeypadComma */
281 { VK_F5, 0x3F, TRUE }, /* kVK_F5 */
282 { VK_F6, 0x40, TRUE }, /* kVK_F6 */
283 { VK_F7, 0x41, TRUE }, /* kVK_F7 */
284 { VK_F3, 0x3D, TRUE }, /* kVK_F3 */
285 { VK_F8, 0x42, TRUE }, /* kVK_F8 */
286 { VK_F9, 0x43, TRUE }, /* kVK_F9 */
287 { 0xFF, 0x72, TRUE }, /* kVK_JIS_Eisu */
288 { VK_F11, 0x57, TRUE }, /* kVK_F11 */
289 { VK_OEM_RESET, 0x71, TRUE }, /* kVK_JIS_Kana */
290 { VK_F13, 0x64, TRUE }, /* kVK_F13 */
291 { VK_F16, 0x67, TRUE }, /* kVK_F16 */
292 { VK_F14, 0x65, TRUE }, /* kVK_F14 */
293 { 0, 0, FALSE }, /* 0x6C unused */
294 { VK_F10, 0x44, TRUE }, /* kVK_F10 */
295 { 0, 0, FALSE }, /* 0x6E unused */
296 { VK_F12, 0x58, TRUE }, /* kVK_F12 */
297 { 0, 0, FALSE }, /* 0x70 unused */
298 { VK_F15, 0x66, TRUE }, /* kVK_F15 */
299 { VK_INSERT, 0x52 | 0x100, TRUE }, /* kVK_Help */ /* map to Insert */
300 { VK_HOME, 0x47 | 0x100, TRUE }, /* kVK_Home */
301 { VK_PRIOR, 0x49 | 0x100, TRUE }, /* kVK_PageUp */
302 { VK_DELETE, 0x53 | 0x100, TRUE }, /* kVK_ForwardDelete */
303 { VK_F4, 0x3E, TRUE }, /* kVK_F4 */
304 { VK_END, 0x4F | 0x100, TRUE }, /* kVK_End */
305 { VK_F2, 0x3C, TRUE }, /* kVK_F2 */
306 { VK_NEXT, 0x51 | 0x100, TRUE }, /* kVK_PageDown */
307 { VK_F1, 0x3B, TRUE }, /* kVK_F1 */
308 { VK_LEFT, 0x4B | 0x100, TRUE }, /* kVK_LeftArrow */
309 { VK_RIGHT, 0x4D | 0x100, TRUE }, /* kVK_RightArrow */
310 { VK_DOWN, 0x50 | 0x100, TRUE }, /* kVK_DownArrow */
311 { VK_UP, 0x48 | 0x100, TRUE }, /* kVK_UpArrow */
315 static const struct {
316 DWORD vkey;
317 const char *name;
318 } vkey_names[] = {
319 { VK_ADD, "Num +" },
320 { VK_BACK, "Backspace" },
321 { VK_CAPITAL, "Caps Lock" },
322 { VK_CONTROL, "Ctrl" },
323 { VK_DECIMAL, "Num Del" },
324 { VK_DELETE | 0x100, "Delete" },
325 { VK_DIVIDE | 0x100, "Num /" },
326 { VK_DOWN | 0x100, "Down" },
327 { VK_END | 0x100, "End" },
328 { VK_ESCAPE, "Esc" },
329 { VK_F1, "F1" },
330 { VK_F2, "F2" },
331 { VK_F3, "F3" },
332 { VK_F4, "F4" },
333 { VK_F5, "F5" },
334 { VK_F6, "F6" },
335 { VK_F7, "F7" },
336 { VK_F8, "F8" },
337 { VK_F9, "F9" },
338 { VK_F10, "F10" },
339 { VK_F11, "F11" },
340 { VK_F12, "F12" },
341 { VK_F13, "F13" },
342 { VK_F14, "F14" },
343 { VK_F15, "F15" },
344 { VK_F16, "F16" },
345 { VK_F17, "F17" },
346 { VK_F18, "F18" },
347 { VK_F19, "F19" },
348 { VK_F20, "F20" },
349 { VK_F21, "F21" },
350 { VK_F22, "F22" },
351 { VK_F23, "F23" },
352 { VK_F24, "F24" },
353 { VK_HELP | 0x100, "Help" },
354 { VK_HOME | 0x100, "Home" },
355 { VK_INSERT | 0x100, "Insert" },
356 { VK_LCONTROL, "Ctrl" },
357 { VK_LEFT | 0x100, "Left" },
358 { VK_LMENU, "Alt" },
359 { VK_LSHIFT, "Shift" },
360 { VK_LWIN | 0x100, "Win" },
361 { VK_MENU, "Alt" },
362 { VK_MULTIPLY, "Num *" },
363 { VK_NEXT | 0x100, "Page Down" },
364 { VK_NUMLOCK | 0x100, "Num Lock" },
365 { VK_NUMPAD0, "Num 0" },
366 { VK_NUMPAD1, "Num 1" },
367 { VK_NUMPAD2, "Num 2" },
368 { VK_NUMPAD3, "Num 3" },
369 { VK_NUMPAD4, "Num 4" },
370 { VK_NUMPAD5, "Num 5" },
371 { VK_NUMPAD6, "Num 6" },
372 { VK_NUMPAD7, "Num 7" },
373 { VK_NUMPAD8, "Num 8" },
374 { VK_NUMPAD9, "Num 9" },
375 { VK_OEM_CLEAR, "Num Clear" },
376 { VK_OEM_NEC_EQUAL | 0x100, "Num =" },
377 { VK_PRIOR | 0x100, "Page Up" },
378 { VK_RCONTROL | 0x100, "Right Ctrl" },
379 { VK_RETURN, "Return" },
380 { VK_RETURN | 0x100, "Num Enter" },
381 { VK_RIGHT | 0x100, "Right" },
382 { VK_RMENU | 0x100, "Right Alt" },
383 { VK_RSHIFT, "Right Shift" },
384 { VK_RWIN | 0x100, "Right Win" },
385 { VK_SEPARATOR, "Num ," },
386 { VK_SHIFT, "Shift" },
387 { VK_SPACE, "Space" },
388 { VK_SUBTRACT, "Num -" },
389 { VK_TAB, "Tab" },
390 { VK_UP | 0x100, "Up" },
391 { VK_VOLUME_DOWN | 0x100, "Volume Down" },
392 { VK_VOLUME_MUTE | 0x100, "Mute" },
393 { VK_VOLUME_UP | 0x100, "Volume Up" },
397 static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
399 BOOL ret;
400 CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
401 CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, strlenW(string), kCFAllocatorNull);
402 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
403 if (ignore_diacritics)
404 flags |= kCFCompareDiacriticInsensitive;
405 ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
406 CFRelease(s1);
407 CFRelease(s2);
408 return ret;
412 /* Filter Apple-specific private-use characters (see NSEvent.h) out of a
413 * string. Returns the length of the string after stripping. */
414 static int strip_apple_private_chars(LPWSTR bufW, int len)
416 int i;
417 for (i = 0; i < len; )
419 if (0xF700 <= bufW[i] && bufW[i] <= 0xF8FF)
421 memmove(&bufW[i], &bufW[i+1], (len - i - 1) * sizeof(bufW[0]));
422 len--;
424 else
425 i++;
427 return len;
431 /***********************************************************************
432 * macdrv_compute_keyboard_layout
434 void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
436 int keyc;
437 WCHAR vkey;
438 const UCKeyboardLayout *uchr;
439 const UInt32 modifier_combos[] = {
441 shiftKey >> 8,
442 cmdKey >> 8,
443 (shiftKey | cmdKey) >> 8,
444 optionKey >> 8,
445 (shiftKey | optionKey) >> 8,
447 UniChar map[128][sizeof(modifier_combos) / sizeof(modifier_combos[0])][4 + 1];
448 int combo;
449 BYTE vkey_used[256];
450 int ignore_diacritics;
451 static const struct {
452 WCHAR wchar;
453 DWORD vkey;
454 } symbol_vkeys[] = {
455 { '-', VK_OEM_MINUS },
456 { '+', VK_OEM_PLUS },
457 { '_', VK_OEM_MINUS },
458 { ',', VK_OEM_COMMA },
459 { '.', VK_OEM_PERIOD },
460 { '=', VK_OEM_PLUS },
461 { '>', VK_OEM_PERIOD },
462 { '<', VK_OEM_COMMA },
463 { '|', VK_OEM_5 },
464 { '\\', VK_OEM_5 },
465 { '`', VK_OEM_3 },
466 { '[', VK_OEM_4 },
467 { '~', VK_OEM_3 },
468 { '?', VK_OEM_2 },
469 { ']', VK_OEM_6 },
470 { '/', VK_OEM_2 },
471 { ':', VK_OEM_1 },
472 { '}', VK_OEM_6 },
473 { '{', VK_OEM_4 },
474 { ';', VK_OEM_1 },
475 { '\'', VK_OEM_7 },
476 { ':', VK_OEM_PERIOD },
477 { ';', VK_OEM_COMMA },
478 { '"', VK_OEM_7 },
479 { 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
480 { '\'', VK_OEM_2 },
481 { 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
482 { '*', VK_OEM_PLUS },
483 { 0x00B4, VK_OEM_7 },
484 { '`', VK_OEM_4 },
485 { '[', VK_OEM_6 },
486 { '/', VK_OEM_5 },
487 { '^', VK_OEM_6 },
488 { '*', VK_OEM_2 },
489 { '{', VK_OEM_6 },
490 { '~', VK_OEM_1 },
491 { '?', VK_OEM_PLUS },
492 { '?', VK_OEM_4 },
493 { 0x00B4, VK_OEM_3 },
494 { '?', VK_OEM_COMMA },
495 { '~', VK_OEM_PLUS },
496 { ']', VK_OEM_4 },
497 { '\'', VK_OEM_3 },
498 { 0x00A7, VK_OEM_7 },
500 int i;
502 /* Vkeys that are suitable for assigning to arbitrary keys, organized in
503 contiguous ranges. */
504 static const struct {
505 WORD first, last;
506 } vkey_ranges[] = {
507 { 'A', 'Z' },
508 { '0', '9' },
509 { VK_OEM_1, VK_OEM_3 },
510 { VK_OEM_4, VK_ICO_CLEAR },
511 { 0xe9, 0xf5 },
512 { VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
513 { VK_F1, VK_F24 },
514 { 0, 0 }
516 int vkey_range;
518 if (!thread_data->keyboard_layout_uchr)
520 ERR("no keyboard layout UCHR data\n");
521 return;
524 memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
525 memset(vkey_used, 0, sizeof(vkey_used));
527 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
529 thread_data->keyc2scan[keyc] = default_map[keyc].scan;
530 if (default_map[keyc].fixed)
532 vkey = default_map[keyc].vkey;
533 thread_data->keyc2vkey[keyc] = vkey;
534 vkey_used[vkey] = 1;
535 TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
539 if (thread_data->iso_keyboard)
541 /* In almost all cases, the Mac key codes indicate a physical key position
542 and this corresponds nicely to Win32 scan codes. However, the Mac key
543 codes differ in one case between ANSI and ISO keyboards. For ANSI
544 keyboards, the key to the left of the digits and above the Tab key
545 produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
546 some position produces kVK_ISO_Section. The additional key on ISO
547 keyboards, the one to the right of the left Shift key, produces
548 kVK_ANSI_Grave, which is just weird.
550 Since we want the key in that upper left corner to always produce the
551 same scan code (0x29), we need to swap the scan codes of those two
552 Mac key codes for ISO keyboards. */
553 DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
554 thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
555 thread_data->keyc2scan[kVK_ISO_Section] = temp;
558 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
560 /* Using the keyboard layout, build a map of key code + modifiers -> characters. */
561 memset(map, 0, sizeof(map));
562 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
564 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
565 if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
567 TRACE("keyc 0x%04x: ", keyc);
569 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
571 UInt32 deadKeyState;
572 UniCharCount len;
573 OSStatus status;
575 deadKeyState = 0;
576 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
577 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
578 &deadKeyState, sizeof(map[keyc][combo])/sizeof(map[keyc][combo][0]) - 1,
579 &len, map[keyc][combo]);
580 if (status != noErr)
581 map[keyc][combo][0] = 0;
583 TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
586 TRACE("\n");
589 /* First try to match key codes to the vkeys for the letters A through Z.
590 Try unmodified first, then with various modifier combinations in succession.
591 On the first pass, try to get a match lacking diacritical marks. On the
592 second pass, accept matches with diacritical marks. */
593 for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
595 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
597 for (vkey = 'A'; vkey <= 'Z'; vkey++)
599 if (vkey_used[vkey])
600 continue;
602 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
604 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
605 continue;
607 if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
609 thread_data->keyc2vkey[keyc] = vkey;
610 vkey_used[vkey] = 1;
611 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
612 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
613 break;
620 /* Next try to match key codes to the vkeys for the digits 0 through 9. */
621 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
623 for (vkey = '0'; vkey <= '9'; vkey++)
625 if (vkey_used[vkey])
626 continue;
628 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
630 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
631 continue;
633 if (char_matches_string(vkey, map[keyc][combo], FALSE))
635 thread_data->keyc2vkey[keyc] = vkey;
636 vkey_used[vkey] = 1;
637 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
638 debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
639 break;
645 /* Now try to match key codes for certain common punctuation characters to
646 the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
647 for (i = 0; i < sizeof(symbol_vkeys) / sizeof(symbol_vkeys[0]); i++)
649 vkey = symbol_vkeys[i].vkey;
651 if (vkey_used[vkey])
652 continue;
654 for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
656 for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
658 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
659 if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
660 continue;
662 if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
664 thread_data->keyc2vkey[keyc] = vkey;
665 vkey_used[vkey] = 1;
666 TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
667 debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
668 break;
672 if (vkey_used[vkey])
673 break;
677 /* For those key codes still without a vkey, try to use the default vkey
678 from the default map, if it's still available. */
679 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
681 DWORD vkey = default_map[keyc].vkey;
683 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
684 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
686 if (!vkey_used[vkey])
688 thread_data->keyc2vkey[keyc] = vkey;
689 vkey_used[vkey] = 1;
690 TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
694 /* For any unassigned key codes which would map to a letter in the default
695 map, but whose normal letter vkey wasn't available, try to find a
696 different letter. */
697 vkey = 'A';
698 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
700 if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
701 continue; /* not a letter in ANSI layout */
702 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
703 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
705 while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
706 if (vkey <= 'Z')
708 thread_data->keyc2vkey[keyc] = vkey;
709 vkey_used[vkey] = 1;
710 TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
712 else
713 break; /* no more unused letter vkeys, so stop trying */
716 /* Same thing but with the digits. */
717 vkey = '0';
718 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
720 if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
721 continue; /* not a digit in ANSI layout */
722 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
723 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
725 while (vkey <= '9' && vkey_used[vkey]) vkey++;
726 if (vkey <= '9')
728 thread_data->keyc2vkey[keyc] = vkey;
729 vkey_used[vkey] = 1;
730 TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
732 else
733 break; /* no more unused digit vkeys, so stop trying */
736 /* Last chance. Assign any available vkey. */
737 vkey_range = 0;
738 vkey = vkey_ranges[vkey_range].first;
739 for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
741 if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
742 if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
744 while (vkey && vkey_used[vkey])
746 if (vkey == vkey_ranges[vkey_range].last)
748 vkey_range++;
749 vkey = vkey_ranges[vkey_range].first;
751 else
752 vkey++;
755 if (!vkey)
757 WARN("No more vkeys available!\n");
758 break;
761 thread_data->keyc2vkey[keyc] = vkey;
762 vkey_used[vkey] = 1;
763 TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
768 /***********************************************************************
769 * macdrv_send_keyboard_input
771 static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD flags, DWORD time)
773 INPUT input;
775 TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
777 input.type = INPUT_KEYBOARD;
778 input.ki.wVk = vkey;
779 input.ki.wScan = scan;
780 input.ki.dwFlags = flags;
781 input.ki.time = time;
782 input.ki.dwExtraInfo = 0;
784 __wine_send_input(hwnd, &input);
788 /***********************************************************************
789 * macdrv_key_event
791 * Handler for KEY_PRESS and KEY_RELEASE events.
793 void macdrv_key_event(HWND hwnd, const macdrv_event *event)
795 struct macdrv_thread_data *thread_data = macdrv_thread_data();
796 WORD vkey, scan;
797 DWORD flags;
799 TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
800 hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
801 event->key.keycode, event->key.modifiers);
803 thread_data->last_modifiers = event->key.modifiers;
805 if (event->key.keycode < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
807 vkey = thread_data->keyc2vkey[event->key.keycode];
808 scan = thread_data->keyc2scan[event->key.keycode];
810 else
811 vkey = scan = 0;
813 TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
814 event->key.keycode, vkey, scan);
816 if (!vkey) return;
818 flags = 0;
819 if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
820 if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
822 macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
826 /***********************************************************************
827 * macdrv_keyboard_changed
829 * Handler for KEYBOARD_CHANGED events.
831 void macdrv_keyboard_changed(const macdrv_event *event)
833 struct macdrv_thread_data *thread_data = macdrv_thread_data();
835 TRACE("new keyboard layout uchr data %p, type %u, iso %d\n", event->keyboard_changed.uchr,
836 event->keyboard_changed.keyboard_type, event->keyboard_changed.iso_keyboard);
838 if (thread_data->keyboard_layout_uchr)
839 CFRelease(thread_data->keyboard_layout_uchr);
840 thread_data->keyboard_layout_uchr = CFDataCreateCopy(NULL, event->keyboard_changed.uchr);
841 thread_data->keyboard_type = event->keyboard_changed.keyboard_type;
842 thread_data->iso_keyboard = event->keyboard_changed.iso_keyboard;
843 thread_data->dead_key_state = 0;
845 macdrv_compute_keyboard_layout(thread_data);
849 /***********************************************************************
850 * get_locale_keyboard_layout
852 static HKL get_locale_keyboard_layout(void)
854 ULONG_PTR layout;
855 LANGID langid;
857 layout = GetUserDefaultLCID();
860 * Microsoft Office expects this value to be something specific
861 * for Japanese and Korean Windows with an IME the value is 0xe001
862 * We should probably check to see if an IME exists and if so then
863 * set this word properly.
865 langid = PRIMARYLANGID(LANGIDFROMLCID(layout));
866 if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
867 layout |= 0xe001 << 16; /* IME */
868 else
869 layout |= layout << 16;
871 return (HKL)layout;
875 /***********************************************************************
876 * match_keyboard_layout
878 static BOOL match_keyboard_layout(HKL hkl)
880 const DWORD isIME = 0xE0000000;
881 HKL current_hkl = get_locale_keyboard_layout();
883 /* if the layout is an IME, only match the low word (LCID) */
884 if (((ULONG_PTR)hkl & isIME) == isIME)
885 return (LOWORD(hkl) == LOWORD(current_hkl));
886 else
887 return (hkl == current_hkl);
891 /***********************************************************************
892 * macdrv_process_text_input
894 BOOL macdrv_process_text_input(UINT vkey, UINT scan, UINT repeat, const BYTE *key_state, void *himc)
896 struct macdrv_thread_data *thread_data = macdrv_thread_data();
897 unsigned int flags;
898 int keyc;
899 BOOL ret = FALSE;
901 TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", vkey, scan, repeat, himc);
903 flags = thread_data->last_modifiers;
904 if (key_state[VK_SHIFT] & 0x80)
905 flags |= NX_SHIFTMASK;
906 else
907 flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK);
908 if (key_state[VK_CAPITAL] & 0x01)
909 flags |= NX_ALPHASHIFTMASK;
910 else
911 flags &= ~NX_ALPHASHIFTMASK;
912 if (key_state[VK_CONTROL] & 0x80)
913 flags |= NX_CONTROLMASK;
914 else
915 flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK);
916 if (key_state[VK_MENU] & 0x80)
917 flags |= NX_COMMANDMASK;
918 else
919 flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
921 /* Find the Mac keycode corresponding to the scan code */
922 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
923 if (thread_data->keyc2vkey[keyc] == vkey) break;
925 if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
926 goto done;
928 TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc);
930 ret = macdrv_send_text_input_event(((scan & 0x8000) == 0), flags, repeat, keyc, himc);
932 done:
933 TRACE(" -> %s\n", ret ? "TRUE" : "FALSE");
934 return ret;
938 /***********************************************************************
939 * ActivateKeyboardLayout (MACDRV.@)
941 HKL CDECL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
943 HKL oldHkl = 0;
944 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
946 /* FIXME: Use Text Input Services or NSTextInputContext to actually
947 change the Mac keyboard input source. */
949 FIXME("hkl %p flags %04x: semi-stub!\n", hkl, flags);
950 if (flags & KLF_SETFORPROCESS)
952 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
953 FIXME("KLF_SETFORPROCESS not supported\n");
954 return 0;
957 if (flags)
958 FIXME("flags %x not supported\n",flags);
960 if (hkl == (HKL)HKL_NEXT || hkl == (HKL)HKL_PREV)
962 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
963 FIXME("HKL_NEXT and HKL_PREV not supported\n");
964 return 0;
967 if (!match_keyboard_layout(hkl))
969 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
970 FIXME("setting keyboard of different locales not supported\n");
971 return 0;
974 oldHkl = thread_data->active_keyboard_layout;
975 if (!oldHkl) oldHkl = get_locale_keyboard_layout();
977 thread_data->active_keyboard_layout = hkl;
979 return oldHkl;
983 /***********************************************************************
984 * Beep (MACDRV.@)
986 void CDECL macdrv_Beep(void)
988 macdrv_beep();
992 /***********************************************************************
993 * GetKeyNameText (MACDRV.@)
995 INT CDECL macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
997 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
998 int scan, keyc;
1000 scan = (lparam >> 16) & 0x1FF;
1001 for (keyc = 0; keyc < sizeof(thread_data->keyc2scan)/sizeof(thread_data->keyc2scan[0]); keyc++)
1003 if (thread_data->keyc2scan[keyc] == scan)
1005 static const WCHAR dead[] = {' ','d','e','a','d',0};
1006 const UCKeyboardLayout *uchr;
1007 UInt32 deadKeyState = 0;
1008 UniCharCount len;
1009 OSStatus status;
1011 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1012 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
1013 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
1014 if (status != noErr)
1015 len = 0;
1016 if (len && isgraphW(buffer[0]))
1017 buffer[len] = 0;
1018 else
1020 DWORD vkey = thread_data->keyc2vkey[keyc];
1021 int i;
1023 if (scan & 0x100) vkey |= 0x100;
1025 if (lparam & (1 << 25))
1027 /* Caller doesn't care about distinctions between left and
1028 right keys. */
1029 switch (vkey)
1031 case VK_LSHIFT:
1032 case VK_RSHIFT:
1033 vkey = VK_SHIFT; break;
1034 case VK_LCONTROL:
1035 case VK_RCONTROL:
1036 vkey = VK_CONTROL; break;
1037 case VK_LMENU:
1038 case VK_RMENU:
1039 vkey = VK_MENU; break;
1043 len = 0;
1044 for (i = 0; i < sizeof(vkey_names) / sizeof(vkey_names[0]); i++)
1046 if (vkey_names[i].vkey == vkey)
1048 len = MultiByteToWideChar(CP_UTF8, 0, vkey_names[i].name, -1, buffer, size);
1049 if (len) len--;
1050 break;
1054 if (!len)
1056 static const WCHAR format[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1057 snprintfW(buffer, size, format, vkey);
1058 len = strlenW(buffer);
1062 if (!len)
1063 break;
1065 if (status == noErr && deadKeyState)
1067 lstrcpynW(buffer + len, dead, size - len);
1068 len = strlenW(buffer);
1071 TRACE("lparam 0x%08x -> %s\n", lparam, debugstr_w(buffer));
1072 return len;
1076 WARN("found no name for lparam 0x%08x\n", lparam);
1077 return 0;
1081 /***********************************************************************
1082 * GetKeyboardLayout (MACDRV.@)
1084 HKL CDECL macdrv_GetKeyboardLayout(DWORD thread_id)
1086 if (!thread_id || thread_id == GetCurrentThreadId())
1088 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1089 if (thread_data && thread_data->active_keyboard_layout)
1090 return thread_data->active_keyboard_layout;
1092 else
1093 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id);
1095 /* FIXME: Use TISGetInputSourceProperty() and kTISPropertyInputSourceLanguages
1096 * to get input source language ID string. Use
1097 * CFLocaleGetWindowsLocaleCodeFromLocaleIdentifier() to convert that
1098 * to a Windows locale ID and from there to a layout handle.
1101 return get_locale_keyboard_layout();
1105 /***********************************************************************
1106 * GetKeyboardLayoutName (MACDRV.@)
1108 BOOL CDECL macdrv_GetKeyboardLayoutName(LPWSTR name)
1110 static const WCHAR formatW[] = {'%','0','8','x',0};
1111 DWORD layout;
1113 layout = HandleToUlong(get_locale_keyboard_layout());
1114 if (HIWORD(layout) == LOWORD(layout)) layout = LOWORD(layout);
1115 sprintfW(name, formatW, layout);
1116 TRACE("returning %s\n", debugstr_w(name));
1117 return TRUE;
1121 /***********************************************************************
1122 * MapVirtualKeyEx (MACDRV.@)
1124 UINT CDECL macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1126 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1127 UINT ret = 0;
1128 int keyc;
1130 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1132 switch (wMapType)
1134 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1135 case MAPVK_VK_TO_VSC_EX:
1136 switch (wCode)
1138 case VK_SHIFT: wCode = VK_LSHIFT; break;
1139 case VK_CONTROL: wCode = VK_LCONTROL; break;
1140 case VK_MENU: wCode = VK_LMENU; break;
1143 /* vkey -> keycode -> scan */
1144 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1146 if (thread_data->keyc2vkey[keyc] == wCode)
1148 ret = thread_data->keyc2scan[keyc] & 0xFF;
1149 break;
1152 break;
1154 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1155 case MAPVK_VSC_TO_VK_EX:
1156 /* scan -> keycode -> vkey */
1157 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1158 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1160 ret = thread_data->keyc2vkey[keyc];
1161 /* Only stop if it's not a numpad vkey; otherwise keep
1162 looking for a potential better vkey. */
1163 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1164 break;
1167 if (wMapType == MAPVK_VSC_TO_VK)
1168 switch (ret)
1170 case VK_LSHIFT:
1171 case VK_RSHIFT:
1172 ret = VK_SHIFT; break;
1173 case VK_LCONTROL:
1174 case VK_RCONTROL:
1175 ret = VK_CONTROL; break;
1176 case VK_LMENU:
1177 case VK_RMENU:
1178 ret = VK_MENU; break;
1181 break;
1183 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1185 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1186 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1187 const UCKeyboardLayout *uchr;
1188 UniChar s[10];
1189 OSStatus status;
1190 UInt32 deadKeyState;
1191 UniCharCount len;
1192 BOOL deadKey = FALSE;
1194 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1195 (VK_F1 <= wCode && wCode <= VK_F24))
1196 break;
1198 if (!thread_data || !thread_data->keyboard_layout_uchr)
1200 WARN("No keyboard layout uchr data\n");
1201 break;
1204 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1206 /* Find the Mac keycode corresponding to the vkey */
1207 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1208 if (thread_data->keyc2vkey[keyc] == wCode) break;
1210 if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
1212 WARN("Unknown virtual key %X\n", wCode);
1213 break;
1216 TRACE("Found keycode %u\n", keyc);
1218 deadKeyState = 0;
1219 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1220 thread_data->keyboard_type, 0, &deadKeyState,
1221 sizeof(s)/sizeof(s[0]), &len, s);
1222 if (status == noErr && !len && deadKeyState)
1224 deadKey = TRUE;
1225 deadKeyState = 0;
1226 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1227 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1228 &deadKeyState, sizeof(s)/sizeof(s[0]), &len, s);
1231 if (status == noErr && len)
1232 ret = toupperW(s[0]) | (deadKey ? 0x80000000 : 0);
1234 break;
1236 default: /* reserved */
1237 FIXME("Unknown wMapType %d\n", wMapType);
1238 break;
1241 TRACE("returning 0x%04x\n", ret);
1242 return ret;
1246 /***********************************************************************
1247 * ToUnicodeEx (MACDRV.@)
1249 * The ToUnicode function translates the specified virtual-key code and keyboard
1250 * state to the corresponding Windows character or characters.
1252 * If the specified key is a dead key, the return value is negative. Otherwise,
1253 * it is one of the following values:
1254 * Value Meaning
1255 * -1 The specified virtual key is a dead-key. If possible, the
1256 * non-combining form of the dead character is written to bufW.
1257 * 0 The specified virtual key has no translation for the current
1258 * state of the keyboard.
1259 * 1 One Windows character was copied to the buffer.
1260 * 2 or more Multiple characters were copied to the buffer. This usually
1261 * happens when a dead-key character (accent or diacritic) stored
1262 * in the keyboard layout cannot be composed with the specified
1263 * virtual key to form a single character.
1266 INT CDECL macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1267 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1269 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1270 INT ret = 0;
1271 int keyc;
1272 BOOL is_menu = (flags & 0x1);
1273 OSStatus status;
1274 const UCKeyboardLayout *uchr;
1275 UInt16 keyAction;
1276 UInt32 modifierKeyState;
1277 OptionBits options;
1278 UInt32 deadKeyState, savedDeadKeyState;
1279 UniCharCount len;
1280 BOOL dead = FALSE;
1282 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1283 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1285 if (!virtKey)
1286 goto done;
1288 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1289 modifier key press. We want it to effectively ignore modifier key
1290 presses. I think that one isn't supposed to call it at all for modifier
1291 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1292 are different event types than key up/down events. */
1293 switch (virtKey)
1295 case VK_SHIFT:
1296 case VK_CONTROL:
1297 case VK_MENU:
1298 case VK_CAPITAL:
1299 case VK_LSHIFT:
1300 case VK_RSHIFT:
1301 case VK_LCONTROL:
1302 case VK_RCONTROL:
1303 case VK_LMENU:
1304 case VK_RMENU:
1305 goto done;
1308 /* There are a number of key combinations for which Windows does not
1309 produce characters, but Mac keyboard layouts may. Eat them. Do this
1310 here to avoid the expense of UCKeyTranslate() but also because these
1311 keys shouldn't terminate dead key sequences. */
1312 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1313 goto done;
1315 /* Shift + <non-digit keypad keys>. */
1316 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1317 goto done;
1319 if (lpKeyState[VK_CONTROL] & 0x80)
1321 /* Control-Tab, with or without other modifiers. */
1322 if (virtKey == VK_TAB)
1323 goto done;
1325 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1326 for these keys. */
1327 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1329 switch (virtKey)
1331 case VK_CANCEL:
1332 case VK_BACK:
1333 case VK_ESCAPE:
1334 case VK_SPACE:
1335 case VK_RETURN:
1336 goto done;
1341 if (thread_data->keyboard_layout_uchr)
1342 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1343 else
1344 uchr = NULL;
1346 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1348 modifierKeyState = 0;
1349 if (lpKeyState[VK_SHIFT] & 0x80)
1350 modifierKeyState |= (shiftKey >> 8);
1351 if (lpKeyState[VK_CAPITAL] & 0x01)
1352 modifierKeyState |= (alphaLock >> 8);
1353 if (lpKeyState[VK_CONTROL] & 0x80)
1354 modifierKeyState |= (controlKey >> 8);
1355 if (lpKeyState[VK_MENU] & 0x80)
1356 modifierKeyState |= (cmdKey >> 8);
1357 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1358 modifierKeyState |= (optionKey >> 8);
1360 /* Find the Mac keycode corresponding to the vkey */
1361 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1362 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1364 if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
1366 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1367 goto done;
1370 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1371 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1373 if (is_menu)
1375 if (keyAction == kUCKeyActionUp)
1376 goto done;
1378 options = kUCKeyTranslateNoDeadKeysMask;
1379 deadKeyState = 0;
1381 else
1383 options = 0;
1384 deadKeyState = thread_data->dead_key_state;
1386 savedDeadKeyState = deadKeyState;
1387 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1388 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1389 &len, bufW);
1390 if (status != noErr)
1392 ERR_(key)("Couldn't translate keycode 0x%04x, status %ld\n", keyc, status);
1393 goto done;
1395 if (!is_menu)
1397 thread_data->dead_key_state = deadKeyState;
1399 if (keyAction == kUCKeyActionUp)
1400 goto done;
1403 if (len == 0 && deadKeyState)
1405 /* Repeat the translation, but disabling dead-key generation to
1406 learn which dead key it was. */
1407 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1408 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1409 &savedDeadKeyState, bufW_size, &len, bufW);
1410 if (status != noErr)
1412 ERR_(key)("Couldn't translate keycode 0x%04x, status %ld\n", keyc, status);
1413 goto done;
1416 dead = TRUE;
1419 if (len > 0)
1420 len = strip_apple_private_chars(bufW, len);
1422 if (dead && len > 0) ret = -1;
1423 else ret = len;
1425 /* Control-Return produces line feed instead of carriage return. */
1426 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1428 int i;
1429 for (i = 0; i < len; i++)
1430 if (bufW[i] == '\r')
1431 bufW[i] = '\n';
1434 done:
1435 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1436 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1437 if (1 <= ret && ret < bufW_size)
1438 bufW[ret] = 0;
1440 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, abs(ret)));
1441 return ret;
1445 /***********************************************************************
1446 * VkKeyScanEx (MACDRV.@)
1448 * Note: Windows ignores HKL parameter and uses current active layout instead
1450 SHORT CDECL macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1452 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1453 SHORT ret = -1;
1454 int state;
1455 const UCKeyboardLayout *uchr;
1457 TRACE("%04x, %p\n", wChar, hkl);
1459 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1460 if (!uchr)
1462 TRACE("no keyboard layout UCHR data; returning -1\n");
1463 return -1;
1466 for (state = 0; state < 8; state++)
1468 UInt32 modifierKeyState = 0;
1469 int keyc;
1471 if (state & 1)
1472 modifierKeyState |= (shiftKey >> 8);
1473 if ((state & 6) == 6)
1474 modifierKeyState |= (optionKey >> 8);
1475 else
1477 if (state & 2)
1478 modifierKeyState |= (controlKey >> 8);
1479 if (state & 4)
1480 modifierKeyState |= (cmdKey >> 8);
1483 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey) / sizeof(thread_data->keyc2vkey[0]); keyc++)
1485 UInt32 deadKeyState = 0;
1486 UniChar uchar;
1487 UniCharCount len;
1488 OSStatus status;
1490 if (!thread_data->keyc2vkey[keyc]) continue;
1492 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1493 thread_data->keyboard_type, 0, &deadKeyState,
1494 1, &len, &uchar);
1495 if (status == noErr && len == 1 && uchar == wChar)
1497 WORD vkey = thread_data->keyc2vkey[keyc];
1499 ret = vkey | (state << 8);
1500 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1501 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1502 keyc == kVK_ANSI_KeypadEquals)
1504 /* Keep searching for a non-numpad match, which is preferred. */
1506 else
1507 goto done;
1512 done:
1513 TRACE(" -> 0x%04x\n", ret);
1514 return ret;