gdkdnd-x11.c 79.2 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 227

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

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

236
  contexts = g_list_remove (contexts, context);
237

238
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
239
}
240

241 242
/* Drag Contexts */

243
static GdkDragContext *
244
gdk_drag_context_find (GdkDisplay *display,
245 246 247
                       gboolean    is_source,
                       Window      source_xid,
                       Window      dest_xid)
248
{
249
  GList *tmp_list;
Jeff Garzik's avatar
Jeff Garzik committed
250
  GdkDragContext *context;
251
  GdkX11DragContext *context_x11;
252
  Window context_dest_xid;
253

254
  for (tmp_list = contexts; tmp_list; tmp_list = tmp_list->next)
255
    {
Jeff Garzik's avatar
Jeff Garzik committed
256
      context = (GdkDragContext *)tmp_list->data;
257
      context_x11 = (GdkX11DragContext *)context;
258

259
      if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
260
          (context->dest_window && gdk_window_get_display (context->dest_window) != display))
Matthias Clasen's avatar
Matthias Clasen committed
261
        continue;
262

Matthias Clasen's avatar
Matthias Clasen committed
263 264 265 266 267
      context_dest_xid = context->dest_window
                            ? (context_x11->drop_xid
                                  ? context_x11->drop_xid
                                  : GDK_WINDOW_XID (context->dest_window))
                            : None;
268 269

      if ((!context->is_source == !is_source) &&
Matthias Clasen's avatar
Matthias Clasen committed
270 271 272 273
          ((source_xid == None) || (context->source_window &&
            (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
          ((dest_xid == None) || (context_dest_xid == dest_xid)))
        return context;
274
    }
Matthias Clasen's avatar
Matthias Clasen committed
275

276 277 278
  return NULL;
}

279 280 281 282 283 284 285 286 287 288
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)
289
        g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
290 291

      _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
292 293
                               (const gchar **)targets->pdata,
                               targets->len);
294 295

      for (i =0; i < targets->len; i++)
296
        g_free (targets->pdata[i]);
297 298 299 300 301

      g_ptr_array_free (targets, TRUE);
    }
}

302 303
/* Utility functions */

304 305 306 307 308
static void
free_cache_child (GdkCacheChild *child,
                  GdkDisplay    *display)
{
  if (child->shape)
Benjamin Otte's avatar
Benjamin Otte committed
309
    cairo_region_destroy (child->shape);
310 311 312

  if (child->shape_selected && display)
    {
313
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
314 315 316 317 318 319 320

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

  g_free (child);
}

321 322
static void
gdk_window_cache_add (GdkWindowCache *cache,
323 324 325 326 327 328
                      guint32         xid,
                      gint            x,
                      gint            y,
                      gint            width,
                      gint            height,
                      gboolean        mapped)
329 330 331 332 333 334 335 336 337
{
  GdkCacheChild *child = g_new (GdkCacheChild, 1);

  child->xid = xid;
  child->x = x;
  child->y = y;
  child->width = width;
  child->height = height;
  child->mapped = mapped;
338 339 340
  child->shape_selected = FALSE;
  child->shape_valid = FALSE;
  child->shape = NULL;
341 342

  cache->children = g_list_prepend (cache->children, child);
343 344
  g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
                       cache->children);
345 346
}

347 348 349 350 351 352 353 354
static GdkFilterReturn
gdk_window_cache_shape_filter (GdkXEvent *xev,
                               GdkEvent  *event,
                               gpointer   data)
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

355
  GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

  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
371
              cairo_region_destroy (child->shape);
372 373 374 375 376 377 378 379 380 381
              child->shape = NULL;
            }
        }

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

382 383
static GdkFilterReturn
gdk_window_cache_filter (GdkXEvent *xev,
384 385
                         GdkEvent  *event,
                         gpointer   data)
386 387 388 389 390 391 392 393 394 395
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

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

static GdkWindowCache *
504
gdk_window_cache_new (GdkScreen *screen)
505 506
{
  XWindowAttributes xwa;
507 508
  Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
  GdkWindow *root_window = gdk_screen_get_root_window (screen);
509 510
  GdkChildInfoX11 *children;
  guint nchildren, i;
511
#ifdef HAVE_XCOMPOSITE
512
  Window cow;
513
#endif
514

515 516 517 518
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

  result->children = NULL;
  result->child_hash = g_hash_table_new (g_direct_hash, NULL);
519
  result->screen = screen;
520
  result->ref_count = 1;
521

522
  XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
523
  result->old_event_mask = xwa.your_event_mask;
524

525
  if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
526 527 528
    {
      GList *toplevel_windows, *list;
      GdkWindow *window;
529
      GdkWindowImplX11 *impl;
530
      gint x, y, width, height;
531

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

548
  XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
549
                result->old_event_mask | SubstructureNotifyMask);
550
  gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
551
  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
552

553
  if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
554 555 556
                                       GDK_WINDOW_XID (root_window),
                                       FALSE, NULL,
                                       &children, &nchildren))
557
    return result;
558

559 560
  for (i = 0; i < nchildren ; i++)
    {
561
      gdk_window_cache_add (result, children[i].window,
562 563
                            children[i].x, children[i].y, children[i].width, children[i].height,
                            children[i].is_mapped);
564 565
    }

566
  g_free (children);
567

568
#ifdef HAVE_XCOMPOSITE
569 570 571 572 573 574 575 576 577
  /*
   * 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))
    {
578
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
579 580 581 582
      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);
583
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
584
    }
585
#endif
586

587 588 589 590 591 592
  return result;
}

static void
gdk_window_cache_destroy (GdkWindowCache *cache)
{
593
  GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
594
  GdkDisplay *display;
595 596

  XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
597 598
                GDK_WINDOW_XID (root_window),
                cache->old_event_mask);
599
  gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
600
  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
601

602
  display = gdk_screen_get_display (cache->screen);
603

604 605 606
  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);
607

608 609
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
610 611

  g_free (cache);
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 644 645 646 647 648
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
649
  cache = gdk_window_cache_new (screen);
650

Matthias Clasen's avatar
Matthias Clasen committed
651
  window_caches = g_slist_prepend (window_caches, cache);
652

Matthias Clasen's avatar
Matthias Clasen committed
653
  return cache;
654 655
}

Matthias Clasen's avatar
Matthias Clasen committed
656 657 658 659 660
static gboolean
is_pointer_within_shape (GdkDisplay    *display,
                         GdkCacheChild *child,
                         gint           x_pos,
                         gint           y_pos)
661
{
Matthias Clasen's avatar
Matthias Clasen committed
662
  if (!child->shape_selected)
663
    {
Matthias Clasen's avatar
Matthias Clasen committed
664
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
665

Matthias Clasen's avatar
Matthias Clasen committed
666 667 668 669 670 671 672
      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;
673

Matthias Clasen's avatar
Matthias Clasen committed
674 675 676
      child->shape = NULL;
      if (gdk_display_supports_shapes (display))
        child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
677
                                                   child->xid, 1,  ShapeBounding);
Matthias Clasen's avatar
Matthias Clasen committed
678 679 680 681
#ifdef ShapeInput
      input_shape = NULL;
      if (gdk_display_supports_input_shapes (display))
        input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
682
                                                  child->xid, 1, ShapeInput);
683

Matthias Clasen's avatar
Matthias Clasen committed
684 685 686 687 688 689 690 691 692 693
      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
694

Matthias Clasen's avatar
Matthias Clasen committed
695
      child->shape_valid = TRUE;
696 697
    }

Matthias Clasen's avatar
Matthias Clasen committed
698 699
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
700 701
}

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

Matthias Clasen's avatar
Matthias Clasen committed
716 717 718 719
  if (!_gdk_x11_get_window_child_info (display, win, TRUE,
                                       is_toplevel? &has_wm_state : NULL,
                                       &children, &nchildren))
    return None;
720

Matthias Clasen's avatar
Matthias Clasen committed
721 722 723
  if (has_wm_state)
    {
      g_free (children);
724

Matthias Clasen's avatar
Matthias Clasen committed
725 726
      return win;
    }
727

Matthias Clasen's avatar
Matthias Clasen committed
728 729 730
  for (i = nchildren - 1; (i >= 0) && !found_child; i--)
    {
      GdkChildInfoX11 *cur_child = &children[i];
731

Matthias Clasen's avatar
Matthias Clasen committed
732 733 734 735 736 737 738 739 740 741
      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;
        }
    }
742

Matthias Clasen's avatar
Matthias Clasen committed
743
  g_free (children);
744

Matthias Clasen's avatar
Matthias Clasen committed
745 746 747 748 749 750 751 752 753
  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;
754
}
755

Matthias Clasen's avatar
Matthias Clasen committed
756 757 758 759 760
static Window
get_client_window_at_coords (GdkWindowCache *cache,
                             Window          ignore,
                             gint            x_root,
                             gint            y_root)
761
{
Matthias Clasen's avatar
Matthias Clasen committed
762 763
  GList *tmp_list;
  Window retval = None;
764
  GdkDisplay *display;
765

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

Matthias Clasen's avatar
Matthias Clasen committed
768
  gdk_x11_display_error_trap_push (display);
769

Matthias Clasen's avatar
Matthias Clasen committed
770
  tmp_list = cache->children;
771

Matthias Clasen's avatar
Matthias Clasen committed
772
  while (tmp_list && !retval)
773
    {
Matthias Clasen's avatar
Matthias Clasen committed
774
      GdkCacheChild *child = tmp_list->data;
775

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

Matthias Clasen's avatar
Matthias Clasen committed
800
  gdk_x11_display_error_trap_pop_ignored (display);
801

Matthias Clasen's avatar
Matthias Clasen committed
802 803 804 805 806
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
807

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

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

/* Utility functions */

static struct {
829
  const gchar *name;
830
  GdkAtom atom;
831 832
  GdkDragAction action;
} xdnd_actions_table[] = {
833 834 835 836 837
    { "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 },
838 839
  };

840
static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
841 842 843 844 845 846
static gboolean xdnd_actions_initialized = FALSE;

static void
xdnd_initialize_actions (void)
{
  gint i;
847

848
  xdnd_actions_initialized = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
849
  for (i = 0; i < xdnd_n_actions; i++)
Matthias Clasen's avatar
Matthias Clasen committed
850
    xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
851 852 853
}

static GdkDragAction
854
xdnd_action_from_atom (GdkDisplay *display,
855
                       Atom        xatom)
856
{
857
  GdkAtom atom;
858 859
  gint i;

860 861 862 863 864
  if (xatom == None)
    return 0;

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

865 866 867
  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
868
  for (i = 0; i < xdnd_n_actions; i++)
869 870 871 872 873 874
    if (atom == xdnd_actions_table[i].atom)
      return xdnd_actions_table[i].action;

  return 0;
}

875
static Atom
876
xdnd_action_to_atom (GdkDisplay    *display,
877
                     GdkDragAction  action)
878 879 880 881 882 883
{
  gint i;

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
884
  for (i = 0; i < xdnd_n_actions; i++)
885
    if (action == xdnd_actions_table[i].action)
886
      return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
887

888
  return None;
889 890 891 892
}

/* Source side */

893
static GdkFilterReturn
894
xdnd_status_filter (GdkXEvent *xev,
895 896
                    GdkEvent  *event,
                    gpointer   data)