]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkimage-x11.c
The whole GDK_IS_WINDOW() branch of this was a bit screwed up, because it
[~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 static GList *image_list = NULL;
56 static gpointer parent_class = NULL;
57
58 static void gdk_x11_image_destroy (GdkImage      *image);
59 static void gdk_image_init        (GdkImage      *image);
60 static void gdk_image_class_init  (GdkImageClass *klass);
61 static void gdk_image_finalize    (GObject       *object);
62
63 #define PRIVATE_DATA(image) ((GdkImagePrivateX11 *) GDK_IMAGE (image)->windowing_data)
64
65 GType
66 gdk_image_get_type (void)
67 {
68   static GType object_type = 0;
69
70   if (!object_type)
71     {
72       static const GTypeInfo object_info =
73       {
74         sizeof (GdkImageClass),
75         (GBaseInitFunc) NULL,
76         (GBaseFinalizeFunc) NULL,
77         (GClassInitFunc) gdk_image_class_init,
78         NULL,           /* class_finalize */
79         NULL,           /* class_data */
80         sizeof (GdkImage),
81         0,              /* n_preallocs */
82         (GInstanceInitFunc) gdk_image_init,
83       };
84       
85       object_type = g_type_register_static (G_TYPE_OBJECT,
86                                             "GdkImage",
87                                             &object_info, 0);
88     }
89   
90   return object_type;
91 }
92
93 static void
94 gdk_image_init (GdkImage *image)
95 {
96   image->windowing_data = g_new0 (GdkImagePrivateX11, 1);
97 }
98
99 static void
100 gdk_image_class_init (GdkImageClass *klass)
101 {
102   GObjectClass *object_class = G_OBJECT_CLASS (klass);
103
104   parent_class = g_type_class_peek_parent (klass);
105
106   object_class->finalize = gdk_image_finalize;
107 }
108
109 static void
110 gdk_image_finalize (GObject *object)
111 {
112   GdkImage *image = GDK_IMAGE (object);
113
114   gdk_x11_image_destroy (image);
115   
116   G_OBJECT_CLASS (parent_class)->finalize (object);
117 }
118
119
120 void
121 gdk_image_exit (void)
122 {
123   GdkImage *image;
124
125   while (image_list)
126     {
127       image = image_list->data;
128       gdk_x11_image_destroy (image);
129     }
130 }
131
132 GdkImage *
133 gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h)
134 /*
135  * Desc: create a new bitmap image
136  */
137 {
138   Visual *xvisual;
139   GdkImage *image;
140   GdkImagePrivateX11 *private;
141   image = g_object_new (gdk_image_get_type (), NULL);
142   private = PRIVATE_DATA (image);
143   private->xdisplay = gdk_display;
144   image->type = GDK_IMAGE_NORMAL;
145   image->visual = visual;
146   image->width = w;
147   image->height = h;
148   image->depth = 1;
149   image->bits_per_pixel = 1;
150   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
151   private->ximage = XCreateImage(private->xdisplay, xvisual, 1, XYBitmap,
152                                  0, 0, w ,h, 8, 0);
153   private->ximage->data = data;
154   private->ximage->bitmap_bit_order = MSBFirst;
155   private->ximage->byte_order = MSBFirst;
156   image->byte_order = MSBFirst;
157   image->mem =  private->ximage->data;
158   image->bpl = private->ximage->bytes_per_line;
159   image->bpp = 1;
160   return(image);
161 } /* gdk_image_new_bitmap() */
162
163 static int
164 gdk_image_check_xshm(Display *display)
165 /* 
166  * Desc: query the server for support for the MIT_SHM extension
167  * Return:  0 = not available
168  *          1 = shared XImage support available
169  *          2 = shared Pixmap support available also
170  */
171 {
172 #ifdef USE_SHM
173   int major, minor, ignore;
174   Bool pixmaps;
175   
176   if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) 
177     {
178       if (XShmQueryVersion(display, &major, &minor, &pixmaps )==True) 
179         {
180           return (pixmaps==True) ? 2 : 1;
181         }
182     }
183 #endif /* USE_SHM */
184   return 0;
185 }
186
187 void
188 _gdk_windowing_image_init (void)
189 {
190   if (gdk_use_xshm)
191     {
192       if (!gdk_image_check_xshm (gdk_display))
193         {
194           gdk_use_xshm = False;
195         }
196     }
197 }
198
199 GdkImage*
200 gdk_image_new (GdkImageType  type,
201                GdkVisual    *visual,
202                gint          width,
203                gint          height)
204 {
205   GdkImage *image;
206   GdkImagePrivateX11 *private;
207 #ifdef USE_SHM
208   XShmSegmentInfo *x_shm_info;
209 #endif /* USE_SHM */
210   Visual *xvisual;
211
212   switch (type)
213     {
214     case GDK_IMAGE_FASTEST:
215       image = gdk_image_new (GDK_IMAGE_SHARED, visual, width, height);
216
217       if (!image)
218         image = gdk_image_new (GDK_IMAGE_NORMAL, visual, width, height);
219       break;
220
221     default:
222       image = g_object_new (gdk_image_get_type (), NULL);
223       
224       private = PRIVATE_DATA (image);
225
226       private->xdisplay = gdk_display;
227
228       image->type = type;
229       image->visual = visual;
230       image->width = width;
231       image->height = height;
232       image->depth = visual->depth;
233
234       xvisual = ((GdkVisualPrivate*) visual)->xvisual;
235
236       switch (type)
237         {
238         case GDK_IMAGE_SHARED:
239 #ifdef USE_SHM
240           if (gdk_use_xshm)
241             {
242               private->x_shm_info = g_new (XShmSegmentInfo, 1);
243               x_shm_info = private->x_shm_info;
244               x_shm_info->shmid = -1;
245               x_shm_info->shmaddr = (char*) -1;
246
247               private->ximage = XShmCreateImage (private->xdisplay, xvisual, visual->depth,
248                                                  ZPixmap, NULL, x_shm_info, width, height);
249               if (private->ximage == NULL)
250                 {
251                   g_warning ("XShmCreateImage failed");
252                   gdk_use_xshm = FALSE;
253
254                   goto error;
255                 }
256
257               x_shm_info->shmid = shmget (IPC_PRIVATE,
258                                           private->ximage->bytes_per_line * private->ximage->height,
259                                           IPC_CREAT | 0600);
260
261               if (x_shm_info->shmid == -1)
262                 {
263                   /* EINVAL indicates, most likely, that the segment we asked for
264                    * is bigger than SHMMAX, so we don't treat it as a permanent
265                    * error. ENOSPC and ENOMEM may also indicate this, but
266                    * more likely are permanent errors.
267                    */
268                   if (errno != EINVAL)
269                     {
270                       g_warning ("shmget failed: error %d (%s)", errno, g_strerror (errno));
271                       gdk_use_xshm = FALSE;
272                     }
273
274                   goto error;
275                 }
276
277               x_shm_info->readOnly = False;
278               x_shm_info->shmaddr = shmat (x_shm_info->shmid, 0, 0);
279               private->ximage->data = x_shm_info->shmaddr;
280
281               if (x_shm_info->shmaddr == (char*) -1)
282                 {
283                   g_warning ("shmat failed: error %d (%s)", errno, g_strerror (errno));
284                   /* Failure in shmat is almost certainly permanent. Most likely error is
285                    * EMFILE, which would mean that we've exceeded the per-process
286                    * Shm segment limit.
287                    */
288                   gdk_use_xshm = FALSE;
289                   goto error;
290                 }
291
292               gdk_error_trap_push ();
293
294               XShmAttach (private->xdisplay, x_shm_info);
295               XSync (private->xdisplay, False);
296
297               if (gdk_error_trap_pop ())
298                 {
299                   /* this is the common failure case so omit warning */
300                   gdk_use_xshm = FALSE;
301                   goto error;
302                 }
303               
304               /* We mark the segment as destroyed so that when
305                * the last process detaches, it will be deleted.
306                * There is a small possibility of leaking if
307                * we die in XShmAttach. In theory, a signal handler
308                * could be set up.
309                */
310               shmctl (x_shm_info->shmid, IPC_RMID, 0);                
311
312               if (image)
313                 image_list = g_list_prepend (image_list, image);
314             }
315           else
316 #endif /* USE_SHM */
317             goto error;
318           break;
319         case GDK_IMAGE_NORMAL:
320           private->ximage = XCreateImage (private->xdisplay, xvisual, visual->depth,
321                                           ZPixmap, 0, 0, width, height, 32, 0);
322
323           /* Use malloc, not g_malloc here, because X will call free()
324            * on this data
325            */
326           private->ximage->data = malloc (private->ximage->bytes_per_line *
327                                           private->ximage->height);
328           if (!private->ximage->data)
329             goto error;
330           break;
331
332         case GDK_IMAGE_FASTEST:
333           g_assert_not_reached ();
334         }
335
336       if (image)
337         {
338           image->byte_order = private->ximage->byte_order;
339           image->mem = private->ximage->data;
340           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;
343         }
344     }
345
346   return image;
347
348  error:
349   if (private->ximage)
350     {
351       XDestroyImage (private->ximage);
352       private->ximage = NULL;
353     }
354 #ifdef USE_SHM
355   if (private->x_shm_info)
356     {
357       x_shm_info = private->x_shm_info;
358       
359       if (x_shm_info->shmaddr != (char *)-1)
360         shmdt (x_shm_info->shmaddr);
361       if (x_shm_info->shmid != -1) 
362         shmctl (x_shm_info->shmid, IPC_RMID, 0);
363       
364       g_free (x_shm_info);
365       private->x_shm_info = NULL;
366     }
367 #endif /* USE_SHM */
368   g_object_unref (image);
369   
370   return NULL;
371 }
372
373 GdkImage*
374 _gdk_x11_get_image (GdkDrawable    *drawable,
375                     gint            x,
376                     gint            y,
377                     gint            width,
378                     gint            height)
379 {
380   GdkImage *image;
381   GdkImagePrivateX11 *private;
382   GdkDrawableImplX11 *impl;
383   GdkVisual *visual;
384   gboolean have_grab;
385   GdkRectangle req;
386   GdkRectangle window_rect;
387   XImage *ximage;
388   
389   g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_X11 (drawable), NULL);
390
391   visual = gdk_drawable_get_visual (drawable);
392
393   g_assert (visual || !GDK_IS_WINDOW_IMPL_X11 (drawable));
394
395   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
396   
397   have_grab = FALSE;
398
399   if (GDK_IS_WINDOW_IMPL_X11 (drawable))
400     {
401       GdkRectangle screen_rect;
402       Window child;
403
404       g_assert (visual);
405
406       have_grab = TRUE;
407       gdk_x11_grab_server ();
408
409       /* Translate screen area into window coordinates */
410       XTranslateCoordinates (gdk_display,
411                              gdk_root_window,
412                              impl->xid,
413                              0, 0, 
414                              &screen_rect.x, &screen_rect.y, 
415                              &child);
416
417       screen_rect.width = gdk_screen_width ();
418       screen_rect.height = gdk_screen_height ();
419       
420       gdk_error_trap_push ();
421
422       window_rect.x = 0;
423       window_rect.y = 0;
424       
425       gdk_window_get_geometry (GDK_WINDOW (impl->wrapper),
426                                NULL, NULL,
427                                &window_rect.width,
428                                &window_rect.height,
429                                NULL);
430       
431       /* compute intersection of screen and window, in window
432        * coordinates
433        */
434       if (gdk_error_trap_pop () ||
435           !gdk_rectangle_intersect (&window_rect, &screen_rect, 
436                                     &window_rect))
437         {
438           if (have_grab)
439             gdk_x11_ungrab_server ();
440           return image = gdk_image_new (GDK_IMAGE_FASTEST,
441                                         visual,
442                                         width, height);
443         }
444     }
445   else
446     {
447       window_rect.x = 0;
448       window_rect.y = 0;
449       gdk_drawable_get_size (drawable,
450                              &window_rect.width,
451                              &window_rect.height);
452     }
453       
454   req.x = x;
455   req.y = y;
456   req.width = width;
457   req.height = height;
458   
459   /* window_rect specifies the part of drawable which we can get from
460    * the server in window coordinates. 
461    * For pixmaps this is all of the pixmap, for windows it is just 
462    * the onscreen part.
463    */
464   if (!gdk_rectangle_intersect (&req, &window_rect, &req) && visual) 
465     {      
466       if (have_grab)
467         gdk_x11_ungrab_server ();
468       return image = gdk_image_new (GDK_IMAGE_FASTEST,
469                                     visual,
470                                     width, height);
471     }
472
473   if (req.x != x || req.y != y)
474     {
475       g_assert (GDK_IS_WINDOW (drawable));
476       g_assert (visual);
477
478       image = gdk_image_new (GDK_IMAGE_FASTEST,
479                              visual,
480                              width, height);
481       if (image == NULL)
482         {
483           if (have_grab)
484             gdk_x11_ungrab_server ();
485           return NULL;
486         }
487
488       private = PRIVATE_DATA (image);
489
490       gdk_error_trap_push ();
491
492       ximage = XGetSubImage (impl->xdisplay,
493                              impl->xid,
494                              req.x, req.y, req.width, req.height,
495                              AllPlanes, ZPixmap,
496                              private->ximage,
497                              req.x - x, req.y - y);
498
499       if (have_grab)
500         {
501           gdk_x11_ungrab_server ();
502           have_grab = FALSE;
503         }
504       
505       if (gdk_error_trap_pop () || ximage == NULL)
506         {
507           g_object_unref (G_OBJECT (image));
508           return NULL;
509         }
510
511       g_assert (ximage == private->ximage);
512     }
513   else
514     {
515       /* Here we ignore the req.width, req.height -
516        * XGetImage() will do the right thing without
517        * them.
518        */
519       ximage = XGetImage (impl->xdisplay,
520                           impl->xid,
521                           x, y, width, height,
522                           AllPlanes, ZPixmap);
523
524       if (have_grab)
525         {
526           gdk_x11_ungrab_server ();
527           have_grab = FALSE;
528         }      
529
530       if (!ximage)
531         return NULL;
532
533       image = g_object_new (gdk_image_get_type (), NULL);
534
535       private = PRIVATE_DATA (image);
536
537       private->xdisplay = gdk_display;
538       private->ximage = ximage;
539
540       image->type = GDK_IMAGE_NORMAL;
541       image->visual = visual; /* May be NULL */
542       image->width = width;
543       image->height = height;
544       image->depth = gdk_drawable_get_depth (drawable);
545
546       image->mem = private->ximage->data;
547       image->bpl = private->ximage->bytes_per_line;
548       image->bits_per_pixel = private->ximage->bits_per_pixel;
549       image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
550       image->byte_order = private->ximage->byte_order; 
551     }
552
553   g_assert (!have_grab);
554   
555   return image;
556 }
557
558 guint32
559 gdk_image_get_pixel (GdkImage *image,
560                      gint x,
561                      gint y)
562 {
563   guint32 pixel;
564   GdkImagePrivateX11 *private;
565
566   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
567
568   private = PRIVATE_DATA (image);
569
570   pixel = XGetPixel (private->ximage, x, y);
571
572   return pixel;
573 }
574
575 void
576 gdk_image_put_pixel (GdkImage *image,
577                      gint x,
578                      gint y,
579                      guint32 pixel)
580 {
581   GdkImagePrivateX11 *private;
582
583   g_return_if_fail (GDK_IS_IMAGE (image));
584
585   private = PRIVATE_DATA (image);
586
587   pixel = XPutPixel (private->ximage, x, y, pixel);
588 }
589
590 static void
591 gdk_x11_image_destroy (GdkImage *image)
592 {
593   GdkImagePrivateX11 *private;
594 #ifdef USE_SHM
595   XShmSegmentInfo *x_shm_info;
596 #endif /* USE_SHM */
597
598   g_return_if_fail (GDK_IS_IMAGE (image));
599
600   private = PRIVATE_DATA (image);
601
602   if (private == NULL) /* This means that gdk_image_exit() destroyed the
603                         * image already, and now we're called a second
604                         * time from _finalize()
605                         */
606     return;
607
608   if (private->ximage)          /* Deal with failure of creation */
609     {
610       switch (image->type)
611         {
612         case GDK_IMAGE_NORMAL:
613           XDestroyImage (private->ximage);
614           break;
615           
616         case GDK_IMAGE_SHARED:
617 #ifdef USE_SHM
618           gdk_flush();
619           
620           XShmDetach (private->xdisplay, private->x_shm_info);
621           XDestroyImage (private->ximage);
622           
623           x_shm_info = private->x_shm_info;
624           shmdt (x_shm_info->shmaddr);
625           
626           g_free (private->x_shm_info);
627           private->x_shm_info = NULL;
628           
629           image_list = g_list_remove (image_list, image);
630 #else /* USE_SHM */
631           g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
632 #endif /* USE_SHM */
633           break;
634           
635         case GDK_IMAGE_FASTEST:
636           g_assert_not_reached ();
637         }
638     }
639
640   g_free (private);
641   image->windowing_data = NULL;
642 }