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 "glass_gtkcompat.h"
  26 #include "glass_general.h"
  27 #include <gdk/gdk.h>
  28 #include <gtk/gtk.h>
  29 
  30 gboolean disableGrab = FALSE;
  31 
  32 static gboolean configure_transparent_window(GtkWidget *window);
  33 static void configure_opaque_window(GtkWidget *window);
  34 static gboolean configure_window_transparency(GtkWidget *window,
  35                                               gboolean transparent);
  36 
  37 #if GTK_CHECK_VERSION(3, 0, 0)
  38 typedef struct _DeviceGrabContext {
  39     GdkWindow * window;
  40     gboolean grabbed;
  41 } DeviceGrabContext;
  42 
  43 static void grab_mouse_device(GdkDevice *device, DeviceGrabContext *context);
  44 static void ungrab_mouse_device(GdkDevice *device);
  45 
  46 GdkScreen *
  47 glass_gdk_window_get_screen(GdkWindow * gdkWindow) {
  48     GdkVisual * gdkVisual = gdk_window_get_visual(gdkWindow);
  49     return gdk_visual_get_screen(gdkVisual);
  50 }
  51 
  52 GdkDisplay * glass_gdk_window_get_display(GdkWindow * gdkWindow) {
  53     return gdk_window_get_display(gdkWindow);
  54 }
  55 
  56 
  57 gboolean
  58 glass_gdk_mouse_devices_grab(GdkWindow *gdkWindow) {
  59     if (disableGrab) {
  60         return TRUE;
  61     }
  62 
  63     DeviceGrabContext context;
  64     GList *devices = gdk_device_manager_list_devices(
  65                          gdk_display_get_device_manager(
  66                              gdk_display_get_default()),
  67                              GDK_DEVICE_TYPE_MASTER);
  68 
  69     context.window = gdkWindow;
  70     context.grabbed = FALSE;
  71     g_list_foreach(devices, (GFunc) grab_mouse_device, &context);
  72 
  73     return context.grabbed;
  74 }
  75 
  76 void
  77 glass_gdk_mouse_devices_ungrab() {
  78     GList *devices = gdk_device_manager_list_devices(
  79                          gdk_display_get_device_manager(
  80                              gdk_display_get_default()),
  81                              GDK_DEVICE_TYPE_MASTER);
  82     g_list_foreach(devices, (GFunc) ungrab_mouse_device, NULL);
  83 }
  84 
  85 void
  86 glass_gdk_master_pointer_grab(GdkWindow *window, GdkCursor *cursor) {
  87     if (disableGrab) {
  88         gdk_window_set_cursor(window, cursor);
  89         return;
  90     }
  91     gdk_device_grab(gdk_device_manager_get_client_pointer(
  92                         gdk_display_get_device_manager(
  93                             gdk_display_get_default())),
  94                     window, GDK_OWNERSHIP_NONE, FALSE, GDK_ALL_EVENTS_MASK,
  95                     cursor, GDK_CURRENT_TIME);
  96 }
  97 
  98 void
  99 glass_gdk_master_pointer_ungrab() {
 100     gdk_device_ungrab(gdk_device_manager_get_client_pointer(
 101                           gdk_display_get_device_manager(
 102                               gdk_display_get_default())),
 103                       GDK_CURRENT_TIME);
 104 }
 105 
 106 void
 107 glass_gdk_master_pointer_get_position(gint *x, gint *y) {
 108     gdk_device_get_position(gdk_device_manager_get_client_pointer(
 109                                 gdk_display_get_device_manager(
 110                                     gdk_display_get_default())),
 111                             NULL, x, y);
 112 }
 113 
 114 gboolean
 115 glass_gdk_device_is_grabbed(GdkDevice *device) {
 116     return gdk_display_device_is_grabbed(gdk_display_get_default(), device);
 117 }
 118 
 119 void
 120 glass_gdk_device_ungrab(GdkDevice *device) {
 121     gdk_device_ungrab(device, GDK_CURRENT_TIME);
 122 }
 123 
 124 GdkWindow *
 125 glass_gdk_device_get_window_at_position(GdkDevice *device, gint *x, gint *y) {
 126     return gdk_device_get_window_at_position(device, x, y);
 127 }
 128 
 129 void
 130 glass_gtk_configure_transparency_and_realize(GtkWidget *window,
 131                                              gboolean transparent) {
 132     gboolean isTransparent = configure_window_transparency(window, transparent);
 133     gtk_widget_realize(window);
 134     if (isTransparent) {
 135         GdkRGBA rgba = { 1.0, 1.0, 1.0, 0.0 };
 136         gdk_window_set_background_rgba(gtk_widget_get_window(window), &rgba);
 137     }
 138 }
 139 
 140 void
 141 glass_gtk_window_configure_from_visual(GtkWidget *widget, GdkVisual *visual) {
 142     gtk_widget_set_visual(widget, visual);
 143 }
 144 
 145 static gboolean
 146 configure_transparent_window(GtkWidget *window) {
 147     GdkScreen *default_screen = gdk_screen_get_default();
 148     GdkDisplay *default_display = gdk_display_get_default();
 149     GdkVisual *visual = gdk_screen_get_rgba_visual(default_screen);
 150     if (visual
 151             && gdk_display_supports_composite(default_display)
 152             && gdk_screen_is_composited(default_screen)) {
 153         gtk_widget_set_visual(window, visual);
 154         return TRUE;
 155     }
 156 
 157     return FALSE;
 158 }
 159 
 160 static void
 161 grab_mouse_device(GdkDevice *device, DeviceGrabContext *context) {
 162     GdkInputSource source = gdk_device_get_source(device);
 163     if (source == GDK_SOURCE_MOUSE) {
 164         GdkGrabStatus status = gdk_device_grab(device,
 165                                                context->window,
 166                                                GDK_OWNERSHIP_NONE,
 167                                                TRUE,
 168                                                GDK_ALL_EVENTS_MASK,
 169                                                NULL,
 170                                                GDK_CURRENT_TIME);
 171         if (status == GDK_GRAB_SUCCESS) {
 172             context->grabbed = TRUE;
 173         }
 174     }
 175 }
 176 
 177 static void
 178 ungrab_mouse_device(GdkDevice *device) {
 179     GdkInputSource source = gdk_device_get_source(device);
 180     if (source == GDK_SOURCE_MOUSE) {
 181         gdk_device_ungrab(device, GDK_CURRENT_TIME);
 182     }
 183 }
 184 
 185 int glass_gtk_fixup_typed_key(int key, int keyval) {
 186     return key;
 187 }
 188 
 189 void glass_gdk_window_get_size(GdkWindow *window, gint *w, gint *h) {
 190     *w = gdk_window_get_width(window);
 191     *h = gdk_window_get_height(window);
 192 }
 193 
 194 void glass_gdk_display_get_pointer(GdkDisplay* display, gint* x, gint *y) {
 195     gdk_device_get_position(gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(display)),
 196         NULL , x, y);
 197 }
 198 
 199 #else /* GTK_CHECK_VERSION(3, 0, 0) */
 200 
 201 GdkScreen *
 202 glass_gdk_window_get_screen(GdkWindow * gdkWindow) {
 203     return gdk_drawable_get_screen(GDK_DRAWABLE(gdkWindow));
 204 }
 205 
 206 GdkDisplay * glass_gdk_window_get_display(GdkWindow * gdkWindow) {
 207     return gdk_drawable_get_display(GDK_DRAWABLE(gdkWindow));
 208 }
 209 
 210 gboolean
 211 glass_gdk_mouse_devices_grab(GdkWindow *gdkWindow) {
 212     return glass_gdk_mouse_devices_grab_with_cursor(gdkWindow, NULL, TRUE);
 213 }
 214 
 215 gboolean
 216 glass_gdk_mouse_devices_grab_with_cursor(GdkWindow *gdkWindow, GdkCursor *cursor) {
 217     return glass_gdk_mouse_devices_grab_with_cursor(gdkWindow, cursor, TRUE);
 218 }
 219 
 220 gboolean
 221 glass_gdk_mouse_devices_grab_with_cursor(GdkWindow *gdkWindow, GdkCursor *cursor, gboolean owner_events) {
 222     if (disableGrab) {
 223         return TRUE;
 224     }
 225     GdkGrabStatus status = gdk_pointer_grab(gdkWindow, owner_events, (GdkEventMask)
 226                                             (GDK_POINTER_MOTION_MASK
 227                                                 | GDK_POINTER_MOTION_HINT_MASK
 228                                                 | GDK_BUTTON_MOTION_MASK
 229                                                 | GDK_BUTTON1_MOTION_MASK
 230                                                 | GDK_BUTTON2_MOTION_MASK
 231                                                 | GDK_BUTTON3_MOTION_MASK
 232                                                 | GDK_BUTTON_PRESS_MASK
 233                                                 | GDK_BUTTON_RELEASE_MASK),
 234                                             NULL, cursor, GDK_CURRENT_TIME);
 235 
 236     return (status == GDK_GRAB_SUCCESS) ? TRUE : FALSE;
 237 }
 238 
 239 void
 240 glass_gdk_mouse_devices_ungrab() {
 241     gdk_pointer_ungrab(GDK_CURRENT_TIME);
 242 }
 243 
 244 void
 245 glass_gdk_master_pointer_grab(GdkWindow *window, GdkCursor *cursor) {
 246     if (disableGrab) {
 247         gdk_window_set_cursor(window, cursor);
 248         return;
 249     }
 250     gdk_pointer_grab(window, FALSE, (GdkEventMask)
 251                      (GDK_POINTER_MOTION_MASK
 252                          | GDK_BUTTON_MOTION_MASK
 253                          | GDK_BUTTON1_MOTION_MASK
 254                          | GDK_BUTTON2_MOTION_MASK
 255                          | GDK_BUTTON3_MOTION_MASK
 256                          | GDK_BUTTON_RELEASE_MASK),
 257                      NULL, cursor, GDK_CURRENT_TIME);
 258 }
 259 
 260 void
 261 glass_gdk_master_pointer_ungrab() {
 262     gdk_pointer_ungrab(GDK_CURRENT_TIME);
 263 }
 264 
 265 void
 266 glass_gdk_master_pointer_get_position(gint *x, gint *y) {
 267     gdk_display_get_pointer(gdk_display_get_default(), NULL, x, y, NULL);
 268 }
 269 
 270 gboolean
 271 glass_gdk_device_is_grabbed(GdkDevice *device) {
 272     (void) device;
 273     return gdk_display_pointer_is_grabbed(gdk_display_get_default());
 274 }
 275 
 276 void
 277 glass_gdk_device_ungrab(GdkDevice *device) {
 278     (void) device;
 279     gdk_pointer_ungrab(GDK_CURRENT_TIME);
 280 }
 281 
 282 GdkWindow *
 283 glass_gdk_device_get_window_at_position(GdkDevice *device, gint *x, gint *y) {
 284     (void) device;
 285     return gdk_display_get_window_at_pointer(gdk_display_get_default(), x, y);
 286 }
 287 
 288 void
 289 glass_gtk_configure_transparency_and_realize(GtkWidget *window,
 290                                              gboolean transparent) {
 291     configure_window_transparency(window, transparent);
 292     gtk_widget_realize(window);
 293 }
 294 
 295 void
 296 glass_gtk_window_configure_from_visual(GtkWidget *widget, GdkVisual *visual) {
 297     GdkColormap *colormap = gdk_colormap_new(visual, TRUE);
 298     gtk_widget_set_colormap(widget, colormap);
 299 }
 300 
 301 static gboolean
 302 configure_transparent_window(GtkWidget *window) {
 303     GdkScreen *default_screen = gdk_screen_get_default();
 304     GdkDisplay *default_display = gdk_display_get_default();
 305     GdkColormap *colormap = gdk_screen_get_rgba_colormap(default_screen);
 306     if (colormap
 307             && gdk_display_supports_composite(default_display)
 308             && gdk_screen_is_composited(default_screen)) {
 309         gtk_widget_set_colormap(window, colormap);
 310         return TRUE;
 311     }
 312 
 313     return FALSE;
 314 }
 315 
 316 int glass_gtk_fixup_typed_key(int key, int keyval) {
 317     if (key == 0) {
 318         // Work around "bug" fixed in gtk-3.0:
 319         // http://mail.gnome.org/archives/commits-list/2011-March/msg06832.html
 320         switch (keyval) {
 321         case 0xFF08 /* Backspace */: return '\b';
 322         case 0xFF09 /* Tab       */: return '\t';
 323         case 0xFF0A /* Linefeed  */: return '\n';
 324         case 0xFF0B /* Vert. Tab */: return '\v';
 325         case 0xFF0D /* Return    */: return '\r';
 326         case 0xFF1B /* Escape    */: return '\033';
 327         case 0xFFFF /* Delete    */: return '\177';
 328         }
 329     }
 330     return key;
 331 }
 332 
 333 void glass_gdk_window_get_size(GdkWindow *window, gint *w, gint *h) {
 334     gdk_drawable_get_size(GDK_DRAWABLE(window), w, h);
 335 }
 336 
 337 void glass_gdk_display_get_pointer(GdkDisplay* display, gint* x, gint *y) {
 338     gdk_display_get_pointer(display, NULL, x, y, NULL);
 339 }
 340 
 341 #endif /* GTK_CHECK_VERSION(3, 0, 0) */
 342 
 343 const guchar*
 344 glass_gtk_selection_data_get_data_with_length(
 345         GtkSelectionData * selectionData,
 346         gint * length) {
 347     if (selectionData == NULL) {
 348         return NULL;
 349     }
 350 
 351     *length = gtk_selection_data_get_length(selectionData);
 352     return gtk_selection_data_get_data(selectionData);
 353 }
 354 
 355 static void
 356 configure_opaque_window(GtkWidget *window) {
 357     (void)window;
 358 
 359     gtk_widget_set_visual(window,
 360                           gdk_screen_get_system_visual(
 361                               gdk_screen_get_default()));
 362 }
 363 
 364 static gboolean
 365 configure_window_transparency(GtkWidget *window, gboolean transparent) {
 366     if (transparent) {
 367         if (configure_transparent_window(window)) {
 368             return TRUE;
 369         }
 370 
 371         ERROR0("Can't create transparent stage, because your screen doesn't"
 372                " support alpha channel."
 373                " You need to enable XComposite extension.\n");
 374     }
 375 
 376     configure_opaque_window(window);
 377     return FALSE;
 378 }
 379