]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkimage-x11.c
If possible, create only a single shm segment and use multiple parts of 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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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-1999.  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 /* gcc -ansi -pedantic on GNU/Linux causes warnings and errors
30  * unless this is defined:
31  * warning: #warning "Files using this header must be compiled with _SVID_SOURCE or _XOPEN_SOURCE"
32  */
33 #ifndef _XOPEN_SOURCE
34 #  define _XOPEN_SOURCE 1
35 #endif
36
37 #include <stdlib.h>
38 #include <sys/types.h>
39
40 #if defined (HAVE_IPC_H) && defined (HAVE_SHM_H) && defined (HAVE_XSHM_H)
41 #define USE_SHM
42 #endif
43
44 #ifdef USE_SHM
45 #include <sys/ipc.h>
46 #include <sys/shm.h>
47 #endif /* USE_SHM */
48
49 #include <X11/Xlib.h>
50 #include <X11/Xutil.h>
51
52 #ifdef USE_SHM
53 #include <X11/extensions/XShm.h>
54 #endif /* USE_SHM */
55
56 #include <errno.h>
57
58 #include "gdk.h"                /* For gdk_error_trap_* / gdk_flush_* */
59 #include "gdkimage.h"
60 #include "gdkprivate.h"
61 #include "gdkprivate-x11.h"
62
63 static GList *image_list = NULL;
64 static gpointer parent_class = NULL;
65
66 static void gdk_x11_image_destroy    (GdkImage      *image);
67 static void gdk_image_init       (GdkImage      *image);
68 static void gdk_image_class_init (GdkImageClass *klass);
69 static void gdk_image_finalize   (GObject       *object);
70
71 #define PRIVATE_DATA(image) ((GdkImagePrivateX11 *) GDK_IMAGE (image)->windowing_data)
72
73 GType
74 gdk_image_get_type (void)
75 {
76   static GType object_type = 0;
77
78   if (!object_type)
79     {
80       static const GTypeInfo object_info =
81       {
82         sizeof (GdkImageClass),
83         (GBaseInitFunc) NULL,
84         (GBaseFinalizeFunc) NULL,
85         (GClassInitFunc) gdk_image_class_init,
86         NULL,           /* class_finalize */
87         NULL,           /* class_data */
88         sizeof (GdkImage),
89         0,              /* n_preallocs */
90         (GInstanceInitFunc) gdk_image_init,
91       };
92       
93       object_type = g_type_register_static (G_TYPE_OBJECT,
94                                             "GdkImage",
95                                             &object_info);
96     }
97   
98   return object_type;
99 }
100
101 static void
102 gdk_image_init (GdkImage *image)
103 {
104   image->windowing_data = g_new0 (GdkImagePrivateX11, 1);
105   
106 }
107
108 static void
109 gdk_image_class_init (GdkImageClass *klass)
110 {
111   GObjectClass *object_class = G_OBJECT_CLASS (klass);
112
113   parent_class = g_type_class_peek_parent (klass);
114
115   object_class->finalize = gdk_image_finalize;
116 }
117
118 static void
119 gdk_image_finalize (GObject *object)
120 {
121   GdkImage *image = GDK_IMAGE (object);
122
123   gdk_x11_image_destroy (image);
124   
125   G_OBJECT_CLASS (parent_class)->finalize (object);
126 }
127
128
129 void
130 gdk_image_exit (void)
131 {
132   GdkImage *image;
133
134   while (image_list)
135     {
136       image = image_list->data;
137       gdk_x11_image_destroy (image);
138     }
139 }
140
141 GdkImage *
142 gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h)
143 /*
144  * Desc: create a new bitmap image
145  */
146 {
147   Visual *xvisual;
148   GdkImage *image;
149   GdkImagePrivateX11 *private;
150   image = GDK_IMAGE (g_type_create_instance (gdk_image_get_type ()));
151   private = PRIVATE_DATA (image);
152   private->xdisplay = gdk_display;
153   image->type = GDK_IMAGE_NORMAL;
154   image->visual = visual;
155   image->width = w;
156   image->height = h;
157   image->depth = 1;
158   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
159   private->ximage = XCreateImage(private->xdisplay, xvisual, 1, XYBitmap,
160                                  0, 0, w ,h, 8, 0);
161   private->ximage->data = data;
162   private->ximage->bitmap_bit_order = MSBFirst;
163   private->ximage->byte_order = MSBFirst;
164   image->byte_order = MSBFirst;
165   image->mem =  private->ximage->data;
166   image->bpl = private->ximage->bytes_per_line;
167   image->bpp = 1;
168   return(image);
169 } /* gdk_image_new_bitmap() */
170
171 static int
172 gdk_image_check_xshm(Display *display)
173 /* 
174  * Desc: query the server for support for the MIT_SHM extension
175  * Return:  0 = not available
176  *          1 = shared XImage support available
177  *          2 = shared Pixmap support available also
178  */
179 {
180 #ifdef USE_SHM
181   int major, minor, ignore;
182   Bool pixmaps;
183   
184   if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) 
185     {
186       if (XShmQueryVersion(display, &major, &minor, &pixmaps )==True) 
187         {
188           return (pixmaps==True) ? 2 : 1;
189         }
190     }
191 #endif /* USE_SHM */
192   return 0;
193 }
194
195 void
196 _gdk_windowing_image_init (void)
197 {
198   if (gdk_use_xshm)
199     {
200       if (!gdk_image_check_xshm (gdk_display))
201         {
202           gdk_use_xshm = False;
203         }
204     }
205 }
206
207 GdkImage*
208 gdk_image_new (GdkImageType  type,
209                GdkVisual    *visual,
210                gint          width,
211                gint          height)
212 {
213   GdkImage *image;
214   GdkImagePrivateX11 *private;
215 #ifdef USE_SHM
216   XShmSegmentInfo *x_shm_info;
217 #endif /* USE_SHM */
218   Visual *xvisual;
219
220   switch (type)
221     {
222     case GDK_IMAGE_FASTEST:
223       image = gdk_image_new (GDK_IMAGE_SHARED, visual, width, height);
224
225       if (!image)
226         image = gdk_image_new (GDK_IMAGE_NORMAL, visual, width, height);
227       break;
228
229     default:
230       image = GDK_IMAGE (g_type_create_instance (gdk_image_get_type ()));
231       
232       private = PRIVATE_DATA (image);
233
234       private->xdisplay = gdk_display;
235
236       image->type = type;
237       image->visual = visual;
238       image->width = width;
239       image->height = height;
240       image->depth = visual->depth;
241
242       xvisual = ((GdkVisualPrivate*) visual)->xvisual;
243
244       switch (type)
245         {
246         case GDK_IMAGE_SHARED:
247 #ifdef USE_SHM
248           if (gdk_use_xshm)
249             {
250               private->x_shm_info = g_new (XShmSegmentInfo, 1);
251               x_shm_info = private->x_shm_info;
252
253               private->ximage = XShmCreateImage (private->xdisplay, xvisual, visual->depth,
254                                                  ZPixmap, NULL, x_shm_info, width, height);
255               if (private->ximage == NULL)
256                 {
257                   g_warning ("XShmCreateImage failed");
258                   
259                   g_free (image);
260                   gdk_use_xshm = False;
261                   return NULL;
262                 }
263
264               x_shm_info->shmid = shmget (IPC_PRIVATE,
265                                           private->ximage->bytes_per_line * private->ximage->height,
266                                           IPC_CREAT | 0777);
267
268               if (x_shm_info->shmid == -1)
269                 {
270                   /* EINVAL indicates, most likely, that the segment we asked for
271                    * is bigger than SHMMAX, so we don't treat it as a permanently
272                    * fatal error. ENOSPC and ENOMEM may also indicate this, but
273                    * more likely are permanent errors.
274                    */
275                   if (errno != EINVAL)
276                     {
277                       g_warning ("shmget failed!");
278                       gdk_use_xshm = False;
279                     }
280
281                   XDestroyImage (private->ximage);
282                   g_free (private->x_shm_info);
283                   g_free (image);
284
285                   return NULL;
286                 }
287
288               x_shm_info->readOnly = False;
289               x_shm_info->shmaddr = shmat (x_shm_info->shmid, 0, 0);
290               private->ximage->data = x_shm_info->shmaddr;
291
292               if (x_shm_info->shmaddr == (char*) -1)
293                 {
294                   g_warning ("shmat failed!");
295
296                   XDestroyImage (private->ximage);
297                   shmctl (x_shm_info->shmid, IPC_RMID, 0);
298                   
299                   g_free (private->x_shm_info);
300                   g_free (image);
301
302                   return NULL;
303                 }
304
305               gdk_error_trap_push ();
306
307               XShmAttach (private->xdisplay, x_shm_info);
308               XSync (private->xdisplay, False);
309
310               if (gdk_error_trap_pop ())
311                 {
312                   /* this is the common failure case so omit warning */
313                   XDestroyImage (private->ximage);
314                   shmdt (x_shm_info->shmaddr);
315                   shmctl (x_shm_info->shmid, IPC_RMID, 0);
316                   
317                   g_free (private->x_shm_info);
318                   g_free (image);
319
320                   gdk_use_xshm = False;
321
322                   return NULL;
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             {
338               g_free (image);
339               return NULL;
340             }
341           break;
342 #else /* USE_SHM */
343           g_free (image);
344           return NULL;
345 #endif /* USE_SHM */
346         case GDK_IMAGE_NORMAL:
347           private->ximage = XCreateImage (private->xdisplay, xvisual, visual->depth,
348                                           ZPixmap, 0, 0, width, height, 32, 0);
349
350           /* Use malloc, not g_malloc here, because X will call free()
351            * on this data
352            */
353           private->ximage->data = malloc (private->ximage->bytes_per_line *
354                                           private->ximage->height);
355           break;
356
357         case GDK_IMAGE_FASTEST:
358         case GDK_IMAGE_SHARED_PIXMAP:
359           g_assert_not_reached ();
360         }
361
362       if (image)
363         {
364           image->byte_order = private->ximage->byte_order;
365           image->mem = private->ximage->data;
366           image->bpl = private->ximage->bytes_per_line;
367           image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
368         }
369     }
370
371   return image;
372 }
373
374 GdkImage*
375 gdk_image_get (GdkWindow *window,
376                gint       x,
377                gint       y,
378                gint       width,
379                gint       height)
380 {
381   GdkImage *image;
382   GdkImagePrivateX11 *private;
383
384   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
385
386   if (GDK_WINDOW_DESTROYED (window))
387     return NULL;
388
389   image = GDK_IMAGE (g_type_create_instance (gdk_image_get_type ()));
390   private = PRIVATE_DATA (image);
391
392   private->xdisplay = gdk_display;
393   private->ximage = XGetImage (private->xdisplay,
394                                GDK_DRAWABLE_XID (window),
395                                x, y, width, height,
396                                AllPlanes, ZPixmap);
397
398   image->type = GDK_IMAGE_NORMAL;
399   image->visual = gdk_window_get_visual (window);
400   image->width = width;
401   image->height = height;
402   image->depth = private->ximage->depth;
403
404   image->mem = private->ximage->data;
405   image->bpl = private->ximage->bytes_per_line;
406   if (private->ximage->bits_per_pixel <= 8)
407     image->bpp = 1;
408   else if (private->ximage->bits_per_pixel <= 16)
409     image->bpp = 2;
410   else if (private->ximage->bits_per_pixel <= 24)
411     image->bpp = 3;
412   else
413     image->bpp = 4;
414   image->byte_order = private->ximage->byte_order;
415
416   return image;
417 }
418
419 guint32
420 gdk_image_get_pixel (GdkImage *image,
421                      gint x,
422                      gint y)
423 {
424   guint32 pixel;
425   GdkImagePrivateX11 *private;
426
427   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
428
429   private = PRIVATE_DATA (image);
430
431   pixel = XGetPixel (private->ximage, x, y);
432
433   return pixel;
434 }
435
436 void
437 gdk_image_put_pixel (GdkImage *image,
438                      gint x,
439                      gint y,
440                      guint32 pixel)
441 {
442   GdkImagePrivateX11 *private;
443
444   g_return_if_fail (GDK_IS_IMAGE (image));
445
446   private = PRIVATE_DATA (image);
447
448   pixel = XPutPixel (private->ximage, x, y, pixel);
449 }
450
451 static void
452 gdk_x11_image_destroy (GdkImage *image)
453 {
454   GdkImagePrivateX11 *private;
455 #ifdef USE_SHM
456   XShmSegmentInfo *x_shm_info;
457 #endif /* USE_SHM */
458
459   g_return_if_fail (GDK_IS_IMAGE (image));
460
461   private = PRIVATE_DATA (image);
462
463   if (private == NULL) /* This means that gdk_image_exit() destroyed the
464                         * image already, and now we're called a second
465                         * time from _finalize()
466                         */
467     return;
468   
469   switch (image->type)
470     {
471     case GDK_IMAGE_NORMAL:
472       XDestroyImage (private->ximage);
473       break;
474
475     case GDK_IMAGE_SHARED:
476 #ifdef USE_SHM
477       gdk_flush();
478
479       XShmDetach (private->xdisplay, private->x_shm_info);
480       XDestroyImage (private->ximage);
481
482       x_shm_info = private->x_shm_info;
483       shmdt (x_shm_info->shmaddr);
484       
485       g_free (private->x_shm_info);
486       private->x_shm_info = NULL;
487       
488       image_list = g_list_remove (image_list, image);
489 #else /* USE_SHM */
490       g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
491 #endif /* USE_SHM */
492       break;
493
494     case GDK_IMAGE_FASTEST:
495     case GDK_IMAGE_SHARED_PIXMAP:
496       g_assert_not_reached ();
497     }
498
499   g_free (private);
500   image->windowing_data = NULL;
501 }