Subject: bug#56538: closed (Re: bug#56538: 29.0.50; [PATCH] Colored highlight in Lucid backend)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 16 Jul 2022 11:00:02 +0000

Your bug report

#56538: 29.0.50; [PATCH] Colored highlight in Lucid backend

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
56538: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=56538
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#56538: 29.0.50; [PATCH] Colored highlight in Lucid backend
From: Po Lu <[email-masked]>
To: Manuel Giraud <[email-masked]>
Cc: [email-masked]
Date: Sat, 16 Jul 2022 18:59:35 +0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Manuel Giraud <[email-masked]> writes:

> Po Lu <[email-masked]> writes:
>
>> How about "by the mouse or key navigation"?
>
> 👍 Here's the updated version.
>
> From f0a2392c04b2d3f514716df9757f0b8ed96efa22 Mon Sep 17 00:00:00 2001
> From: Manuel Giraud <[email-masked]>
> Date: Mon, 11 Jul 2022 11:14:08 +0200
> Subject: [PATCH] Colored menu highlight in Lucid backend

Installed, so I'm closing this bug.

Thanks for working on Emacs.

Subject: 29.0.50; [PATCH] Colored highlight in Lucid backend
From: Manuel Giraud <[email-masked]>
To: [email-masked]
Date: Wed, 13 Jul 2022 15:38:35 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Hi,

Here is a patch that add a X resource to color the highlighted menu
entry in the Lucid backend.  There is much going on here because I
needed to understand this code and end up needed to refactor some part
to implement this feature.  This was about ten clunky patches that I
finally squashed into this one.

I have tested that it works with the 2 following configuration
     - 	--with-x-toolkit=athena
     - 	--with-x-toolkit=athena --without-xft --without-cairo

Here is how it looks like:
>From f51e1dbd3174d727a12e673e0b820b4cf35e7046 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Mon, 11 Jul 2022 11:14:08 +0200
Subject: [PATCH] Colored menu highlight in Lucid backend

* lwlib/xlwmenuP.h:
* lwlib/xlwmenu.h:
* lwlib/xlwmenu.c: Introduce resources to handle colored highlighting
of menu entries.
* doc/emacs/xresources.texi (Lucid Resources): Documentation.
---
 doc/emacs/xresources.texi |   4 +
 etc/NEWS                  |   4 +
 lwlib/xlwmenu.c           | 341 ++++++++++++++++++++++++++++----------
 lwlib/xlwmenu.h           |  14 ++
 lwlib/xlwmenuP.h          |  18 +-
 5 files changed, 293 insertions(+), 88 deletions(-)

diff --git a/doc/emacs/xresources.texi b/doc/emacs/xresources.texi
index 2c2700bc15..f8be06b5e7 100644
--- a/doc/emacs/xresources.texi
+++ b/doc/emacs/xresources.texi
@@ -449,6 +449,10 @@ Lucid Resources
 Foreground color.
 @item disabledForeground
 Foreground color for a disabled menu item.
+@item highlightForeground
+Foreground color for a highlighted menu item.
+@item highlightBackground
+Background color for a highlighted menu item.
 @ifnottex
 @item horizontalSpacing
 Horizontal spacing in pixels between items.  Default is 3.
diff --git a/etc/NEWS b/etc/NEWS
index bf36316890..b33d604a6f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -544,6 +544,10 @@ and pop-up menus.
 This controls the style of the pre-edit and status areas of X input
 methods.
 
++++
+** New X resources: "highlightForeground" and "highlightBackground"
+This controls colors used for highlighted menu item.
+
 +++
 ** On X11, Emacs now tries to synchronize window resize with the window manager.
 This leads to less flicker and empty areas of a frame being displayed
diff --git a/lwlib/xlwmenu.c b/lwlib/xlwmenu.c
index 68f49e646d..32310e18ef 100644
--- a/lwlib/xlwmenu.c
+++ b/lwlib/xlwmenu.c
@@ -121,6 +121,10 @@ #define DEFAULT_FONTNAME "XtDefaultFont"
    offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
+  {XtNhighlightForeground, XtCHighlightForeground, XtRPixel, sizeof(Pixel),
+   offset(menu.highlight_foreground), XtRString, "XtDefaultForeground"},
+  {XtNhighlightBackground, XtCHighlightBackground, XtRPixel, sizeof(Pixel),
+   offset(menu.highlight_background), XtRImmediate, (XtPointer)-1},
   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
      offset(menu.margin), XtRImmediate, (XtPointer)1},
   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
@@ -142,6 +146,19 @@ #define DEFAULT_FONTNAME "XtDefaultFont"
   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
 
+  {XmNtopHighlightShadowColor, XmCTopHighlightShadowColor, XtRPixel,
+   sizeof (Pixel), offset (menu.top_highlight_shadow_color),
+   XtRImmediate, (XtPointer)-1},
+  {XmNbottomHighlightShadowColor, XmCBottomHighlightShadowColor, XtRPixel,
+   sizeof (Pixel), offset (menu.bottom_highlight_shadow_color),
+   XtRImmediate, (XtPointer)-1},
+  {XmNtopHighlightShadowPixmap, XmCTopHighlightShadowPixmap, XtRPixmap,
+   sizeof (Pixmap), offset (menu.top_highlight_shadow_pixmap),XtRImmediate,
+   (XtPointer)None},
+  {XmNbottomHighlightShadowPixmap, XmCBottomHighlightShadowPixmap, XtRPixmap,
+   sizeof (Pixmap), offset (menu.bottom_highlight_shadow_pixmap),XtRImmediate,
+   (XtPointer)None},
+
   {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
      offset(menu.open), XtRCallback, (XtPointer)NULL},
   {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
@@ -570,8 +587,7 @@ draw_arrow (XlwMenuWidget mw,
             int down_p)
 {
   Display *dpy = XtDisplay (mw);
-  GC top_gc = mw->menu.shadow_top_gc;
-  GC bottom_gc = mw->menu.shadow_bottom_gc;
+  GC top_gc, bottom_gc;
   int thickness = mw->menu.shadow_thickness;
   int height = width;
   XPoint pt[10];
@@ -584,10 +600,14 @@ draw_arrow (XlwMenuWidget mw,
 
   if (down_p)
     {
-      GC temp;
-      temp = top_gc;
-      top_gc = bottom_gc;
-      bottom_gc = temp;
+      /* XXX the following permutation is on purpose */
+      top_gc = mw->menu.highlight_shadow_bottom_gc;
+      bottom_gc = mw->menu.highlight_shadow_top_gc;
+    }
+  else
+    {
+      top_gc = mw->menu.shadow_top_gc;
+      bottom_gc = mw->menu.shadow_bottom_gc;
     }
 
   pt[0].x = x;
@@ -621,8 +641,12 @@ draw_arrow (XlwMenuWidget mw,
   XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
 }
 
-
-
+/*
+ * Generic draw shadow rectangle function.  It is used to draw menus, menu items
+ * and also toggle buttons.  When ERASE_P is true, it clears shadows.  DOWN_P is
+ * true when a menu item is pushed or a button toggled.  TOP_GC and BOTTOM_GC
+ * are the graphic contexts used to draw the top and bottom shadow respectively.
+ */
 static void
 draw_shadow_rectangle (XlwMenuWidget mw,
                        Window window,
@@ -631,14 +655,28 @@ draw_shadow_rectangle (XlwMenuWidget mw,
                        int width,
                        int height,
                        int erase_p,
-                       int down_p)
+                       int down_p,
+		       GC top_gc,
+		       GC bottom_gc)
 {
   Display *dpy = XtDisplay (mw);
-  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
-  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
   int thickness = !x && !y ? mw->menu.border_thickness : mw->menu.shadow_thickness;
   XPoint points [4];
 
+  /* Choose correct GC with a standard default if NULL */
+  if (erase_p)
+    {
+      top_gc = mw->menu.background_gc;
+      bottom_gc = mw->menu.background_gc;
+    }
+  else
+    {
+      if (top_gc == NULL)
+	top_gc = mw->menu.shadow_top_gc;
+      if (bottom_gc == NULL)
+	bottom_gc = mw->menu.shadow_bottom_gc;
+    }
+
   if (!erase_p && width == height && width == toggle_button_width (mw))
     {
       points [0].x = x;
@@ -654,6 +692,7 @@ draw_shadow_rectangle (XlwMenuWidget mw,
                     points, 4, Convex, CoordModeOrigin);
     }
 
+
   if (!erase_p && down_p)
     {
       GC temp;
@@ -662,6 +701,7 @@ draw_shadow_rectangle (XlwMenuWidget mw,
       bottom_gc = temp;
     }
 
+  /* Do draw (or erase) shadows */
   points [0].x = x;
   points [0].y = y;
   points [1].x = x + width;
@@ -709,14 +749,28 @@ draw_shadow_rhombus (XlwMenuWidget mw,
                      int width,
                      int height,
                      int erase_p,
-                     int down_p)
+                     int down_p,
+		     GC top_gc,
+		     GC bottom_gc)
 {
   Display *dpy = XtDisplay (mw);
-  GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
-  GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
   int thickness = mw->menu.shadow_thickness;
   XPoint points [4];
 
+  /* Choose correct GC with a standard default if NULL */
+  if (erase_p)
+    {
+      top_gc = mw->menu.background_gc;
+      bottom_gc = mw->menu.background_gc;
+    }
+  else
+    {
+      if (top_gc == NULL)
+	top_gc = mw->menu.shadow_top_gc;
+      if (bottom_gc == NULL)
+	top_gc = mw->menu.shadow_bottom_gc;
+    }
+
   if (!erase_p && width == height && width == radio_button_width (mw))
     {
       points [0].x = x;
@@ -784,15 +838,29 @@ draw_shadow_rhombus (XlwMenuWidget mw,
    toggle button is selected.  */
 
 static void
-draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
+draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
+	     int highlighted_p)
 {
   int width, height;
+  GC top_gc, bottom_gc;
+
+  if (highlighted_p)
+    {
+      top_gc = mw->menu.highlight_shadow_top_gc;
+      bottom_gc = mw->menu.highlight_shadow_bottom_gc;
+    }
+  else
+    {
+      top_gc = mw->menu.shadow_top_gc;
+      bottom_gc = mw->menu.shadow_bottom_gc;
+    }
 
   width = toggle_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
   y += (MENU_FONT_ASCENT (mw) - height) / 2;
-  draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
+  draw_shadow_rectangle (mw, window, x, y, width, height, False,
+			 selected_p, top_gc, bottom_gc);
 }

@@ -801,15 +869,29 @@ draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
    toggle button is selected.  */
 
 static void
-draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
+draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
+	    int highlighted_p)
 {
   int width, height;
+  GC top_gc, bottom_gc;
+
+  if (highlighted_p)
+    {
+      top_gc = mw->menu.highlight_shadow_top_gc;
+      bottom_gc = mw->menu.highlight_shadow_bottom_gc;
+    }
+  else
+    {
+      top_gc = mw->menu.shadow_top_gc;
+      bottom_gc = mw->menu.shadow_bottom_gc;
+    }
 
   width = radio_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
   y += (MENU_FONT_ASCENT (mw) - height) / 2;
-  draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
+  draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p,
+		       top_gc, bottom_gc);
 }

@@ -968,6 +1050,33 @@ separator_height (enum menu_separator separator)
     }
 }
 
+static void
+draw_highlight (XlwMenuWidget mw,
+		Window window,
+		int x,
+		int y,
+		int width,
+		int height)
+{
+  Display *dpy = XtDisplay (mw);
+  XPoint points [4];
+
+  points [0].x = x;
+  points [0].y = y;
+  points [1].x = x + width;
+  points [1].y = y;
+  points [2].x = x + width;
+  points [2].y = y + height;
+  points [3].x = x;
+  points [3].y = y + height;
+  XFillPolygon (dpy, window,
+		mw->menu.highlight_background_gc,
+		points, 4, Convex, CoordModeOrigin);
+
+  draw_shadow_rectangle(mw, window, x, y, width, height, False, False,
+			mw->menu.highlight_shadow_top_gc,
+			mw->menu.highlight_shadow_bottom_gc);
+}
 
 /* Display the menu item and increment where.x and where.y to show how large
    the menu item was.  */
@@ -983,7 +1092,6 @@ display_menu_item (XlwMenuWidget mw,
 {
   GC deco_gc;
   GC text_gc;
-  int font_height = MENU_FONT_HEIGHT (mw);
   int font_ascent = MENU_FONT_ASCENT (mw);
   int shadow = mw->menu.shadow_thickness;
   int margin = mw->menu.margin;
@@ -1032,12 +1140,21 @@ display_menu_item (XlwMenuWidget mw,
 
       /* pick the foreground and background GC. */
       if (val->enabled)
-	text_gc = mw->menu.foreground_gc;
+	if (highlighted_p)
+	  text_gc = mw->menu.highlight_foreground_gc;
+	else
+	  text_gc = mw->menu.foreground_gc;
       else
 	text_gc = mw->menu.disabled_gc;
       deco_gc = mw->menu.foreground_gc;
 #if defined USE_CAIRO || defined HAVE_XFT
-      xftfg = val->enabled ? &mw->menu.xft_fg : &mw->menu.xft_disabled_fg;
+      if (val->enabled)
+	if (highlighted_p)
+	  xftfg = &mw->menu.xft_highlight_fg;
+	else
+	  xftfg = &mw->menu.xft_fg;
+      else
+	xftfg = &mw->menu.xft_disabled_fg;
 #endif
 
       if (separator_p)
@@ -1048,8 +1165,11 @@ display_menu_item (XlwMenuWidget mw,
 	{
 	  int x_offset = x + h_spacing + shadow;
 	  char* display_string = resource_widget_value (mw, val);
-	  draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, True,
-				 False);
+	  /* Clears shadows and maybe highlight */
+	  draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
+				 True, False, NULL, NULL);
+	  if (highlighted_p)
+	    draw_highlight (mw, ws->pixmap, x, y, width, height);
 
 	  /* Deal with centering a menu title. */
 	  if (!horizontal_p && !val->contents && !val->call_data)
@@ -1095,10 +1215,10 @@ display_menu_item (XlwMenuWidget mw,
 	    {
 	      if (val->button_type == BUTTON_TYPE_TOGGLE)
 		draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
-			     val->selected);
+			     val->selected, highlighted_p);
 	      else if (val->button_type == BUTTON_TYPE_RADIO)
 		draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
-			    val->selected);
+			    val->selected, highlighted_p);
 
 	      if (val->contents)
 		{
@@ -1145,25 +1265,18 @@ display_menu_item (XlwMenuWidget mw,
 	    }
 	  else
 	    {
-	      XDrawRectangle (XtDisplay (mw), ws->pixmap,
-			      mw->menu.background_gc,
-			      x + shadow, y + shadow,
-			      label_width + h_spacing - 1,
-			      font_height + 2 * v_spacing - 1);
-	      draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
-				     True, False);
+	      /* If not highlighted, clears shadows for horizontal
+		 menu item */
+	      if (!highlighted_p)
+		draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
+				       True, False, NULL, NULL);
 	    }
 #ifdef USE_CAIRO
 	  if (ws->xft_draw)
 	    cairo_surface_flush (cairo_get_target (ws->xft_draw));
 #endif
-
-	  if (highlighted_p)
-	    draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
-				   False);
 	}
     }
-
   where->x += width;
   where->y += height;
 }
@@ -1257,7 +1370,7 @@ display_menu (XlwMenuWidget mw,
   if (!just_compute_p)
     {
       draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
-                             False, False);
+                             False, False, NULL, NULL);
       XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
                  mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
     }
@@ -1714,6 +1827,16 @@ #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color
   xgcv.foreground = mw->core.background_pixel;
   xgcv.background = mw->menu.foreground;
   mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
+
+  xgcv.foreground = mw->menu.highlight_foreground;
+  xgcv.background = (mw->menu.highlight_background == -1) ?
+    mw->core.background_pixel : mw->menu.highlight_background;
+  mw->menu.highlight_foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
+
+  xgcv.foreground = (mw->menu.highlight_background == -1) ?
+    mw->core.background_pixel : mw->menu.highlight_background;
+  xgcv.background = mw->menu.foreground;
+  mw->menu.highlight_background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
 }
 
 static void
@@ -1724,12 +1847,16 @@ release_drawing_gcs (XlwMenuWidget mw)
   XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
+  XtReleaseGC ((Widget) mw, mw->menu.highlight_foreground_gc);
+  XtReleaseGC ((Widget) mw, mw->menu.highlight_background_gc);
   /* let's get some segvs if we try to use these... */
   mw->menu.foreground_gc = (GC) -1;
   mw->menu.button_gc = (GC) -1;
   mw->menu.disabled_gc = (GC) -1;
   mw->menu.inactive_button_gc = (GC) -1;
   mw->menu.background_gc = (GC) -1;
+  mw->menu.highlight_foreground_gc = (GC) -1;
+  mw->menu.highlight_background_gc = (GC) -1;
 }
 
 #ifndef emacs
@@ -1738,29 +1865,34 @@ #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
 #endif
 
 static void
-make_shadow_gcs (XlwMenuWidget mw)
+compute_shadow_colors(XlwMenuWidget mw,
+		      Pixel *top_color,
+		      Pixel *bottom_color,
+		      Boolean *free_top_p,
+		      Boolean *free_bottom_p,
+		      Pixmap *top_pixmap,
+		      Pixmap *bottom_pixmap,
+		      Pixel fore_color,
+		      Pixel back_color)
 {
-  XGCValues xgcv;
-  unsigned long pm = 0;
   Display *dpy = XtDisplay ((Widget) mw);
   Screen *screen = XtScreen ((Widget) mw);
   Colormap cmap = mw->core.colormap;
   XColor topc, botc;
   int top_frobbed = 0, bottom_frobbed = 0;
 
-  mw->menu.free_top_shadow_color_p = 0;
-  mw->menu.free_bottom_shadow_color_p = 0;
+  *free_top_p = False;
+  *free_bottom_p = False;
 
-  if (mw->menu.top_shadow_color == -1)
-    mw->menu.top_shadow_color = mw->core.background_pixel;
+  if (*top_color == -1)
+    *top_color = back_color;
 
-  if (mw->menu.bottom_shadow_color == -1)
-    mw->menu.bottom_shadow_color = mw->menu.foreground;
+  if (*bottom_color == -1)
+    *bottom_color = fore_color;
 
-  if (mw->menu.top_shadow_color == mw->core.background_pixel ||
-      mw->menu.top_shadow_color == mw->menu.foreground)
+  if (*top_color == back_color || *top_color == fore_color)
     {
-      topc.pixel = mw->core.background_pixel;
+      topc.pixel = back_color;
 #ifdef emacs
       if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
 					    &topc.pixel,
@@ -1774,15 +1906,14 @@ make_shadow_gcs (XlwMenuWidget mw)
       if (XAllocColor (dpy, cmap, &topc))
 #endif
 	{
-	  mw->menu.top_shadow_color = topc.pixel;
-	  mw->menu.free_top_shadow_color_p = 1;
+	  *top_color = topc.pixel;
+	  *free_top_p = True;
 	  top_frobbed = 1;
 	}
     }
-  if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
-      mw->menu.bottom_shadow_color == mw->core.background_pixel)
+  if (*bottom_color == fore_color || *bottom_color == back_color)
     {
-      botc.pixel = mw->core.background_pixel;
+      botc.pixel = back_color;
 #ifdef emacs
       if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
 					    &botc.pixel,
@@ -1795,8 +1926,8 @@ make_shadow_gcs (XlwMenuWidget mw)
       if (XAllocColor (dpy, cmap, &botc))
 #endif
 	{
-	  mw->menu.bottom_shadow_color = botc.pixel;
-	  mw->menu.free_bottom_shadow_color_p = 1;
+	  *bottom_color = botc.pixel;
+	  *free_bottom_p = True;
 	  bottom_frobbed = 1;
 	}
     }
@@ -1805,52 +1936,76 @@ make_shadow_gcs (XlwMenuWidget mw)
     {
       if (topc.pixel == botc.pixel)
 	{
-	  if (botc.pixel == mw->menu.foreground)
+	  if (botc.pixel == fore_color)
 	    {
-	      if (mw->menu.free_top_shadow_color_p)
+	      if (*free_top_p)
 		{
-		  x_free_dpy_colors (dpy, screen, cmap,
-				     &mw->menu.top_shadow_color, 1);
-		  mw->menu.free_top_shadow_color_p = 0;
+		  x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
+		  *free_top_p = False;
 		}
-	      mw->menu.top_shadow_color = mw->core.background_pixel;
+	      *top_color = back_color;
 	    }
 	  else
 	    {
-	      if (mw->menu.free_bottom_shadow_color_p)
+	      if (*free_bottom_p)
 		{
-		  x_free_dpy_colors (dpy, screen, cmap,
-				     &mw->menu.bottom_shadow_color, 1);
-		  mw->menu.free_bottom_shadow_color_p = 0;
+		  x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
+		  *free_bottom_p = False;
 		}
-	      mw->menu.bottom_shadow_color = mw->menu.foreground;
+	      *bottom_color = fore_color;
 	    }
 	}
     }
 
-  if (!mw->menu.top_shadow_pixmap
-      && mw->menu.top_shadow_color == mw->core.background_pixel)
+  if (!*top_pixmap && *top_color == back_color)
     {
-      mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
-      if (mw->menu.free_top_shadow_color_p)
+      *top_pixmap = mw->menu.gray_pixmap;
+      if (*free_top_p)
 	{
-	  x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
-	  mw->menu.free_top_shadow_color_p = 0;
+	  x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
+	  *free_top_p = False;
 	}
-      mw->menu.top_shadow_color = mw->menu.foreground;
+      *top_color = fore_color;
     }
-  if (!mw->menu.bottom_shadow_pixmap
-      && mw->menu.bottom_shadow_color == mw->core.background_pixel)
+  if (!*bottom_pixmap && *bottom_color == back_color)
     {
-      mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
-      if (mw->menu.free_bottom_shadow_color_p)
+      *bottom_pixmap = mw->menu.gray_pixmap;
+      if (*free_bottom_p)
 	{
-	  x_free_dpy_colors (dpy, screen, cmap,
-			     &mw->menu.bottom_shadow_color, 1);
-	  mw->menu.free_bottom_shadow_color_p = 0;
+	  x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
+	  *free_bottom_p = False;
 	}
-      mw->menu.bottom_shadow_color = mw->menu.foreground;
+      *bottom_color = fore_color;
     }
+}
+
+static void
+make_shadow_gcs (XlwMenuWidget mw)
+{
+  XGCValues xgcv;
+  unsigned long pm = 0;
+
+  /* Normal shadows */
+  compute_shadow_colors(mw,
+			&(mw->menu.top_shadow_color),
+			&(mw->menu.bottom_shadow_color),
+			&(mw->menu.free_top_shadow_color_p),
+			&(mw->menu.free_bottom_shadow_color_p),
+			&(mw->menu.top_shadow_pixmap),
+			&(mw->menu.bottom_shadow_pixmap),
+			mw->menu.foreground,
+			mw->core.background_pixel);
+
+  /* Highlight shadows */
+  compute_shadow_colors(mw,
+			&(mw->menu.top_highlight_shadow_color),
+			&(mw->menu.bottom_highlight_shadow_color),
+			&(mw->menu.free_top_highlight_shadow_color_p),
+			&(mw->menu.free_bottom_highlight_shadow_color_p),
+			&(mw->menu.top_highlight_shadow_pixmap),
+			&(mw->menu.bottom_highlight_shadow_pixmap),
+			mw->menu.highlight_foreground,
+			mw->menu.highlight_background);
 
   xgcv.fill_style = FillStippled;
   xgcv.foreground = mw->menu.top_shadow_color;
@@ -1862,6 +2017,16 @@ make_shadow_gcs (XlwMenuWidget mw)
   xgcv.stipple = mw->menu.bottom_shadow_pixmap;
   pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
   mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
+
+  xgcv.foreground = mw->menu.top_highlight_shadow_color;
+  xgcv.stipple = mw->menu.top_highlight_shadow_pixmap;
+  pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
+  mw->menu.highlight_shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
+
+  xgcv.foreground = mw->menu.bottom_highlight_shadow_color;
+  xgcv.stipple = mw->menu.bottom_highlight_shadow_pixmap;
+  pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
+  mw->menu.highlight_shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
 }

@@ -2038,12 +2203,14 @@ XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
 #if defined USE_CAIRO || defined HAVE_XFT
   if (mw->menu.xft_font)
     {
-      XColor colors[3];
+      XColor colors[4];
       colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
       colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
       colors[2].pixel = mw->menu.xft_disabled_fg.pixel
         = mw->menu.disabled_foreground;
-      XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
+      colors[3].pixel = mw->menu.xft_highlight_fg.pixel
+	= mw->menu.highlight_foreground;
+      XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 4);
       mw->menu.xft_fg.color.alpha = 0xFFFF;
       mw->menu.xft_fg.color.red = colors[0].red;
       mw->menu.xft_fg.color.green = colors[0].green;
@@ -2056,6 +2223,10 @@ XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
       mw->menu.xft_disabled_fg.color.red = colors[2].red;
       mw->menu.xft_disabled_fg.color.green = colors[2].green;
       mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
+      mw->menu.xft_highlight_fg.color.alpha = 0xFFFF;
+      mw->menu.xft_highlight_fg.color.red = colors[3].red;
+      mw->menu.xft_highlight_fg.color.green = colors[3].green;
+      mw->menu.xft_highlight_fg.color.blue = colors[3].blue;
     }
 #endif
 }
diff --git a/lwlib/xlwmenu.h b/lwlib/xlwmenu.h
index 7f4bf35939..f68d913b5a 100644
--- a/lwlib/xlwmenu.h
+++ b/lwlib/xlwmenu.h
@@ -58,6 +58,10 @@ #define XtNallowResize "allowResize"
 #define XtCAllowResize "AllowResize"
 #define XtNborderThickness "borderThickness"
 #define XtCBorderThickness "BorderThickness"
+#define XtNhighlightForeground "highlightForeground"
+#define XtCHighlightForeground "HighlightForeground"
+#define XtNhighlightBackground "highlightBackground"
+#define XtCHighlightBackground "HighlightBackground"
 
 /* Motif-compatible resource names */
 #define XmNshadowThickness	"shadowThickness"
@@ -70,6 +74,16 @@ #define XmNtopShadowPixmap	"topShadowPixmap"
 #define XmCTopShadowPixmap	"TopShadowPixmap"
 #define XmNbottomShadowPixmap	"bottomShadowPixmap"
 #define XmCBottomShadowPixmap	"BottomShadowPixmap"
+
+#define XmNtopHighlightShadowColor	"topHighlightShadowColor"
+#define XmCTopHighlightShadowColor	"TopHighlightShadowColor"
+#define XmNbottomHighlightShadowColor	"bottomHighlightShadowColor"
+#define XmCBottomHighlightShadowColor	"BottomHighlightShadowColor"
+#define XmNtopHighlightShadowPixmap	"topHighlightShadowPixmap"
+#define XmCTopHighlightShadowPixmap	"TopHighlightShadowPixmap"
+#define XmNbottomHighlightShadowPixmap	"bottomHighlightShadowPixmap"
+#define XmCBottomHighlightShadowPixmap	"BottomHighlightShadowPixmap"
+
 #define XmRHorizontalDimension	"HorizontalDimension"
 
 typedef struct _XlwMenuRec *XlwMenuWidget;
diff --git a/lwlib/xlwmenuP.h b/lwlib/xlwmenuP.h
index 455ecdbce0..c314eb3e91 100644
--- a/lwlib/xlwmenuP.h
+++ b/lwlib/xlwmenuP.h
@@ -63,13 +63,15 @@ #define _XlwMenuP_h
 #if defined USE_CAIRO || defined HAVE_XFT
   int           default_face;
   XftFont*      xft_font;
-  XftColor      xft_fg, xft_bg, xft_disabled_fg;
+  XftColor      xft_fg, xft_bg, xft_disabled_fg, xft_highlight_fg;
 #endif
   String	fontName;
   XFontStruct*	font;
   Pixel		foreground;
   Pixel		disabled_foreground;
   Pixel		button_foreground;
+  Pixel		highlight_foreground;
+  Pixel		highlight_background;
   Dimension	margin;
   Dimension	horizontal_spacing;
   Dimension	vertical_spacing;
@@ -80,6 +82,10 @@ #define _XlwMenuP_h
   Pixel 	bottom_shadow_color;
   Pixmap	top_shadow_pixmap;
   Pixmap	bottom_shadow_pixmap;
+  Pixel 	top_highlight_shadow_color;
+  Pixel 	bottom_highlight_shadow_color;
+  Pixmap	top_highlight_shadow_pixmap;
+  Pixmap	bottom_highlight_shadow_pixmap;
   Cursor	cursor_shape;
   XtCallbackList	open;
   XtCallbackList	select, highlight;
@@ -88,8 +94,10 @@ #define _XlwMenuP_h
   int		horizontal;
 
   /* True means top_shadow_color and/or bottom_shadow_color must be freed.  */
-  bool_bf free_top_shadow_color_p : 1;
-  bool_bf free_bottom_shadow_color_p : 1;
+  Boolean free_top_shadow_color_p;
+  Boolean free_bottom_shadow_color_p;
+  Boolean free_top_highlight_shadow_color_p;
+  Boolean free_bottom_highlight_shadow_color_p;
 
   /* State of the XlwMenu */
   int                   top_depth;
@@ -112,9 +120,13 @@ #define _XlwMenuP_h
   GC			button_gc;
   GC			background_gc;
   GC			disabled_gc;
+  GC			highlight_foreground_gc;
+  GC			highlight_background_gc;
   GC			inactive_button_gc;
   GC			shadow_top_gc;
   GC			shadow_bottom_gc;
+  GC			highlight_shadow_top_gc;
+  GC			highlight_shadow_bottom_gc;
   Cursor		cursor;
   Boolean		popped_up;
   Pixmap		gray_pixmap;
-- 
2.36.1

In GNU Emacs 29.0.50 (build 1, x86_64-unknown-openbsd7.1, X toolkit, cairo version 1.17.6, Xaw scroll bars)
 of 2022-07-13 built on elite.giraud
Repository revision: f51e1dbd3174d727a12e673e0b820b4cf35e7046
Repository branch: mgi/ui3
Windowing system distributor 'The X.Org Foundation', version 11.0.12101003
System Description: OpenBSD elite.giraud 7.1 GENERIC.MP#579 amd64

Configured using:
 'configure --prefix=/home/manuel/emacs --bindir=/home/manuel/bin
 --with-x-toolkit=athena --without-sound --without-compress-install
 CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib'

Configured features:
CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG JSON
LCMS2 LIBOTF LIBXML2 MODULES NOTIFY KQUEUE PDUMPER PNG RSVG SQLITE3
THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM LUCID
ZLIB

Important settings:
  value of $LC_ALL: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  paredit-mode: t
  icomplete-mode: t
  display-time-mode: t
  shell-dirtrack-mode: t
  global-so-long-mode: t
  repeat-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/manuel/.emacs.d/elpa/transient-20220527.2213/transient hides /home/manuel/emacs/share/emacs/29.0.50/lisp/transient

Features:
(shadow sort mail-extr emacsbug vc-git whitespace magit-patch
magit-extras face-remap magit-bookmark magit-submodule magit-obsolete
magit-blame magit-stash magit-reflog magit-bisect magit-push magit-pull
magit-fetch magit-clone magit-remote magit-commit magit-sequence
magit-notes magit-worktree magit-tag magit-merge magit-branch
magit-reset magit-files magit-refs magit-status magit magit-repos
magit-apply magit-wip magit-log which-func imenu magit-diff smerge-mode
diff git-commit log-edit pcvs-util add-log magit-core magit-autorevert
autorevert magit-margin magit-transient magit-process with-editor
magit-mode transient magit-git magit-base magit-section dash compat-27
compat-26 compat pulse vc-hg diff-mode vc-dispatcher vc-svn conf-mode
facemenu paredit edmacro icomplete time battery exwm-randr xcb-randr
exwm-config exwm exwm-input xcb-keysyms xcb-xkb exwm-manage
exwm-floating xcb-cursor xcb-render exwm-layout exwm-workspace exwm-core
xcb-ewmh xcb-icccm xcb xcb-xproto xcb-types xcb-debug kmacro server
stimmung-themes modus-operandi-theme modus-themes osm bookmark mingus
libmpdee transmission diary-lib diary-loaddefs color calc-bin calc-ext
calc calc-loaddefs rect calc-macs w3m-load mu4e mu4e-org mu4e-main
mu4e-view mu4e-view-gnus mu4e-view-common mu4e-headers mu4e-compose
mu4e-context mu4e-draft mu4e-actions ido rfc2368 smtpmail mu4e-mark
mu4e-proc mu4e-utils doc-view filenotify jka-compr image-mode exif
mu4e-lists mu4e-message flow-fill mule-util hl-line mu4e-vars mu4e-meta
supercite regi ebdb-message ebdb-gnus gnus-msg gnus-art mm-uu mml2015
mm-view mml-smime smime gnutls dig gnus-sum shr pixel-fill kinsoku
url-file url-dired svg dom gnus-group gnus-undo gnus-start gnus-dbus
gnus-cloud nnimap nnmail mail-source utf7 netrc nnoo gnus-spec gnus-int
gnus-range message sendmail yank-media puny rfc822 mml mml-sec epa
derived epg rfc6068 epg-config mm-decode mm-bodies mm-encode mail-parse
rfc2231 rfc2047 rfc2045 ietf-drums gmm-utils mailheader gnus-win gnus
nnheader gnus-util mail-utils range mm-util mail-prsvr ebdb-mua ebdb-com
crm ebdb-format ebdb mailabbrev eieio-opt cl-extra help-mode speedbar
ezimage dframe eieio-base pcase timezone org ob ob-tangle ob-ref ob-lob
ob-table ob-exp org-macro org-footnote org-src ob-comint org-pcomplete
org-list org-faces org-entities org-version ob-emacs-lisp ob-core
ob-eval org-table oc-basic bibtex ol rx org-keys oc org-compat org-macs
org-loaddefs find-func cal-menu calendar cal-loaddefs visual-basic-mode
cl web-mode disp-table erlang-start smart-tabs-mode skeleton cc-mode
cc-fonts cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars
cc-defs slime-asdf grep slime-tramp tramp tramp-loaddefs trampver
tramp-integration cus-edit cus-load wid-edit files-x tramp-compat shell
pcomplete parse-time iso8601 time-date ls-lisp format-spec slime-fancy
slime-indentation slime-cl-indent cl-indent slime-trace-dialog
slime-fontifying-fu slime-package-fu slime-references
slime-compiler-notes-tree advice slime-scratch slime-presentations
bridge slime-macrostep macrostep slime-mdot-fu slime-enclosing-context
slime-fuzzy slime-fancy-trace slime-fancy-inspector slime-c-p-c
slime-editing-commands slime-autodoc slime-repl slime-parse slime
compile text-property-search etags fileloop generator xref project
arc-mode archive-mode noutline outline pp comint ansi-color ring
hyperspec thingatpt slime-autoloads dired-aux dired-x dired
dired-loaddefs so-long notifications dbus xml repeat easy-mmode tex-site
hyperbole-autoloads magit-autoloads git-commit-autoloads
magit-section-autoloads dash-autoloads rust-mode-autoloads
stimmung-themes-autoloads with-editor-autoloads info compat-autoloads
package browse-url url url-proxy url-privacy url-expand url-methods
url-history url-cookie generate-lisp-file url-domsuf url-util mailcap
url-handlers url-parse auth-source cl-seq eieio eieio-core cl-macs
password-cache json subr-x map byte-opt gv bytecomp byte-compile cconv
url-vars cl-loaddefs cl-lib rmc iso-transl tooltip eldoc paren electric
uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel
term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
faces cus-face macroexp files window text-properties overlay sha1 md5
base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind kqueue lcms2
dynamic-setting system-font-setting font-render-setting cairo x-toolkit
xinput2 x multi-tty make-network-process emacs)

Memory information:
((conses 16 563139 59476)
 (symbols 48 54905 4)
 (strings 32 171200 8786)
 (string-bytes 1 5703800)
 (vectors 16 99692)
 (vector-slots 8 1289985 58168)
 (floats 8 522 566)
 (intervals 56 2981 2094)
 (buffers 992 16))

-- 
Manuel Giraud