1 /*
   2  * Copyright (c) 2011, 2018, 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 #include "common.h"
  27 #include "math.h"
  28 
  29 #include "KeyTable.h"
  30 
  31 #include "com_sun_glass_ui_Robot.h"
  32 #include "com_sun_glass_ui_win_WinRobot.h"
  33 #include "GlassScreen.h"
  34 
  35 
  36 static BOOL KeyEvent(JNIEnv *env, int code, bool isPress) {
  37     UINT vkey, modifiers;
  38 
  39     JavaKeyToWindowsKey(code, vkey, modifiers);
  40 
  41     if (!vkey) {
  42         return FALSE;
  43     } else {
  44         UINT scancode = ::MapVirtualKey(vkey, 0);
  45 
  46         INPUT keyInput = {0};
  47         keyInput.type = INPUT_KEYBOARD;
  48         keyInput.ki.wVk = vkey;
  49         keyInput.ki.wScan = scancode;
  50         keyInput.ki.time = 0;
  51         keyInput.ki.dwExtraInfo = 0;
  52         keyInput.ki.dwFlags = isPress ?  0 : KEYEVENTF_KEYUP;
  53         if (IsExtendedKey(vkey)) {
  54             keyInput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
  55         }
  56 
  57         ::SendInput(1, &keyInput, sizeof(keyInput));
  58 
  59         return TRUE;
  60     }
  61 }
  62 
  63 inline static jint WinToJavaPixel(USHORT r, USHORT g, USHORT b)
  64 {
  65     jint value =
  66             0xFF << 24 | // alpha channel is always turned all the way up
  67             r << 16 |
  68             g << 8  |
  69             b << 0;
  70     return value;
  71 }
  72 
  73 extern "C" {
  74 
  75 /*
  76  * Class:     com_sun_glass_ui_win_WinRobot
  77  * Method:    _keyPress
  78  * Signature: (I)V
  79  */
  80 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1keyPress
  81     (JNIEnv *env, jobject jrobot, jint code)
  82 {
  83     KeyEvent(env, code, true);
  84 }
  85 
  86 /*
  87  * Class:     com_sun_glass_ui_win_WinRobot
  88  * Method:    _keyRelease
  89  * Signature: (I)V
  90  */
  91 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1keyRelease
  92     (JNIEnv *env, jobject jrobot, jint code)
  93 {
  94     KeyEvent(env, code, false);
  95 }
  96 
  97 static int signum(int i) {
  98     // special version of signum which returns 1 when value is 0
  99     return i >= 0 ? 1 : -1;
 100     }
 101 
 102 /*
 103  * Class:     com_sun_glass_ui_win_WinRobot
 104  * Method:    _mouseMove
 105  * Signature: (II)V
 106  */
 107 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseMove
 108     (JNIEnv *env, jobject jrobot, jint x, jint y)
 109 {
 110     jfloat fx = (jfloat) x + 0.5f;
 111     jfloat fy = (jfloat) y + 0.5f;
 112     GlassScreen::FX2Win(&fx, &fy);
 113     INPUT mouseInput = {0};
 114     mouseInput.type = INPUT_MOUSE;
 115     mouseInput.mi.time = 0;
 116     mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
 117     mouseInput.mi.dx = (jint)(fx * 65535.0 / ::GetSystemMetrics(SM_CXSCREEN)) + signum((int)fx);
 118     mouseInput.mi.dy = (jint)(fy * 65535.0 / ::GetSystemMetrics(SM_CYSCREEN)) + signum((int)fy);
 119     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 120 }
 121 
 122 /*
 123  * Class:     com_sun_glass_ui_win_WinRobot
 124  * Method:    _getMouseX
 125  * Signature: ()I
 126  */
 127 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getMouseX
 128     (JNIEnv *env, jobject jrobot)
 129 {
 130     POINT curPos;
 131     ::GetCursorPos(&curPos);
 132     jfloat fx = (jfloat) curPos.x + 0.5f;
 133     jfloat fy = (jfloat) curPos.y + 0.5f;
 134     GlassScreen::Win2FX(&fx, &fy);
 135     return (jint) fx;
 136 }
 137 
 138 /*
 139  * Class:     com_sun_glass_ui_win_WinRobot
 140  * Method:    _getMouseY
 141  * Signature: ()I
 142  */
 143 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getMouseY
 144     (JNIEnv *env, jobject jrobot)
 145 {
 146     POINT curPos;
 147     ::GetCursorPos(&curPos);
 148     jfloat fx = (jfloat) curPos.x + 0.5f;
 149     jfloat fy = (jfloat) curPos.y + 0.5f;
 150     GlassScreen::Win2FX(&fx, &fy);
 151     return (jint) fy;
 152 }
 153 
 154 /*
 155  * Class:     com_sun_glass_ui_win_WinRobot
 156  * Method:    _mousePress
 157  * Signature: (I)V
 158  */
 159 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mousePress
 160     (JNIEnv *env, jobject jrobot, jint buttons)
 161 {
 162     DWORD dwFlags = 0L;
 163     // According to MSDN: Software Driving Software
 164     // application should consider SM_SWAPBUTTON to correctly emulate user with
 165     // left handed mouse setup
 166     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 167 
 168     if (buttons & (1 << 0)) {
 169         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
 170     }
 171     if (buttons & (1 << 1)) {
 172         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN;
 173     }
 174     if (buttons & (1 << 2)) {
 175         dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
 176     }
 177 
 178     INPUT mouseInput = {0};
 179     mouseInput.type = INPUT_MOUSE;
 180     mouseInput.mi.time = 0;
 181     mouseInput.mi.dwFlags = dwFlags;
 182 
 183     // Support for extra buttons
 184     if (buttons & (1 << 3)) {
 185         mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN;
 186         mouseInput.mi.mouseData = XBUTTON1;
 187     }
 188     if (buttons & (1 << 4)) {
 189         mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN;
 190         mouseInput.mi.mouseData = XBUTTON2;
 191     }
 192 
 193     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 194 }
 195 
 196 /*
 197  * Class:     com_sun_glass_ui_win_WinRobot
 198  * Method:    _mouseRelease
 199  * Signature: (I)V
 200  */
 201 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseRelease
 202     (JNIEnv *env, jobject jrobot, jint buttons)
 203 {
 204     DWORD dwFlags = 0L;
 205     // According to MSDN: Software Driving Software
 206     // application should consider SM_SWAPBUTTON to correctly emulate user with
 207     // left handed mouse setup
 208     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 209 
 210     if (buttons & (1 << 0)) {
 211         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
 212     }
 213     if (buttons & (1 << 1)) {
 214         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP;
 215     }
 216     if (buttons & (1 << 2)) {
 217         dwFlags |= MOUSEEVENTF_MIDDLEUP;
 218     }
 219 
 220     INPUT mouseInput = {0};
 221     mouseInput.type = INPUT_MOUSE;
 222     mouseInput.mi.time = 0;
 223     mouseInput.mi.dwFlags = dwFlags;
 224 
 225     // Support for extra buttons
 226     if (buttons & (1 << 3)) {
 227         mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP;
 228         mouseInput.mi.mouseData = XBUTTON1;
 229     }
 230     if (buttons & (1 << 4)) {
 231         mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP;
 232         mouseInput.mi.mouseData = XBUTTON2;
 233     }
 234 
 235     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 236 }
 237 
 238 /*
 239  * Class:     com_sun_glass_ui_win_WinRobot
 240  * Method:    _mouseWheel
 241  * Signature: (I)V
 242  */
 243 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseWheel
 244     (JNIEnv *env, jobject jrobot, jint wheelAmt)
 245 {
 246     ::mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0);
 247 }
 248 
 249 void GetScreenCapture(jint x, jint y, jint devw, jint devh,
 250                       jint *pixelData, jint retw, jint reth);
 251 
 252 /*
 253  * Class:     com_sun_glass_ui_win_WinRobot
 254  * Method:    _getPixelColor
 255  * Signature: (II)I
 256  */
 257 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getPixelColor
 258     (JNIEnv *env, jobject jrobot, jint x, jint y)
 259 {
 260     jfloat fx = (jfloat) x + 0.5f;
 261     jfloat fy = (jfloat) y + 0.5f;
 262     GlassScreen::FX2Win(&fx, &fy);
 263     jint dx = (jint) fx;
 264     jint dy = (jint) fy;
 265 
 266     jint val = 0;
 267     //NOTE: we don't use the ::GetPixel() on the screen DC because it's not capable of
 268     //      getting the correct colors when non-opaque windows are present
 269     GetScreenCapture(dx, dy, 1, 1, &val, 1, 1);
 270     return val;
 271 }
 272 
 273 /*
 274  * Class:     com_sun_glass_ui_win_WinRobot
 275  * Method:    _getScreenCapture
 276  * Signature: (IIII[I;)V
 277  */
 278 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1getScreenCapture
 279     (JNIEnv *env, jobject jrobot, jint x, jint y, jint width, jint height, jintArray pixelArray)
 280 {
 281     int numPixels = width * height;
 282     int pixelDataSize = sizeof(jint) * numPixels;
 283     ASSERT(pixelDataSize > 0 && pixelDataSize % 4 == 0);
 284 
 285     jint * pixelData = (jint *)(new BYTE[pixelDataSize]);
 286 
 287     if (pixelData) {
 288         GetScreenCapture(x, y, width, height, pixelData, width, height);
 289 
 290         // copy pixels into Java array
 291         env->SetIntArrayRegion(pixelArray, 0, numPixels, pixelData);
 292         delete[] pixelData;
 293     }
 294 }
 295 
 296 void GetScreenCapture(jint x, jint y, jint devw, jint devh,
 297                       jint *pixelData, jint retw, jint reth)
 298 {
 299     HDC hdcScreen = ::CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
 300     HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
 301     HBITMAP hbitmap;
 302     HBITMAP hOldBitmap;
 303     HPALETTE hOldPalette = NULL;
 304 
 305     // create an offscreen bitmap
 306     hbitmap = ::CreateCompatibleBitmap(hdcScreen, retw, reth);
 307     if (hbitmap == NULL) {
 308         //TODO: OOM might be better?
 309         //throw std::bad_alloc();
 310     }
 311     hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hbitmap);
 312 
 313     /* TODO: check this out
 314     // REMIND: not multimon-friendly...
 315     int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
 316     hOldPalette =
 317         AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex);
 318     AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex);
 319     */
 320 
 321     // copy screen image to offscreen bitmap
 322     // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents
 323     // correctly on Win2K/XP
 324     static const DWORD dwRop = SRCCOPY|CAPTUREBLT;
 325     if (retw == devw && reth == devh) {
 326         ::BitBlt(hdcMem, 0, 0, retw, reth, hdcScreen, x, y, dwRop);
 327     } else {
 328         ::StretchBlt(hdcMem, 0, 0, retw, reth, hdcScreen, x, y, devw, devh, dwRop);
 329     }
 330 
 331     static const int BITS_PER_PIXEL = 32;
 332 
 333     struct {
 334         BITMAPINFOHEADER bmiHeader;
 335         RGBQUAD          bmiColors[3];
 336     } BitmapInfo;
 337 
 338     // prepare BITMAPINFO for a 32-bit RGB bitmap
 339     ::memset(&BitmapInfo, 0, sizeof(BitmapInfo));
 340     BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 341     BitmapInfo.bmiHeader.biWidth = retw;
 342     BitmapInfo.bmiHeader.biHeight = -reth; // negative height means a top-down DIB
 343     BitmapInfo.bmiHeader.biPlanes = 1;
 344     BitmapInfo.bmiHeader.biBitCount = BITS_PER_PIXEL;
 345     BitmapInfo.bmiHeader.biCompression = BI_BITFIELDS;
 346 
 347     // Setup up color masks
 348     static const RGBQUAD redMask =   {0, 0, 0xFF, 0};
 349     static const RGBQUAD greenMask = {0, 0xFF, 0, 0};
 350     static const RGBQUAD blueMask =  {0xFF, 0, 0, 0};
 351 
 352     BitmapInfo.bmiColors[0] = redMask;
 353     BitmapInfo.bmiColors[1] = greenMask;
 354     BitmapInfo.bmiColors[2] = blueMask;
 355 
 356     // Get the bitmap data in device-independent, 32-bit packed pixel format
 357     ::GetDIBits(hdcMem, hbitmap, 0, reth, pixelData, (BITMAPINFO *)&BitmapInfo, DIB_RGB_COLORS);
 358 
 359     // convert Win32 pixel format (BGRX) to Java format (ARGB)
 360     ASSERT(sizeof(jint) == sizeof(RGBQUAD));
 361     jint numPixels = retw * reth;
 362     jint *pPixel = pixelData;
 363     for(int nPixel = 0; nPixel < numPixels; nPixel++) {
 364         RGBQUAD * prgbq = (RGBQUAD *) pPixel;
 365         *pPixel++ = WinToJavaPixel(prgbq->rgbRed, prgbq->rgbGreen, prgbq->rgbBlue);
 366     }
 367 
 368     // free all the GDI objects we made
 369     ::SelectObject(hdcMem, hOldBitmap);
 370     if (hOldPalette != NULL) {
 371         ::SelectPalette(hdcMem, hOldPalette, FALSE);
 372     }
 373     ::DeleteObject(hbitmap);
 374     ::DeleteDC(hdcMem);
 375     ::DeleteDC(hdcScreen);
 376 }
 377 
 378 }
 379