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