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 #include <X11/Xlib.h>
  26 #include <X11/Xutil.h>
  27 #include <X11/extensions/XTest.h>
  28 #include <assert.h>
  29 #include <stdlib.h>
  30 #include <gdk/gdk.h>
  31 #include <gdk/gdkx.h>
  32 
  33 #include <com_sun_glass_ui_gtk_GtkRobot.h>
  34 #include <com_sun_glass_events_MouseEvent.h>
  35 #include "glass_general.h"
  36 #include "glass_key.h"
  37 
  38 
  39 static void checkXTest(JNIEnv* env) {
  40     int32_t major_opcode, first_event, first_error;
  41     int32_t  event_basep, error_basep, majorp, minorp;
  42     static int32_t isXTestAvailable;
  43     static gboolean checkDone = FALSE;
  44     if (!checkDone) {
  45         /* check if XTest is available */
  46         isXTestAvailable = XQueryExtension(gdk_x11_get_default_xdisplay(), XTestExtensionName, &major_opcode, &first_event, &first_error);
  47         if (isXTestAvailable) {
  48             /* check if XTest version is OK */
  49             XTestQueryExtension(gdk_x11_get_default_xdisplay(), &event_basep, &error_basep, &majorp, &minorp);
  50             if (majorp < 2 || (majorp == 2 && minorp < 2)) {
  51                     isXTestAvailable = False;
  52             } else {
  53                 XTestGrabControl(gdk_x11_get_default_xdisplay(), True);
  54             }
  55         }
  56         checkDone = TRUE;
  57     }
  58     if (!isXTestAvailable) {
  59         jclass cls = env->FindClass("java/lang/UnsupportedOperationException");
  60         if (env->ExceptionCheck()) return;
  61         env->ThrowNew(cls, "Glass Robot needs XTest extension to work");
  62     }
  63 }
  64 
  65 static void keyButton(jint code, gboolean press)
  66 {
  67     Display *xdisplay = gdk_x11_get_default_xdisplay();
  68     gint gdk_keyval = find_gdk_keyval_for_glass_keycode(code);
  69     GdkKeymapKey *keys;
  70     gint n_keys;
  71     if (gdk_keyval == -1) {
  72         return;
  73     }
  74     gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
  75             gdk_keyval, &keys, &n_keys);
  76     if (n_keys < 1) {
  77         return;
  78     }
  79 
  80     XTestFakeKeyEvent(xdisplay,
  81                       keys[0].keycode,
  82                       press ? True : False,
  83                       CurrentTime);
  84     g_free(keys);
  85     XSync(xdisplay, False);
  86 }
  87 
  88 extern "C" {
  89 
  90 /*
  91  * Class:     com_sun_glass_ui_gtk_GtkRobot
  92  * Method:    _keyPress
  93  * Signature: (I)V
  94  */
  95 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1keyPress
  96   (JNIEnv *env, jobject obj, jint code)
  97 {
  98     (void)obj;
  99 
 100     checkXTest(env);
 101     keyButton(code, TRUE);
 102 }
 103 
 104 /*
 105  * Class:     com_sun_glass_ui_gtk_GtkRobot
 106  * Method:    _keyRelease
 107  * Signature: (I)V
 108  */
 109 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1keyRelease
 110   (JNIEnv *env, jobject obj, jint code)
 111 {
 112     (void)obj;
 113 
 114     checkXTest(env);
 115     keyButton(code, FALSE);
 116 }
 117 
 118 /*
 119  * Class:     com_sun_glass_ui_gtk_GtkRobot
 120  * Method:    _mouseMove
 121  * Signature: (II)V
 122  */
 123 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseMove
 124   (JNIEnv *env, jobject obj, jint x, jint y)
 125 {
 126     (void)obj;
 127 
 128     Display *xdisplay = gdk_x11_get_default_xdisplay();
 129     checkXTest(env);
 130     XWarpPointer(xdisplay,
 131             None,
 132             XRootWindow(xdisplay,gdk_x11_get_default_screen()),
 133             0, 0, 0, 0, x, y);
 134     XSync(xdisplay, False);
 135 }
 136 
 137 static void mouseButtons(jint buttons, gboolean press)
 138 {
 139     Display *xdisplay = gdk_x11_get_default_xdisplay();
 140     if (buttons & com_sun_glass_ui_gtk_GtkRobot_MOUSE_LEFT_BTN) {
 141         XTestFakeButtonEvent(xdisplay, 1, press, CurrentTime);
 142     }
 143     if (buttons & com_sun_glass_ui_gtk_GtkRobot_MOUSE_MIDDLE_BTN) {
 144         XTestFakeButtonEvent(xdisplay, 2, press, CurrentTime);
 145     }
 146     if (buttons & com_sun_glass_ui_gtk_GtkRobot_MOUSE_RIGHT_BTN) {
 147         XTestFakeButtonEvent(xdisplay, 3, press, CurrentTime);
 148     }
 149 
 150     XSync(xdisplay, False);
 151 }
 152 
 153 /*
 154  * Class:     com_sun_glass_ui_gtk_GtkRobot
 155  * Method:    _mousePress
 156  * Signature: (I)V
 157  */
 158 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mousePress
 159   (JNIEnv *env, jobject obj, jint buttons)
 160 {
 161     (void)obj;
 162 
 163     checkXTest(env);
 164     mouseButtons(buttons, TRUE);
 165 }
 166 
 167 /*
 168  * Class:     com_sun_glass_ui_gtk_GtkRobot
 169  * Method:    _mouseRelease
 170  * Signature: (I)V
 171  */
 172 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseRelease
 173   (JNIEnv *env, jobject obj, jint buttons)
 174 {
 175     (void)obj;
 176 
 177     checkXTest(env);
 178     mouseButtons(buttons, FALSE);
 179 }
 180 
 181 /*
 182  * Class:     com_sun_glass_ui_gtk_GtkRobot
 183  * Method:    _mouseWheel
 184  * Signature: (I)V
 185  */
 186 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1mouseWheel
 187   (JNIEnv *env, jobject obj, jint amt)
 188 {
 189     (void)obj;
 190 
 191     Display *xdisplay = gdk_x11_get_default_xdisplay();
 192     int repeat = abs(amt);
 193     int button = amt < 0 ? 5 : 4;
 194     int i;
 195 
 196     checkXTest(env);
 197     for (i = 0; i < repeat; i++) {
 198         XTestFakeButtonEvent(xdisplay, button, True, CurrentTime);
 199         XTestFakeButtonEvent(xdisplay, button, False, CurrentTime);
 200     }
 201     XSync(xdisplay, False);
 202 }
 203 
 204 /*
 205  * Class:     com_sun_glass_ui_gtk_GtkRobot
 206  * Method:    _getMouseX
 207  * Signature: ()I
 208  */
 209 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getMouseX
 210   (JNIEnv *env, jobject obj)
 211 {
 212     (void)env;
 213     (void)obj;
 214 
 215     jint x;
 216     glass_gdk_display_get_pointer(gdk_display_get_default(), &x, NULL);
 217     return x;
 218 }
 219 
 220 /*
 221  * Class:     com_sun_glass_ui_gtk_GtkRobot
 222  * Method:    _getMouseY
 223  * Signature: ()I
 224  */
 225 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getMouseY
 226   (JNIEnv *env, jobject obj)
 227 {
 228     (void)env;
 229     (void)obj;
 230 
 231     jint y;
 232     glass_gdk_display_get_pointer(gdk_display_get_default(), NULL, &y);
 233     return y;
 234 }
 235 
 236 /*
 237  * Class:     com_sun_glass_ui_gtk_GtkRobot
 238  * Method:    _getScreenCapture
 239  * Signature: (IIII[I)V
 240  */
 241 JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkRobot__1getScreenCapture
 242   (JNIEnv * env, jobject obj, jint x, jint y, jint width, jint height, jintArray data)
 243 {
 244     (void)obj;
 245 
 246     GdkPixbuf *screenshot, *tmp;
 247     GdkWindow *root_window = gdk_get_default_root_window();
 248 
 249     tmp = glass_pixbuf_from_window(root_window, x, y, width, height);
 250     screenshot = gdk_pixbuf_add_alpha(tmp, FALSE, 0, 0, 0);
 251     g_object_unref(tmp);
 252 
 253     jint *pixels = (jint *)convert_BGRA_to_RGBA((int*)gdk_pixbuf_get_pixels(screenshot), width * 4, height);
 254     env->SetIntArrayRegion(data, 0, height * width, pixels);
 255     g_free(pixels);
 256 
 257     g_object_unref(screenshot);
 258 }
 259 
 260 } // extern "C"