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); // no flags means key down
 303 }
 304 
 305 void AwtRobot::KeyRelease( jint jkey )
 306 {
 307     DoKeyEvent(jkey, KEYEVENTF_KEYUP);
 308 }
 309 
 310 void AwtRobot::DoKeyEvent( jint jkey, DWORD dwFlags )
 311 {
 312     UINT        vkey;
 313     UINT        modifiers;
 314     UINT        scancode;
 315     JNIEnv *    env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 316 
 317     // convert Java key into Windows key (and modifiers too)
 318     AwtComponent::JavaKeyToWindowsKey(jkey, &vkey, &modifiers);
 319 
 320     if (vkey == 0) {
 321         /* vkey would be 0 for all non-ascii inputs. If non-ascii
 322            then we assume they are unicode characters and will
 323            supply such input to SendInput() which can handle unicode
 324            characters as well as ascii chars. Windows provides api's to
 325            handle ascii and unicode characters. All ascii characters
 326            (both OEM and standard ascii) would be supplied to keybd_event().
 327 
 328            HandleUnicodeKeys() returns the status of the SendInput()
 329            which returns 0 if there is a fail else non-zero.
 330            The status 0 tells that the SendInput() in unable to interpret
 331            the supplied input upon which the illegal argument exception
 332            would be raised.
 333         */
 334         if(!HandleUnicodeKeys(jkey, dwFlags)) {
 335             // no equivalent Windows key found for given Java keycode
 336             JNU_ThrowIllegalArgumentException(env, "Invalid key code");
 337         }
 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 UINT AwtRobot::HandleUnicodeKeys(jint key, DWORD dwFlags)
 359 {
 360     NSWinInput::INPUT ip;
 361     ip.type = 1; //INPUT_KEYBOARD;
 362     ip.ki.wVk = 0;
 363     ip.ki.wScan = key;
 364     ip.ki.dwFlags = (DWORD)(dwFlags | 4); //KEYEVENTF_UNICODE(4)
 365     ip.ki.dwExtraInfo = 0;
 366     return SendInput(1, (LPINPUT)&ip, sizeof(INPUT));
 367 }
 368 
 369 //
 370 // utility function to get the C++ object from the Java one
 371 //
 372 // (static)
 373 AwtRobot * AwtRobot::GetRobot( jobject self )
 374 {
 375     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 376     AwtRobot * robot = (AwtRobot *)JNI_GET_PDATA(self);
 377     DASSERT( !::IsBadWritePtr( robot, sizeof(AwtRobot)));
 378     return robot;
 379 }
 380 
 381 //////////////////////////////////////////////////////////////////////////////////////////////
 382 // Native method declarations
 383 //
 384 
 385 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_create(
 386     JNIEnv * env, jobject self)
 387 {
 388     TRY;
 389 
 390     new AwtRobot(self);
 391 
 392     CATCH_BAD_ALLOC;
 393 }
 394 
 395 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer__1dispose(
 396     JNIEnv *env, jobject self)
 397 {
 398     TRY_NO_VERIFY;
 399 
 400     AwtObject::_Dispose(self);
 401 
 402     CATCH_BAD_ALLOC;
 403 }
 404 
 405 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseMoveImpl(
 406     JNIEnv * env, jobject self, jint x, jint y)
 407 {
 408     TRY;
 409 
 410     AwtRobot::GetRobot(self)->MouseMove(x, y);
 411 
 412     CATCH_BAD_ALLOC;
 413 }
 414 
 415 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mousePress(
 416     JNIEnv * env, jobject self, jint buttons)
 417 {
 418     TRY;
 419 
 420     AwtRobot::GetRobot(self)->MousePress(buttons);
 421 
 422     CATCH_BAD_ALLOC;
 423 }
 424 
 425 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseRelease(
 426     JNIEnv * env, jobject self, jint buttons)
 427 {
 428     TRY;
 429 
 430     AwtRobot::GetRobot(self)->MouseRelease(buttons);
 431 
 432     CATCH_BAD_ALLOC;
 433 }
 434 
 435 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseWheel(
 436     JNIEnv * env, jobject self, jint wheelAmt)
 437 {
 438     TRY;
 439 
 440     AwtRobot::GetRobot(self)->MouseWheel(wheelAmt);
 441 
 442     CATCH_BAD_ALLOC;
 443 }
 444 
 445 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_getRGBPixels(
 446     JNIEnv *env, jobject self, jint x, jint y, jint width, jint height, jintArray pixelArray)
 447 {
 448     TRY;
 449 
 450     AwtRobot::GetRobot(self)->GetRGBPixels(x, y, width, height, pixelArray);
 451 
 452     CATCH_BAD_ALLOC;
 453 }
 454 
 455 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyPress(
 456   JNIEnv *, jobject self, jint javakey )
 457 {
 458     TRY;
 459 
 460     AwtRobot::GetRobot(self)->KeyPress(javakey);
 461 
 462     CATCH_BAD_ALLOC;
 463 }
 464 
 465 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyRelease(
 466   JNIEnv *, jobject self, jint javakey )
 467 {
 468     TRY;
 469 
 470     AwtRobot::GetRobot(self)->KeyRelease(javakey);
 471 
 472     CATCH_BAD_ALLOC;
 473 }