blob: 7739af44b7a1d45517fb559aa9068ceafed22bf2 [file] [log] [blame]
/*
* 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 <winuser.h>
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);
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) {
// 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;
}