/* * Copyright (c) 1998, 2017, 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); // copy screen image to offscreen bitmap // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents // correctly on Win2K/XP VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y, 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) { /* vkey would be 0 for all non-ascii inputs. If non-ascii then we assume they are unicode characters and will supply such input to SendInput() which can handle unicode characters as well as ascii chars. Windows provides api's to handle ascii and unicode characters. All ascii characters (both OEM and standard ascii) would be supplied to keybd_event(). HandleUnicodeKeys() returns the status of the SendInput() which returns 0 if there is a fail else non-zero. The status 0 tells that the SendInput() in unable to interpret the supplied input upon which the illegal argument exception would be raised. */ if(!HandleUnicodeKeys(jkey, dwFlags)) { // 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); if (vkey == VK_RMENU || vkey == VK_DELETE || vkey == VK_INSERT || vkey == VK_NEXT || vkey == VK_PRIOR || vkey == VK_HOME || vkey == VK_END || vkey == VK_LEFT || vkey == VK_RIGHT || vkey == VK_UP || vkey == VK_DOWN) { dwFlags |= KEYEVENTF_EXTENDEDKEY; } keybd_event(vkey, scancode, dwFlags, 0); } } UINT AwtRobot::HandleUnicodeKeys(jint key, DWORD dwFlags) { NSWinInput::INPUT ip; ip.type = 1; //INPUT_KEYBOARD; ip.ki.wVk = 0; ip.ki.wScan = key; ip.ki.dwFlags = (DWORD)(dwFlags | 4); //KEYEVENTF_UNICODE(4) ip.ki.dwExtraInfo = 0; return SendInput(1, (LPINPUT)&ip, sizeof(INPUT)); } // // 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; }