/* * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #import #import #import #import "LWCToolkit.h" #import "ThreadUtilities.h" #import "java_awt_event_InputEvent.h" #import "java_awt_event_KeyEvent.h" #import "java_awt_event_MouseEvent.h" /* * Table to map typed characters to their Java virtual key equivalent and back. * We use the incoming unichar (ignoring all modifiers) and try to figure out * which virtual key code is appropriate. A lot of them just have direct * mappings (the function keys, arrow keys, etc.) so they aren't a problem. * We had to do something a little funky to catch the keys on the numeric * key pad (i.e. using event mask to distinguish between period on regular * keyboard and decimal on keypad). We also have to do something incredibly * hokey with regards to the shifted punctuation characters. For examples, * consider '&' which is usually Shift-7. For the Java key typed events, * that's no problem, we just say pass the unichar. But for the * KeyPressed/Released events, we need to identify the virtual key code * (which roughly correspond to hardware keys) which means we are supposed * to say the virtual 7 key was pressed. But how are we supposed to know * when we get a punctuation char what was the real hardware key was that * was pressed? Although '&' often comes from Shift-7 the keyboard can be * remapped! I don't think there really is a good answer, and hopefully * all good applets are only interested in logical key typed events not * press/release. Meanwhile, we are hard-coding the shifted punctuation * to trigger the virtual keys that are the expected ones under a standard * keymapping. Looking at Windows & Mac, they don't actually do this, the * Mac seems to just put the ascii code in for the shifted punctuation * (which means they actually end up with bogus key codes on the Java side), * Windows I can't even figure out what it's doing. */ #define KL_STANDARD java_awt_event_KeyEvent_KEY_LOCATION_STANDARD #define KL_NUMPAD java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD #define KL_UNKNOWN java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN static struct _key { unsigned short keyCode; BOOL postsTyped; jint javaKeyLocation; jint javaKeyCode; } const keyTable[] = { {0x00, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A}, {0x01, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S}, {0x02, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D}, {0x03, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F}, {0x04, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H}, {0x05, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G}, {0x06, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z}, {0x07, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X}, {0x08, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C}, {0x09, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V}, {0x0A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE}, {0x0B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B}, {0x0C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q}, {0x0D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W}, {0x0E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E}, {0x0F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R}, {0x10, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y}, {0x11, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T}, {0x12, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1}, {0x13, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2}, {0x14, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3}, {0x15, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4}, {0x16, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6}, {0x17, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5}, {0x18, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS}, {0x19, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9}, {0x1A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7}, {0x1B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS}, {0x1C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8}, {0x1D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0}, {0x1E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET}, {0x1F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O}, {0x20, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U}, {0x21, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET}, {0x22, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I}, {0x23, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P}, {0x24, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER}, {0x25, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L}, {0x26, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J}, {0x27, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE}, {0x28, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K}, {0x29, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON}, {0x2A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, {0x2B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA}, {0x2C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH}, {0x2D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N}, {0x2E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M}, {0x2F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD}, {0x30, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB}, {0x31, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE}, {0x32, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE}, {0x33, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE}, {0x34, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER}, {0x35, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE}, {0x36, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x37, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_META}, // **** {0x38, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_SHIFT}, // **** {0x39, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK}, {0x3A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_ALT}, // **** {0x3B, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_CONTROL}, // **** {0x3C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x3D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x3E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x3F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // the 'fn' key on PowerBooks {0x40, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x41, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DECIMAL}, {0x42, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x43, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_MULTIPLY}, {0x44, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x45, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ADD}, {0x46, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x47, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_CLEAR}, {0x48, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x49, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x4A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x4B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DIVIDE}, {0x4C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER}, {0x4D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x4E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_SUBTRACT}, {0x4F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x50, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x51, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_EQUALS}, {0x52, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD0}, {0x53, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD1}, {0x54, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD2}, {0x55, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD3}, {0x56, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD4}, {0x57, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD5}, {0x58, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD6}, {0x59, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD7}, {0x5A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x5B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD8}, {0x5C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD9}, {0x5D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, // This is a combo yen/backslash on JIS keyboards. {0x5E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_UNDERSCORE}, {0x5F, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_COMMA}, {0x60, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F5}, {0x61, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F6}, {0x62, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F7}, {0x63, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F3}, {0x64, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F8}, {0x65, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F9}, {0x66, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC}, {0x67, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F11}, {0x68, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA}, {0x69, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F13}, {0x6A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F16}, {0x6B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F14}, {0x6C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x6D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F10}, {0x6E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x6F, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F12}, {0x70, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, {0x71, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F15}, {0x72, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HELP}, {0x73, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HOME}, {0x74, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP}, {0x75, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE}, {0x76, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F4}, {0x77, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_END}, {0x78, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F2}, {0x79, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN}, {0x7A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F1}, {0x7B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT}, {0x7C, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT}, {0x7D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN}, {0x7E, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_UP}, {0x7F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, }; /* * This table was stolen from the Windows implementation for mapping * Unicode values to VK codes for dead keys. On Windows, some layouts * return ASCII punctuation for dead accents, while some return spacing * accent chars, so both should be listed. However, in all of the * keyboard layouts I tried only the Unicode values are used. */ struct CharToVKEntry { UniChar c; jint javaKey; }; static const struct CharToVKEntry charToDeadVKTable[] = { {0x0060, java_awt_event_KeyEvent_VK_DEAD_GRAVE}, {0x00B4, java_awt_event_KeyEvent_VK_DEAD_ACUTE}, {0x0384, java_awt_event_KeyEvent_VK_DEAD_ACUTE}, // Unicode "GREEK TONOS" -- Greek keyboard, semicolon key {0x005E, java_awt_event_KeyEvent_VK_DEAD_CIRCUMFLEX}, {0x007E, java_awt_event_KeyEvent_VK_DEAD_TILDE}, {0x02DC, java_awt_event_KeyEvent_VK_DEAD_TILDE}, // Unicode "SMALL TILDE" {0x00AF, java_awt_event_KeyEvent_VK_DEAD_MACRON}, {0x02D8, java_awt_event_KeyEvent_VK_DEAD_BREVE}, {0x02D9, java_awt_event_KeyEvent_VK_DEAD_ABOVEDOT}, {0x00A8, java_awt_event_KeyEvent_VK_DEAD_DIAERESIS}, {0x02DA, java_awt_event_KeyEvent_VK_DEAD_ABOVERING}, {0x02DD, java_awt_event_KeyEvent_VK_DEAD_DOUBLEACUTE}, {0x02C7, java_awt_event_KeyEvent_VK_DEAD_CARON}, {0x00B8, java_awt_event_KeyEvent_VK_DEAD_CEDILLA}, {0x02DB, java_awt_event_KeyEvent_VK_DEAD_OGONEK}, {0x037A, java_awt_event_KeyEvent_VK_DEAD_IOTA}, {0x309B, java_awt_event_KeyEvent_VK_DEAD_VOICED_SOUND}, {0x309C, java_awt_event_KeyEvent_VK_DEAD_SEMIVOICED_SOUND}, {0,0} }; // TODO: some constants below are part of CGS (private interfaces)... // for now we will look at the raw key code to determine left/right status // but not sure this is foolproof... static struct _nsKeyToJavaModifier { NSUInteger nsMask; //NSUInteger cgsLeftMask; //NSUInteger cgsRightMask; unsigned short leftKeyCode; unsigned short rightKeyCode; jint javaMask; jint javaKey; } const nsKeyToJavaModifierTable[] = { { NSAlphaShiftKeyMask, 0, 0, 0, // no Java equivalent java_awt_event_KeyEvent_VK_CAPS_LOCK }, { NSShiftKeyMask, //kCGSFlagsMaskAppleShiftKey, //kCGSFlagsMaskAppleRightShiftKey, 56, 60, java_awt_event_InputEvent_SHIFT_DOWN_MASK, java_awt_event_KeyEvent_VK_SHIFT }, { NSControlKeyMask, //kCGSFlagsMaskAppleControlKey, //kCGSFlagsMaskAppleRightControlKey, 59, 62, java_awt_event_InputEvent_CTRL_DOWN_MASK, java_awt_event_KeyEvent_VK_CONTROL }, { NSAlternateKeyMask, //kCGSFlagsMaskAppleLeftAlternateKey, //kCGSFlagsMaskAppleRightAlternateKey, 58, 61, java_awt_event_InputEvent_ALT_DOWN_MASK, java_awt_event_KeyEvent_VK_ALT }, { NSCommandKeyMask, //kCGSFlagsMaskAppleLeftCommandKey, //kCGSFlagsMaskAppleRightCommandKey, 55, 54, java_awt_event_InputEvent_META_DOWN_MASK, java_awt_event_KeyEvent_VK_META }, // NSNumericPadKeyMask { NSHelpKeyMask, 0, 0, 0, // no Java equivalent java_awt_event_KeyEvent_VK_HELP }, // NSFunctionKeyMask {0, 0, 0, 0, 0} }; /* * Almost all unicode characters just go from NS to Java with no translation. * For the few exceptions, we handle it here with this small table. */ #define ALL_NS_KEY_MODIFIERS_MASK \ (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask) static struct _char { NSUInteger modifier; unichar nsChar; unichar javaChar; } const charTable[] = { // map enter on keypad to same as return key {0, NSEnterCharacter, NSNewlineCharacter}, // [3134616] return newline instead of carriage return {0, NSCarriageReturnCharacter, NSNewlineCharacter}, // "delete" means backspace in Java {ALL_NS_KEY_MODIFIERS_MASK, NSDeleteCharacter, NSBackspaceCharacter}, {ALL_NS_KEY_MODIFIERS_MASK, NSDeleteFunctionKey, NSDeleteCharacter}, // back-tab is only differentiated from tab by Shift flag {NSShiftKeyMask, NSBackTabCharacter, NSTabCharacter}, {0, 0, 0} }; static unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers) { const struct _char *cur; // Mask off just the keyboard modifiers from the event modifier mask. NSUInteger testableFlags = (modifiers & ALL_NS_KEY_MODIFIERS_MASK); // walk through table & find the match for (cur = charTable; cur->nsChar != 0 ; cur++) { // Need to determine if we are looking at // a plain keypress or a modified keypress. Don't adjust the // character of a keypress with a modifier. if (cur->nsChar == nsChar) { if (cur->modifier == 0 && testableFlags == 0) { // If the modifier field is 0, that means to transform // this character if no additional keyboard modifiers are set. // This lets ctrl-C be reported as ctrl-C and not transformed // into Newline. return cur->javaChar; } else if (cur->modifier != 0 && (testableFlags & cur->modifier) == testableFlags) { // Likewise, if the modifier field is nonzero, that means // transform this character if only these modifiers are // set in the testable flags. return cur->javaChar; } } } if (nsChar >= NSUpArrowFunctionKey && nsChar <= NSModeSwitchFunctionKey) { return java_awt_event_KeyEvent_CHAR_UNDEFINED; } // otherwise return character unchanged return nsChar; } /* * This is the function that uses the table above to take incoming * NSEvent keyCodes and translate to the Java virtual key code. */ static void NsCharToJavaVirtualKeyCode(unichar ch, unichar deadChar, NSUInteger flags, unsigned short key, jint *keyCode, jint *keyLocation, BOOL *postsTyped) { static size_t size = sizeof(keyTable) / sizeof(struct _key); NSInteger offset; if (deadChar) { const struct CharToVKEntry *map; for (map = charToDeadVKTable; map->c != 0; ++map) { if (deadChar == map->c) { *keyCode = map->javaKey; *postsTyped = NO; // TODO: use UNKNOWN here? *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; return; } } // If we got here, we keep looking for a normal key. } if ([[NSCharacterSet letterCharacterSet] characterIsMember:ch]) { // key is an alphabetic character unichar lower; lower = tolower(ch); offset = lower - 'a'; if (offset >= 0 && offset <= 25) { // some chars in letter set are NOT actually A-Z characters?! // skip them... *postsTyped = YES; // do quick conversion *keyCode = java_awt_event_KeyEvent_VK_A + offset; *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD; return; } } if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:ch]) { // key is a digit offset = ch - '0'; // make sure in range for decimal digits if (offset >= 0 && offset <= 9) { jboolean numpad = (flags & NSNumericPadKeyMask) != 0; *postsTyped = YES; if (numpad) { *keyCode = offset + java_awt_event_KeyEvent_VK_NUMPAD0; *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD; } else { *keyCode = offset + java_awt_event_KeyEvent_VK_0; *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD; } return; } } if (key < size) { *postsTyped = keyTable[key].postsTyped; *keyCode = keyTable[key].javaKeyCode; *keyLocation = keyTable[key].javaKeyLocation; } else { // Should we report this? This means we've got a keyboard // we don't know about... *postsTyped = NO; *keyCode = java_awt_event_KeyEvent_VK_UNDEFINED; *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; } } /* * This returns the java key data for the key NSEvent modifiers * (after NSFlagChanged). */ static void NsKeyModifiersToJavaKeyInfo(NSUInteger nsFlags, unsigned short eventKeyCode, jint *javaKeyCode, jint *javaKeyLocation, jint *javaKeyType) { static NSUInteger sPreviousNSFlags = 0; const struct _nsKeyToJavaModifier* cur; NSUInteger oldNSFlags = sPreviousNSFlags; NSUInteger changedNSFlags = oldNSFlags ^ nsFlags; sPreviousNSFlags = nsFlags; *javaKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED; *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; *javaKeyType = java_awt_event_KeyEvent_KEY_PRESSED; for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) { if (changedNSFlags & cur->nsMask) { *javaKeyCode = cur->javaKey; *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD; // TODO: uses SPI... //if (changedNSFlags & cur->cgsLeftMask) { // *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT; //} else if (changedNSFlags & cur->cgsRightMask) { // *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT; //} if (eventKeyCode == cur->leftKeyCode) { *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT; } else if (eventKeyCode == cur->rightKeyCode) { *javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT; } *javaKeyType = (cur->nsMask & nsFlags) ? java_awt_event_KeyEvent_KEY_PRESSED : java_awt_event_KeyEvent_KEY_RELEASED; break; } } } /* * This returns the java modifiers for a key NSEvent. */ static jint NsKeyModifiersToJavaModifiers(NSUInteger nsFlags) { jint javaModifiers = 0; const struct _nsKeyToJavaModifier* cur; for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) { if ((cur->nsMask & nsFlags) != 0) { javaModifiers |= cur->javaMask; } } return javaModifiers; } /* * Returns the correct java character for a key event. Most unicode * characters don't require any fussing, but a few seem to need adjusting, * see nsCharToJavaChar. */ static unichar GetJavaCharacter(NSEvent *event, unsigned int index) { unichar returnValue = java_awt_event_KeyEvent_CHAR_UNDEFINED; NSString *chars = nil; unichar testChar = 0, testDeadChar = 0; jint javaModifiers = NsKeyModifiersToJavaModifiers([event modifierFlags]); switch ([event type]) { case NSFlagsChanged: // no character for modifier keys returnValue = java_awt_event_KeyEvent_CHAR_UNDEFINED; break; case NSKeyDown: case NSKeyUp: chars = [event characters]; if ([chars length] > 0) { testChar = [chars characterAtIndex:index]; } if (javaModifiers == 0) { // TODO: uses SPI... //if (TSMGetDeadKeyState() != 0) { // testDeadChar = [self deadKeyCharacter]; //} } if (testChar != 0) { returnValue = NsCharToJavaChar(testChar, [event modifierFlags]); } else if (testDeadChar != 0) { returnValue = NsCharToJavaChar(testDeadChar, [event modifierFlags]); } else { returnValue = java_awt_event_KeyEvent_CHAR_UNDEFINED; } break; default: //[NSException raise:@"AWT error" format:@"Attempt to get character code from non-key event!"]; break; } return returnValue; } /* static jchar GetDeadKeyCharacter(NSEvent *event) { // If the current event is not a dead key, return 0. // TODO: this uses SPI; it's an optimization but not strictly necessary //if (TSMGetDeadKeyState() == 0) { // return 0; //} // AppKit does not track dead-key states directly, but TSM does. Even then, // it's not necessarily all that accurate, because the dead key can change // given some combination of modifier keys on certain layouts. // As a result, finding the unicode value for the front end of the dead // key is a bit of a heuristic. // This algorithm was suggested by Aki Inoue. // When you get a dead key, you need to simiulate what would happen in // the current dead-key and modifier state if the user hit the spacebar. // That will tell you the front end of the dead-key combination. unichar returnValue = 0; const UInt16 VIRTUAL_KEY_SPACE = 49; UInt32 deadKeyState = 0; UInt32 appkitFlags = [event modifierFlags]; UniCharCount actualStringLength; UniChar unicodeInputString[16]; TISInputSourceRef keyLayout; const void *chrData; keyLayout = TISCopyCurrentKeyboardLayoutInputSource(); CFDataRef cfUchrData = TISGetInputSourceProperty(keyLayout, kTISPropertyUnicodeKeyLayoutData); if (cfUchrData == NULL) { return returnValue; } // The actual 'uchr' table is inside the CFDataRef. chrData = CFDataGetBytePtr(cfUchrData); UInt8 keyboardType = LMGetKbdType(); UInt32 keyEventModifiers = 0; if (appkitFlags & NSShiftKeyMask) keyEventModifiers |= shiftKey; if (appkitFlags & NSCommandKeyMask) keyEventModifiers |= cmdKey; if (appkitFlags & NSAlphaShiftKeyMask) keyEventModifiers |= alphaLock; if (appkitFlags & NSControlKeyMask) keyEventModifiers |= controlKey; if (appkitFlags & NSAlternateKeyMask) keyEventModifiers |= optionKey; if (noErr == UCKeyTranslate(chrData, VIRTUAL_KEY_SPACE, ([event type] == NSKeyDown ? kUCKeyActionDown : kUCKeyActionUp), keyEventModifiers, keyboardType, kUCKeyTranslateNoDeadKeysMask, &deadKeyState, 16, &actualStringLength, unicodeInputString)) { if (actualStringLength > 0) { returnValue = unicodeInputString[0]; } } return returnValue; } */ // REMIND: The fix for MACOSX_PORT-539 introduces Java-level implementation // of the function below (see CPlatformResponder). Consider removing this code. void DeliverJavaKeyEvent(JNIEnv *env, NSEvent *event, jobject peer) { jint javaKeyType = java_awt_event_KeyEvent_KEY_PRESSED; jint javaKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED; jint javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; NSString *chars = nil; BOOL postsTyped; unichar testChar = java_awt_event_KeyEvent_CHAR_UNDEFINED; unichar testDeadChar = 0; jint javaModifiers = 0; switch ([event type]) { case NSFlagsChanged: NsKeyModifiersToJavaKeyInfo([event modifierFlags], [event keyCode], &javaKeyCode, &javaKeyLocation, &javaKeyType); break; case NSKeyDown: case NSKeyUp: chars = [event charactersIgnoringModifiers]; if ([chars length] > 0) { testChar = [chars characterAtIndex:0]; } javaModifiers = NsKeyModifiersToJavaModifiers([event modifierFlags]); if (javaModifiers == 0) { // TODO: dead key chars // testDeadChar = GetDeadKeyCharacter(event); } NsCharToJavaVirtualKeyCode(testChar, testDeadChar, [event modifierFlags], [event keyCode], &javaKeyCode, &javaKeyLocation, &postsTyped); if( !postsTyped ) { testChar = java_awt_event_KeyEvent_CHAR_UNDEFINED; } javaKeyType = ([event type] == NSKeyDown) ? java_awt_event_KeyEvent_KEY_PRESSED : java_awt_event_KeyEvent_KEY_RELEASED; break; default: //[NSException raise:@"AWT error" format:@"Attempt to get virtual key code from non-key event!"]; break; } if (env != NULL) { static JNF_CLASS_CACHE(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView"); static JNF_MEMBER_CACHE(jm_deliverKeyEvent, jc_CPlatformView, "deliverKeyEvent", "(IICII)V"); JNFCallVoidMethod(env, peer, jm_deliverKeyEvent, javaKeyType, javaModifiers, testChar, javaKeyCode, javaKeyLocation); } } jint GetJavaMouseModifiers(NSInteger button, NSUInteger modifierFlags) { // Mousing needs the key modifiers jint modifiers = NsKeyModifiersToJavaModifiers(modifierFlags); /* * Ask Quartz about mouse buttons state */ if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft)) { modifiers |= java_awt_event_InputEvent_BUTTON1_DOWN_MASK; } if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonRight)) { modifiers |= java_awt_event_InputEvent_BUTTON3_DOWN_MASK; } if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonCenter)) { modifiers |= java_awt_event_InputEvent_BUTTON2_DOWN_MASK; } NSInteger extraButton = 3; for (; extraButton < gNumberOfButtons; extraButton++) { if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, extraButton)) { modifiers |= gButtonDownMasks[extraButton]; } } return modifiers; } /* * Converts an NSEvent button number to a MouseEvent constant. */ static jint NSButtonToJavaButton(NSInteger nsButtonNumber) { jint jbutton = java_awt_event_MouseEvent_NOBUTTON; if (nsButtonNumber == 0) { // left jbutton = java_awt_event_MouseEvent_BUTTON1; } else if (nsButtonNumber == 1) { // right jbutton = java_awt_event_MouseEvent_BUTTON3; } else if (nsButtonNumber == 2) { // middle jbutton = java_awt_event_MouseEvent_BUTTON2; } return jbutton; } static BOOL isDragging = NO; void DeliverMouseClickedEvent(JNIEnv *env, NSEvent *event, jobject peer) { NSPoint pt = [event locationInWindow]; NSPoint pOnScreen = [NSEvent mouseLocation]; jint etype = java_awt_event_MouseEvent_MOUSE_CLICKED; jint modifiers = GetJavaMouseModifiers([event buttonNumber], [event modifierFlags]); jint clickCount = [event clickCount]; jint button = NSButtonToJavaButton([event buttonNumber]); if (env != NULL) { static JNF_CLASS_CACHE(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView"); static JNF_MEMBER_CACHE(jm_deliverMouseEvent, jc_CPlatformView, "deliverMouseEvent", "(IIIIFFFF)V"); JNFCallVoidMethod(env, peer, jm_deliverMouseEvent, etype, modifiers, clickCount, button, pt.x, pt.y, pOnScreen.x, pOnScreen.y); } } /* * After every key down event, this is called to make the matching * KEY_TYPED (if this key posts those). We use the same NSEvent for it, * but create a KEY_TYPED java event this time. * If this key doesn't post typed, we don't post the event. * * TODO: some duplicated effort here; could just fold it * into DeliverJavaKeyEvent... */ static void DeliverKeyTypedEvents(JNIEnv *env, NSEvent *nsEvent, jobject peer) { if (peer == NULL) { return; } jint javaKeyCode, javaKeyLocation; BOOL postsTyped = NO; unichar testChar, testDeadChar = 0; jint javaModifiers = NsKeyModifiersToJavaModifiers([nsEvent modifierFlags]); if (javaModifiers == 0) { testDeadChar = [nsEvent deadKeyCharacter]; } NSString *theChars = [nsEvent characters]; unsigned i, stringLength = [theChars length]; for (i = 0; i < stringLength; i++) { testChar = [theChars characterAtIndex:i]; NsCharToJavaVirtualKeyCode(testChar, testDeadChar, [nsEvent modifierFlags], [nsEvent keyCode], &javaKeyCode, &javaKeyLocation, &postsTyped); if (postsTyped) { // Some keys may generate a KEY_TYPED, but we can't determine // what that character is. That's likely a bug, but for now we // just check for CHAR_UNDEFINED. unichar theChar = GetJavaCharacter(nsEvent, i); if (theChar != java_awt_event_KeyEvent_CHAR_UNDEFINED) { if (env != NULL) { static JNF_CLASS_CACHE(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView"); static JNF_MEMBER_CACHE(jm_deliverKeyEvent, jc_CPlatformView, "deliverKeyEvent", "(IICII)V"); JNFCallVoidMethod(env, peer, jm_deliverKeyEvent, java_awt_event_KeyEvent_KEY_TYPED, javaModifiers, theChar, java_awt_event_KeyEvent_VK_UNDEFINED, java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN); } } } } } /* * There are a couple of extra events that Java expects to get that don't * actually correspond to a direct NSEvent, KEY_TYPED and MOUSE_CLICKED are * both extra events that are sort of redundant with ordinary * key downs and mouse ups. In this extra message, we take the original * input event and if necessary, cons up a special follow-on event which * we dispatch over to Java. * * For Java, keyDown's generate a KeyPressed (for each hardware key as it * goes down) and then a "logical KeyTyped" event for the key event. (So * a shift-a generates two presses, one keytyped of "A", and then two * releases). The standard event utility function converts a key down to * a key pressed. When appropriate, we need to cons up another event * (KEY_TYPED) to follow a keyDown. * * Java expects you to send a clicked event if you got a down & up, with no * intervening drag. So in addition to the MOUSE_RELEASED event that a * mouseUp is translated to, we also have to cons up a MOUSE_CLICKED event * for that case. Mike Paquette, god of Window Server event handling, * confirmed this fact about how to determine if a mouse up event had an * intervening drag: * An initial mouse-down gets a click count of 1. Subsequent left or right * mouse-downs within the space/time tolerance limits increment the click * count. A mouse-up will have the clickCount of the last mouseDown if * mouse is not outside the tolerance limits, but 0 otherwise. Thus, a * down-up sequence without any intervening drag will have a click count * of 0 in the mouse-up event. NOTE: The problem with this is that * clickCount goes to zero after some point in time. So a long, click & * hold without moving and then release the mouse doesn't create a * MOUSE_CLICK event as it should. Java AWT now tracks the drag state itself. * * As another add-on, we also check for the status of mouse-motion events * after a mouse-down, so we know whether to generate mouse-dragged events * during this down sequence. */ void SendAdditionalJavaEvents(JNIEnv *env, NSEvent *nsEvent, jobject peer) { AWT_ASSERT_APPKIT_THREAD; NSEventType type = [nsEvent type]; switch (type) { case NSKeyDown: break; case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: // TODO: we may need to pull in changedDragToMove here... //if (!isDragging && ([NSViewAWT changedDragToMove]==NO)) { if (!isDragging) { // got down/up pair with no dragged in between; ignores drag events // that have been morphed to move events DeliverMouseClickedEvent(env, nsEvent, peer); } break; // TODO: to be implemented... #if 0 case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged: // // During a drag, the AppKit does not send mouseEnter and mouseExit // events. It turns out that doing a hitTest causes the window's // view hierarchy to be locked from drawing and that, of course, // slows everything way down. Synthesize mouseEnter and mouseExit // then forward. // NSView *hitView = [[source model] hitTest:[nsEvent locationInWindow]]; if ((hitView != nil) && ([hitView conformsToProtocol:@protocol(AWTPeerControl)])) { if (sLastMouseDraggedView == nil) { sLastMouseDraggedView = hitView; } else if (hitView != sLastMouseDraggedView) { // We know sLastMouseDraggedView is a AWTPeerControl. jobject lastPeer = [(id )sLastMouseDraggedView peer]; // Send mouseExit to sLastMouseDraggedView jobject exitEvent = makeMouseEvent(env, nsEvent, lastPeer, sLastMouseDraggedView, java_awt_event_MouseEvent_MOUSE_EXITED); pushEventForward(exitEvent, env); (*env)->DeleteLocalRef(env, exitEvent); // Send mouseEnter to hitView jobject enterEvent = makeMouseEvent(env, nsEvent, peer, hitView, java_awt_event_MouseEvent_MOUSE_ENTERED); pushEventForward(enterEvent, env); (*env)->DeleteLocalRef(env, enterEvent); // Set sLastMouseDraggedView = hitView sLastMouseDraggedView = hitView; } } break; #endif default: break; } } jlong UTC(NSEvent *event) { struct timeval tv; if (gettimeofday(&tv, NULL) == 0) { long long sec = (long long)tv.tv_sec; return (sec*1000) + (tv.tv_usec/1000); } return 0; } JNIEXPORT void JNICALL Java_java_awt_AWTEvent_nativeSetSource (JNIEnv *env, jobject self, jobject newSource) { } /* * Class: sun_lwawt_macosx_event_NSEvent * Method: nsToJavaMouseModifiers * Signature: (II)I */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_event_NSEvent_nsToJavaMouseModifiers (JNIEnv *env, jclass cls, jint buttonNumber, jint modifierFlags) { jint jmodifiers = 0; JNF_COCOA_ENTER(env); jmodifiers = GetJavaMouseModifiers(buttonNumber, modifierFlags); JNF_COCOA_EXIT(env); return jmodifiers; } /* * Class: sun_lwawt_macosx_event_NSEvent * Method: nsToJavaKeyModifiers * Signature: (I)I */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_event_NSEvent_nsToJavaKeyModifiers (JNIEnv *env, jclass cls, jint modifierFlags) { jint jmodifiers = 0; JNF_COCOA_ENTER(env); jmodifiers = NsKeyModifiersToJavaModifiers(modifierFlags); JNF_COCOA_EXIT(env); return jmodifiers; } /* * Class: sun_lwawt_macosx_event_NSEvent * Method: nsToJavaKeyInfo * Signature: ([I[I)Z */ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_event_NSEvent_nsToJavaKeyInfo (JNIEnv *env, jclass cls, jintArray inData, jintArray outData) { BOOL postsTyped = NO; JNF_COCOA_ENTER(env); jboolean copy = JNI_FALSE; jint *data = (*env)->GetIntArrayElements(env, inData, ©); // in = [testChar, testDeadChar, modifierFlags, keyCode] jchar testChar = (jchar)data[0]; jchar testDeadChar = (jchar)data[1]; jint modifierFlags = data[2]; jshort keyCode = (jshort)data[3]; jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED; jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; NsCharToJavaVirtualKeyCode((unichar)testChar, (unichar)testDeadChar, (NSUInteger)modifierFlags, (unsigned short)keyCode, &jkeyCode, &jkeyLocation, &postsTyped); // out = [jkeyCode, jkeyLocation]; (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode); (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation); (*env)->ReleaseIntArrayElements(env, inData, data, 0); JNF_COCOA_EXIT(env); return postsTyped; } /* * Class: sun_lwawt_macosx_event_NSEvent * Method: nsKeyModifiersToJavaKeyInfo * Signature: ([I[I)V */ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_event_NSEvent_nsKeyModifiersToJavaKeyInfo (JNIEnv *env, jclass cls, jintArray inData, jintArray outData) { JNF_COCOA_ENTER(env); jboolean copy = JNI_FALSE; jint *data = (*env)->GetIntArrayElements(env, inData, ©); // in = [modifierFlags, keyCode] jint modifierFlags = data[0]; jshort keyCode = (jshort)data[1]; jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED; jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN; jint jkeyType = java_awt_event_KeyEvent_KEY_PRESSED; NsKeyModifiersToJavaKeyInfo(modifierFlags, keyCode, &jkeyCode, &jkeyLocation, &jkeyType); // out = [jkeyCode, jkeyLocation, jkeyType]; (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode); (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation); (*env)->SetIntArrayRegion(env, outData, 2, 1, &jkeyType); (*env)->ReleaseIntArrayElements(env, inData, data, 0); JNF_COCOA_EXIT(env); } /* * Class: sun_lwawt_macosx_event_NSEvent * Method: nsToJavaChar * Signature: (CI)C */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_event_NSEvent_nsToJavaChar (JNIEnv *env, jclass cls, char nsChar, jint modifierFlags) { jchar javaChar = 0; JNF_COCOA_ENTER(env); javaChar = NsCharToJavaChar(nsChar, modifierFlags); JNF_COCOA_EXIT(env); return javaChar; }