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