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