gdkdnd-x11.c 79.6 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 80
  gint start_x;                /* Where the drag started */
  gint start_y;
81
  guint16 last_x;              /* Coordinates from last event */
82
  guint16 last_y;
83 84 85
  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 */
86 87 88
  guint version;               /* Xdnd protocol version */

  GSList *window_caches;
89

90 91
  GdkWindow *drag_window;

92 93 94
  gint hot_x;
  gint hot_y;

95 96 97 98 99 100 101
  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 */
102 103
};

104 105 106 107 108
struct _GdkX11DragContextClass
{
  GdkDragContextClass parent_class;
};

109 110
/* Forward declarations */

111 112 113
static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
static void            gdk_window_cache_unref (GdkWindowCache *cache);
114

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

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

138
static GList *contexts;
139
static GSList *window_caches;
140

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


154
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
155

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

162 163
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
164 165 166 167 168
                                                     GdkWindow       *drag_window,
                                                     GdkScreen       *screen,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragProtocol *protocol);
169
static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
170 171 172 173 174 175 176
                                                     GdkWindow       *dest_window,
                                                     GdkDragProtocol  protocol,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragAction    suggested_action,
                                                     GdkDragAction    possible_actions,
                                                     guint32          time);
177
static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
178 179
                                                     GdkDragAction    action,
                                                     guint32          time_);
180
static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
181
                                                     guint32          time_);
182
static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
183
                                                     guint32          time_);
184
static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
185 186
                                                     gboolean         accept,
                                                     guint32          time_);
187
static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
188 189
                                                     gboolean         success,
                                                     guint32          time_);
190 191
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
192
static GdkWindow * gdk_x11_drag_context_get_drag_window (GdkDragContext *context);
193 194 195
static void        gdk_x11_drag_context_set_hotspot (GdkDragContext  *context,
                                                     gint             hot_x,
                                                     gint             hot_y);
196 197
static void        gdk_x11_drag_context_drop_done     (GdkDragContext  *context,
                                                       gboolean         success);
198

199
static void
200
gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
201
{
202
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Matthias Clasen's avatar
Matthias Clasen committed
203
  GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
204

205
  object_class->finalize = gdk_x11_drag_context_finalize;
Matthias Clasen's avatar
Matthias Clasen committed
206

207 208 209 210 211 212 213 214 215
  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;
216
  context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
217
  context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
218
  context_class->drop_done = gdk_x11_drag_context_drop_done;
219 220
}

221
static void
222
gdk_x11_drag_context_finalize (GObject *object)
223
{
224
  GdkDragContext *context = GDK_DRAG_CONTEXT (object);
225
  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object);
226
  GdkWindow *drag_window;
227 228

  if (context->source_window)
229
    {
Matthias Clasen's avatar
Matthias Clasen committed
230
      if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
231 232
        xdnd_manage_source_filter (context, context->source_window, FALSE);
    }
233

234 235
  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
236

237
  contexts = g_list_remove (contexts, context);
238

239 240
  drag_window = context->drag_window;

241
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
242 243 244

  if (drag_window)
    gdk_window_destroy (drag_window);
245
}
246

247 248
/* Drag Contexts */

249
static GdkDragContext *
250
gdk_drag_context_find (GdkDisplay *display,
251 252 253
                       gboolean    is_source,
                       Window      source_xid,
                       Window      dest_xid)
254
{
255
  GList *tmp_list;
Jeff Garzik's avatar
Jeff Garzik committed
256
  GdkDragContext *context;
257
  GdkX11DragContext *context_x11;
258
  Window context_dest_xid;
259

260
  for (tmp_list = contexts; tmp_list; tmp_list = tmp_list->next)
261
    {
Jeff Garzik's avatar
Jeff Garzik committed
262
      context = (GdkDragContext *)tmp_list->data;
263
      context_x11 = (GdkX11DragContext *)context;
264

265
      if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
266
          (context->dest_window && gdk_window_get_display (context->dest_window) != display))
Matthias Clasen's avatar
Matthias Clasen committed
267
        continue;
268

Matthias Clasen's avatar
Matthias Clasen committed
269 270 271 272 273
      context_dest_xid = context->dest_window
                            ? (context_x11->drop_xid
                                  ? context_x11->drop_xid
                                  : GDK_WINDOW_XID (context->dest_window))
                            : None;
274 275

      if ((!context->is_source == !is_source) &&
Matthias Clasen's avatar
Matthias Clasen committed
276 277 278 279
          ((source_xid == None) || (context->source_window &&
            (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
          ((dest_xid == None) || (context_dest_xid == dest_xid)))
        return context;
280
    }
Matthias Clasen's avatar
Matthias Clasen committed
281

282 283 284
  return NULL;
}

285 286 287 288 289 290 291 292 293 294
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)
295
        g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
296 297

      _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
298 299
                               (const gchar **)targets->pdata,
                               targets->len);
300 301

      for (i =0; i < targets->len; i++)
302
        g_free (targets->pdata[i]);
303 304 305 306 307

      g_ptr_array_free (targets, TRUE);
    }
}

308 309
/* Utility functions */

310 311 312 313 314
static void
free_cache_child (GdkCacheChild *child,
                  GdkDisplay    *display)
{
  if (child->shape)
Benjamin Otte's avatar
Benjamin Otte committed
315
    cairo_region_destroy (child->shape);
316 317 318

  if (child->shape_selected && display)
    {
319
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
320 321 322 323 324 325 326

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

  g_free (child);
}

327 328
static void
gdk_window_cache_add (GdkWindowCache *cache,
329 330 331 332 333 334
                      guint32         xid,
                      gint            x,
                      gint            y,
                      gint            width,
                      gint            height,
                      gboolean        mapped)
335 336 337 338 339 340 341 342 343
{
  GdkCacheChild *child = g_new (GdkCacheChild, 1);

  child->xid = xid;
  child->x = x;
  child->y = y;
  child->width = width;
  child->height = height;
  child->mapped = mapped;
344 345 346
  child->shape_selected = FALSE;
  child->shape_valid = FALSE;
  child->shape = NULL;
347 348

  cache->children = g_list_prepend (cache->children, child);
349 350
  g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
                       cache->children);
351 352
}

353 354 355 356 357 358 359 360
static GdkFilterReturn
gdk_window_cache_shape_filter (GdkXEvent *xev,
                               GdkEvent  *event,
                               gpointer   data)
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

361
  GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

  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
377
              cairo_region_destroy (child->shape);
378 379 380 381 382 383 384 385 386 387
              child->shape = NULL;
            }
        }

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

388 389
static GdkFilterReturn
gdk_window_cache_filter (GdkXEvent *xev,
390 391
                         GdkEvent  *event,
                         gpointer   data)
392 393 394 395 396 397 398 399 400 401
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

  switch (xevent->type)
    {
    case CirculateNotify:
      break;
    case ConfigureNotify:
      {
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 430 431 432 433 434 435 436 437 438 439 440
        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;
441 442 443
      }
    case CreateNotify:
      {
444 445 446 447 448 449 450 451
        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;
452 453 454
      }
    case DestroyNotify:
      {
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
        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;
472 473 474
      }
    case MapNotify:
      {
475 476 477 478 479 480 481 482 483 484 485
        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;
486 487 488 489 490
      }
    case ReparentNotify:
      break;
    case UnmapNotify:
      {
491 492 493 494 495 496 497 498 499 500 501
        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;
502 503 504 505 506 507 508 509
      }
    default:
      return GDK_FILTER_CONTINUE;
    }
  return GDK_FILTER_REMOVE;
}

static GdkWindowCache *
510
gdk_window_cache_new (GdkScreen *screen)
511 512
{
  XWindowAttributes xwa;
513 514
  Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
  GdkWindow *root_window = gdk_screen_get_root_window (screen);
515 516
  GdkChildInfoX11 *children;
  guint nchildren, i;
517
#ifdef HAVE_XCOMPOSITE
518
  Window cow;
519
#endif
520

521 522 523 524
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

  result->children = NULL;
  result->child_hash = g_hash_table_new (g_direct_hash, NULL);
525
  result->screen = screen;
526
  result->ref_count = 1;
527

528
  XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
529
  result->old_event_mask = xwa.your_event_mask;
530

531
  if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
532 533 534
    {
      GList *toplevel_windows, *list;
      GdkWindow *window;
535
      GdkWindowImplX11 *impl;
536
      gint x, y, width, height;
537

538
      toplevel_windows = gdk_screen_get_toplevel_windows (screen);
539 540 541
      for (list = toplevel_windows; list; list = list->next)
        {
          window = GDK_WINDOW (list->data);
542
	  impl = GDK_WINDOW_IMPL_X11 (window->impl);
543 544
          gdk_window_get_geometry (window, &x, &y, &width, &height);
          gdk_window_cache_add (result, GDK_WINDOW_XID (window),
545 546 547
                                x * impl->window_scale, y * impl->window_scale, 
				width * impl->window_scale, 
				height * impl->window_scale,
548 549
                                gdk_window_is_visible (window));
        }
550 551 552 553
      g_list_free (toplevel_windows);
      return result;
    }

554
  XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
555
                result->old_event_mask | SubstructureNotifyMask);
556
  gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
557
  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
558

559
  if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
560 561 562
                                       GDK_WINDOW_XID (root_window),
                                       FALSE, NULL,
                                       &children, &nchildren))
563
    return result;
564

565 566
  for (i = 0; i < nchildren ; i++)
    {
567
      gdk_window_cache_add (result, children[i].window,
568 569
                            children[i].x, children[i].y, children[i].width, children[i].height,
                            children[i].is_mapped);
570 571
    }

572
  g_free (children);
573

574
#ifdef HAVE_XCOMPOSITE
575 576 577 578 579 580 581 582 583
  /*
   * 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))
    {
584
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
585 586 587 588
      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);
589
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
590
    }
591
#endif
592

593 594 595 596 597 598
  return result;
}

static void
gdk_window_cache_destroy (GdkWindowCache *cache)
{
599
  GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
600
  GdkDisplay *display;
601 602

  XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
603 604
                GDK_WINDOW_XID (root_window),
                cache->old_event_mask);
605
  gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
606
  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
607

608
  display = gdk_screen_get_display (cache->screen);
609

610 611 612
  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);
613

614 615
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
616 617

  g_free (cache);
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 644 645 646 647 648 649 650 651 652 653 654
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
655
  cache = gdk_window_cache_new (screen);
656

Matthias Clasen's avatar
Matthias Clasen committed
657
  window_caches = g_slist_prepend (window_caches, cache);
658

Matthias Clasen's avatar
Matthias Clasen committed
659
  return cache;
660 661
}

Matthias Clasen's avatar
Matthias Clasen committed
662 663 664 665 666
static gboolean
is_pointer_within_shape (GdkDisplay    *display,
                         GdkCacheChild *child,
                         gint           x_pos,
                         gint           y_pos)
667
{
Matthias Clasen's avatar
Matthias Clasen committed
668
  if (!child->shape_selected)
669
    {
Matthias Clasen's avatar
Matthias Clasen committed
670
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
671

Matthias Clasen's avatar
Matthias Clasen committed
672 673 674 675 676 677 678
      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;
679

Matthias Clasen's avatar
Matthias Clasen committed
680 681 682
      child->shape = NULL;
      if (gdk_display_supports_shapes (display))
        child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
683
                                                   child->xid, 1,  ShapeBounding);
Matthias Clasen's avatar
Matthias Clasen committed
684 685 686 687
#ifdef ShapeInput
      input_shape = NULL;
      if (gdk_display_supports_input_shapes (display))
        input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
688
                                                  child->xid, 1, ShapeInput);
689

Matthias Clasen's avatar
Matthias Clasen committed
690 691 692 693 694 695 696 697 698 699
      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
700

Matthias Clasen's avatar
Matthias Clasen committed
701
      child->shape_valid = TRUE;
702 703
    }

Matthias Clasen's avatar
Matthias Clasen committed
704 705
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
706 707
}

Matthias Clasen's avatar
Matthias Clasen committed
708 709 710 711 712 713
static Window
get_client_window_at_coords_recurse (GdkDisplay *display,
                                     Window      win,
                                     gboolean    is_toplevel,
                                     gint        x,
                                     gint        y)
714
{
Matthias Clasen's avatar
Matthias Clasen committed
715 716 717 718 719 720
  GdkChildInfoX11 *children;
  unsigned int nchildren;
  int i;
  gboolean found_child = FALSE;
  GdkChildInfoX11 child = { 0, };
  gboolean has_wm_state = FALSE;
721

Matthias Clasen's avatar
Matthias Clasen committed
722 723 724 725
  if (!_gdk_x11_get_window_child_info (display, win, TRUE,
                                       is_toplevel? &has_wm_state : NULL,
                                       &children, &nchildren))
    return None;
726

Matthias Clasen's avatar
Matthias Clasen committed
727 728 729
  if (has_wm_state)
    {
      g_free (children);
730

Matthias Clasen's avatar
Matthias Clasen committed
731 732
      return win;
    }
733

Matthias Clasen's avatar
Matthias Clasen committed
734 735 736
  for (i = nchildren - 1; (i >= 0) && !found_child; i--)
    {
      GdkChildInfoX11 *cur_child = &children[i];
737

Matthias Clasen's avatar
Matthias Clasen committed
738 739 740 741 742 743 744 745 746 747
      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;
        }
    }
748

Matthias Clasen's avatar
Matthias Clasen committed
749
  g_free (children);
750

Matthias Clasen's avatar
Matthias Clasen committed
751 752 753 754 755 756 757 758 759
  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;
760
}
761

Matthias Clasen's avatar
Matthias Clasen committed
762 763 764 765 766
static Window
get_client_window_at_coords (GdkWindowCache *cache,
                             Window          ignore,
                             gint            x_root,
                             gint            y_root)
767
{
Matthias Clasen's avatar
Matthias Clasen committed
768 769
  GList *tmp_list;
  Window retval = None;
770
  GdkDisplay *display;
771

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

Matthias Clasen's avatar
Matthias Clasen committed
774
  gdk_x11_display_error_trap_push (display);
775

Matthias Clasen's avatar
Matthias Clasen committed
776
  tmp_list = cache->children;
777

Matthias Clasen's avatar
Matthias Clasen committed
778
  while (tmp_list && !retval)
779
    {
Matthias Clasen's avatar
Matthias Clasen committed
780
      GdkCacheChild *child = tmp_list->data;
781

Matthias Clasen's avatar
Matthias Clasen committed
782
      if ((child->xid != ignore) && (child->mapped))
783
        {
Matthias Clasen's avatar
Matthias Clasen committed
784 785
          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
              (y_root >= child->y) && (y_root < child->y + child->height))
786
            {
Matthias Clasen's avatar
Matthias Clasen committed
787 788 789 790 791 792 793 794 795 796 797 798 799 800
              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;
801 802
            }
        }
Matthias Clasen's avatar
Matthias Clasen committed
803
      tmp_list = tmp_list->next;
804
    }
805

Matthias Clasen's avatar
Matthias Clasen committed
806
  gdk_x11_display_error_trap_pop_ignored (display);
807

Matthias Clasen's avatar
Matthias Clasen committed
808 809 810 811 812
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
813

Matthias Clasen's avatar
Matthias Clasen committed
814 815 816 817 818
#ifdef G_ENABLE_DEBUG
static void
print_target_list (GList *targets)
{
  while (targets)
819
    {
Matthias Clasen's avatar
Matthias Clasen committed
820 821 822 823
      gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
      g_message ("\t%s", name);
      g_free (name);
      targets = targets->next;
824 825
    }
}
Matthias Clasen's avatar
Matthias Clasen committed
826
#endif /* G_ENABLE_DEBUG */
827 828 829 830 831 832 833 834

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

/* Utility functions */

static struct {
835
  const gchar *name;
836
  GdkAtom atom;
837 838
  GdkDragAction action;
} xdnd_actions_table[] = {
839 840 841 842 843
    { "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 },
844 845
  };

846
static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
847 848 849 850 851 852
static gboolean xdnd_actions_initialized = FALSE;

static void
xdnd_initialize_actions (void)
{
  gint i;
853

854
  xdnd_actions_initialized = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
855
  for (i = 0; i < xdnd_n_actions; i++)
Matthias Clasen's avatar
Matthias Clasen committed
856
    xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
857 858 859
}

static GdkDragAction
860
xdnd_action_from_atom (GdkDisplay *display,
861
                       Atom        xatom)
862
{
863
  GdkAtom atom;
864 865
  gint i;

866 867 868 869 870
  if (xatom == None)
    return 0;

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

871 872 873
  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
874
  for (i = 0; i < xdnd_n_actions; i++)
875 876 877 878 879 880
    if (atom == xdnd_actions_table[i].atom)
      return xdnd_actions_table[i].action;

  return 0;
}

881
static Atom
882
xdnd_action_to_atom (GdkDisplay    *display,
883
                     GdkDragAction  action)
884 885 886 887 888 889
{
  gint i;

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
890
  for (i = 0; i < xdnd_n_actions; i++)
891
    if (action == xdnd_actions_table[i].action)
892
      return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
893

894
  return None;
895 896 897