1 /*
   2  * Copyright (c) 1999, 2010, 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_Component.h"
  32 #include "awt_GraphicsEnv.h"
  33 #define XK_MISCELLANY
  34 #include <X11/keysymdef.h>
  35 #include <X11/Intrinsic.h>
  36 #include <X11/Xutil.h>
  37 #include <X11/Xmd.h>
  38 #include <X11/extensions/xtestext1.h>
  39 #include <X11/extensions/XTest.h>
  40 #include <X11/extensions/XInput.h>
  41 #include <X11/extensions/XI.h>
  42 #include <jni.h>
  43 #include "robot_common.h"
  44 #include "canvas.h"
  45 #include "wsutils.h"
  46 #include "list.h"
  47 #include "multiVis.h"
  48 #ifdef __linux__
  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     masks = (jint *)malloc(sizeof(jint) * num_buttons);
 179     if (masks == (jint *) NULL) {
 180         JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL);
 181         (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
 182         return;
 183     }
 184     for (i = 0; i < num_buttons; i++) {
 185         masks[i] = tmp[i];
 186     }
 187     (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
 188 
 189     AWT_LOCK();
 190     xtestAvailable = isXTestAvailable();
 191     DTRACE_PRINTLN1("RobotPeer: XTest available = %d", xtestAvailable);
 192     if (!xtestAvailable) {
 193         JNU_ThrowByName(env, "java/awt/AWTException", "java.awt.Robot requires your X server support the XTEST extension version 2.2");
 194     }
 195 
 196     AWT_UNLOCK();
 197 }
 198 
 199 
 200 JNIEXPORT void JNICALL
 201 Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,
 202                              jclass cls,
 203                              jobject xgc,
 204                              jint x,
 205                              jint y,
 206                              jint width,
 207                              jint height,
 208                              jintArray pixelArray) {
 209 
 210     XImage *image;
 211     jint *ary;               /* Array of jints for sending pixel values back
 212                               * to parent process.
 213                               */
 214     Window rootWindow;
 215     AwtGraphicsConfigDataPtr adata;
 216 
 217     DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, x, y, width, height, pixelArray);
 218 
 219     AWT_LOCK();
 220 
 221     /* avoid a lot of work for empty rectangles */
 222     if ((width * height) == 0) {
 223         AWT_UNLOCK();
 224         return;
 225     }
 226     DASSERT(width * height > 0); /* only allow positive size */
 227 
 228     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
 229     DASSERT(adata != NULL);
 230 
 231     rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);
 232     image = getWindowImage(awt_display, rootWindow, x, y, width, height);
 233 
 234     /* Array to use to crunch around the pixel values */
 235     ary = (jint *) malloc(width * height * sizeof (jint));
 236     if (ary == NULL) {
 237         JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 238         XDestroyImage(image);
 239         AWT_UNLOCK();
 240         return;
 241     }
 242     /* convert to Java ARGB pixels */
 243     for (y = 0; y < height; y++) {
 244         for (x = 0; x < width; x++) {
 245             jint pixel = (jint) XGetPixel(image, x, y); /* Note ignore upper
 246                                                          * 32-bits on 64-bit
 247                                                          * OSes.
 248                                                          */
 249 
 250             pixel |= 0xff000000; /* alpha - full opacity */
 251 
 252             ary[(y * width) + x] = pixel;
 253         }
 254     }
 255     (*env)->SetIntArrayRegion(env, pixelArray, 0, height * width, ary);
 256     free(ary);
 257 
 258     XDestroyImage(image);
 259 
 260     AWT_UNLOCK();
 261 }
 262 
 263 JNIEXPORT void JNICALL
 264 Java_sun_awt_X11_XRobotPeer_keyPressImpl (JNIEnv *env,
 265                          jclass cls,
 266                          jint keycode) {
 267 
 268     AWT_LOCK();
 269 
 270     DTRACE_PRINTLN1("RobotPeer: keyPressImpl(%i)", keycode);
 271 
 272     XTestFakeKeyEvent(awt_display,
 273                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
 274                       True,
 275                       CurrentTime);
 276 
 277     XSync(awt_display, False);
 278 
 279     AWT_UNLOCK();
 280 
 281 }
 282 
 283 JNIEXPORT void JNICALL
 284 Java_sun_awt_X11_XRobotPeer_keyReleaseImpl (JNIEnv *env,
 285                            jclass cls,
 286                            jint keycode) {
 287     AWT_LOCK();
 288 
 289     DTRACE_PRINTLN1("RobotPeer: keyReleaseImpl(%i)", keycode);
 290 
 291     XTestFakeKeyEvent(awt_display,
 292                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
 293                       False,
 294                       CurrentTime);
 295 
 296     XSync(awt_display, False);
 297 
 298     AWT_UNLOCK();
 299 }
 300 
 301 JNIEXPORT void JNICALL
 302 Java_sun_awt_X11_XRobotPeer_mouseMoveImpl (JNIEnv *env,
 303                           jclass cls,
 304                           jobject xgc,
 305                           jint root_x,
 306                           jint root_y) {
 307 
 308     AwtGraphicsConfigDataPtr adata;
 309 
 310     AWT_LOCK();
 311 
 312     DTRACE_PRINTLN3("RobotPeer: mouseMoveImpl(%lx, %i, %i)", xgc, root_x, root_y);
 313 
 314     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
 315     DASSERT(adata != NULL);
 316 
 317     XWarpPointer(awt_display, None, XRootWindow(awt_display, adata->awt_visInfo.screen), 0, 0, 0, 0, root_x, root_y);
 318     XSync(awt_display, False);
 319 
 320     AWT_UNLOCK();
 321 }
 322 
 323 /*
 324   * Function joining the code of mousePressImpl and mouseReleaseImpl
 325   */
 326 void mouseAction(JNIEnv *env,
 327                  jclass cls,
 328                  jint buttonMask,
 329                  Bool isMousePress)
 330 {
 331     AWT_LOCK();
 332 
 333     DTRACE_PRINTLN1("RobotPeer: mouseAction(%i)", buttonMask);
 334     DTRACE_PRINTLN1("RobotPeer: mouseAction, press = %d", isMousePress);
 335 
 336     if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
 337         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK )
 338     {
 339         XTestFakeButtonEvent(awt_display, 1, isMousePress, CurrentTime);
 340     }
 341     if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
 342          buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) &&
 343         (num_buttons >= 2)) {
 344         XTestFakeButtonEvent(awt_display, 2, isMousePress, CurrentTime);
 345     }
 346     if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
 347          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) &&
 348         (num_buttons >= 3)) {
 349         XTestFakeButtonEvent(awt_display, 3, isMousePress, CurrentTime);
 350     }
 351 
 352     if (num_buttons > 3){
 353         int32_t i;
 354         int32_t button = 0;
 355         for (i = 3; i<num_buttons; i++){
 356             if ((buttonMask & masks[i])) {
 357                 // arrays starts from zero index => +1
 358                 // users wants to affect 4 or 5 button but they are assigned
 359                 // to the wheel so => we have to shift it to the right by 2.
 360                 button = i + 3;
 361                 XTestFakeButtonEvent(awt_display, button, isMousePress, CurrentTime);
 362             }
 363         }
 364     }
 365 
 366     XSync(awt_display, False);
 367     AWT_UNLOCK();
 368 }
 369 
 370 JNIEXPORT void JNICALL
 371 Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,
 372                            jclass cls,
 373                            jint buttonMask) {
 374     mouseAction(env, cls, buttonMask, True);
 375 }
 376 
 377 JNIEXPORT void JNICALL
 378 Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl (JNIEnv *env,
 379                              jclass cls,
 380                              jint buttonMask) {
 381     mouseAction(env, cls, buttonMask, False);
 382 }
 383 
 384 JNIEXPORT void JNICALL
 385 Java_sun_awt_X11_XRobotPeer_mouseWheelImpl (JNIEnv *env,
 386                            jclass cls,
 387                            jint wheelAmt) {
 388 /* Mouse wheel is implemented as a button press of button 4 and 5, so it */
 389 /* probably could have been hacked into robot_mouseButtonEvent, but it's */
 390 /* cleaner to give it its own command type, in case the implementation   */
 391 /* needs to be changed later.  -bchristi, 6/20/01                        */
 392 
 393     int32_t repeat = abs(wheelAmt);
 394     int32_t button = wheelAmt < 0 ? 4 : 5;  /* wheel up:   button 4 */
 395                                                  /* wheel down: button 5 */
 396     int32_t loopIdx;
 397 
 398     AWT_LOCK();
 399 
 400     DTRACE_PRINTLN1("RobotPeer: mouseWheelImpl(%i)", wheelAmt);
 401 
 402     for (loopIdx = 0; loopIdx < repeat; loopIdx++) { /* do nothing for   */
 403                                                      /* wheelAmt == 0    */
 404         XTestFakeButtonEvent(awt_display, button, True, CurrentTime);
 405         XTestFakeButtonEvent(awt_display, button, False, CurrentTime);
 406     }
 407     XSync(awt_display, False);
 408 
 409     AWT_UNLOCK();
 410 }