riched20: Move underline drawing to a common function.
[wine.git] / dlls / winemac.drv / keyboard.c
blob7b773e3347f3ee5cca3681358d3e2bab7e1f0cfc
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 * ActivateKeyboardLayout (MACDRV.@)
894 HKL CDECL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
896 HKL oldHkl = 0;
897 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
899 /* FIXME: Use Text Input Services or NSTextInputContext to actually
900 change the Mac keyboard input source. */
902 FIXME("hkl %p flags %04x: semi-stub!\n", hkl, flags);
903 if (flags & KLF_SETFORPROCESS)
905 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
906 FIXME("KLF_SETFORPROCESS not supported\n");
907 return 0;
910 if (flags)
911 FIXME("flags %x not supported\n",flags);
913 if (hkl == (HKL)HKL_NEXT || hkl == (HKL)HKL_PREV)
915 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
916 FIXME("HKL_NEXT and HKL_PREV not supported\n");
917 return 0;
920 if (!match_keyboard_layout(hkl))
922 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
923 FIXME("setting keyboard of different locales not supported\n");
924 return 0;
927 oldHkl = thread_data->active_keyboard_layout;
928 if (!oldHkl) oldHkl = get_locale_keyboard_layout();
930 thread_data->active_keyboard_layout = hkl;
932 return oldHkl;
936 /***********************************************************************
937 * Beep (MACDRV.@)
939 void CDECL macdrv_Beep(void)
941 macdrv_beep();
945 /***********************************************************************
946 * GetKeyNameText (MACDRV.@)
948 INT CDECL macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size)
950 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
951 int scan, keyc;
953 scan = (lparam >> 16) & 0x1FF;
954 for (keyc = 0; keyc < sizeof(thread_data->keyc2scan)/sizeof(thread_data->keyc2scan[0]); keyc++)
956 if (thread_data->keyc2scan[keyc] == scan)
958 static const WCHAR dead[] = {' ','d','e','a','d',0};
959 const UCKeyboardLayout *uchr;
960 UInt32 deadKeyState = 0;
961 UniCharCount len;
962 OSStatus status;
964 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
965 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, 0, thread_data->keyboard_type,
966 0, &deadKeyState, size - 1, &len, (UniChar*)buffer);
967 if (status != noErr)
968 len = 0;
969 if (len && isgraphW(buffer[0]))
970 buffer[len] = 0;
971 else
973 DWORD vkey = thread_data->keyc2vkey[keyc];
974 int i;
976 if (scan & 0x100) vkey |= 0x100;
978 if (lparam & (1 << 25))
980 /* Caller doesn't care about distinctions between left and
981 right keys. */
982 switch (vkey)
984 case VK_LSHIFT:
985 case VK_RSHIFT:
986 vkey = VK_SHIFT; break;
987 case VK_LCONTROL:
988 case VK_RCONTROL:
989 vkey = VK_CONTROL; break;
990 case VK_LMENU:
991 case VK_RMENU:
992 vkey = VK_MENU; break;
996 len = 0;
997 for (i = 0; i < sizeof(vkey_names) / sizeof(vkey_names[0]); i++)
999 if (vkey_names[i].vkey == vkey)
1001 len = MultiByteToWideChar(CP_UTF8, 0, vkey_names[i].name, -1, buffer, size);
1002 if (len) len--;
1003 break;
1007 if (!len)
1009 static const WCHAR format[] = {'K','e','y',' ','0','x','%','0','2','x',0};
1010 snprintfW(buffer, size, format, vkey);
1011 len = strlenW(buffer);
1015 if (!len)
1016 break;
1018 if (status == noErr && deadKeyState)
1020 lstrcpynW(buffer + len, dead, size - len);
1021 len = strlenW(buffer);
1024 TRACE("lparam 0x%08x -> %s\n", lparam, debugstr_w(buffer));
1025 return len;
1029 WARN("found no name for lparam 0x%08x\n", lparam);
1030 return 0;
1034 /***********************************************************************
1035 * GetKeyboardLayout (MACDRV.@)
1037 HKL CDECL macdrv_GetKeyboardLayout(DWORD thread_id)
1039 if (!thread_id || thread_id == GetCurrentThreadId())
1041 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1042 if (thread_data && thread_data->active_keyboard_layout)
1043 return thread_data->active_keyboard_layout;
1045 else
1046 FIXME("couldn't return keyboard layout for thread %04x\n", thread_id);
1048 /* FIXME: Use TISGetInputSourceProperty() and kTISPropertyInputSourceLanguages
1049 * to get input source language ID string. Use
1050 * CFLocaleGetWindowsLocaleCodeFromLocaleIdentifier() to convert that
1051 * to a Windows locale ID and from there to a layout handle.
1054 return get_locale_keyboard_layout();
1058 /***********************************************************************
1059 * GetKeyboardLayoutName (MACDRV.@)
1061 BOOL CDECL macdrv_GetKeyboardLayoutName(LPWSTR name)
1063 static const WCHAR formatW[] = {'%','0','8','x',0};
1064 DWORD layout;
1066 layout = HandleToUlong(get_locale_keyboard_layout());
1067 if (HIWORD(layout) == LOWORD(layout)) layout = LOWORD(layout);
1068 sprintfW(name, formatW, layout);
1069 TRACE("returning %s\n", debugstr_w(name));
1070 return TRUE;
1074 /***********************************************************************
1075 * MapVirtualKeyEx (MACDRV.@)
1077 UINT CDECL macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
1079 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1080 UINT ret = 0;
1081 int keyc;
1083 TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
1085 switch (wMapType)
1087 case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
1088 case MAPVK_VK_TO_VSC_EX:
1089 switch (wCode)
1091 case VK_SHIFT: wCode = VK_LSHIFT; break;
1092 case VK_CONTROL: wCode = VK_LCONTROL; break;
1093 case VK_MENU: wCode = VK_LMENU; break;
1096 /* vkey -> keycode -> scan */
1097 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1099 if (thread_data->keyc2vkey[keyc] == wCode)
1101 ret = thread_data->keyc2scan[keyc] & 0xFF;
1102 break;
1105 break;
1107 case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
1108 case MAPVK_VSC_TO_VK_EX:
1109 /* scan -> keycode -> vkey */
1110 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1111 if ((thread_data->keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1113 ret = thread_data->keyc2vkey[keyc];
1114 /* Only stop if it's not a numpad vkey; otherwise keep
1115 looking for a potential better vkey. */
1116 if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
1117 break;
1120 if (wMapType == MAPVK_VSC_TO_VK)
1121 switch (ret)
1123 case VK_LSHIFT:
1124 case VK_RSHIFT:
1125 ret = VK_SHIFT; break;
1126 case VK_LCONTROL:
1127 case VK_RCONTROL:
1128 ret = VK_CONTROL; break;
1129 case VK_LMENU:
1130 case VK_RMENU:
1131 ret = VK_MENU; break;
1134 break;
1136 case MAPVK_VK_TO_CHAR: /* vkey-code to character */
1138 /* vkey -> keycode -> (UCKeyTranslate) wide char */
1139 struct macdrv_thread_data *thread_data = macdrv_thread_data();
1140 const UCKeyboardLayout *uchr;
1141 UniChar s[10];
1142 OSStatus status;
1143 UInt32 deadKeyState;
1144 UniCharCount len;
1145 BOOL deadKey = FALSE;
1147 if ((VK_PRIOR <= wCode && wCode <= VK_HELP) ||
1148 (VK_F1 <= wCode && wCode <= VK_F24))
1149 break;
1151 if (!thread_data || !thread_data->keyboard_layout_uchr)
1153 WARN("No keyboard layout uchr data\n");
1154 break;
1157 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1159 /* Find the Mac keycode corresponding to the vkey */
1160 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1161 if (thread_data->keyc2vkey[keyc] == wCode) break;
1163 if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
1165 WARN("Unknown virtual key %X\n", wCode);
1166 break;
1169 TRACE("Found keycode %u\n", keyc);
1171 deadKeyState = 0;
1172 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1173 thread_data->keyboard_type, 0, &deadKeyState,
1174 sizeof(s)/sizeof(s[0]), &len, s);
1175 if (status == noErr && !len && deadKeyState)
1177 deadKey = TRUE;
1178 deadKeyState = 0;
1179 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, 0,
1180 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1181 &deadKeyState, sizeof(s)/sizeof(s[0]), &len, s);
1184 if (status == noErr && len)
1185 ret = toupperW(s[0]) | (deadKey ? 0x80000000 : 0);
1187 break;
1189 default: /* reserved */
1190 FIXME("Unknown wMapType %d\n", wMapType);
1191 break;
1194 TRACE("returning 0x%04x\n", ret);
1195 return ret;
1199 /***********************************************************************
1200 * ToUnicodeEx (MACDRV.@)
1202 * The ToUnicode function translates the specified virtual-key code and keyboard
1203 * state to the corresponding Windows character or characters.
1205 * If the specified key is a dead key, the return value is negative. Otherwise,
1206 * it is one of the following values:
1207 * Value Meaning
1208 * -1 The specified virtual key is a dead-key. If possible, the
1209 * non-combining form of the dead character is written to bufW.
1210 * 0 The specified virtual key has no translation for the current
1211 * state of the keyboard.
1212 * 1 One Windows character was copied to the buffer.
1213 * 2 or more Multiple characters were copied to the buffer. This usually
1214 * happens when a dead-key character (accent or diacritic) stored
1215 * in the keyboard layout cannot be composed with the specified
1216 * virtual key to form a single character.
1219 INT CDECL macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
1220 LPWSTR bufW, int bufW_size, UINT flags, HKL hkl)
1222 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1223 INT ret = 0;
1224 int keyc;
1225 BOOL is_menu = (flags & 0x1);
1226 OSStatus status;
1227 const UCKeyboardLayout *uchr;
1228 UInt16 keyAction;
1229 UInt32 modifierKeyState;
1230 OptionBits options;
1231 UInt32 deadKeyState, savedDeadKeyState;
1232 UniCharCount len;
1233 BOOL dead = FALSE;
1235 TRACE_(key)("virtKey 0x%04x scanCode 0x%04x lpKeyState %p bufW %p bufW_size %d flags 0x%08x hkl %p\n",
1236 virtKey, scanCode, lpKeyState, bufW, bufW_size, flags, hkl);
1238 if (!virtKey)
1239 goto done;
1241 /* UCKeyTranslate, below, terminates a dead-key sequence if passed a
1242 modifier key press. We want it to effectively ignore modifier key
1243 presses. I think that one isn't supposed to call it at all for modifier
1244 events (e.g. NSFlagsChanged or kEventRawKeyModifiersChanged), since they
1245 are different event types than key up/down events. */
1246 switch (virtKey)
1248 case VK_SHIFT:
1249 case VK_CONTROL:
1250 case VK_MENU:
1251 case VK_CAPITAL:
1252 case VK_LSHIFT:
1253 case VK_RSHIFT:
1254 case VK_LCONTROL:
1255 case VK_RCONTROL:
1256 case VK_LMENU:
1257 case VK_RMENU:
1258 goto done;
1261 /* There are a number of key combinations for which Windows does not
1262 produce characters, but Mac keyboard layouts may. Eat them. Do this
1263 here to avoid the expense of UCKeyTranslate() but also because these
1264 keys shouldn't terminate dead key sequences. */
1265 if ((VK_PRIOR <= virtKey && virtKey <= VK_HELP) || (VK_F1 <= virtKey && virtKey <= VK_F24))
1266 goto done;
1268 /* Shift + <non-digit keypad keys>. */
1269 if ((lpKeyState[VK_SHIFT] & 0x80) && VK_MULTIPLY <= virtKey && virtKey <= VK_DIVIDE)
1270 goto done;
1272 if (lpKeyState[VK_CONTROL] & 0x80)
1274 /* Control-Tab, with or without other modifiers. */
1275 if (virtKey == VK_TAB)
1276 goto done;
1278 /* Control-Shift-<key>, Control-Alt-<key>, and Control-Alt-Shift-<key>
1279 for these keys. */
1280 if ((lpKeyState[VK_SHIFT] & 0x80) || (lpKeyState[VK_MENU] & 0x80))
1282 switch (virtKey)
1284 case VK_CANCEL:
1285 case VK_BACK:
1286 case VK_ESCAPE:
1287 case VK_SPACE:
1288 case VK_RETURN:
1289 goto done;
1294 if (thread_data->keyboard_layout_uchr)
1295 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1296 else
1297 uchr = NULL;
1299 keyAction = (scanCode & 0x8000) ? kUCKeyActionUp : kUCKeyActionDown;
1301 modifierKeyState = 0;
1302 if (lpKeyState[VK_SHIFT] & 0x80)
1303 modifierKeyState |= (shiftKey >> 8);
1304 if (lpKeyState[VK_CAPITAL] & 0x01)
1305 modifierKeyState |= (alphaLock >> 8);
1306 if (lpKeyState[VK_CONTROL] & 0x80)
1307 modifierKeyState |= (controlKey >> 8);
1308 if (lpKeyState[VK_MENU] & 0x80)
1309 modifierKeyState |= (cmdKey >> 8);
1310 if (thread_data->last_modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
1311 modifierKeyState |= (optionKey >> 8);
1313 /* Find the Mac keycode corresponding to the vkey */
1314 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++)
1315 if (thread_data->keyc2vkey[keyc] == virtKey) break;
1317 if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
1319 WARN_(key)("Unknown virtual key 0x%04x\n", virtKey);
1320 goto done;
1323 TRACE_(key)("Key code 0x%04x %s, faked modifiers = 0x%04x\n", keyc,
1324 (keyAction == kUCKeyActionDown) ? "pressed" : "released", (unsigned)modifierKeyState);
1326 if (is_menu)
1328 options = kUCKeyTranslateNoDeadKeysMask;
1329 deadKeyState = 0;
1331 else
1333 options = 0;
1334 deadKeyState = thread_data->dead_key_state;
1336 savedDeadKeyState = deadKeyState;
1337 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1338 thread_data->keyboard_type, options, &deadKeyState, bufW_size,
1339 &len, bufW);
1340 if (status != noErr)
1342 ERR_(key)("Couldn't translate keycode 0x%04x, status %ld\n", keyc, status);
1343 goto done;
1345 if (!is_menu)
1346 thread_data->dead_key_state = deadKeyState;
1348 if (len == 0 && deadKeyState)
1350 /* Repeat the translation, but disabling dead-key generation to
1351 learn which dead key it was. */
1352 status = UCKeyTranslate(uchr, keyc, keyAction, modifierKeyState,
1353 thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
1354 &savedDeadKeyState, bufW_size, &len, bufW);
1355 if (status != noErr)
1357 ERR_(key)("Couldn't translate keycode 0x%04x, status %ld\n", keyc, status);
1358 goto done;
1361 dead = TRUE;
1364 if (len > 0)
1365 len = strip_apple_private_chars(bufW, len);
1367 if (dead && len > 0) ret = -1;
1368 else ret = len;
1370 /* Control-Return produces line feed instead of carriage return. */
1371 if (ret > 0 && (lpKeyState[VK_CONTROL] & 0x80) && virtKey == VK_RETURN)
1373 int i;
1374 for (i = 0; i < len; i++)
1375 if (bufW[i] == '\r')
1376 bufW[i] = '\n';
1379 done:
1380 /* Null-terminate the buffer, if there's room. MSDN clearly states that the
1381 caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
1382 if (1 <= ret && ret < bufW_size)
1383 bufW[ret] = 0;
1385 TRACE_(key)("returning %d / %s\n", ret, debugstr_wn(bufW, abs(ret)));
1386 return ret;
1390 /***********************************************************************
1391 * VkKeyScanEx (MACDRV.@)
1393 * Note: Windows ignores HKL parameter and uses current active layout instead
1395 SHORT CDECL macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl)
1397 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
1398 SHORT ret = -1;
1399 int state;
1400 const UCKeyboardLayout *uchr;
1402 TRACE("%04x, %p\n", wChar, hkl);
1404 uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
1405 if (!uchr)
1407 TRACE("no keyboard layout UCHR data; returning -1\n");
1408 return -1;
1411 for (state = 0; state < 8; state++)
1413 UInt32 modifierKeyState = 0;
1414 int keyc;
1416 if (state & 1)
1417 modifierKeyState |= (shiftKey >> 8);
1418 if ((state & 6) == 6)
1419 modifierKeyState |= (optionKey >> 8);
1420 else
1422 if (state & 2)
1423 modifierKeyState |= (controlKey >> 8);
1424 if (state & 4)
1425 modifierKeyState |= (cmdKey >> 8);
1428 for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey) / sizeof(thread_data->keyc2vkey[0]); keyc++)
1430 UInt32 deadKeyState = 0;
1431 UniChar uchar;
1432 UniCharCount len;
1433 OSStatus status;
1435 if (!thread_data->keyc2vkey[keyc]) continue;
1437 status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifierKeyState,
1438 thread_data->keyboard_type, 0, &deadKeyState,
1439 1, &len, &uchar);
1440 if (status == noErr && len == 1 && uchar == wChar)
1442 WORD vkey = thread_data->keyc2vkey[keyc];
1444 ret = vkey | (state << 8);
1445 if ((VK_NUMPAD0 <= vkey && vkey <= VK_DIVIDE) ||
1446 keyc == kVK_ANSI_KeypadClear || keyc == kVK_ANSI_KeypadEnter ||
1447 keyc == kVK_ANSI_KeypadEquals)
1449 /* Keep searching for a non-numpad match, which is preferred. */
1451 else
1452 goto done;
1457 done:
1458 TRACE(" -> 0x%04x\n", ret);
1459 return ret;