]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/gdkpixmap-x11.c
Merges from gtk-1-2
[~andy/gtk] / gdk / x11 / gdkpixmap-x11.c
index ab131e6acab9944193ce0c6dd93ce3739714d204..45807cb6136ceb6051577fc62afb7c92448634fd 100644 (file)
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
-#include "../config.h"
+
+/*
+ * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include "config.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -23,8 +32,9 @@
 #include <unistd.h>
 #include <X11/Xlib.h>
 
-#include "gdk.h"
+#include "gdkpixmap.h"
 #include "gdkprivate.h"
+#include "gdkx.h"
 
 typedef struct
 {
@@ -33,6 +43,51 @@ typedef struct
   gint transparent;
 } _GdkPixmapColor;
 
+typedef struct
+{
+  guint ncolors;
+  GdkColormap *colormap;
+  gulong pixels[1];
+} _GdkPixmapInfo;
+
+GdkDrawableClass _gdk_x11_pixmap_class;
+
+static void
+gdk_x11_pixmap_destroy (GdkPixmap *pixmap)
+{
+  XFreePixmap (GDK_DRAWABLE_XDISPLAY (pixmap), GDK_DRAWABLE_XID (pixmap));
+  gdk_xid_table_remove (GDK_DRAWABLE_XID (pixmap));
+
+  g_free (GDK_DRAWABLE_XDATA (pixmap));
+}
+
+static GdkDrawable *
+gdk_x11_pixmap_alloc (void)
+{
+  GdkDrawable *drawable;
+  GdkDrawablePrivate *private;
+  
+  static GdkDrawableClass klass;
+  static gboolean initialized = FALSE;
+
+  if (!initialized)
+    {
+      initialized = TRUE;
+      
+      klass = _gdk_x11_drawable_class;
+      klass.destroy = gdk_x11_pixmap_destroy;
+    }
+
+  drawable = gdk_drawable_alloc ();
+  private = (GdkDrawablePrivate *)drawable;
+
+  private->klass = &klass;
+  private->klass_data = g_new (GdkDrawableXData, 1);
+  private->window_type = GDK_DRAWABLE_PIXMAP;
+
+  return drawable;
+}
+
 GdkPixmap*
 gdk_pixmap_new (GdkWindow *window,
                gint       width,
@@ -40,234 +95,230 @@ gdk_pixmap_new (GdkWindow *window,
                gint       depth)
 {
   GdkPixmap *pixmap;
-  GdkWindowPrivate *private;
-  GdkWindowPrivate *window_private;
+  GdkDrawablePrivate *private;
 
+  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
-
+  g_return_val_if_fail ((width != 0) && (height != 0), NULL);
+  
   if (!window)
-    window = (GdkWindow*) &gdk_root_parent;
+    window = gdk_parent_root;
 
-  window_private = (GdkWindowPrivate*) window;
-  if (window_private->destroyed)
+  if (GDK_DRAWABLE_DESTROYED (window))
     return NULL;
 
   if (depth == -1)
-    gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
-
-  private = g_new (GdkWindowPrivate, 1);
-  pixmap = (GdkPixmap*) private;
-
-  private->xdisplay = window_private->xdisplay;
-  private->window_type = GDK_WINDOW_PIXMAP;
-  private->xwindow = XCreatePixmap (private->xdisplay, window_private->xwindow,
-                                   width, height, depth);
-  private->parent = NULL;
-  private->x = 0;
-  private->y = 0;
+    depth = gdk_drawable_get_visual (window)->depth;
+
+  pixmap = gdk_x11_pixmap_alloc ();
+  private = (GdkDrawablePrivate *)pixmap;
+
+  GDK_DRAWABLE_XDATA (private)->xdisplay = GDK_DRAWABLE_XDISPLAY (window);
+  GDK_DRAWABLE_XDATA (private)->xid = XCreatePixmap (GDK_DRAWABLE_XDISPLAY (pixmap),
+                                                    GDK_DRAWABLE_XID (window),
+                                                    width, height, depth);
   private->width = width;
   private->height = height;
-  private->resize_count = 0;
-  private->ref_count = 1;
-  private->destroyed = 0;
 
-  gdk_xid_table_insert (&private->xwindow, pixmap);
+  gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
 
   return pixmap;
 }
 
 GdkPixmap *
-gdk_bitmap_create_from_data (GdkWindow *window,
-                            gchar     *data,
-                            gint       width,
-                            gint       height)
+gdk_bitmap_create_from_data (GdkWindow   *window,
+                            const gchar *data,
+                            gint         width,
+                            gint         height)
 {
   GdkPixmap *pixmap;
-  GdkWindowPrivate *private;
-  GdkWindowPrivate *window_private;
+  GdkDrawablePrivate *private;
 
   g_return_val_if_fail (data != NULL, NULL);
+  g_return_val_if_fail ((width != 0) && (height != 0), NULL);
+  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
 
   if (!window)
-    window = (GdkWindow*) &gdk_root_parent;
+    window = gdk_parent_root;
 
-  window_private = (GdkWindowPrivate*) window;
-  if (window_private->destroyed)
+  if (GDK_DRAWABLE_DESTROYED (window))
     return NULL;
 
-  private = g_new (GdkWindowPrivate, 1);
-  pixmap = (GdkPixmap*) private;
+  pixmap = gdk_x11_pixmap_alloc ();
+  private = (GdkDrawablePrivate *)pixmap;
 
-  private->parent = NULL;
-  private->xdisplay = window_private->xdisplay;
-  private->window_type = GDK_WINDOW_PIXMAP;
-  private->x = 0;
-  private->y = 0;
   private->width = width;
   private->height = height;
-  private->resize_count = 0;
-  private->ref_count = 1;
-  private->destroyed = FALSE;
 
-  private->xwindow = XCreateBitmapFromData (private->xdisplay,
-                                           window_private->xwindow,
-                                           data, width, height);
+  GDK_DRAWABLE_XDATA (private)->xdisplay = GDK_DRAWABLE_XDISPLAY (window);
+  GDK_DRAWABLE_XDATA (private)->xid = XCreateBitmapFromData (GDK_DRAWABLE_XDISPLAY (window),
+                                                            GDK_DRAWABLE_XID (window),
+                                                            (char *)data, width, height);
 
-  gdk_xid_table_insert (&private->xwindow, pixmap);
+  gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
 
   return pixmap;
 }
 
 GdkPixmap*
-gdk_pixmap_create_from_data (GdkWindow *window,
-                            gchar     *data,
-                            gint       width,
-                            gint       height,
-                            gint       depth,
-                            GdkColor  *fg,
-                            GdkColor  *bg)
+gdk_pixmap_create_from_data (GdkWindow   *window,
+                            const gchar *data,
+                            gint         width,
+                            gint         height,
+                            gint         depth,
+                            GdkColor    *fg,
+                            GdkColor    *bg)
 {
   GdkPixmap *pixmap;
-  GdkWindowPrivate *private;
-  GdkWindowPrivate *window_private;
+  GdkDrawablePrivate *private;
 
+  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
   g_return_val_if_fail (data != NULL, NULL);
   g_return_val_if_fail (fg != NULL, NULL);
   g_return_val_if_fail (bg != NULL, NULL);
   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
+  g_return_val_if_fail ((width != 0) && (height != 0), NULL);
 
   if (!window)
-    window = (GdkWindow*) &gdk_root_parent;
+    window = gdk_parent_root;
 
-  window_private = (GdkWindowPrivate*) window;
-  if (window_private->destroyed)
+  if (GDK_DRAWABLE_DESTROYED (window))
     return NULL;
 
   if (depth == -1)
-    gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
+    depth = gdk_drawable_get_visual (window)->depth;
 
-  private = g_new (GdkWindowPrivate, 1);
-  pixmap = (GdkPixmap*) private;
+  pixmap = gdk_x11_pixmap_alloc ();
+  private = (GdkDrawablePrivate *)pixmap;
 
-  private->parent = NULL;
-  private->xdisplay = window_private->xdisplay;
-  private->window_type = GDK_WINDOW_PIXMAP;
-  private->x = 0;
-  private->y = 0;
   private->width = width;
   private->height = height;
-  private->resize_count = 0;
-  private->ref_count = 1;
-  private->destroyed = FALSE;
 
-  private->xwindow = XCreatePixmapFromBitmapData (private->xdisplay,
-                                                 window_private->xwindow,
-                                                 data, width, height,
-                                                 fg->pixel, bg->pixel, depth);
+  GDK_DRAWABLE_XDATA (private)->xdisplay = GDK_DRAWABLE_XDISPLAY (window);
+  GDK_DRAWABLE_XDATA (private)->xid = XCreatePixmapFromBitmapData (GDK_DRAWABLE_XDISPLAY (window),
+                                                                  GDK_DRAWABLE_XID (window),
+                                                                  (char *)data, width, height,
+                                                                  fg->pixel, bg->pixel, depth);
 
-  gdk_xid_table_insert (&private->xwindow, pixmap);
+  gdk_xid_table_insert (&GDK_DRAWABLE_XID (pixmap), pixmap);
 
   return pixmap;
 }
 
-gint
+static gint
 gdk_pixmap_seek_string (FILE  *infile,
                         const gchar *str,
                         gint   skip_comments)
 {
   char instr[1024];
 
-  while (!feof (infile))
+  while (1)
     {
-      fscanf (infile, "%s", instr);
+      if (fscanf (infile, "%1023s", instr) != 1)
+       return FALSE;
+         
       if (skip_comments == TRUE && strcmp (instr, "/*") == 0)
         {
-          fscanf (infile, "%s", instr);
-          while (!feof (infile) && strcmp (instr, "*/") != 0)
-            fscanf (infile, "%s", instr);
-          fscanf(infile, "%s", instr);
+         do
+           {
+             if (fscanf (infile, "%1023s", instr) != 1)
+               return FALSE;
+           }
+         while (strcmp (instr, "*/") != 0);
         }
-      if (strcmp (instr, str)==0)
+      else if (strcmp (instr, str) == 0)
         return TRUE;
     }
-
-  return FALSE;
 }
 
-gint
+static gint
 gdk_pixmap_seek_char (FILE  *infile,
                       gchar  c)
 {
-  gchar b, oldb;
+  gint b, oldb;
 
-  while (!feof (infile))
+  while ((b = getc(infile)) != EOF)
     {
-      fscanf(infile, "%c", &b);
       if (c != b && b == '/')
-        {
-          fscanf (infile, "%c", &b);
-          if (b == '*')
-            {
-              oldb = b;
-              while (!feof (infile) && !(oldb == '*' && b == '/'))
-                {
-                  oldb = b;
-                  fscanf (infile, "%c", &b);
-                }
-              fscanf (infile, "%c", &b);
-            }
+       {
+         b = getc (infile);
+         if (b == EOF)
+           return FALSE;
+         else if (b == '*')    /* we have a comment */
+           {
+             b = -1;
+             do
+               {
+                 oldb = b;
+                 b = getc (infile);
+                 if (b == EOF)
+                   return FALSE;
+               }
+             while (!(oldb == '*' && b == '/'));
+           }
         }
-      if (c == b)
-        return TRUE;
+      else if (c == b)
+       return TRUE;
     }
-
   return FALSE;
 }
 
-gint
+static gint
 gdk_pixmap_read_string (FILE  *infile,
                         gchar **buffer,
                        guint *buffer_size)
 {
-  gchar c;
-  guint cnt = 0;
+  gint c;
+  guint cnt = 0, bufsiz, ret = FALSE;
+  gchar *buf;
 
-  if ((*buffer) == NULL)
+  buf = *buffer;
+  bufsiz = *buffer_size;
+  if (buf == NULL)
     {
-      (*buffer_size) = 10 * sizeof (gchar);
-      (*buffer) = (gchar *) malloc (*buffer_size);
+      bufsiz = 10 * sizeof (gchar);
+      buf = g_new(gchar, bufsiz);
     }
 
   do
-    fscanf (infile, "%c", &c);
-  while (!feof (infile) && c != '"');
+    c = getc (infile);
+  while (c != EOF && c != '"');
 
   if (c != '"')
-    return FALSE;
+    goto out;
 
-  while (!feof (infile))
+  while ((c = getc(infile)) != EOF)
     {
-      fscanf (infile, "%c", &c);
-
-      if (cnt == (*buffer_size))
+      if (cnt == bufsiz)
        {
-         (*buffer_size) *= 2;
-         (*buffer) = (gchar *) realloc ((*buffer), *buffer_size);        
+         guint new_size = bufsiz * 2;
+         if (new_size > bufsiz)
+           bufsiz = new_size;
+         else
+           goto out;
+         
+         buf = (gchar *) g_realloc (buf, bufsiz);
+         buf[bufsiz-1] = '\0';
        }
 
       if (c != '"')
-        (*buffer)[cnt++] = c;
+        buf[cnt++] = c;
       else
         {
-          (*buffer)[cnt++] = 0;
-          return TRUE;
+          buf[cnt] = 0;
+         ret = TRUE;
+         break;
         }
     }
 
-  return FALSE;
+ out:
+  buf[bufsiz-1] = '\0';                /* ensure null termination for errors */
+  *buffer = buf;
+  *buffer_size = bufsiz;
+  return ret;
 }
 
-gchar*
+static gchar*
 gdk_pixmap_skip_whitespaces (gchar *buffer)
 {
   gint32 index = 0;
@@ -278,7 +329,7 @@ gdk_pixmap_skip_whitespaces (gchar *buffer)
   return &buffer[index];
 }
 
-gchar*
+static gchar*
 gdk_pixmap_skip_string (gchar *buffer)
 {
   gint32 index = 0;
@@ -289,12 +340,16 @@ gdk_pixmap_skip_string (gchar *buffer)
   return &buffer[index];
 }
 
-gchar*
+/* Xlib crashed ince at a color name lengths around 125 */
+#define MAX_COLOR_LEN 120
+
+static gchar*
 gdk_pixmap_extract_color (gchar *buffer)
 {
-  gint counter, finished = FALSE, numnames;
+  gint counter, numnames;
   gchar *ptr = NULL, ch, temp[128];
-  gchar color[128], *retcol;
+  gchar color[MAX_COLOR_LEN], *retcol;
+  gint space;
 
   counter = 0;
   while (ptr == NULL)
@@ -311,279 +366,199 @@ gdk_pixmap_extract_color (gchar *buffer)
       counter++;
     }
 
-  if (ptr == NULL)
-    return NULL;
-
   ptr = gdk_pixmap_skip_whitespaces (ptr);
 
   if (ptr[0] == 0)
     return NULL;
   else if (ptr[0] == '#')
     {
-      retcol = g_new(gchar, strlen (ptr) + 1);
-      strcpy (retcol, ptr);
+      counter = 1;
+      while (ptr[counter] != 0 && 
+             ((ptr[counter] >= '0' && ptr[counter] <= '9') ||
+              (ptr[counter] >= 'a' && ptr[counter] <= 'f') ||
+              (ptr[counter] >= 'A' && ptr[counter] <= 'F')))
+        counter++;
+
+      retcol = g_new (gchar, counter+1);
+      strncpy (retcol, ptr, counter);
+
+      retcol[counter] = 0;
+      
       return retcol;
     }
 
   color[0] = 0;
   numnames = 0;
 
-  while (finished == FALSE)
+  space = MAX_COLOR_LEN - 1;
+  while (space > 0)
     {
-      sscanf (ptr, "%s", temp);
+      sscanf (ptr, "%127s", temp);
 
-      if ((gint)ptr[0] == 0 || strcmp ("s", temp) == 0 || strcmp ("m", temp) == 0 ||
-          strcmp ("g", temp) == 0 || strcmp ("g4", temp) == 0)
-       finished = TRUE;
+      if (((gint)ptr[0] == 0) ||
+         (strcmp ("s", temp) == 0) || (strcmp ("m", temp) == 0) ||
+          (strcmp ("g", temp) == 0) || (strcmp ("g4", temp) == 0))
+       {
+         break;
+       }
       else
         {
           if (numnames > 0)
-            strcat (color, " ");
-          strcat (color, temp);
+           {
+             space -= 1;
+             strcat (color, " ");
+           }
+         strncat (color, temp, space);
+         space -= MIN (space, strlen (temp));
           ptr = gdk_pixmap_skip_string (ptr);
           ptr = gdk_pixmap_skip_whitespaces (ptr);
           numnames++;
         }
     }
 
-  retcol = g_new(gchar, strlen (color) + 1);
-  strcpy (retcol, color);
+  retcol = g_strdup (color);
   return retcol;
 }
 
 
-GdkPixmap*
-gdk_pixmap_create_from_xpm (GdkWindow  *window,
-                           GdkBitmap **mask,
-                           GdkColor   *transparent_color,
-                           const gchar *filename)
+enum buffer_op
 {
-  FILE *infile = NULL;
-  GdkPixmap *pixmap = NULL;
-  GdkImage *image = NULL;
-  GdkColormap *colormap;
-  GdkVisual *visual;
-  GdkGC *gc;
-  GdkColor tmp_color;
-  gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt;
-  gchar *buffer = NULL, *color_name = NULL, pixel_str[32];
-  guint buffer_size = 0;
-  _GdkPixmapColor *colors = NULL, *color = NULL;
-  gulong index;
-
-  g_return_val_if_fail (window != NULL, NULL);
+  op_header,
+  op_cmap,
+  op_body
+};
+  
+
+static void 
+gdk_xpm_destroy_notify (gpointer data)
+{
+  _GdkPixmapInfo *info = (_GdkPixmapInfo *)data;
+  GdkColor color;
+  int i;
 
-  infile = fopen (filename, "rb");
-  if (infile != NULL)
+  for (i=0; i<info->ncolors; i++)
     {
-      if (gdk_pixmap_seek_string (infile, "XPM", FALSE) == TRUE)
-        {
-          if (gdk_pixmap_seek_char (infile,'{') == TRUE)
-            {
-              gdk_pixmap_seek_char (infile, '"');
-              fseek (infile, -1, SEEK_CUR);
-              gdk_pixmap_read_string (infile, &buffer, &buffer_size);
-
-              sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
-
-              colors = g_new(_GdkPixmapColor, num_cols);
-
-              colormap = gdk_window_get_colormap (window);
-              visual = gdk_window_get_visual (window);
-
-             if (transparent_color == NULL) 
-               {
-                 gdk_color_white (colormap, &tmp_color);
-                 transparent_color = &tmp_color;
-               }
-
-              for (cnt = 0; cnt < num_cols; cnt++)
-                {
-                  gdk_pixmap_seek_char (infile, '"');
-                  fseek (infile, -1, SEEK_CUR);
-                  gdk_pixmap_read_string (infile, &buffer, &buffer_size);
-
-                  colors[cnt].color_string = g_new(gchar, cpp + 1);
-                  for (n = 0; n < cpp; n++)
-                    colors[cnt].color_string[n] = buffer[n];
-                  colors[cnt].color_string[n] = 0;
-                 colors[cnt].transparent = FALSE;
-
-                  if (color_name != NULL)
-                    g_free (color_name);
-
-                  color_name = gdk_pixmap_extract_color (&buffer[cpp]);
-
-                  if (color_name != NULL)
-                    {
-                      if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
-                       {
-                         colors[cnt].color = *transparent_color;
-                         colors[cnt].transparent = TRUE;
-                       }
-                    }
-                  else
-                   {
-                     colors[cnt].color = *transparent_color;
-                     colors[cnt].transparent = TRUE;
-                   }
-
-                  gdk_color_alloc (colormap, &colors[cnt].color);
-                }
-
-              index = 0;
-              image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
-
-             gc = NULL;
-             if (mask)
-               {
-                 /* The pixmap mask is just a bits pattern.
-                  * Color 0 is used for background and 1 for foreground.
-                  * We don't care about the colormap, we just need 0 and 1.
-                  */
-                 GdkColor mask_pattern;
-                 
-                 *mask = gdk_pixmap_new (window, width, height, 1);
-                 gc = gdk_gc_new (*mask);
-                 
-                 mask_pattern.pixel = 0;
-                 gdk_gc_set_foreground (gc, &mask_pattern);
-                 gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
-                 
-                 mask_pattern.pixel = 1;
-                 gdk_gc_set_foreground (gc, &mask_pattern);
-               }
-
-              for (ycnt = 0; ycnt < height; ycnt++)
-                {
-                  gdk_pixmap_read_string (infile, &buffer, &buffer_size);
-
-                  for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
-                    {
-                      strncpy (pixel_str, &buffer[n], cpp);
-                      pixel_str[cpp] = 0;
-                      color = NULL;
-                      ns = 0;
-
-                      while (color == NULL)
-                        {
-                          if (strcmp (pixel_str, colors[ns].color_string) == 0)
-                            color = &colors[ns];
-                          else
-                            ns++;
-                        }
-
-                      gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
-
-                     if (mask && color->transparent)
-                       {
-                         if (cnt < xcnt)
-                           gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
-                         cnt = xcnt + 1;
-                       }
-                    }
-
-                 if (mask && (cnt < xcnt))
-                   gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
-                }
-
-             if (mask)
-               gdk_gc_destroy (gc);
-
-              pixmap = gdk_pixmap_new (window, width, height, visual->depth);
-
-              gc = gdk_gc_new (pixmap);
-              gdk_gc_set_foreground (gc, transparent_color);
-              gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
-              gdk_gc_destroy (gc);
-              gdk_image_destroy (image);
-            }
-        }
-
-      fclose (infile);
-      free (buffer);
-
-      if (colors != NULL)
-        {
-          for (cnt = 0; cnt < num_cols; cnt++)
-            g_free (colors[cnt].color_string);
-          g_free (colors);
-        }
+      color.pixel = info->pixels[i];
+      gdk_colormap_free_colors (info->colormap, &color, 1);
     }
 
-  return pixmap;
+  gdk_colormap_unref (info->colormap);
+  g_free (info);
 }
-
-GdkPixmap*
-gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
-                             GdkBitmap **mask,
-                             GdkColor   *transparent_color,
-                             gchar     **data)
+  
+static GdkPixmap *
+_gdk_pixmap_create_from_xpm (GdkWindow  *window,
+                            GdkColormap *colormap,
+                            GdkBitmap **mask,
+                            GdkColor   *transparent_color,
+                            gchar *   (*get_buf) (enum buffer_op op,
+                                                  gpointer       handle),
+                            gpointer    handle)
 {
   GdkPixmap *pixmap = NULL;
   GdkImage *image = NULL;
-  GdkColormap *colormap;
   GdkVisual *visual;
-  GdkGC *gc;
+  GdkGC *gc = NULL;
   GdkColor tmp_color;
-  gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt, i;
-  gchar *buffer, *color_name = NULL, pixel_str[32];
-  _GdkPixmapColor *colors = NULL, *color = NULL;
+  gint width, height, num_cols, cpp, n, ns, cnt, xcnt, ycnt, wbytes;
+  gchar *buffer, pixel_str[32];
+  gchar *name_buf;
+  _GdkPixmapColor *color = NULL, *fallbackcolor = NULL;
+  _GdkPixmapColor *colors = NULL;
   gulong index;
-
-  g_return_val_if_fail (window != NULL, NULL);
-
-  i = 0;
-  buffer = data[i++];
+  GHashTable *color_hash = NULL;
+  _GdkPixmapInfo *color_info = NULL;
+  
+  if ((window == NULL) && (colormap == NULL))
+    g_warning ("Creating pixmap from xpm with NULL window and colormap");
+  
+  if (window == NULL)
+    window = gdk_parent_root;
+  
+  if (colormap == NULL)
+    {
+      colormap = gdk_drawable_get_colormap (window);
+      visual = gdk_drawable_get_visual (window);
+    }
+  else
+    visual = ((GdkColormapPrivate *)colormap)->visual;
+  
+  buffer = (*get_buf) (op_header, handle);
+  if (buffer == NULL)
+    return NULL;
+  
   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
-
-  colors = g_new(_GdkPixmapColor, num_cols);
-
-  colormap = gdk_window_get_colormap (window);
-  visual = gdk_window_get_visual (window);
-
-  if (transparent_color == NULL) 
+  if (cpp >= 32)
+    {
+      g_warning ("Pixmap has more than 31 characters per color\n");
+      return NULL;
+    }
+  
+  color_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  
+  if (transparent_color == NULL)
     {
       gdk_color_white (colormap, &tmp_color);
       transparent_color = &tmp_color;
     }
 
-  for (cnt = 0; cnt < num_cols; cnt++)
+  /* For pseudo-color and grayscale visuals, we have to remember
+   * the colors we allocated, so we can free them later.
+   */
+  if ((visual->type == GDK_VISUAL_PSEUDO_COLOR) ||
+      (visual->type == GDK_VISUAL_GRAYSCALE))
     {
-      buffer = data[i++];
-
-      colors[cnt].color_string = g_new(gchar, cpp + 1);
-      for (n = 0; n < cpp; n++)
-       colors[cnt].color_string[n] = buffer[n];
-      colors[cnt].color_string[n] = 0;
-      colors[cnt].transparent = FALSE;
-
-      if (color_name != NULL)
-       g_free (color_name);
+      color_info = g_malloc (sizeof (_GdkPixmapInfo) + 
+                            sizeof(gulong) * (num_cols - 1));
+      color_info->ncolors = num_cols;
+      color_info->colormap = colormap;
+      gdk_colormap_ref (colormap);
+    }
 
-      color_name = gdk_pixmap_extract_color (&buffer[cpp]);
+  name_buf = g_new (gchar, num_cols * (cpp+1));
+  colors = g_new (_GdkPixmapColor, num_cols);
 
-      if (color_name != NULL)
-       {
-         if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
-           {
-             colors[cnt].color = *transparent_color;
-             colors[cnt].transparent = TRUE;
-           }
-       }
-      else
+  for (cnt = 0; cnt < num_cols; cnt++)
+    {
+      gchar *color_name;
+      
+      buffer = (*get_buf) (op_cmap, handle);
+      if (buffer == NULL)
+       goto error;
+      
+      color = &colors[cnt];
+      color->color_string = &name_buf [cnt * (cpp + 1)];
+      strncpy (color->color_string, buffer, cpp);
+      color->color_string[cpp] = 0;
+      buffer += strlen (color->color_string);
+      color->transparent = FALSE;
+      
+      color_name = gdk_pixmap_extract_color (buffer);
+      
+      if (color_name == NULL || g_strcasecmp (color_name, "None") == 0 ||
+         gdk_color_parse (color_name, &color->color) == FALSE)
        {
-         colors[cnt].color = *transparent_color;
-         colors[cnt].transparent = TRUE;
+         color->color = *transparent_color;
+         color->transparent = TRUE;
        }
-
-      gdk_color_alloc (colormap, &colors[cnt].color);
+      
+      g_free (color_name);
+      
+      /* FIXME: The remaining slowness appears to happen in this
+         function. */
+      gdk_color_alloc (colormap, &color->color);
+
+      if (color_info)
+       color_info->pixels[cnt] = color->color.pixel;
+      
+      g_hash_table_insert (color_hash, color->color_string, color);
+      if (cnt == 0)
+       fallbackcolor = color;
     }
-
+  
   index = 0;
   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
-
-  gc = NULL;
+  
   if (mask)
     {
       /* The pixmap mask is just a bits pattern.
@@ -591,39 +566,43 @@ gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
        * We don't care about the colormap, we just need 0 and 1.
        */
       GdkColor mask_pattern;
-
+      
       *mask = gdk_pixmap_new (window, width, height, 1);
       gc = gdk_gc_new (*mask);
-
+      
       mask_pattern.pixel = 0;
       gdk_gc_set_foreground (gc, &mask_pattern);
       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
-
+      
       mask_pattern.pixel = 1;
       gdk_gc_set_foreground (gc, &mask_pattern);
     }
-
+  
+  wbytes = width * cpp;
   for (ycnt = 0; ycnt < height; ycnt++)
     {
-      buffer = data[i++];
-
-      for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
+      buffer = (*get_buf) (op_body, handle);
+      
+      /* FIXME: this slows things down a little - it could be
+       * integrated into the strncpy below, perhaps. OTOH, strlen
+       * is fast.
+       */
+      if ((buffer == NULL) || strlen (buffer) < wbytes)
+       continue;
+      
+      for (n = 0, cnt = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++)
        {
          strncpy (pixel_str, &buffer[n], cpp);
          pixel_str[cpp] = 0;
-         color = NULL;
          ns = 0;
-
-         while (color == NULL)
-           {
-             if (strcmp (pixel_str, colors[ns].color_string) == 0)
-               color = &colors[ns];
-             else
-               ns++;
-           }
-
+         
+         color = g_hash_table_lookup (color_hash, pixel_str);
+         
+         if (!color) /* screwed up XPM file */
+           color = fallbackcolor;
+         
          gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
-
+         
          if (mask && color->transparent)
            {
              if (cnt < xcnt)
@@ -631,65 +610,202 @@ gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
              cnt = xcnt + 1;
            }
        }
-
+      
       if (mask && (cnt < xcnt))
        gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
     }
-
+  
+ error:
+  
   if (mask)
-    gdk_gc_destroy (gc);
+    gdk_gc_unref (gc);
+  
+  if (image != NULL)
+    {
+      pixmap = gdk_pixmap_new (window, width, height, visual->depth);
+
+      if (color_info)
+       gdk_drawable_set_data (pixmap, "gdk-xpm", color_info, 
+                              gdk_xpm_destroy_notify);
+      
+      gc = gdk_gc_new (pixmap);
+      gdk_gc_set_foreground (gc, transparent_color);
+      gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
+      gdk_gc_unref (gc);
+      gdk_image_unref (image);
+    }
+  else if (color_info)
+    gdk_xpm_destroy_notify (color_info);
+  
+  if (color_hash != NULL)
+    g_hash_table_destroy (color_hash);
 
-  pixmap = gdk_pixmap_new (window, width, height, visual->depth);
+  if (colors != NULL)
+    g_free (colors);
 
-  gc = gdk_gc_new (pixmap);
-  gdk_gc_set_foreground (gc, transparent_color);
-  gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
-  gdk_gc_destroy (gc);
-  gdk_image_destroy (image);
+  if (name_buf != NULL)
+    g_free (name_buf);
 
-  if (colors != NULL)
+  return pixmap;
+}
+
+
+struct file_handle
+{
+  FILE *infile;
+  gchar *buffer;
+  guint buffer_size;
+};
+
+
+static gchar *
+file_buffer (enum buffer_op op, gpointer handle)
+{
+  struct file_handle *h = handle;
+
+  switch (op)
     {
-      for (cnt = 0; cnt < num_cols; cnt++)
-       g_free (colors[cnt].color_string);
-      g_free (colors);
+    case op_header:
+      if (gdk_pixmap_seek_string (h->infile, "XPM", FALSE) != TRUE)
+       break;
+
+      if (gdk_pixmap_seek_char (h->infile,'{') != TRUE)
+       break;
+      /* Fall through to the next gdk_pixmap_seek_char. */
+
+    case op_cmap:
+      gdk_pixmap_seek_char (h->infile, '"');
+      fseek (h->infile, -1, SEEK_CUR);
+      /* Fall through to the gdk_pixmap_read_string. */
+
+    case op_body:
+      gdk_pixmap_read_string (h->infile, &h->buffer, &h->buffer_size);
+      return h->buffer;
     }
-
-  return pixmap;
+  return 0;
 }
 
+
 GdkPixmap*
-gdk_pixmap_ref (GdkPixmap *pixmap)
+gdk_pixmap_colormap_create_from_xpm (GdkWindow   *window,
+                                    GdkColormap *colormap,
+                                    GdkBitmap  **mask,
+                                    GdkColor    *transparent_color,
+                                    const gchar *filename)
 {
-  GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
-  g_return_val_if_fail (pixmap != NULL, NULL);
+  struct file_handle h;
+  GdkPixmap *pixmap = NULL;
+
+  memset (&h, 0, sizeof (h));
+  h.infile = fopen (filename, "rb");
+  if (h.infile != NULL)
+    {
+      pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask,
+                                           transparent_color,
+                                           file_buffer, &h);
+      fclose (h.infile);
+      g_free (h.buffer);
+    }
 
-  private->ref_count += 1;
   return pixmap;
 }
 
-void
-gdk_pixmap_unref (GdkPixmap *pixmap)
+GdkPixmap*
+gdk_pixmap_create_from_xpm (GdkWindow  *window,
+                           GdkBitmap **mask,
+                           GdkColor   *transparent_color,
+                           const gchar *filename)
 {
-  GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
-  g_return_if_fail(pixmap != NULL);
+  return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask,
+                                      transparent_color, filename);
+}
+
+
+struct mem_handle
+{
+  gchar **data;
+  int offset;
+};
+
 
-  private->ref_count -= 1;
-  if (private->ref_count == 0)
+static gchar *
+mem_buffer (enum buffer_op op, gpointer handle)
+{
+  struct mem_handle *h = handle;
+  switch (op)
     {
-      XFreePixmap (private->xdisplay, private->xwindow);
-      gdk_xid_table_remove (private->xwindow);
-      g_free (private);
+    case op_header:
+    case op_cmap:
+    case op_body:
+      if (h->data[h->offset])
+       return h->data[h->offset ++];
     }
+  return 0;
 }
 
-GdkBitmap *
-gdk_bitmap_ref (GdkBitmap *bitmap)
+
+GdkPixmap*
+gdk_pixmap_colormap_create_from_xpm_d (GdkWindow  *window,
+                                      GdkColormap *colormap,
+                                      GdkBitmap **mask,
+                                      GdkColor   *transparent_color,
+                                      gchar     **data)
 {
-  return (GdkBitmap *)gdk_pixmap_ref ((GdkPixmap *)bitmap);
+  struct mem_handle h;
+  GdkPixmap *pixmap = NULL;
+
+  memset (&h, 0, sizeof (h));
+  h.data = data;
+  pixmap = _gdk_pixmap_create_from_xpm (window, colormap, mask,
+                                       transparent_color,
+                                       mem_buffer, &h);
+  return pixmap;
 }
 
-void
-gdk_bitmap_unref (GdkBitmap *bitmap)
+
+GdkPixmap*
+gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
+                             GdkBitmap **mask,
+                             GdkColor   *transparent_color,
+                             gchar     **data)
 {
-  gdk_pixmap_unref ((GdkPixmap *)bitmap);
+  return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask,
+                                               transparent_color, data);
+}
+
+GdkPixmap*
+gdk_pixmap_foreign_new (guint32 anid)
+{
+  GdkPixmap *pixmap;
+  GdkDrawablePrivate *private;
+  Pixmap xpixmap;
+  Window root_return;
+  unsigned int x_ret, y_ret, w_ret, h_ret, bw_ret, depth_ret;
+
+  /* check to make sure we were passed something at
+     least a little sane */
+  g_return_val_if_fail((anid != 0), NULL);
+  
+  /* set the pixmap to the passed in value */
+  xpixmap = anid;
+
+  /* get information about the Pixmap to fill in the structure for
+     the gdk window */
+  if (!XGetGeometry(GDK_DISPLAY(),
+                   xpixmap, &root_return,
+                   &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret))
+      return NULL;
+      
+  pixmap = gdk_x11_pixmap_alloc ();
+  private = (GdkDrawablePrivate *)pixmap;
+
+  GDK_DRAWABLE_XDATA (private)->xdisplay = GDK_DISPLAY ();
+  GDK_DRAWABLE_XDATA (private)->xid = xpixmap;
+
+  private->width = w_ret;
+  private->height = h_ret;
+  
+  gdk_xid_table_insert(&GDK_DRAWABLE_XID (pixmap), pixmap);
+
+  return pixmap;
 }