/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 <JavaRuntimeSupport/JavaRuntimeSupport.h>
#import <sys/time.h>
#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
* 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.
*/
{
}
const keyTable[] =
{
{0x5D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, // This is a combo yen/backslash on JIS keyboards.
};
/*
* 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.
*/
UniChar c;
};
{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},
{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},
{0,0}
};
// TODO: some constants below are part of CGS (private interfaces)...
// but not sure this is foolproof...
static struct _nsKeyToJavaModifier
{
//NSUInteger cgsLeftMask;
//NSUInteger cgsRightMask;
unsigned short leftKeyCode;
unsigned short rightKeyCode;
}
const nsKeyToJavaModifierTable[] =
{
{
0,
0,
0, // no Java equivalent
0, // no Java equivalent
},
{
//kCGSFlagsMaskAppleShiftKey,
//kCGSFlagsMaskAppleRightShiftKey,
56,
60,
},
{
//kCGSFlagsMaskAppleControlKey,
//kCGSFlagsMaskAppleRightControlKey,
59,
62,
},
{
//kCGSFlagsMaskAppleLeftAlternateKey,
//kCGSFlagsMaskAppleRightAlternateKey,
58,
61,
},
{
//kCGSFlagsMaskAppleLeftCommandKey,
//kCGSFlagsMaskAppleRightCommandKey,
55,
54,
},
// NSNumericPadKeyMask
{
0,
0,
0, // no Java equivalent
0, // no Java equivalent
},
// NSFunctionKeyMask
{0, 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.
*/
}
const charTable[] = {
// map enter on keypad to same as return key
// [3134616] return newline instead of carriage return
// "delete" means backspace in Java
// back-tab is only differentiated from tab by Shift flag
{0, 0, 0}
};
{
// Mask off just the keyboard modifiers from the event modifier mask.
// walk through table & find the match
// <rdar://Problem/3476426> 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 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.
{
// Likewise, if the modifier field is nonzero, that means
// transform this character if only these modifiers are
// set in the testable flags.
}
}
}
}
// otherwise return character unchanged
return nsChar;
}
{
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
// Carbon modifiers should be used instead of NSEvent modifiers
if (keyboardLayout) {
// get the deadKeyState
// Press SPACE to get the dead key char
LMGetKbdType(), 0,
return unicodeString[0];
}
}
}
return 0;
}
/*
* This is the function that uses the table above to take incoming
* NSEvent keyCodes and translate to the Java virtual key code.
*/
static void
{
if (isDeadChar) {
if (testDeadChar == map->c) {
*postsTyped = NO;
// TODO: use UNKNOWN here?
*deadChar = testDeadChar;
return;
}
}
// If we got here, we keep looking for a normal key.
}
// key is an alphabetic character
// some chars in letter set are NOT actually A-Z characters?!
// skip them...
*postsTyped = YES;
// do quick conversion
return;
}
}
// key is a digit
// make sure in range for decimal digits
*postsTyped = YES;
if (numpad) {
} else {
}
return;
}
}
} else {
// Should we report this? This means we've got a keyboard
// we don't know about...
*postsTyped = NO;
}
}
/*
* This returns the java key data for the key NSEvent modifiers
* (after NSFlagChanged).
*/
static void
{
// 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;
//}
}
break;
}
}
}
/*
* This returns the java modifiers for a key NSEvent.
*/
{
}
}
return javaModifiers;
}
/*
* This returns the NSEvent flags for java key modifiers.
*/
{
}
}
// special case
}
return nsFlags;
}
/*
* 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
{
case NSFlagsChanged:
// no character for modifier keys
break;
case NSKeyDown:
case NSKeyUp:
}
if (javaModifiers == 0) {
// TODO: uses SPI...
//if (TSMGetDeadKeyState() != 0) {
// testDeadChar = [self deadKeyCharacter];
//}
}
if (testChar != 0) {
} else if (testDeadChar != 0) {
} else {
}
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
{
case NSFlagsChanged:
&javaKeyType);
break;
case NSKeyDown:
case NSKeyUp:
}
if( !postsTyped ) {
}
break;
default:
//[NSException raise:@"AWT error" format:@"Attempt to get virtual key code from non-key event!"];
break;
}
}
}
{
// Mousing needs the key modifiers
/*
* Ask Quartz about mouse buttons state
*/
}
}
}
extraButton)) {
}
}
return modifiers;
}
/*
* Converts an NSEvent button number to a MouseEvent constant.
*/
static jint
{
}
return jbutton;
}
void
{
"deliverMouseEvent", "(IIIIFFFF)V");
}
}
/*
* 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
{
return;
}
for (i = 0; i < stringLength; i++) {
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.
static JNF_CLASS_CACHE(jc_CPlatformView,
"sun/lwawt/macosx/CPlatformView");
"deliverKeyEvent", "(IICII)V");
}
}
}
}
}
/*
* 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
* 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
{
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) {
// that have been morphed to move events
}
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.
//
{
if (sLastMouseDraggedView == nil) {
}
else if (hitView != sLastMouseDraggedView) {
// We know sLastMouseDraggedView is a AWTPeerControl.
// Send mouseExit to sLastMouseDraggedView
// Send mouseEnter to hitView
// Set sLastMouseDraggedView = hitView
}
}
break;
default:
break;
}
}
}
return 0;
}
{
}
/*
* Class: sun_lwawt_macosx_event_NSEvent
* Method: nsToJavaMouseModifiers
* Signature: (II)I
*/
{
return jmodifiers;
}
/*
* Class: sun_lwawt_macosx_event_NSEvent
* Method: nsToJavaKeyModifiers
* Signature: (I)I
*/
{
return jmodifiers;
}
/*
* Class: sun_lwawt_macosx_event_NSEvent
* Method: nsToJavaKeyInfo
* Signature: ([I[I)Z
*/
{
// in = [testChar, testDeadChar, modifierFlags, keyCode]
// out = [jkeyCode, jkeyLocation, deadKeyChar];
return postsTyped;
}
/*
* Class: sun_lwawt_macosx_event_NSEvent
* Method: nsKeyModifiersToJavaKeyInfo
* Signature: ([I[I)V
*/
{
// in = [modifierFlags, keyCode]
&jkeyCode,
&jkeyType);
// out = [jkeyCode, jkeyLocation, jkeyType];
}
/*
* Class: sun_lwawt_macosx_event_NSEvent
* Method: nsToJavaChar
* Signature: (CI)C
*/
{
return javaChar;
}