1 /* 2 * Copyright (c) 2011, 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 <JavaNativeFoundation/JavaNativeFoundation.h> 27 #import <ApplicationServices/ApplicationServices.h> 28 29 #import "LWCToolkit.h" 30 #import "sun_lwawt_macosx_CRobot.h" 31 #import "java_awt_event_InputEvent.h" 32 33 34 // Starting number for event numbers generated by Robot. 35 // Apple docs don't mention at all what are the requirements 36 // for these numbers. It seems that they must be higher 37 // than event numbers from real events, which start at some 38 // value close to zero. There is no API for obtaining current 39 // event number, so we have to start from some random number. 40 // 32000 as starting value works for me, let's hope that it will 41 // work for others as well. 42 #define ROBOT_EVENT_NUMBER_START 32000 43 44 #define k_JAVA_ROBOT_WHEEL_COUNT 1 45 46 #if !defined(kCGBitmapByteOrder32Host) 47 #define kCGBitmapByteOrder32Host 0 48 #endif 49 50 // In OS X, left and right mouse button share the same click count. 51 // That is, if one starts clicking the left button rapidly and then 52 // switches to the right button, then the click count will continue 53 // increasing, without dropping to 1 in between. The middle button, 54 // however, has its own click count. 55 // For robot, we aren't going to emulate all that complexity. All our 56 // synhtetic clicks share the same click count. 57 static int gsClickCount; 58 static NSTimeInterval gsLastClickTime; 59 60 // Apparently, for mouse up/down events we have to set an event number 61 // that is incremented on each button press. Otherwise, strange things 62 // happen with z-order. 63 static int gsEventNumber; 64 static int* gsButtonEventNumber; 65 66 static inline CGKeyCode GetCGKeyCode(jint javaKeyCode); 67 68 static void PostMouseEvent(const CGPoint point, CGMouseButton button, 69 CGEventType type, int clickCount, int eventNumber); 70 71 static int GetClickCount(BOOL isDown); 72 73 static void 74 CreateJavaException(JNIEnv* env, CGError err) 75 { 76 // Throw a java exception indicating what is wrong. 77 NSString* s = [NSString stringWithFormat:@"Robot: CGError: %d", err]; 78 (*env)->ThrowNew(env, (*env)->FindClass(env, "java/awt/AWTException"), 79 [s UTF8String]); 80 } 81 82 /* 83 * Class: sun_lwawt_macosx_CRobot 84 * Method: initRobot 85 * Signature: (V)V 86 */ 87 JNIEXPORT void JNICALL 88 Java_sun_lwawt_macosx_CRobot_initRobot 89 (JNIEnv *env, jobject peer) 90 { 91 // Set things up to let our app act like a synthetic keyboard and mouse. 92 // Always set all states, in case Apple ever changes default behaviors. 93 static int setupDone = 0; 94 if (!setupDone) { 95 int i; 96 jint* tmp; 97 jboolean copy = JNI_FALSE; 98 99 setupDone = 1; 100 // Don't block local events after posting ours 101 CGSetLocalEventsSuppressionInterval(0.0); 102 103 // Let our event's modifier key state blend with local hardware events 104 CGEnableEventStateCombining(TRUE); 105 106 // Don't let our events block local hardware events 107 CGSetLocalEventsFilterDuringSupressionState( 108 kCGEventFilterMaskPermitAllEvents, 109 kCGEventSupressionStateSupressionInterval); 110 CGSetLocalEventsFilterDuringSupressionState( 111 kCGEventFilterMaskPermitAllEvents, 112 kCGEventSupressionStateRemoteMouseDrag); 113 114 gsClickCount = 0; 115 gsLastClickTime = 0; 116 gsEventNumber = ROBOT_EVENT_NUMBER_START; 117 118 gsButtonEventNumber = (int*)malloc(sizeof(int) * gNumberOfButtons); 119 if (gsButtonEventNumber == NULL) { 120 JNU_ThrowOutOfMemoryError(env, NULL); 121 return; 122 } 123 124 for (i = 0; i < gNumberOfButtons; ++i) { 125 gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START; 126 } 127 } 128 } 129 130 /* 131 * Class: sun_lwawt_macosx_CRobot 132 * Method: mouseEvent 133 * Signature: (IIIIZZ)V 134 */ 135 JNIEXPORT void JNICALL 136 Java_sun_lwawt_macosx_CRobot_mouseEvent 137 (JNIEnv *env, jobject peer, 138 jint screenIndex, jint mouseLastX, jint mouseLastY, jint buttonsState, 139 jboolean isButtonsDownState, jboolean isMouseMove) 140 { 141 JNF_COCOA_ENTER(env); 142 143 // This is the native method called when Robot mouse events occur. 144 // The CRobot tracks the mouse position, and which button was 145 // pressed. If the mouse position is unknown it is obtained from 146 // CGEvents. The peer also tracks the mouse button desired state, 147 // the appropriate key modifier state, and whether the mouse action 148 // is simply a mouse move with no mouse button state changes. 149 150 CGError err = kCGErrorSuccess; 151 152 CGDirectDisplayID displayID = 153 FindCGDirectDisplayIDForScreenIndex(screenIndex); 154 CGRect globalDeviceBounds = CGDisplayBounds(displayID); 155 156 // Set unknown mouse location, if needed. 157 if ((mouseLastX == sun_lwawt_macosx_CRobot_MOUSE_LOCATION_UNKNOWN) || 158 (mouseLastY == sun_lwawt_macosx_CRobot_MOUSE_LOCATION_UNKNOWN)) 159 { 160 CGEventRef event = CGEventCreate(NULL); 161 if (event == NULL) { 162 return; 163 } 164 165 CGPoint globalPos = CGEventGetLocation(event); 166 CFRelease(event); 167 168 // Normalize the coords within this display device, as 169 // per Robot rules. 170 if (globalPos.x < CGRectGetMinX(globalDeviceBounds)) { 171 globalPos.x = CGRectGetMinX(globalDeviceBounds); 172 } 173 else if (globalPos.x > CGRectGetMaxX(globalDeviceBounds)) { 174 globalPos.x = CGRectGetMaxX(globalDeviceBounds); 175 } 176 177 if (globalPos.y < CGRectGetMinY(globalDeviceBounds)) { 178 globalPos.y = CGRectGetMinY(globalDeviceBounds); 179 } 180 else if (globalPos.y > CGRectGetMaxY(globalDeviceBounds)) { 181 globalPos.y = CGRectGetMaxY(globalDeviceBounds); 182 } 183 184 mouseLastX = (jint)globalPos.x; 185 mouseLastY = (jint)globalPos.y; 186 } 187 188 // volatile, otherwise it warns that it might be clobbered by 'longjmp' 189 volatile CGPoint point; 190 191 // Translate the device relative point into a valid global CGPoint. 192 point.x = mouseLastX + globalDeviceBounds.origin.x; 193 point.y = mouseLastY + globalDeviceBounds.origin.y; 194 195 __block CGMouseButton button = kCGMouseButtonLeft; 196 __block CGEventType type = kCGEventMouseMoved; 197 198 void (^HandleRobotButton)(CGMouseButton, CGEventType, CGEventType, CGEventType) = 199 ^(CGMouseButton cgButton, CGEventType cgButtonUp, CGEventType cgButtonDown, 200 CGEventType cgButtonDragged) { 201 202 button = cgButton; 203 type = cgButtonUp; 204 205 if (isButtonsDownState) { 206 if (isMouseMove) { 207 type = cgButtonDragged; 208 } else { 209 type = cgButtonDown; 210 } 211 } 212 }; 213 214 // Left 215 if (buttonsState & java_awt_event_InputEvent_BUTTON1_MASK || 216 buttonsState & java_awt_event_InputEvent_BUTTON1_DOWN_MASK ) { 217 218 HandleRobotButton(kCGMouseButtonLeft, kCGEventLeftMouseUp, 219 kCGEventLeftMouseDown, kCGEventLeftMouseDragged); 220 } 221 222 // Other 223 if (buttonsState & java_awt_event_InputEvent_BUTTON2_MASK || 224 buttonsState & java_awt_event_InputEvent_BUTTON2_DOWN_MASK ) { 225 226 HandleRobotButton(kCGMouseButtonCenter, kCGEventOtherMouseUp, 227 kCGEventOtherMouseDown, kCGEventOtherMouseDragged); 228 } 229 230 // Right 231 if (buttonsState & java_awt_event_InputEvent_BUTTON3_MASK || 232 buttonsState & java_awt_event_InputEvent_BUTTON3_DOWN_MASK ) { 233 234 HandleRobotButton(kCGMouseButtonRight, kCGEventRightMouseUp, 235 kCGEventRightMouseDown, kCGEventRightMouseDragged); 236 } 237 238 // Extra 239 if (gNumberOfButtons > 3) { 240 int extraButton; 241 for (extraButton = 3; extraButton < gNumberOfButtons; ++extraButton) { 242 if ((buttonsState & gButtonDownMasks[extraButton])) { 243 HandleRobotButton(extraButton, kCGEventOtherMouseUp, 244 kCGEventOtherMouseDown, kCGEventOtherMouseDragged); 245 } 246 } 247 } 248 249 int clickCount = 0; 250 int eventNumber = gsEventNumber; 251 252 if (isMouseMove) { 253 // any mouse movement resets click count 254 gsLastClickTime = 0; 255 } else { 256 clickCount = GetClickCount(isButtonsDownState); 257 258 if (isButtonsDownState) { 259 gsButtonEventNumber[button] = gsEventNumber++; 260 } 261 eventNumber = gsButtonEventNumber[button]; 262 } 263 264 PostMouseEvent(point, button, type, clickCount, eventNumber); 265 266 JNF_COCOA_EXIT(env); 267 } 268 269 /* 270 * Class: sun_lwawt_macosx_CRobot 271 * Method: mouseWheel 272 * Signature: (I)V 273 */ 274 JNIEXPORT void JNICALL 275 Java_sun_lwawt_macosx_CRobot_mouseWheel 276 (JNIEnv *env, jobject peer, jint wheelAmt) 277 { 278 CGEventRef event = CGEventCreateScrollWheelEvent(NULL, 279 kCGScrollEventUnitLine, 280 k_JAVA_ROBOT_WHEEL_COUNT, wheelAmt); 281 282 if (event != NULL) { 283 CGEventPost(kCGSessionEventTap, event); 284 CFRelease(event); 285 } 286 } 287 288 /* 289 * Class: sun_lwawt_macosx_CRobot 290 * Method: keyEvent 291 * Signature: (IZ)V 292 */ 293 JNIEXPORT void JNICALL 294 Java_sun_lwawt_macosx_CRobot_keyEvent 295 (JNIEnv *env, jobject peer, jint javaKeyCode, jboolean keyPressed) 296 { 297 /* 298 * Well, using CGEventCreateKeyboardEvent/CGEventPost would have been 299 * a better solution, however, it gives me all kinds of trouble and I have 300 * no idea how to solve them without inserting delays between simulated 301 * events. So, I've ended up disabling it and opted for another approach 302 * that uses Accessibility API instead. 303 */ 304 CGKeyCode keyCode = GetCGKeyCode(javaKeyCode); 305 AXUIElementRef elem = AXUIElementCreateSystemWide(); 306 AXUIElementPostKeyboardEvent(elem, (CGCharCode)0, keyCode, keyPressed); 307 CFRelease(elem); 308 309 310 #if 0 311 CGEventRef event = CGEventCreateKeyboardEvent(NULL, keyCode, keyPressed); 312 if (event != NULL) { 313 CGEventPost(kCGSessionEventTap, event); 314 CFRelease(event); 315 } 316 #endif 317 } 318 319 /* 320 * Class: sun_lwawt_macosx_CRobot 321 * Method: nativeGetScreenPixels 322 * Signature: (IIIII[I)V 323 */ 324 JNIEXPORT void JNICALL 325 Java_sun_lwawt_macosx_CRobot_nativeGetScreenPixels 326 (JNIEnv *env, jobject peer, 327 jint x, jint y, jint width, jint height, jintArray pixels) 328 { 329 JNF_COCOA_ENTER(env); 330 331 jint picX = x; 332 jint picY = y; 333 jint picWidth = width; 334 jint picHeight = height; 335 336 CGRect screenRect = CGRectMake(picX, picY, picWidth, picHeight); 337 CGImageRef screenPixelsImage = CGWindowListCreateImage(screenRect, 338 kCGWindowListOptionOnScreenOnly, 339 kCGNullWindowID, kCGWindowImageDefault); 340 341 if (screenPixelsImage == NULL) { 342 return; 343 } 344 345 // get a pointer to the Java int array 346 void *jPixelData = (*env)->GetPrimitiveArrayCritical(env, pixels, 0); 347 348 // create a graphics context around the Java int array 349 CGColorSpaceRef picColorSpace = CGColorSpaceCreateWithName( 350 kCGColorSpaceGenericRGB); 351 CGContextRef jPicContextRef = CGBitmapContextCreate( 352 jPixelData, 353 picWidth, picHeight, 354 8, picWidth * sizeof(jint), 355 picColorSpace, 356 kCGBitmapByteOrder32Host | 357 kCGImageAlphaPremultipliedFirst); 358 359 CGColorSpaceRelease(picColorSpace); 360 361 // flip, scale, and color correct the screen image into the Java pixels 362 CGRect bounds = { { 0, 0 }, { picWidth, picHeight } }; 363 CGContextDrawImage(jPicContextRef, bounds, screenPixelsImage); 364 CGContextFlush(jPicContextRef); 365 366 // cleanup 367 CGContextRelease(jPicContextRef); 368 CGImageRelease(screenPixelsImage); 369 370 // release the Java int array back up to the JVM 371 (*env)->ReleasePrimitiveArrayCritical(env, pixels, jPixelData, 0); 372 373 JNF_COCOA_EXIT(env); 374 } 375 376 /**************************************************** 377 * Helper methods 378 ****************************************************/ 379 380 static void PostMouseEvent(const CGPoint point, CGMouseButton button, 381 CGEventType type, int clickCount, int eventNumber) 382 { 383 CGEventRef mouseEvent = CGEventCreateMouseEvent(NULL, type, point, button); 384 if (mouseEvent != NULL) { 385 CGEventSetIntegerValueField(mouseEvent, kCGMouseEventClickState, clickCount); 386 CGEventSetIntegerValueField(mouseEvent, kCGMouseEventNumber, eventNumber); 387 CGEventPost(kCGSessionEventTap, mouseEvent); 388 CFRelease(mouseEvent); 389 } 390 } 391 392 // NOTE: Don't modify this table directly. It is machine generated. See below. 393 static const unsigned char javaToMacKeyCode[] = { 394 127, // 0 0 VK_UNDEFINED No_Equivalent 395 127, // 1 0x1 Not_Used 396 127, // 2 0x2 Not_Used 397 127, // 3 0x3 VK_CANCEL No_Equivalent 398 127, // 4 0x4 Not_Used 399 127, // 5 0x5 Not_Used 400 127, // 6 0x6 Not_Used 401 127, // 7 0x7 Not_Used 402 51, // 8 0x8 VK_BACK_SPACE 403 48, // 9 0x9 VK_TAB 404 36, // 10 0xa VK_ENTER 405 127, // 11 0xb Not_Used 406 71, // 12 0xc VK_CLEAR 407 127, // 13 0xd Not_Used 408 127, // 14 0xe Not_Used 409 127, // 15 0xf Not_Used 410 56, // 16 0x10 VK_SHIFT 411 59, // 17 0x11 VK_CONTROL 412 58, // 18 0x12 VK_ALT 413 113, // 19 0x13 VK_PAUSE 414 57, // 20 0x14 VK_CAPS_LOCK 415 127, // 21 0x15 VK_KANA No_Equivalent 416 127, // 22 0x16 Not_Used 417 127, // 23 0x17 Not_Used 418 127, // 24 0x18 VK_FINAL No_Equivalent 419 127, // 25 0x19 VK_KANJI No_Equivalent 420 127, // 26 0x1a Not_Used 421 53, // 27 0x1b VK_ESCAPE 422 127, // 28 0x1c VK_CONVERT No_Equivalent 423 127, // 29 0x1d VK_NONCONVERT No_Equivalent 424 127, // 30 0x1e VK_ACCEPT No_Equivalent 425 127, // 31 0x1f VK_MODECHANGE No_Equivalent 426 49, // 32 0x20 VK_SPACE 427 116, // 33 0x21 VK_PAGE_UP 428 121, // 34 0x22 VK_PAGE_DOWN 429 119, // 35 0x23 VK_END 430 115, // 36 0x24 VK_HOME 431 123, // 37 0x25 VK_LEFT 432 126, // 38 0x26 VK_UP 433 124, // 39 0x27 VK_RIGHT 434 125, // 40 0x28 VK_DOWN 435 127, // 41 0x29 Not_Used 436 127, // 42 0x2a Not_Used 437 127, // 43 0x2b Not_Used 438 43, // 44 0x2c VK_COMMA 439 27, // 45 0x2d VK_MINUS 440 47, // 46 0x2e VK_PERIOD 441 44, // 47 0x2f VK_SLASH 442 29, // 48 0x30 VK_0 443 18, // 49 0x31 VK_1 444 19, // 50 0x32 VK_2 445 20, // 51 0x33 VK_3 446 21, // 52 0x34 VK_4 447 23, // 53 0x35 VK_5 448 22, // 54 0x36 VK_6 449 26, // 55 0x37 VK_7 450 28, // 56 0x38 VK_8 451 25, // 57 0x39 VK_9 452 127, // 58 0x3a Not_Used 453 41, // 59 0x3b VK_SEMICOLON 454 127, // 60 0x3c Not_Used 455 24, // 61 0x3d VK_EQUALS 456 127, // 62 0x3e Not_Used 457 127, // 63 0x3f Not_Used 458 127, // 64 0x40 Not_Used 459 0, // 65 0x41 VK_A 460 11, // 66 0x42 VK_B 461 8, // 67 0x43 VK_C 462 2, // 68 0x44 VK_D 463 14, // 69 0x45 VK_E 464 3, // 70 0x46 VK_F 465 5, // 71 0x47 VK_G 466 4, // 72 0x48 VK_H 467 34, // 73 0x49 VK_I 468 38, // 74 0x4a VK_J 469 40, // 75 0x4b VK_K 470 37, // 76 0x4c VK_L 471 46, // 77 0x4d VK_M 472 45, // 78 0x4e VK_N 473 31, // 79 0x4f VK_O 474 35, // 80 0x50 VK_P 475 12, // 81 0x51 VK_Q 476 15, // 82 0x52 VK_R 477 1, // 83 0x53 VK_S 478 17, // 84 0x54 VK_T 479 32, // 85 0x55 VK_U 480 9, // 86 0x56 VK_V 481 13, // 87 0x57 VK_W 482 7, // 88 0x58 VK_X 483 16, // 89 0x59 VK_Y 484 6, // 90 0x5a VK_Z 485 33, // 91 0x5b VK_OPEN_BRACKET 486 42, // 92 0x5c VK_BACK_SLASH 487 30, // 93 0x5d VK_CLOSE_BRACKET 488 127, // 94 0x5e Not_Used 489 127, // 95 0x5f Not_Used 490 82, // 96 0x60 VK_NUMPAD0 491 83, // 97 0x61 VK_NUMPAD1 492 84, // 98 0x62 VK_NUMPAD2 493 85, // 99 0x63 VK_NUMPAD3 494 86, // 100 0x64 VK_NUMPAD4 495 87, // 101 0x65 VK_NUMPAD5 496 88, // 102 0x66 VK_NUMPAD6 497 89, // 103 0x67 VK_NUMPAD7 498 91, // 104 0x68 VK_NUMPAD8 499 92, // 105 0x69 VK_NUMPAD9 500 67, // 106 0x6a VK_MULTIPLY 501 69, // 107 0x6b VK_ADD 502 127, // 108 0x6c VK_SEPARATER No_Equivalent 503 78, // 109 0x6d VK_SUBTRACT 504 65, // 110 0x6e VK_DECIMAL 505 75, // 111 0x6f VK_DIVIDE 506 122, // 112 0x70 VK_F1 507 120, // 113 0x71 VK_F2 508 99, // 114 0x72 VK_F3 509 118, // 115 0x73 VK_F4 510 96, // 116 0x74 VK_F5 511 97, // 117 0x75 VK_F6 512 98, // 118 0x76 VK_F7 513 100, // 119 0x77 VK_F8 514 101, // 120 0x78 VK_F9 515 109, // 121 0x79 VK_F10 516 103, // 122 0x7a VK_F11 517 111, // 123 0x7b VK_F12 518 127, // 124 0x7c Not_Used 519 127, // 125 0x7d Not_Used 520 127, // 126 0x7e Not_Used 521 117, // 127 0x7f VK_DELETE 522 127, // 128 0x80 VK_DEAD_GRAVE No_Equivalent 523 127, // 129 0x81 VK_DEAD_ACUTE No_Equivalent 524 127, // 130 0x82 VK_DEAD_CIRCUMFLEX No_Equivalent 525 127, // 131 0x83 VK_DEAD_TILDE No_Equivalent 526 127, // 132 0x84 VK_DEAD_MACRON No_Equivalent 527 127, // 133 0x85 VK_DEAD_BREVE No_Equivalent 528 127, // 134 0x86 VK_DEAD_ABOVEDOT No_Equivalent 529 127, // 135 0x87 VK_DEAD_DIAERESIS No_Equivalent 530 127, // 136 0x88 VK_DEAD_ABOVERING No_Equivalent 531 127, // 137 0x89 VK_DEAD_DOUBLEACUTE No_Equivalent 532 127, // 138 0x8a VK_DEAD_CARON No_Equivalent 533 127, // 139 0x8b VK_DEAD_CEDILLA No_Equivalent 534 127, // 140 0x8c VK_DEAD_OGONEK No_Equivalent 535 127, // 141 0x8d VK_DEAD_IOTA No_Equivalent 536 127, // 142 0x8e VK_DEAD_VOICED_SOUND No_Equivalent 537 127, // 143 0x8f VK_DEAD_SEMIVOICED_SOUND No_Equivalent 538 127, // 144 0x90 VK_NUM_LOCK No_Equivalent 539 107, // 145 0x91 VK_SCROLL_LOCK 540 127, // 146 0x92 Not_Used 541 127, // 147 0x93 Not_Used 542 127, // 148 0x94 Not_Used 543 127, // 149 0x95 Not_Used 544 127, // 150 0x96 VK_AMPERSAND No_Equivalent 545 127, // 151 0x97 VK_ASTERISK No_Equivalent 546 127, // 152 0x98 VK_QUOTEDBL No_Equivalent 547 127, // 153 0x99 VK_LESS No_Equivalent 548 105, // 154 0x9a VK_PRINTSCREEN 549 127, // 155 0x9b VK_INSERT No_Equivalent 550 114, // 156 0x9c VK_HELP 551 55, // 157 0x9d VK_META 552 127, // 158 0x9e Not_Used 553 127, // 159 0x9f Not_Used 554 127, // 160 0xa0 VK_GREATER No_Equivalent 555 127, // 161 0xa1 VK_BRACELEFT No_Equivalent 556 127, // 162 0xa2 VK_BRACERIGHT No_Equivalent 557 127, // 163 0xa3 Not_Used 558 127, // 164 0xa4 Not_Used 559 127, // 165 0xa5 Not_Used 560 127, // 166 0xa6 Not_Used 561 127, // 167 0xa7 Not_Used 562 127, // 168 0xa8 Not_Used 563 127, // 169 0xa9 Not_Used 564 127, // 170 0xaa Not_Used 565 127, // 171 0xab Not_Used 566 127, // 172 0xac Not_Used 567 127, // 173 0xad Not_Used 568 127, // 174 0xae Not_Used 569 127, // 175 0xaf Not_Used 570 127, // 176 0xb0 Not_Used 571 127, // 177 0xb1 Not_Used 572 127, // 178 0xb2 Not_Used 573 127, // 179 0xb3 Not_Used 574 127, // 180 0xb4 Not_Used 575 127, // 181 0xb5 Not_Used 576 127, // 182 0xb6 Not_Used 577 127, // 183 0xb7 Not_Used 578 127, // 184 0xb8 Not_Used 579 127, // 185 0xb9 Not_Used 580 127, // 186 0xba Not_Used 581 127, // 187 0xbb Not_Used 582 127, // 188 0xbc Not_Used 583 127, // 189 0xbd Not_Used 584 127, // 190 0xbe Not_Used 585 127, // 191 0xbf Not_Used 586 50, // 192 0xc0 VK_BACK_QUOTE 587 127, // 193 0xc1 Not_Used 588 127, // 194 0xc2 Not_Used 589 127, // 195 0xc3 Not_Used 590 127, // 196 0xc4 Not_Used 591 127, // 197 0xc5 Not_Used 592 127, // 198 0xc6 Not_Used 593 127, // 199 0xc7 Not_Used 594 127, // 200 0xc8 Not_Used 595 127, // 201 0xc9 Not_Used 596 127, // 202 0xca Not_Used 597 127, // 203 0xcb Not_Used 598 127, // 204 0xcc Not_Used 599 127, // 205 0xcd Not_Used 600 127, // 206 0xce Not_Used 601 127, // 207 0xcf Not_Used 602 127, // 208 0xd0 Not_Used 603 127, // 209 0xd1 Not_Used 604 127, // 210 0xd2 Not_Used 605 127, // 211 0xd3 Not_Used 606 127, // 212 0xd4 Not_Used 607 127, // 213 0xd5 Not_Used 608 127, // 214 0xd6 Not_Used 609 127, // 215 0xd7 Not_Used 610 127, // 216 0xd8 Not_Used 611 127, // 217 0xd9 Not_Used 612 127, // 218 0xda Not_Used 613 127, // 219 0xdb Not_Used 614 127, // 220 0xdc Not_Used 615 127, // 221 0xdd Not_Used 616 39 // 222 0xde VK_QUOTE 617 }; 618 619 // NOTE: All values above 222 don't have an equivalent on MacOSX. 620 static inline CGKeyCode GetCGKeyCode(jint javaKeyCode) 621 { 622 if (javaKeyCode > 222) { 623 return 127; 624 } else { 625 return javaToMacKeyCode[javaKeyCode]; 626 } 627 } 628 629 static int GetClickCount(BOOL isDown) { 630 NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate]; 631 NSTimeInterval clickInterval = now - gsLastClickTime; 632 BOOL isWithinTreshold = clickInterval < [NSEvent doubleClickInterval]; 633 634 if (isDown) { 635 if (isWithinTreshold) { 636 gsClickCount++; 637 } else { 638 gsClickCount = 1; 639 } 640 641 gsLastClickTime = now; 642 } else { 643 // In OS X, a mouse up has the click count of the last mouse down 644 // if an interval between up and down is within the double click 645 // threshold, and 0 otherwise. 646 if (!isWithinTreshold) { 647 gsClickCount = 0; 648 } 649 } 650 651 return gsClickCount; 652 }