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
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
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
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);
    }

655
  cache = gdk_window_cache_new (screen);
656

657
  window_caches = g_slist_prepend (window_caches, cache);
658

659
  return cache;
660 661
}

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

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

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);
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

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

701
      child->shape_valid = TRUE;
702 703
    }

704 705
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
706 707
}

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
{
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

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

727 728 729
  if (has_wm_state)
    {
      g_free (children);
730

731 732
      return win;
    }
733

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

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

749
  g_free (children);
750

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

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

772
  display = gdk_screen_get_display (cache->screen);
773

774
  gdk_x11_display_error_trap_push (display);
775

776
  tmp_list = cache->children;
777

778
  while (tmp_list && !retval)
779
    {
780
      GdkCacheChild *child = tmp_list->data;
781

782
      if ((child->xid != ignore) && (child->mapped))
783
        {
784 785
          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
              (y_root >= child->y) && (y_root < child->y + child->height))
786
            {
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
            }
        }
803
      tmp_list = tmp_list->next;
804
    }
805

806
  gdk_x11_display_error_trap_pop_ignored (display);
807

808 809 810 811 812
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
813

814 815 816 817 818
#ifdef G_ENABLE_DEBUG
static void
print_target_list (GList *targets)
{
  while (targets)
819
    {
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
    }
}
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 898
}

/* Source side */

899
static GdkFilterReturn
900
xdnd_status_filter (GdkXEvent *xev,
901 902
                    GdkEvent  *event,
                    gpointer   data)
903
{
904
  GdkDisplay *display;
905 906 907
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  guint32 flags = xevent->xclient.data.l[1];
908
  Atom action = xevent->xclient.data.l[4];
909
  GdkDragContext *context;
910 911 912

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
913 914 915 916 917
    return GDK_FILTER_CONTINUE;                 /* Not for us */

  GDK_NOTE (DND,
            g_message ("XdndStatus: dest_window: %#x  action: %ld",
                       dest_window, action));
918

919
  display = gdk_window_get_display (event->any.window);
920
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
921

922 923
  if (context)
    {
924
      GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
925 926
      if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
        context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
927

928 929 930
      event->dnd.send_event = FALSE;
      event->dnd.type = GDK_DRAG_STATUS;
      event->dnd.context = context;
931
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
932
      g_object_ref (context);
933 934 935

      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
      if (!(action != 0) != !(flags & 1))
936 937 938 939 940
        {
          GDK_NOTE (DND,
                    g_warning ("Received status event with flags not corresponding to action!\n"));
          action = 0;
        }
941

942
      context->action = xdnd_action_from_atom (display, action);
943 944 945 946 947 948 949

      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

950
static GdkFilterReturn
951
xdnd_finished_filter (GdkXEvent *xev,
952 953
                      GdkEvent  *event,
                      gpointer   data)
954
{
955
  GdkDisplay *display;
956 957 958
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  GdkDragContext *context;
959
  GdkX11DragContext *context_x11;
960 961 962

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
963 964 965 966
    return GDK_FILTER_CONTINUE;                 /* Not for us */

  GDK_NOTE (DND,
            g_message ("XdndFinished: dest_window: %#x", dest_window));
967

968
  display = gdk_window_get_display (event->any.window);
969
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
970

971 972
  if (context)
    {
973
      context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
974
      if (context_x11->version == 5)
975
        context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
Matthias Clasen's avatar
Matthias Clasen committed
976

977 978
      event->dnd.type = GDK_DROP_FINISHED;
      event->dnd.context = context;
979
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
980
      g_object_ref (context);
981

982 983
      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */

984 985 986 987 988 989 990
      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

static void
991
xdnd_set_targets (GdkX11DragContext *context_x11)
992
{
Matthias Clasen's avatar
Matthias Clasen committed
993
  GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
994
  Atom *atomlist;
995 996
  GList *tmp_list = context->targets;
  gint i;
997
  gint n_atoms = g_list_length (context->targets);
998
  GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
999

1000
  atomlist = g_new (Atom, n_atoms);
1001 1002 1003
  i = 0;
  while (tmp_list)
    {
1004
      atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
1005 1006 1007 1008
      tmp_list = tmp_list->next;
      i++;
    }

1009
  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
1010 1011 1012 1013
                   GDK_WINDOW_XID (context->source_window),
                   gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
                   XA_ATOM, 32, PropModeReplace,
                   (guchar *)atomlist, n_atoms);
1014

1015 1016
  g_free (atomlist);

Matthias Clasen's avatar
Matthias Clasen committed
1017
  context_x11->xdnd_targets_set = 1;
1018 1019
}

1020
static void
1021
xdnd_set_actions (GdkX11DragContext *context_x11)
1022
{
Matthias Clasen's avatar
Matthias Clasen committed
1023
  GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1024
  Atom *atomlist;
1025 1026 1027
  gint i;
  gint n_atoms;
  guint actions;
1028
  GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1029 1030 1031

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();
1032

1033 1034
  actions = context->actions;
  n_atoms = 0;
Matthias Clasen's avatar
Matthias Clasen committed
1035
  for (i = 0; i < xdnd_n_actions; i++)
1036 1037
    {
      if (actions & xdnd_actions_table[i].action)
1038 1039 1040 1041
        {
          actions &= ~xdnd_actions_table[i].action;
          n_atoms++;
        }
1042 1043
    }

1044
  atomlist = g_new (Atom, n_atoms);