gdkimage-x11.c 21.1 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
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.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

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

Jeff Garzik's avatar
Jeff Garzik committed
27 28
#include <config.h>

29
#include <stdlib.h>
Elliot Lee's avatar
Elliot Lee committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#include <sys/types.h>

#if defined (HAVE_IPC_H) && defined (HAVE_SHM_H) && defined (HAVE_XSHM_H)
#define USE_SHM
#endif

#ifdef USE_SHM
#include <sys/ipc.h>
#include <sys/shm.h>
#endif /* USE_SHM */

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#ifdef USE_SHM
#include <X11/extensions/XShm.h>
#endif /* USE_SHM */

48 49
#include <errno.h>

Owen Taylor's avatar
Started  
Owen Taylor committed
50
#include "gdk.h"		/* For gdk_error_trap_* / gdk_flush_* */
51
#include "gdkx.h"
Owen Taylor's avatar
Started  
Owen Taylor committed
52
#include "gdkimage.h"
Elliot Lee's avatar
Elliot Lee committed
53
#include "gdkprivate.h"
54
#include "gdkprivate-x11.h"
55 56
#include "gdkdisplay-x11.h"
#include "gdkscreen-x11.h"
Elliot Lee's avatar
Elliot Lee committed
57

58 59 60 61 62
typedef struct _GdkImagePrivateX11     GdkImagePrivateX11;

struct _GdkImagePrivateX11
{
  XImage *ximage;
63
  GdkScreen *screen;
64
  gpointer x_shm_info;
65
  Pixmap shm_pixmap;
66 67
};

Elliot Lee's avatar
Elliot Lee committed
68
static GList *image_list = NULL;
69 70
static gpointer parent_class = NULL;

71 72 73 74
static void gdk_x11_image_destroy (GdkImage      *image);
static void gdk_image_init        (GdkImage      *image);
static void gdk_image_class_init  (GdkImageClass *klass);
static void gdk_image_finalize    (GObject       *object);
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

#define PRIVATE_DATA(image) ((GdkImagePrivateX11 *) GDK_IMAGE (image)->windowing_data)

GType
gdk_image_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
      static const GTypeInfo object_info =
      {
        sizeof (GdkImageClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gdk_image_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GdkImage),
        0,              /* n_preallocs */
        (GInstanceInitFunc) gdk_image_init,
      };
      
      object_type = g_type_register_static (G_TYPE_OBJECT,
                                            "GdkImage",
100
                                            &object_info, 0);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    }
  
  return object_type;
}

static void
gdk_image_init (GdkImage *image)
{
  image->windowing_data = g_new0 (GdkImagePrivateX11, 1);
}

static void
gdk_image_class_init (GdkImageClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = gdk_image_finalize;
}

static void
gdk_image_finalize (GObject *object)
{
  GdkImage *image = GDK_IMAGE (object);

  gdk_x11_image_destroy (image);
  
  G_OBJECT_CLASS (parent_class)->finalize (object);
}
Elliot Lee's avatar
Elliot Lee committed
131 132 133


void
134
_gdk_image_exit (void)
Elliot Lee's avatar
Elliot Lee committed
135 136 137 138 139 140
{
  GdkImage *image;

  while (image_list)
    {
      image = image_list->data;
141
      gdk_x11_image_destroy (image);
Elliot Lee's avatar
Elliot Lee committed
142 143 144
    }
}

145 146 147 148
/**
 * gdk_image_new_bitmap:
 * @visual: the #GdkVisual to use for the image.
 * @data: the pixel data. 
149 150
 * @width: the width of the image in pixels. 
 * @height: the height of the image in pixels. 
151 152 153 154 155 156 157 158
 * 
 * Creates a new #GdkImage with a depth of 1 from the given data.
 * <warning><para>THIS FUNCTION IS INCREDIBLY BROKEN. The passed-in data must 
 * be allocated by malloc() (NOT g_malloc()) and will be freed when the 
 * image is freed.</para></warning>
 * 
 * Return value: a new #GdkImage.
 **/
Elliot Lee's avatar
Elliot Lee committed
159
GdkImage *
160
gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint width, gint height)
Elliot Lee's avatar
Elliot Lee committed
161
{
162 163
  Visual *xvisual;
  GdkImage *image;
164
  GdkDisplay *display;
165
  GdkImagePrivateX11 *private;
166
  
167
  image = g_object_new (gdk_image_get_type (), NULL);
168
  private = PRIVATE_DATA (image);
169
  private->screen = gdk_visual_get_screen (visual);
170 171
  display = GDK_SCREEN_DISPLAY (private->screen);
  
172 173
  image->type = GDK_IMAGE_NORMAL;
  image->visual = visual;
174 175
  image->width = width;
  image->height = height;
176
  image->depth = 1;
177
  image->bits_per_pixel = 1;
178 179 180 181 182 183 184
  if (display->closed)
    private->ximage = NULL;
  else
    {
      xvisual = ((GdkVisualPrivate*) visual)->xvisual;
      private->ximage = XCreateImage (GDK_SCREEN_XDISPLAY (private->screen),
				      xvisual, 1, XYBitmap,
185
				      0, 0, width, height, 8, 0);
186 187
    }
  
188 189 190 191 192 193 194 195
  private->ximage->data = data;
  private->ximage->bitmap_bit_order = MSBFirst;
  private->ximage->byte_order = MSBFirst;
  image->byte_order = MSBFirst;
  image->mem =  private->ximage->data;
  image->bpl = private->ximage->bytes_per_line;
  image->bpp = 1;
  return(image);
Elliot Lee's avatar
Elliot Lee committed
196 197 198
} /* gdk_image_new_bitmap() */

void
199
_gdk_windowing_image_init (GdkDisplay *display)
Elliot Lee's avatar
Elliot Lee committed
200
{
201 202 203
  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
  
  if (display_x11->use_xshm)
Elliot Lee's avatar
Elliot Lee committed
204
    {
205 206 207 208 209 210 211 212 213 214 215 216 217 218
#ifdef USE_SHM
      Display *xdisplay = display_x11->xdisplay;
      int major, minor, event_base;
      Bool pixmaps;
  
      if (XShmQueryExtension (xdisplay) &&
	  XShmQueryVersion (xdisplay, &major, &minor, &pixmaps))
	{
	  display_x11->have_shm_pixmaps = pixmaps;
	  event_base = XShmGetEventBase (xdisplay);

	  _gdk_x11_register_event_type (display,
					event_base, ShmNumberEvents);
	}
219
      else
220
#endif /* USE_SHM */
221
	display_x11->use_xshm = FALSE;
Elliot Lee's avatar
Elliot Lee committed
222 223 224 225
    }
}

GdkImage*
226 227
_gdk_image_new_for_depth (GdkScreen    *screen,
			  GdkImageType  type,
228 229 230 231
			  GdkVisual    *visual,
			  gint          width,
			  gint          height,
			  gint          depth)
Elliot Lee's avatar
Elliot Lee committed
232 233
{
  GdkImage *image;
234
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
235 236 237
#ifdef USE_SHM
  XShmSegmentInfo *x_shm_info;
#endif /* USE_SHM */
238
  Visual *xvisual = NULL;
239 240
  GdkDisplayX11 *display_x11;
  GdkScreenX11 *screen_x11;
Elliot Lee's avatar
Elliot Lee committed
241

242 243
  g_return_val_if_fail (!visual || GDK_IS_VISUAL (visual), NULL);
  g_return_val_if_fail (visual || depth != -1, NULL);
244 245 246 247 248
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
  
  screen_x11 = GDK_SCREEN_X11 (screen);
  display_x11 = GDK_DISPLAY_X11 (screen_x11->display);
  
249 250 251
  if (visual)
    depth = visual->depth;
  
Elliot Lee's avatar
Elliot Lee committed
252 253 254
  switch (type)
    {
    case GDK_IMAGE_FASTEST:
255 256
      image = _gdk_image_new_for_depth (screen, GDK_IMAGE_SHARED, 
					visual, width, height, depth);
Elliot Lee's avatar
Elliot Lee committed
257
      if (!image)
258 259
	image = _gdk_image_new_for_depth (screen, GDK_IMAGE_NORMAL,
					  visual, width, height, depth);
Elliot Lee's avatar
Elliot Lee committed
260 261 262
      break;

    default:
263
      image = g_object_new (gdk_image_get_type (), NULL);
264 265
      
      private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
266

267
      private->screen = screen;
Elliot Lee's avatar
Elliot Lee committed
268 269 270 271 272

      image->type = type;
      image->visual = visual;
      image->width = width;
      image->height = height;
273
      image->depth = depth;
Elliot Lee's avatar
Elliot Lee committed
274

275 276
      if (visual)
	xvisual = ((GdkVisualPrivate*) visual)->xvisual;
Elliot Lee's avatar
Elliot Lee committed
277 278 279 280 281

      switch (type)
	{
	case GDK_IMAGE_SHARED:
#ifdef USE_SHM
282
	  if (display_x11->use_xshm)
Elliot Lee's avatar
Elliot Lee committed
283
	    {
284
	      private->x_shm_info = g_new (XShmSegmentInfo, 1);
Elliot Lee's avatar
Elliot Lee committed
285
	      x_shm_info = private->x_shm_info;
Owen Taylor's avatar
Owen Taylor committed
286 287
	      x_shm_info->shmid = -1;
	      x_shm_info->shmaddr = (char*) -1;
Elliot Lee's avatar
Elliot Lee committed
288

289
	      private->ximage = XShmCreateImage (screen_x11->xdisplay, xvisual, depth,
Elliot Lee's avatar
Elliot Lee committed
290 291 292 293
						 ZPixmap, NULL, x_shm_info, width, height);
	      if (private->ximage == NULL)
		{
		  g_warning ("XShmCreateImage failed");
294 295
		  display_x11->use_xshm = FALSE;
		  
Owen Taylor's avatar
Owen Taylor committed
296
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
297 298 299 300
		}

	      x_shm_info->shmid = shmget (IPC_PRIVATE,
					  private->ximage->bytes_per_line * private->ximage->height,
301
					  IPC_CREAT | 0600);
Elliot Lee's avatar
Elliot Lee committed
302 303 304

	      if (x_shm_info->shmid == -1)
		{
305
		  /* EINVAL indicates, most likely, that the segment we asked for
306 307
		   * is bigger than SHMMAX, so we don't treat it as a permanent
		   * error. ENOSPC and ENOMEM may also indicate this, but
308 309 310 311
		   * more likely are permanent errors.
		   */
		  if (errno != EINVAL)
		    {
312
		      g_warning ("shmget failed: error %d (%s)", errno, g_strerror (errno));
313
		      display_x11->use_xshm = FALSE;
314
		    }
Elliot Lee's avatar
Elliot Lee committed
315

Owen Taylor's avatar
Owen Taylor committed
316
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
317 318 319 320 321 322 323 324
		}

	      x_shm_info->readOnly = False;
	      x_shm_info->shmaddr = shmat (x_shm_info->shmid, 0, 0);
	      private->ximage->data = x_shm_info->shmaddr;

	      if (x_shm_info->shmaddr == (char*) -1)
		{
325 326 327 328 329
		  g_warning ("shmat failed: error %d (%s)", errno, g_strerror (errno));
		  /* Failure in shmat is almost certainly permanent. Most likely error is
		   * EMFILE, which would mean that we've exceeded the per-process
		   * Shm segment limit.
		   */
330
		  display_x11->use_xshm = FALSE;
Owen Taylor's avatar
Owen Taylor committed
331
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
332 333
		}

334
	      gdk_error_trap_push ();
Elliot Lee's avatar
Elliot Lee committed
335

336 337
	      XShmAttach (screen_x11->xdisplay, x_shm_info);
	      XSync (screen_x11->xdisplay, False);
Elliot Lee's avatar
Elliot Lee committed
338

339
	      if (gdk_error_trap_pop ())
Elliot Lee's avatar
Elliot Lee committed
340
		{
341
		  /* this is the common failure case so omit warning */
342
		  display_x11->use_xshm = FALSE;
Owen Taylor's avatar
Owen Taylor committed
343
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
344
		}
345 346 347 348 349 350 351 352
	      
	      /* We mark the segment as destroyed so that when
	       * the last process detaches, it will be deleted.
	       * There is a small possibility of leaking if
	       * we die in XShmAttach. In theory, a signal handler
	       * could be set up.
	       */
	      shmctl (x_shm_info->shmid, IPC_RMID, 0);		      
Elliot Lee's avatar
Elliot Lee committed
353 354 355 356 357 358

	      if (image)
		image_list = g_list_prepend (image_list, image);
	    }
	  else
#endif /* USE_SHM */
359
	    goto error;
Owen Taylor's avatar
Owen Taylor committed
360
	  break;
Elliot Lee's avatar
Elliot Lee committed
361
	case GDK_IMAGE_NORMAL:
362
	  private->ximage = XCreateImage (screen_x11->xdisplay, xvisual, depth,
Elliot Lee's avatar
Elliot Lee committed
363 364
					  ZPixmap, 0, 0, width, height, 32, 0);

365 366 367 368 369
	  /* Use malloc, not g_malloc here, because X will call free()
	   * on this data
	   */
	  private->ximage->data = malloc (private->ximage->bytes_per_line *
					  private->ximage->height);
Owen Taylor's avatar
Owen Taylor committed
370 371
	  if (!private->ximage->data)
	    goto error;
Elliot Lee's avatar
Elliot Lee committed
372 373 374 375 376 377 378 379
	  break;

	case GDK_IMAGE_FASTEST:
	  g_assert_not_reached ();
	}

      if (image)
	{
380
	  image->byte_order = (private->ximage->byte_order == LSBFirst) ? GDK_LSB_FIRST : GDK_MSB_FIRST;
Elliot Lee's avatar
Elliot Lee committed
381 382
	  image->mem = private->ximage->data;
	  image->bpl = private->ximage->bytes_per_line;
383
	  image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
384
	  image->bits_per_pixel = private->ximage->bits_per_pixel;
Elliot Lee's avatar
Elliot Lee committed
385 386 387 388
	}
    }

  return image;
Owen Taylor's avatar
Owen Taylor committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

 error:
  if (private->ximage)
    {
      XDestroyImage (private->ximage);
      private->ximage = NULL;
    }
#ifdef USE_SHM
  if (private->x_shm_info)
    {
      x_shm_info = private->x_shm_info;
      
      if (x_shm_info->shmaddr != (char *)-1)
	shmdt (x_shm_info->shmaddr);
      if (x_shm_info->shmid != -1) 
	shmctl (x_shm_info->shmid, IPC_RMID, 0);
      
      g_free (x_shm_info);
      private->x_shm_info = NULL;
    }
#endif /* USE_SHM */
  g_object_unref (image);
  
  return NULL;
Elliot Lee's avatar
Elliot Lee committed
413 414
}

415 416 417 418
Pixmap
_gdk_x11_image_get_shm_pixmap (GdkImage *image)
{
  GdkImagePrivateX11 *private = PRIVATE_DATA (image);
419
  GdkDisplay *display = GDK_SCREEN_DISPLAY (private->screen);
420

421 422 423
  if (display->closed)
    return None;

424 425
#ifdef USE_SHM  
  /* Future: do we need one of these per-screen per-image? ShmPixmaps
426 427
   * are the same for every screen, but can they be shared? Not a concern
   * right now since we tie images to a particular screen.
428
   */
429 430 431 432
  if (!private->shm_pixmap && image->type == GDK_IMAGE_SHARED && 
      GDK_DISPLAY_X11 (display)->have_shm_pixmaps)
    private->shm_pixmap = XShmCreatePixmap (GDK_SCREEN_XDISPLAY (private->screen),
					    GDK_SCREEN_XROOTWIN (private->screen),
433 434 435 436 437 438 439 440 441
					    image->mem, private->x_shm_info, 
					    image->width, image->height, image->depth);

  return private->shm_pixmap;
#else
  return None;
#endif    
}

Mark McLoughlin's avatar
Mark McLoughlin committed
442
static GdkImage*
443 444 445 446 447
get_full_image (GdkDrawable    *drawable,
		gint            src_x,
		gint            src_y,
		gint            width,
		gint            height)
Elliot Lee's avatar
Elliot Lee committed
448 449
{
  GdkImage *image;
450 451 452 453 454 455
  GdkImagePrivateX11 *private;
  GdkDrawableImplX11 *impl;
  XImage *ximage;

  impl = GDK_DRAWABLE_IMPL_X11 (drawable);
  
456
  ximage = XGetImage (GDK_SCREEN_XDISPLAY (impl->screen),
457 458 459 460 461 462 463 464 465 466 467
		      impl->xid,
		      src_x, src_y, width, height,
		      AllPlanes, ZPixmap);
  
  if (!ximage)
    return NULL;
  
  image = g_object_new (gdk_image_get_type (), NULL);
  
  private = PRIVATE_DATA (image);
  
468
  private->screen = impl->screen;
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
  private->ximage = ximage;
  
  image->type = GDK_IMAGE_NORMAL;
  image->visual = gdk_drawable_get_visual (drawable); /* could be NULL */
  image->width = width;
  image->height = height;
  image->depth = gdk_drawable_get_depth (drawable);
  
  image->mem = private->ximage->data;
  image->bpl = private->ximage->bytes_per_line;
  image->bits_per_pixel = private->ximage->bits_per_pixel;
  image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
  image->byte_order = (private->ximage->byte_order == LSBFirst) ? GDK_LSB_FIRST : GDK_MSB_FIRST;
  
  return image;
}

GdkImage*
_gdk_x11_copy_to_image (GdkDrawable    *drawable,
			GdkImage       *image,
			gint            src_x,
			gint            src_y,
			gint            dest_x,
			gint            dest_y,
			gint            width,
			gint            height)
{
496
  GdkImagePrivateX11 *private;
Havoc Pennington's avatar
Havoc Pennington committed
497 498
  GdkDrawableImplX11 *impl;
  GdkVisual *visual;
499 500
  GdkDisplay *display;
  Display *xdisplay;
501
  gboolean have_grab;
502
  GdkRectangle req;
503
  GdkRectangle window_rect;
504 505
  Pixmap shm_pixmap = None;
  gboolean success = TRUE;
506
  
Havoc Pennington's avatar
Havoc Pennington committed
507
  g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_X11 (drawable), NULL);
508
  g_return_val_if_fail (image != NULL || (dest_x == 0 && dest_y == 0), NULL);
Elliot Lee's avatar
Elliot Lee committed
509

Havoc Pennington's avatar
Havoc Pennington committed
510 511
  visual = gdk_drawable_get_visual (drawable);
  impl = GDK_DRAWABLE_IMPL_X11 (drawable);
512 513
  display = gdk_drawable_get_display (drawable);
  xdisplay = gdk_x11_display_get_xdisplay (display);
514 515 516

  if (display->closed)
    return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
517
  
518
  have_grab = FALSE;
519

520 521
#define UNGRAB() G_STMT_START {					\
    if (have_grab) {						\
522
      gdk_x11_display_ungrab (display);				\
523
      have_grab = FALSE; }					\
524 525 526 527 528 529 530 531 532 533
  } G_STMT_END

  if (!image && !GDK_IS_WINDOW_IMPL_X11 (drawable))
    return get_full_image (drawable, src_x, src_y, width, height);

  if (image && image->type == GDK_IMAGE_SHARED)
    {
      shm_pixmap = _gdk_x11_image_get_shm_pixmap (image);
      if (shm_pixmap)
	{
534 535 536
	  GC xgc;
	  XGCValues values;

537 538
	  /* Again easy, we can just XCopyArea, and don't have to worry about clipping
	   */
539
	  values.subwindow_mode = IncludeInferiors;
540
	  xgc = XCreateGC (xdisplay, impl->xid, GCSubwindowMode, &values);
541
	  
542
	  XCopyArea (xdisplay, impl->xid, shm_pixmap, xgc,
543
		     src_x, src_y, width, height, dest_x, dest_y);
544
	  XSync (xdisplay, FALSE);
545
	  
546
	  XFreeGC (xdisplay, xgc);
547 548 549 550 551 552 553 554 555
	  
	  return image;
	}
    }

  /* Now the general case - we may have to worry about clipping to the screen
   * bounds, in which case we'll have to grab the server and only get a piece
   * of the window.
   */
556
  if (GDK_IS_WINDOW_IMPL_X11 (drawable))
557 558
    {
      GdkRectangle screen_rect;
559 560
      Window child;

561
      have_grab = TRUE;
562
      gdk_x11_display_grab (display);
563 564

      /* Translate screen area into window coordinates */
565
      XTranslateCoordinates (xdisplay,
566 567
			     GDK_SCREEN_XROOTWIN (impl->screen),
			     impl->xid,
568 569 570
			     0, 0, 
			     &screen_rect.x, &screen_rect.y, 
			     &child);
571

572 573
      screen_rect.width = gdk_screen_get_width (impl->screen);
      screen_rect.height = gdk_screen_get_height (impl->screen);
574 575
      
      gdk_error_trap_push ();
576 577 578

      window_rect.x = 0;
      window_rect.y = 0;
579
      
580 581
      gdk_window_get_geometry (GDK_WINDOW (impl->wrapper),
                               NULL, NULL,
582 583 584
                               &window_rect.width,
                               &window_rect.height,
                               NULL);
585 586 587 588
      
      /* compute intersection of screen and window, in window
       * coordinates
       */
589
      if (gdk_error_trap_pop () ||
590
          !gdk_rectangle_intersect (&window_rect, &screen_rect, 
591
                                    &window_rect))
592
	goto out;
593
    }
594 595 596 597 598 599 600 601 602
  else
    {
      window_rect.x = 0;
      window_rect.y = 0;
      gdk_drawable_get_size (drawable,
			     &window_rect.width,
			     &window_rect.height);
    }
      
603 604
  req.x = src_x;
  req.y = src_y;
605 606 607 608
  req.width = width;
  req.height = height;
  
  /* window_rect specifies the part of drawable which we can get from
609 610 611 612
   * the server in window coordinates. 
   * For pixmaps this is all of the pixmap, for windows it is just 
   * the onscreen part.
   */
613 614
  if (!gdk_rectangle_intersect (&req, &window_rect, &req))
    goto out;
Elliot Lee's avatar
Elliot Lee committed
615

616 617 618 619 620 621 622 623
  gdk_error_trap_push ();
  
  if (!image &&
      req.x == src_x && req.y == src_y && req.width == width && req.height == height)
    {
      image = get_full_image (drawable, src_x, src_y, width, height);
      if (!image)
	success = FALSE;
624
    }
625 626
  else
    {
627 628 629 630
      gboolean created_image = FALSE;
      
      if (!image)
	{
631 632
	  image = _gdk_image_new_for_depth (impl->screen, GDK_IMAGE_NORMAL, 
					    visual, width, height,
633 634 635
					    gdk_drawable_get_depth (drawable));
	  created_image = TRUE;
	}
636

637 638
      private = PRIVATE_DATA (image);

639 640 641
      /* In the ShmImage but no ShmPixmap case, we could use XShmGetImage when
       * we are getting the entire image.
       */
642
      if (XGetSubImage (xdisplay, impl->xid,
643 644 645 646 647 648 649 650 651 652
			req.x, req.y, req.width, req.height,
			AllPlanes, ZPixmap,
			private->ximage,
			dest_x + req.x - src_x, dest_y + req.y - src_y) == None)
	{
	  if (created_image)
	    g_object_unref (image);
	  image = NULL;
	  success = FALSE;
	}
653
    }
Elliot Lee's avatar
Elliot Lee committed
654

655 656
  gdk_error_trap_pop ();

657 658 659 660
 out:
  
  if (have_grab)
    {				
661
      gdk_x11_display_ungrab (display);
662 663
      have_grab = FALSE;
    }
664
  
665 666 667
  if (success && !image)
    {
      /* We "succeeded", but could get no content for the image so return junk */
668 669
      image = _gdk_image_new_for_depth (impl->screen, GDK_IMAGE_NORMAL, 
					visual, width, height,
670 671 672
					gdk_drawable_get_depth (drawable));
    }
      
Elliot Lee's avatar
Elliot Lee committed
673 674 675 676 677 678 679 680 681
  return image;
}

guint32
gdk_image_get_pixel (GdkImage *image,
		     gint x,
		     gint y)
{
  guint32 pixel;
682
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
683

684
  g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
685 686
  g_return_val_if_fail (x >= 0 && x < image->width, 0);
  g_return_val_if_fail (y >= 0 && y < image->height, 0);
Elliot Lee's avatar
Elliot Lee committed
687

688
  private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
689

690 691 692 693
  if (!private->screen->closed)
    pixel = XGetPixel (private->ximage, x, y);
  else
    pixel = 0;
Elliot Lee's avatar
Elliot Lee committed
694 695 696 697 698 699 700 701 702 703

  return pixel;
}

void
gdk_image_put_pixel (GdkImage *image,
		     gint x,
		     gint y,
		     guint32 pixel)
{
704
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
705

706
  g_return_if_fail (GDK_IS_IMAGE (image));
707 708
  g_return_if_fail (x >= 0 && x < image->width);
  g_return_if_fail (y >= 0 && y < image->height);
Elliot Lee's avatar
Elliot Lee committed
709

710
  private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
711

712 713
  if (!private->screen->closed)
    pixel = XPutPixel (private->ximage, x, y, pixel);
Elliot Lee's avatar
Elliot Lee committed
714 715
}

716 717
static void
gdk_x11_image_destroy (GdkImage *image)
Elliot Lee's avatar
Elliot Lee committed
718
{
719
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
720 721 722 723
#ifdef USE_SHM
  XShmSegmentInfo *x_shm_info;
#endif /* USE_SHM */

724
  g_return_if_fail (GDK_IS_IMAGE (image));
Elliot Lee's avatar
Elliot Lee committed
725

726 727
  private = PRIVATE_DATA (image);

728
  if (private == NULL) /* This means that _gdk_image_exit() destroyed the
729 730 731 732
                        * image already, and now we're called a second
                        * time from _finalize()
                        */
    return;
Elliot Lee's avatar
Elliot Lee committed
733

Owen Taylor's avatar
Owen Taylor committed
734 735 736 737 738
  if (private->ximage)		/* Deal with failure of creation */
    {
      switch (image->type)
	{
	case GDK_IMAGE_NORMAL:
739 740
	  if (!private->screen->closed)
	    XDestroyImage (private->ximage);
Owen Taylor's avatar
Owen Taylor committed
741 742 743
	  break;
	  
	case GDK_IMAGE_SHARED:
Elliot Lee's avatar
Elliot Lee committed
744
#ifdef USE_SHM
745 746 747
	  if (!private->screen->closed)
	    {
	      gdk_display_sync (GDK_SCREEN_DISPLAY (private->screen));
748

749 750
	      if (private->shm_pixmap)
		XFreePixmap (GDK_SCREEN_XDISPLAY (private->screen), private->shm_pixmap);
751
	  	  
752 753 754
	      XShmDetach (GDK_SCREEN_XDISPLAY (private->screen), private->x_shm_info);
	      XDestroyImage (private->ximage);
	    }
Owen Taylor's avatar
Owen Taylor committed
755
	  
756 757
	  image_list = g_list_remove (image_list, image);

Owen Taylor's avatar
Owen Taylor committed
758 759 760 761 762
	  x_shm_info = private->x_shm_info;
	  shmdt (x_shm_info->shmaddr);
	  
	  g_free (private->x_shm_info);
	  private->x_shm_info = NULL;
763

Elliot Lee's avatar
Elliot Lee committed
764
#else /* USE_SHM */
Owen Taylor's avatar
Owen Taylor committed
765
	  g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
Elliot Lee's avatar
Elliot Lee committed
766
#endif /* USE_SHM */
Owen Taylor's avatar
Owen Taylor committed
767 768 769 770 771
	  break;
	  
	case GDK_IMAGE_FASTEST:
	  g_assert_not_reached ();
	}
Elliot Lee's avatar
Elliot Lee committed
772 773
    }

774 775
  g_free (private);
  image->windowing_data = NULL;
Elliot Lee's avatar
Elliot Lee committed
776
}
777

Matthias Clasen's avatar
Matthias Clasen committed
778 779 780 781 782 783 784 785
/**
 * gdk_x11_image_get_xdisplay:
 * @image: a #GdkImage.
 * 
 * Returns the display of a #GdkImage.
 * 
 * Return value: an Xlib <type>Display*</type>.
 **/
786 787 788 789 790 791 792 793 794
Display *
gdk_x11_image_get_xdisplay (GdkImage *image)
{
  GdkImagePrivateX11 *private;

  g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);

  private = PRIVATE_DATA (image);

795
  return GDK_SCREEN_XDISPLAY (private->screen);
796 797
}

Matthias Clasen's avatar
Matthias Clasen committed
798 799 800 801 802 803 804 805
/**
 * gdk_x11_image_get_ximage:
 * @image: a #GdkImage.
 * 
 * Returns the X image belonging to a #GdkImage.
 * 
 * Return value: an <type>XImage*</type>.
 **/
806 807 808 809 810 811 812 813 814
XImage *
gdk_x11_image_get_ximage (GdkImage *image)
{
  GdkImagePrivateX11 *private;

  g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);

  private = PRIVATE_DATA (image);

815 816 817 818
  if (private->screen->closed)
    return NULL;
  else
    return private->ximage;
819
}
820 821

gint
822 823
_gdk_windowing_get_bits_for_depth (GdkDisplay *display,
				   gint        depth)
824 825 826
{
  XPixmapFormatValues *formats;
  gint count, i;
827

828
  formats = XListPixmapFormats (GDK_DISPLAY_XDISPLAY (display), &count);
829 830 831 832 833 834 835 836 837 838 839 840
  
  for (i = 0; i < count; i++)
    if (formats[i].depth == depth)
      {
	gint result = formats[i].bits_per_pixel;
	XFree (formats);
	return result;
      }

  g_assert_not_reached ();
  return -1;
}
841