diff src/objects-x.c @ 219:262b8bb4a523 r20-4b8

Import from CVS: tag r20-4b8
author cvs
date Mon, 13 Aug 2007 10:09:35 +0200
parents 3d6bfa290dbd
children b2472a1930f2
line wrap: on
line diff
--- a/src/objects-x.c	Mon Aug 13 10:08:36 2007 +0200
+++ b/src/objects-x.c	Mon Aug 13 10:09:35 2007 +0200
@@ -44,67 +44,148 @@
 /************************************************************************/
 
 /* Replacement for XAllocColor() that tries to return the nearest
-   available color if the colormap is full.  From FSF Emacs. */
+   available color if the colormap is full.  Original was from FSFmacs,
+   but rewritten by Jareth Hein <jareth@camelot-soft.com> 97/11/25 */
 
+/* Return value is 1 for normal success, 2 for nearest color success,
+   3 for Non-deallocable sucess, and 0 for absolute failure (shouldn't
+   happen?) */
 int
-allocate_nearest_color (Display *display, Colormap screen_colormap,
+allocate_nearest_color (Display *display, Colormap colormap, Visual *visual,
 		        XColor *color_def)
 {
   int status;
 
-  status = XAllocColor (display, screen_colormap, color_def);
-  if (!status)
+  if (visual->class == DirectColor || visual->class == TrueColor)
     {
-      /* If we got to this point, the colormap is full, so we're
-	 going to try and get the next closest color.
-	 The algorithm used is a least-squares matching, which is
-	 what X uses for closest color matching with StaticColor visuals.  */
-
-      XColor *cells;
-      int no_cells;
-      int nearest;
-      long nearest_delta, trial_delta;
-      int x;
-
-      no_cells = XDisplayCells (display, XDefaultScreen (display));
-      cells = alloca_array (XColor, no_cells);
-
-      for (x = 0; x < no_cells; x++)
-	cells[x].pixel = x;
+      if (XAllocColor (display, colormap, color_def) != 0)
+	{
+	  status = 1;
+	}
+      else
+	{
+	  /* We're dealing with a TrueColor/DirectColor visual, so play games
+	     with the RGB values in the XColor struct. */
+	  /* ### JH: I'm not sure how a call to XAllocColor can fail in a
+	     TrueColor or DirectColor visual, so I will just reformat the
+	     request to match the requirements of the visual, and re-issue
+	     the request.  If this fails for anybody, I wanna know about it
+	     so I can come up with a better plan */
 
-      XQueryColors (display, screen_colormap, cells, no_cells);
-      nearest = 0;
-      /* I'm assuming CSE so I'm not going to condense this. */
-      nearest_delta = ((((color_def->red >> 8) - (cells[0].red >> 8))
-			* ((color_def->red >> 8) - (cells[0].red >> 8)))
-		       +
-		       (((color_def->green >> 8) - (cells[0].green >> 8))
-			* ((color_def->green >> 8) - (cells[0].green >> 8)))
-		       +
-		       (((color_def->blue >> 8) - (cells[0].blue >> 8))
-			* ((color_def->blue >> 8) - (cells[0].blue >> 8))));
-      for (x = 1; x < no_cells; x++)
-	{
-	  trial_delta = ((((color_def->red >> 8) - (cells[x].red >> 8))
-			  * ((color_def->red >> 8) - (cells[x].red >> 8)))
-			 +
-			 (((color_def->green >> 8) - (cells[x].green >> 8))
-			  * ((color_def->green >> 8) - (cells[x].green >> 8)))
-			 +
-			 (((color_def->blue >> 8) - (cells[x].blue >> 8))
-			  * ((color_def->blue >> 8) - (cells[x].blue >> 8))));
-	  if (trial_delta < nearest_delta)
-	    {
-	      nearest = x;
-	      nearest_delta = trial_delta;
+	  unsigned long rshift,gshift,bshift,rbits,gbits,bbits,junk;
+	  junk = visual->red_mask;
+	  rshift = 0;
+	  while ((junk & 0x1) == 0) {
+	    junk = junk >> 1;
+	    rshift ++;
+	  }
+	  rbits = 0;
+	  while (junk != 0) {
+	    junk = junk >> 1;
+	    rbits++;
+	  }
+	  junk = visual->green_mask;
+	  gshift = 0;
+	  while ((junk & 0x1) == 0) {
+	    junk = junk >> 1;
+	    gshift ++;
+	  }
+	  gbits = 0;
+	  while (junk != 0) {
+	    junk = junk >> 1;
+	    gbits++;
+	  }
+	  junk = visual->blue_mask;
+	  bshift = 0;
+	  while ((junk & 0x1) == 0) {
+	    junk = junk >> 1;
+	    bshift ++;
+	  }
+	  bbits = 0;
+	  while (junk != 0) {
+	    junk = junk >> 1;
+	    bbits++;
+ 	  }
+
+	  color_def->red = color_def->red >> (16 - rbits);
+	  color_def->green = color_def->green >> (16 - gbits);
+	  color_def->blue = color_def->blue >> (16 - bbits);
+	  if (XAllocColor (display, colormap, color_def) != 0)
+	    status = 1;
+	  else
+  	    {
+	      /* ### JH: I'm punting here, knowing that doing this will at
+		 least draw the color correctly.  However, unless we convert
+		 all of the functions that allocate colors (graphics
+		 libraries, etc) to use this function doing this is very
+		 likely to cause problems later... */
+	      color_def->pixel = (color_def->red << rshift) | (color_def->green << gshift) |
+				   (color_def->blue << bshift);
+	      status = 3;
 	    }
 	}
-      color_def->red = cells[nearest].red;
-      color_def->green = cells[nearest].green;
-      color_def->blue = cells[nearest].blue;
-      status = XAllocColor (display, screen_colormap, color_def);
     }
+  else
+    {
+      if (XAllocColor (display, colormap, color_def) != 0)
+	status = 1;
+      else
+	{
+	  /* If we got to this point, the colormap is full, so we're
+	     going to try and get the next closest color.  The algorithm used
+	     is a least-squares matching, which is what X uses for closest
+	     color matching with StaticColor visuals. */
+	  XColor *cells;
+	  /* JH: I can't believe there's no way to go backwards from a
+	     colormap ID and get its visual and number of entries, but X
+	     apparently isn't built that way... */
+	  int no_cells = visual->map_entries; 
+	  int nearest;
+	  long nearest_delta, trial_delta;
+	  int x;
 
+	  cells = alloca_array (XColor, no_cells);
+
+	  for (x = 0; x < no_cells; x++)
+	    cells[x].pixel = x;
+ 
+	  /* read the current colormap */
+	  XQueryColors (display, colormap, cells, no_cells);
+	  nearest = 0;
+	  /* I'm assuming CSE so I'm not going to condense this. */
+	  nearest_delta = ((((color_def->red >> 8) - (cells[0].red >> 8))
+			    * ((color_def->red >> 8) - (cells[0].red >> 8)))
+			   +
+			   (((color_def->green >> 8) - (cells[0].green >> 8))
+			    * ((color_def->green >> 8) - (cells[0].green >> 8)))
+			   +
+			   (((color_def->blue >> 8) - (cells[0].blue >> 8))
+			    * ((color_def->blue >> 8) - (cells[0].blue >> 8))));
+	  for (x = 1; x < no_cells; x++)
+	    {
+	      trial_delta = ((((color_def->red >> 8) - (cells[x].red >> 8))
+			      * ((color_def->red >> 8) - (cells[x].red >> 8)))
+			     +
+			     (((color_def->green >> 8) - (cells[x].green >> 8))
+			      * ((color_def->green >> 8) - (cells[x].green >> 8)))
+			     +
+			     (((color_def->blue >> 8) - (cells[x].blue >> 8))
+			      * ((color_def->blue >> 8) - (cells[x].blue >> 8))));
+	      if (trial_delta < nearest_delta)
+		{
+		  nearest = x;
+		  nearest_delta = trial_delta;
+		}
+	    }
+	  color_def->red = cells[nearest].red;
+	  color_def->green = cells[nearest].green;
+	  color_def->blue = cells[nearest].blue;
+	  if (XAllocColor (display, colormap, color_def) != 0)
+	    status = 2;
+	  else
+	    status = 0; /* JH: how does this happen??? DOES this happen??? */
+	}
+    }
   return status;
 }
 
@@ -115,11 +196,13 @@
   Display *dpy;
   Screen *xs;
   Colormap cmap;
+  Visual *visual;
   int result;
 
   dpy = DEVICE_X_DISPLAY (d);
   xs = DefaultScreenOfDisplay (dpy);
-  cmap = DefaultColormapOfScreen (xs);
+  cmap = DEVICE_X_COLORMAP(d);
+  visual = DEVICE_X_VISUAL (d);
 
   memset (color, 0, sizeof (*color));
   {
@@ -135,7 +218,7 @@
 				 Qcolor, errb);
       return 0;
     }
-  result = allocate_nearest_color (dpy, cmap, color);
+  result = allocate_nearest_color (dpy, cmap, visual, color);
   if (!result)
     {
       maybe_signal_simple_error ("couldn't allocate color",
@@ -143,7 +226,7 @@
       return 0;
     }
 
-  return 1;
+  return result;
 }
 
 static int
@@ -164,6 +247,10 @@
   /* Don't allocate the data until we're sure that we will succeed,
      or the finalize method may get fucked. */
   c->data = xnew (struct x_color_instance_data);
+  if (result == 3) 
+    COLOR_INSTANCE_X_DEALLOC (c) = 0;
+  else
+    COLOR_INSTANCE_X_DEALLOC (c) = 1;
   COLOR_INSTANCE_X_COLOR (c) = color;
   return 1;
 }
@@ -187,11 +274,11 @@
     {
       if (DEVICE_LIVE_P (XDEVICE (c->device)))
 	{
-	  Display *dpy = DEVICE_X_DISPLAY (XDEVICE (c->device));
-
-	  XFreeColors (dpy,
-		       DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)),
-		       &COLOR_INSTANCE_X_COLOR (c).pixel, 1, 0);
+	  if (COLOR_INSTANCE_X_DEALLOC (c)) 
+	    {
+	      XFreeColors (DEVICE_X_DISPLAY (XDEVICE (c->device)), DEVICE_X_COLORMAP (XDEVICE (c->device)),
+			   &COLOR_INSTANCE_X_COLOR (c).pixel, 1, 0);
+	    }
 	}
       xfree (c->data);
       c->data = 0;
@@ -200,8 +287,8 @@
 
 /* Color instances are equal if they resolve to the same color on the
    screen (have the same RGB values).  I imagine that
-   "same RGV values" == "same cell in the colormap."  Arguably we should
-   be comparing their names instead. */
+   "same RGB values" == "same cell in the colormap."  Arguably we should
+   be comparing their names or pixel values instead. */
 
 static int
 x_color_instance_equal (struct Lisp_Color_Instance *c1,
@@ -236,12 +323,13 @@
 {
   XColor c;
   Display *dpy = DEVICE_X_DISPLAY (d);
+  Colormap cmap = DEVICE_X_COLORMAP (d);
+
   CONST char *extname;
 
   GET_C_STRING_CTEXT_DATA_ALLOCA (color, extname);
 
-  return XParseColor (dpy,
-		      DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)),
+  return XParseColor (dpy, cmap,
 		      extname, &c);
 }