All Downloads are FREE. Search and download functionalities are using the official Maven repository.

native-glass.mac.GlassRobot.m Maven / Gradle / Ivy

/*
 * Copyright (c) 2011, 2013, 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 "common.h"
#import "com_sun_glass_ui_mac_MacRobot.h"

#import 
#import 

#import "GlassMacros.h"
#import "GlassKey.h"
#import "GlassHelper.h"

//#define VERBOSE
#ifndef VERBOSE
    #define LOG(MSG, ...)
#else
    #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
#endif

#define kMouseButtonNone 0

static inline void DumpImage(CGImageRef image)
{
    fprintf(stderr, "CGImageRef: %p\n", image);
    if (image != NULL)
    {
        fprintf(stderr, "    CGImageGetWidth(): %d\n", (int)CGImageGetWidth(image));
        fprintf(stderr, "    CGImageGetHeight(): %d\n", (int)CGImageGetHeight(image));
        fprintf(stderr, "    CGImageGetBitsPerComponent(): %d\n", (int)CGImageGetBitsPerComponent(image));
        fprintf(stderr, "    CGImageGetBitsPerPixel(): %d\n", (int)CGImageGetBitsPerPixel(image));
        fprintf(stderr, "    CGImageGetBytesPerRow(): %d\n", (int)CGImageGetBytesPerRow(image));
        CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image) & kCGBitmapAlphaInfoMask;
        switch (alpha)
        {
            case kCGImageAlphaNone: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaNone\n"); break;
            case kCGImageAlphaPremultipliedLast: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaPremultipliedLast\n"); break;
            case kCGImageAlphaPremultipliedFirst: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaPremultipliedFirst\n"); break;
            case kCGImageAlphaLast: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaLast\n"); break;
            case kCGImageAlphaFirst: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaFirst\n"); break;
            case kCGImageAlphaNoneSkipLast: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaNoneSkipLast\n"); break;
            case kCGImageAlphaNoneSkipFirst: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaNoneSkipFirst\n"); break;
            case kCGImageAlphaOnly: fprintf(stderr, "    CGImageGetAlphaInfo(): kCGImageAlphaOnly\n"); break;
            default: fprintf(stderr, "    CGImageGetAlphaInfo(): unknown\n");
        }
        CGBitmapInfo bitmap = CGImageGetBitmapInfo(image) & kCGBitmapByteOrderMask;
        switch (bitmap)
        {
            case kCGBitmapByteOrderDefault: fprintf(stderr, "    CGImageGetBitmapInfo(): kCGBitmapByteOrderDefault\n"); break;
            case kCGBitmapByteOrder16Little: fprintf(stderr, "    CGImageGetBitmapInfo(): kCGBitmapByteOrder16Little\n"); break;
            case kCGBitmapByteOrder32Little: fprintf(stderr, "    CGImageGetBitmapInfo(): kCGBitmapByteOrder32Little\n"); break;
            case kCGBitmapByteOrder16Big: fprintf(stderr, "    CGImageGetBitmapInfo(): kCGBitmapByteOrder16Big\n"); break;
            case kCGBitmapByteOrder32Big: fprintf(stderr, "    CGImageGetBitmapInfo(): kCGBitmapByteOrder32Big\n"); break;
            default: fprintf(stderr, "    CGImageGetBitmapInfo(): unknown\n");
        }
    }
}

static inline void PostGlassMouseEvent(CGPoint location, UInt32 buttons, BOOL buttonPressed)
{
    // for each one bit in buttons, post a new mouse {press/release} event
    if (buttons != 0) {
        for (UInt32 index = 0; buttons != 0; index++, buttons >>= 1) {
            if (buttons & 1) {
                CGEventType type;
                switch (index) {
                    case 0:
                        type = buttonPressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp;
                        break;
                    case 1:
                        type = buttonPressed ? kCGEventRightMouseDown : kCGEventRightMouseUp;
                        break;
                    default:
                        type = buttonPressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp;
                        break;
                }
                
                CGEventRef newEvent = CGEventCreateMouseEvent(NULL, type, location, (CGMouseButton)index);
                CGEventPost(kCGHIDEventTap, newEvent);
                CFRelease(newEvent);
            }
        }
    }
}

static inline void PostGlassKeyEvent(jint code, BOOL keyPressed)
{
    unsigned short macCode;
    if (GetMacKey(code, &macCode)) {
        // Using CGEvent API proved to be problematic - events for some keys were missing sometimes.
        // So we use the A11Y API instead - just as we do in AWT. It works fine in all cases.
        AXUIElementRef elem = AXUIElementCreateSystemWide();
        AXUIElementPostKeyboardEvent(elem, (CGCharCode)0, macCode, keyPressed);
        CFRelease(elem);
    }
}

@interface GlassRobot : NSObject
{
    UInt32 mouseButtons;
}

- (void)mouseMove:(NSValue*)p;
- (void)mousePress:(NSNumber*)buttons;
- (void)mouseRelease:(NSNumber*)buttons;
- (void)getMousePos:(NSMutableArray*)args;
@end

@implementation GlassRobot

- (id)init
{
    self = [super init];
    if (self != nil)
    {
        self->mouseButtons = kMouseButtonNone;
    }
    return self;
}


- (void)mouseMove:(NSValue*)p
{
    CGPoint location = NSPointToCGPoint([p pointValue]);
    UInt32 buttons = self->mouseButtons;
    CGEventType type=kCGEventMouseMoved;
    UInt32 index=0;
    for (; buttons != 0; index++, buttons >>= 1)
    {
        if (buttons & 1)
        {        
            switch (index)
            {
                case 0:
                    type = kCGEventLeftMouseDragged;
                    break;
                case 1:
                    type = kCGEventRightMouseDragged;
                    break;
                default:
                    type = kCGEventOtherMouseDragged;
                    break;
            }
        }
    }
    CGEventRef newEvent = CGEventCreateMouseEvent(NULL, type, location, (CGMouseButton)index);
    CGEventPost(kCGHIDEventTap, newEvent);
    CGWarpMouseCursorPosition(location);
    CFRelease(newEvent);

}

- (CGPoint)getMousePosFlipped
{
    CGPoint where = NSPointToCGPoint([NSEvent mouseLocation]);
    NSScreen * screen = [[NSScreen screens] objectAtIndex: 0];
    NSRect screenFrame = screen.frame;
    where.y = screenFrame.size.height - where.y;
    return where;
}

- (void)mousePress:(NSNumber*)buttons
{
    UInt32 newPressed = (UInt32)[buttons unsignedLongValue];
    //Add new pressed buttons
    self->mouseButtons = self->mouseButtons | newPressed;
    PostGlassMouseEvent([self getMousePosFlipped], newPressed, YES);
}

- (void)mouseRelease:(NSNumber*)buttons
{
    UInt32 newReleased = (UInt32)[buttons unsignedLongValue];
    PostGlassMouseEvent([self getMousePosFlipped], newReleased, NO);
    //reset buttons
    self->mouseButtons = self->mouseButtons & (~newReleased);
}

- (void)getMousePos:(NSMutableArray*)args
{
    CGPoint where = [self getMousePosFlipped];
    [args addObject: [NSValue valueWithPoint: *(NSPoint*)&where]];
}

@end

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _init
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacRobot__1init
(JNIEnv *env, jobject jrobot)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1init");
    
    return ptr_to_jlong([[GlassRobot alloc] init]);
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _destroy
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1destroy
(JNIEnv *env, jobject jThis, jlong ptr)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1destroy");

    [(GlassRobot*)jlong_to_ptr(ptr) release];
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _keyPress
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1keyPress
(JNIEnv *env, jobject jrobot, jint code)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1keyPress");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        PostGlassKeyEvent(code, YES);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _keyRelease
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1keyRelease
(JNIEnv *env, jobject jrobot, jint code)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1keyRelease");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        PostGlassKeyEvent(code, NO);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _mouseMove
 * Signature: (JII)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseMove
(JNIEnv *env, jobject jrobot, jlong ptr, jint x, jint y)
{
        LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseMove");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        GLASS_PERFORM_WITH_ARG((GlassRobot*)jlong_to_ptr(ptr),
                mouseMove, [NSValue valueWithPoint: NSMakePoint((float)x, (float)y)], NO);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _getMouseX
 * Signature: (J)I
 */
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getMouseX
(JNIEnv *env, jobject jrobot, jlong ptr)
{
        LOG("Java_com_sun_glass_ui_mac_MacRobot__1getMouseX");
    
    jint x = 0;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        NSMutableArray * args = [NSMutableArray arrayWithCapacity: 0];

        GLASS_PERFORM_WITH_ARG((GlassRobot*)jlong_to_ptr(ptr),
                getMousePos, args, YES);

        x = (jint)[(NSValue*)[args lastObject] pointValue].x;
    }
    GLASS_POOL_EXIT;
    
    return x;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _getMouseY
 * Signature: (J)I
 */
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getMouseY
(JNIEnv *env, jobject jrobot, jlong ptr)
{
        LOG("Java_com_sun_glass_ui_mac_MacRobot__1getMouseY");
    
    jint y = 0;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        NSMutableArray * args = [NSMutableArray arrayWithCapacity: 0];

        GLASS_PERFORM_WITH_ARG((GlassRobot*)jlong_to_ptr(ptr),
                getMousePos, args, YES);

        y = (jint)[(NSValue*)[args lastObject] pointValue].y;
    }
    GLASS_POOL_EXIT;
    
    return y;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _mousePress
 * Signature: (JI)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mousePress
(JNIEnv *env, jobject jrobot, jlong ptr, jint buttons)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1mousePress");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        GLASS_PERFORM_WITH_ARG((GlassRobot*)jlong_to_ptr(ptr),
                mousePress, [NSNumber numberWithInt: buttons], NO);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _mouseRelease
 * Signature: (JI)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseRelease
(JNIEnv *env, jobject jrobot, jlong ptr, jint buttons)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseRelease");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        GLASS_PERFORM_WITH_ARG((GlassRobot*)jlong_to_ptr(ptr),
                mouseRelease, [NSNumber numberWithInt: buttons], NO);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _mouseWheel
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacRobot__1mouseWheel
(JNIEnv *env, jobject jrobot, jint wheelAmt)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1mouseWheel");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        CGEventRef newEvent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, (int32_t)wheelAmt);
        CGEventPost(kCGHIDEventTap, newEvent);
        CFRelease(newEvent);
    }
    GLASS_POOL_EXIT;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _getPixelColor
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getPixelColor
(JNIEnv *env, jobject jrobot, jint x, jint y)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1getPixelColor");
    
    jint color = 0;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        CGRect bounds = CGRectMake((CGFloat)x, (CGFloat)y, 1.0f, 1.0f);
        CGImageRef screenImage = CGWindowListCreateImage(bounds, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);
        if (screenImage != NULL)
        {
            //DumpImage(screenImage);
            CGDataProviderRef provider = CGImageGetDataProvider(screenImage);
            if (provider != NULL)
            {
                CFDataRef data = CGDataProviderCopyData(provider);
                if (data != NULL)
                {
                    jint *pixels = (jint*)CFDataGetBytePtr(data);
                    if (pixels != NULL)
                    {
                        color = *pixels;
                    }
                }
                CFRelease(data);
            }
            CGImageRelease(screenImage);
        }
    }
    GLASS_POOL_EXIT;
    
    return color;
}

/*
 * Class:     com_sun_glass_ui_mac_MacRobot
 * Method:    _getScreenCapture
 * Signature: (IIIIZ)Lcom/sun/glass/ui/Pixels;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacRobot__1getScreenCapture
(JNIEnv *env, jobject jrobot, jint x, jint y, jint width, jint height, jboolean isHiDPI)
{
    LOG("Java_com_sun_glass_ui_mac_MacRobot__1getScreenCapture");
    
    jobject pixels = NULL;

    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER
    {
        CGRect bounds = CGRectMake((CGFloat)x, (CGFloat)y, (CGFloat)width, (CGFloat)height);
        CGImageRef screenImage = CGWindowListCreateImage(bounds, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);
        if (screenImage != NULL)
        {
            jint pixWidth, pixHeight;

            if (isHiDPI) {
                pixWidth = (jint)CGImageGetWidth(screenImage);
                pixHeight = (jint)CGImageGetHeight(screenImage);
            } else {
                pixWidth = width;
                pixHeight = height;
            }

            jintArray pixelArray = (*env)->NewIntArray(env, (jsize)pixWidth*pixHeight);
            if (pixelArray)
            {
                jint *javaPixels = (jint*)(*env)->GetIntArrayElements(env, pixelArray, 0);
                if (javaPixels != NULL)
                {
                    // create a graphics context around the Java int array
                    CGColorSpaceRef picColorSpace = CGColorSpaceCreateWithName(
                            kCGColorSpaceGenericRGB);
                    CGContextRef jPicContextRef = CGBitmapContextCreate(
                            javaPixels,
                            pixWidth, pixHeight,
                            8, pixWidth * sizeof(jint),
                            picColorSpace,
                            kCGBitmapByteOrder32Host |
                            kCGImageAlphaPremultipliedFirst);

                    CGColorSpaceRelease(picColorSpace);

                    // flip, scale, and color correct the screen image into the Java pixels
                    CGRect zeroBounds = { { 0, 0 }, { pixWidth, pixHeight } };
                    CGContextDrawImage(jPicContextRef, zeroBounds, screenImage);
                    CGContextFlush(jPicContextRef);

                    // cleanup
                    CGContextRelease(jPicContextRef);
                    (*env)->ReleaseIntArrayElements(env, pixelArray, javaPixels, 0);

                    jfloat scale = (*env)->CallStaticFloatMethod(env,
                            [GlassHelper ClassForName:"com.sun.glass.ui.Application" withEnv:env],
                            javaIDs.Application.getScaleFactor, x, y, width, height);

                    // create Pixels
                    pixels = (*env)->CallStaticObjectMethod(env,
                            [GlassHelper ClassForName:"com.sun.glass.ui.Application" withEnv:env],
                            javaIDs.Application.createPixels, pixWidth, pixHeight, pixelArray, scale);
                }
            }

            CGImageRelease(screenImage);
        }
    }
    GLASS_POOL_EXIT;

    return pixels;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy