Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/sun/awt/CRobot.m
38829 views
/*1* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/242526#import "jni_util.h"2728#import <JavaNativeFoundation/JavaNativeFoundation.h>29#import <ApplicationServices/ApplicationServices.h>3031#import "LWCToolkit.h"32#import "sun_lwawt_macosx_CRobot.h"33#import "java_awt_event_InputEvent.h"34#import "sizecalc.h"353637// Starting number for event numbers generated by Robot.38// Apple docs don't mention at all what are the requirements39// for these numbers. It seems that they must be higher40// than event numbers from real events, which start at some41// value close to zero. There is no API for obtaining current42// event number, so we have to start from some random number.43// 32000 as starting value works for me, let's hope that it will44// work for others as well.45#define ROBOT_EVENT_NUMBER_START 320004647#define k_JAVA_ROBOT_WHEEL_COUNT 14849#if !defined(kCGBitmapByteOrder32Host)50#define kCGBitmapByteOrder32Host 051#endif5253// In OS X, left and right mouse button share the same click count.54// That is, if one starts clicking the left button rapidly and then55// switches to the right button, then the click count will continue56// increasing, without dropping to 1 in between. The middle button,57// however, has its own click count.58// For robot, we aren't going to emulate all that complexity. All our59// synhtetic clicks share the same click count.60static int gsClickCount;61static NSTimeInterval gsLastClickTime;6263// Apparently, for mouse up/down events we have to set an event number64// that is incremented on each button press. Otherwise, strange things65// happen with z-order.66static int gsEventNumber;67static int* gsButtonEventNumber;6869static inline CGKeyCode GetCGKeyCode(jint javaKeyCode);7071static void PostMouseEvent(const CGPoint point, CGMouseButton button,72CGEventType type, int clickCount, int eventNumber);7374static int GetClickCount(BOOL isDown);7576static void77CreateJavaException(JNIEnv* env, CGError err)78{79// Throw a java exception indicating what is wrong.80NSString* s = [NSString stringWithFormat:@"Robot: CGError: %d", err];81(*env)->ThrowNew(env, (*env)->FindClass(env, "java/awt/AWTException"),82[s UTF8String]);83}8485/*86* Class: sun_lwawt_macosx_CRobot87* Method: initRobot88* Signature: (V)V89*/90JNIEXPORT void JNICALL91Java_sun_lwawt_macosx_CRobot_initRobot92(JNIEnv *env, jobject peer)93{94// Set things up to let our app act like a synthetic keyboard and mouse.95// Always set all states, in case Apple ever changes default behaviors.96static int setupDone = 0;97if (!setupDone) {98int i;99jint* tmp;100jboolean copy = JNI_FALSE;101102setupDone = 1;103// Don't block local events after posting ours104CGSetLocalEventsSuppressionInterval(0.0);105106// Let our event's modifier key state blend with local hardware events107CGEnableEventStateCombining(TRUE);108109// Don't let our events block local hardware events110CGSetLocalEventsFilterDuringSupressionState(111kCGEventFilterMaskPermitAllEvents,112kCGEventSupressionStateSupressionInterval);113CGSetLocalEventsFilterDuringSupressionState(114kCGEventFilterMaskPermitAllEvents,115kCGEventSupressionStateRemoteMouseDrag);116117gsClickCount = 0;118gsLastClickTime = 0;119gsEventNumber = ROBOT_EVENT_NUMBER_START;120121gsButtonEventNumber = (int*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(int), gNumberOfButtons);122if (gsButtonEventNumber == NULL) {123JNU_ThrowOutOfMemoryError(env, NULL);124return;125}126127for (i = 0; i < gNumberOfButtons; ++i) {128gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START;129}130}131}132133/*134* Class: sun_lwawt_macosx_CRobot135* Method: mouseEvent136* Signature: (IIIIZZ)V137*/138JNIEXPORT void JNICALL139Java_sun_lwawt_macosx_CRobot_mouseEvent140(JNIEnv *env, jobject peer,141jint displayID, jint mouseLastX, jint mouseLastY, jint buttonsState,142jboolean isButtonsDownState, jboolean isMouseMove)143{144JNF_COCOA_ENTER(env);145146// This is the native method called when Robot mouse events occur.147// The CRobot tracks the mouse position, and which button was148// pressed. The peer also tracks the mouse button desired state,149// the appropriate key modifier state, and whether the mouse action150// is simply a mouse move with no mouse button state changes.151152// volatile, otherwise it warns that it might be clobbered by 'longjmp'153volatile CGPoint point;154155point.x = mouseLastX;156point.y = mouseLastY;157158__block CGMouseButton button = kCGMouseButtonLeft;159__block CGEventType type = kCGEventMouseMoved;160161void (^HandleRobotButton)(CGMouseButton, CGEventType, CGEventType, CGEventType) =162^(CGMouseButton cgButton, CGEventType cgButtonUp, CGEventType cgButtonDown,163CGEventType cgButtonDragged) {164165button = cgButton;166type = cgButtonUp;167168if (isButtonsDownState) {169if (isMouseMove) {170type = cgButtonDragged;171} else {172type = cgButtonDown;173}174}175};176177// Left178if (buttonsState & java_awt_event_InputEvent_BUTTON1_MASK ||179buttonsState & java_awt_event_InputEvent_BUTTON1_DOWN_MASK ) {180181HandleRobotButton(kCGMouseButtonLeft, kCGEventLeftMouseUp,182kCGEventLeftMouseDown, kCGEventLeftMouseDragged);183}184185// Other186if (buttonsState & java_awt_event_InputEvent_BUTTON2_MASK ||187buttonsState & java_awt_event_InputEvent_BUTTON2_DOWN_MASK ) {188189HandleRobotButton(kCGMouseButtonCenter, kCGEventOtherMouseUp,190kCGEventOtherMouseDown, kCGEventOtherMouseDragged);191}192193// Right194if (buttonsState & java_awt_event_InputEvent_BUTTON3_MASK ||195buttonsState & java_awt_event_InputEvent_BUTTON3_DOWN_MASK ) {196197HandleRobotButton(kCGMouseButtonRight, kCGEventRightMouseUp,198kCGEventRightMouseDown, kCGEventRightMouseDragged);199}200201// Extra202if (gNumberOfButtons > 3) {203int extraButton;204for (extraButton = 3; extraButton < gNumberOfButtons; ++extraButton) {205if ((buttonsState & gButtonDownMasks[extraButton])) {206HandleRobotButton(extraButton, kCGEventOtherMouseUp,207kCGEventOtherMouseDown, kCGEventOtherMouseDragged);208}209}210}211212int clickCount = 0;213int eventNumber = gsEventNumber;214215if (isMouseMove) {216// any mouse movement resets click count217gsLastClickTime = 0;218} else {219clickCount = GetClickCount(isButtonsDownState);220221if (isButtonsDownState) {222gsButtonEventNumber[button] = gsEventNumber++;223}224eventNumber = gsButtonEventNumber[button];225}226227PostMouseEvent(point, button, type, clickCount, eventNumber);228229JNF_COCOA_EXIT(env);230}231232/*233* Class: sun_lwawt_macosx_CRobot234* Method: mouseWheel235* Signature: (I)V236*/237JNIEXPORT void JNICALL238Java_sun_lwawt_macosx_CRobot_mouseWheel239(JNIEnv *env, jobject peer, jint wheelAmt)240{241CGEventRef event = CGEventCreateScrollWheelEvent(NULL,242kCGScrollEventUnitLine,243k_JAVA_ROBOT_WHEEL_COUNT, wheelAmt);244245if (event != NULL) {246CGEventPost(kCGSessionEventTap, event);247CFRelease(event);248}249}250251/*252* Class: sun_lwawt_macosx_CRobot253* Method: keyEvent254* Signature: (IZ)V255*/256JNIEXPORT void JNICALL257Java_sun_lwawt_macosx_CRobot_keyEvent258(JNIEnv *env, jobject peer, jint javaKeyCode, jboolean keyPressed)259{260/*261* Well, using CGEventCreateKeyboardEvent/CGEventPost would have been262* a better solution, however, it gives me all kinds of trouble and I have263* no idea how to solve them without inserting delays between simulated264* events. So, I've ended up disabling it and opted for another approach265* that uses Accessibility API instead.266*/267CGKeyCode keyCode = GetCGKeyCode(javaKeyCode);268AXUIElementRef elem = AXUIElementCreateSystemWide();269AXUIElementPostKeyboardEvent(elem, (CGCharCode)0, keyCode, keyPressed);270CFRelease(elem);271272273#if 0274CGEventRef event = CGEventCreateKeyboardEvent(NULL, keyCode, keyPressed);275if (event != NULL) {276CGEventPost(kCGSessionEventTap, event);277CFRelease(event);278}279#endif280}281282/*283* Class: sun_lwawt_macosx_CRobot284* Method: nativeGetScreenPixels285* Signature: (IIIII[I)V286*/287JNIEXPORT void JNICALL288Java_sun_lwawt_macosx_CRobot_nativeGetScreenPixels289(JNIEnv *env, jobject peer,290jint x, jint y, jint width, jint height, jintArray pixels)291{292JNF_COCOA_ENTER(env);293294jint picX = x;295jint picY = y;296jint picWidth = width;297jint picHeight = height;298299CGRect screenRect = CGRectMake(picX, picY, picWidth, picHeight);300CGImageRef screenPixelsImage = CGWindowListCreateImage(screenRect,301kCGWindowListOptionOnScreenOnly,302kCGNullWindowID, kCGWindowImageDefault);303304if (screenPixelsImage == NULL) {305return;306}307308// get a pointer to the Java int array309void *jPixelData = (*env)->GetPrimitiveArrayCritical(env, pixels, 0);310CHECK_NULL(jPixelData);311312// create a graphics context around the Java int array313CGColorSpaceRef picColorSpace = CGColorSpaceCreateWithName(314kCGColorSpaceGenericRGB);315CGContextRef jPicContextRef = CGBitmapContextCreate(316jPixelData,317picWidth, picHeight,3188, picWidth * sizeof(jint),319picColorSpace,320kCGBitmapByteOrder32Host |321kCGImageAlphaPremultipliedFirst);322323CGColorSpaceRelease(picColorSpace);324325// flip, scale, and color correct the screen image into the Java pixels326CGRect bounds = { { 0, 0 }, { picWidth, picHeight } };327CGContextDrawImage(jPicContextRef, bounds, screenPixelsImage);328CGContextFlush(jPicContextRef);329330// cleanup331CGContextRelease(jPicContextRef);332CGImageRelease(screenPixelsImage);333334// release the Java int array back up to the JVM335(*env)->ReleasePrimitiveArrayCritical(env, pixels, jPixelData, 0);336337JNF_COCOA_EXIT(env);338}339340/****************************************************341* Helper methods342****************************************************/343344static void PostMouseEvent(const CGPoint point, CGMouseButton button,345CGEventType type, int clickCount, int eventNumber)346{347CGEventRef mouseEvent = CGEventCreateMouseEvent(NULL, type, point, button);348if (mouseEvent != NULL) {349CGEventSetIntegerValueField(mouseEvent, kCGMouseEventClickState, clickCount);350CGEventSetIntegerValueField(mouseEvent, kCGMouseEventNumber, eventNumber);351CGEventPost(kCGSessionEventTap, mouseEvent);352CFRelease(mouseEvent);353}354}355356// NOTE: Don't modify this table directly. It is machine generated. See below.357static const unsigned char javaToMacKeyCode[] = {358127, // 0 0 VK_UNDEFINED No_Equivalent359127, // 1 0x1 Not_Used360127, // 2 0x2 Not_Used361127, // 3 0x3 VK_CANCEL No_Equivalent362127, // 4 0x4 Not_Used363127, // 5 0x5 Not_Used364127, // 6 0x6 Not_Used365127, // 7 0x7 Not_Used36651, // 8 0x8 VK_BACK_SPACE36748, // 9 0x9 VK_TAB36836, // 10 0xa VK_ENTER369127, // 11 0xb Not_Used37071, // 12 0xc VK_CLEAR371127, // 13 0xd Not_Used372127, // 14 0xe Not_Used373127, // 15 0xf Not_Used37456, // 16 0x10 VK_SHIFT37559, // 17 0x11 VK_CONTROL37658, // 18 0x12 VK_ALT377113, // 19 0x13 VK_PAUSE37857, // 20 0x14 VK_CAPS_LOCK379127, // 21 0x15 VK_KANA No_Equivalent380127, // 22 0x16 Not_Used381127, // 23 0x17 Not_Used382127, // 24 0x18 VK_FINAL No_Equivalent383127, // 25 0x19 VK_KANJI No_Equivalent384127, // 26 0x1a Not_Used38553, // 27 0x1b VK_ESCAPE386127, // 28 0x1c VK_CONVERT No_Equivalent387127, // 29 0x1d VK_NONCONVERT No_Equivalent388127, // 30 0x1e VK_ACCEPT No_Equivalent389127, // 31 0x1f VK_MODECHANGE No_Equivalent39049, // 32 0x20 VK_SPACE391116, // 33 0x21 VK_PAGE_UP392121, // 34 0x22 VK_PAGE_DOWN393119, // 35 0x23 VK_END394115, // 36 0x24 VK_HOME395123, // 37 0x25 VK_LEFT396126, // 38 0x26 VK_UP397124, // 39 0x27 VK_RIGHT398125, // 40 0x28 VK_DOWN399127, // 41 0x29 Not_Used400127, // 42 0x2a Not_Used401127, // 43 0x2b Not_Used40243, // 44 0x2c VK_COMMA40327, // 45 0x2d VK_MINUS40447, // 46 0x2e VK_PERIOD40544, // 47 0x2f VK_SLASH40629, // 48 0x30 VK_040718, // 49 0x31 VK_140819, // 50 0x32 VK_240920, // 51 0x33 VK_341021, // 52 0x34 VK_441123, // 53 0x35 VK_541222, // 54 0x36 VK_641326, // 55 0x37 VK_741428, // 56 0x38 VK_841525, // 57 0x39 VK_9416127, // 58 0x3a Not_Used41741, // 59 0x3b VK_SEMICOLON418127, // 60 0x3c Not_Used41924, // 61 0x3d VK_EQUALS420127, // 62 0x3e Not_Used421127, // 63 0x3f Not_Used422127, // 64 0x40 Not_Used4230, // 65 0x41 VK_A42411, // 66 0x42 VK_B4258, // 67 0x43 VK_C4262, // 68 0x44 VK_D42714, // 69 0x45 VK_E4283, // 70 0x46 VK_F4295, // 71 0x47 VK_G4304, // 72 0x48 VK_H43134, // 73 0x49 VK_I43238, // 74 0x4a VK_J43340, // 75 0x4b VK_K43437, // 76 0x4c VK_L43546, // 77 0x4d VK_M43645, // 78 0x4e VK_N43731, // 79 0x4f VK_O43835, // 80 0x50 VK_P43912, // 81 0x51 VK_Q44015, // 82 0x52 VK_R4411, // 83 0x53 VK_S44217, // 84 0x54 VK_T44332, // 85 0x55 VK_U4449, // 86 0x56 VK_V44513, // 87 0x57 VK_W4467, // 88 0x58 VK_X44716, // 89 0x59 VK_Y4486, // 90 0x5a VK_Z44933, // 91 0x5b VK_OPEN_BRACKET45042, // 92 0x5c VK_BACK_SLASH45130, // 93 0x5d VK_CLOSE_BRACKET452127, // 94 0x5e Not_Used453127, // 95 0x5f Not_Used45482, // 96 0x60 VK_NUMPAD045583, // 97 0x61 VK_NUMPAD145684, // 98 0x62 VK_NUMPAD245785, // 99 0x63 VK_NUMPAD345886, // 100 0x64 VK_NUMPAD445987, // 101 0x65 VK_NUMPAD546088, // 102 0x66 VK_NUMPAD646189, // 103 0x67 VK_NUMPAD746291, // 104 0x68 VK_NUMPAD846392, // 105 0x69 VK_NUMPAD946467, // 106 0x6a VK_MULTIPLY46569, // 107 0x6b VK_ADD466127, // 108 0x6c VK_SEPARATER No_Equivalent46778, // 109 0x6d VK_SUBTRACT46865, // 110 0x6e VK_DECIMAL46975, // 111 0x6f VK_DIVIDE470122, // 112 0x70 VK_F1471120, // 113 0x71 VK_F247299, // 114 0x72 VK_F3473118, // 115 0x73 VK_F447496, // 116 0x74 VK_F547597, // 117 0x75 VK_F647698, // 118 0x76 VK_F7477100, // 119 0x77 VK_F8478101, // 120 0x78 VK_F9479109, // 121 0x79 VK_F10480103, // 122 0x7a VK_F11481111, // 123 0x7b VK_F12482127, // 124 0x7c Not_Used483127, // 125 0x7d Not_Used484127, // 126 0x7e Not_Used485117, // 127 0x7f VK_DELETE486127, // 128 0x80 VK_DEAD_GRAVE No_Equivalent487127, // 129 0x81 VK_DEAD_ACUTE No_Equivalent488127, // 130 0x82 VK_DEAD_CIRCUMFLEX No_Equivalent489127, // 131 0x83 VK_DEAD_TILDE No_Equivalent490127, // 132 0x84 VK_DEAD_MACRON No_Equivalent491127, // 133 0x85 VK_DEAD_BREVE No_Equivalent492127, // 134 0x86 VK_DEAD_ABOVEDOT No_Equivalent493127, // 135 0x87 VK_DEAD_DIAERESIS No_Equivalent494127, // 136 0x88 VK_DEAD_ABOVERING No_Equivalent495127, // 137 0x89 VK_DEAD_DOUBLEACUTE No_Equivalent496127, // 138 0x8a VK_DEAD_CARON No_Equivalent497127, // 139 0x8b VK_DEAD_CEDILLA No_Equivalent498127, // 140 0x8c VK_DEAD_OGONEK No_Equivalent499127, // 141 0x8d VK_DEAD_IOTA No_Equivalent500127, // 142 0x8e VK_DEAD_VOICED_SOUND No_Equivalent501127, // 143 0x8f VK_DEAD_SEMIVOICED_SOUND No_Equivalent502127, // 144 0x90 VK_NUM_LOCK No_Equivalent503107, // 145 0x91 VK_SCROLL_LOCK504127, // 146 0x92 Not_Used505127, // 147 0x93 Not_Used506127, // 148 0x94 Not_Used507127, // 149 0x95 Not_Used508127, // 150 0x96 VK_AMPERSAND No_Equivalent509127, // 151 0x97 VK_ASTERISK No_Equivalent510127, // 152 0x98 VK_QUOTEDBL No_Equivalent511127, // 153 0x99 VK_LESS No_Equivalent512105, // 154 0x9a VK_PRINTSCREEN513127, // 155 0x9b VK_INSERT No_Equivalent514114, // 156 0x9c VK_HELP51555, // 157 0x9d VK_META516127, // 158 0x9e Not_Used517127, // 159 0x9f Not_Used518127, // 160 0xa0 VK_GREATER No_Equivalent519127, // 161 0xa1 VK_BRACELEFT No_Equivalent520127, // 162 0xa2 VK_BRACERIGHT No_Equivalent521127, // 163 0xa3 Not_Used522127, // 164 0xa4 Not_Used523127, // 165 0xa5 Not_Used524127, // 166 0xa6 Not_Used525127, // 167 0xa7 Not_Used526127, // 168 0xa8 Not_Used527127, // 169 0xa9 Not_Used528127, // 170 0xaa Not_Used529127, // 171 0xab Not_Used530127, // 172 0xac Not_Used531127, // 173 0xad Not_Used532127, // 174 0xae Not_Used533127, // 175 0xaf Not_Used534127, // 176 0xb0 Not_Used535127, // 177 0xb1 Not_Used536127, // 178 0xb2 Not_Used537127, // 179 0xb3 Not_Used538127, // 180 0xb4 Not_Used539127, // 181 0xb5 Not_Used540127, // 182 0xb6 Not_Used541127, // 183 0xb7 Not_Used542127, // 184 0xb8 Not_Used543127, // 185 0xb9 Not_Used544127, // 186 0xba Not_Used545127, // 187 0xbb Not_Used546127, // 188 0xbc Not_Used547127, // 189 0xbd Not_Used548127, // 190 0xbe Not_Used549127, // 191 0xbf Not_Used55050, // 192 0xc0 VK_BACK_QUOTE551127, // 193 0xc1 Not_Used552127, // 194 0xc2 Not_Used553127, // 195 0xc3 Not_Used554127, // 196 0xc4 Not_Used555127, // 197 0xc5 Not_Used556127, // 198 0xc6 Not_Used557127, // 199 0xc7 Not_Used558127, // 200 0xc8 Not_Used559127, // 201 0xc9 Not_Used560127, // 202 0xca Not_Used561127, // 203 0xcb Not_Used562127, // 204 0xcc Not_Used563127, // 205 0xcd Not_Used564127, // 206 0xce Not_Used565127, // 207 0xcf Not_Used566127, // 208 0xd0 Not_Used567127, // 209 0xd1 Not_Used568127, // 210 0xd2 Not_Used569127, // 211 0xd3 Not_Used570127, // 212 0xd4 Not_Used571127, // 213 0xd5 Not_Used572127, // 214 0xd6 Not_Used573127, // 215 0xd7 Not_Used574127, // 216 0xd8 Not_Used575127, // 217 0xd9 Not_Used576127, // 218 0xda Not_Used577127, // 219 0xdb Not_Used578127, // 220 0xdc Not_Used579127, // 221 0xdd Not_Used58039 // 222 0xde VK_QUOTE581};582583// NOTE: All values above 222 don't have an equivalent on MacOSX.584static inline CGKeyCode GetCGKeyCode(jint javaKeyCode)585{586if (javaKeyCode > 222) {587return 127;588} else {589return javaToMacKeyCode[javaKeyCode];590}591}592593static int GetClickCount(BOOL isDown) {594NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate];595NSTimeInterval clickInterval = now - gsLastClickTime;596BOOL isWithinTreshold = clickInterval < [NSEvent doubleClickInterval];597598if (isDown) {599if (isWithinTreshold) {600gsClickCount++;601} else {602gsClickCount = 1;603}604605gsLastClickTime = now;606} else {607// In OS X, a mouse up has the click count of the last mouse down608// if an interval between up and down is within the double click609// threshold, and 0 otherwise.610if (!isWithinTreshold) {611gsClickCount = 0;612}613}614615return gsClickCount;616}617618619