src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c
changeset 47216 71c04702a3d5
parent 44130 f3516efccffa
child 52542 8c7638601045
child 56230 489867818774
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1999, 2017, 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 "jvm_md.h"
       
    31 #include <dlfcn.h>
       
    32 
       
    33 #include "awt_p.h"
       
    34 #include "awt_GraphicsEnv.h"
       
    35 #define XK_MISCELLANY
       
    36 #include <X11/keysymdef.h>
       
    37 #include <X11/Xutil.h>
       
    38 #include <X11/Xmd.h>
       
    39 #include <X11/extensions/xtestext1.h>
       
    40 #include <X11/extensions/XTest.h>
       
    41 #include <X11/extensions/XInput.h>
       
    42 #include <X11/extensions/XI.h>
       
    43 #include <jni.h>
       
    44 #include <sizecalc.h>
       
    45 #include "robot_common.h"
       
    46 #include "canvas.h"
       
    47 #include "wsutils.h"
       
    48 #include "list.h"
       
    49 #include "multiVis.h"
       
    50 #include "gtk_interface.h"
       
    51 
       
    52 #if defined(__linux__) || defined(MACOSX)
       
    53 #include <sys/socket.h>
       
    54 #endif
       
    55 
       
    56 static Bool   (*compositeQueryExtension)   (Display*, int*, int*);
       
    57 static Status (*compositeQueryVersion)     (Display*, int*, int*);
       
    58 static Window (*compositeGetOverlayWindow) (Display *, Window);
       
    59 
       
    60 extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
       
    61 
       
    62 static jint * masks;
       
    63 static jint num_buttons;
       
    64 
       
    65 static void *xCompositeHandle;
       
    66 
       
    67 static const char* XCOMPOSITE = JNI_LIB_NAME("Xcomposite");
       
    68 static const char* XCOMPOSITE_VERSIONED = VERSIONED_JNI_LIB_NAME("Xcomposite", "1");
       
    69 
       
    70 static Bool checkXCompositeFunctions(void) {
       
    71     return (compositeQueryExtension   != NULL   &&
       
    72             compositeQueryVersion     != NULL   &&
       
    73             compositeGetOverlayWindow != NULL);
       
    74 }
       
    75 
       
    76 static void initXCompositeFunctions(void) {
       
    77 
       
    78     if (xCompositeHandle == NULL) {
       
    79         xCompositeHandle = dlopen(XCOMPOSITE, RTLD_LAZY | RTLD_GLOBAL);
       
    80         if (xCompositeHandle == NULL) {
       
    81             xCompositeHandle = dlopen(XCOMPOSITE_VERSIONED, RTLD_LAZY | RTLD_GLOBAL);
       
    82         }
       
    83     }
       
    84     //*(void **)(&asyncGetCallTraceFunction)
       
    85     if (xCompositeHandle != NULL) {
       
    86         *(void **)(&compositeQueryExtension) = dlsym(xCompositeHandle, "XCompositeQueryExtension");
       
    87         *(void **)(&compositeQueryVersion) = dlsym(xCompositeHandle, "XCompositeQueryVersion");
       
    88         *(void **)(&compositeGetOverlayWindow) = dlsym(xCompositeHandle, "XCompositeGetOverlayWindow");
       
    89     }
       
    90 
       
    91     if (xCompositeHandle && !checkXCompositeFunctions()) {
       
    92         dlclose(xCompositeHandle);
       
    93     }
       
    94 }
       
    95 
       
    96 static int32_t isXTestAvailable() {
       
    97     int32_t major_opcode, first_event, first_error;
       
    98     int32_t  event_basep, error_basep, majorp, minorp;
       
    99     int32_t isXTestAvailable;
       
   100 
       
   101     /* check if XTest is available */
       
   102     isXTestAvailable = XQueryExtension(awt_display, XTestExtensionName, &major_opcode, &first_event, &first_error);
       
   103     if (isXTestAvailable) {
       
   104         DTRACE_PRINTLN3("RobotPeer: XQueryExtension(XTEST) returns major_opcode = %d, first_event = %d, first_error = %d",
       
   105                         major_opcode, first_event, first_error);
       
   106         /* check if XTest version is OK */
       
   107         XTestQueryExtension(awt_display, &event_basep, &error_basep, &majorp, &minorp);
       
   108         DTRACE_PRINTLN4("RobotPeer: XTestQueryExtension returns event_basep = %d, error_basep = %d, majorp = %d, minorp = %d",
       
   109                         event_basep, error_basep, majorp, minorp);
       
   110         if (majorp < 2 || (majorp == 2 && minorp < 2)) {
       
   111             /* bad version*/
       
   112             DTRACE_PRINTLN2("XRobotPeer: XTEST version is %d.%d \n", majorp, minorp);
       
   113             if (majorp == 2 && minorp == 1) {
       
   114                 DTRACE_PRINTLN("XRobotPeer: XTEST is 2.1 - no grab is available\n");
       
   115             } else {
       
   116                 isXTestAvailable = False;
       
   117             }
       
   118         } else {
       
   119             /* allow XTest calls even if someone else has the grab; e.g. during
       
   120              * a window resize operation. Works only with XTEST2.2*/
       
   121             XTestGrabControl(awt_display, True);
       
   122         }
       
   123     } else {
       
   124         DTRACE_PRINTLN("RobotPeer: XTEST extension is unavailable");
       
   125     }
       
   126 
       
   127     return isXTestAvailable;
       
   128 }
       
   129 
       
   130 static Bool hasXCompositeOverlayExtension(Display *display) {
       
   131 
       
   132     int xoverlay = False;
       
   133     int eventBase, errorBase;
       
   134     if (checkXCompositeFunctions() &&
       
   135         compositeQueryExtension(display, &eventBase, &errorBase))
       
   136     {
       
   137         int major = 0;
       
   138         int minor = 0;
       
   139 
       
   140         compositeQueryVersion(display, &major, &minor);
       
   141         if (major > 0 || minor >= 3) {
       
   142             xoverlay = True;
       
   143         }
       
   144     }
       
   145 
       
   146     return xoverlay;
       
   147 }
       
   148 
       
   149 static jboolean isXCompositeDisplay(Display *display, int screenNumber) {
       
   150 
       
   151     char NET_WM_CM_Sn[25];
       
   152     snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d\0", screenNumber);
       
   153 
       
   154     Atom managerSelection = XInternAtom(display, NET_WM_CM_Sn, 0);
       
   155     Window owner = XGetSelectionOwner(display, managerSelection);
       
   156 
       
   157     return owner != 0;
       
   158 }
       
   159 
       
   160 static XImage *getWindowImage(Display * display, Window window,
       
   161                               int32_t x, int32_t y,
       
   162                               int32_t w, int32_t h) {
       
   163     XImage         *image;
       
   164     int32_t        transparentOverlays;
       
   165     int32_t        numVisuals;
       
   166     XVisualInfo    *pVisuals;
       
   167     int32_t        numOverlayVisuals;
       
   168     OverlayInfo    *pOverlayVisuals;
       
   169     int32_t        numImageVisuals;
       
   170     XVisualInfo    **pImageVisuals;
       
   171     list_ptr       vis_regions;    /* list of regions to read from */
       
   172     list_ptr       vis_image_regions ;
       
   173     int32_t        allImage = 0 ;
       
   174     int32_t        format = ZPixmap;
       
   175 
       
   176     /* prevent user from moving stuff around during the capture */
       
   177     XGrabServer(display);
       
   178 
       
   179     /*
       
   180      * The following two functions live in multiVis.c-- they are pretty
       
   181      * much verbatim taken from the source to the xwd utility from the
       
   182      * X11 source. This version of the xwd source was somewhat better written
       
   183      * for reuse compared to Sun's version.
       
   184      *
       
   185      *        ftp.x.org/pub/R6.3/xc/programs/xwd
       
   186      *
       
   187      * We use these functions since they do the very tough job of capturing
       
   188      * the screen correctly when it contains multiple visuals. They take into
       
   189      * account the depth/colormap of each visual and produce a capture as a
       
   190      * 24-bit RGB image so we don't have to fool around with colormaps etc.
       
   191      */
       
   192 
       
   193     GetMultiVisualRegions(
       
   194         display,
       
   195         window,
       
   196         x, y, w, h,
       
   197         &transparentOverlays,
       
   198         &numVisuals,
       
   199         &pVisuals,
       
   200         &numOverlayVisuals,
       
   201         &pOverlayVisuals,
       
   202         &numImageVisuals,
       
   203         &pImageVisuals,
       
   204         &vis_regions,
       
   205         &vis_image_regions,
       
   206         &allImage );
       
   207 
       
   208     image = ReadAreaToImage(
       
   209         display,
       
   210         window,
       
   211         x, y, w, h,
       
   212         numVisuals,
       
   213         pVisuals,
       
   214         numOverlayVisuals,
       
   215         pOverlayVisuals,
       
   216         numImageVisuals,
       
   217         pImageVisuals,
       
   218         vis_regions,
       
   219         vis_image_regions,
       
   220         format,
       
   221         allImage );
       
   222 
       
   223     /* allow user to do stuff again */
       
   224     XUngrabServer(display);
       
   225 
       
   226     /* make sure the grab/ungrab is flushed */
       
   227     XSync(display, False);
       
   228 
       
   229     return image;
       
   230 }
       
   231 
       
   232 /*********************************************************************************************/
       
   233 
       
   234 // this should be called from XRobotPeer constructor
       
   235 JNIEXPORT void JNICALL
       
   236 Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls, jint numberOfButtons, jintArray buttonDownMasks)
       
   237 {
       
   238     int32_t xtestAvailable;
       
   239     jint *tmp;
       
   240     int i;
       
   241 
       
   242     DTRACE_PRINTLN("RobotPeer: setup()");
       
   243 
       
   244     num_buttons = numberOfButtons;
       
   245     tmp = (*env)->GetIntArrayElements(env, buttonDownMasks, JNI_FALSE);
       
   246     CHECK_NULL(tmp);
       
   247 
       
   248     masks = (jint *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), num_buttons);
       
   249     if (masks == (jint *) NULL) {
       
   250         (*env)->ExceptionClear(env);
       
   251         (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
       
   252         JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL);
       
   253         return;
       
   254     }
       
   255     for (i = 0; i < num_buttons; i++) {
       
   256         masks[i] = tmp[i];
       
   257     }
       
   258     (*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);
       
   259 
       
   260     AWT_LOCK();
       
   261     xtestAvailable = isXTestAvailable();
       
   262     DTRACE_PRINTLN1("RobotPeer: XTest available = %d", xtestAvailable);
       
   263     if (!xtestAvailable) {
       
   264         JNU_ThrowByName(env, "java/awt/AWTException", "java.awt.Robot requires your X server support the XTEST extension version 2.2");
       
   265     }
       
   266 
       
   267     AWT_UNLOCK();
       
   268 }
       
   269 
       
   270 
       
   271 JNIEXPORT void JNICALL
       
   272 Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,
       
   273                              jclass cls,
       
   274                              jobject xgc,
       
   275                              jint jx,
       
   276                              jint jy,
       
   277                              jint jwidth,
       
   278                              jint jheight,
       
   279                              jintArray pixelArray,
       
   280                              jboolean useGtk) {
       
   281     XImage *image;
       
   282     jint *ary;               /* Array of jints for sending pixel values back
       
   283                               * to parent process.
       
   284                               */
       
   285     Window rootWindow;
       
   286     XWindowAttributes attr;
       
   287     AwtGraphicsConfigDataPtr adata;
       
   288 
       
   289     DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, jx, jy, jwidth, jheight, pixelArray);
       
   290 
       
   291     if (jwidth <= 0 || jheight <= 0) {
       
   292         return;
       
   293     }
       
   294 
       
   295     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
       
   296     DASSERT(adata != NULL);
       
   297 
       
   298     AWT_LOCK();
       
   299 
       
   300     rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);
       
   301 
       
   302     if (!useGtk) {
       
   303         if (hasXCompositeOverlayExtension(awt_display) &&
       
   304             isXCompositeDisplay(awt_display, adata->awt_visInfo.screen))
       
   305         {
       
   306             rootWindow = compositeGetOverlayWindow(awt_display, rootWindow);
       
   307         }
       
   308     }
       
   309 
       
   310     if (!XGetWindowAttributes(awt_display, rootWindow, &attr)
       
   311             || jx + jwidth <= attr.x
       
   312             || attr.x + attr.width <= jx
       
   313             || jy + jheight <= attr.y
       
   314             || attr.y + attr.height <= jy) {
       
   315 
       
   316         AWT_UNLOCK();
       
   317         return; // Does not intersect with root window
       
   318     }
       
   319 
       
   320     gboolean gtk_failed = TRUE;
       
   321     jint _x, _y;
       
   322 
       
   323     jint x = MAX(jx, attr.x);
       
   324     jint y = MAX(jy, attr.y);
       
   325     jint width = MIN(jx + jwidth, attr.x + attr.width) - x;
       
   326     jint height = MIN(jy + jheight, attr.y + attr.height) - y;
       
   327 
       
   328     int dx = attr.x > jx ? attr.x - jx : 0;
       
   329     int dy = attr.y > jy ? attr.y - jy : 0;
       
   330 
       
   331     int index;
       
   332 
       
   333     if (useGtk) {
       
   334         gtk->gdk_threads_enter();
       
   335         gtk_failed = gtk->get_drawable_data(env, pixelArray, x, y, width,
       
   336                                             height, jwidth, dx, dy, 1);
       
   337         gtk->gdk_threads_leave();
       
   338     }
       
   339 
       
   340     if (gtk_failed) {
       
   341         image = getWindowImage(awt_display, rootWindow, x, y, width, height);
       
   342 
       
   343         ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL);
       
   344 
       
   345         if (!ary) {
       
   346             XDestroyImage(image);
       
   347             AWT_UNLOCK();
       
   348             return;
       
   349         }
       
   350 
       
   351         /* convert to Java ARGB pixels */
       
   352         for (_y = 0; _y < height; _y++) {
       
   353             for (_x = 0; _x < width; _x++) {
       
   354                 jint pixel = (jint) XGetPixel(image, _x, _y);
       
   355                                                               /* Note ignore upper
       
   356                                                                * 32-bits on 64-bit
       
   357                                                                * OSes.
       
   358                                                                */
       
   359                 pixel |= 0xff000000; /* alpha - full opacity */
       
   360 
       
   361                 index = (_y + dy) * jwidth + (_x + dx);
       
   362                 ary[index] = pixel;
       
   363             }
       
   364         }
       
   365 
       
   366         XDestroyImage(image);
       
   367         (*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0);
       
   368     }
       
   369     AWT_UNLOCK();
       
   370 }
       
   371 
       
   372 JNIEXPORT void JNICALL
       
   373 Java_sun_awt_X11_XRobotPeer_keyPressImpl (JNIEnv *env,
       
   374                          jclass cls,
       
   375                          jint keycode) {
       
   376 
       
   377     AWT_LOCK();
       
   378 
       
   379     DTRACE_PRINTLN1("RobotPeer: keyPressImpl(%i)", keycode);
       
   380 
       
   381     XTestFakeKeyEvent(awt_display,
       
   382                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
       
   383                       True,
       
   384                       CurrentTime);
       
   385 
       
   386     XSync(awt_display, False);
       
   387 
       
   388     AWT_UNLOCK();
       
   389 
       
   390 }
       
   391 
       
   392 JNIEXPORT void JNICALL
       
   393 Java_sun_awt_X11_XRobotPeer_keyReleaseImpl (JNIEnv *env,
       
   394                            jclass cls,
       
   395                            jint keycode) {
       
   396     AWT_LOCK();
       
   397 
       
   398     DTRACE_PRINTLN1("RobotPeer: keyReleaseImpl(%i)", keycode);
       
   399 
       
   400     XTestFakeKeyEvent(awt_display,
       
   401                       XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),
       
   402                       False,
       
   403                       CurrentTime);
       
   404 
       
   405     XSync(awt_display, False);
       
   406 
       
   407     AWT_UNLOCK();
       
   408 }
       
   409 
       
   410 JNIEXPORT void JNICALL
       
   411 Java_sun_awt_X11_XRobotPeer_mouseMoveImpl (JNIEnv *env,
       
   412                           jclass cls,
       
   413                           jobject xgc,
       
   414                           jint root_x,
       
   415                           jint root_y) {
       
   416 
       
   417     AwtGraphicsConfigDataPtr adata;
       
   418 
       
   419     AWT_LOCK();
       
   420 
       
   421     DTRACE_PRINTLN3("RobotPeer: mouseMoveImpl(%lx, %i, %i)", xgc, root_x, root_y);
       
   422 
       
   423     adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
       
   424     DASSERT(adata != NULL);
       
   425 
       
   426     XWarpPointer(awt_display, None, XRootWindow(awt_display, adata->awt_visInfo.screen), 0, 0, 0, 0, root_x, root_y);
       
   427     XSync(awt_display, False);
       
   428 
       
   429     AWT_UNLOCK();
       
   430 }
       
   431 
       
   432 /*
       
   433   * Function joining the code of mousePressImpl and mouseReleaseImpl
       
   434   */
       
   435 void mouseAction(JNIEnv *env,
       
   436                  jclass cls,
       
   437                  jint buttonMask,
       
   438                  Bool isMousePress)
       
   439 {
       
   440     AWT_LOCK();
       
   441 
       
   442     DTRACE_PRINTLN1("RobotPeer: mouseAction(%i)", buttonMask);
       
   443     DTRACE_PRINTLN1("RobotPeer: mouseAction, press = %d", isMousePress);
       
   444 
       
   445     if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||
       
   446         buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK )
       
   447     {
       
   448         XTestFakeButtonEvent(awt_display, 1, isMousePress, CurrentTime);
       
   449     }
       
   450     if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||
       
   451          buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) &&
       
   452         (num_buttons >= 2)) {
       
   453         XTestFakeButtonEvent(awt_display, 2, isMousePress, CurrentTime);
       
   454     }
       
   455     if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||
       
   456          buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) &&
       
   457         (num_buttons >= 3)) {
       
   458         XTestFakeButtonEvent(awt_display, 3, isMousePress, CurrentTime);
       
   459     }
       
   460 
       
   461     if (num_buttons > 3){
       
   462         int32_t i;
       
   463         int32_t button = 0;
       
   464         for (i = 3; i<num_buttons; i++){
       
   465             if ((buttonMask & masks[i])) {
       
   466                 // arrays starts from zero index => +1
       
   467                 // users wants to affect 4 or 5 button but they are assigned
       
   468                 // to the wheel so => we have to shift it to the right by 2.
       
   469                 button = i + 3;
       
   470                 XTestFakeButtonEvent(awt_display, button, isMousePress, CurrentTime);
       
   471             }
       
   472         }
       
   473     }
       
   474 
       
   475     XSync(awt_display, False);
       
   476     AWT_UNLOCK();
       
   477 }
       
   478 
       
   479 JNIEXPORT void JNICALL
       
   480 Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,
       
   481                            jclass cls,
       
   482                            jint buttonMask) {
       
   483     mouseAction(env, cls, buttonMask, True);
       
   484 }
       
   485 
       
   486 JNIEXPORT void JNICALL
       
   487 Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl (JNIEnv *env,
       
   488                              jclass cls,
       
   489                              jint buttonMask) {
       
   490     mouseAction(env, cls, buttonMask, False);
       
   491 }
       
   492 
       
   493 JNIEXPORT void JNICALL
       
   494 Java_sun_awt_X11_XRobotPeer_mouseWheelImpl (JNIEnv *env,
       
   495                            jclass cls,
       
   496                            jint wheelAmt) {
       
   497 /* Mouse wheel is implemented as a button press of button 4 and 5, so it */
       
   498 /* probably could have been hacked into robot_mouseButtonEvent, but it's */
       
   499 /* cleaner to give it its own command type, in case the implementation   */
       
   500 /* needs to be changed later.  -bchristi, 6/20/01                        */
       
   501 
       
   502     int32_t repeat = abs(wheelAmt);
       
   503     int32_t button = wheelAmt < 0 ? 4 : 5;  /* wheel up:   button 4 */
       
   504                                                  /* wheel down: button 5 */
       
   505     int32_t loopIdx;
       
   506 
       
   507     AWT_LOCK();
       
   508 
       
   509     DTRACE_PRINTLN1("RobotPeer: mouseWheelImpl(%i)", wheelAmt);
       
   510 
       
   511     for (loopIdx = 0; loopIdx < repeat; loopIdx++) { /* do nothing for   */
       
   512                                                      /* wheelAmt == 0    */
       
   513         XTestFakeButtonEvent(awt_display, button, True, CurrentTime);
       
   514         XTestFakeButtonEvent(awt_display, button, False, CurrentTime);
       
   515     }
       
   516     XSync(awt_display, False);
       
   517 
       
   518     AWT_UNLOCK();
       
   519 }
       
   520 
       
   521 JNIEXPORT void JNICALL
       
   522 Java_sun_awt_X11_XRobotPeer_loadNativeLibraries (JNIEnv *env, jclass cls) {
       
   523     initXCompositeFunctions();
       
   524 }