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 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 42 AwtRobot::~AwtRobot() 43 { 44 } 45 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 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 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 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 147 void AwtRobot::MouseWheel (jint wheelAmt) { 148 mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0); 149 } 150 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 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 258 void AwtRobot::KeyPress( jint jkey ) 259 { 260 DoKeyEvent(jkey, 0); // no flags means key down 261 } 262 263 void AwtRobot::KeyRelease( jint jkey ) 264 { 265 DoKeyEvent(jkey, KEYEVENTF_KEYUP); 266 } 267 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) 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 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 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 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 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 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 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 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 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 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 }