1 /*
   2  * Copyright (c) 2010, 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 package com.sun.glass.ui;
  26 
  27 import static com.sun.javafx.FXPermissions.CREATE_ROBOT_PERMISSION;
  28 import java.nio.IntBuffer;
  29 
  30 public abstract class Robot {
  31 
  32     final static public int MOUSE_LEFT_BTN   = 1;
  33     final static public int MOUSE_RIGHT_BTN  = 2;
  34     final static public int MOUSE_MIDDLE_BTN = 4;
  35 
  36     protected abstract void _create();
  37     protected Robot() {
  38         // Ensure proper permission
  39         final SecurityManager sm = System.getSecurityManager();
  40         if (sm != null) {
  41             sm.checkPermission(CREATE_ROBOT_PERMISSION);
  42         }
  43         Application.checkEventThread();
  44         _create();
  45     }
  46 
  47     protected abstract void _destroy();
  48     public void destroy() {
  49         Application.checkEventThread();
  50         _destroy();
  51     }
  52 
  53     protected abstract void _keyPress(int code);
  54     /**
  55      * Generate a key pressed event.
  56      * @param code key code for this event
  57      */
  58     public void keyPress(int code) {
  59         Application.checkEventThread();
  60         _keyPress(code);
  61     }
  62 
  63     protected abstract void _keyRelease(int code);
  64     /**
  65      * Generate a key released event.
  66      *
  67      * @param code key code for this event
  68      */
  69     public void keyRelease(int code) {
  70         Application.checkEventThread();
  71         _keyRelease(code);
  72     }
  73 
  74     protected abstract void _mouseMove(int x, int y);
  75     /**
  76      * Generate a mouse moved event.
  77      *
  78      * @param x screen coordinate x
  79      * @param y screen coordinate y
  80      */
  81     public void mouseMove(int x, int y) {
  82         Application.checkEventThread();
  83         _mouseMove(x, y);
  84     }
  85 
  86     protected abstract void _mousePress(int buttons);
  87     /**
  88      * Generate a mouse press event with specified buttons mask.
  89      *
  90      * Up to 32-buttons mice are supported. Other buttons are inaccessible
  91      * by the robot. Bits 0, 1, and 2 mean LEFT, RIGHT, and MIDDLE mouse buttons
  92      * respectively.
  93      *
  94      * @param buttons buttons to have generated the event
  95      */
  96     public void mousePress(int buttons) {
  97         Application.checkEventThread();
  98         _mousePress(buttons);
  99     }
 100 
 101     protected abstract void _mouseRelease(int buttons);
 102     /**
 103      * Generate a mouse release event with specified buttons mask.
 104      *
 105      * @param buttons buttons to have generated the event
 106      */
 107     public void mouseRelease(int buttons) {
 108         Application.checkEventThread();
 109         _mouseRelease(buttons);
 110     }
 111 
 112     protected abstract void _mouseWheel(int wheelAmt);
 113     /**
 114      * Generate a mouse wheel event.
 115      *
 116      * @param wheelAmt amount the wheel has turned of wheel turning
 117      */
 118     public void mouseWheel(int wheelAmt) {
 119         Application.checkEventThread();
 120         _mouseWheel(wheelAmt);
 121     }
 122 
 123     protected abstract int _getMouseX();
 124     public int getMouseX() {
 125         Application.checkEventThread();
 126         return _getMouseX();
 127     }
 128 
 129     protected abstract int _getMouseY();
 130     public int getMouseY() {
 131         Application.checkEventThread();
 132         return _getMouseY();
 133     }
 134 
 135     protected abstract int _getPixelColor(int x, int y);
 136     /**
 137      * Returns pixel color at specified screen coordinates in IntARGB format.
 138      */
 139     public int getPixelColor(int x, int y) {
 140         Application.checkEventThread();
 141         return _getPixelColor(x, y);
 142     }
 143 
 144     // Subclasses must override and implement at least one of the following two
 145     // _getScreenCapture methods
 146 
 147     protected void _getScreenCapture(int x, int y, int width, int height, int[] data) {
 148         throw new UnsupportedOperationException("Not implementated in the base class");
 149     }
 150 
 151     protected Pixels _getScreenCapture(int x, int y, int width, int height, boolean isHiDPI) {
 152         Screen mainScreen = Screen.getMainScreen();
 153         float uiScaleX = mainScreen.getPlatformScaleX();
 154         float uiScaleY = mainScreen.getPlatformScaleY();
 155         int data[];
 156         int dw, dh;
 157         if (uiScaleX == 1.0f && uiScaleY == 1.0f) {
 158             data = new int[width * height];
 159             _getScreenCapture(x, y, width, height, data);
 160             dw = width;
 161             dh = height;
 162         } else {
 163             int pminx = (int) Math.floor(x * uiScaleX);
 164             int pminy = (int) Math.floor(y * uiScaleY);
 165             int pmaxx = (int) Math.ceil((x + width) * uiScaleX);
 166             int pmaxy = (int) Math.ceil((y + height) * uiScaleY);
 167             int pwidth = pmaxx - pminx;
 168             int pheight = pmaxy - pminy;
 169             int tmpdata[] = new int[pwidth * pheight];
 170             _getScreenCapture(pminx, pminy, pwidth, pheight, tmpdata);
 171             if (isHiDPI) {
 172                 data = tmpdata;
 173                 dw = pwidth;
 174                 dh = pheight;
 175             } else {
 176                 data = new int[width * height];
 177                 int index = 0;
 178                 for (int iy = 0; iy < height; iy++) {
 179                     float rely = ((y + iy + 0.5f) * uiScaleY) - (pminy + 0.5f);
 180                     int irely = (int) Math.floor(rely);
 181                     int fracty = (int) ((rely - irely) * 256);
 182                     for (int ix = 0; ix < width; ix++) {
 183                         float relx = ((x + ix + 0.5f) * uiScaleX) - (pminx + 0.5f);
 184                         int irelx = (int) Math.floor(relx);
 185                         int fractx = (int) ((relx - irelx) * 256);
 186                         data[index++] =
 187                             interp(tmpdata, irelx, irely, pwidth, pheight, fractx, fracty);
 188                     }
 189                 }
 190                 dw = width;
 191                 dh = height;
 192             }
 193         }
 194         return Application.GetApplication().createPixels(dw, dh, IntBuffer.wrap(data));
 195     }
 196 
 197     /**
 198      * Returns a capture of the specified rectangular area of the screen.
 199      *
 200      * If {@code isHiDPI} argument is {@code true}, the returned Pixels object
 201      * dimensions may differ from the requested {@code width} and {@code
 202      * height} depending on how many physical pixels the area occupies on the
 203      * screen.  E.g. in HiDPI mode on the Mac (aka Retina display) the pixels
 204      * are doubled, and thus a screen capture of an area of size (10x10) pixels
 205      * will result in a Pixels object with dimensions (20x20). Calling code
 206      * should use the returned objects's getWidth() and getHeight() methods
 207      * to determine the image size.
 208      *
 209      * If (@code isHiDPI) is {@code false}, the returned Pixels object is of
 210      * the requested size. Note that in this case the image may be scaled in
 211      * order to fit to the requested dimensions if running on a HiDPI display.
 212      */
 213     public Pixels getScreenCapture(int x, int y, int width, int height, boolean isHiDPI) {
 214         Application.checkEventThread();
 215         return _getScreenCapture(x, y, width, height, isHiDPI);
 216     }
 217 
 218     /**
 219      * Returns a capture of the specified area of the screen.
 220      * It is equivalent to calling getScreenCapture(x, y, width, height, false),
 221      * i.e. this method takes a "LowDPI" screen shot.
 222      */
 223     public Pixels getScreenCapture(int x, int y, int width, int height) {
 224         return getScreenCapture(x, y, width, height, false);
 225     }
 226 
 227     private static int interp(int pixels[], int x, int y, int w, int h, int fractx1, int fracty1) {
 228         int fractx0 = 256 - fractx1;
 229         int fracty0 = 256 - fracty1;
 230         int i = y * w + x;
 231         int rgb00 = (x < 0 || y < 0 || x >= w || y >= h) ? 0 : pixels[i];
 232         if (fracty1 == 0) {
 233             // No interplation with pixels[y+1]
 234             if (fractx1 == 0) {
 235                 // No interpolation with any neighbors
 236                 return rgb00;
 237             }
 238             int rgb10 = (y < 0 || x+1 >= w || y >= h) ? 0 : pixels[i+1];
 239             return interp(rgb00, rgb10, fractx0, fractx1);
 240         } else if (fractx1 == 0) {
 241             // No interpolation with pixels[x+1]
 242             int rgb01 = (x < 0 || x >= w || y+1 >= h) ? 0 : pixels[i+w];
 243             return interp(rgb00, rgb01, fracty0, fracty1);
 244         } else {
 245             // All 4 neighbors must be interpolated
 246             int rgb10 = (y < 0 || x+1 >= w || y >= h) ? 0 : pixels[i+1];
 247             int rgb01 = (x < 0 || x >= w || y+1 >= h) ? 0 : pixels[i+w];
 248             int rgb11 = (x+1 >= w || y+1 >= h) ? 0 : pixels[i+w+1];
 249             return interp(interp(rgb00, rgb10, fractx0, fractx1),
 250                           interp(rgb01, rgb11, fractx0, fractx1),
 251                           fracty0, fracty1);
 252         }
 253     }
 254 
 255     private static int interp(int rgb0, int rgb1, int fract0, int fract1) {
 256         int a0 = (rgb0 >> 24) & 0xff;
 257         int r0 = (rgb0 >> 16) & 0xff;
 258         int g0 = (rgb0 >>  8) & 0xff;
 259         int b0 = (rgb0      ) & 0xff;
 260         int a1 = (rgb1 >> 24) & 0xff;
 261         int r1 = (rgb1 >> 16) & 0xff;
 262         int g1 = (rgb1 >>  8) & 0xff;
 263         int b1 = (rgb1      ) & 0xff;
 264         int a = (a0 * fract0 + a1 * fract1) >> 8;
 265         int r = (r0 * fract0 + r1 * fract1) >> 8;
 266         int g = (g0 * fract0 + g1 * fract1) >> 8;
 267         int b = (b0 * fract0 + b1 * fract1) >> 8;
 268         return (a << 24) | (r << 16) | (g << 8) | b;
 269     }
 270 }