gdkdnd-x11.c 97 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
  GdkDragContext context;

77 78
  gint start_x;                /* Where the drag started */
  gint start_y;
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
  GdkWindow *ipc_window;
  GdkCursor *cursor;
  GdkSeat *grab_seat;
  GdkDragAction actions;
  GdkDragAction current_action;

96 97 98
  gint hot_x;
  gint hot_y;

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

108 109 110 111 112
struct _GdkX11DragContextClass
{
  GdkDragContextClass parent_class;
};

113 114 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
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 }
};

142 143
/* Forward declarations */

144 145 146
static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
static void            gdk_window_cache_unref (GdkWindowCache *cache);
147

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

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

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

176
static GList *contexts;
177
static GSList *window_caches;
178

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


192
G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
193

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

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

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

252
  object_class->finalize = gdk_x11_drag_context_finalize;
Matthias Clasen's avatar
Matthias Clasen committed
253

254 255 256 257 258 259 260 261 262
  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;
263
  context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
264
  context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
265
  context_class->drop_done = gdk_x11_drag_context_drop_done;
266 267 268 269 270 271
  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;
272 273
}

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

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

287 288
  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
289

290
  contexts = g_list_remove (contexts, context);
291

292 293
  drag_window = context->drag_window;

294
  G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
295 296 297

  if (drag_window)
    gdk_window_destroy (drag_window);
298
}
299

300 301
/* Drag Contexts */

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

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

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

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

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

335 336 337
  return NULL;
}

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

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

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

      g_ptr_array_free (targets, TRUE);
    }
}

361 362
/* Utility functions */

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

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

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

  g_free (child);
}

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

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

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

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

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

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

      return GDK_FILTER_REMOVE;
    }

  return GDK_FILTER_CONTINUE;
}

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

  switch (xevent->type)
    {
    case CirculateNotify:
      break;
    case ConfigureNotify:
      {
455 456 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
        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;
494 495 496
      }
    case CreateNotify:
      {
497 498 499 500 501 502 503 504
        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;
505 506 507
      }
    case DestroyNotify:
      {
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
        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;
525 526 527
      }
    case MapNotify:
      {
528 529 530 531 532 533 534 535 536 537 538
        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;
539 540 541 542 543
      }
    case ReparentNotify:
      break;
    case UnmapNotify:
      {
544 545 546 547 548 549 550 551 552 553 554
        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;
555 556 557 558 559 560 561 562
      }
    default:
      return GDK_FILTER_CONTINUE;
    }
  return GDK_FILTER_REMOVE;
}

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

574 575 576 577
  GdkWindowCache *result = g_new (GdkWindowCache, 1);

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

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

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

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

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

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

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

625
  g_free (children);
626

627
#ifdef HAVE_XCOMPOSITE
628 629 630 631 632 633 634 635 636
  /*
   * 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))
    {
637
      cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
638
      gdk_window_cache_add (result, cow, 0, 0, 
639 640
			    gdk_x11_screen_get_width (screen) * GDK_X11_SCREEN(screen)->window_scale, 
			    gdk_x11_screen_get_height (screen) * GDK_X11_SCREEN(screen)->window_scale, 
641
			    TRUE);
642
      XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
643
    }
644
#endif
645

646 647 648 649 650 651
  return result;
}

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

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

661
  display = gdk_screen_get_display (cache->screen);
662

663 664 665
  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);
666

667 668
  g_list_free (cache->children);
  g_hash_table_destroy (cache->child_hash);
669 670

  g_free (cache);
671 672
}

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

708
  cache = gdk_window_cache_new (screen);
709

710
  window_caches = g_slist_prepend (window_caches, cache);
711

712
  return cache;
713 714
}

715 716 717 718 719
static gboolean
is_pointer_within_shape (GdkDisplay    *display,
                         GdkCacheChild *child,
                         gint           x_pos,
                         gint           y_pos)
720
{
721
  if (!child->shape_selected)
722
    {
723
      GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
724

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

733 734 735
      child->shape = NULL;
      if (gdk_display_supports_shapes (display))
        child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
736
                                                   child->xid, 1,  ShapeBounding);
737 738 739 740
#ifdef ShapeInput
      input_shape = NULL;
      if (gdk_display_supports_input_shapes (display))
        input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
741
                                                  child->xid, 1, ShapeInput);
742

743 744 745 746 747 748 749 750 751 752
      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
753

754
      child->shape_valid = TRUE;
755 756
    }

757 758
  return child->shape == NULL ||
         cairo_region_contains_point (child->shape, x_pos, y_pos);
759 760
}

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

775 776 777 778
  if (!_gdk_x11_get_window_child_info (display, win, TRUE,
                                       is_toplevel? &has_wm_state : NULL,
                                       &children, &nchildren))
    return None;
779

780 781 782
  if (has_wm_state)
    {
      g_free (children);
783

784 785
      return win;
    }
786

787 788 789
  for (i = nchildren - 1; (i >= 0) && !found_child; i--)
    {
      GdkChildInfoX11 *cur_child = &children[i];
790

791 792 793 794 795 796 797 798 799 800
      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;
        }
    }
801

802
  g_free (children);
803

804 805 806 807 808 809 810 811 812
  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;
813
}
814

815 816 817 818 819
static Window
get_client_window_at_coords (GdkWindowCache *cache,
                             Window          ignore,
                             gint            x_root,
                             gint            y_root)
820
{
821 822
  GList *tmp_list;
  Window retval = None;
823
  GdkDisplay *display;
824

825
  display = gdk_screen_get_display (cache->screen);
826

827
  gdk_x11_display_error_trap_push (display);
828

829
  tmp_list = cache->children;
830

831
  while (tmp_list && !retval)
832
    {
833
      GdkCacheChild *child = tmp_list->data;
834

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

859
  gdk_x11_display_error_trap_pop_ignored (display);
860

861 862 863 864 865
  if (retval)
    return retval;
  else
    return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
}
866

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

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

/* Utility functions */

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

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

static void
xdnd_initialize_actions (void)
{
  gint i;
906

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

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

919 920 921 922 923
  if (xatom == None)
    return 0;

  atom = gdk_x11_xatom_to_atom_for_display (display, xatom);

924 925 926
  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
927
  for (i = 0; i < xdnd_n_actions; i++)
928 929 930 931 932 933
    if (atom == xdnd_actions_table[i].atom)
      return xdnd_actions_table[i].action;

  return 0;
}

934
static Atom
935
xdnd_action_to_atom (GdkDisplay    *display,
936
                     GdkDragAction  action)
937 938 939 940 941 942
{
  gint i;

  if (!xdnd_actions_initialized)
    xdnd_initialize_actions();

Matthias Clasen's avatar
Matthias Clasen committed
943
  for (i = 0; i < xdnd_n_actions; i++)
944
    if (action == xdnd_actions_table[i].action)
945
      return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
946

947
  return None;
948 949 950 951
}

/* Source side */

952
static GdkFilterReturn
953
xdnd_status_filter (GdkXEvent *xev,
954 955
                    GdkEvent  *event,
                    gpointer   data)
956
{
957
  GdkDisplay *display;
958 959 960
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  guint32 flags = xevent->xclient.data.l[1];
961
  Atom action = xevent->xclient.data.l[4];
962
  GdkDragContext *context;
963 964 965

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
966 967 968 969 970
    return GDK_FILTER_CONTINUE;                 /* Not for us */

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

972
  display = gdk_window_get_display (event->any.window);
973
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
974

975 976
  if (context)
    {
977
      GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
978 979
      if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
        context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
980

981 982 983
      event->dnd.send_event = FALSE;
      event->dnd.type = GDK_DRAG_STATUS;
      event->dnd.context = context;
984
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
985
      g_object_ref (context);
986 987 988

      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
      if (!(action != 0) != !(flags & 1))
989 990
        {
          GDK_NOTE (DND,
991
                    g_warning ("Received status event with flags not corresponding to action!"));
992 993
          action = 0;
        }
994

995
      context->action = xdnd_action_from_atom (display, action);
996 997 998 999 1000 1001 1002

      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

1003
static GdkFilterReturn
1004
xdnd_finished_filter (GdkXEvent *xev,
1005 1006
                      GdkEvent  *event,
                      gpointer   data)
1007
{
1008
  GdkDisplay *display;
1009 1010 1011
  XEvent *xevent = (XEvent *)xev;
  guint32 dest_window = xevent->xclient.data.l[0];
  GdkDragContext *context;
1012
  GdkX11DragContext *context_x11;
1013 1014 1015

  if (!event->any.window ||
      gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
1016 1017 1018 1019
    return GDK_FILTER_CONTINUE;                 /* Not for us */

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

1021
  display = gdk_window_get_display (event->any.window);
1022
  context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
1023

1024 1025
  if (context)
    {
1026
      context_x11 = GDK_X11_DRAG_CONTEXT (context);
Matthias Clasen's avatar
Matthias Clasen committed
1027
      if (context_x11->version == 5)
1028
        context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
Matthias Clasen's avatar
Matthias Clasen committed
1029

1030 1031
      event->dnd.type = GDK_DROP_FINISHED;
      event->dnd.context = context;
1032
      gdk_event_set_device (event, gdk_drag_context_get_device (context));
1033
      g_object_ref (context);
1034

1035 1036
      event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */

1037 1038 1039 1040 1041 1042 1043
      return GDK_FILTER_TRANSLATE;
    }

  return GDK_FILTER_REMOVE;
}

static void
1044
xdnd_set_targets (GdkX11DragContext *context_x11)
1045
{
Matthias Clasen's avatar
Matthias Clasen committed
1046
  GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1047
  Atom *atomlist;
1048 1049
  GList *tmp_list = context->targets;
  gint i;
1050
  gint n_atoms = g_list_length (context->targets);
1051
  GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1052

1053
  atomlist = g_new (Atom, n_atoms);
1054 1055 1056
  i = 0;
  while (tmp_list)
    {
1057
      atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
1058 1059 1060 1061
      tmp_list = tmp_list->next;
      i++;
    }

1062
  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
1063 1064 1065 1066
                   GDK_WINDOW_XID (context->source_window),
                   gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
                   XA_ATOM, 32, PropModeReplace,
                   (guchar *)atomlist, n_atoms);
1067

1068 1069
  g_free (atomlist);

Matthias Clasen's avatar
Matthias Clasen committed
1070
  context_x11->xdnd_targets_set = 1;