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

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
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
  guint16 last_x;              /* Coordinates from last event */
80
  guint16 last_y;
81 82 83
  GdkDragAction old_action;    /* The last action we sent to the source */
  GdkDragAction old_actions;   /* The last actions we sent to the source */
  GdkDragAction xdnd_actions;  /* What is currently set in XdndActionList */
84 85 86
  guint version;               /* Xdnd protocol version */

  GSList *window_caches;
87

88 89
  GdkWindow *drag_window;

90 91 92
  gint hot_x;
  gint hot_y;

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

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

107 108
/* Forward declarations */

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

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

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

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

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


152
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
153

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

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

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

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

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

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

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

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

231
  contexts = g_list_remove (contexts, context);
232

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

236 237
/* Drag Contexts */

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

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

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

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

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

271 272 273
  return NULL;
}

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

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

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

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

      g_ptr_array_free (targets, TRUE);
    }
}

297 298
/* Utility functions */

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

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

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

  g_free (child);
}

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

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

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

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

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

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

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

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

561
  g_free (children);
562

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

582 583 584 585 586 587
  return result;
}

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

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

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

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

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

  g_free (cache);
607 608
}

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

  return cache;
}

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

  cache->ref_count -= 1;

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

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

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

644
  cache = gdk_window_cache_new (screen);
645

646
  window_caches = g_slist_prepend (window_caches, cache);
647

648
  return cache;
649 650
}

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

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

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

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

690
      child->shape_valid = TRUE;
691 692
    }

693 694
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
695 696
}

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

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

716 717 718
  if (has_wm_state)
    {
      g_free (children);
719

720 721
      return win;
    }
722

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

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

738
  g_free (children);
739

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

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

761
  display = gdk_screen_get_display (cache->screen);
762

763
  gdk_x11_display_error_trap_push (display);
764

765
  tmp_list = cache->children;
766

767
  while (tmp_list && !retval)
768
    {
769
      GdkCacheChild *child = tmp_list->data;
770

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

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

795
  gdk_x11_display_error_trap_pop_ignored (display);
796

797 798 799 800 801
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
802

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

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

/* Utility functions */

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

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

static void
xdnd_initialize_actions (void)
{
  gint i;
842

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

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

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

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

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

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

  return 0;
}

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

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

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

883
  return None;
884 885 886 887
}

/* Source side */

888
static GdkFilterReturn
889
xdnd_status_filter (GdkXEvent *xev,
890 891
                    GdkEvent  *event,
                    gpointer   data)
892
{
893
  GdkDisplay *display;
894 895 896
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  guint32 flags = xevent->xclient.data.l[1];
897
  Atom action = xevent->xclient.data.l[4];
898
  GdkDragContext *context;
899 900 901

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
902 903 904 905 906
    return GDK_FILTER_CONTINUE;                 /* Not for us */

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

908
  display = gdk_window_get_display (event->any.window);
909
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
910

911 912
  if (context)
    {
913
      GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
914 915
      if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
        context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
916

917 918 919
      event->dnd.send_event = FALSE;
      event->dnd.type = GDK_DRAG_STATUS;
      event->dnd.context = context;
920
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
921
      g_object_ref (context);
922 923 924

      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
      if (!(action != 0) != !(flags & 1))
925 926 927 928 929
        {
          GDK_NOTE (DND,
                    g_warning ("Received status event with flags not corresponding to action!\n"));
          action = 0;
        }
930

931
      context->action = xdnd_action_from_atom (display, action);
932 933 934 935 936 937 938

      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

939
static GdkFilterReturn
940
xdnd_finished_filter (GdkXEvent *xev,
941 942
                      GdkEvent  *event,
                      gpointer   data)
943
{
944
  GdkDisplay *display;
945 946 947
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  GdkDragContext *context;
948
  GdkX11DragContext *context_x11;
949 950 951

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
952 953 954 955
    return GDK_FILTER_CONTINUE;                 /* Not for us */

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

957
  display = gdk_window_get_display (event->any.window);
958
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
959

960 961
  if (context)
    {
962
      context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
963
      if (context_x11->version == 5)
964
        context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
Matthias Clasen's avatar
Matthias Clasen committed
965

966 967
      event->dnd.type = GDK_DROP_FINISHED;
      event->dnd.context = context;
968
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
969
      g_object_ref (context);
970

971 972
      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */

973 974 975 976 977 978 979
      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

static void
980
xdnd_set_targets (GdkX11DragContext *context_x11)
981
{
Matthias Clasen's avatar
Matthias Clasen committed
982
  GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
983
  Atom *atomlist;
984 985
  GList *tmp_list = context->targets;
  gint i;
986
  gint n_atoms = g_list_length (context->targets);
987
  GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
988

989
  atomlist = g_new (Atom, n_atoms);
990 991 992
  i = 0;
  while (tmp_list)
    {
993
      atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
994 995 996 997
      tmp_list = tmp_list->next;
      i++;
    }

998
  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
999 1000 1001 1002
                   GDK_WINDOW_XID (context->source_window),
                   gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
                   XA_ATOM, 32, PropModeReplace,
                   (guchar *)atomlist, n_atoms);
1003

1004 1005
  g_free (atomlist);

Matthias Clasen's avatar
Matthias Clasen committed
1006
  context_x11->xdnd_targets_set = 1;
1007 1008
}

1009
static void
1010
xdnd_set_actions (GdkX11DragContext *context_x11)
1011
{
Matthias Clasen's avatar
Matthias Clasen committed
1012
  GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1013
  Atom *atomlist;
1014 1015 1016
  gint i;
  gint n_atoms;
  guint actions;
1017
  GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1018 1019 1020

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();
1021

1022 1023
  actions = context->actions;
  n_atoms = 0;
Matthias Clasen's avatar
Matthias Clasen committed
1024
  for (i = 0; i < xdnd_n_actions; i++)
1025 1026
    {
      if (actions & xdnd_actions_table[i].action)
1027 1028 1029 1030
        {
          actions &= ~xdnd_actions_table[i].action;
          n_atoms++;
        }
1031 1032
    }

1033
  atomlist = g_new (Atom, n_atoms);
1034 1035 1036

  actions = context->actions;
  n_atoms = 0;
Matthias Clasen's avatar
Matthias Clasen committed
1037
  for (i = 0; i < xdnd_n_actions; i++)
1038 1039
    {
      if (actions & xdnd_actions_table[i].action)
1040 1041 1042 1043 1044
        {
          actions &= ~xdnd_actions_table[i].action;
          atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
          n_atoms++;
        }
1045 1046
    }