Subject: Re: bug#36304: 27.0.50; request: switch to the superior HTML #RGB convention for colors

From: Pip Cet <[email-masked]>
Date: Sat, 22 Jun 2019 11:23:35 +0000

On Fri, Jun 21, 2019 at 1:54 AM Richard Stallman <[email-masked]> wrote:
>   > We're still using the X convention. It's time to switch. The HTML
>   > convention is what virtually everyone would expect these days, it
>   > allows specifying "white" concisely, and it's what we use in some
>   > contexts, such as when rendering SVGs.
>
> It seems plausible to me, speaking as a non-expert.

Here's a patch. I've read the documentation and it appears already to
have been updated by someone so it no longer links to the X
documentation.

* src/xterm.c (x_parse_color): Translate #RGB notation to rgb:R/G/B
  notation, which X handles slightly differently.
---
 src/xterm.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/xterm.c b/src/xterm.c
index 1acff2af0d..614774f6bc 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2381,6 +2381,8 @@ x_query_frame_background_color (struct frame *f,
XColor *bgcolor)
   x_query_colors (f, bgcolor, 1);
 }

+#define HEX_COLOR_NAME_LENGTH 32
+
 /* On frame F, translate the color name to RGB values.  Use cached
    information, if possible.

@@ -2398,9 +2400,35 @@ x_query_frame_background_color (struct frame
*f, XColor *bgcolor)

   if (color_name[0] == '#')
     {
-      /* The hex form is parsed directly by XParseColor without
+      /* Don't pass #RGB strings directly to XParseColor, because that
+     follows the old convention of zero-extending each channel
+     value: #f00 means #f00000. We want the new convention of
+     scaling channel values, so #f00 means #ff0000.
+
+     So we translate #f00 to rgb:f/0/0, which X handles
+     differently. */
+      char rgb_color_name[HEX_COLOR_NAME_LENGTH];
+      int len = strlen (color_name);
+      int digits_per_channel;
+      if (len == 4)
+    digits_per_channel = 1;
+      else if (len == 7)
+    digits_per_channel = 2;
+      else if (len == 10)
+    digits_per_channel = 3;
+      else if (len == 13)
+    digits_per_channel = 4;
+      else
+    return 0;
+
+      snprintf (rgb_color_name, sizeof rgb_color_name, "rgb:%.*s/%.*s/%.*s",
+        digits_per_channel, color_name + 1,
+        digits_per_channel, color_name + digits_per_channel + 1,
+        digits_per_channel, color_name + 2 * digits_per_channel + 1);
+
+      /* The rgb form is parsed directly by XParseColor without
      talking to the X server.  No need for caching.  */
-      return XParseColor (dpy, cmap, color_name, color);
+      return XParseColor (dpy, cmap, rgb_color_name, color);
     }

   for (cache_entry = FRAME_DISPLAY_INFO (f)->color_names; cache_entry;
-- 
2.20.1
From b603f409f2f5d1ec7c1a6bcfd0cb27ea2565026e Mon Sep 17 00:00:00 2001
From: Pip Cet <[email-masked]>
Date: Sat, 22 Jun 2019 11:04:20 +0000
Subject: [PATCH] Change color convention so #f00 means the same as #ff0000.

* src/xterm.c (x_parse_color): Translate #RGB notation to rgb:R/G/B
  notation, which X handles slightly differently.
---
 src/xterm.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/xterm.c b/src/xterm.c
index 1acff2af0d..614774f6bc 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2381,6 +2381,8 @@ x_query_frame_background_color (struct frame *f, XColor *bgcolor)
   x_query_colors (f, bgcolor, 1);
 }
 
+#define HEX_COLOR_NAME_LENGTH 32
+
 /* On frame F, translate the color name to RGB values.  Use cached
    information, if possible.
 
@@ -2398,9 +2400,35 @@ x_query_frame_background_color (struct frame *f, XColor *bgcolor)
 
   if (color_name[0] == '#')
     {
-      /* The hex form is parsed directly by XParseColor without
+      /* Don't pass #RGB strings directly to XParseColor, because that
+	 follows the old convention of zero-extending each channel
+	 value: #f00 means #f00000. We want the new convention of
+	 scaling channel values, so #f00 means #ff0000.
+
+	 So we translate #f00 to rgb:f/0/0, which X handles
+	 differently. */
+      char rgb_color_name[HEX_COLOR_NAME_LENGTH];
+      int len = strlen (color_name);
+      int digits_per_channel;
+      if (len == 4)
+	digits_per_channel = 1;
+      else if (len == 7)
+	digits_per_channel = 2;
+      else if (len == 10)
+	digits_per_channel = 3;
+      else if (len == 13)
+	digits_per_channel = 4;
+      else
+	return 0;
+
+      snprintf (rgb_color_name, sizeof rgb_color_name, "rgb:%.*s/%.*s/%.*s",
+		digits_per_channel, color_name + 1,
+		digits_per_channel, color_name + digits_per_channel + 1,
+		digits_per_channel, color_name + 2 * digits_per_channel + 1);
+
+      /* The rgb form is parsed directly by XParseColor without
 	 talking to the X server.  No need for caching.  */
-      return XParseColor (dpy, cmap, color_name, color);
+      return XParseColor (dpy, cmap, rgb_color_name, color);
     }
 
   for (cache_entry = FRAME_DISPLAY_INFO (f)->color_names; cache_entry;
-- 
2.20.1