1 /*
   2  * Copyright (c) 1999, 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 #ifdef HEADLESS
  27     #error This file should not be included in headless library
  28 #endif
  29 
  30 #include "awt_p.h"
  31 #include "awt_GraphicsEnv.h"
  32 #define XK_MISCELLANY
  33 #include <X11/keysymdef.h>
  34 #include <X11/Intrinsic.h>
  35 #include <X11/Xutil.h>
  36 #include <X11/Xmd.h>
  37 #include <X11/extensions/xtestext1.h>
  38 #include <X11/extensions/XTest.h>
  39 #include <X11/extensions/XInput.h>
  40 #include <X11/extensions/XI.h>
  41 #include <jni.h>
  42 #include <sizecalc.h>
  43 #include "robot_common.h"
  44 #include "canvas.h"
  45 #include "wsutils.h"
  46 #include "list.h"
  47 #include "multiVis.h"
  48 #if defined(__linux__) || defined(MACOSX)
  49 #include <sys/socket.h>
  50 #endif
  51 
  52 extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
  53 
  54 static jint * masks;
  55 static jint num_buttons;
  56 
  57 static int32_t isXTestAvailable() {
  58     int32_t major_opcode, first_event, first_error;
  59     int32_t  event_basep, error_basep, majorp, minorp;
  60     int32_t isXTestAvailable;
  61 
  62     /* check if XTest is available */
  63     isXTestAvailable = XQueryExtension(awt_display, XTestExtensionName, &major_opcode, &first_event, &first_error);
  64     DTRACE_PRINTLN3("RobotPeer: XQueryExtension(XTEST) returns major_opcode = %d, first_event = %d, first_error = %d",
  65                     major_opcode, first_event, first_error);
  66     if (isXTestAvailable) {
  67         /* check if XTest version is OK */
  68         XTestQueryExtension(awt_display, &event_basep, &error_basep, &majorp, &minorp);
  69         DTRACE_PRINTLN4("RobotPeer: XTestQueryExtension returns event_basep = %d, error_basep = %d, majorp = %d, minorp = %d",
  70                         event_basep, error_basep, majorp, minorp);
  71         if (majorp < 2 || (majorp == 2 && minorp < 2)) {
  72             /* bad version*/
  73             DTRACE_PRINTLN2("XRobotPeer: XTEST version is %d.%d \n", majorp, minorp);
  74             if (majorp == 2 && minorp == 1) {
  75                 DTRACE_PRINTLN("XRobotPeer: XTEST is 2.1 - no grab is available\n");
  76             } else {
  77                 isXTestAvailable = False;
  78             }
  79         } else {
  80             /* allow XTest calls even if someone else has the grab; e.g. during
  81              * a window resize operation. Works only with XTEST2.2*/
  82             XTestGrabControl(awt_display, True);
  83         }
  84     } else {
  85         DTRACE_PRINTLN("RobotPeer: XTEST extension is unavailable");
  86     }
  87 
  88     return isXTestAvailable;
  89 }
  90 
  91 
  92 static XImage *getWindowImage(Display * display, Window window,
  93                               int32_t x, int32_t y,
  94                               int32_t w, int32_t h) {
  95     XImage         *image;
  96     int32_t        transparentOverlays;
  97     int32_t        numVisuals;
  98     XVisualInfo    *pVisuals;
  99     int32_t        numOverlayVisuals;
 100     OverlayInfo    *pOverlayVisuals;
 101     int32_t        numImageVisuals;
 102     XVisualInfo    **pImageVisuals;
 103     list_ptr       vis_regions;    /* list of regions to read from */
 104     list_ptr       vis_image_regions ;
 105     int32_t        allImage = 0 ;
 106     int32_t        format = ZPixmap;
 107 
 108     /* prevent user from moving stuff around during the capture */
 109     XGrabServer(display);
 110 
 111     /*
 112      * The following two functions live in multiVis.c-- they are pretty
 113      * much verbatim taken from the source to the xwd utility from the
 114      * X11 source. This version of the xwd source was somewhat better written
 115      * for reuse compared to Sun's version.
 116      *
 117      *        ftp.x.org/pub/R6.3/xc/programs/xwd
 118      *
 119      * We use these functions since they do the very tough job of capturing
 120      * the screen correctly when it contains multiple visuals. They take into
 121      * account the depth/colormap of each visual and produce a capture as a
 122      * 24-bit RGB image so we don't have to fool around with colormaps etc.
 123      */
 124 
 125     GetMultiVisualRegions(
 126         display,
 127         window,
 128         x, y, w, h,
 129         &transparentOverlays,
 130         &numVisuals,
 131         &pVisuals,
 132         &numOverlayVisuals,
 133         &pOverlayVisuals,
 134         &numImageVisuals,
 135         &pImageVisuals,
 136         &vis_regions,
 137         &vis_image_regions,
 138         &allImage );
 139 
 140     image = ReadAreaToImage(
 141         display,
 142         window,
 143         x, y, w, h,
 144         numVisuals,
 145         pVisuals,
 146         numOverlayVisuals,
 147         pOverlayVisuals,
 148         numImageVisuals,
 149         pImageVisuals,
 150         vis_regions,
 151         vis_image_regions,
 152         format,
 153         allImage );
 154 
 155     /* allow user to do stuff again */
 156     XUngrabServer(display);
 157 
 158     /* make sure the grab/ungrab is flushed */
 159     XSync(display, False);
 160 
 161     return image;
 162 }
 163 
 164 /*********************************************************************************************/
 165 
 166 // this should be called from XRobotPeer constructor
 167 JNIEXPORT void JNICALL
 168 Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls, jint numberOfButtons, jintArray buttonDownMasks)
 169 {
 170     int32_t xtestAvailable;
 171     jint *tmp;
 172     int i;
 173 
 174     DTRACE_PRINTLN("RobotPeer: setup()");
 175 
 176     num_buttons = numberOfButtons;
 177     tmp = (*env)->GetIntArrayElements(env, buttonDownMasks, JNI_FALSE);
 178     CHECK_NULL(tmp);
 179 
 180     masks = (jint *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), num_buttons);
 181     if (masks == (jint *) NULL) {
 182         (*env)->ExceptionClear(env);
 183         (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
 184         JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL);
 185         return;
 186     }
 187     for (i = 0; i < num_buttons; i++) {
 188         masks[i] = tmp[i];
 189     }
 190     (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
 191 
 192     AWT_LOCK();
 193     xtestAvailable = isXTestAvailable();
 194     DTRACE_PRINTLN1("RobotPeer: XTest available = %d", xtestAvailable);
 195     if (!xtestAvailable) {
 196         JNU_ThrowByName(env, "java/awt/AWTException", "java.awt.Robot requires your X server support the XTEST extension version 2.2");
 197     }
 198 
 199     AWT_UNLOCK();
 200 }
 201 
 202 
 203 JNIEXPORT void JNICALL
 204 Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,
 205                              jclass cls,
 206                              jobject xgc,
 207                              jint x,
 208                              jint y,
 209                              jint width,
 210                              jint height,
 211                              jintArray pixelArray) {
 212 
 213     XImage *image;
 214     jint *ary;               /* Array of jints for sending pixel values back
 215                               * to parent process.
 216                               */
 217     Window rootWindow;
 218     AwtGraphicsConfigDataPtr adata;
 219 
 220     DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, x, y, width, height, pixelArray);
 221 
 222     AWT_LOCK();
 223 
 224     /* avoid a lot of work for empty rectangles */
 225     if ((width * height) == 0) {
 226         AWT_UNLOCK();
 227         return;
 228     }
 229     DASSERT(width * height > 0); /* only allow positive size */
 230 
 231     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
 232     DASSERT(adata != NULL);
 233 
 234     rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);
 235     image = getWindowImage(awt_display, rootWindow, x, y, width, height);
 236 
 237     /* Array to use to crunch around the pixel values */
 238     if (!IS_SAFE_SIZE_MUL(width, height) ||
 239         !(ary = (jint *) SAFE_SIZE_ARRAY_ALLOC(malloc, width * height, sizeof (jint))))
 240     {
 241         JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 242         XDestroyImage(image);
 243         AWT_UNLOCK();
 244         return;
 245     }
 246     /* convert to Java ARGB pixels */
 247     for (y = 0; y < height; y++) {
 248         for (x = 0; x < width; x++) {
 249             jint pixel = (jint) XGetPixel(image, x, y); /* Note ignore upper
 250                                                          * 32-bits on 64-bit
 251                                                          * OSes.
 252                                                          */
 253 
 254             pixel |= 0xff000000; /* alpha - full opacity */
 255 
 256             ary[(y * width) + x] = pixel;
 257         }
 258     }
 259     (*env)->SetIntArrayRegion(env, pixelArray, 0, height * width, ary);
 260     free(ary);
 261 
 262     XDestroyImage(image);
 263 
 264     AWT_UNLOCK();
 265 }
 266 
 267 JNIEXPORT void JNICALL
 268 Java_sun_awt_X11_XRobotPeer_keyPressImpl (JNIEnv *env,
 269                          jclass cls,
 270                          jint keycode) {
 271 
 272     AWT_LOCK();
 273 
 274     DTRACE_PRINTLN1("RobotPeer: keyPressImpl(%i)", keycode);
 275 
 276     XTestFakeKeyEvent(awt_display,
 277                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
 278                       True,
 279                       CurrentTime);
 280 
 281     XSync(awt_display, False);
 282 
 283     AWT_UNLOCK();
 284 
 285 }
 286 
 287 JNIEXPORT void JNICALL
 288 Java_sun_awt_X11_XRobotPeer_keyReleaseImpl (JNIEnv *env,
 289                            jclass cls,
 290                            jint keycode) {
 291     AWT_LOCK();
 292 
 293     DTRACE_PRINTLN1("RobotPeer: keyReleaseImpl(%i)", keycode);
 294 
 295     XTestFakeKeyEvent(awt_display,
 296                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
 297                       False,
 298                       CurrentTime);
 299 
 300     XSync(awt_display, False);
 301 
 302     AWT_UNLOCK();
 303 }
 304 
 305 JNIEXPORT void JNICALL
 306 Java_sun_awt_X11_XRobotPeer_mouseMoveImpl (JNIEnv *env,
 307                           jclass cls,
 308                           jobject xgc,
 309                           jint root_x,
 310                           jint root_y) {
 311 
 312     AwtGraphicsConfigDataPtr adata;
 313 
 314     AWT_LOCK();
 315 
 316     DTRACE_PRINTLN3("RobotPeer: mouseMoveImpl(%lx, %i, %i)", xgc, root_x, root_y);
 317 
 318     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
 319     DASSERT(adata != NULL);
 320 
 321     XWarpPointer(awt_display, None, XRootWindow(awt_display, adata->awt_visInfo.screen), 0, 0, 0, 0, root_x, root_y);
 322     XSync(awt_display, False);
 323 
 324     AWT_UNLOCK();
 325 }
 326 
 327 /*
 328   * Function joining the code of mousePressImpl and mouseReleaseImpl
 329   */
 330 void mouseAction(JNIEnv *env,
 331                  jclass cls,
 332                  jint buttonMask,
 333                  Bool isMousePress)
 334 {
 335     AWT_LOCK();
 336 
 337     DTRACE_PRINTLN1("RobotPeer: mouseAction(%i)", buttonMask);
 338     DTRACE_PRINTLN1("RobotPeer: mouseAction, press = %d", isMousePress);
 339 
 340     if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 341         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK )
 342     {
 343         XTestFakeButtonEvent(awt_display, 1, isMousePress, CurrentTime);
 344     }
 345     if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 346          buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) &&
 347         (num_buttons >= 2)) {
 348         XTestFakeButtonEvent(awt_display, 2, isMousePress, CurrentTime);
 349     }
 350     if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 351          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) &&
 352         (num_buttons >= 3)) {
 353         XTestFakeButtonEvent(awt_display, 3, isMousePress, CurrentTime);
 354     }
 355 
 356     if (num_buttons > 3){
 357         int32_t i;
 358         int32_t button = 0;
 359         for (i = 3; i<num_buttons; i++){
 360             if ((buttonMask & masks[i])) {
 361                 // arrays starts from zero index => +1
 362                 // users wants to affect 4 or 5 button but they are assigned
 363                 // to the wheel so => we have to shift it to the right by 2.
 364                 button = i + 3;
 365                 XTestFakeButtonEvent(awt_display, button, isMousePress, CurrentTime);
 366             }
 367         }
 368     }
 369 
 370     XSync(awt_display, False);
 371     AWT_UNLOCK();
 372 }
 373 
 374 JNIEXPORT void JNICALL
 375 Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,
 376                            jclass cls,
 377                            jint buttonMask) {
 378     mouseAction(env, cls, buttonMask, True);
 379 }
 380 
 381 JNIEXPORT void JNICALL
 382 Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl (JNIEnv *env,
 383                              jclass cls,
 384                              jint buttonMask) {
 385     mouseAction(env, cls, buttonMask, False);
 386 }
 387 
 388 JNIEXPORT void JNICALL
 389 Java_sun_awt_X11_XRobotPeer_mouseWheelImpl (JNIEnv *env,
 390                            jclass cls,
 391                            jint wheelAmt) {
 392 /* Mouse wheel is implemented as a button press of button 4 and 5, so it */
 393 /* probably could have been hacked into robot_mouseButtonEvent, but it's */
 394 /* cleaner to give it its own command type, in case the implementation   */
 395 /* needs to be changed later.  -bchristi, 6/20/01                        */
 396 
 397     int32_t repeat = abs(wheelAmt);
 398     int32_t button = wheelAmt < 0 ? 4 : 5;  /* wheel up:   button 4 */
 399                                                  /* wheel down: button 5 */
 400     int32_t loopIdx;
 401 
 402     AWT_LOCK();
 403 
 404     DTRACE_PRINTLN1("RobotPeer: mouseWheelImpl(%i)", wheelAmt);
 405 
 406     for (loopIdx = 0; loopIdx < repeat; loopIdx++) { /* do nothing for   */
 407                                                      /* wheelAmt == 0    */
 408         XTestFakeButtonEvent(awt_display, button, True, CurrentTime);
 409         XTestFakeButtonEvent(awt_display, button, False, CurrentTime);
 410     }
 411     XSync(awt_display, False);
 412 
 413     AWT_UNLOCK();
 414 }