1 /*
   2  * Copyright (c) 1998, 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 #include "awt.h"
  27 #include "awt_Toolkit.h"
  28 #include "awt_Component.h"
  29 #include "awt_Robot.h"
  30 #include "sun_awt_windows_WRobotPeer.h"
  31 #include "java_awt_event_InputEvent.h"
  32 #include <winuser.h>
  33 
  34 AwtRobot::AwtRobot(jobject peer, int screen)
  35 {
  36     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
  37     m_deviceIndex = screen;
  38     m_peerObject = env->NewWeakGlobalRef(peer);
  39     JNU_CHECK_EXCEPTION(env);
  40     JNI_SET_PDATA(peer, this);
  41 }
  42 
  43 AwtRobot::~AwtRobot()
  44 {
  45 }
  46 
  47 #ifndef SPI_GETMOUSESPEED
  48 #define SPI_GETMOUSESPEED 112
  49 #endif
  50 
  51 #ifndef SPI_SETMOUSESPEED
  52 #define SPI_SETMOUSESPEED 113
  53 #endif
  54 
  55 void AwtRobot::MouseMove( jint x, jint y)
  56 {
  57     // Fix for Bug 4288230. See Q193003 from MSDN.
  58       int oldAccel[3], newAccel[3];
  59       INT_PTR oldSpeed, newSpeed;
  60       BOOL bResult;
  61 
  62    // The following values set mouse ballistics to 1 mickey/pixel.
  63       newAccel[0] = 0;
  64       newAccel[1] = 0;
  65       newAccel[2] = 0;
  66       newSpeed = 10;
  67 
  68       // Save the Current Mouse Acceleration Constants
  69       bResult = SystemParametersInfo(SPI_GETMOUSE,0,oldAccel,0);
  70       bResult = SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed,0);
  71       // Set the new Mouse Acceleration Constants (Disabled).
  72       bResult = SystemParametersInfo(SPI_SETMOUSE,0,newAccel,SPIF_SENDCHANGE);
  73       bResult = SystemParametersInfo(SPI_SETMOUSESPEED, 0,
  74                 // 4504963: Though the third argument to SystemParameterInfo is
  75                 // declared as a PVOID, as of Windows 2000 it is apparently
  76                 // interpreted as an int.  (The MSDN docs for SPI_SETMOUSESPEED
  77                 // say that it's an integer between 1 and 20, the default being
  78                 // 10).  Instead of passing the @ of the desired value, the
  79                 // value itself is now passed, cast as a PVOID so as to
  80                 // compile.  -bchristi 10/02/2001
  81                                      (PVOID)newSpeed,
  82                                      SPIF_SENDCHANGE);
  83 
  84       Devices::InstanceAccess devices;
  85       AwtWin32GraphicsDevice *device = devices->GetDevice(m_deviceIndex);
  86 
  87       RECT rect = {0, 0, 0, 0};
  88       MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(m_deviceIndex), &rect);
  89 
  90       x = rect.left + (device == NULL ? x : device->ScaleUpX(x));
  91       y = rect.top + (device == NULL ? y : device->ScaleUpY(y));
  92 
  93       POINT curPos;
  94       ::GetCursorPos(&curPos);
  95       x -= curPos.x;
  96       y -= curPos.y;
  97 
  98       mouse_event(MOUSEEVENTF_MOVE,x,y,0,0);
  99       // Move the cursor to the desired coordinates.
 100 
 101       // Restore the old Mouse Acceleration Constants.
 102       bResult = SystemParametersInfo(SPI_SETMOUSE,0, oldAccel, SPIF_SENDCHANGE);
 103       bResult = SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)oldSpeed,
 104                                      SPIF_SENDCHANGE);
 105 }
 106 
 107 void AwtRobot::MousePress( jint buttonMask )
 108 {
 109     DWORD dwFlags = 0L;
 110     // According to MSDN: Software Driving Software
 111     // application should consider SM_SWAPBUTTON to correctly emulate user with
 112     // left handed mouse setup
 113     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 114 
 115     if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 116         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
 117     {
 118         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
 119     }
 120 
 121     if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 122          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
 123     {
 124         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN;
 125     }
 126 
 127     if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 128          buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
 129     {
 130         dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
 131     }
 132 
 133     INPUT mouseInput = {0};
 134     mouseInput.type = INPUT_MOUSE;
 135     mouseInput.mi.time = 0;
 136     mouseInput.mi.dwFlags = dwFlags;
 137     if ( buttonMask & AwtComponent::masks[3] ) {
 138         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
 139         mouseInput.mi.mouseData = XBUTTON1;
 140     }
 141 
 142     if ( buttonMask & AwtComponent::masks[4] ) {
 143         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
 144         mouseInput.mi.mouseData = XBUTTON2;
 145     }
 146     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 147 }
 148 
 149 void AwtRobot::MouseRelease( jint buttonMask )
 150 {
 151     DWORD dwFlags = 0L;
 152     // According to MSDN: Software Driving Software
 153     // application should consider SM_SWAPBUTTON to correctly emulate user with
 154     // left handed mouse setup
 155     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 156 
 157     if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 158         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
 159     {
 160         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
 161     }
 162 
 163     if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 164          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
 165     {
 166         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP;
 167     }
 168 
 169     if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 170         buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
 171     {
 172         dwFlags |= MOUSEEVENTF_MIDDLEUP;
 173     }
 174 
 175     INPUT mouseInput = {0};
 176     mouseInput.type = INPUT_MOUSE;
 177     mouseInput.mi.time = 0;
 178     mouseInput.mi.dwFlags = dwFlags;
 179 
 180     if ( buttonMask & AwtComponent::masks[3] ) {
 181         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
 182         mouseInput.mi.mouseData = XBUTTON1;
 183     }
 184 
 185     if ( buttonMask & AwtComponent::masks[4] ) {
 186         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
 187         mouseInput.mi.mouseData = XBUTTON2;
 188     }
 189     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 190 }
 191 
 192 void AwtRobot::MouseWheel (jint wheelAmt) {
 193     mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0);
 194 }
 195 
 196 inline jint AwtRobot::WinToJavaPixel(USHORT r, USHORT g, USHORT b)
 197 {
 198     jint value =
 199             0xFF << 24 | // alpha channel is always turned all the way up
 200             r << 16 |
 201             g << 8  |
 202             b << 0;
 203     return value;
 204 }
 205 
 206 void AwtRobot::GetRGBPixels(jint x, jint y, jint width, jint height, jintArray pixelArray)
 207 {
 208     DASSERT(width > 0 && height > 0);
 209 
 210     MONITORINFOEX *pMonInfo =
 211          (LPMONITORINFOEX)AwtWin32GraphicsDevice::GetMonitorInfo(m_deviceIndex);
 212     HDC hdcScreen = ::CreateDC(TEXT("DISPLAY"), pMonInfo->szDevice, NULL, NULL);
 213     HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
 214     HBITMAP hbitmap;
 215     HBITMAP hOldBitmap;
 216     HPALETTE hOldPalette = NULL;
 217     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 218 
 219     // create an offscreen bitmap
 220     hbitmap = ::CreateCompatibleBitmap(hdcScreen, width, height);
 221     if (hbitmap == NULL) {
 222         throw std::bad_alloc();
 223     }
 224     hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hbitmap);
 225 
 226     hOldPalette =
 227         AwtWin32GraphicsDevice::SelectPalette(hdcMem, m_deviceIndex);
 228     AwtWin32GraphicsDevice::RealizePalette(hdcMem, m_deviceIndex);
 229 
 230     Devices::InstanceAccess devices;
 231     AwtWin32GraphicsDevice *device = devices->GetDevice(m_deviceIndex);
 232     int sWidth = (device == NULL) ? width : device->ScaleUpX(width);
 233     int sHeight = (device == NULL) ? height : device->ScaleUpY(height);
 234 
 235     // copy screen image to offscreen bitmap
 236     // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents
 237     // correctly on Win2K/XP
 238     if (width == sWidth && height == sHeight) {
 239         VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y,
 240                SRCCOPY | CAPTUREBLT) != 0);
 241     } else {
 242         int sX = (device == NULL) ? x : device->ScaleUpX(x);
 243         int sY = (device == NULL) ? y : device->ScaleUpY(y);
 244         VERIFY(::StretchBlt(hdcMem, 0, 0, width, height,
 245                hdcScreen, sX, sY, sWidth, sHeight,
 246                SRCCOPY | CAPTUREBLT) != 0);
 247     }
 248 
 249     static const int BITS_PER_PIXEL = 32;
 250     static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8;
 251 
 252     if (!IS_SAFE_SIZE_MUL(width, height)) throw std::bad_alloc();
 253     int numPixels = width*height;
 254     if (!IS_SAFE_SIZE_MUL(BYTES_PER_PIXEL, numPixels)) throw std::bad_alloc();
 255     int pixelDataSize = BYTES_PER_PIXEL*numPixels;
 256     DASSERT(pixelDataSize > 0 && pixelDataSize % 4 == 0);
 257     // allocate memory for BITMAPINFO + pixel data
 258     // 4620932: When using BI_BITFIELDS, GetDIBits expects an array of 3
 259     // RGBQUADS to follow the BITMAPINFOHEADER, but we were only allocating the
 260     // 1 that is included in BITMAPINFO.  Thus, GetDIBits was writing off the
 261     // end of our block of memory.  Now we allocate sufficient memory.
 262     // See MSDN docs for BITMAPINFOHEADER -bchristi
 263 
 264     if (!IS_SAFE_SIZE_ADD(sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD), pixelDataSize)) {
 265         throw std::bad_alloc();
 266     }
 267     BITMAPINFO * pinfo = (BITMAPINFO *)(new BYTE[sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD) + pixelDataSize]);
 268 
 269     // pixel data starts after 3 RGBQUADS for color masks
 270     RGBQUAD *pixelData = &pinfo->bmiColors[3];
 271 
 272     // prepare BITMAPINFO for a 32-bit RGB bitmap
 273     ::memset(pinfo, 0, sizeof(*pinfo));
 274     pinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 275     pinfo->bmiHeader.biWidth = width;
 276     pinfo->bmiHeader.biHeight = -height; // negative height means a top-down DIB
 277     pinfo->bmiHeader.biPlanes = 1;
 278     pinfo->bmiHeader.biBitCount = BITS_PER_PIXEL;
 279     pinfo->bmiHeader.biCompression = BI_BITFIELDS;
 280 
 281     // Setup up color masks
 282     static const RGBQUAD redMask =   {0, 0, 0xFF, 0};
 283     static const RGBQUAD greenMask = {0, 0xFF, 0, 0};
 284     static const RGBQUAD blueMask =  {0xFF, 0, 0, 0};
 285 
 286     pinfo->bmiColors[0] = redMask;
 287     pinfo->bmiColors[1] = greenMask;
 288     pinfo->bmiColors[2] = blueMask;
 289 
 290     // Get the bitmap data in device-independent, 32-bit packed pixel format
 291     ::GetDIBits(hdcMem, hbitmap, 0, height, pixelData, pinfo, DIB_RGB_COLORS);
 292 
 293     // convert Win32 pixel format (BGRX) to Java format (ARGB)
 294     DASSERT(sizeof(jint) == sizeof(RGBQUAD));
 295     for(int nPixel = 0; nPixel < numPixels; nPixel++) {
 296         RGBQUAD * prgbq = &pixelData[nPixel];
 297         jint jpixel = WinToJavaPixel(prgbq->rgbRed, prgbq->rgbGreen, prgbq->rgbBlue);
 298         // stuff the 32-bit pixel back into the 32-bit RGBQUAD
 299         *prgbq = *( (RGBQUAD *)(&jpixel) );
 300     }
 301 
 302     // copy pixels into Java array
 303     env->SetIntArrayRegion(pixelArray, 0, numPixels, (jint *)pixelData);
 304     delete pinfo;
 305 
 306     // free all the GDI objects we made
 307     ::SelectObject(hdcMem, hOldBitmap);
 308     if (hOldPalette != NULL) {
 309         ::SelectPalette(hdcMem, hOldPalette, FALSE);
 310     }
 311     ::DeleteObject(hbitmap);
 312     ::DeleteDC(hdcMem);
 313     ::DeleteDC(hdcScreen);
 314 }
 315 
 316 void AwtRobot::KeyPress( jint jkey )
 317 {
 318     DoKeyEvent(jkey, 0); // no flags means key down
 319 }
 320 
 321 void AwtRobot::KeyRelease( jint jkey )
 322 {
 323     DoKeyEvent(jkey, KEYEVENTF_KEYUP);
 324 }
 325 
 326 void AwtRobot::DoKeyEvent( jint jkey, DWORD dwFlags )
 327 {
 328     UINT        vkey;
 329     UINT        modifiers;
 330     UINT        scancode;
 331     JNIEnv *    env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 332 
 333     // convert Java key into Windows key (and modifiers too)
 334     AwtComponent::JavaKeyToWindowsKey(jkey, &vkey, &modifiers);
 335     if (vkey == 0) {
 336         // no equivalent Windows key found for given Java keycode
 337         JNU_ThrowIllegalArgumentException(env, "Invalid key code");
 338     } else {
 339         // get the scancode from the virtual key
 340         scancode = ::MapVirtualKey(vkey, 0);
 341         if (vkey == VK_RMENU ||
 342             vkey == VK_DELETE ||
 343             vkey == VK_INSERT ||
 344             vkey == VK_NEXT ||
 345             vkey == VK_PRIOR ||
 346             vkey == VK_HOME ||
 347             vkey == VK_END ||
 348             vkey == VK_LEFT ||
 349             vkey == VK_RIGHT ||
 350             vkey == VK_UP ||
 351             vkey == VK_DOWN) {
 352             dwFlags |= KEYEVENTF_EXTENDEDKEY;
 353         }
 354         keybd_event(vkey, scancode, dwFlags, 0);
 355     }
 356 }
 357 
 358 //
 359 // utility function to get the C++ object from the Java one
 360 //
 361 // (static)
 362 AwtRobot * AwtRobot::GetRobot( jobject self )
 363 {
 364     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 365     AwtRobot * robot = (AwtRobot *)JNI_GET_PDATA(self);
 366     DASSERT( !::IsBadWritePtr( robot, sizeof(AwtRobot)));
 367     return robot;
 368 }
 369 
 370 //////////////////////////////////////////////////////////////////////////////////////////////
 371 // Native method declarations
 372 //
 373 
 374 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_create(
 375     JNIEnv * env, jobject self, jint screen)
 376 {
 377     TRY;
 378 
 379     new AwtRobot(self, screen);
 380 
 381     CATCH_BAD_ALLOC;
 382 }
 383 
 384 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer__1dispose(
 385     JNIEnv *env, jobject self)
 386 {
 387     TRY_NO_VERIFY;
 388 
 389     AwtObject::_Dispose(self);
 390 
 391     CATCH_BAD_ALLOC;
 392 }
 393 
 394 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseMoveImpl(
 395     JNIEnv * env, jobject self, jint x, jint y)
 396 {
 397     TRY;
 398 
 399     AwtRobot::GetRobot(self)->MouseMove(x, y);
 400 
 401     CATCH_BAD_ALLOC;
 402 }
 403 
 404 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mousePress(
 405     JNIEnv * env, jobject self, jint buttons)
 406 {
 407     TRY;
 408 
 409     AwtRobot::GetRobot(self)->MousePress(buttons);
 410 
 411     CATCH_BAD_ALLOC;
 412 }
 413 
 414 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseRelease(
 415     JNIEnv * env, jobject self, jint buttons)
 416 {
 417     TRY;
 418 
 419     AwtRobot::GetRobot(self)->MouseRelease(buttons);
 420 
 421     CATCH_BAD_ALLOC;
 422 }
 423 
 424 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseWheel(
 425     JNIEnv * env, jobject self, jint wheelAmt)
 426 {
 427     TRY;
 428 
 429     AwtRobot::GetRobot(self)->MouseWheel(wheelAmt);
 430 
 431     CATCH_BAD_ALLOC;
 432 }
 433 
 434 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_getRGBPixels(
 435     JNIEnv *env, jobject self, jint x, jint y, jint width, jint height, jintArray pixelArray)
 436 {
 437     TRY;
 438 
 439     AwtRobot::GetRobot(self)->GetRGBPixels(x, y, width, height, pixelArray);
 440 
 441     CATCH_BAD_ALLOC;
 442 }
 443 
 444 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyPress(
 445   JNIEnv *, jobject self, jint javakey )
 446 {
 447     TRY;
 448 
 449     AwtRobot::GetRobot(self)->KeyPress(javakey);
 450 
 451     CATCH_BAD_ALLOC;
 452 }
 453 
 454 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyRelease(
 455   JNIEnv *, jobject self, jint javakey )
 456 {
 457     TRY;
 458 
 459     AwtRobot::GetRobot(self)->KeyRelease(javakey);
 460 
 461     CATCH_BAD_ALLOC;
 462 }