1/*
2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#import "java_awt_event_InputEvent.h"
27#import "java_awt_event_KeyEvent.h"
28#import "LWCToolkit.h"
29
30#import "JNIUtilities.h"
31
32#import <sys/time.h>
33#import <Carbon/Carbon.h>
34
35/*
36 * Table to map typed characters to their Java virtual key equivalent and back.
37 * We use the incoming unichar (ignoring all modifiers) and try to figure out
38 * which virtual key code is appropriate. A lot of them just have direct
39 * mappings (the function keys, arrow keys, etc.) so they aren't a problem.
40 * We had to do something a little funky to catch the keys on the numeric
41 * key pad (i.e. using event mask to distinguish between period on regular
42 * keyboard and decimal on keypad). We also have to do something incredibly
43 * hokey with regards to the shifted punctuation characters. For examples,
44 * consider '&' which is usually Shift-7.  For the Java key typed events,
45 * that's no problem, we just say pass the unichar. But for the
46 * KeyPressed/Released events, we need to identify the virtual key code
47 * (which roughly correspond to hardware keys) which means we are supposed
48 * to say the virtual 7 key was pressed.  But how are we supposed to know
49 * when we get a punctuation char what was the real hardware key was that
50 * was pressed?  Although '&' often comes from Shift-7 the keyboard can be
51 * remapped!  I don't think there really is a good answer, and hopefully
52 * all good applets are only interested in logical key typed events not
53 * press/release.  Meanwhile, we are hard-coding the shifted punctuation
54 * to trigger the virtual keys that are the expected ones under a standard
55 * keymapping. Looking at Windows & Mac, they don't actually do this, the
56 * Mac seems to just put the ascii code in for the shifted punctuation
57 * (which means they actually end up with bogus key codes on the Java side),
58 * Windows I can't even figure out what it's doing.
59 */
60#define KL_STANDARD java_awt_event_KeyEvent_KEY_LOCATION_STANDARD
61#define KL_NUMPAD   java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD
62#define KL_UNKNOWN  java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN
63static struct _key
64{
65    unsigned short keyCode;
66    BOOL postsTyped;
67    jint javaKeyLocation;
68    jint javaKeyCode;
69}
70const keyTable[] =
71{
72    {0x00, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A},
73    {0x01, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S},
74    {0x02, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D},
75    {0x03, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F},
76    {0x04, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H},
77    {0x05, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G},
78    {0x06, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z},
79    {0x07, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X},
80    {0x08, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C},
81    {0x09, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V},
82    {0x0A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
83    {0x0B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B},
84    {0x0C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q},
85    {0x0D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W},
86    {0x0E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E},
87    {0x0F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R},
88    {0x10, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y},
89    {0x11, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T},
90    {0x12, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1},
91    {0x13, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2},
92    {0x14, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3},
93    {0x15, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4},
94    {0x16, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6},
95    {0x17, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5},
96    {0x18, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS},
97    {0x19, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9},
98    {0x1A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7},
99    {0x1B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS},
100    {0x1C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8},
101    {0x1D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0},
102    {0x1E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
103    {0x1F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O},
104    {0x20, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U},
105    {0x21, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
106    {0x22, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I},
107    {0x23, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P},
108    {0x24, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER},
109    {0x25, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L},
110    {0x26, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J},
111    {0x27, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE},
112    {0x28, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K},
113    {0x29, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON},
114    {0x2A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH},
115    {0x2B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA},
116    {0x2C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH},
117    {0x2D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N},
118    {0x2E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M},
119    {0x2F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD},
120    {0x30, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB},
121    {0x31, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE},
122    {0x32, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
123    {0x33, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE},
124    {0x34, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_ENTER},
125    {0x35, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE},
126    {0x36, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
127    {0x37, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_META},      // ****
128    {0x38, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_SHIFT},     // ****
129    {0x39, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK},
130    {0x3A, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_ALT},       // ****
131    {0x3B, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_CONTROL},   // ****
132    {0x3C, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
133    {0x3D, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_ALT_GRAPH},
134    {0x3E, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
135    {0x3F, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED}, // the 'fn' key on PowerBooks
136    {0x40, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F17},
137    {0x41, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_DECIMAL},
138    {0x42, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
139    {0x43, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_MULTIPLY},
140    {0x44, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
141    {0x45, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_ADD},
142    {0x46, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
143    {0x47, NO,  KL_NUMPAD,   java_awt_event_KeyEvent_VK_CLEAR},
144    {0x48, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
145    {0x49, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
146    {0x4A, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
147    {0x4B, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_DIVIDE},
148    {0x4C, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_ENTER},
149    {0x4D, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
150    {0x4E, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_SUBTRACT},
151    {0x4F, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F18},
152    {0x50, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F19},
153    {0x51, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_EQUALS},
154    {0x52, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD0},
155    {0x53, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD1},
156    {0x54, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD2},
157    {0x55, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD3},
158    {0x56, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD4},
159    {0x57, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD5},
160    {0x58, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD6},
161    {0x59, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD7},
162    {0x5A, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F20},
163    {0x5B, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD8},
164    {0x5C, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_NUMPAD9},
165    {0x5D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, // This is a combo yen/backslash on JIS keyboards.
166    {0x5E, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_UNDERSCORE},
167    {0x5F, YES, KL_NUMPAD,   java_awt_event_KeyEvent_VK_COMMA},
168    {0x60, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F5},
169    {0x61, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F6},
170    {0x62, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F7},
171    {0x63, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F3},
172    {0x64, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F8},
173    {0x65, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F9},
174    {0x66, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC},
175    {0x67, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F11},
176    {0x68, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA},
177    {0x69, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F13},
178    {0x6A, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F16},
179    {0x6B, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F14},
180    {0x6C, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
181    {0x6D, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F10},
182    {0x6E, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
183    {0x6F, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F12},
184    {0x70, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
185    {0x71, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F15},
186    {0x72, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_HELP},
187    {0x73, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_HOME},
188    {0x74, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP},
189    {0x75, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE},
190    {0x76, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F4},
191    {0x77, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_END},
192    {0x78, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F2},
193    {0x79, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN},
194    {0x7A, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_F1},
195    {0x7B, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT},
196    {0x7C, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT},
197    {0x7D, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN},
198    {0x7E, NO,  KL_STANDARD, java_awt_event_KeyEvent_VK_UP},
199    {0x7F, NO,  KL_UNKNOWN,  java_awt_event_KeyEvent_VK_UNDEFINED},
200};
201
202/*
203 * This table was stolen from the Windows implementation for mapping
204 * Unicode values to VK codes for dead keys.  On Windows, some layouts
205 * return ASCII punctuation for dead accents, while some return spacing
206 * accent chars, so both should be listed.  However, in all of the
207 * keyboard layouts I tried only the Unicode values are used.
208 */
209struct CharToVKEntry {
210    UniChar c;
211    jint javaKey;
212};
213static const struct CharToVKEntry charToDeadVKTable[] = {
214    {0x0060, java_awt_event_KeyEvent_VK_DEAD_GRAVE},
215    {0x00B4, java_awt_event_KeyEvent_VK_DEAD_ACUTE},
216    {0x0384, java_awt_event_KeyEvent_VK_DEAD_ACUTE}, // Unicode "GREEK TONOS" -- Greek keyboard, semicolon key
217    {0x005E, java_awt_event_KeyEvent_VK_DEAD_CIRCUMFLEX},
218    {0x007E, java_awt_event_KeyEvent_VK_DEAD_TILDE},
219    {0x02DC, java_awt_event_KeyEvent_VK_DEAD_TILDE}, // Unicode "SMALL TILDE"
220    {0x00AF, java_awt_event_KeyEvent_VK_DEAD_MACRON},
221    {0x02D8, java_awt_event_KeyEvent_VK_DEAD_BREVE},
222    {0x02D9, java_awt_event_KeyEvent_VK_DEAD_ABOVEDOT},
223    {0x00A8, java_awt_event_KeyEvent_VK_DEAD_DIAERESIS},
224    {0x02DA, java_awt_event_KeyEvent_VK_DEAD_ABOVERING},
225    {0x02DD, java_awt_event_KeyEvent_VK_DEAD_DOUBLEACUTE},
226    {0x02C7, java_awt_event_KeyEvent_VK_DEAD_CARON},
227    {0x00B8, java_awt_event_KeyEvent_VK_DEAD_CEDILLA},
228    {0x02DB, java_awt_event_KeyEvent_VK_DEAD_OGONEK},
229    {0x037A, java_awt_event_KeyEvent_VK_DEAD_IOTA},
230    {0x309B, java_awt_event_KeyEvent_VK_DEAD_VOICED_SOUND},
231    {0x309C, java_awt_event_KeyEvent_VK_DEAD_SEMIVOICED_SOUND},
232    {0,0}
233};
234
235// TODO: some constants below are part of CGS (private interfaces)...
236// for now we will look at the raw key code to determine left/right status
237// but not sure this is foolproof...
238static struct _nsKeyToJavaModifier
239{
240    NSUInteger nsMask;
241    //NSUInteger cgsLeftMask;
242    //NSUInteger cgsRightMask;
243    unsigned short leftKeyCode;
244    unsigned short rightKeyCode;
245    jint javaExtMask;
246    jint javaMask;
247    jint javaKey;
248}
249const nsKeyToJavaModifierTable[] =
250{
251    {
252        NSAlphaShiftKeyMask,
253        0,
254        0,
255        0, // no Java equivalent
256        0, // no Java equivalent
257        java_awt_event_KeyEvent_VK_CAPS_LOCK
258    },
259    {
260        NSShiftKeyMask,
261        //kCGSFlagsMaskAppleShiftKey,
262        //kCGSFlagsMaskAppleRightShiftKey,
263        56,
264        60,
265        java_awt_event_InputEvent_SHIFT_DOWN_MASK,
266        java_awt_event_InputEvent_SHIFT_MASK,
267        java_awt_event_KeyEvent_VK_SHIFT
268    },
269    {
270        NSControlKeyMask,
271        //kCGSFlagsMaskAppleControlKey,
272        //kCGSFlagsMaskAppleRightControlKey,
273        59,
274        62,
275        java_awt_event_InputEvent_CTRL_DOWN_MASK,
276        java_awt_event_InputEvent_CTRL_MASK,
277        java_awt_event_KeyEvent_VK_CONTROL
278    },
279    {
280        NSCommandKeyMask,
281        //kCGSFlagsMaskAppleLeftCommandKey,
282        //kCGSFlagsMaskAppleRightCommandKey,
283        55,
284        54,
285        java_awt_event_InputEvent_META_DOWN_MASK,
286        java_awt_event_InputEvent_META_MASK,
287        java_awt_event_KeyEvent_VK_META
288    },
289    {
290        NSAlternateKeyMask,
291        //kCGSFlagsMaskAppleLeftAlternateKey,
292        //kCGSFlagsMaskAppleRightAlternateKey,
293        58,
294        0,
295        java_awt_event_InputEvent_ALT_DOWN_MASK,
296        java_awt_event_InputEvent_ALT_MASK,
297        java_awt_event_KeyEvent_VK_ALT
298    },
299    {
300        NSAlternateKeyMask,
301        0,
302        61,
303        java_awt_event_InputEvent_ALT_DOWN_MASK | java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK,
304        java_awt_event_InputEvent_ALT_MASK | java_awt_event_InputEvent_ALT_GRAPH_MASK,
305        java_awt_event_KeyEvent_VK_ALT | java_awt_event_KeyEvent_VK_ALT_GRAPH
306    },
307    // NSNumericPadKeyMask
308    {
309        NSHelpKeyMask,
310        0,
311        0,
312        0, // no Java equivalent
313        0, // no Java equivalent
314        java_awt_event_KeyEvent_VK_HELP
315    },
316    // NSFunctionKeyMask
317    {0, 0, 0, 0, 0, 0}
318};
319
320static BOOL leftAltKeyPressed;
321static BOOL altGRPressed = NO;
322
323/*
324 * Almost all unicode characters just go from NS to Java with no translation.
325 *  For the few exceptions, we handle it here with this small table.
326 */
327#define ALL_NS_KEY_MODIFIERS_MASK \
328    (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)
329
330static struct _char {
331    NSUInteger modifier;
332    unichar nsChar;
333    unichar javaChar;
334}
335const charTable[] = {
336    // map enter on keypad to same as return key
337    {0,                         NSEnterCharacter,          NSNewlineCharacter},
338
339    // [3134616] return newline instead of carriage return
340    {0,                         NSCarriageReturnCharacter, NSNewlineCharacter},
341
342    // "delete" means backspace in Java
343    {ALL_NS_KEY_MODIFIERS_MASK, NSDeleteCharacter,         NSBackspaceCharacter},
344    {ALL_NS_KEY_MODIFIERS_MASK, NSDeleteFunctionKey,       NSDeleteCharacter},
345
346    // back-tab is only differentiated from tab by Shift flag
347    {NSShiftKeyMask,            NSBackTabCharacter,        NSTabCharacter},
348
349    {0, 0, 0}
350};
351
352unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers, BOOL spaceKeyTyped)
353{
354    const struct _char *cur;
355    // Mask off just the keyboard modifiers from the event modifier mask.
356    NSUInteger testableFlags = (modifiers & ALL_NS_KEY_MODIFIERS_MASK);
357
358    // walk through table & find the match
359    for (cur = charTable; cur->nsChar != 0 ; cur++) {
360        // <rdar://Problem/3476426> Need to determine if we are looking at
361        // a plain keypress or a modified keypress.  Don't adjust the
362        // character of a keypress with a modifier.
363        if (cur->nsChar == nsChar) {
364            if (cur->modifier == 0 && testableFlags == 0) {
365                // If the modifier field is 0, that means to transform
366                // this character if no additional keyboard modifiers are set.
367                // This lets ctrl-C be reported as ctrl-C and not transformed
368                // into Newline.
369                return cur->javaChar;
370            } else if (cur->modifier != 0 &&
371                       (testableFlags & cur->modifier) == testableFlags)
372            {
373                // Likewise, if the modifier field is nonzero, that means
374                // transform this character if only these modifiers are
375                // set in the testable flags.
376                return cur->javaChar;
377            }
378        }
379    }
380
381    if (nsChar >= NSUpArrowFunctionKey && nsChar <= NSModeSwitchFunctionKey) {
382        return java_awt_event_KeyEvent_CHAR_UNDEFINED;
383    }
384
385    // nsChar receives value 0 when SPACE key is typed.
386    if (nsChar == 0 && spaceKeyTyped == YES) {
387        return java_awt_event_KeyEvent_VK_SPACE;
388    }
389
390    // otherwise return character unchanged
391    return nsChar;
392}
393
394static unichar NsGetDeadKeyChar(unsigned short keyCode)
395{
396    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
397    CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
398    if (uchr == nil) { return 0; }
399    const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
400    // Carbon modifiers should be used instead of NSEvent modifiers
401    UInt32 modifierKeyState = (GetCurrentEventKeyModifiers() >> 8) & 0xFF;
402
403    if (keyboardLayout) {
404        UInt32 deadKeyState = 0;
405        UniCharCount maxStringLength = 255;
406        UniCharCount actualStringLength = 0;
407        UniChar unicodeString[maxStringLength];
408
409        // get the deadKeyState
410        OSStatus status = UCKeyTranslate(keyboardLayout,
411                                         keyCode, kUCKeyActionDown, modifierKeyState,
412                                         LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
413                                         &deadKeyState,
414                                         maxStringLength,
415                                         &actualStringLength, unicodeString);
416
417        if (status == noErr && deadKeyState != 0) {
418            // Press SPACE to get the dead key char
419            status = UCKeyTranslate(keyboardLayout,
420                                    kVK_Space, kUCKeyActionDown, 0,
421                                    LMGetKbdType(), 0,
422                                    &deadKeyState,
423                                    maxStringLength,
424                                    &actualStringLength, unicodeString);
425
426            if (status == noErr && actualStringLength > 0) {
427                return unicodeString[0];
428            }
429        }
430    }
431    return 0;
432}
433
434/*
435 * This is the function that uses the table above to take incoming
436 * NSEvent keyCodes and translate to the Java virtual key code.
437 */
438static void
439NsCharToJavaVirtualKeyCode(unichar ch, BOOL isDeadChar,
440                           NSUInteger flags, unsigned short key,
441                           jint *keyCode, jint *keyLocation, BOOL *postsTyped,
442                           unichar *deadChar)
443{
444    static size_t size = sizeof(keyTable) / sizeof(struct _key);
445    NSInteger offset;
446
447    if (isDeadChar) {
448        unichar testDeadChar = NsGetDeadKeyChar(key);
449        const struct CharToVKEntry *map;
450        for (map = charToDeadVKTable; map->c != 0; ++map) {
451            if (testDeadChar == map->c) {
452                *keyCode = map->javaKey;
453                *postsTyped = NO;
454                // TODO: use UNKNOWN here?
455                *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
456                *deadChar = testDeadChar;
457                return;
458            }
459        }
460        // If we got here, we keep looking for a normal key.
461    }
462
463    if ([[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
464        // key is an alphabetic character
465        unichar lower;
466        lower = tolower(ch);
467        offset = lower - 'a';
468        if (offset >= 0 && offset <= 25) {
469            // some chars in letter set are NOT actually A-Z characters?!
470            // skip them...
471            *postsTyped = YES;
472            // do quick conversion
473            *keyCode = java_awt_event_KeyEvent_VK_A + offset;
474            *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
475            return;
476        }
477    }
478
479    if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:ch]) {
480        // key is a digit
481        offset = ch - '0';
482        // make sure in range for decimal digits
483        if (offset >= 0 && offset <= 9)    {
484            jboolean numpad = ((flags & NSNumericPadKeyMask) &&
485                               (key > 81 && key < 93));
486            *postsTyped = YES;
487            if (numpad) {
488                *keyCode = offset + java_awt_event_KeyEvent_VK_NUMPAD0;
489                *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD;
490            } else {
491                *keyCode = offset + java_awt_event_KeyEvent_VK_0;
492                *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
493            }
494            return;
495        }
496    }
497
498    if (key < size) {
499        *postsTyped = keyTable[key].postsTyped;
500        *keyCode = keyTable[key].javaKeyCode;
501        *keyLocation = keyTable[key].javaKeyLocation;
502    } else {
503        // Should we report this? This means we've got a keyboard
504        // we don't know about...
505        *postsTyped = NO;
506        *keyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
507        *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
508    }
509}
510
511/*
512 * This returns the java key data for the key NSEvent modifiers
513 * (after NSFlagChanged).
514 */
515static void
516NsKeyModifiersToJavaKeyInfo(NSUInteger nsFlags, unsigned short eventKeyCode,
517                            jint *javaKeyCode,
518                            jint *javaKeyLocation,
519                            jint *javaKeyType)
520{
521    static NSUInteger sPreviousNSFlags = 0;
522
523    const struct _nsKeyToJavaModifier* cur;
524    NSUInteger oldNSFlags = sPreviousNSFlags;
525    NSUInteger changedNSFlags = oldNSFlags ^ nsFlags;
526    sPreviousNSFlags = nsFlags;
527
528    *javaKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
529    *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
530    *javaKeyType = java_awt_event_KeyEvent_KEY_PRESSED;
531
532    for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) {
533        if (changedNSFlags & cur->nsMask) {
534            *javaKeyCode = cur->javaKey;
535            *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
536            // TODO: uses SPI...
537            //if (changedNSFlags & cur->cgsLeftMask) {
538            //    *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
539            //} else if (changedNSFlags & cur->cgsRightMask) {
540            //    *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
541            //}
542            if (eventKeyCode == cur->leftKeyCode) {
543                leftAltKeyPressed = YES;
544                *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
545            } else if (eventKeyCode == cur->rightKeyCode) {
546                *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
547            } else if (cur->nsMask == NSAlternateKeyMask) {
548                leftAltKeyPressed = NO;
549                continue;
550            }
551            *javaKeyType = (cur->nsMask & nsFlags) ?
552            java_awt_event_KeyEvent_KEY_PRESSED :
553            java_awt_event_KeyEvent_KEY_RELEASED;
554            break;
555        }
556    }
557}
558
559/*
560 * This returns the java modifiers for a key NSEvent.
561 */
562jint NsKeyModifiersToJavaModifiers(NSUInteger nsFlags, BOOL isExtMods)
563{
564    jint javaModifiers = 0;
565    const struct _nsKeyToJavaModifier* cur;
566
567    for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) {
568        if ((cur->nsMask & nsFlags) != 0) {
569
570            if (cur->nsMask == NSAlternateKeyMask) {
571                if (leftAltKeyPressed == YES) {
572                    javaModifiers |= isExtMods? cur->javaExtMask : cur->javaMask;
573                    if (altGRPressed == NO)
574                        break;
575                    } else {
576                        leftAltKeyPressed = YES;
577                        altGRPressed = YES;
578                        continue;
579                    }
580                }
581            javaModifiers |= isExtMods ? cur->javaExtMask : cur->javaMask;
582        }
583    }
584
585    return javaModifiers;
586}
587
588/*
589 * This returns the NSEvent flags for java key modifiers.
590 */
591NSUInteger JavaModifiersToNsKeyModifiers(jint javaModifiers, BOOL isExtMods)
592{
593    NSUInteger nsFlags = 0;
594    const struct _nsKeyToJavaModifier* cur;
595
596    for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) {
597        jint mask = isExtMods? cur->javaExtMask : cur->javaMask;
598        if ((mask & javaModifiers) != 0) {
599            nsFlags |= cur->nsMask;
600        }
601    }
602
603    // special case
604    jint mask = isExtMods? java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK :
605                           java_awt_event_InputEvent_ALT_GRAPH_MASK;
606
607    if ((mask & javaModifiers) != 0) {
608        nsFlags |= NSAlternateKeyMask;
609    }
610
611    return nsFlags;
612}
613
614
615jint GetJavaMouseModifiers(NSUInteger modifierFlags)
616{
617    // Mousing needs the key modifiers
618    jint modifiers = NsKeyModifiersToJavaModifiers(modifierFlags, YES);
619
620
621    /*
622     * Ask Quartz about mouse buttons state
623     */
624
625    if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState,
626                                 kCGMouseButtonLeft)) {
627        modifiers |= java_awt_event_InputEvent_BUTTON1_DOWN_MASK;
628    }
629
630    if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState,
631                                 kCGMouseButtonRight)) {
632        modifiers |= java_awt_event_InputEvent_BUTTON3_DOWN_MASK;
633    }
634
635    if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState,
636                                 kCGMouseButtonCenter)) {
637        modifiers |= java_awt_event_InputEvent_BUTTON2_DOWN_MASK;
638    }
639
640    NSInteger extraButton = 3;
641    for (; extraButton < gNumberOfButtons; extraButton++) {
642        if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState,
643                                 extraButton)) {
644            modifiers |= gButtonDownMasks[extraButton];
645        }
646    }
647
648    return modifiers;
649}
650
651jlong UTC(NSEvent *event) {
652    struct timeval tv;
653    if (gettimeofday(&tv, NULL) == 0) {
654        long long sec = (long long)tv.tv_sec;
655        return (sec*1000) + (tv.tv_usec/1000);
656    }
657    return 0;
658}
659
660JNIEXPORT void JNICALL
661Java_java_awt_AWTEvent_nativeSetSource
662    (JNIEnv *env, jobject self, jobject newSource)
663{
664}
665
666/*
667 * Class:     sun_lwawt_macosx_NSEvent
668 * Method:    nsToJavaModifiers
669 * Signature: (II)I
670 */
671JNIEXPORT jint JNICALL
672Java_sun_lwawt_macosx_NSEvent_nsToJavaModifiers
673(JNIEnv *env, jclass cls, jint modifierFlags)
674{
675    jint jmodifiers = 0;
676
677JNI_COCOA_ENTER(env);
678
679    jmodifiers = GetJavaMouseModifiers(modifierFlags);
680
681JNI_COCOA_EXIT(env);
682
683    return jmodifiers;
684}
685
686/*
687 * Class:     sun_lwawt_macosx_NSEvent
688 * Method:    nsToJavaKeyInfo
689 * Signature: ([I[I)Z
690 */
691JNIEXPORT jboolean JNICALL
692Java_sun_lwawt_macosx_NSEvent_nsToJavaKeyInfo
693(JNIEnv *env, jclass cls, jintArray inData, jintArray outData)
694{
695    BOOL postsTyped = NO;
696
697JNI_COCOA_ENTER(env);
698
699    jboolean copy = JNI_FALSE;
700    jint *data = (*env)->GetIntArrayElements(env, inData, &copy);
701    CHECK_NULL_RETURN(data, postsTyped);
702
703    // in  = [testChar, testDeadChar, modifierFlags, keyCode]
704    jchar testChar = (jchar)data[0];
705    BOOL isDeadChar = (data[1] != 0);
706    jint modifierFlags = data[2];
707    jshort keyCode = (jshort)data[3];
708
709    jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
710    jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
711    jint testDeadChar = 0;
712
713    NsCharToJavaVirtualKeyCode((unichar)testChar, isDeadChar,
714                               (NSUInteger)modifierFlags, (unsigned short)keyCode,
715                               &jkeyCode, &jkeyLocation, &postsTyped,
716                               (unichar *) &testDeadChar);
717
718    // out = [jkeyCode, jkeyLocation, deadChar];
719    (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
720    (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
721    (*env)->SetIntArrayRegion(env, outData, 2, 1, &testDeadChar);
722
723    (*env)->ReleaseIntArrayElements(env, inData, data, 0);
724
725JNI_COCOA_EXIT(env);
726
727    return postsTyped;
728}
729
730/*
731 * Class:     sun_lwawt_macosx_NSEvent
732 * Method:    nsKeyModifiersToJavaKeyInfo
733 * Signature: ([I[I)V
734 */
735JNIEXPORT void JNICALL
736Java_sun_lwawt_macosx_NSEvent_nsKeyModifiersToJavaKeyInfo
737(JNIEnv *env, jclass cls, jintArray inData, jintArray outData)
738{
739JNI_COCOA_ENTER(env);
740
741    jboolean copy = JNI_FALSE;
742    jint *data = (*env)->GetIntArrayElements(env, inData, &copy);
743    CHECK_NULL(data);
744
745    // in  = [modifierFlags, keyCode]
746    jint modifierFlags = data[0];
747    jshort keyCode = (jshort)data[1];
748
749    jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
750    jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
751    jint jkeyType = java_awt_event_KeyEvent_KEY_PRESSED;
752
753    NsKeyModifiersToJavaKeyInfo(modifierFlags,
754                                keyCode,
755                                &jkeyCode,
756                                &jkeyLocation,
757                                &jkeyType);
758
759    // out = [jkeyCode, jkeyLocation, jkeyType];
760    (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
761    (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
762    (*env)->SetIntArrayRegion(env, outData, 2, 1, &jkeyType);
763
764    (*env)->ReleaseIntArrayElements(env, inData, data, 0);
765
766JNI_COCOA_EXIT(env);
767}
768
769/*
770 * Class:     sun_lwawt_macosx_NSEvent
771 * Method:    nsToJavaChar
772 * Signature: (CI)C
773 */
774JNIEXPORT jint JNICALL
775Java_sun_lwawt_macosx_NSEvent_nsToJavaChar
776(JNIEnv *env, jclass cls, jchar nsChar, jint modifierFlags, jboolean spaceKeyTyped)
777{
778    jchar javaChar = 0;
779
780JNI_COCOA_ENTER(env);
781
782    javaChar = NsCharToJavaChar(nsChar, modifierFlags, spaceKeyTyped);
783
784JNI_COCOA_EXIT(env);
785
786    return javaChar;
787}
788