]> Pileus Git - ~andy/gtk/blob - gdk/gdkimage.c
[ Merges from gtk-1-2 ]
[~andy/gtk] / gdk / gdkimage.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 "gdk.h"
57 #include "gdkprivate.h"
58
59
60 static void gdk_image_put_normal (GdkDrawable *drawable,
61                                   GdkGC       *gc,
62                                   GdkImage    *image,
63                                   gint         xsrc,
64                                   gint         ysrc,
65                                   gint         xdest,
66                                   gint         ydest,
67                                   gint         width,
68                                   gint         height);
69 static void gdk_image_put_shared (GdkDrawable *drawable,
70                                   GdkGC       *gc,
71                                   GdkImage    *image,
72                                   gint         xsrc,
73                                   gint         ysrc,
74                                   gint         xdest,
75                                   gint         ydest,
76                                   gint         width,
77                                   gint         height);
78
79
80 static GList *image_list = NULL;
81
82
83 void
84 gdk_image_exit (void)
85 {
86   GdkImage *image;
87
88   while (image_list)
89     {
90       image = image_list->data;
91       gdk_image_destroy (image);
92     }
93 }
94
95 GdkImage *
96 gdk_image_new_bitmap(GdkVisual *visual, gpointer data, gint w, gint h)
97 /*
98  * Desc: create a new bitmap image
99  */
100 {
101         Visual *xvisual;
102         GdkImage *image;
103         GdkImagePrivate *private;
104         private = g_new(GdkImagePrivate, 1);
105         image = (GdkImage *) private;
106         private->xdisplay = gdk_display;
107         private->image_put = gdk_image_put_normal;
108         image->type = GDK_IMAGE_NORMAL;
109         image->visual = visual;
110         image->width = w;
111         image->height = h;
112         image->depth = 1;
113         xvisual = ((GdkVisualPrivate*) visual)->xvisual;
114         private->ximage = XCreateImage(private->xdisplay, xvisual, 1, XYBitmap,
115                                        0, 0, w ,h, 8, 0);
116         private->ximage->data = data;
117         private->ximage->bitmap_bit_order = MSBFirst;
118         private->ximage->byte_order = MSBFirst;
119         image->byte_order = MSBFirst;
120         image->mem =  private->ximage->data;
121         image->bpl = private->ximage->bytes_per_line;
122         image->bpp = 1;
123         return(image);
124 } /* gdk_image_new_bitmap() */
125
126 static int
127 gdk_image_check_xshm(Display *display)
128 /* 
129  * Desc: query the server for support for the MIT_SHM extension
130  * Return:  0 = not available
131  *          1 = shared XImage support available
132  *          2 = shared Pixmap support available also
133  */
134 {
135 #ifdef USE_SHM
136   int major, minor, ignore;
137   Bool pixmaps;
138   
139   if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) 
140     {
141       if (XShmQueryVersion(display, &major, &minor, &pixmaps )==True) 
142         {
143           return (pixmaps==True) ? 2 : 1;
144         }
145     }
146 #endif /* USE_SHM */
147   return 0;
148 }
149
150 void
151 gdk_image_init (void)
152 {
153   if (gdk_use_xshm)
154     {
155       if (!gdk_image_check_xshm (gdk_display))
156         {
157           gdk_use_xshm = False;
158         }
159     }
160 }
161
162 GdkImage*
163 gdk_image_new (GdkImageType  type,
164                GdkVisual    *visual,
165                gint          width,
166                gint          height)
167 {
168   GdkImage *image;
169   GdkImagePrivate *private;
170 #ifdef USE_SHM
171   XShmSegmentInfo *x_shm_info;
172 #endif /* USE_SHM */
173   Visual *xvisual;
174
175   switch (type)
176     {
177     case GDK_IMAGE_FASTEST:
178       image = gdk_image_new (GDK_IMAGE_SHARED, visual, width, height);
179
180       if (!image)
181         image = gdk_image_new (GDK_IMAGE_NORMAL, visual, width, height);
182       break;
183
184     default:
185       private = g_new (GdkImagePrivate, 1);
186       image = (GdkImage*) private;
187
188       private->xdisplay = gdk_display;
189       private->image_put = NULL;
190
191       image->type = type;
192       image->visual = visual;
193       image->width = width;
194       image->height = height;
195       image->depth = visual->depth;
196
197       xvisual = ((GdkVisualPrivate*) visual)->xvisual;
198
199       switch (type)
200         {
201         case GDK_IMAGE_SHARED:
202 #ifdef USE_SHM
203           if (gdk_use_xshm)
204             {
205               private->image_put = gdk_image_put_shared;
206
207               private->x_shm_info = g_new (XShmSegmentInfo, 1);
208               x_shm_info = private->x_shm_info;
209
210               private->ximage = XShmCreateImage (private->xdisplay, xvisual, visual->depth,
211                                                  ZPixmap, NULL, x_shm_info, width, height);
212               if (private->ximage == NULL)
213                 {
214                   g_warning ("XShmCreateImage failed");
215                   
216                   g_free (image);
217                   gdk_use_xshm = False;
218                   return NULL;
219                 }
220
221               x_shm_info->shmid = shmget (IPC_PRIVATE,
222                                           private->ximage->bytes_per_line * private->ximage->height,
223                                           IPC_CREAT | 0777);
224
225               if (x_shm_info->shmid == -1)
226                 {
227                   g_warning ("shmget failed!");
228
229                   XDestroyImage (private->ximage);
230                   g_free (private->x_shm_info);
231                   g_free (image);
232
233                   gdk_use_xshm = False;
234                   return NULL;
235                 }
236
237               x_shm_info->readOnly = False;
238               x_shm_info->shmaddr = shmat (x_shm_info->shmid, 0, 0);
239               private->ximage->data = x_shm_info->shmaddr;
240
241               if (x_shm_info->shmaddr == (char*) -1)
242                 {
243                   g_warning ("shmat failed!");
244
245                   XDestroyImage (private->ximage);
246                   shmctl (x_shm_info->shmid, IPC_RMID, 0);
247                   
248                   g_free (private->x_shm_info);
249                   g_free (image);
250
251                   return NULL;
252                 }
253
254               gdk_error_trap_push ();
255
256               XShmAttach (private->xdisplay, x_shm_info);
257               XSync (private->xdisplay, False);
258
259               if (gdk_error_trap_pop ())
260                 {
261                   /* this is the common failure case so omit warning */
262                   XDestroyImage (private->ximage);
263                   shmdt (x_shm_info->shmaddr);
264                   shmctl (x_shm_info->shmid, IPC_RMID, 0);
265                   
266                   g_free (private->x_shm_info);
267                   g_free (image);
268
269                   gdk_use_xshm = False;
270
271                   return NULL;
272                 }
273               
274               /* We mark the segment as destroyed so that when
275                * the last process detaches, it will be deleted.
276                * There is a small possibility of leaking if
277                * we die in XShmAttach. In theory, a signal handler
278                * could be set up.
279                */
280               shmctl (x_shm_info->shmid, IPC_RMID, 0);                
281
282               if (image)
283                 image_list = g_list_prepend (image_list, image);
284             }
285           else
286             {
287               g_free (image);
288               return NULL;
289             }
290           break;
291 #else /* USE_SHM */
292           g_free (image);
293           return NULL;
294 #endif /* USE_SHM */
295         case GDK_IMAGE_NORMAL:
296           private->image_put = gdk_image_put_normal;
297
298           private->ximage = XCreateImage (private->xdisplay, xvisual, visual->depth,
299                                           ZPixmap, 0, 0, width, height, 32, 0);
300
301           /* Use malloc, not g_malloc here, because X will call free()
302            * on this data
303            */
304           private->ximage->data = malloc (private->ximage->bytes_per_line *
305                                           private->ximage->height);
306           break;
307
308         case GDK_IMAGE_FASTEST:
309           g_assert_not_reached ();
310         }
311
312       if (image)
313         {
314           image->byte_order = private->ximage->byte_order;
315           image->mem = private->ximage->data;
316           image->bpl = private->ximage->bytes_per_line;
317           image->bpp = (private->ximage->bits_per_pixel + 7) / 8;
318         }
319     }
320
321   return image;
322 }
323
324 GdkImage*
325 gdk_image_get (GdkWindow *window,
326                gint       x,
327                gint       y,
328                gint       width,
329                gint       height)
330 {
331   GdkImage *image;
332   GdkImagePrivate *private;
333   GdkWindowPrivate *win_private;
334
335   g_return_val_if_fail (window != NULL, NULL);
336
337   win_private = (GdkWindowPrivate *) window;
338   if (win_private->destroyed)
339     return NULL;
340
341   private = g_new (GdkImagePrivate, 1);
342   image = (GdkImage*) private;
343
344   private->xdisplay = gdk_display;
345   private->image_put = gdk_image_put_normal;
346   private->ximage = XGetImage (private->xdisplay,
347                                win_private->xwindow,
348                                x, y, width, height,
349                                AllPlanes, ZPixmap);
350
351   image->type = GDK_IMAGE_NORMAL;
352   image->visual = gdk_window_get_visual (window);
353   image->width = width;
354   image->height = height;
355   image->depth = private->ximage->depth;
356
357   image->mem = private->ximage->data;
358   image->bpl = private->ximage->bytes_per_line;
359   if (private->ximage->bits_per_pixel <= 8)
360     image->bpp = 1;
361   else if (private->ximage->bits_per_pixel <= 16)
362     image->bpp = 2;
363   else if (private->ximage->bits_per_pixel <= 24)
364     image->bpp = 3;
365   else
366     image->bpp = 4;
367   image->byte_order = private->ximage->byte_order;
368
369   return image;
370 }
371
372 guint32
373 gdk_image_get_pixel (GdkImage *image,
374                      gint x,
375                      gint y)
376 {
377   guint32 pixel;
378   GdkImagePrivate *private;
379
380   g_return_val_if_fail (image != NULL, 0);
381
382   private = (GdkImagePrivate *) image;
383
384   pixel = XGetPixel (private->ximage, x, y);
385
386   return pixel;
387 }
388
389 void
390 gdk_image_put_pixel (GdkImage *image,
391                      gint x,
392                      gint y,
393                      guint32 pixel)
394 {
395   GdkImagePrivate *private;
396
397   g_return_if_fail (image != NULL);
398
399   private = (GdkImagePrivate *) image;
400
401   pixel = XPutPixel (private->ximage, x, y, pixel);
402 }
403
404 void
405 gdk_image_destroy (GdkImage *image)
406 {
407   GdkImagePrivate *private;
408 #ifdef USE_SHM
409   XShmSegmentInfo *x_shm_info;
410 #endif /* USE_SHM */
411
412   g_return_if_fail (image != NULL);
413
414   private = (GdkImagePrivate*) image;
415   switch (image->type)
416     {
417     case GDK_IMAGE_NORMAL:
418       XDestroyImage (private->ximage);
419       break;
420
421     case GDK_IMAGE_SHARED:
422 #ifdef USE_SHM
423       gdk_flush();
424
425       XShmDetach (private->xdisplay, private->x_shm_info);
426       XDestroyImage (private->ximage);
427
428       x_shm_info = private->x_shm_info;
429       shmdt (x_shm_info->shmaddr);
430       
431       g_free (private->x_shm_info);
432
433       image_list = g_list_remove (image_list, image);
434 #else /* USE_SHM */
435       g_error ("trying to destroy shared memory image when gdk was compiled without shared memory support");
436 #endif /* USE_SHM */
437       break;
438
439     case GDK_IMAGE_FASTEST:
440       g_assert_not_reached ();
441     }
442
443   g_free (image);
444 }
445
446 static void
447 gdk_image_put_normal (GdkDrawable *drawable,
448                       GdkGC       *gc,
449                       GdkImage    *image,
450                       gint         xsrc,
451                       gint         ysrc,
452                       gint         xdest,
453                       gint         ydest,
454                       gint         width,
455                       gint         height)
456 {
457   GdkWindowPrivate *drawable_private;
458   GdkImagePrivate *image_private;
459   GdkGCPrivate *gc_private;
460
461   g_return_if_fail (drawable != NULL);
462   g_return_if_fail (image != NULL);
463   g_return_if_fail (gc != NULL);
464
465   drawable_private = (GdkWindowPrivate*) drawable;
466   if (drawable_private->destroyed)
467     return;
468   image_private = (GdkImagePrivate*) image;
469   gc_private = (GdkGCPrivate*) gc;
470
471   g_return_if_fail (image->type == GDK_IMAGE_NORMAL);
472
473   XPutImage (drawable_private->xdisplay, drawable_private->xwindow,
474              gc_private->xgc, image_private->ximage,
475              xsrc, ysrc, xdest, ydest, width, height);
476 }
477
478 static void
479 gdk_image_put_shared (GdkDrawable *drawable,
480                       GdkGC       *gc,
481                       GdkImage    *image,
482                       gint         xsrc,
483                       gint         ysrc,
484                       gint         xdest,
485                       gint         ydest,
486                       gint         width,
487                       gint         height)
488 {
489 #ifdef USE_SHM
490   GdkWindowPrivate *drawable_private;
491   GdkImagePrivate *image_private;
492   GdkGCPrivate *gc_private;
493
494   g_return_if_fail (drawable != NULL);
495   g_return_if_fail (image != NULL);
496   g_return_if_fail (gc != NULL);
497
498   drawable_private = (GdkWindowPrivate*) drawable;
499   if (drawable_private->destroyed)
500     return;
501   image_private = (GdkImagePrivate*) image;
502   gc_private = (GdkGCPrivate*) gc;
503
504   g_return_if_fail (image->type == GDK_IMAGE_SHARED);
505
506   XShmPutImage (drawable_private->xdisplay, drawable_private->xwindow,
507                 gc_private->xgc, image_private->ximage,
508                 xsrc, ysrc, xdest, ydest, width, height, False);
509 #else /* USE_SHM */
510   g_error ("trying to draw shared memory image when gdk was compiled without shared memory support");
511 #endif /* USE_SHM */
512 }