1 /* 2 * Copyright (c) 2011, 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 "common.h" 27 #include "math.h" 28 29 #include "KeyTable.h" 30 31 #include "com_sun_glass_ui_Robot.h" 32 #include "com_sun_glass_ui_win_WinRobot.h" 33 #include "GlassScreen.h" 34 35 36 static BOOL KeyEvent(JNIEnv *env, int code, bool isPress) { 37 UINT vkey, modifiers; 38 39 JavaKeyToWindowsKey(code, vkey, modifiers); 40 41 if (!vkey) { 42 return FALSE; 43 } else { 44 UINT scancode = ::MapVirtualKey(vkey, 0); 45 46 INPUT keyInput = {0}; 47 keyInput.type = INPUT_KEYBOARD; 48 keyInput.ki.wVk = vkey; 49 keyInput.ki.wScan = scancode; 50 keyInput.ki.time = 0; 51 keyInput.ki.dwExtraInfo = 0; 52 keyInput.ki.dwFlags = isPress ? 0 : KEYEVENTF_KEYUP; 53 if (IsExtendedKey(vkey)) { 54 keyInput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 55 } 56 57 ::SendInput(1, &keyInput, sizeof(keyInput)); 58 59 return TRUE; 60 } 61 } 62 63 inline static jint WinToJavaPixel(USHORT r, USHORT g, USHORT b) 64 { 65 jint value = 66 0xFF << 24 | // alpha channel is always turned all the way up 67 r << 16 | 68 g << 8 | 69 b << 0; 70 return value; 71 } 72 73 extern "C" { 74 75 /* 76 * Class: com_sun_glass_ui_win_WinRobot 77 * Method: _keyPress 78 * Signature: (I)V 79 */ 80 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1keyPress 81 (JNIEnv *env, jobject jrobot, jint code) 82 { 83 KeyEvent(env, code, true); 84 } 85 86 /* 87 * Class: com_sun_glass_ui_win_WinRobot 88 * Method: _keyRelease 89 * Signature: (I)V 90 */ 91 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1keyRelease 92 (JNIEnv *env, jobject jrobot, jint code) 93 { 94 KeyEvent(env, code, false); 95 } 96 97 static int signum(int i) { 98 // special version of signum which returns 1 when value is 0 99 return i >= 0 ? 1 : -1; 100 } 101 102 /* 103 * Class: com_sun_glass_ui_win_WinRobot 104 * Method: _mouseMove 105 * Signature: (II)V 106 */ 107 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseMove 108 (JNIEnv *env, jobject jrobot, jint x, jint y) 109 { 110 jfloat fx = (jfloat) x + 0.5f; 111 jfloat fy = (jfloat) y + 0.5f; 112 GlassScreen::FX2Win(&fx, &fy); 113 INPUT mouseInput = {0}; 114 mouseInput.type = INPUT_MOUSE; 115 mouseInput.mi.time = 0; 116 mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; 117 mouseInput.mi.dx = (jint)(fx * 65535.0 / ::GetSystemMetrics(SM_CXSCREEN)) + signum((int)fx); 118 mouseInput.mi.dy = (jint)(fy * 65535.0 / ::GetSystemMetrics(SM_CYSCREEN)) + signum((int)fy); 119 ::SendInput(1, &mouseInput, sizeof(mouseInput)); 120 } 121 122 /* 123 * Class: com_sun_glass_ui_win_WinRobot 124 * Method: _getMouseX 125 * Signature: ()I 126 */ 127 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getMouseX 128 (JNIEnv *env, jobject jrobot) 129 { 130 POINT curPos; 131 ::GetCursorPos(&curPos); 132 jfloat fx = (jfloat) curPos.x + 0.5f; 133 jfloat fy = (jfloat) curPos.y + 0.5f; 134 GlassScreen::Win2FX(&fx, &fy); 135 return (jint) fx; 136 } 137 138 /* 139 * Class: com_sun_glass_ui_win_WinRobot 140 * Method: _getMouseY 141 * Signature: ()I 142 */ 143 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getMouseY 144 (JNIEnv *env, jobject jrobot) 145 { 146 POINT curPos; 147 ::GetCursorPos(&curPos); 148 jfloat fx = (jfloat) curPos.x + 0.5f; 149 jfloat fy = (jfloat) curPos.y + 0.5f; 150 GlassScreen::Win2FX(&fx, &fy); 151 return (jint) fy; 152 } 153 154 /* 155 * Class: com_sun_glass_ui_win_WinRobot 156 * Method: _mousePress 157 * Signature: (I)V 158 */ 159 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mousePress 160 (JNIEnv *env, jobject jrobot, jint buttons) 161 { 162 DWORD dwFlags = 0L; 163 // According to MSDN: Software Driving Software 164 // application should consider SM_SWAPBUTTON to correctly emulate user with 165 // left handed mouse setup 166 BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON); 167 168 if (buttons & (1 << 0)) { 169 dwFlags |= !bSwap ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN; 170 } 171 if (buttons & (1 << 1)) { 172 dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN; 173 } 174 if (buttons & (1 << 2)) { 175 dwFlags |= MOUSEEVENTF_MIDDLEDOWN; 176 } 177 178 INPUT mouseInput = {0}; 179 mouseInput.type = INPUT_MOUSE; 180 mouseInput.mi.time = 0; 181 mouseInput.mi.dwFlags = dwFlags; 182 183 // Support for extra buttons 184 if (buttons & (1 << 3)) { 185 mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN; 186 mouseInput.mi.mouseData = XBUTTON1; 187 } 188 if (buttons & (1 << 4)) { 189 mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN; 190 mouseInput.mi.mouseData = XBUTTON2; 191 } 192 193 ::SendInput(1, &mouseInput, sizeof(mouseInput)); 194 } 195 196 /* 197 * Class: com_sun_glass_ui_win_WinRobot 198 * Method: _mouseRelease 199 * Signature: (I)V 200 */ 201 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseRelease 202 (JNIEnv *env, jobject jrobot, jint buttons) 203 { 204 DWORD dwFlags = 0L; 205 // According to MSDN: Software Driving Software 206 // application should consider SM_SWAPBUTTON to correctly emulate user with 207 // left handed mouse setup 208 BOOL bSwap = ::GetSystemMetrics(SM_SWAPBUTTON); 209 210 if (buttons & (1 << 0)) { 211 dwFlags |= !bSwap ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP; 212 } 213 if (buttons & (1 << 1)) { 214 dwFlags |= !bSwap ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP; 215 } 216 if (buttons & (1 << 2)) { 217 dwFlags |= MOUSEEVENTF_MIDDLEUP; 218 } 219 220 INPUT mouseInput = {0}; 221 mouseInput.type = INPUT_MOUSE; 222 mouseInput.mi.time = 0; 223 mouseInput.mi.dwFlags = dwFlags; 224 225 // Support for extra buttons 226 if (buttons & (1 << 3)) { 227 mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP; 228 mouseInput.mi.mouseData = XBUTTON1; 229 } 230 if (buttons & (1 << 4)) { 231 mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP; 232 mouseInput.mi.mouseData = XBUTTON2; 233 } 234 235 ::SendInput(1, &mouseInput, sizeof(mouseInput)); 236 } 237 238 /* 239 * Class: com_sun_glass_ui_win_WinRobot 240 * Method: _mouseWheel 241 * Signature: (I)V 242 */ 243 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1mouseWheel 244 (JNIEnv *env, jobject jrobot, jint wheelAmt) 245 { 246 ::mouse_event(MOUSEEVENTF_WHEEL, 0, 0, wheelAmt * -1 * WHEEL_DELTA, 0); 247 } 248 249 void GetScreenCapture(jint x, jint y, jint devw, jint devh, 250 jint *pixelData, jint retw, jint reth); 251 252 /* 253 * Class: com_sun_glass_ui_win_WinRobot 254 * Method: _getPixelColor 255 * Signature: (II)I 256 */ 257 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinRobot__1getPixelColor 258 (JNIEnv *env, jobject jrobot, jint x, jint y) 259 { 260 jfloat fx = (jfloat) x + 0.5f; 261 jfloat fy = (jfloat) y + 0.5f; 262 GlassScreen::FX2Win(&fx, &fy); 263 jint dx = (jint) fx; 264 jint dy = (jint) fy; 265 266 jint val = 0; 267 //NOTE: we don't use the ::GetPixel() on the screen DC because it's not capable of 268 // getting the correct colors when non-opaque windows are present 269 GetScreenCapture(dx, dy, 1, 1, &val, 1, 1); 270 return val; 271 } 272 273 /* 274 * Class: com_sun_glass_ui_win_WinRobot 275 * Method: _getScreenCapture 276 * Signature: (IIII[I;)V 277 */ 278 JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinRobot__1getScreenCapture 279 (JNIEnv *env, jobject jrobot, jint x, jint y, jint width, jint height, jintArray pixelArray) 280 { 281 int numPixels = width * height; 282 int pixelDataSize = sizeof(jint) * numPixels; 283 ASSERT(pixelDataSize > 0 && pixelDataSize % 4 == 0); 284 285 jint * pixelData = (jint *)(new BYTE[pixelDataSize]); 286 287 if (pixelData) { 288 GetScreenCapture(x, y, width, height, pixelData, width, height); 289 290 // copy pixels into Java array 291 env->SetIntArrayRegion(pixelArray, 0, numPixels, pixelData); 292 delete[] pixelData; 293 } 294 } 295 296 void GetScreenCapture(jint x, jint y, jint devw, jint devh, 297 jint *pixelData, jint retw, jint reth) 298 { 299 HDC hdcScreen = ::CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL); 300 HDC hdcMem = ::CreateCompatibleDC(hdcScreen); 301 HBITMAP hbitmap; 302 HBITMAP hOldBitmap; 303 HPALETTE hOldPalette = NULL; 304 305 // create an offscreen bitmap 306 hbitmap = ::CreateCompatibleBitmap(hdcScreen, retw, reth); 307 if (hbitmap == NULL) { 308 //TODO: OOM might be better? 309 //throw std::bad_alloc(); 310 } 311 hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hbitmap); 312 313 /* TODO: check this out 314 // REMIND: not multimon-friendly... 315 int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex(); 316 hOldPalette = 317 AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex); 318 AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex); 319 */ 320 321 // copy screen image to offscreen bitmap 322 // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents 323 // correctly on Win2K/XP 324 static const DWORD dwRop = SRCCOPY|CAPTUREBLT; 325 if (retw == devw && reth == devh) { 326 ::BitBlt(hdcMem, 0, 0, retw, reth, hdcScreen, x, y, dwRop); 327 } else { 328 ::StretchBlt(hdcMem, 0, 0, retw, reth, hdcScreen, x, y, devw, devh, dwRop); 329 } 330 331 static const int BITS_PER_PIXEL = 32; 332 333 struct { 334 BITMAPINFOHEADER bmiHeader; 335 RGBQUAD bmiColors[3]; 336 } BitmapInfo; 337 338 // prepare BITMAPINFO for a 32-bit RGB bitmap 339 ::memset(&BitmapInfo, 0, sizeof(BitmapInfo)); 340 BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 341 BitmapInfo.bmiHeader.biWidth = retw; 342 BitmapInfo.bmiHeader.biHeight = -reth; // negative height means a top-down DIB 343 BitmapInfo.bmiHeader.biPlanes = 1; 344 BitmapInfo.bmiHeader.biBitCount = BITS_PER_PIXEL; 345 BitmapInfo.bmiHeader.biCompression = BI_BITFIELDS; 346 347 // Setup up color masks 348 static const RGBQUAD redMask = {0, 0, 0xFF, 0}; 349 static const RGBQUAD greenMask = {0, 0xFF, 0, 0}; 350 static const RGBQUAD blueMask = {0xFF, 0, 0, 0}; 351 352 BitmapInfo.bmiColors[0] = redMask; 353 BitmapInfo.bmiColors[1] = greenMask; 354 BitmapInfo.bmiColors[2] = blueMask; 355 356 // Get the bitmap data in device-independent, 32-bit packed pixel format 357 ::GetDIBits(hdcMem, hbitmap, 0, reth, pixelData, (BITMAPINFO *)&BitmapInfo, DIB_RGB_COLORS); 358 359 // convert Win32 pixel format (BGRX) to Java format (ARGB) 360 ASSERT(sizeof(jint) == sizeof(RGBQUAD)); 361 jint numPixels = retw * reth; 362 jint *pPixel = pixelData; 363 for(int nPixel = 0; nPixel < numPixels; nPixel++) { 364 RGBQUAD * prgbq = (RGBQUAD *) pPixel; 365 *pPixel++ = WinToJavaPixel(prgbq->rgbRed, prgbq->rgbGreen, prgbq->rgbBlue); 366 } 367 368 // free all the GDI objects we made 369 ::SelectObject(hdcMem, hOldBitmap); 370 if (hOldPalette != NULL) { 371 ::SelectPalette(hdcMem, hOldPalette, FALSE); 372 } 373 ::DeleteObject(hbitmap); 374 ::DeleteDC(hdcMem); 375 ::DeleteDC(hdcScreen); 376 } 377 378 } 379