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