gdkimage-x11.c 15.9 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 51
#include "gdk.h"		/* For gdk_error_trap_* / gdk_flush_* */
#include "gdkimage.h"
Elliot Lee's avatar
Elliot Lee committed
52
#include "gdkprivate.h"
53
#include "gdkprivate-x11.h"
Elliot Lee's avatar
Elliot Lee committed
54 55

static GList *image_list = NULL;
56 57
static gpointer parent_class = NULL;

58 59 60 61
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);
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

#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",
87
                                            &object_info, 0);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    }
  
  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
118 119 120


void
121
gdk_image_exit (void)
Elliot Lee's avatar
Elliot Lee committed
122 123 124 125 126 127
{
  GdkImage *image;

  while (image_list)
    {
      image = image_list->data;
128
      gdk_x11_image_destroy (image);
Elliot Lee's avatar
Elliot Lee committed
129 130 131 132 133 134 135 136 137
    }
}

GdkImage *
gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h)
/*
 * Desc: create a new bitmap image
 */
{
138 139 140
  Visual *xvisual;
  GdkImage *image;
  GdkImagePrivateX11 *private;
141
  image = g_object_new (gdk_image_get_type (), NULL);
142 143 144 145 146 147 148
  private = PRIVATE_DATA (image);
  private->xdisplay = gdk_display;
  image->type = GDK_IMAGE_NORMAL;
  image->visual = visual;
  image->width = w;
  image->height = h;
  image->depth = 1;
149
  image->bits_per_pixel = 1;
150 151 152 153 154 155 156 157 158 159 160
  xvisual = ((GdkVisualPrivate*) visual)->xvisual;
  private->ximage = XCreateImage(private->xdisplay, xvisual, 1, XYBitmap,
				 0, 0, w ,h, 8, 0);
  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
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
} /* gdk_image_new_bitmap() */

static int
gdk_image_check_xshm(Display *display)
/* 
 * Desc: query the server for support for the MIT_SHM extension
 * Return:  0 = not available
 *          1 = shared XImage support available
 *          2 = shared Pixmap support available also
 */
{
#ifdef USE_SHM
  int major, minor, ignore;
  Bool pixmaps;
  
  if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) 
    {
      if (XShmQueryVersion(display, &major, &minor, &pixmaps )==True) 
	{
	  return (pixmaps==True) ? 2 : 1;
	}
    }
#endif /* USE_SHM */
  return 0;
}

void
188
_gdk_windowing_image_init (void)
Elliot Lee's avatar
Elliot Lee committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
{
  if (gdk_use_xshm)
    {
      if (!gdk_image_check_xshm (gdk_display))
	{
	  gdk_use_xshm = False;
	}
    }
}

GdkImage*
gdk_image_new (GdkImageType  type,
	       GdkVisual    *visual,
	       gint          width,
	       gint          height)
{
  GdkImage *image;
206
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
#ifdef USE_SHM
  XShmSegmentInfo *x_shm_info;
#endif /* USE_SHM */
  Visual *xvisual;

  switch (type)
    {
    case GDK_IMAGE_FASTEST:
      image = gdk_image_new (GDK_IMAGE_SHARED, visual, width, height);

      if (!image)
	image = gdk_image_new (GDK_IMAGE_NORMAL, visual, width, height);
      break;

    default:
222
      image = g_object_new (gdk_image_get_type (), NULL);
223 224
      
      private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

      private->xdisplay = gdk_display;

      image->type = type;
      image->visual = visual;
      image->width = width;
      image->height = height;
      image->depth = visual->depth;

      xvisual = ((GdkVisualPrivate*) visual)->xvisual;

      switch (type)
	{
	case GDK_IMAGE_SHARED:
#ifdef USE_SHM
	  if (gdk_use_xshm)
	    {
242
	      private->x_shm_info = g_new (XShmSegmentInfo, 1);
Elliot Lee's avatar
Elliot Lee committed
243
	      x_shm_info = private->x_shm_info;
Owen Taylor's avatar
Owen Taylor committed
244 245
	      x_shm_info->shmid = -1;
	      x_shm_info->shmaddr = (char*) -1;
Elliot Lee's avatar
Elliot Lee committed
246 247 248 249 250 251

	      private->ximage = XShmCreateImage (private->xdisplay, xvisual, visual->depth,
						 ZPixmap, NULL, x_shm_info, width, height);
	      if (private->ximage == NULL)
		{
		  g_warning ("XShmCreateImage failed");
Owen Taylor's avatar
Owen Taylor committed
252 253 254
		  gdk_use_xshm = FALSE;

		  goto error;
Elliot Lee's avatar
Elliot Lee committed
255 256 257 258
		}

	      x_shm_info->shmid = shmget (IPC_PRIVATE,
					  private->ximage->bytes_per_line * private->ximage->height,
259
					  IPC_CREAT | 0600);
Elliot Lee's avatar
Elliot Lee committed
260 261 262

	      if (x_shm_info->shmid == -1)
		{
263
		  /* EINVAL indicates, most likely, that the segment we asked for
264 265
		   * is bigger than SHMMAX, so we don't treat it as a permanent
		   * error. ENOSPC and ENOMEM may also indicate this, but
266 267 268 269
		   * more likely are permanent errors.
		   */
		  if (errno != EINVAL)
		    {
270
		      g_warning ("shmget failed: error %d (%s)", errno, g_strerror (errno));
Owen Taylor's avatar
Owen Taylor committed
271
		      gdk_use_xshm = FALSE;
272
		    }
Elliot Lee's avatar
Elliot Lee committed
273

Owen Taylor's avatar
Owen Taylor committed
274
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
275 276 277 278 279 280 281 282
		}

	      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)
		{
283 284 285 286 287
		  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.
		   */
Owen Taylor's avatar
Owen Taylor committed
288 289
		  gdk_use_xshm = FALSE;
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
290 291
		}

292
	      gdk_error_trap_push ();
Elliot Lee's avatar
Elliot Lee committed
293 294 295 296

	      XShmAttach (private->xdisplay, x_shm_info);
	      XSync (private->xdisplay, False);

297
	      if (gdk_error_trap_pop ())
Elliot Lee's avatar
Elliot Lee committed
298
		{
299
		  /* this is the common failure case so omit warning */
Owen Taylor's avatar
Owen Taylor committed
300 301
		  gdk_use_xshm = FALSE;
		  goto error;
Elliot Lee's avatar
Elliot Lee committed
302
		}
303 304 305 306 307 308 309 310
	      
	      /* 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
311 312 313 314 315 316

	      if (image)
		image_list = g_list_prepend (image_list, image);
	    }
	  else
#endif /* USE_SHM */
317
	    goto error;
Owen Taylor's avatar
Owen Taylor committed
318
	  break;
Elliot Lee's avatar
Elliot Lee committed
319 320 321 322
	case GDK_IMAGE_NORMAL:
	  private->ximage = XCreateImage (private->xdisplay, xvisual, visual->depth,
					  ZPixmap, 0, 0, width, height, 32, 0);

323 324 325 326 327
	  /* 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
328 329
	  if (!private->ximage->data)
	    goto error;
Elliot Lee's avatar
Elliot Lee committed
330 331 332 333 334 335 336 337 338 339 340
	  break;

	case GDK_IMAGE_FASTEST:
	  g_assert_not_reached ();
	}

      if (image)
	{
	  image->byte_order = private->ximage->byte_order;
	  image->mem = private->ximage->data;
	  image->bpl = private->ximage->bytes_per_line;
341
	  image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
342
	  image->bits_per_pixel = private->ximage->bits_per_pixel;
Elliot Lee's avatar
Elliot Lee committed
343 344 345 346
	}
    }

  return image;
Owen Taylor's avatar
Owen Taylor committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

 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
371 372 373
}

GdkImage*
Havoc Pennington's avatar
Havoc Pennington committed
374 375 376 377 378
_gdk_x11_get_image (GdkDrawable    *drawable,
                    gint            x,
                    gint            y,
                    gint            width,
                    gint            height)
Elliot Lee's avatar
Elliot Lee committed
379 380
{
  GdkImage *image;
381
  GdkImagePrivateX11 *private;
Havoc Pennington's avatar
Havoc Pennington committed
382 383
  GdkDrawableImplX11 *impl;
  GdkVisual *visual;
384
  gboolean have_grab;
385
  GdkRectangle req;
386
  GdkRectangle window_rect;
387
  XImage *ximage;
388
  
Havoc Pennington's avatar
Havoc Pennington committed
389
  g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_X11 (drawable), NULL);
Elliot Lee's avatar
Elliot Lee committed
390

Havoc Pennington's avatar
Havoc Pennington committed
391
  visual = gdk_drawable_get_visual (drawable);
Elliot Lee's avatar
Elliot Lee committed
392

393
  g_assert (visual || !GDK_IS_WINDOW_IMPL_X11 (drawable));
Elliot Lee's avatar
Elliot Lee committed
394

Havoc Pennington's avatar
Havoc Pennington committed
395 396
  impl = GDK_DRAWABLE_IMPL_X11 (drawable);
  
397
  have_grab = FALSE;
398

399
  if (GDK_IS_WINDOW_IMPL_X11 (drawable))
400 401
    {
      GdkRectangle screen_rect;
402 403 404
      Window child;

      g_assert (visual);
405

406 407
      have_grab = TRUE;
      gdk_x11_grab_server ();
408 409 410

      /* Translate screen area into window coordinates */
      XTranslateCoordinates (gdk_display,
411
			     gdk_root_window,
412
                             impl->xid,
413 414 415
			     0, 0, 
			     &screen_rect.x, &screen_rect.y, 
			     &child);
416

417 418 419 420
      screen_rect.width = gdk_screen_width ();
      screen_rect.height = gdk_screen_height ();
      
      gdk_error_trap_push ();
421 422 423

      window_rect.x = 0;
      window_rect.y = 0;
424
      
425 426
      gdk_window_get_geometry (GDK_WINDOW (impl->wrapper),
                               NULL, NULL,
427 428 429
                               &window_rect.width,
                               &window_rect.height,
                               NULL);
430 431 432 433
      
      /* compute intersection of screen and window, in window
       * coordinates
       */
434
      if (gdk_error_trap_pop () ||
435
          !gdk_rectangle_intersect (&window_rect, &screen_rect, 
436 437
                                    &window_rect))
        {
438 439
          if (have_grab)
            gdk_x11_ungrab_server ();
440 441 442
          return image = gdk_image_new (GDK_IMAGE_FASTEST,
                                        visual,
                                        width, height);
443 444
        }
    }
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
  else
    {
      window_rect.x = 0;
      window_rect.y = 0;
      gdk_drawable_get_size (drawable,
			     &window_rect.width,
			     &window_rect.height);
    }
      
  req.x = x;
  req.y = y;
  req.width = width;
  req.height = height;
  
  /* window_rect specifies the part of drawable which we can get from
460 461 462 463
   * the server in window coordinates. 
   * For pixmaps this is all of the pixmap, for windows it is just 
   * the onscreen part.
   */
464
  if (!gdk_rectangle_intersect (&req, &window_rect, &req) && visual) 
465
    {      
466 467 468 469 470 471
      if (have_grab)
	gdk_x11_ungrab_server ();
      return image = gdk_image_new (GDK_IMAGE_FASTEST,
                                    visual,
                                    width, height);
    }
472

473 474 475 476
  if (req.x != x || req.y != y)
    {
      g_assert (GDK_IS_WINDOW (drawable));
      g_assert (visual);
477

478 479 480 481
      image = gdk_image_new (GDK_IMAGE_FASTEST,
                             visual,
                             width, height);
      if (image == NULL)
482 483 484 485 486
        {
          if (have_grab)
            gdk_x11_ungrab_server ();
          return NULL;
        }
487

488
      private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
489

490
      gdk_error_trap_push ();
Elliot Lee's avatar
Elliot Lee committed
491

492 493 494 495 496 497 498 499
      ximage = XGetSubImage (impl->xdisplay,
                             impl->xid,
                             req.x, req.y, req.width, req.height,
                             AllPlanes, ZPixmap,
                             private->ximage,
                             req.x - x, req.y - y);

      if (have_grab)
500 501 502 503
        {
          gdk_x11_ungrab_server ();
          have_grab = FALSE;
        }
504 505 506 507 508 509 510 511
      
      if (gdk_error_trap_pop () || ximage == NULL)
        {
          g_object_unref (G_OBJECT (image));
          return NULL;
        }

      g_assert (ximage == private->ximage);
512
    }
513 514
  else
    {
515 516 517 518
      /* Here we ignore the req.width, req.height -
       * XGetImage() will do the right thing without
       * them.
       */
519 520 521 522 523
      ximage = XGetImage (impl->xdisplay,
                          impl->xid,
                          x, y, width, height,
                          AllPlanes, ZPixmap);

524
      if (have_grab)
525
        {
526 527 528 529 530 531
          gdk_x11_ungrab_server ();
          have_grab = FALSE;
        }      

      if (!ximage)
        return NULL;
Elliot Lee's avatar
Elliot Lee committed
532

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
      image = g_object_new (gdk_image_get_type (), NULL);

      private = PRIVATE_DATA (image);

      private->xdisplay = gdk_display;
      private->ximage = ximage;

      image->type = GDK_IMAGE_NORMAL;
      image->visual = visual; /* May 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; 
    }
Elliot Lee's avatar
Elliot Lee committed
552

553 554
  g_assert (!have_grab);
  
Elliot Lee's avatar
Elliot Lee committed
555 556 557 558 559 560 561 562 563
  return image;
}

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

566
  g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
Elliot Lee's avatar
Elliot Lee committed
567

568
  private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
569 570 571 572 573 574 575 576 577 578 579 580

  pixel = XGetPixel (private->ximage, x, y);

  return pixel;
}

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

583
  g_return_if_fail (GDK_IS_IMAGE (image));
Elliot Lee's avatar
Elliot Lee committed
584

585
  private = PRIVATE_DATA (image);
Elliot Lee's avatar
Elliot Lee committed
586 587 588 589

  pixel = XPutPixel (private->ximage, x, y, pixel);
}

590 591
static void
gdk_x11_image_destroy (GdkImage *image)
Elliot Lee's avatar
Elliot Lee committed
592
{
593
  GdkImagePrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
594 595 596 597
#ifdef USE_SHM
  XShmSegmentInfo *x_shm_info;
#endif /* USE_SHM */

598
  g_return_if_fail (GDK_IS_IMAGE (image));
Elliot Lee's avatar
Elliot Lee committed
599

600 601 602 603 604 605 606
  private = PRIVATE_DATA (image);

  if (private == NULL) /* This means that gdk_image_exit() destroyed the
                        * image already, and now we're called a second
                        * time from _finalize()
                        */
    return;
Elliot Lee's avatar
Elliot Lee committed
607

Owen Taylor's avatar
Owen Taylor committed
608 609 610 611 612 613 614 615 616
  if (private->ximage)		/* Deal with failure of creation */
    {
      switch (image->type)
	{
	case GDK_IMAGE_NORMAL:
	  XDestroyImage (private->ximage);
	  break;
	  
	case GDK_IMAGE_SHARED:
Elliot Lee's avatar
Elliot Lee committed
617
#ifdef USE_SHM
Owen Taylor's avatar
Owen Taylor committed
618 619 620 621 622 623 624 625 626 627 628 629
	  gdk_flush();
	  
	  XShmDetach (private->xdisplay, private->x_shm_info);
	  XDestroyImage (private->ximage);
	  
	  x_shm_info = private->x_shm_info;
	  shmdt (x_shm_info->shmaddr);
	  
	  g_free (private->x_shm_info);
	  private->x_shm_info = NULL;
	  
	  image_list = g_list_remove (image_list, image);
Elliot Lee's avatar
Elliot Lee committed
630
#else /* USE_SHM */
Owen Taylor's avatar
Owen Taylor committed
631
	  g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
Elliot Lee's avatar
Elliot Lee committed
632
#endif /* USE_SHM */
Owen Taylor's avatar
Owen Taylor committed
633 634 635 636 637
	  break;
	  
	case GDK_IMAGE_FASTEST:
	  g_assert_not_reached ();
	}
Elliot Lee's avatar
Elliot Lee committed
638 639
    }

640 641
  g_free (private);
  image->windowing_data = NULL;
Elliot Lee's avatar
Elliot Lee committed
642
}