]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdrawable-x11.c
8fba6eb23cb37d314a6ce8b6b4e8ac1b613897f5
[~andy/gtk] / gdk / x11 / gdkdrawable-x11.c
1 /* 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 "gdkx.h"
30
31 #include <cairo-xlib.h>
32
33 #include <stdlib.h>
34 #include <string.h>             /* for memcpy() */
35
36 #if defined (HAVE_IPC_H) && defined (HAVE_SHM_H) && defined (HAVE_XSHM_H)
37 #define USE_SHM
38 #endif
39
40 #ifdef USE_SHM
41 #include <X11/extensions/XShm.h>
42 #endif /* USE_SHM */
43
44 #include "gdkprivate-x11.h"
45 #include "gdkdrawable-x11.h"
46 #include "gdkpixmap-x11.h"
47 #include "gdkscreen-x11.h"
48 #include "gdkdisplay-x11.h"
49
50
51 static cairo_surface_t *gdk_x11_ref_cairo_surface (GdkDrawable *drawable);
52      
53 static void gdk_x11_set_colormap   (GdkDrawable    *drawable,
54                                     GdkColormap    *colormap);
55
56 static GdkColormap* gdk_x11_get_colormap   (GdkDrawable    *drawable);
57 static gint         gdk_x11_get_depth      (GdkDrawable    *drawable);
58 static GdkScreen *  gdk_x11_get_screen     (GdkDrawable    *drawable);
59 static GdkVisual*   gdk_x11_get_visual     (GdkDrawable    *drawable);
60
61 static void gdk_drawable_impl_x11_finalize   (GObject *object);
62
63 static const cairo_user_data_key_t gdk_x11_cairo_key;
64
65 G_DEFINE_TYPE (GdkDrawableImplX11, _gdk_drawable_impl_x11, GDK_TYPE_DRAWABLE)
66
67 static void
68 _gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass)
69 {
70   GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
71   GObjectClass *object_class = G_OBJECT_CLASS (klass);
72   
73   object_class->finalize = gdk_drawable_impl_x11_finalize;
74   
75   drawable_class->ref_cairo_surface = gdk_x11_ref_cairo_surface;
76
77   drawable_class->set_colormap = gdk_x11_set_colormap;
78   drawable_class->get_colormap = gdk_x11_get_colormap;
79
80   drawable_class->get_depth = gdk_x11_get_depth;
81   drawable_class->get_screen = gdk_x11_get_screen;
82   drawable_class->get_visual = gdk_x11_get_visual;
83 }
84
85 static void
86 _gdk_drawable_impl_x11_init (GdkDrawableImplX11 *impl)
87 {
88 }
89
90 static void
91 gdk_drawable_impl_x11_finalize (GObject *object)
92 {
93   gdk_drawable_set_colormap (GDK_DRAWABLE (object), NULL);
94
95   G_OBJECT_CLASS (_gdk_drawable_impl_x11_parent_class)->finalize (object);
96 }
97
98 /**
99  * _gdk_x11_drawable_finish:
100  * @drawable: a #GdkDrawableImplX11.
101  * 
102  * Performs necessary cleanup prior to freeing a pixmap or
103  * destroying a window.
104  **/
105 void
106 _gdk_x11_drawable_finish (GdkDrawable *drawable)
107 {
108   GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
109   
110   if (impl->picture)
111     {
112       XRenderFreePicture (GDK_SCREEN_XDISPLAY (impl->screen),
113                           impl->picture);
114       impl->picture = None;
115     }
116   
117   if (impl->cairo_surface)
118     {
119       cairo_surface_finish (impl->cairo_surface);
120       cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
121                                    NULL, NULL);
122     }
123 }
124
125 /**
126  * _gdk_x11_drawable_update_size:
127  * @drawable: a #GdkDrawableImplX11.
128  * 
129  * Updates the state of the drawable (in particular the drawable's
130  * cairo surface) when its size has changed.
131  **/
132 void
133 _gdk_x11_drawable_update_size (GdkDrawable *drawable)
134 {
135   GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
136   
137   if (impl->cairo_surface)
138     {
139       int width, height;
140       
141       gdk_drawable_get_size (drawable, &width, &height);
142       cairo_xlib_surface_set_size (impl->cairo_surface, width, height);
143     }
144 }
145
146 static void
147 try_pixmap (Display *xdisplay,
148             int      screen,
149             int      depth)
150 {
151   Pixmap pixmap = XCreatePixmap (xdisplay,
152                                  RootWindow (xdisplay, screen),
153                                  1, 1, depth);
154   XFreePixmap (xdisplay, pixmap);
155 }
156
157 gboolean
158 _gdk_x11_have_render (GdkDisplay *display)
159 {
160   Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
161   GdkDisplayX11 *x11display = GDK_DISPLAY_X11 (display);
162
163   if (x11display->have_render == GDK_UNKNOWN)
164     {
165       int event_base, error_base;
166       x11display->have_render =
167         XRenderQueryExtension (xdisplay, &event_base, &error_base)
168         ? GDK_YES : GDK_NO;
169
170       if (x11display->have_render == GDK_YES)
171         {
172           /*
173            * Sun advertises RENDER, but fails to support 32-bit pixmaps.
174            * That is just no good.  Therefore, we check all screens
175            * for proper support.
176            */
177
178           int screen;
179           for (screen = 0; screen < ScreenCount (xdisplay); screen++)
180             {
181               int count;
182               int *depths = XListDepths (xdisplay, screen, &count);
183               gboolean has_8 = FALSE, has_32 = FALSE;
184
185               if (depths)
186                 {
187                   int i;
188
189                   for (i = 0; i < count; i++)
190                     {
191                       if (depths[i] == 8)
192                         has_8 = TRUE;
193                       else if (depths[i] == 32)
194                         has_32 = TRUE;
195                     }
196                   XFree (depths);
197                 }
198
199               /* At this point, we might have a false positive;
200                * buggy versions of Xinerama only report depths for
201                * which there is an associated visual; so we actually
202                * go ahead and try create pixmaps.
203                */
204               if (!(has_8 && has_32))
205                 {
206                   gdk_error_trap_push ();
207                   if (!has_8)
208                     try_pixmap (xdisplay, screen, 8);
209                   if (!has_32)
210                     try_pixmap (xdisplay, screen, 32);
211                   XSync (xdisplay, False);
212                   if (gdk_error_trap_pop () == 0)
213                     {
214                       has_8 = TRUE;
215                       has_32 = TRUE;
216                     }
217                 }
218               
219               if (!(has_8 && has_32))
220                 {
221                   g_warning ("The X server advertises that RENDER support is present,\n"
222                              "but fails to supply the necessary pixmap support.  In\n"
223                              "other words, it is buggy.");
224                   x11display->have_render = GDK_NO;
225                   break;
226                 }
227             }
228         }
229     }
230
231   return x11display->have_render == GDK_YES;
232 }
233
234 /*****************************************************
235  * X11 specific implementations of generic functions *
236  *****************************************************/
237
238 static GdkColormap*
239 gdk_x11_get_colormap (GdkDrawable *drawable)
240 {
241   GdkDrawableImplX11 *impl;
242
243   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
244
245   return impl->colormap;
246 }
247
248 static void
249 gdk_x11_set_colormap (GdkDrawable *drawable,
250                       GdkColormap *colormap)
251 {
252   GdkDrawableImplX11 *impl;
253
254   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
255
256   if (impl->colormap == colormap)
257     return;
258   
259   if (impl->colormap)
260     g_object_unref (impl->colormap);
261   impl->colormap = colormap;
262   if (impl->colormap)
263     g_object_ref (impl->colormap);
264 }
265
266 static gint
267 gdk_x11_get_depth (GdkDrawable *drawable)
268 {
269   /* This is a bit bogus but I'm not sure the other way is better */
270
271   return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper);
272 }
273
274 static GdkDrawable *
275 get_impl_drawable (GdkDrawable *drawable)
276 {
277   if (GDK_IS_WINDOW (drawable))
278     return ((GdkWindowObject *)drawable)->impl;
279   else if (GDK_IS_PIXMAP (drawable))
280     return ((GdkPixmapObject *)drawable)->impl;
281   else
282     {
283       g_warning (G_STRLOC " drawable is not a pixmap or window");
284       return NULL;
285     }
286 }
287
288 static GdkScreen*
289 gdk_x11_get_screen (GdkDrawable *drawable)
290 {
291   if (GDK_IS_DRAWABLE_IMPL_X11 (drawable))
292     return GDK_DRAWABLE_IMPL_X11 (drawable)->screen;
293   else
294     return GDK_DRAWABLE_IMPL_X11 (get_impl_drawable (drawable))->screen;
295 }
296
297 static GdkVisual*
298 gdk_x11_get_visual (GdkDrawable    *drawable)
299 {
300   return gdk_drawable_get_visual (GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper);
301 }
302
303 /**
304  * gdk_x11_drawable_get_xdisplay:
305  * @drawable: a #GdkDrawable.
306  * 
307  * Returns the display of a #GdkDrawable.
308  * 
309  * Return value: an Xlib <type>Display*</type>.
310  **/
311 Display *
312 gdk_x11_drawable_get_xdisplay (GdkDrawable *drawable)
313 {
314   if (GDK_IS_DRAWABLE_IMPL_X11 (drawable))
315     return GDK_SCREEN_XDISPLAY (GDK_DRAWABLE_IMPL_X11 (drawable)->screen);
316   else
317     return GDK_SCREEN_XDISPLAY (GDK_DRAWABLE_IMPL_X11 (get_impl_drawable (drawable))->screen);
318 }
319
320 /**
321  * gdk_x11_drawable_get_xid:
322  * @drawable: a #GdkDrawable.
323  * 
324  * Returns the X resource (window or pixmap) belonging to a #GdkDrawable.
325  * 
326  * Return value: the ID of @drawable's X resource.
327  **/
328 XID
329 gdk_x11_drawable_get_xid (GdkDrawable *drawable)
330 {
331   GdkDrawable *impl;
332   
333   if (GDK_IS_WINDOW (drawable))
334     {
335       GdkWindow *window = (GdkWindow *)drawable;
336       
337       /* Try to ensure the window has a native window */
338       if (!_gdk_window_has_impl (window))
339         {
340           gdk_window_ensure_native (window);
341
342           /* We sync here to ensure the window is created in the Xserver when
343            * this function returns. This is required because the returned XID
344            * for this window must be valid immediately, even with another
345            * connection to the Xserver */
346           gdk_display_sync (gdk_drawable_get_display (window));
347         }
348       
349       if (!GDK_WINDOW_IS_X11 (window))
350         {
351           g_warning (G_STRLOC " drawable is not a native X11 window");
352           return None;
353         }
354       
355       impl = ((GdkWindowObject *)drawable)->impl;
356     }
357   else if (GDK_IS_PIXMAP (drawable))
358     impl = ((GdkPixmapObject *)drawable)->impl;
359   else
360     {
361       g_warning (G_STRLOC " drawable is not a pixmap or window");
362       return None;
363     }
364
365   return ((GdkDrawableImplX11 *)impl)->xid;
366 }
367
368 GdkDrawable *
369 gdk_x11_window_get_drawable_impl (GdkWindow *window)
370 {
371   return ((GdkWindowObject *)window)->impl;
372 }
373 GdkDrawable *
374 gdk_x11_pixmap_get_drawable_impl (GdkPixmap *pixmap)
375 {
376   return ((GdkPixmapObject *)pixmap)->impl;
377 }
378
379 #if 0
380 static void
381 list_formats (XRenderPictFormat *pf)
382 {
383   gint i;
384   
385   for (i=0 ;; i++)
386     {
387       XRenderPictFormat *pf = XRenderFindFormat (impl->xdisplay, 0, NULL, i);
388       if (pf)
389         {
390           g_print ("%2d R-%#06x/%#06x G-%#06x/%#06x B-%#06x/%#06x A-%#06x/%#06x\n",
391                    pf->depth,
392                    pf->direct.red,
393                    pf->direct.redMask,
394                    pf->direct.green,
395                    pf->direct.greenMask,
396                    pf->direct.blue,
397                    pf->direct.blueMask,
398                    pf->direct.alpha,
399                    pf->direct.alphaMask);
400         }
401       else
402         break;
403     }
404 }
405 #endif  
406
407 void
408 _gdk_x11_convert_to_format (guchar           *src_buf,
409                             gint              src_rowstride,
410                             guchar           *dest_buf,
411                             gint              dest_rowstride,
412                             GdkX11FormatType  dest_format,
413                             GdkByteOrder      dest_byteorder,
414                             gint              width,
415                             gint              height)
416 {
417   gint i;
418
419   for (i=0; i < height; i++)
420     {
421       switch (dest_format)
422         {
423         case GDK_X11_FORMAT_EXACT_MASK:
424           {
425             memcpy (dest_buf + i * dest_rowstride,
426                     src_buf + i * src_rowstride,
427                     width * 4);
428             break;
429           }
430         case GDK_X11_FORMAT_ARGB_MASK:
431           {
432             guchar *row = src_buf + i * src_rowstride;
433             if (((gsize)row & 3) != 0)
434               {
435                 guchar *p = row;
436                 guint32 *q = (guint32 *)(dest_buf + i * dest_rowstride);
437                 guchar *end = p + 4 * width;
438
439                 while (p < end)
440                   {
441                     *q = (p[3] << 24) | (p[0] << 16) | (p[1] << 8) | p[2];
442                     p += 4;
443                     q++;
444                   }
445               }
446             else
447               {
448                 guint32 *p = (guint32 *)row;
449                 guint32 *q = (guint32 *)(dest_buf + i * dest_rowstride);
450                 guint32 *end = p + width;
451
452 #if G_BYTE_ORDER == G_LITTLE_ENDIAN         
453                 if (dest_byteorder == GDK_LSB_FIRST)
454                   {
455                     /* ABGR => ARGB */
456                 
457                     while (p < end)
458                       {
459                         *q = ( (*p & 0xff00ff00) |
460                                ((*p & 0x000000ff) << 16) |
461                                ((*p & 0x00ff0000) >> 16));
462                         q++;
463                         p++;
464                       }
465                   }
466                 else
467                   {
468                     /* ABGR => BGRA */
469                 
470                     while (p < end)
471                       {
472                         *q = (((*p & 0xff000000) >> 24) |
473                               ((*p & 0x00ffffff) << 8));
474                         q++;
475                         p++;
476                       }
477                   }
478 #else /* G_BYTE_ORDER == G_BIG_ENDIAN */
479                 if (dest_byteorder == GDK_LSB_FIRST)
480                   {
481                     /* RGBA => BGRA */
482                 
483                     while (p < end)
484                       {
485                         *q = ( (*p & 0x00ff00ff) |
486                                ((*p & 0x0000ff00) << 16) |
487                                ((*p & 0xff000000) >> 16));
488                         q++;
489                         p++;
490                       }
491                   }
492                 else
493                   {
494                     /* RGBA => ARGB */
495                 
496                     while (p < end)
497                       {
498                         *q = (((*p & 0xffffff00) >> 8) |
499                               ((*p & 0x000000ff) << 24));
500                         q++;
501                         p++;
502                       }
503                   }
504 #endif /* G_BYTE_ORDER*/            
505               }
506             break;
507           }
508         case GDK_X11_FORMAT_ARGB:
509           {
510             guchar *p = (src_buf + i * src_rowstride);
511             guchar *q = (dest_buf + i * dest_rowstride);
512             guchar *end = p + 4 * width;
513             guint t1,t2,t3;
514             
515 #define MULT(d,c,a,t) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END
516             
517             if (dest_byteorder == GDK_LSB_FIRST)
518               {
519                 while (p < end)
520                   {
521                     MULT(q[0], p[2], p[3], t1);
522                     MULT(q[1], p[1], p[3], t2);
523                     MULT(q[2], p[0], p[3], t3);
524                     q[3] = p[3];
525                     p += 4;
526                     q += 4;
527                   }
528               }
529             else
530               {
531                 while (p < end)
532                   {
533                     q[0] = p[3];
534                     MULT(q[1], p[0], p[3], t1);
535                     MULT(q[2], p[1], p[3], t2);
536                     MULT(q[3], p[2], p[3], t3);
537                     p += 4;
538                     q += 4;
539                   }
540               }
541 #undef MULT
542             break;
543           }
544         case GDK_X11_FORMAT_NONE:
545           g_assert_not_reached ();
546           break;
547         }
548     }
549 }
550
551 static void
552 gdk_x11_cairo_surface_destroy (void *data)
553 {
554   GdkDrawableImplX11 *impl = data;
555
556   impl->cairo_surface = NULL;
557 }
558
559 void
560 _gdk_windowing_set_cairo_surface_size (cairo_surface_t *surface,
561                                        int width,
562                                        int height)
563 {
564   cairo_xlib_surface_set_size (surface, width, height);
565 }
566
567 cairo_surface_t *
568 _gdk_windowing_create_cairo_surface (GdkDrawable *drawable,
569                                      int width,
570                                      int height)
571 {
572   GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
573   GdkVisual *visual;
574     
575   visual = gdk_drawable_get_visual (drawable);
576   if (visual) 
577     return cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (impl->screen),
578                                       impl->xid,
579                                       GDK_VISUAL_XVISUAL (visual),
580                                       width, height);
581   else if (gdk_drawable_get_depth (drawable) == 1)
582     return cairo_xlib_surface_create_for_bitmap (GDK_SCREEN_XDISPLAY (impl->screen),
583                                                     impl->xid,
584                                                     GDK_SCREEN_XSCREEN (impl->screen),
585                                                     width, height);
586   else
587     {
588       g_warning ("Using Cairo rendering requires the drawable argument to\n"
589                  "have a specified colormap. All windows have a colormap,\n"
590                  "however, pixmaps only have colormap by default if they\n"
591                  "were created with a non-NULL window argument. Otherwise\n"
592                  "a colormap must be set on them with gdk_drawable_set_colormap");
593       return NULL;
594     }
595   
596 }
597
598 static cairo_surface_t *
599 gdk_x11_ref_cairo_surface (GdkDrawable *drawable)
600 {
601   GdkDrawableImplX11 *impl = GDK_DRAWABLE_IMPL_X11 (drawable);
602
603   if (GDK_IS_WINDOW_IMPL_X11 (drawable) &&
604       GDK_WINDOW_DESTROYED (impl->wrapper))
605     return NULL;
606
607   if (!impl->cairo_surface)
608     {
609       int width, height;
610   
611       gdk_drawable_get_size (impl->wrapper, &width, &height);
612
613       impl->cairo_surface = _gdk_windowing_create_cairo_surface (drawable, width, height);
614       
615       if (impl->cairo_surface)
616         cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
617                                      drawable, gdk_x11_cairo_surface_destroy);
618     }
619   else
620     cairo_surface_reference (impl->cairo_surface);
621
622   return impl->cairo_surface;
623 }