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