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