gdkdnd-x11.c 75.5 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 93 94 95 96
  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 */
97 98
};

99 100 101 102 103
struct _GdkX11DragContextClass
{
  GdkDragContextClass parent_class;
};

104 105
/* Forward declarations */

106 107 108
static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
static void            gdk_window_cache_unref (GdkWindowCache *cache);
109

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

129
static void   xdnd_manage_source_filter (GdkDragContext *context,
130 131
                                         GdkWindow      *window,
                                         gboolean        add_filter);
132

133
static GList *contexts;
134
static GSList *window_caches;
135

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


149
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
150

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

157 158
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
159 160 161 162 163
                                                     GdkWindow       *drag_window,
                                                     GdkScreen       *screen,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragProtocol *protocol);
164
static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
165 166 167 168 169 170 171
                                                     GdkWindow       *dest_window,
                                                     GdkDragProtocol  protocol,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragAction    suggested_action,
                                                     GdkDragAction    possible_actions,
                                                     guint32          time);
172
static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
173 174
                                                     GdkDragAction    action,
                                                     guint32          time_);
175
static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
176
                                                     guint32          time_);
177
static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
178
                                                     guint32          time_);
179
static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
180 181
                                                     gboolean         accept,
                                                     guint32          time_);
182
static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
183 184
                                                     gboolean         success,
                                                     guint32          time_);
185 186
static gboolean    gdk_x11_drag_context_drop_status (GdkDragContext  *context);
static GdkAtom     gdk_x11_drag_context_get_selection (GdkDragContext  *context);
187

188 189 190 191 192 193
static GdkWindow *
gdk_x11_drag_context_get_drag_window (GdkDragContext *context)
{
  return GDK_X11_DRAG_CONTEXT (context)->drag_window;
}

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

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

202 203 204 205 206 207 208 209 210
  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;
211
  context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
212 213
}

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

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

226 227
  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
228

229
  contexts = g_list_remove (contexts, context);
230

231
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
232
}
233

234 235
/* Drag Contexts */

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

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

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

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

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

269 270 271
  return NULL;
}

272 273 274 275 276 277 278 279 280 281
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)
282
        g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
283 284

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

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

      g_ptr_array_free (targets, TRUE);
    }
}

295 296
/* Utility functions */

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

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

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

  g_free (child);
}

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

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

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

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

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

  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
364
              cairo_region_destroy (child->shape);
365 366 367 368 369 370 371 372 373 374
              child->shape = NULL;
            }
        }

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

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

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

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

508 509 510 511
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

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

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

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

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

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

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

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

559
  g_free (children);
560

561
#ifdef HAVE_XCOMPOSITE
562 563 564 565 566 567 568 569 570
  /*
   * 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))
    {
571
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
572 573 574 575
      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);
576
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
577
    }
578
#endif
579

580 581 582 583 584 585
  return result;
}

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

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

595
  display = gdk_screen_get_display (cache->screen);
596

597 598 599
  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);
600

601 602
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
603 604

  g_free (cache);
605 606
}

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

642
  cache = gdk_window_cache_new (screen);
643

644
  window_caches = g_slist_prepend (window_caches, cache);
645

646
  return cache;
647 648
}

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

659 660 661 662 663 664 665
      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;
666

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

677 678 679 680 681 682 683 684 685 686
      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
687

688
      child->shape_valid = TRUE;
689 690
    }

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

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

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

714 715 716
  if (has_wm_state)
    {
      g_free (children);
717

718 719
      return win;
    }
720

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

725 726 727 728 729 730 731 732 733 734
      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;
        }
    }
735

736
  g_free (children);
737

738 739 740 741 742 743 744 745 746
  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;
747
}
748

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

759
  display = gdk_screen_get_display (cache->screen);
760

761
  gdk_x11_display_error_trap_push (display);
762

763
  tmp_list = cache->children;
764

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

769
      if ((child->xid != ignore) && (child->mapped))
770
        {
771 772
          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
              (y_root >= child->y) && (y_root < child->y + child->height))
773
            {
774 775 776 777 778 779 780 781 782 783 784 785 786 787
              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;
788 789
            }
        }
790
      tmp_list = tmp_list->next;
791
    }
792

793
  gdk_x11_display_error_trap_pop_ignored (display);
794

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

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

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

/* Utility functions */

static struct {
822
  const gchar *name;
823
  GdkAtom atom;
824 825
  GdkDragAction action;
} xdnd_actions_table[] = {
826 827 828 829 830
    { "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 },
831 832
  };

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

static void
xdnd_initialize_actions (void)
{
  gint i;
840

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

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

853 854 855 856 857
  if (xatom == None)
    return 0;

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

858 859 860
  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

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

  return 0;
}

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

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

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

881
  return None;
882 883 884 885
}

/* Source side */

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

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

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

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

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

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

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

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

      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

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

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

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

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

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

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

969 970
      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */

971 972 973 974 975 976 977
      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

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

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

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

1002 1003
  g_free (atomlist);

Matthias Clasen's avatar
Matthias Clasen committed
1004
  context_x11->xdnd_targets_set = 1;
1005 1006
}

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

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();
1019

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

1031
  atomlist = g_new (Atom, n_atoms);
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 1042
        {
          actions &= ~xdnd_actions_table[i].action;
          atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
          n_atoms++;
        }
1043 1044
    }

1045
  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
1046 1047 1048 1049
                   GDK_WINDOW_XID (context->source_window),
                   gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
                   XA_ATOM, 32, PropModeReplace,
                   (guchar *)atomlist, n_atoms);