gdkdnd-x11.c 76.4 KB
Newer Older
1
/* GDK - The GIMP Drawing Kit
2
 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 4
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardon's avatar
Javier Jardon committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
Matthias Clasen's avatar
Matthias Clasen committed
22
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 24
 */

25
#include "config.h"
26

27
#include "gdkx11dnd.h"
28
#include "gdkdndprivate.h"
Owen Taylor's avatar
Started  
Owen Taylor committed
29

30
#include "gdkmain.h"
Matthias Clasen's avatar
Matthias Clasen committed
31
#include "gdkinternals.h"
32
#include "gdkasync.h"
Owen Taylor's avatar
Started  
Owen Taylor committed
33
#include "gdkproperty.h"
34
#include "gdkprivate-x11.h"
35 36
#include "gdkscreen-x11.h"
#include "gdkdisplay-x11.h"
37

38 39 40 41
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
42
#ifdef HAVE_XCOMPOSITE
43
#include <X11/extensions/Xcomposite.h>
44
#endif
45 46 47

#include <string.h>

48 49 50 51 52 53 54 55 56 57 58
typedef enum {
  GDK_DRAG_STATUS_DRAG,
  GDK_DRAG_STATUS_MOTION_WAIT,
  GDK_DRAG_STATUS_ACTION_WAIT,
  GDK_DRAG_STATUS_DROP
} GtkDragStatus;

typedef struct {
  guint32 xid;
  gint x, y, width, height;
  gboolean mapped;
59 60
  gboolean shape_selected;
  gboolean shape_valid;
61
  cairo_region_t *shape;
62 63 64 65 66 67
} GdkCacheChild;

typedef struct {
  GList *children;
  GHashTable *child_hash;
  guint old_event_mask;
68
  GdkScreen *screen;
69
  gint ref_count;
70 71
} GdkWindowCache;

Matthias Clasen's avatar
Matthias Clasen committed
72

73 74
struct _GdkX11DragContext
{
75 76 77 78
  GdkDragContext context;

  guint   ref_count;

79
  guint16 last_x;              /* Coordinates from last event */
80
  guint16 last_y;
81 82 83
  GdkDragAction old_action;    /* The last action we sent to the source */
  GdkDragAction old_actions;   /* The last actions we sent to the source */
  GdkDragAction xdnd_actions;  /* What is currently set in XdndActionList */
84 85 86
  guint version;               /* Xdnd protocol version */

  GSList *window_caches;
87

88 89
  GdkWindow *drag_window;

90 91 92
  gint hot_x;
  gint hot_y;

93 94 95 96 97 98 99
  Window dest_xid;             /* The last window we looked up */
  Window drop_xid;             /* The (non-proxied) window that is receiving drops */
  guint xdnd_targets_set  : 1; /* Whether we've already set XdndTypeList */
  guint xdnd_actions_set  : 1; /* Whether we've already set XdndActionList */
  guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
  guint drag_status       : 4; /* current status of drag */
  guint drop_failed       : 1; /* Whether the drop was unsuccessful */
100 101
};

102 103 104 105 106
struct _GdkX11DragContextClass
{
  GdkDragContextClass parent_class;
};

107 108
/* Forward declarations */

109 110 111
static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
static void            gdk_window_cache_unref (GdkWindowCache *cache);
112

113
static GdkFilterReturn xdnd_enter_filter    (GdkXEvent *xev,
114 115
                                             GdkEvent  *event,
                                             gpointer   data);
116
static GdkFilterReturn xdnd_leave_filter    (GdkXEvent *xev,
117 118
                                             GdkEvent  *event,
                                             gpointer   data);
119
static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
120 121
                                             GdkEvent  *event,
                                             gpointer   data);
122
static GdkFilterReturn xdnd_status_filter   (GdkXEvent *xev,
123 124
                                             GdkEvent  *event,
                                             gpointer   data);
125
static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
126 127
                                             GdkEvent  *event,
                                             gpointer   data);
128
static GdkFilterReturn xdnd_drop_filter     (GdkXEvent *xev,
129 130
                                             GdkEvent  *event,
                                             gpointer   data);
131

132
static void   xdnd_manage_source_filter (GdkDragContext *context,
133 134
                                         GdkWindow      *window,
                                         gboolean        add_filter);
135

136
static GList *contexts;
137
static GSList *window_caches;
138

Matthias Clasen's avatar
Matthias Clasen committed
139
static const struct {
140 141
  const char *atom_name;
  GdkFilterFunc func;
Matthias Clasen's avatar
Matthias Clasen committed
142
} xdnd_filters[] = {
143 144 145 146 147 148
  { "XdndEnter",                    xdnd_enter_filter },
  { "XdndLeave",                    xdnd_leave_filter },
  { "XdndPosition",                 xdnd_position_filter },
  { "XdndStatus",                   xdnd_status_filter },
  { "XdndFinished",                 xdnd_finished_filter },
  { "XdndDrop",                     xdnd_drop_filter },
149
};
150 151


152
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
153

Matthias Clasen's avatar
Matthias Clasen committed
154
static void
155
gdk_x11_drag_context_init (GdkX11DragContext *context)
Matthias Clasen's avatar
Matthias Clasen committed
156 157 158 159
{
  contexts = g_list_prepend (contexts, context);
}

160 161
static void        gdk_x11_drag_context_finalize (GObject *object);
static GdkWindow * gdk_x11_drag_context_find_window (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
162 163 164 165 166
                                                     GdkWindow       *drag_window,
                                                     GdkScreen       *screen,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragProtocol *protocol);
167
static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
168 169 170 171 172 173 174
                                                     GdkWindow       *dest_window,
                                                     GdkDragProtocol  protocol,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragAction    suggested_action,
                                                     GdkDragAction    possible_actions,
                                                     guint32          time);
175
static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
176 177
                                                     GdkDragAction    action,
                                                     guint32          time_);
178
static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
179
                                                     guint32          time_);
180
static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
181
                                                     guint32          time_);
182
static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
183 184
                                                     gboolean         accept,
                                                     guint32          time_);
185
static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
186 187
                                                     gboolean         success,
                                                     guint32          time_);
188 189
static gboolean    gdk_x11_drag_context_drop_status (GdkDragContext  *context);
static GdkAtom     gdk_x11_drag_context_get_selection (GdkDragContext  *context);
Matthias Clasen's avatar
Matthias Clasen committed
190
static GdkWindow * gdk_x11_drag_context_get_drag_window (GdkDragContext *context);
191 192 193
static void        gdk_x11_drag_context_set_hotspot (GdkDragContext  *context,
                                                     gint             hot_x,
                                                     gint             hot_y);
194

195
static void
196
gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
197
{
198
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Matthias Clasen's avatar
Matthias Clasen committed
199
  GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
200

201
  object_class->finalize = gdk_x11_drag_context_finalize;
Matthias Clasen's avatar
Matthias Clasen committed
202

203 204 205 206 207 208 209 210 211
  context_class->find_window = gdk_x11_drag_context_find_window;
  context_class->drag_status = gdk_x11_drag_context_drag_status;
  context_class->drag_motion = gdk_x11_drag_context_drag_motion;
  context_class->drag_abort = gdk_x11_drag_context_drag_abort;
  context_class->drag_drop = gdk_x11_drag_context_drag_drop;
  context_class->drop_reply = gdk_x11_drag_context_drop_reply;
  context_class->drop_finish = gdk_x11_drag_context_drop_finish;
  context_class->drop_status = gdk_x11_drag_context_drop_status;
  context_class->get_selection = gdk_x11_drag_context_get_selection;
212
  context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
213
  context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
214 215
}

216
static void
217
gdk_x11_drag_context_finalize (GObject *object)
218
{
219
  GdkDragContext *context = GDK_DRAG_CONTEXT (object);
220
  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object);
221 222

  if (context->source_window)
223
    {
Matthias Clasen's avatar
Matthias Clasen committed
224
      if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
225 226
        xdnd_manage_source_filter (context, context->source_window, FALSE);
    }
227

228 229
  g_slist_free_full (x11_context->window_caches, (GDestroyNotify)gdk_window_cache_unref);
  x11_context->window_caches = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
230

231
  contexts = g_list_remove (contexts, context);
232

233
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
234
}
235

236 237
/* Drag Contexts */

238
static GdkDragContext *
239
gdk_drag_context_find (GdkDisplay *display,
240 241 242
                       gboolean    is_source,
                       Window      source_xid,
                       Window      dest_xid)
243
{
244
  GList *tmp_list;
Jeff Garzik's avatar
Jeff Garzik committed
245
  GdkDragContext *context;
246
  GdkX11DragContext *context_x11;
247
  Window context_dest_xid;
248

249
  for (tmp_list = contexts; tmp_list; tmp_list = tmp_list->next)
250
    {
Jeff Garzik's avatar
Jeff Garzik committed
251
      context = (GdkDragContext *)tmp_list->data;
252
      context_x11 = (GdkX11DragContext *)context;
253

254
      if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
255
          (context->dest_window && gdk_window_get_display (context->dest_window) != display))
Matthias Clasen's avatar
Matthias Clasen committed
256
        continue;
257

Matthias Clasen's avatar
Matthias Clasen committed
258 259 260 261 262
      context_dest_xid = context->dest_window
                            ? (context_x11->drop_xid
                                  ? context_x11->drop_xid
                                  : GDK_WINDOW_XID (context->dest_window))
                            : None;
263 264

      if ((!context->is_source == !is_source) &&
Matthias Clasen's avatar
Matthias Clasen committed
265 266 267 268
          ((source_xid == None) || (context->source_window &&
            (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
          ((dest_xid == None) || (context_dest_xid == dest_xid)))
        return context;
269
    }
Matthias Clasen's avatar
Matthias Clasen committed
270

271 272 273
  return NULL;
}

274 275 276 277 278 279 280 281 282 283
static void
precache_target_list (GdkDragContext *context)
{
  if (context->targets)
    {
      GPtrArray *targets = g_ptr_array_new ();
      GList *tmp_list;
      int i;

      for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
284
        g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
285 286

      _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
287 288
                               (const gchar **)targets->pdata,
                               targets->len);
289 290

      for (i =0; i < targets->len; i++)
291
        g_free (targets->pdata[i]);
292 293 294 295 296

      g_ptr_array_free (targets, TRUE);
    }
}

297 298
/* Utility functions */

299 300 301 302 303
static void
free_cache_child (GdkCacheChild *child,
                  GdkDisplay    *display)
{
  if (child->shape)
Benjamin Otte's avatar
Benjamin Otte committed
304
    cairo_region_destroy (child->shape);
305 306 307

  if (child->shape_selected && display)
    {
308
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
309 310 311 312 313 314 315

      XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
    }

  g_free (child);
}

316 317
static void
gdk_window_cache_add (GdkWindowCache *cache,
318 319 320 321 322 323
                      guint32         xid,
                      gint            x,
                      gint            y,
                      gint            width,
                      gint            height,
                      gboolean        mapped)
324 325 326 327 328 329 330 331 332
{
  GdkCacheChild *child = g_new (GdkCacheChild, 1);

  child->xid = xid;
  child->x = x;
  child->y = y;
  child->width = width;
  child->height = height;
  child->mapped = mapped;
333 334 335
  child->shape_selected = FALSE;
  child->shape_valid = FALSE;
  child->shape = NULL;
336 337

  cache->children = g_list_prepend (cache->children, child);
338 339
  g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
                       cache->children);
340 341
}

342 343 344 345 346 347 348 349
static GdkFilterReturn
gdk_window_cache_shape_filter (GdkXEvent *xev,
                               GdkEvent  *event,
                               gpointer   data)
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

350
  GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

  if (display->have_shapes &&
      xevent->type == display->shape_event_base + ShapeNotify)
    {
      XShapeEvent *xse = (XShapeEvent*)xevent;
      GList *node;

      node = g_hash_table_lookup (cache->child_hash,
                                  GUINT_TO_POINTER (xse->window));
      if (node)
        {
          GdkCacheChild *child = node->data;
          child->shape_valid = FALSE;
          if (child->shape)
            {
Benjamin Otte's avatar
Benjamin Otte committed
366
              cairo_region_destroy (child->shape);
367 368 369 370 371 372 373 374 375 376
              child->shape = NULL;
            }
        }

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

377 378
static GdkFilterReturn
gdk_window_cache_filter (GdkXEvent *xev,
379 380
                         GdkEvent  *event,
                         gpointer   data)
381 382 383 384 385 386 387 388 389 390
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

  switch (xevent->type)
    {
    case CirculateNotify:
      break;
    case ConfigureNotify:
      {
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
        XConfigureEvent *xce = &xevent->xconfigure;
        GList *node;

        node = g_hash_table_lookup (cache->child_hash,
                                    GUINT_TO_POINTER (xce->window));
        if (node)
          {
            GdkCacheChild *child = node->data;
            child->x = xce->x;
            child->y = xce->y;
            child->width = xce->width;
            child->height = xce->height;
            if (xce->above == None && (node->next))
              {
                GList *last = g_list_last (cache->children);
                cache->children = g_list_remove_link (cache->children, node);
                last->next = node;
                node->next = NULL;
                node->prev = last;
              }
            else
              {
                GList *above_node = g_hash_table_lookup (cache->child_hash,
                                                         GUINT_TO_POINTER (xce->above));
                if (above_node && node->next != above_node)
                  {
                    /* Put the window above (before in the list) above_node */
                    cache->children = g_list_remove_link (cache->children, node);
                    node->prev = above_node->prev;
                    if (node->prev)
                      node->prev->next = node;
                    else
                      cache->children = node;
                    node->next = above_node;
                    above_node->prev = node;
                  }
              }
          }
        break;
430 431 432
      }
    case CreateNotify:
      {
433 434 435 436 437 438 439 440
        XCreateWindowEvent *xcwe = &xevent->xcreatewindow;

        if (!g_hash_table_lookup (cache->child_hash,
                                  GUINT_TO_POINTER (xcwe->window)))
          gdk_window_cache_add (cache, xcwe->window,
                                xcwe->x, xcwe->y, xcwe->width, xcwe->height,
                                FALSE);
        break;
441 442 443
      }
    case DestroyNotify:
      {
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
        XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
        GList *node;

        node = g_hash_table_lookup (cache->child_hash,
                                    GUINT_TO_POINTER (xdwe->window));
        if (node)
          {
            GdkCacheChild *child = node->data;

            g_hash_table_remove (cache->child_hash,
                                 GUINT_TO_POINTER (xdwe->window));
            cache->children = g_list_remove_link (cache->children, node);
            /* window is destroyed, no need to disable ShapeNotify */
            free_cache_child (child, NULL);
            g_list_free_1 (node);
          }
        break;
461 462 463
      }
    case MapNotify:
      {
464 465 466 467 468 469 470 471 472 473 474
        XMapEvent *xme = &xevent->xmap;
        GList *node;

        node = g_hash_table_lookup (cache->child_hash,
                                    GUINT_TO_POINTER (xme->window));
        if (node)
          {
            GdkCacheChild *child = node->data;
            child->mapped = TRUE;
          }
        break;
475 476 477 478 479
      }
    case ReparentNotify:
      break;
    case UnmapNotify:
      {
480 481 482 483 484 485 486 487 488 489 490
        XMapEvent *xume = &xevent->xmap;
        GList *node;

        node = g_hash_table_lookup (cache->child_hash,
                                    GUINT_TO_POINTER (xume->window));
        if (node)
          {
            GdkCacheChild *child = node->data;
            child->mapped = FALSE;
          }
        break;
491 492 493 494 495 496 497 498
      }
    default:
      return GDK_FILTER_CONTINUE;
    }
  return GDK_FILTER_REMOVE;
}

static GdkWindowCache *
499
gdk_window_cache_new (GdkScreen *screen)
500 501
{
  XWindowAttributes xwa;
502 503
  Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
  GdkWindow *root_window = gdk_screen_get_root_window (screen);
504 505
  GdkChildInfoX11 *children;
  guint nchildren, i;
506
#ifdef HAVE_XCOMPOSITE
507
  Window cow;
508
#endif
509

510 511 512 513
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

  result->children = NULL;
  result->child_hash = g_hash_table_new (g_direct_hash, NULL);
514
  result->screen = screen;
515
  result->ref_count = 1;
516

517
  XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
518
  result->old_event_mask = xwa.your_event_mask;
519

520
  if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
521 522 523
    {
      GList *toplevel_windows, *list;
      GdkWindow *window;
524
      GdkWindowImplX11 *impl;
525
      gint x, y, width, height;
526

527
      toplevel_windows = gdk_screen_get_toplevel_windows (screen);
528 529 530
      for (list = toplevel_windows; list; list = list->next)
        {
          window = GDK_WINDOW (list->data);
531
	  impl = GDK_WINDOW_IMPL_X11 (window->impl);
532 533
          gdk_window_get_geometry (window, &x, &y, &width, &height);
          gdk_window_cache_add (result, GDK_WINDOW_XID (window),
534 535 536
                                x * impl->window_scale, y * impl->window_scale, 
				width * impl->window_scale, 
				height * impl->window_scale,
537 538
                                gdk_window_is_visible (window));
        }
539 540 541 542
      g_list_free (toplevel_windows);
      return result;
    }

543
  XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
544
                result->old_event_mask | SubstructureNotifyMask);
545
  gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
546
  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
547

548
  if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
549 550 551
                                       GDK_WINDOW_XID (root_window),
                                       FALSE, NULL,
                                       &children, &nchildren))
552
    return result;
553

554 555
  for (i = 0; i < nchildren ; i++)
    {
556
      gdk_window_cache_add (result, children[i].window,
557 558
                            children[i].x, children[i].y, children[i].width, children[i].height,
                            children[i].is_mapped);
559 560
    }

561
  g_free (children);
562

563
#ifdef HAVE_XCOMPOSITE
564 565 566 567 568 569 570 571 572
  /*
   * Add the composite overlay window to the cache, as this can be a reasonable
   * Xdnd proxy as well.
   * This is only done when the screen is composited in order to avoid mapping
   * the COW. We assume that the CM is using the COW (which is true for pretty
   * much any CM currently in use).
   */
  if (gdk_screen_is_composited (screen))
    {
573
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
574 575 576 577
      gdk_window_cache_add (result, cow, 0, 0, 
			    gdk_screen_get_width (screen) * GDK_X11_SCREEN(screen)->window_scale, 
			    gdk_screen_get_height (screen) * GDK_X11_SCREEN(screen)->window_scale, 
			    TRUE);
578
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
579
    }
580
#endif
581

582 583 584 585 586 587
  return result;
}

static void
gdk_window_cache_destroy (GdkWindowCache *cache)
{
588
  GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
589
  GdkDisplay *display;
590 591

  XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
592 593
                GDK_WINDOW_XID (root_window),
                cache->old_event_mask);
594
  gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
595
  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
596

597
  display = gdk_screen_get_display (cache->screen);
598

599 600 601
  gdk_x11_display_error_trap_push (display);
  g_list_foreach (cache->children, (GFunc)free_cache_child, display);
  gdk_x11_display_error_trap_pop_ignored (display);
602

603 604
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
605 606

  g_free (cache);
607 608
}

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
static GdkWindowCache *
gdk_window_cache_ref (GdkWindowCache *cache)
{
  cache->ref_count += 1;

  return cache;
}

static void
gdk_window_cache_unref (GdkWindowCache *cache)
{
  g_assert (cache->ref_count > 0);

  cache->ref_count -= 1;

  if (cache->ref_count == 0)
    {
      window_caches = g_slist_remove (window_caches, cache);
      gdk_window_cache_destroy (cache);
    }
}

GdkWindowCache *
gdk_window_cache_get (GdkScreen *screen)
{
  GSList *list;
  GdkWindowCache *cache;

  for (list = window_caches; list; list = list->next)
    {
      cache = list->data;
      if (cache->screen == screen)
        return gdk_window_cache_ref (cache);
    }

Matthias Clasen's avatar
Matthias Clasen committed
644
  cache = gdk_window_cache_new (screen);
645

Matthias Clasen's avatar
Matthias Clasen committed
646
  window_caches = g_slist_prepend (window_caches, cache);
647

Matthias Clasen's avatar
Matthias Clasen committed
648
  return cache;
649 650
}

Matthias Clasen's avatar
Matthias Clasen committed
651 652 653 654 655
static gboolean
is_pointer_within_shape (GdkDisplay    *display,
                         GdkCacheChild *child,
                         gint           x_pos,
                         gint           y_pos)
656
{
Matthias Clasen's avatar
Matthias Clasen committed
657
  if (!child->shape_selected)
658
    {
Matthias Clasen's avatar
Matthias Clasen committed
659
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
660

Matthias Clasen's avatar
Matthias Clasen committed
661 662 663 664 665 666 667
      XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
      child->shape_selected = TRUE;
    }
  if (!child->shape_valid)
    {
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
      cairo_region_t *input_shape;
668

Matthias Clasen's avatar
Matthias Clasen committed
669 670 671
      child->shape = NULL;
      if (gdk_display_supports_shapes (display))
        child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
672
                                                   child->xid, 1,  ShapeBounding);
Matthias Clasen's avatar
Matthias Clasen committed
673 674 675 676
#ifdef ShapeInput
      input_shape = NULL;
      if (gdk_display_supports_input_shapes (display))
        input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
677
                                                  child->xid, 1, ShapeInput);
678

Matthias Clasen's avatar
Matthias Clasen committed
679 680 681 682 683 684 685 686 687 688
      if (child->shape && input_shape)
        {
          cairo_region_intersect (child->shape, input_shape);
          cairo_region_destroy (input_shape);
        }
      else if (input_shape)
        {
          child->shape = input_shape;
        }
#endif
689

Matthias Clasen's avatar
Matthias Clasen committed
690
      child->shape_valid = TRUE;
691 692
    }

Matthias Clasen's avatar
Matthias Clasen committed
693 694
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
695 696
}

Matthias Clasen's avatar
Matthias Clasen committed
697 698 699 700 701 702
static Window
get_client_window_at_coords_recurse (GdkDisplay *display,
                                     Window      win,
                                     gboolean    is_toplevel,
                                     gint        x,
                                     gint        y)
703
{
Matthias Clasen's avatar
Matthias Clasen committed
704 705 706 707 708 709
  GdkChildInfoX11 *children;
  unsigned int nchildren;
  int i;
  gboolean found_child = FALSE;
  GdkChildInfoX11 child = { 0, };
  gboolean has_wm_state = FALSE;
710

Matthias Clasen's avatar
Matthias Clasen committed
711 712 713 714
  if (!_gdk_x11_get_window_child_info (display, win, TRUE,
                                       is_toplevel? &has_wm_state : NULL,
                                       &children, &nchildren))
    return None;
715

Matthias Clasen's avatar
Matthias Clasen committed
716 717 718
  if (has_wm_state)
    {
      g_free (children);
719

Matthias Clasen's avatar
Matthias Clasen committed
720 721
      return win;
    }
722

Matthias Clasen's avatar
Matthias Clasen committed
723 724 725
  for (i = nchildren - 1; (i >= 0) && !found_child; i--)
    {
      GdkChildInfoX11 *cur_child = &children[i];
726

Matthias Clasen's avatar
Matthias Clasen committed
727 728 729 730 731 732 733 734 735 736
      if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
          (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
          (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
        {
          x -= cur_child->x;
          y -= cur_child->y;
          child = *cur_child;
          found_child = TRUE;
        }
    }
737

Matthias Clasen's avatar
Matthias Clasen committed
738
  g_free (children);
739

Matthias Clasen's avatar
Matthias Clasen committed
740 741 742 743 744 745 746 747 748
  if (found_child)
    {
      if (child.has_wm_state)
        return child.window;
      else
        return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
    }
  else
    return None;
749
}
750

Matthias Clasen's avatar
Matthias Clasen committed
751 752 753 754 755
static Window
get_client_window_at_coords (GdkWindowCache *cache,
                             Window          ignore,
                             gint            x_root,
                             gint            y_root)
756
{
Matthias Clasen's avatar
Matthias Clasen committed
757 758
  GList *tmp_list;
  Window retval = None;
759
  GdkDisplay *display;
760

Matthias Clasen's avatar
Matthias Clasen committed
761
  display = gdk_screen_get_display (cache->screen);
762

Matthias Clasen's avatar
Matthias Clasen committed
763
  gdk_x11_display_error_trap_push (display);
764

Matthias Clasen's avatar
Matthias Clasen committed
765
  tmp_list = cache->children;
766

Matthias Clasen's avatar
Matthias Clasen committed
767
  while (tmp_list && !retval)
768
    {
Matthias Clasen's avatar
Matthias Clasen committed
769
      GdkCacheChild *child = tmp_list->data;
770

Matthias Clasen's avatar
Matthias Clasen committed
771
      if ((child->xid != ignore) && (child->mapped))
772
        {
Matthias Clasen's avatar
Matthias Clasen committed
773 774
          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
              (y_root >= child->y) && (y_root < child->y + child->height))
775
            {
Matthias Clasen's avatar
Matthias Clasen committed
776 777 778 779 780 781 782 783 784 785 786 787 788 789
              if (!is_pointer_within_shape (display, child,
                                            x_root - child->x,
                                            y_root - child->y))
                {
                  tmp_list = tmp_list->next;
                  continue;
                }

              retval = get_client_window_at_coords_recurse (display,
                  child->xid, TRUE,
                  x_root - child->x,
                  y_root - child->y);
              if (!retval)
                retval = child->xid;
790 791
            }
        }
Matthias Clasen's avatar
Matthias Clasen committed
792
      tmp_list = tmp_list->next;
793
    }
794

Matthias Clasen's avatar
Matthias Clasen committed
795
  gdk_x11_display_error_trap_pop_ignored (display);
796

Matthias Clasen's avatar
Matthias Clasen committed
797 798 799 800 801
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
802

Matthias Clasen's avatar
Matthias Clasen committed
803 804 805 806 807
#ifdef G_ENABLE_DEBUG
static void
print_target_list (GList *targets)
{
  while (targets)
808
    {
Matthias Clasen's avatar
Matthias Clasen committed
809 810 811 812
      gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
      g_message ("\t%s", name);
      g_free (name);
      targets = targets->next;
813 814
    }
}
Matthias Clasen's avatar
Matthias Clasen committed
815
#endif /* G_ENABLE_DEBUG */
816 817 818 819 820 821 822 823

/*************************************************************
 ***************************** XDND **************************
 *************************************************************/

/* Utility functions */

static struct {
824
  const gchar *name;
825
  GdkAtom atom;
826 827
  GdkDragAction action;
} xdnd_actions_table[] = {
828 829 830 831 832
    { "XdndActionCopy",    None, GDK_ACTION_COPY },
    { "XdndActionMove",    None, GDK_ACTION_MOVE },
    { "XdndActionLink",    None, GDK_ACTION_LINK },
    { "XdndActionAsk",     None, GDK_ACTION_ASK  },
    { "XdndActionPrivate", None, GDK_ACTION_COPY },
833 834
  };

835
static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
836 837 838 839 840 841
static gboolean xdnd_actions_initialized = FALSE;

static void
xdnd_initialize_actions (void)
{
  gint i;
842

843
  xdnd_actions_initialized = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
844
  for (i = 0; i < xdnd_n_actions; i++)
Matthias Clasen's avatar
Matthias Clasen committed
845
    xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
846 847 848
}

static GdkDragAction
849
xdnd_action_from_atom (GdkDisplay *display,
850
                       Atom        xatom)
851
{
852
  GdkAtom atom;
853 854
  gint i;

855 856 857 858 859
  if (xatom == None)
    return 0;

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

860 861 862
  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
863
  for (i = 0; i < xdnd_n_actions; i++)
864 865 866 867 868 869
    if (atom == xdnd_actions_table[i].atom)
      return xdnd_actions_table[i].action;

  return 0;
}

870
static Atom
871
xdnd_action_to_atom (GdkDisplay    *display,
872
                     GdkDragAction  action)
873 874 875 876 877 878
{
  gint i;

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
879
  for (i = 0; i < xdnd_n_actions; i++)
880
    if (action == xdnd_actions_table[i].action)
881
      return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
882

883
  return None;
884 885 886 887
}

/* Source side */

888
static GdkFilterReturn
889
xdnd_status_filter (GdkXEvent *xev,
890 891
                    GdkEvent  *event,
                    gpointer   data)
892
{
893
  GdkDisplay *display;
894 895 896
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];