1 /* 2 * Copyright (c) 2011, 2016, 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 "common.h" 27 #import "com_sun_glass_ui_mac_MacRobot.h" 28 29 #import <CoreServices/CoreServices.h> 30 #import <ApplicationServices/ApplicationServices.h> 31 32 #import "GlassMacros.h" 33 #import "GlassKey.h" 34 #import "GlassHelper.h" 35 36 //#define VERBOSE 37 #ifndef VERBOSE 38 #define LOG(MSG, ...) 39 #else 40 #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__); 41 #endif 42 43 #define kMouseButtonNone 0 44 45 static inline void DumpImage(CGImageRef image) 46 { 47 fprintf(stderr, "CGImageRef: %p\n", image); 48 if (image != NULL) 49 { 50 fprintf(stderr, " CGImageGetWidth(): %d\n", (int)CGImageGetWidth(image)); 51 fprintf(stderr, " CGImageGetHeight(): %d\n", (int)CGImageGetHeight(image)); 52 fprintf(stderr, " CGImageGetBitsPerComponent(): %d\n", (int)CGImageGetBitsPerComponent(image)); 53 fprintf(stderr, " CGImageGetBitsPerPixel(): %d\n", (int)CGImageGetBitsPerPixel(image)); 54 fprintf(stderr, " CGImageGetBytesPerRow(): %d\n", (int)CGImageGetBytesPerRow(image)); 55 CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image) & kCGBitmapAlphaInfoMask; 56 switch (alpha) 57 { 58 case kCGImageAlphaNone: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaNone\n"); break; 59 case kCGImageAlphaPremultipliedLast: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaPremultipliedLast\n"); break; 60 case kCGImageAlphaPremultipliedFirst: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaPremultipliedFirst\n"); break; 61 case kCGImageAlphaLast: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaLast\n"); break; 62 case kCGImageAlphaFirst: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaFirst\n"); break; 63 case kCGImageAlphaNoneSkipLast: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaNoneSkipLast\n"); break; 64 case kCGImageAlphaNoneSkipFirst: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaNoneSkipFirst\n"); break; 65 case kCGImageAlphaOnly: fprintf(stderr, " CGImageGetAlphaInfo(): kCGImageAlphaOnly\n"); break; 66 default: fprintf(stderr, " CGImageGetAlphaInfo(): unknown\n"); 67 } 68 CGBitmapInfo bitmap = CGImageGetBitmapInfo(image) & kCGBitmapByteOrderMask; 69 switch (bitmap) 70 { 71 case kCGBitmapByteOrderDefault: fprintf(stderr, " CGImageGetBitmapInfo(): kCGBitmapByteOrderDefault\n"); break; 72 case kCGBitmapByteOrder16Little: fprintf(stderr, " CGImageGetBitmapInfo(): kCGBitmapByteOrder16Little\n"); break; 73 case kCGBitmapByteOrder32Little: fprintf(stderr, " CGImageGetBitmapInfo(): kCGBitmapByteOrder32Little\n"); break; 74 case kCGBitmapByteOrder16Big: fprintf(stderr, " CGImageGetBitmapInfo(): kCGBitmapByteOrder16Big\n"); break; 75 case kCGBitmapByteOrder32Big: fprintf(stderr, " CGImageGetBitmapInfo(): kCGBitmapByteOrder32Big\n"); break; 76 default: fprintf(stderr, " CGImageGetBitmapInfo(): unknown\n"); 77 } 78 } 79 } 80 81 static inline void PostGlassMouseEvent(CGPoint location, UInt32 buttons, BOOL buttonPressed) 82 { 83 // for each one bit in buttons, post a new mouse {press/release} event 84 if (buttons != 0) { 85 for (UInt32 index = 0; buttons != 0; index++, buttons >>= 1) { 86 if (buttons & 1) { 87 CGEventType type; 88 switch (index) { 89 case 0: 90 type = buttonPressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp; 91 break; 92 case 1: 93 type = buttonPressed ? kCGEventRightMouseDown : kCGEventRightMouseUp; 94 break; 95 default: 96 type = buttonPressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp; 97 break; 98 } 99 100 CGEventRef newEvent = CGEventCreateMouseEvent(NULL, type, location, (CGMouseButton)index); 101 CGEventPost(kCGHIDEventTap, newEvent); 102 CFRelease(newEvent); 103 } 104 } 105 } 106 } 107 108 static inline void PostGlassKeyEvent(jint code, BOOL keyPressed) 109 { 110 unsigned short macCode; 111 if (GetMacKey(code, &macCode)) { 112 // Using CGEvent API proved to be problematic - events for some keys were missing sometimes. 113 // So we use the A11Y API instead - just as we do in AWT. It works fine in all cases. 114 AXUIElementRef elem = AXUIElementCreateSystemWide(); 115 AXUIElementPostKeyboardEvent(elem, (CGCharCode)0, macCode, keyPressed); 116 CFRelease(elem); 117 } 118 } 119 120 @interface GlassRobot : NSObject 121 { 122 UInt32 mouseButtons; 123 } 124 125 - (void)mouseMove:(NSPoint)p; 126 - (void)mousePress:(UInt32)buttons; 127 - (void)mouseRelease:(UInt32)buttons; 128 - (CGPoint)getMousePosFlipped; 129 @end 130 131 @implementation GlassRobot 132 133 - (id)init 134 { 135 self = [super init]; 136 if (self != nil) 137 { 138 self->mouseButtons = kMouseButtonNone; 139 } 140 return self; 141 } 142 143 144 - (void)mouseMove:(NSPoint)p 145 { 146 CGPoint location = NSPointToCGPoint(p); 147 UInt32 buttons = self->mouseButtons; 148 CGEventType type=kCGEventMouseMoved; 149 UInt32 index=0; 150 for (; buttons != 0; index++, buttons >>= 1) 151 { 152 if (buttons & 1) 153 { 154 switch (index) 155 { 156 case 0: 157 type = kCGEventLeftMouseDragged; 158 break; 159 case 1: 160 type = kCGEventRightMouseDragged; 161 break; 162 default: 163 type = kCGEventOtherMouseDragged; 164 break; 165 } 166 } 167 } 168 CGEventRef newEvent = CGEventCreateMouseEvent(NULL, type, location, (CGMouseButton)index); 169 CGEventPost(kCGHIDEventTap, newEvent); 170 CGWarpMouseCursorPosition(location); 171 CFRelease(newEvent); 172 173 } 174 175 - (CGPoint)getMousePosFlipped 176 { 177 CGPoint where = NSPointToCGPoint([NSEvent mouseLocation]); 178 NSScreen * screen = [[NSScreen screens] objectAtIndex: 0]; 179 NSRect screenFrame = screen.frame; 180 where.y = screenFrame.size.height - where.y; 181 return where; 182 } 183 184 - (void)mousePress:(UInt32)buttons 185 { 186 //Add new pressed buttons 187 self->mouseButtons = self->mouseButtons | buttons; 188 PostGlassMouseEvent([self getMousePosFlipped], buttons, YES); 189 } 190 191 - (void)mouseRelease:(UInt32)buttons 192 { 193 PostGlassMouseEvent([self getMousePosFlipped], buttons, NO); 194 //reset buttons 195 self->mouseButtons = self->mouseButtons & (~buttons); 196 } 197 198 @end 199 200 /* 201 * Class: com_sun_glass_ui_mac_MacRobot 202 * Method: _init 203 * Signature: ()J 204 */ 205 JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacRobot__1init 206 (JNIEnv *env, jobject jrobot) 207 { 208 LOG("Java_com_sun_glass_ui_mac_MacRobot__1init"); 209 210 return ptr_to_jlong([[GlassRobot alloc] init]); 211 } 212 213 /* 214 * Class: com_sun_glass_ui_mac_MacRobot 215 * Method: _destroy 216 * Signature: (J)V 217 */ 218 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1destroy 219 (JNIEnv *env, jobject jThis, jlong ptr) 220 { 221 LOG("Java_com_sun_glass_ui_mac_MacRobot__1destroy"); 222 223 [(GlassRobot*)jlong_to_ptr(ptr) release]; 224 } 225 226 /* 227 * Class: com_sun_glass_ui_mac_MacRobot 228 * Method: _keyPress 229 * Signature: (I)V 230 */ 231 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1keyPress 232 (JNIEnv *env, jobject jrobot, jint code) 233 { 234 LOG("Java_com_sun_glass_ui_mac_MacRobot__1keyPress"); 235 236 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 237 GLASS_POOL_ENTER 238 { 239 PostGlassKeyEvent(code, YES); 240 } 241 GLASS_POOL_EXIT; 242 } 243 244 /* 245 * Class: com_sun_glass_ui_mac_MacRobot 246 * Method: _keyRelease 247 * Signature: (I)V 248 */ 249 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1keyRelease 250 (JNIEnv *env, jobject jrobot, jint code) 251 { 252 LOG("Java_com_sun_glass_ui_mac_MacRobot__1keyRelease"); 253 254 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 255 GLASS_POOL_ENTER 256 { 257 PostGlassKeyEvent(code, NO); 258 } 259 GLASS_POOL_EXIT; 260 } 261 262 /* 263 * Class: com_sun_glass_ui_mac_MacRobot 264 * Method: _mouseMove 265 * Signature: (JII)V 266 */ 267 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseMove 268 (JNIEnv *env, jobject jrobot, jlong ptr, jint x, jint y) 269 { 270 LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseMove"); 271 272 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 273 GLASS_POOL_ENTER 274 { 275 GlassRobot * robot = (GlassRobot*)jlong_to_ptr(ptr); 276 [robot mouseMove:NSMakePoint((float)x, (float)y)]; 277 } 278 GLASS_POOL_EXIT; 279 } 280 281 /* 282 * Class: com_sun_glass_ui_mac_MacRobot 283 * Method: _getMouseX 284 * Signature: (J)I 285 */ 286 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getMouseX 287 (JNIEnv *env, jobject jrobot, jlong ptr) 288 { 289 LOG("Java_com_sun_glass_ui_mac_MacRobot__1getMouseX"); 290 291 jint x = 0; 292 293 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 294 GLASS_POOL_ENTER 295 { 296 GlassRobot * robot = (GlassRobot*)jlong_to_ptr(ptr); 297 x = (jint)[robot getMousePosFlipped].x; 298 } 299 GLASS_POOL_EXIT; 300 301 return x; 302 } 303 304 /* 305 * Class: com_sun_glass_ui_mac_MacRobot 306 * Method: _getMouseY 307 * Signature: (J)I 308 */ 309 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getMouseY 310 (JNIEnv *env, jobject jrobot, jlong ptr) 311 { 312 LOG("Java_com_sun_glass_ui_mac_MacRobot__1getMouseY"); 313 314 jint y = 0; 315 316 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 317 GLASS_POOL_ENTER 318 { 319 GlassRobot * robot = (GlassRobot*)jlong_to_ptr(ptr); 320 y = (jint)[robot getMousePosFlipped].y; 321 } 322 GLASS_POOL_EXIT; 323 324 return y; 325 } 326 327 /* 328 * Class: com_sun_glass_ui_mac_MacRobot 329 * Method: _mousePress 330 * Signature: (JI)V 331 */ 332 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mousePress 333 (JNIEnv *env, jobject jrobot, jlong ptr, jint buttons) 334 { 335 LOG("Java_com_sun_glass_ui_mac_MacRobot__1mousePress"); 336 337 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 338 GLASS_POOL_ENTER 339 { 340 GlassRobot * robot = (GlassRobot*)jlong_to_ptr(ptr); 341 [robot mousePress:(UInt32)buttons]; 342 } 343 GLASS_POOL_EXIT; 344 } 345 346 /* 347 * Class: com_sun_glass_ui_mac_MacRobot 348 * Method: _mouseRelease 349 * Signature: (JI)V 350 */ 351 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseRelease 352 (JNIEnv *env, jobject jrobot, jlong ptr, jint buttons) 353 { 354 LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseRelease"); 355 356 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 357 GLASS_POOL_ENTER 358 { 359 GlassRobot * robot = (GlassRobot*)jlong_to_ptr(ptr); 360 [robot mouseRelease:(UInt32)buttons]; 361 } 362 GLASS_POOL_EXIT; 363 } 364 365 /* 366 * Class: com_sun_glass_ui_mac_MacRobot 367 * Method: _mouseWheel 368 * Signature: (I)V 369 */ 370 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseWheel 371 (JNIEnv *env, jobject jrobot, jint wheelAmt) 372 { 373 LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseWheel"); 374 375 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 376 GLASS_POOL_ENTER 377 { 378 CGEventRef newEvent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, (int32_t)wheelAmt); 379 CGEventPost(kCGHIDEventTap, newEvent); 380 CFRelease(newEvent); 381 } 382 GLASS_POOL_EXIT; 383 } 384 385 /* 386 * Class: com_sun_glass_ui_mac_MacRobot 387 * Method: _getPixelColor 388 * Signature: (II)I 389 */ 390 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getPixelColor 391 (JNIEnv *env, jobject jrobot, jint x, jint y) 392 { 393 LOG("Java_com_sun_glass_ui_mac_MacRobot__1getPixelColor"); 394 395 jint color = 0; 396 397 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 398 GLASS_POOL_ENTER 399 { 400 CGRect bounds = CGRectMake((CGFloat)x, (CGFloat)y, 1.0f, 1.0f); 401 CGImageRef screenImage = CGWindowListCreateImage(bounds, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault); 402 if (screenImage != NULL) 403 { 404 //DumpImage(screenImage); 405 CGDataProviderRef provider = CGImageGetDataProvider(screenImage); 406 if (provider != NULL) 407 { 408 CFDataRef data = CGDataProviderCopyData(provider); 409 if (data != NULL) 410 { 411 jint *pixels = (jint*)CFDataGetBytePtr(data); 412 if (pixels != NULL) 413 { 414 color = *pixels; 415 } 416 } 417 CFRelease(data); 418 } 419 CGImageRelease(screenImage); 420 } 421 } 422 GLASS_POOL_EXIT; 423 424 return color; 425 } 426 427 /* 428 * Class: com_sun_glass_ui_mac_MacRobot 429 * Method: _getScreenCapture 430 * Signature: (IIIIZ)Lcom/sun/glass/ui/Pixels; 431 */ 432 JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getScreenCapture 433 (JNIEnv *env, jobject jrobot, jint x, jint y, jint width, jint height, jboolean isHiDPI) 434 { 435 LOG("Java_com_sun_glass_ui_mac_MacRobot__1getScreenCapture"); 436 437 jobject pixels = NULL; 438 439 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 440 GLASS_POOL_ENTER 441 { 442 CGRect bounds = CGRectMake((CGFloat)x, (CGFloat)y, (CGFloat)width, (CGFloat)height); 443 CGImageRef screenImage = CGWindowListCreateImage(bounds, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault); 444 if (screenImage != NULL) 445 { 446 jint pixWidth, pixHeight; 447 448 if (isHiDPI) { 449 pixWidth = (jint)CGImageGetWidth(screenImage); 450 pixHeight = (jint)CGImageGetHeight(screenImage); 451 } else { 452 pixWidth = width; 453 pixHeight = height; 454 } 455 456 jintArray pixelArray = (*env)->NewIntArray(env, (jsize)pixWidth*pixHeight); 457 if (pixelArray) 458 { 459 jint *javaPixels = (jint*)(*env)->GetIntArrayElements(env, pixelArray, 0); 460 if (javaPixels != NULL) 461 { 462 // create a graphics context around the Java int array 463 CGColorSpaceRef picColorSpace = CGColorSpaceCreateWithName( 464 kCGColorSpaceGenericRGB); 465 CGContextRef jPicContextRef = CGBitmapContextCreate( 466 javaPixels, 467 pixWidth, pixHeight, 468 8, pixWidth * sizeof(jint), 469 picColorSpace, 470 kCGBitmapByteOrder32Host | 471 kCGImageAlphaPremultipliedFirst); 472 473 CGColorSpaceRelease(picColorSpace); 474 475 // flip, scale, and color correct the screen image into the Java pixels 476 CGRect zeroBounds = { { 0, 0 }, { pixWidth, pixHeight } }; 477 CGContextDrawImage(jPicContextRef, zeroBounds, screenImage); 478 CGContextFlush(jPicContextRef); 479 480 // cleanup 481 CGContextRelease(jPicContextRef); 482 (*env)->ReleaseIntArrayElements(env, pixelArray, javaPixels, 0); 483 484 jclass applicationClass = 485 [GlassHelper ClassForName:"com.sun.glass.ui.Application" withEnv:env]; 486 if (!applicationClass) return NULL; 487 488 jfloat scale = (*env)->CallStaticFloatMethod(env, 489 applicationClass, 490 javaIDs.Application.getScaleFactor, x, y, width, height); 491 if ((*env)->ExceptionCheck(env)) return NULL; 492 493 // create Pixels 494 pixels = (*env)->CallStaticObjectMethod(env, applicationClass, 495 javaIDs.Application.createPixels, 496 pixWidth, pixHeight, 497 pixelArray, scale, scale); 498 if ((*env)->ExceptionCheck(env)) return NULL; 499 } 500 } 501 502 CGImageRelease(screenImage); 503 } 504 } 505 GLASS_POOL_EXIT; 506 507 return pixels; 508 }