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

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
Matthias Clasen's avatar
Matthias Clasen committed
22
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 24
 */

25
#include "config.h"
26

27
#include "gdkx11dnd.h"
28
#include "gdkdndprivate.h"
Owen Taylor's avatar
Started  
Owen Taylor committed
29

30
#include "gdkmain.h"
Matthias Clasen's avatar
Matthias Clasen committed
31
#include "gdkinternals.h"
32
#include "gdkasync.h"
Owen Taylor's avatar
Started  
Owen Taylor committed
33
#include "gdkproperty.h"
34
#include "gdkprivate-x11.h"
35 36
#include "gdkscreen-x11.h"
#include "gdkdisplay-x11.h"
37

38 39 40 41
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
42
#ifdef HAVE_XCOMPOSITE
43
#include <X11/extensions/Xcomposite.h>
44
#endif
45 46 47

#include <string.h>

48 49 50 51 52 53 54 55 56 57 58
typedef enum {
  GDK_DRAG_STATUS_DRAG,
  GDK_DRAG_STATUS_MOTION_WAIT,
  GDK_DRAG_STATUS_ACTION_WAIT,
  GDK_DRAG_STATUS_DROP
} GtkDragStatus;

typedef struct {
  guint32 xid;
  gint x, y, width, height;
  gboolean mapped;
59 60
  gboolean shape_selected;
  gboolean shape_valid;
61
  cairo_region_t *shape;
62 63 64 65 66 67
} GdkCacheChild;

typedef struct {
  GList *children;
  GHashTable *child_hash;
  guint old_event_mask;
68
  GdkScreen *screen;
69
  gint ref_count;
70 71
} GdkWindowCache;

Matthias Clasen's avatar
Matthias Clasen committed
72

73 74
struct _GdkX11DragContext
{
75 76 77 78
  GdkDragContext context;

  guint   ref_count;

79 80
  gint start_x;                /* Where the drag started */
  gint start_y;
81
  guint16 last_x;              /* Coordinates from last event */
82
  guint16 last_y;
83 84 85
  GdkDragAction old_action;    /* The last action we sent to the source */
  GdkDragAction old_actions;   /* The last actions we sent to the source */
  GdkDragAction xdnd_actions;  /* What is currently set in XdndActionList */
86 87 88
  guint version;               /* Xdnd protocol version */

  GSList *window_caches;
89

90 91
  GdkWindow *drag_window;

92 93 94 95 96 97
  GdkWindow *ipc_window;
  GdkCursor *cursor;
  GdkSeat *grab_seat;
  GdkDragAction actions;
  GdkDragAction current_action;

98 99 100
  gint hot_x;
  gint hot_y;

101 102 103 104 105 106 107
  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 */
108 109
};

110 111 112 113 114
struct _GdkX11DragContextClass
{
  GdkDragContextClass parent_class;
};

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
typedef struct {
  gint keysym;
  gint modifiers;
} GrabKey;

static GrabKey grab_keys[] = {
  { XK_Escape, 0 },
  { XK_space, 0 },
  { XK_KP_Space, 0 },
  { XK_Return, 0 },
  { XK_KP_Enter, 0 },
  { XK_Up, 0 },
  { XK_Up, Mod1Mask },
  { XK_Down, 0 },
  { XK_Down, Mod1Mask },
  { XK_Left, 0 },
  { XK_Left, Mod1Mask },
  { XK_Right, 0 },
  { XK_Right, Mod1Mask },
  { XK_KP_Up, 0 },
  { XK_KP_Up, Mod1Mask },
  { XK_KP_Down, 0 },
  { XK_KP_Down, Mod1Mask },
  { XK_KP_Left, 0 },
  { XK_KP_Left, Mod1Mask },
  { XK_KP_Right, 0 },
  { XK_KP_Right, Mod1Mask }
};

144 145
/* Forward declarations */

146 147 148
static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
static void            gdk_window_cache_unref (GdkWindowCache *cache);
149

150
static GdkFilterReturn xdnd_enter_filter    (GdkXEvent *xev,
151 152
                                             GdkEvent  *event,
                                             gpointer   data);
153
static GdkFilterReturn xdnd_leave_filter    (GdkXEvent *xev,
154 155
                                             GdkEvent  *event,
                                             gpointer   data);
156
static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
157 158
                                             GdkEvent  *event,
                                             gpointer   data);
159
static GdkFilterReturn xdnd_status_filter   (GdkXEvent *xev,
160 161
                                             GdkEvent  *event,
                                             gpointer   data);
162
static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
163 164
                                             GdkEvent  *event,
                                             gpointer   data);
165
static GdkFilterReturn xdnd_drop_filter     (GdkXEvent *xev,
166 167
                                             GdkEvent  *event,
                                             gpointer   data);
168

169
static void   xdnd_manage_source_filter (GdkDragContext *context,
170 171
                                         GdkWindow      *window,
                                         gboolean        add_filter);
172

173 174 175 176 177
gboolean gdk_x11_drag_context_handle_event (GdkDragContext *context,
                                            const GdkEvent *event);
void     gdk_x11_drag_context_action_changed (GdkDragContext *context,
                                              GdkDragAction   action);

178
static GList *contexts;
179
static GSList *window_caches;
180

Matthias Clasen's avatar
Matthias Clasen committed
181
static const struct {
182 183
  const char *atom_name;
  GdkFilterFunc func;
Matthias Clasen's avatar
Matthias Clasen committed
184
} xdnd_filters[] = {
185 186 187 188 189 190
  { "XdndEnter",                    xdnd_enter_filter },
  { "XdndLeave",                    xdnd_leave_filter },
  { "XdndPosition",                 xdnd_position_filter },
  { "XdndStatus",                   xdnd_status_filter },
  { "XdndFinished",                 xdnd_finished_filter },
  { "XdndDrop",                     xdnd_drop_filter },
191
};
192 193


194
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
195

Matthias Clasen's avatar
Matthias Clasen committed
196
static void
197
gdk_x11_drag_context_init (GdkX11DragContext *context)
Matthias Clasen's avatar
Matthias Clasen committed
198 199 200 201
{
  contexts = g_list_prepend (contexts, context);
}

202 203
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
204 205 206 207 208
                                                     GdkWindow       *drag_window,
                                                     GdkScreen       *screen,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragProtocol *protocol);
209
static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
210 211 212 213 214 215 216
                                                     GdkWindow       *dest_window,
                                                     GdkDragProtocol  protocol,
                                                     gint             x_root,
                                                     gint             y_root,
                                                     GdkDragAction    suggested_action,
                                                     GdkDragAction    possible_actions,
                                                     guint32          time);
217
static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
218 219
                                                     GdkDragAction    action,
                                                     guint32          time_);
220
static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
221
                                                     guint32          time_);
222
static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
223
                                                     guint32          time_);
224
static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
225 226
                                                     gboolean         accept,
                                                     guint32          time_);
227
static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
Matthias Clasen's avatar
Matthias Clasen committed
228 229
                                                     gboolean         success,
                                                     guint32          time_);
230 231
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
232
static GdkWindow * gdk_x11_drag_context_get_drag_window (GdkDragContext *context);
233 234 235
static void        gdk_x11_drag_context_set_hotspot (GdkDragContext  *context,
                                                     gint             hot_x,
                                                     gint             hot_y);
236 237
static void        gdk_x11_drag_context_drop_done     (GdkDragContext  *context,
                                                       gboolean         success);
238 239 240 241 242
static gboolean    gdk_x11_drag_context_manage_dnd     (GdkDragContext *context,
                                                        GdkWindow      *window,
                                                        GdkDragAction   actions);
static void        gdk_x11_drag_context_set_cursor     (GdkDragContext *context,
                                                        GdkCursor      *cursor);
243 244
static void        gdk_x11_drag_context_cancel         (GdkDragContext      *context,
                                                        GdkDragCancelReason  reason);
245 246
static void        gdk_x11_drag_context_drop_performed (GdkDragContext *context,
                                                        guint32         time);
247

248
static void
249
gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
250
{
251
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Matthias Clasen's avatar
Matthias Clasen committed
252
  GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
253

254
  object_class->finalize = gdk_x11_drag_context_finalize;
Matthias Clasen's avatar
Matthias Clasen committed
255

256 257 258 259 260 261 262 263 264
  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;
265
  context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
266
  context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
267
  context_class->drop_done = gdk_x11_drag_context_drop_done;
268 269 270 271 272 273
  context_class->manage_dnd = gdk_x11_drag_context_manage_dnd;
  context_class->set_cursor = gdk_x11_drag_context_set_cursor;
  context_class->cancel = gdk_x11_drag_context_cancel;
  context_class->drop_performed = gdk_x11_drag_context_drop_performed;
  context_class->handle_event = gdk_x11_drag_context_handle_event;
  context_class->action_changed = gdk_x11_drag_context_action_changed;
274 275
}

276
static void
277
gdk_x11_drag_context_finalize (GObject *object)
278
{
279
  GdkDragContext *context = GDK_DRAG_CONTEXT (object);
280
  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object);
281
  GdkWindow *drag_window;
282 283

  if (context->source_window)
284
    {
Matthias Clasen's avatar
Matthias Clasen committed
285
      if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
286 287
        xdnd_manage_source_filter (context, context->source_window, FALSE);
    }
288

289 290
  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
291

292
  contexts = g_list_remove (contexts, context);
293

294 295
  drag_window = context->drag_window;

296
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
297 298 299

  if (drag_window)
    gdk_window_destroy (drag_window);
300
}
301

302 303
/* Drag Contexts */

304
static GdkDragContext *
305
gdk_drag_context_find (GdkDisplay *display,
306 307 308
                       gboolean    is_source,
                       Window      source_xid,
                       Window      dest_xid)
309
{
310
  GList *tmp_list;
Jeff Garzik's avatar
Jeff Garzik committed
311
  GdkDragContext *context;
312
  GdkX11DragContext *context_x11;
313
  Window context_dest_xid;
314

315
  for (tmp_list = contexts; tmp_list; tmp_list = tmp_list->next)
316
    {
Jeff Garzik's avatar
Jeff Garzik committed
317
      context = (GdkDragContext *)tmp_list->data;
318
      context_x11 = (GdkX11DragContext *)context;
319

320
      if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
321
          (context->dest_window && gdk_window_get_display (context->dest_window) != display))
Matthias Clasen's avatar
Matthias Clasen committed
322
        continue;
323

Matthias Clasen's avatar
Matthias Clasen committed
324 325 326 327 328
      context_dest_xid = context->dest_window
                            ? (context_x11->drop_xid
                                  ? context_x11->drop_xid
                                  : GDK_WINDOW_XID (context->dest_window))
                            : None;
329 330

      if ((!context->is_source == !is_source) &&
Matthias Clasen's avatar
Matthias Clasen committed
331 332 333 334
          ((source_xid == None) || (context->source_window &&
            (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
          ((dest_xid == None) || (context_dest_xid == dest_xid)))
        return context;
335
    }
Matthias Clasen's avatar
Matthias Clasen committed
336

337 338 339
  return NULL;
}

340 341 342 343 344 345 346 347 348 349
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)
350
        g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
351 352

      _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
353 354
                               (const gchar **)targets->pdata,
                               targets->len);
355 356

      for (i =0; i < targets->len; i++)
357
        g_free (targets->pdata[i]);
358 359 360 361 362

      g_ptr_array_free (targets, TRUE);
    }
}

363 364
/* Utility functions */

365 366 367 368 369
static void
free_cache_child (GdkCacheChild *child,
                  GdkDisplay    *display)
{
  if (child->shape)
Benjamin Otte's avatar
Benjamin Otte committed
370
    cairo_region_destroy (child->shape);
371 372 373

  if (child->shape_selected && display)
    {
374
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
375 376 377 378 379 380 381

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

  g_free (child);
}

382 383
static void
gdk_window_cache_add (GdkWindowCache *cache,
384 385 386 387 388 389
                      guint32         xid,
                      gint            x,
                      gint            y,
                      gint            width,
                      gint            height,
                      gboolean        mapped)
390 391 392 393 394 395 396 397 398
{
  GdkCacheChild *child = g_new (GdkCacheChild, 1);

  child->xid = xid;
  child->x = x;
  child->y = y;
  child->width = width;
  child->height = height;
  child->mapped = mapped;
399 400 401
  child->shape_selected = FALSE;
  child->shape_valid = FALSE;
  child->shape = NULL;
402 403

  cache->children = g_list_prepend (cache->children, child);
404 405
  g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
                       cache->children);
406 407
}

408 409 410 411 412 413 414 415
static GdkFilterReturn
gdk_window_cache_shape_filter (GdkXEvent *xev,
                               GdkEvent  *event,
                               gpointer   data)
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

416
  GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431

  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
432
              cairo_region_destroy (child->shape);
433 434 435 436 437 438 439 440 441 442
              child->shape = NULL;
            }
        }

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

443 444
static GdkFilterReturn
gdk_window_cache_filter (GdkXEvent *xev,
445 446
                         GdkEvent  *event,
                         gpointer   data)
447 448 449 450 451 452 453 454 455 456
{
  XEvent *xevent = (XEvent *)xev;
  GdkWindowCache *cache = data;

  switch (xevent->type)
    {
    case CirculateNotify:
      break;
    case ConfigureNotify:
      {
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        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;
496 497 498
      }
    case CreateNotify:
      {
499 500 501 502 503 504 505 506
        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;
507 508 509
      }
    case DestroyNotify:
      {
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
        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;
527 528 529
      }
    case MapNotify:
      {
530 531 532 533 534 535 536 537 538 539 540
        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;
541 542 543 544 545
      }
    case ReparentNotify:
      break;
    case UnmapNotify:
      {
546 547 548 549 550 551 552 553 554 555 556
        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;
557 558 559 560 561 562 563 564
      }
    default:
      return GDK_FILTER_CONTINUE;
    }
  return GDK_FILTER_REMOVE;
}

static GdkWindowCache *
565
gdk_window_cache_new (GdkScreen *screen)
566 567
{
  XWindowAttributes xwa;
568 569
  Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
  GdkWindow *root_window = gdk_screen_get_root_window (screen);
570 571
  GdkChildInfoX11 *children;
  guint nchildren, i;
572
#ifdef HAVE_XCOMPOSITE
573
  Window cow;
574
#endif
575

576 577 578 579
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

  result->children = NULL;
  result->child_hash = g_hash_table_new (g_direct_hash, NULL);
580
  result->screen = screen;
581
  result->ref_count = 1;
582

583
  XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
584
  result->old_event_mask = xwa.your_event_mask;
585

586
  if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
587 588 589
    {
      GList *toplevel_windows, *list;
      GdkWindow *window;
590
      GdkWindowImplX11 *impl;
591
      gint x, y, width, height;
592

593
      toplevel_windows = gdk_screen_get_toplevel_windows (screen);
594 595 596
      for (list = toplevel_windows; list; list = list->next)
        {
          window = GDK_WINDOW (list->data);
597
	  impl = GDK_WINDOW_IMPL_X11 (window->impl);
598 599
          gdk_window_get_geometry (window, &x, &y, &width, &height);
          gdk_window_cache_add (result, GDK_WINDOW_XID (window),
600 601 602
                                x * impl->window_scale, y * impl->window_scale, 
				width * impl->window_scale, 
				height * impl->window_scale,
603 604
                                gdk_window_is_visible (window));
        }
605 606 607 608
      g_list_free (toplevel_windows);
      return result;
    }

609
  XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
610
                result->old_event_mask | SubstructureNotifyMask);
611
  gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
612
  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
613

614
  if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
615 616 617
                                       GDK_WINDOW_XID (root_window),
                                       FALSE, NULL,
                                       &children, &nchildren))
618
    return result;
619

620 621
  for (i = 0; i < nchildren ; i++)
    {
622
      gdk_window_cache_add (result, children[i].window,
623 624
                            children[i].x, children[i].y, children[i].width, children[i].height,
                            children[i].is_mapped);
625 626
    }

627
  g_free (children);
628

629
#ifdef HAVE_XCOMPOSITE
630 631 632 633 634 635 636 637 638
  /*
   * 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))
    {
639
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
640 641 642 643
      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);
644
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
645
    }
646
#endif
647

648 649 650 651 652 653
  return result;
}

static void
gdk_window_cache_destroy (GdkWindowCache *cache)
{
654
  GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
655
  GdkDisplay *display;
656 657

  XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
658 659
                GDK_WINDOW_XID (root_window),
                cache->old_event_mask);
660
  gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
661
  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
662

663
  display = gdk_screen_get_display (cache->screen);
664

665 666 667
  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);
668

669 670
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
671 672

  g_free (cache);
673 674
}

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
static GdkWindowCache *
gdk_window_cache_ref (GdkWindowCache *cache)
{
  cache->ref_count += 1;

  return cache;
}

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

  cache->ref_count -= 1;

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

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

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

Matthias Clasen's avatar
Matthias Clasen committed
710
  cache = gdk_window_cache_new (screen);
711

Matthias Clasen's avatar
Matthias Clasen committed
712
  window_caches = g_slist_prepend (window_caches, cache);
713

Matthias Clasen's avatar
Matthias Clasen committed
714
  return cache;
715 716
}

Matthias Clasen's avatar
Matthias Clasen committed
717 718 719 720 721
static gboolean
is_pointer_within_shape (GdkDisplay    *display,
                         GdkCacheChild *child,
                         gint           x_pos,
                         gint           y_pos)
722
{
Matthias Clasen's avatar
Matthias Clasen committed
723
  if (!child->shape_selected)
724
    {
Matthias Clasen's avatar
Matthias Clasen committed
725
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
726

Matthias Clasen's avatar
Matthias Clasen committed
727 728 729 730 731 732 733
      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;
734

Matthias Clasen's avatar
Matthias Clasen committed
735 736 737
      child->shape = NULL;
      if (gdk_display_supports_shapes (display))
        child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
738
                                                   child->xid, 1,  ShapeBounding);
Matthias Clasen's avatar
Matthias Clasen committed
739 740 741 742
#ifdef ShapeInput
      input_shape = NULL;
      if (gdk_display_supports_input_shapes (display))
        input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
743
                                                  child->xid, 1, ShapeInput);
744

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

Matthias Clasen's avatar
Matthias Clasen committed
756
      child->shape_valid = TRUE;
757 758
    }

Matthias Clasen's avatar
Matthias Clasen committed
759 760
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
761 762
}

Matthias Clasen's avatar
Matthias Clasen committed
763 764 765 766 767 768
static Window
get_client_window_at_coords_recurse (GdkDisplay *display,
                                     Window      win,
                                     gboolean    is_toplevel,
                                     gint        x,
                                     gint        y)
769
{
Matthias Clasen's avatar
Matthias Clasen committed
770 771 772 773 774 775
  GdkChildInfoX11 *children;
  unsigned int nchildren;
  int i;
  gboolean found_child = FALSE;
  GdkChildInfoX11 child = { 0, };
  gboolean has_wm_state = FALSE;
776

Matthias Clasen's avatar
Matthias Clasen committed
777 778 779 780
  if (!_gdk_x11_get_window_child_info (display, win, TRUE,
                                       is_toplevel? &has_wm_state : NULL,
                                       &children, &nchildren))
    return None;
781

Matthias Clasen's avatar
Matthias Clasen committed
782 783 784
  if (has_wm_state)
    {
      g_free (children);
785

Matthias Clasen's avatar
Matthias Clasen committed
786 787
      return win;
    }
788

Matthias Clasen's avatar
Matthias Clasen committed
789 790 791
  for (i = nchildren - 1; (i >= 0) && !found_child; i--)
    {
      GdkChildInfoX11 *cur_child = &children[i];
792

Matthias Clasen's avatar
Matthias Clasen committed
793 794 795 796 797 798 799 800 801 802
      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;
        }
    }
803

Matthias Clasen's avatar
Matthias Clasen committed
804
  g_free (children);
805

Matthias Clasen's avatar
Matthias Clasen committed
806 807 808 809 810 811 812 813 814
  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;
815
}
816

Matthias Clasen's avatar
Matthias Clasen committed
817 818 819 820 821
static Window
get_client_window_at_coords (GdkWindowCache *cache,
                             Window          ignore,
                             gint            x_root,
                             gint            y_root)
822
{
Matthias Clasen's avatar
Matthias Clasen committed
823 824
  GList *tmp_list;
  Window retval = None;
825
  GdkDisplay *display;
826

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

Matthias Clasen's avatar
Matthias Clasen committed
829
  gdk_x11_display_error_trap_push (display);
830

Matthias Clasen's avatar
Matthias Clasen committed
831
  tmp_list = cache->children;
832

Matthias Clasen's avatar
Matthias Clasen committed
833
  while (tmp_list && !retval)
834
    {
Matthias Clasen's avatar
Matthias Clasen committed
835
      GdkCacheChild *child = tmp_list->data;
836

Matthias Clasen's avatar
Matthias Clasen committed
837
      if ((child->xid != ignore) && (child->mapped))
838
        {
Matthias Clasen's avatar
Matthias Clasen committed
839 840
          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
              (y_root >= child->y) && (y_root < child->y + child->height))
841
            {
Matthias Clasen's avatar
Matthias Clasen committed
842 843 844 845 846 847 848 849 850 851 852 853 854 855
              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;
856 857
            }
        }
Matthias Clasen's avatar
Matthias Clasen committed
858
      tmp_list = tmp_list->next;
859
    }
860

Matthias Clasen's avatar
Matthias Clasen committed
861
  gdk_x11_display_error_trap_pop_ignored (display);
862

Matthias Clasen's avatar
Matthias Clasen committed
863 864 865 866 867
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
868

Matthias Clasen's avatar
Matthias Clasen committed
869 870 871 872 873
#ifdef G_ENABLE_DEBUG
static void
print_target_list (GList *targets)
{
  while (targets)
874
    {
Matthias Clasen's avatar
Matthias Clasen committed
875 876 877 878
      gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
      g_message ("\t%s", name);
      g_free (name);
      targets = targets->next;
879 880
    }
}
Matthias Clasen's avatar
Matthias Clasen committed
881
#endif /* G_ENABLE_DEBUG */
882 883 884 885 886 887 888 889

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

/* Utility functions */

static struct {
890
  const gchar *name;
891
  GdkAtom atom;
892 893
  GdkDragAction action;
} xdnd_actions_table[] = {
894 895 896 897 898
    { "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 },
899 900
  };

901
static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
902 903 904 905 906 907
static gboolean xdnd_actions_initialized = FALSE;

static void
xdnd_initialize_actions (void)
{
  gint i;
908

909
  xdnd_actions_initialized = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
910
  for (i = 0; i < xdnd_n_actions; i++)
Matthias Clasen's avatar
Matthias Clasen committed
911
    xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
912 913 914
}

static GdkDragAction
915
xdnd_action_from_atom (GdkDisplay *display,
916
                       Atom        xatom)
917
{
918
  GdkAtom atom;