Path: blob/master/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c
41154 views
/*1* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#ifdef HEADLESS26#error This file should not be included in headless library27#endif2829#include "jvm_md.h"30#include <dlfcn.h>3132#include "awt_p.h"33#include "awt_GraphicsEnv.h"34#define XK_MISCELLANY35#include <X11/keysymdef.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 <sizecalc.h>44#include "canvas.h"45#include "wsutils.h"46#include "list.h"47#include "multiVis.h"48#include "gtk_interface.h"4950#include "java_awt_event_InputEvent.h"5152#if defined(__linux__)53#include <sys/socket.h>54#endif5556static Bool (*compositeQueryExtension) (Display*, int*, int*);57static Status (*compositeQueryVersion) (Display*, int*, int*);58static Window (*compositeGetOverlayWindow) (Display *, Window);5960extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;6162static jint * masks;63static jint num_buttons;6465static void *xCompositeHandle;6667static const char* XCOMPOSITE = JNI_LIB_NAME("Xcomposite");68static const char* XCOMPOSITE_VERSIONED = VERSIONED_JNI_LIB_NAME("Xcomposite", "1");6970static Bool checkXCompositeFunctions(void) {71return (compositeQueryExtension != NULL &&72compositeQueryVersion != NULL &&73compositeGetOverlayWindow != NULL);74}7576static void initXCompositeFunctions(void) {7778if (xCompositeHandle == NULL) {79xCompositeHandle = dlopen(XCOMPOSITE, RTLD_LAZY | RTLD_GLOBAL);80if (xCompositeHandle == NULL) {81xCompositeHandle = dlopen(XCOMPOSITE_VERSIONED, RTLD_LAZY | RTLD_GLOBAL);82}83}84//*(void **)(&asyncGetCallTraceFunction)85if (xCompositeHandle != NULL) {86*(void **)(&compositeQueryExtension) = dlsym(xCompositeHandle, "XCompositeQueryExtension");87*(void **)(&compositeQueryVersion) = dlsym(xCompositeHandle, "XCompositeQueryVersion");88*(void **)(&compositeGetOverlayWindow) = dlsym(xCompositeHandle, "XCompositeGetOverlayWindow");89}9091if (xCompositeHandle && !checkXCompositeFunctions()) {92dlclose(xCompositeHandle);93}94}9596static int32_t isXTestAvailable() {97int32_t major_opcode, first_event, first_error;98int32_t event_basep, error_basep, majorp, minorp;99int32_t isXTestAvailable;100101/* check if XTest is available */102isXTestAvailable = XQueryExtension(awt_display, XTestExtensionName, &major_opcode, &first_event, &first_error);103if (isXTestAvailable) {104DTRACE_PRINTLN3("RobotPeer: XQueryExtension(XTEST) returns major_opcode = %d, first_event = %d, first_error = %d",105major_opcode, first_event, first_error);106/* check if XTest version is OK */107XTestQueryExtension(awt_display, &event_basep, &error_basep, &majorp, &minorp);108DTRACE_PRINTLN4("RobotPeer: XTestQueryExtension returns event_basep = %d, error_basep = %d, majorp = %d, minorp = %d",109event_basep, error_basep, majorp, minorp);110if (majorp < 2 || (majorp == 2 && minorp < 2)) {111/* bad version*/112DTRACE_PRINTLN2("XRobotPeer: XTEST version is %d.%d \n", majorp, minorp);113if (majorp == 2 && minorp == 1) {114DTRACE_PRINTLN("XRobotPeer: XTEST is 2.1 - no grab is available\n");115} else {116isXTestAvailable = False;117}118} else {119/* allow XTest calls even if someone else has the grab; e.g. during120* a window resize operation. Works only with XTEST2.2*/121XTestGrabControl(awt_display, True);122}123} else {124DTRACE_PRINTLN("RobotPeer: XTEST extension is unavailable");125}126127return isXTestAvailable;128}129130static Bool hasXCompositeOverlayExtension(Display *display) {131132int xoverlay = False;133int eventBase, errorBase;134if (checkXCompositeFunctions() &&135compositeQueryExtension(display, &eventBase, &errorBase))136{137int major = 0;138int minor = 0;139140compositeQueryVersion(display, &major, &minor);141if (major > 0 || minor >= 3) {142xoverlay = True;143}144}145146return xoverlay;147}148149static jboolean isXCompositeDisplay(Display *display, int screenNumber) {150151char NET_WM_CM_Sn[25];152snprintf(NET_WM_CM_Sn, sizeof(NET_WM_CM_Sn), "_NET_WM_CM_S%d", screenNumber);153154Atom managerSelection = XInternAtom(display, NET_WM_CM_Sn, 0);155Window owner = XGetSelectionOwner(display, managerSelection);156157return owner != 0;158}159160static XImage *getWindowImage(Display * display, Window window,161int32_t x, int32_t y,162int32_t w, int32_t h) {163XImage *image;164int32_t transparentOverlays;165int32_t numVisuals;166XVisualInfo *pVisuals;167int32_t numOverlayVisuals;168OverlayInfo *pOverlayVisuals;169int32_t numImageVisuals;170XVisualInfo **pImageVisuals;171list_ptr vis_regions; /* list of regions to read from */172list_ptr vis_image_regions ;173int32_t allImage = 0 ;174int32_t format = ZPixmap;175176/* prevent user from moving stuff around during the capture */177XGrabServer(display);178179/*180* The following two functions live in multiVis.c-- they are pretty181* much verbatim taken from the source to the xwd utility from the182* X11 source. This version of the xwd source was somewhat better written183* for reuse compared to Sun's version.184*185* ftp.x.org/pub/R6.3/xc/programs/xwd186*187* We use these functions since they do the very tough job of capturing188* the screen correctly when it contains multiple visuals. They take into189* account the depth/colormap of each visual and produce a capture as a190* 24-bit RGB image so we don't have to fool around with colormaps etc.191*/192193GetMultiVisualRegions(194display,195window,196x, 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 );207208image = ReadAreaToImage(209display,210window,211x, y, w, h,212numVisuals,213pVisuals,214numOverlayVisuals,215pOverlayVisuals,216numImageVisuals,217pImageVisuals,218vis_regions,219vis_image_regions,220format,221allImage );222223/* allow user to do stuff again */224XUngrabServer(display);225226/* make sure the grab/ungrab is flushed */227XSync(display, False);228229return image;230}231232/*********************************************************************************************/233234// this should be called from XRobotPeer constructor235JNIEXPORT void JNICALL236Java_sun_awt_X11_XRobotPeer_setup (JNIEnv * env, jclass cls, jint numberOfButtons, jintArray buttonDownMasks)237{238int32_t xtestAvailable;239jint *tmp;240int i;241242DTRACE_PRINTLN("RobotPeer: setup()");243244num_buttons = numberOfButtons;245tmp = (*env)->GetIntArrayElements(env, buttonDownMasks, JNI_FALSE);246CHECK_NULL(tmp);247248masks = (jint *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), num_buttons);249if (masks == (jint *) NULL) {250(*env)->ExceptionClear(env);251(*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);252JNU_ThrowOutOfMemoryError((JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2), NULL);253return;254}255for (i = 0; i < num_buttons; i++) {256masks[i] = tmp[i];257}258(*env)->ReleaseIntArrayElements(env, buttonDownMasks, tmp, 0);259260AWT_LOCK();261xtestAvailable = isXTestAvailable();262DTRACE_PRINTLN1("RobotPeer: XTest available = %d", xtestAvailable);263if (!xtestAvailable) {264JNU_ThrowByName(env, "java/awt/AWTException", "java.awt.Robot requires your X server support the XTEST extension version 2.2");265}266267AWT_UNLOCK();268}269270271JNIEXPORT void JNICALL272Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,273jclass cls,274jobject xgc,275jint jx,276jint jy,277jint jwidth,278jint jheight,279jintArray pixelArray,280jboolean useGtk) {281XImage *image;282jint *ary; /* Array of jints for sending pixel values back283* to parent process.284*/285Window rootWindow;286XWindowAttributes attr;287AwtGraphicsConfigDataPtr adata;288289DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, jx, jy, jwidth, jheight, pixelArray);290291if (jwidth <= 0 || jheight <= 0) {292return;293}294295adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);296DASSERT(adata != NULL);297298AWT_LOCK();299300rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);301302if (!useGtk) {303if (hasXCompositeOverlayExtension(awt_display) &&304isXCompositeDisplay(awt_display, adata->awt_visInfo.screen))305{306rootWindow = compositeGetOverlayWindow(awt_display, rootWindow);307}308}309310if (!XGetWindowAttributes(awt_display, rootWindow, &attr)311|| jx + jwidth <= attr.x312|| attr.x + attr.width <= jx313|| jy + jheight <= attr.y314|| attr.y + attr.height <= jy) {315316AWT_UNLOCK();317return; // Does not intersect with root window318}319320gboolean gtk_failed = TRUE;321jint _x, _y;322323jint x = MAX(jx, attr.x);324jint y = MAX(jy, attr.y);325jint width = MIN(jx + jwidth, attr.x + attr.width) - x;326jint height = MIN(jy + jheight, attr.y + attr.height) - y;327328int dx = attr.x > jx ? attr.x - jx : 0;329int dy = attr.y > jy ? attr.y - jy : 0;330331int index;332333if (useGtk) {334gtk->gdk_threads_enter();335gtk_failed = gtk->get_drawable_data(env, pixelArray, x, y, width,336height, jwidth, dx, dy, 1);337gtk->gdk_threads_leave();338}339340if (gtk_failed) {341image = getWindowImage(awt_display, rootWindow, x, y, width, height);342343ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL);344345if (!ary) {346XDestroyImage(image);347AWT_UNLOCK();348return;349}350351/* convert to Java ARGB pixels */352for (_y = 0; _y < height; _y++) {353for (_x = 0; _x < width; _x++) {354jint pixel = (jint) XGetPixel(image, _x, _y);355/* Note ignore upper356* 32-bits on 64-bit357* OSes.358*/359pixel |= 0xff000000; /* alpha - full opacity */360361index = (_y + dy) * jwidth + (_x + dx);362ary[index] = pixel;363}364}365366XDestroyImage(image);367(*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0);368}369AWT_UNLOCK();370}371372JNIEXPORT void JNICALL373Java_sun_awt_X11_XRobotPeer_keyPressImpl (JNIEnv *env,374jclass cls,375jint keycode) {376377AWT_LOCK();378379DTRACE_PRINTLN1("RobotPeer: keyPressImpl(%i)", keycode);380381XTestFakeKeyEvent(awt_display,382XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),383True,384CurrentTime);385386XSync(awt_display, False);387388AWT_UNLOCK();389390}391392JNIEXPORT void JNICALL393Java_sun_awt_X11_XRobotPeer_keyReleaseImpl (JNIEnv *env,394jclass cls,395jint keycode) {396AWT_LOCK();397398DTRACE_PRINTLN1("RobotPeer: keyReleaseImpl(%i)", keycode);399400XTestFakeKeyEvent(awt_display,401XKeysymToKeycode(awt_display, awt_getX11KeySym(keycode)),402False,403CurrentTime);404405XSync(awt_display, False);406407AWT_UNLOCK();408}409410JNIEXPORT void JNICALL411Java_sun_awt_X11_XRobotPeer_mouseMoveImpl (JNIEnv *env,412jclass cls,413jobject xgc,414jint root_x,415jint root_y) {416417AwtGraphicsConfigDataPtr adata;418419AWT_LOCK();420421DTRACE_PRINTLN3("RobotPeer: mouseMoveImpl(%lx, %i, %i)", xgc, root_x, root_y);422423adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);424DASSERT(adata != NULL);425426XWarpPointer(awt_display, None, XRootWindow(awt_display, adata->awt_visInfo.screen), 0, 0, 0, 0, root_x, root_y);427XSync(awt_display, False);428429AWT_UNLOCK();430}431432/*433* Function joining the code of mousePressImpl and mouseReleaseImpl434*/435void mouseAction(JNIEnv *env,436jclass cls,437jint buttonMask,438Bool isMousePress)439{440AWT_LOCK();441442DTRACE_PRINTLN1("RobotPeer: mouseAction(%i)", buttonMask);443DTRACE_PRINTLN1("RobotPeer: mouseAction, press = %d", isMousePress);444445if (buttonMask & java_awt_event_InputEvent_BUTTON1_MASK ||446buttonMask & java_awt_event_InputEvent_BUTTON1_DOWN_MASK )447{448XTestFakeButtonEvent(awt_display, 1, isMousePress, CurrentTime);449}450if ((buttonMask & java_awt_event_InputEvent_BUTTON2_MASK ||451buttonMask & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) &&452(num_buttons >= 2)) {453XTestFakeButtonEvent(awt_display, 2, isMousePress, CurrentTime);454}455if ((buttonMask & java_awt_event_InputEvent_BUTTON3_MASK ||456buttonMask & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) &&457(num_buttons >= 3)) {458XTestFakeButtonEvent(awt_display, 3, isMousePress, CurrentTime);459}460461if (num_buttons > 3){462int32_t i;463int32_t button = 0;464for (i = 3; i<num_buttons; i++){465if ((buttonMask & masks[i])) {466// arrays starts from zero index => +1467// users wants to affect 4 or 5 button but they are assigned468// to the wheel so => we have to shift it to the right by 2.469button = i + 3;470XTestFakeButtonEvent(awt_display, button, isMousePress, CurrentTime);471}472}473}474475XSync(awt_display, False);476AWT_UNLOCK();477}478479JNIEXPORT void JNICALL480Java_sun_awt_X11_XRobotPeer_mousePressImpl (JNIEnv *env,481jclass cls,482jint buttonMask) {483mouseAction(env, cls, buttonMask, True);484}485486JNIEXPORT void JNICALL487Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl (JNIEnv *env,488jclass cls,489jint buttonMask) {490mouseAction(env, cls, buttonMask, False);491}492493JNIEXPORT void JNICALL494Java_sun_awt_X11_XRobotPeer_mouseWheelImpl (JNIEnv *env,495jclass cls,496jint 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 */501502int32_t repeat = abs(wheelAmt);503int32_t button = wheelAmt < 0 ? 4 : 5; /* wheel up: button 4 */504/* wheel down: button 5 */505int32_t loopIdx;506507AWT_LOCK();508509DTRACE_PRINTLN1("RobotPeer: mouseWheelImpl(%i)", wheelAmt);510511for (loopIdx = 0; loopIdx < repeat; loopIdx++) { /* do nothing for */512/* wheelAmt == 0 */513XTestFakeButtonEvent(awt_display, button, True, CurrentTime);514XTestFakeButtonEvent(awt_display, button, False, CurrentTime);515}516XSync(awt_display, False);517518AWT_UNLOCK();519}520521JNIEXPORT void JNICALL522Java_sun_awt_X11_XRobotPeer_loadNativeLibraries (JNIEnv *env, jclass cls) {523initXCompositeFunctions();524}525526527