]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkimage-x11.c
fixed a segfault that showed up when using the color picker.
[~andy/gtk] / gdk / x11 / gdkimage-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 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  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * 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
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28
29 #include <stdlib.h>
30 #include <sys/types.h>
31
32 #if defined (HAVE_IPC_H) && defined (HAVE_SHM_H) && defined (HAVE_XSHM_H)
33 #define USE_SHM
34 #endif
35
36 #ifdef USE_SHM
37 #include <sys/ipc.h>
38 #include <sys/shm.h>
39 #endif /* USE_SHM */
40
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43
44 #ifdef USE_SHM
45 #include <X11/extensions/XShm.h>
46 #endif /* USE_SHM */
47
48 #include <errno.h>
49
50 #include "gdk.h"                /* For gdk_error_trap_* / gdk_flush_* */
51 #include "gdkimage.h"
52 #include "gdkprivate.h"
53 #include "gdkprivate-x11.h"
54
55 typedef struct _GdkImagePrivateX11     GdkImagePrivateX11;
56
57 struct _GdkImagePrivateX11
58 {
59   XImage *ximage;
60   Display *xdisplay;
61   gpointer x_shm_info;
62   Pixmap shm_pixmap;
63 };
64
65 static GList *image_list = NULL;
66 static gpointer parent_class = NULL;
67 static gboolean have_shm_pixmaps;
68
69 static void gdk_x11_image_destroy (GdkImage      *image);
70 static void gdk_image_init        (GdkImage      *image);
71 static void gdk_image_class_init  (GdkImageClass *klass);
72 static void gdk_image_finalize    (GObject       *object);
73
74 #define PRIVATE_DATA(image) ((GdkImagePrivateX11 *) GDK_IMAGE (image)->windowing_data)
75
76 GType
77 gdk_image_get_type (void)
78 {
79   static GType object_type = 0;
80
81   if (!object_type)
82     {
83       static const GTypeInfo object_info =
84       {
85         sizeof (GdkImageClass),
86         (GBaseInitFunc) NULL,
87         (GBaseFinalizeFunc) NULL,
88         (GClassInitFunc) gdk_image_class_init,
89         NULL,           /* class_finalize */
90         NULL,           /* class_data */
91         sizeof (GdkImage),
92         0,              /* n_preallocs */
93         (GInstanceInitFunc) gdk_image_init,
94       };
95       
96       object_type = g_type_register_static (G_TYPE_OBJECT,
97                                             "GdkImage",
98                                             &object_info, 0);
99     }
100   
101   return object_type;
102 }
103
104 static void
105 gdk_image_init (GdkImage *image)
106 {
107   image->windowing_data = g_new0 (GdkImagePrivateX11, 1);
108 }
109
110 static void
111 gdk_image_class_init (GdkImageClass *klass)
112 {
113   GObjectClass *object_class = G_OBJECT_CLASS (klass);
114
115   parent_class = g_type_class_peek_parent (klass);
116
117   object_class->finalize = gdk_image_finalize;
118 }
119
120 static void
121 gdk_image_finalize (GObject *object)
122 {
123   GdkImage *image = GDK_IMAGE (object);
124
125   gdk_x11_image_destroy (image);
126   
127   G_OBJECT_CLASS (parent_class)->finalize (object);
128 }
129
130
131 void
132 _gdk_image_exit (void)
133 {
134   GdkImage *image;
135
136   while (image_list)
137     {
138       image = image_list->data;
139       gdk_x11_image_destroy (image);
140     }
141 }
142
143 GdkImage *
144 gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h)
145 /*
146  * Desc: create a new bitmap image
147  */
148 {
149   Visual *xvisual;
150   GdkImage *image;
151   GdkImagePrivateX11 *private;
152   image = g_object_new (gdk_image_get_type (), NULL);
153   private = PRIVATE_DATA (image);
154   private->xdisplay = gdk_display;
155   image->type = GDK_IMAGE_NORMAL;
156   image->visual = visual;
157   image->width = w;
158   image->height = h;
159   image->depth = 1;
160   image->bits_per_pixel = 1;
161   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
162   private->ximage = XCreateImage(private->xdisplay, xvisual, 1, XYBitmap,
163                                  0, 0, w ,h, 8, 0);
164   private->ximage->data = data;
165   private->ximage->bitmap_bit_order = MSBFirst;
166   private->ximage->byte_order = MSBFirst;
167   image->byte_order = MSBFirst;
168   image->mem =  private->ximage->data;
169   image->bpl = private->ximage->bytes_per_line;
170   image->bpp = 1;
171   return(image);
172 } /* gdk_image_new_bitmap() */
173
174 static int
175 gdk_image_check_xshm(Display *display)
176 /* 
177  * Desc: query the server for support for the MIT_SHM extension
178  * Return:  0 = not available
179  *          1 = shared XImage support available
180  *          2 = shared Pixmap support available also
181  */
182 {
183 #ifdef USE_SHM
184   int major, minor, ignore;
185   Bool pixmaps;
186   
187   if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) 
188     {
189       if (XShmQueryVersion(display, &major, &minor, &pixmaps )==True) 
190         {
191           return (pixmaps==True) ? 2 : 1;
192         }
193     }
194 #endif /* USE_SHM */
195   return 0;
196 }
197
198 void
199 _gdk_windowing_image_init (void)
200 {
201   if (_gdk_use_xshm)
202     {
203       gint res = gdk_image_check_xshm (gdk_display);
204
205       if (!res)
206         _gdk_use_xshm = FALSE;
207       else 
208         have_shm_pixmaps = (res == 2);
209     }
210 }
211
212 GdkImage*
213 _gdk_image_new_for_depth (GdkImageType  type,
214                           GdkVisual    *visual,
215                           gint          width,
216                           gint          height,
217                           gint          depth)
218 {
219   GdkImage *image;
220   GdkImagePrivateX11 *private;
221 #ifdef USE_SHM
222   XShmSegmentInfo *x_shm_info;
223 #endif /* USE_SHM */
224   Visual *xvisual = NULL;
225
226   g_return_val_if_fail (!visual || GDK_IS_VISUAL (visual), NULL);
227   g_return_val_if_fail (visual || depth != -1, NULL);
228
229   if (visual)
230     depth = visual->depth;
231   
232   switch (type)
233     {
234     case GDK_IMAGE_FASTEST:
235       image = _gdk_image_new_for_depth (GDK_IMAGE_SHARED, visual, width, height, depth);
236
237       if (!image)
238         image = _gdk_image_new_for_depth (GDK_IMAGE_NORMAL, visual, width, height, depth);
239       break;
240
241     default:
242       image = g_object_new (gdk_image_get_type (), NULL);
243       
244       private = PRIVATE_DATA (image);
245
246       private->xdisplay = gdk_display;
247
248       image->type = type;
249       image->visual = visual;
250       image->width = width;
251       image->height = height;
252       image->depth = depth;
253
254       if (visual)
255         xvisual = ((GdkVisualPrivate*) visual)->xvisual;
256
257       switch (type)
258         {
259         case GDK_IMAGE_SHARED:
260 #ifdef USE_SHM
261           if (_gdk_use_xshm)
262             {
263               private->x_shm_info = g_new (XShmSegmentInfo, 1);
264               x_shm_info = private->x_shm_info;
265               x_shm_info->shmid = -1;
266               x_shm_info->shmaddr = (char*) -1;
267
268               private->ximage = XShmCreateImage (private->xdisplay, xvisual, depth,
269                                                  ZPixmap, NULL, x_shm_info, width, height);
270               if (private->ximage == NULL)
271                 {
272                   g_warning ("XShmCreateImage failed");
273                   _gdk_use_xshm = FALSE;
274
275                   goto error;
276                 }
277
278               x_shm_info->shmid = shmget (IPC_PRIVATE,
279                                           private->ximage->bytes_per_line * private->ximage->height,
280                                           IPC_CREAT | 0600);
281
282               if (x_shm_info->shmid == -1)
283                 {
284                   /* EINVAL indicates, most likely, that the segment we asked for
285                    * is bigger than SHMMAX, so we don't treat it as a permanent
286                    * error. ENOSPC and ENOMEM may also indicate this, but
287                    * more likely are permanent errors.
288                    */
289                   if (errno != EINVAL)
290                     {
291                       g_warning ("shmget failed: error %d (%s)", errno, g_strerror (errno));
292                       _gdk_use_xshm = FALSE;
293                     }
294
295                   goto error;
296                 }
297
298               x_shm_info->readOnly = False;
299               x_shm_info->shmaddr = shmat (x_shm_info->shmid, 0, 0);
300               private->ximage->data = x_shm_info->shmaddr;
301
302               if (x_shm_info->shmaddr == (char*) -1)
303                 {
304                   g_warning ("shmat failed: error %d (%s)", errno, g_strerror (errno));
305                   /* Failure in shmat is almost certainly permanent. Most likely error is
306                    * EMFILE, which would mean that we've exceeded the per-process
307                    * Shm segment limit.
308                    */
309                   _gdk_use_xshm = FALSE;
310                   goto error;
311                 }
312
313               gdk_error_trap_push ();
314
315               XShmAttach (private->xdisplay, x_shm_info);
316               XSync (private->xdisplay, False);
317
318               if (gdk_error_trap_pop ())
319                 {
320                   /* this is the common failure case so omit warning */
321                   _gdk_use_xshm = FALSE;
322                   goto error;
323                 }
324               
325               /* We mark the segment as destroyed so that when
326                * the last process detaches, it will be deleted.
327                * There is a small possibility of leaking if
328                * we die in XShmAttach. In theory, a signal handler
329                * could be set up.
330                */
331               shmctl (x_shm_info->shmid, IPC_RMID, 0);                
332
333               if (image)
334                 image_list = g_list_prepend (image_list, image);
335             }
336           else
337 #endif /* USE_SHM */
338             goto error;
339           break;
340         case GDK_IMAGE_NORMAL:
341           private->ximage = XCreateImage (private->xdisplay, xvisual, depth,
342                                           ZPixmap, 0, 0, width, height, 32, 0);
343
344           /* Use malloc, not g_malloc here, because X will call free()
345            * on this data
346            */
347           private->ximage->data = malloc (private->ximage->bytes_per_line *
348                                           private->ximage->height);
349           if (!private->ximage->data)
350             goto error;
351           break;
352
353         case GDK_IMAGE_FASTEST:
354           g_assert_not_reached ();
355         }
356
357       if (image)
358         {
359           image->byte_order = (private->ximage->byte_order == LSBFirst) ? GDK_LSB_FIRST : GDK_MSB_FIRST;
360           image->mem = private->ximage->data;
361           image->bpl = private->ximage->bytes_per_line;
362           image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
363           image->bits_per_pixel = private->ximage->bits_per_pixel;
364         }
365     }
366
367   return image;
368
369  error:
370   if (private->ximage)
371     {
372       XDestroyImage (private->ximage);
373       private->ximage = NULL;
374     }
375 #ifdef USE_SHM
376   if (private->x_shm_info)
377     {
378       x_shm_info = private->x_shm_info;
379       
380       if (x_shm_info->shmaddr != (char *)-1)
381         shmdt (x_shm_info->shmaddr);
382       if (x_shm_info->shmid != -1) 
383         shmctl (x_shm_info->shmid, IPC_RMID, 0);
384       
385       g_free (x_shm_info);
386       private->x_shm_info = NULL;
387     }
388 #endif /* USE_SHM */
389   g_object_unref (image);
390   
391   return NULL;
392 }
393
394 Pixmap
395 _gdk_x11_image_get_shm_pixmap (GdkImage *image)
396 {
397   GdkImagePrivateX11 *private = PRIVATE_DATA (image);
398
399 #ifdef USE_SHM  
400   /* Future: do we need one of these per-screen per-image? ShmPixmaps
401    * are the same for every screen, but can they be shared?
402    */
403   if (!private->shm_pixmap && image->type == GDK_IMAGE_SHARED && have_shm_pixmaps)
404     private->shm_pixmap = XShmCreatePixmap (private->xdisplay, _gdk_root_window,
405                                             image->mem, private->x_shm_info, 
406                                             image->width, image->height, image->depth);
407
408   return private->shm_pixmap;
409 #else
410   return None;
411 #endif    
412 }
413
414 GdkImage*
415 gdk_image_new (GdkImageType  type,
416                GdkVisual    *visual,
417                gint          width,
418                gint          height)
419 {
420   return _gdk_image_new_for_depth (type, visual, width, height, -1);
421 }
422
423 GdkImage*
424 get_full_image (GdkDrawable    *drawable,
425                 gint            src_x,
426                 gint            src_y,
427                 gint            width,
428                 gint            height)
429 {
430   GdkImage *image;
431   GdkImagePrivateX11 *private;
432   GdkDrawableImplX11 *impl;
433   XImage *ximage;
434
435   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
436   
437   ximage = XGetImage (impl->xdisplay,
438                       impl->xid,
439                       src_x, src_y, width, height,
440                       AllPlanes, ZPixmap);
441   
442   if (!ximage)
443     return NULL;
444   
445   image = g_object_new (gdk_image_get_type (), NULL);
446   
447   private = PRIVATE_DATA (image);
448   
449   private->xdisplay = gdk_display;
450   private->ximage = ximage;
451   
452   image->type = GDK_IMAGE_NORMAL;
453   image->visual = gdk_drawable_get_visual (drawable); /* could be NULL */
454   image->width = width;
455   image->height = height;
456   image->depth = gdk_drawable_get_depth (drawable);
457   
458   image->mem = private->ximage->data;
459   image->bpl = private->ximage->bytes_per_line;
460   image->bits_per_pixel = private->ximage->bits_per_pixel;
461   image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
462   image->byte_order = (private->ximage->byte_order == LSBFirst) ? GDK_LSB_FIRST : GDK_MSB_FIRST;
463   
464   return image;
465 }
466
467 GdkImage*
468 _gdk_x11_copy_to_image (GdkDrawable    *drawable,
469                         GdkImage       *image,
470                         gint            src_x,
471                         gint            src_y,
472                         gint            dest_x,
473                         gint            dest_y,
474                         gint            width,
475                         gint            height)
476 {
477   GdkImagePrivateX11 *private;
478   GdkDrawableImplX11 *impl;
479   GdkVisual *visual;
480   gboolean have_grab;
481   GdkRectangle req;
482   GdkRectangle window_rect;
483   Pixmap shm_pixmap = None;
484   gboolean success = TRUE;
485   
486   g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_X11 (drawable), NULL);
487   g_return_val_if_fail (image != NULL || (dest_x == 0 && dest_y == 0), NULL);
488
489   visual = gdk_drawable_get_visual (drawable);
490   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
491   
492   have_grab = FALSE;
493
494 #define UNGRAB() G_STMT_START {                 \
495     if (have_grab) {                            \
496       gdk_x11_ungrab_server ();                 \
497       XFlush (impl->xdisplay);                  \
498       have_grab = FALSE; }                      \
499   } G_STMT_END
500
501   if (!image && !GDK_IS_WINDOW_IMPL_X11 (drawable))
502     return get_full_image (drawable, src_x, src_y, width, height);
503
504   if (image && image->type == GDK_IMAGE_SHARED)
505     {
506       shm_pixmap = _gdk_x11_image_get_shm_pixmap (image);
507       if (shm_pixmap)
508         {
509           /* Again easy, we can just XCopyArea, and don't have to worry about clipping
510            */
511           GC xgc = XCreateGC (impl->xdisplay, impl->xid, 0, NULL);
512           
513           XCopyArea (impl->xdisplay, impl->xid, shm_pixmap, xgc,
514                      src_x, src_y, width, height, dest_x, dest_y);
515           XSync (impl->xdisplay, FALSE);
516           
517           XFreeGC (impl->xdisplay, xgc);
518           
519           return image;
520         }
521     }
522
523   /* Now the general case - we may have to worry about clipping to the screen
524    * bounds, in which case we'll have to grab the server and only get a piece
525    * of the window.
526    */
527   if (GDK_IS_WINDOW_IMPL_X11 (drawable))
528     {
529       GdkRectangle screen_rect;
530       Window child;
531
532       have_grab = TRUE;
533       gdk_x11_grab_server ();
534
535       /* Translate screen area into window coordinates */
536       XTranslateCoordinates (gdk_display,
537                              _gdk_root_window,
538                              impl->xid,
539                              0, 0, 
540                              &screen_rect.x, &screen_rect.y, 
541                              &child);
542
543       screen_rect.width = gdk_screen_width ();
544       screen_rect.height = gdk_screen_height ();
545       
546       gdk_error_trap_push ();
547
548       window_rect.x = 0;
549       window_rect.y = 0;
550       
551       gdk_window_get_geometry (GDK_WINDOW (impl->wrapper),
552                                NULL, NULL,
553                                &window_rect.width,
554                                &window_rect.height,
555                                NULL);
556       
557       /* compute intersection of screen and window, in window
558        * coordinates
559        */
560       if (gdk_error_trap_pop () ||
561           !gdk_rectangle_intersect (&window_rect, &screen_rect, 
562                                     &window_rect))
563         goto out;
564     }
565   else
566     {
567       window_rect.x = 0;
568       window_rect.y = 0;
569       gdk_drawable_get_size (drawable,
570                              &window_rect.width,
571                              &window_rect.height);
572     }
573       
574   req.x = src_x;
575   req.y = src_y;
576   req.width = width;
577   req.height = height;
578   
579   /* window_rect specifies the part of drawable which we can get from
580    * the server in window coordinates. 
581    * For pixmaps this is all of the pixmap, for windows it is just 
582    * the onscreen part.
583    */
584   if (!gdk_rectangle_intersect (&req, &window_rect, &req))
585     goto out;
586
587   gdk_error_trap_push ();
588   
589   if (!image &&
590       req.x == src_x && req.y == src_y && req.width == width && req.height == height)
591     {
592       image = get_full_image (drawable, src_x, src_y, width, height);
593       if (!image)
594         success = FALSE;
595     }
596   else
597     {
598       gboolean created_image = FALSE;
599       
600       if (!image)
601         {
602           image = _gdk_image_new_for_depth (GDK_IMAGE_NORMAL, visual, width, height,
603                                             gdk_drawable_get_depth (drawable));
604           created_image = TRUE;
605         }
606
607       private = PRIVATE_DATA (image);
608
609       /* In the ShmImage but no ShmPixmap case, we could use XShmGetImage when
610        * we are getting the entire image.
611        */
612       if (XGetSubImage (impl->xdisplay,
613                         impl->xid,
614                         req.x, req.y, req.width, req.height,
615                         AllPlanes, ZPixmap,
616                         private->ximage,
617                         dest_x + req.x - src_x, dest_y + req.y - src_y) == None)
618         {
619           if (created_image)
620             g_object_unref (image);
621           image = NULL;
622           success = FALSE;
623         }
624     }
625
626  out:
627   
628   if (have_grab)
629     {                           
630       gdk_x11_ungrab_server ();
631       XFlush (impl->xdisplay);
632       have_grab = FALSE;
633     }
634   
635   gdk_error_trap_pop ();
636
637   if (success && !image)
638     {
639       /* We "succeeded", but could get no content for the image so return junk */
640       image = _gdk_image_new_for_depth (GDK_IMAGE_NORMAL, visual, width, height,
641                                         gdk_drawable_get_depth (drawable));
642     }
643       
644   return image;
645 }
646
647 guint32
648 gdk_image_get_pixel (GdkImage *image,
649                      gint x,
650                      gint y)
651 {
652   guint32 pixel;
653   GdkImagePrivateX11 *private;
654
655   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
656
657   private = PRIVATE_DATA (image);
658
659   pixel = XGetPixel (private->ximage, x, y);
660
661   return pixel;
662 }
663
664 void
665 gdk_image_put_pixel (GdkImage *image,
666                      gint x,
667                      gint y,
668                      guint32 pixel)
669 {
670   GdkImagePrivateX11 *private;
671
672   g_return_if_fail (GDK_IS_IMAGE (image));
673
674   private = PRIVATE_DATA (image);
675
676   pixel = XPutPixel (private->ximage, x, y, pixel);
677 }
678
679 static void
680 gdk_x11_image_destroy (GdkImage *image)
681 {
682   GdkImagePrivateX11 *private;
683 #ifdef USE_SHM
684   XShmSegmentInfo *x_shm_info;
685 #endif /* USE_SHM */
686
687   g_return_if_fail (GDK_IS_IMAGE (image));
688
689   private = PRIVATE_DATA (image);
690
691   if (private == NULL) /* This means that _gdk_image_exit() destroyed the
692                         * image already, and now we're called a second
693                         * time from _finalize()
694                         */
695     return;
696
697   if (private->ximage)          /* Deal with failure of creation */
698     {
699       switch (image->type)
700         {
701         case GDK_IMAGE_NORMAL:
702           XDestroyImage (private->ximage);
703           break;
704           
705         case GDK_IMAGE_SHARED:
706 #ifdef USE_SHM
707           gdk_flush();
708
709           if (private->shm_pixmap)
710             XFreePixmap (private->xdisplay, private->shm_pixmap);
711                   
712           image_list = g_list_remove (image_list, image);
713           XShmDetach (private->xdisplay, private->x_shm_info);
714           XDestroyImage (private->ximage);
715           
716           x_shm_info = private->x_shm_info;
717           shmdt (x_shm_info->shmaddr);
718           
719           g_free (private->x_shm_info);
720           private->x_shm_info = NULL;
721
722 #else /* USE_SHM */
723           g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
724 #endif /* USE_SHM */
725           break;
726           
727         case GDK_IMAGE_FASTEST:
728           g_assert_not_reached ();
729         }
730     }
731
732   g_free (private);
733   image->windowing_data = NULL;
734 }
735
736 Display *
737 gdk_x11_image_get_xdisplay (GdkImage *image)
738 {
739   GdkImagePrivateX11 *private;
740
741   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
742
743   private = PRIVATE_DATA (image);
744
745   return private->xdisplay;
746 }
747
748 XImage *
749 gdk_x11_image_get_ximage (GdkImage *image)
750 {
751   GdkImagePrivateX11 *private;
752
753   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
754
755   private = PRIVATE_DATA (image);
756
757   return private->ximage;
758 }
759
760 gint
761 _gdk_windowing_get_bits_for_depth (gint depth)
762 {
763   XPixmapFormatValues *formats;
764   gint count, i;
765   
766   formats = XListPixmapFormats (gdk_display, &count);
767   
768   for (i = 0; i < count; i++)
769     if (formats[i].depth == depth)
770       {
771         gint result = formats[i].bits_per_pixel;
772         XFree (formats);
773         return result;
774       }
775
776   g_assert_not_reached ();
777   return -1;
778 }