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