1 /*
   2  * Copyright (c) 1998, 2014, 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       POINT curPos;
  84       ::GetCursorPos(&curPos);
  85       x -= curPos.x;
  86       y -= curPos.y;
  87 
  88       mouse_event(MOUSEEVENTF_MOVE,x,y,0,0);
  89       // Move the cursor to the desired coordinates.
  90 
  91       // Restore the old Mouse Acceleration Constants.
  92       bResult = SystemParametersInfo(SPI_SETMOUSE,0, oldAccel, SPIF_SENDCHANGE);
  93       bResult = SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)oldSpeed,
  94                                      SPIF_SENDCHANGE);
  95 }
  96 
  97 void AwtRobot::MousePress( jint buttonMask )
  98 {
  99     DWORD dwFlags = 0L;
 100     // According to MSDN: Software Driving Software
 101     // application should consider SM_SWAPBUTTON to correctly emulate user with
 102     // left handed mouse setup
 103     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 104 
 105     if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 106         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
 107     {
 108         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
 109     }
 110 
 111     if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 112          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
 113     {
 114         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN;
 115     }
 116 
 117     if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 118          buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
 119     {
 120         dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
 121     }
 122 
 123     INPUT mouseInput = {0};
 124     mouseInput.type = INPUT_MOUSE;
 125     mouseInput.mi.time = 0;
 126     mouseInput.mi.dwFlags = dwFlags;
 127     if ( buttonMask & AwtComponent::masks[3] ) {
 128         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
 129         mouseInput.mi.mouseData = XBUTTON1;
 130     }
 131 
 132     if ( buttonMask & AwtComponent::masks[4] ) {
 133         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN;
 134         mouseInput.mi.mouseData = XBUTTON2;
 135     }
 136     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 137 }
 138 
 139 void AwtRobot::MouseRelease( jint buttonMask )
 140 {
 141     DWORD dwFlags = 0L;
 142     // According to MSDN: Software Driving Software
 143     // application should consider SM_SWAPBUTTON to correctly emulate user with
 144     // left handed mouse setup
 145     BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON);
 146 
 147     if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 148         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK)
 149     {
 150         dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
 151     }
 152 
 153     if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 154          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK)
 155     {
 156         dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP;
 157     }
 158 
 159     if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 160         buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK)
 161     {
 162         dwFlags |= MOUSEEVENTF_MIDDLEUP;
 163     }
 164 
 165     INPUT mouseInput = {0};
 166     mouseInput.type = INPUT_MOUSE;
 167     mouseInput.mi.time = 0;
 168     mouseInput.mi.dwFlags = dwFlags;
 169 
 170     if ( buttonMask & AwtComponent::masks[3] ) {
 171         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
 172         mouseInput.mi.mouseData = XBUTTON1;
 173     }
 174 
 175     if ( buttonMask & AwtComponent::masks[4] ) {
 176         mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP;
 177         mouseInput.mi.mouseData = XBUTTON2;
 178     }
 179     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 180 }
 181 
 182 void AwtRobot::MouseWheel (jint wheelAmt) {
 183     mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0);
 184 }
 185 
 186 inline jint AwtRobot::WinToJavaPixel(USHORT r, USHORT g, USHORT b)
 187 {
 188     jint value =
 189             0xFF << 24 | // alpha channel is always turned all the way up
 190             r << 16 |
 191             g << 8  |
 192             b << 0;
 193     return value;
 194 }
 195 
 196 void AwtRobot::GetRGBPixels(jint x, jint y, jint width, jint height, jintArray pixelArray)
 197 {
 198     DASSERT(width > 0 && height > 0);
 199 
 200     HDC hdcScreen = ::CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
 201     HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
 202     HBITMAP hbitmap;
 203     HBITMAP hOldBitmap;
 204     HPALETTE hOldPalette = NULL;
 205     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 206 
 207     // create an offscreen bitmap
 208     hbitmap = ::CreateCompatibleBitmap(hdcScreen, width, height);
 209     if (hbitmap == NULL) {
 210         throw std::bad_alloc();
 211     }
 212     hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hbitmap);
 213 
 214     // REMIND: not multimon-friendly...
 215     int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
 216     hOldPalette =
 217         AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex);
 218     AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex);
 219 
 220     // copy screen image to offscreen bitmap
 221     // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents
 222     // correctly on Win2K/XP
 223     VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y,
 224                                                 SRCCOPY|CAPTUREBLT) != 0);
 225 
 226     static const int BITS_PER_PIXEL = 32;
 227     static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8;
 228 
 229     if (!IS_SAFE_SIZE_MUL(width, height)) throw std::bad_alloc();
 230     int numPixels = width*height;
 231     if (!IS_SAFE_SIZE_MUL(BYTES_PER_PIXEL, numPixels)) throw std::bad_alloc();
 232     int pixelDataSize = BYTES_PER_PIXEL*numPixels;
 233     DASSERT(pixelDataSize > 0 && pixelDataSize % 4 == 0);
 234     // allocate memory for BITMAPINFO + pixel data
 235     // 4620932: When using BI_BITFIELDS, GetDIBits expects an array of 3
 236     // RGBQUADS to follow the BITMAPINFOHEADER, but we were only allocating the
 237     // 1 that is included in BITMAPINFO.  Thus, GetDIBits was writing off the
 238     // end of our block of memory.  Now we allocate sufficient memory.
 239     // See MSDN docs for BITMAPINFOHEADER -bchristi
 240 
 241     if (!IS_SAFE_SIZE_ADD(sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD), pixelDataSize)) {
 242         throw std::bad_alloc();
 243     }
 244     BITMAPINFO * pinfo = (BITMAPINFO *)(new BYTE[sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD) + pixelDataSize]);
 245 
 246     // pixel data starts after 3 RGBQUADS for color masks
 247     RGBQUAD *pixelData = &pinfo->bmiColors[3];
 248 
 249     // prepare BITMAPINFO for a 32-bit RGB bitmap
 250     ::memset(pinfo, 0, sizeof(*pinfo));
 251     pinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 252     pinfo->bmiHeader.biWidth = width;
 253     pinfo->bmiHeader.biHeight = -height; // negative height means a top-down DIB
 254     pinfo->bmiHeader.biPlanes = 1;
 255     pinfo->bmiHeader.biBitCount = BITS_PER_PIXEL;
 256     pinfo->bmiHeader.biCompression = BI_BITFIELDS;
 257 
 258     // Setup up color masks
 259     static const RGBQUAD redMask =   {0, 0, 0xFF, 0};
 260     static const RGBQUAD greenMask = {0, 0xFF, 0, 0};
 261     static const RGBQUAD blueMask =  {0xFF, 0, 0, 0};
 262 
 263     pinfo->bmiColors[0] = redMask;
 264     pinfo->bmiColors[1] = greenMask;
 265     pinfo->bmiColors[2] = blueMask;
 266 
 267     // Get the bitmap data in device-independent, 32-bit packed pixel format
 268     ::GetDIBits(hdcMem, hbitmap, 0, height, pixelData, pinfo, DIB_RGB_COLORS);
 269 
 270     // convert Win32 pixel format (BGRX) to Java format (ARGB)
 271     DASSERT(sizeof(jint) == sizeof(RGBQUAD));
 272     for(int nPixel = 0; nPixel < numPixels; nPixel++) {
 273         RGBQUAD * prgbq = &pixelData[nPixel];
 274         jint jpixel = WinToJavaPixel(prgbq->rgbRed, prgbq->rgbGreen, prgbq->rgbBlue);
 275         // stuff the 32-bit pixel back into the 32-bit RGBQUAD
 276         *prgbq = *( (RGBQUAD *)(&jpixel) );
 277     }
 278 
 279     // copy pixels into Java array
 280     env->SetIntArrayRegion(pixelArray, 0, numPixels, (jint *)pixelData);
 281     delete pinfo;
 282 
 283     // free all the GDI objects we made
 284     ::SelectObject(hdcMem, hOldBitmap);
 285     if (hOldPalette != NULL) {
 286         ::SelectPalette(hdcMem, hOldPalette, FALSE);
 287     }
 288     ::DeleteObject(hbitmap);
 289     ::DeleteDC(hdcMem);
 290     ::DeleteDC(hdcScreen);
 291 }
 292 
 293 void AwtRobot::KeyPress( jint jkey )
 294 {
 295     DoKeyEvent(jkey, 0); // no flags means key down
 296 }
 297 
 298 void AwtRobot::KeyRelease( jint jkey )
 299 {
 300     DoKeyEvent(jkey, KEYEVENTF_KEYUP);
 301 }
 302 
 303 void AwtRobot::DoKeyEvent( jint jkey, DWORD dwFlags )
 304 {
 305     UINT        vkey;
 306     UINT        modifiers;
 307     UINT        scancode;
 308     JNIEnv *    env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 309 
 310     // convert Java key into Windows key (and modifiers too)
 311     AwtComponent::JavaKeyToWindowsKey(jkey, &vkey, &modifiers);
 312     if (vkey == 0) {
 313         // no equivalent Windows key found for given Java keycode
 314         JNU_ThrowIllegalArgumentException(env, "Invalid key code");
 315     } else {
 316         // get the scancode from the virtual key
 317         scancode = ::MapVirtualKey(vkey, 0);
 318         keybd_event(vkey, scancode, dwFlags, 0);
 319     }
 320 }
 321 
 322 //
 323 // utility function to get the C++ object from the Java one
 324 //
 325 // (static)
 326 AwtRobot * AwtRobot::GetRobot( jobject self )
 327 {
 328     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 329     AwtRobot * robot = (AwtRobot *)JNI_GET_PDATA(self);
 330     DASSERT( !::IsBadWritePtr( robot, sizeof(AwtRobot)));
 331     return robot;
 332 }
 333 
 334 //////////////////////////////////////////////////////////////////////////////////////////////
 335 // Native method declarations
 336 //
 337 
 338 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_create(
 339     JNIEnv * env, jobject self)
 340 {
 341     TRY;
 342 
 343     new AwtRobot(self);
 344 
 345     CATCH_BAD_ALLOC;
 346 }
 347 
 348 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer__1dispose(
 349     JNIEnv *env, jobject self)
 350 {
 351     TRY_NO_VERIFY;
 352 
 353     AwtObject::_Dispose(self);
 354 
 355     CATCH_BAD_ALLOC;
 356 }
 357 
 358 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseMoveImpl(
 359     JNIEnv * env, jobject self, jint x, jint y)
 360 {
 361     TRY;
 362 
 363     AwtRobot::GetRobot(self)->MouseMove(x, y);
 364 
 365     CATCH_BAD_ALLOC;
 366 }
 367 
 368 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mousePress(
 369     JNIEnv * env, jobject self, jint buttons)
 370 {
 371     TRY;
 372 
 373     AwtRobot::GetRobot(self)->MousePress(buttons);
 374 
 375     CATCH_BAD_ALLOC;
 376 }
 377 
 378 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseRelease(
 379     JNIEnv * env, jobject self, jint buttons)
 380 {
 381     TRY;
 382 
 383     AwtRobot::GetRobot(self)->MouseRelease(buttons);
 384 
 385     CATCH_BAD_ALLOC;
 386 }
 387 
 388 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseWheel(
 389     JNIEnv * env, jobject self, jint wheelAmt)
 390 {
 391     TRY;
 392 
 393     AwtRobot::GetRobot(self)->MouseWheel(wheelAmt);
 394 
 395     CATCH_BAD_ALLOC;
 396 }
 397 
 398 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_getRGBPixels(
 399     JNIEnv *env, jobject self, jint x, jint y, jint width, jint height, jintArray pixelArray)
 400 {
 401     TRY;
 402 
 403     AwtRobot::GetRobot(self)->GetRGBPixels(x, y, width, height, pixelArray);
 404 
 405     CATCH_BAD_ALLOC;
 406 }
 407 
 408 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyPress(
 409   JNIEnv *, jobject self, jint javakey )
 410 {
 411     TRY;
 412 
 413     AwtRobot::GetRobot(self)->KeyPress(javakey);
 414 
 415     CATCH_BAD_ALLOC;
 416 }
 417 
 418 JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyRelease(
 419   JNIEnv *, jobject self, jint javakey )
 420 {
 421     TRY;
 422 
 423     AwtRobot::GetRobot(self)->KeyRelease(javakey);
 424 
 425     CATCH_BAD_ALLOC;
 426 }