/* * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "awt.h" #include "awt_Toolkit.h" #include "awt_Component.h" #include "awt_Robot.h" #include "sun_awt_windows_WRobotPeer.h" #include "java_awt_event_InputEvent.h" #include AwtRobot::AwtRobot( jobject peer ) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); m_peerObject = env->NewWeakGlobalRef(peer); JNU_CHECK_EXCEPTION(env); JNI_SET_PDATA(peer, this); } AwtRobot::~AwtRobot() { } #ifndef SPI_GETMOUSESPEED #define SPI_GETMOUSESPEED 112 #endif #ifndef SPI_SETMOUSESPEED #define SPI_SETMOUSESPEED 113 #endif void AwtRobot::MouseMove( jint x, jint y) { // Fix for Bug 4288230. See Q193003 from MSDN. int oldAccel[3], newAccel[3]; INT_PTR oldSpeed, newSpeed; BOOL bResult; // The following values set mouse ballistics to 1 mickey/pixel. newAccel[0] = 0; newAccel[1] = 0; newAccel[2] = 0; newSpeed = 10; // Save the Current Mouse Acceleration Constants bResult = SystemParametersInfo(SPI_GETMOUSE,0,oldAccel,0); bResult = SystemParametersInfo(SPI_GETMOUSESPEED, 0, &oldSpeed,0); // Set the new Mouse Acceleration Constants (Disabled). bResult = SystemParametersInfo(SPI_SETMOUSE,0,newAccel,SPIF_SENDCHANGE); bResult = SystemParametersInfo(SPI_SETMOUSESPEED, 0, // 4504963: Though the third argument to SystemParameterInfo is // declared as a PVOID, as of Windows 2000 it is apparently // interpreted as an int. (The MSDN docs for SPI_SETMOUSESPEED // say that it's an integer between 1 and 20, the default being // 10). Instead of passing the @ of the desired value, the // value itself is now passed, cast as a PVOID so as to // compile. -bchristi 10/02/2001 (PVOID)newSpeed, SPIF_SENDCHANGE); int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex); x = (device == NULL) ? x : device->ScaleUpX(x); y = (device == NULL) ? y : device->ScaleUpY(y); POINT curPos; ::GetCursorPos(&curPos); x -= curPos.x; y -= curPos.y; mouse_event(MOUSEEVENTF_MOVE,x,y,0,0); // Move the cursor to the desired coordinates. // Restore the old Mouse Acceleration Constants. bResult = SystemParametersInfo(SPI_SETMOUSE,0, oldAccel, SPIF_SENDCHANGE); bResult = SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)oldSpeed, SPIF_SENDCHANGE); } void AwtRobot::MousePress( jint buttonMask ) { DWORD dwFlags = 0L; // According to MSDN: Software Driving Software // application should consider SM_SWAPBUTTON to correctly emulate user with // left handed mouse setup BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON); if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK || buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) { dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN; } if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK || buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) { dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN; } if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK || buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) { dwFlags |= MOUSEEVENTF_MIDDLEDOWN; } INPUT mouseInput = {0}; mouseInput.type = INPUT_MOUSE; mouseInput.mi.time = 0; mouseInput.mi.dwFlags = dwFlags; if ( buttonMask & AwtComponent::masks[3] ) { mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN; mouseInput.mi.mouseData = XBUTTON1; } if ( buttonMask & AwtComponent::masks[4] ) { mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XDOWN; mouseInput.mi.mouseData = XBUTTON2; } ::SendInput(1, &mouseInput, sizeof(mouseInput)); } void AwtRobot::MouseRelease( jint buttonMask ) { DWORD dwFlags = 0L; // According to MSDN: Software Driving Software // application should consider SM_SWAPBUTTON to correctly emulate user with // left handed mouse setup BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON); if ( buttonMask & java_awt_event_InputEvent_BUTTON1_MASK || buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) { dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP; } if ( buttonMask & java_awt_event_InputEvent_BUTTON3_MASK || buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) { dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP; } if ( buttonMask & java_awt_event_InputEvent_BUTTON2_MASK || buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) { dwFlags |= MOUSEEVENTF_MIDDLEUP; } INPUT mouseInput = {0}; mouseInput.type = INPUT_MOUSE; mouseInput.mi.time = 0; mouseInput.mi.dwFlags = dwFlags; if ( buttonMask & AwtComponent::masks[3] ) { mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP; mouseInput.mi.mouseData = XBUTTON1; } if ( buttonMask & AwtComponent::masks[4] ) { mouseInput.mi.dwFlags = mouseInput.mi.dwFlags | MOUSEEVENTF_XUP; mouseInput.mi.mouseData = XBUTTON2; } ::SendInput(1, &mouseInput, sizeof(mouseInput)); } void AwtRobot::MouseWheel (jint wheelAmt) { mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0); } inline jint AwtRobot::WinToJavaPixel(USHORT r, USHORT g, USHORT b) { jint value = 0xFF << 24 | // alpha channel is always turned all the way up r << 16 | g << 8 | b << 0; return value; } void AwtRobot::GetRGBPixels(jint x, jint y, jint width, jint height, jintArray pixelArray) { DASSERT(width > 0 && height > 0); HDC hdcScreen = ::CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); HDC hdcMem = ::CreateCompatibleDC(hdcScreen); HBITMAP hbitmap; HBITMAP hOldBitmap; HPALETTE hOldPalette = NULL; JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); // create an offscreen bitmap hbitmap = ::CreateCompatibleBitmap(hdcScreen, width, height); if (hbitmap == NULL) { throw std::bad_alloc(); } hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hbitmap); // REMIND: not multimon-friendly... int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex(); hOldPalette = AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex); AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex); int sWidth = (device == NULL) ? width : device->ScaleUpX(width); int sHeight = (device == NULL) ? height : device->ScaleUpY(height); // copy screen image to offscreen bitmap // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents // correctly on Win2K/XP if (width == sWidth && height == sHeight) { VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y, SRCCOPY | CAPTUREBLT) != 0); } else { int sX = (device == NULL) ? x : device->ScaleUpX(x); int sY = (device == NULL) ? y : device->ScaleUpY(y); VERIFY(::StretchBlt(hdcMem, 0, 0, width, height, hdcScreen, sX, sY, sWidth, sHeight, SRCCOPY | CAPTUREBLT) != 0); } static const int BITS_PER_PIXEL = 32; static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8; if (!IS_SAFE_SIZE_MUL(width, height)) throw std::bad_alloc(); int numPixels = width*height; if (!IS_SAFE_SIZE_MUL(BYTES_PER_PIXEL, numPixels)) throw std::bad_alloc(); int pixelDataSize = BYTES_PER_PIXEL*numPixels; DASSERT(pixelDataSize > 0 && pixelDataSize % 4 == 0); // allocate memory for BITMAPINFO + pixel data // 4620932: When using BI_BITFIELDS, GetDIBits expects an array of 3 // RGBQUADS to follow the BITMAPINFOHEADER, but we were only allocating the // 1 that is included in BITMAPINFO. Thus, GetDIBits was writing off the // end of our block of memory. Now we allocate sufficient memory. // See MSDN docs for BITMAPINFOHEADER -bchristi if (!IS_SAFE_SIZE_ADD(sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD), pixelDataSize)) { throw std::bad_alloc(); } BITMAPINFO * pinfo = (BITMAPINFO *)(new BYTE[sizeof(BITMAPINFOHEADER) + 3 * sizeof(RGBQUAD) + pixelDataSize]); // pixel data starts after 3 RGBQUADS for color masks RGBQUAD *pixelData = &pinfo->bmiColors[3]; // prepare BITMAPINFO for a 32-bit RGB bitmap ::memset(pinfo, 0, sizeof(*pinfo)); pinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pinfo->bmiHeader.biWidth = width; pinfo->bmiHeader.biHeight = -height; // negative height means a top-down DIB pinfo->bmiHeader.biPlanes = 1; pinfo->bmiHeader.biBitCount = BITS_PER_PIXEL; pinfo->bmiHeader.biCompression = BI_BITFIELDS; // Setup up color masks static const RGBQUAD redMask = {0, 0, 0xFF, 0}; static const RGBQUAD greenMask = {0, 0xFF, 0, 0}; static const RGBQUAD blueMask = {0xFF, 0, 0, 0}; pinfo->bmiColors[0] = redMask; pinfo->bmiColors[1] = greenMask; pinfo->bmiColors[2] = blueMask; // Get the bitmap data in device-independent, 32-bit packed pixel format ::GetDIBits(hdcMem, hbitmap, 0, height, pixelData, pinfo, DIB_RGB_COLORS); // convert Win32 pixel format (BGRX) to Java format (ARGB) DASSERT(sizeof(jint) == sizeof(RGBQUAD)); for(int nPixel = 0; nPixel < numPixels; nPixel++) { RGBQUAD * prgbq = &pixelData[nPixel]; jint jpixel = WinToJavaPixel(prgbq->rgbRed, prgbq->rgbGreen, prgbq->rgbBlue); // stuff the 32-bit pixel back into the 32-bit RGBQUAD *prgbq = *( (RGBQUAD *)(&jpixel) ); } // copy pixels into Java array env->SetIntArrayRegion(pixelArray, 0, numPixels, (jint *)pixelData); delete pinfo; // free all the GDI objects we made ::SelectObject(hdcMem, hOldBitmap); if (hOldPalette != NULL) { ::SelectPalette(hdcMem, hOldPalette, FALSE); } ::DeleteObject(hbitmap); ::DeleteDC(hdcMem); ::DeleteDC(hdcScreen); } void AwtRobot::KeyPress( jint jkey ) { DoKeyEvent(jkey, 0); // no flags means key down } void AwtRobot::KeyRelease( jint jkey ) { DoKeyEvent(jkey, KEYEVENTF_KEYUP); } void AwtRobot::DoKeyEvent( jint jkey, DWORD dwFlags ) { UINT vkey; UINT modifiers; UINT scancode; JNIEnv * env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); // convert Java key into Windows key (and modifiers too) AwtComponent::JavaKeyToWindowsKey(jkey, &vkey, &modifiers); if (vkey == 0) { // no equivalent Windows key found for given Java keycode JNU_ThrowIllegalArgumentException(env, "Invalid key code"); } else { // get the scancode from the virtual key scancode = ::MapVirtualKey(vkey, 0); keybd_event(vkey, scancode, dwFlags, 0); } } // // utility function to get the C++ object from the Java one // // (static) AwtRobot * AwtRobot::GetRobot( jobject self ) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); AwtRobot * robot = (AwtRobot *)JNI_GET_PDATA(self); DASSERT( !::IsBadWritePtr( robot, sizeof(AwtRobot))); return robot; } ////////////////////////////////////////////////////////////////////////////////////////////// // Native method declarations // JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_create( JNIEnv * env, jobject self) { TRY; new AwtRobot(self); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer__1dispose( JNIEnv *env, jobject self) { TRY_NO_VERIFY; AwtObject::_Dispose(self); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseMoveImpl( JNIEnv * env, jobject self, jint x, jint y) { TRY; AwtRobot::GetRobot(self)->MouseMove(x, y); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mousePress( JNIEnv * env, jobject self, jint buttons) { TRY; AwtRobot::GetRobot(self)->MousePress(buttons); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseRelease( JNIEnv * env, jobject self, jint buttons) { TRY; AwtRobot::GetRobot(self)->MouseRelease(buttons); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_mouseWheel( JNIEnv * env, jobject self, jint wheelAmt) { TRY; AwtRobot::GetRobot(self)->MouseWheel(wheelAmt); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_getRGBPixels( JNIEnv *env, jobject self, jint x, jint y, jint width, jint height, jintArray pixelArray) { TRY; AwtRobot::GetRobot(self)->GetRGBPixels(x, y, width, height, pixelArray); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyPress( JNIEnv *, jobject self, jint javakey ) { TRY; AwtRobot::GetRobot(self)->KeyPress(javakey); CATCH_BAD_ALLOC; } JNIEXPORT void JNICALL Java_sun_awt_windows_WRobotPeer_keyRelease( JNIEnv *, jobject self, jint javakey ) { TRY; AwtRobot::GetRobot(self)->KeyRelease(javakey); CATCH_BAD_ALLOC; }