Subject: Re: bug#64440: 30.0.50; [PATCH] Highlight on non toolkit menu bar items

From: Manuel Giraud <[email-masked]>
Date: Tue, 04 Jul 2023 18:14:02 +0200

Hi,

Here is a new set of patches with two more on top of the previous ones
(ie. Number 1 and 2 should be the same as before).

Number 3 sets the default mouse cursor to be an arrow on the default
menu bar area.  Number 4 fixes a flickering I had while moving the mouse
pointer *into* a menu bar entry.

Best regards,
-- 
Manuel Giraud
>From ceb818090de83fe216af3e9b6bcc198eba9188e3 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Sat, 1 Jul 2023 21:19:06 +0200
Subject: [PATCH 1/4] Possibility to get enter event from menu_bar window

---
 src/haikuterm.c |  8 ++++----
 src/keyboard.c  |  2 +-
 src/msdos.c     |  2 +-
 src/pgtkterm.c  |  4 ++--
 src/w32inevt.c  |  2 +-
 src/w32term.c   |  8 ++++----
 src/window.c    | 20 ++++++++++++++++++--
 src/window.h    |  2 +-
 src/xdisp.c     |  4 ++--
 src/xterm.c     | 17 +++++++++--------
 10 files changed, 43 insertions(+), 26 deletions(-)

diff --git a/src/haikuterm.c b/src/haikuterm.c
index ed28a806ff2..0af9aa746a9 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -3472,7 +3472,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
 		if (!NILP (Vmouse_autoselect_window))
 		  {
 		    static Lisp_Object last_mouse_window;
-		    Lisp_Object window = window_from_coordinates (f, b->x, b->y, 0, 0, 0);
+		    Lisp_Object window = window_from_coordinates (f, b->x, b->y, 0, 0, 0, 0);
 
 		    if (WINDOWP (window)
 			&& !EQ (window, last_mouse_window)
@@ -3555,7 +3555,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
 		int x = b->x;
 		int y = b->y;
 
-		window = window_from_coordinates (f, x, y, 0, true, true);
+		window = window_from_coordinates (f, x, y, 0, true, true, true);
 		tab_bar_p = EQ (window, f->tab_bar_window);
 
 		if (tab_bar_p)
@@ -3573,7 +3573,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
 		int x = b->x;
 		int y = b->y;
 
-		window = window_from_coordinates (f, x, y, 0, true, true);
+		window = window_from_coordinates (f, x, y, 0, true, true, true);
 		tool_bar_p = (EQ (window, f->tool_bar_window)
 			      && (type != BUTTON_UP
 				  || f->last_tool_bar_item != -1));
@@ -3834,7 +3834,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
 
 	    BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
 
-	    wheel_window = window_from_coordinates (f, x, y, 0, false, false);
+	    wheel_window = window_from_coordinates (f, x, y, 0, false, false, false);
 
 	    if (NILP (wheel_window))
 	      {
diff --git a/src/keyboard.c b/src/keyboard.c
index b61b1766856..4cc0c70b11e 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -5414,7 +5414,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y,
   int xret = 0, yret = 0;
   /* The window or frame under frame pixel coordinates (x,y)  */
   Lisp_Object window_or_frame = f
-    ? window_from_coordinates (f, mx, my, &part, true, true)
+    ? window_from_coordinates (f, mx, my, &part, true, true, true)
     : Qnil;
 #ifdef HAVE_WINDOW_SYSTEM
   bool tool_bar_p = false;
diff --git a/src/msdos.c b/src/msdos.c
index 75a39045cee..6e779ffb2e2 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -2655,7 +2655,7 @@ dos_rawgetc (void)
 	      static Lisp_Object last_mouse_window;
 
 	      mouse_window = window_from_coordinates
-		(SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
+		(SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0, 0);
 	      /* A window will be selected only when it is not
 		 selected now, and the last mouse movement event was
 		 not in it.  A minibuffer window will be selected iff
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index dc2d6477bb5..788d4a7bdfb 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -5891,7 +5891,7 @@ motion_notify_event (GtkWidget *widget, GdkEvent *event,
 	{
 	  static Lisp_Object last_mouse_window;
 	  Lisp_Object window = window_from_coordinates
-	    (f, event->motion.x, event->motion.y, 0, false, false);
+	    (f, event->motion.x, event->motion.y, 0, false, false, false);
 
 	  /* A window will be autoselected only when it is not
 	     selected now and the last mouse movement event was
@@ -6044,7 +6044,7 @@ button_event (GtkWidget *widget, GdkEvent *event,
 	  int x = event->button.x;
 	  int y = event->button.y;
 
-	  window = window_from_coordinates (f, x, y, 0, true, true);
+	  window = window_from_coordinates (f, x, y, 0, true, true, true);
 	  tab_bar_p = EQ (window, f->tab_bar_window);
 
 	  if (tab_bar_p)
diff --git a/src/w32inevt.c b/src/w32inevt.c
index 29717954cfd..630a9f4e5fb 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -496,7 +496,7 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
 	    if (!NILP (Vmouse_autoselect_window))
 	      {
 		Lisp_Object mouse_window = window_from_coordinates (f, mx, my,
-								    0, 0, 0);
+								    0, 0, 0, 0);
 		/* A window will be selected only when it is not
 		   selected now, and the last mouse movement event was
 		   not in it.  A minibuffer window will be selected iff
diff --git a/src/w32term.c b/src/w32term.c
index 57dc6b465e4..9c5a298a645 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3376,7 +3376,7 @@ w32_construct_mouse_wheel (struct input_event *result, W32Msg *msg,
       if (w32_wheel_scroll_lines == UINT_MAX)
 	{
 	  Lisp_Object window = window_from_coordinates (f, p.x, p.y, NULL,
-							false, false);
+							false, false, false);
 	  if (!WINDOWP (window))
 	    {
 	      result->kind = NO_EVENT;
@@ -5335,7 +5335,7 @@ w32_read_socket (struct terminal *terminal,
 		{
 		  static Lisp_Object last_mouse_window;
 		  Lisp_Object window = window_from_coordinates
-		    (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0);
+		    (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0, 0);
 
 		  /* Window will be selected only when it is not
 		     selected now and last mouse movement event was
@@ -5407,7 +5407,7 @@ w32_read_socket (struct terminal *terminal,
 		    int x = XFIXNAT (inev.x);
 		    int y = XFIXNAT (inev.y);
 
-                    window = window_from_coordinates (f, x, y, 0, 1, 1);
+                    window = window_from_coordinates (f, x, y, 0, 1, 1, 1);
 
                     if (EQ (window, f->tab_bar_window))
                       {
@@ -5435,7 +5435,7 @@ w32_read_socket (struct terminal *terminal,
 		    int x = XFIXNAT (inev.x);
 		    int y = XFIXNAT (inev.y);
 
-                    window = window_from_coordinates (f, x, y, 0, 1, 1);
+                    window = window_from_coordinates (f, x, y, 0, 1, 1, 1);
 
                     if (EQ (window, f->tool_bar_window)
 			/* Make sure the tool bar was previously
diff --git a/src/window.c b/src/window.c
index 2a0c62f5d53..af7dcfdd423 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1680,7 +1680,8 @@ check_window_containing (struct window *w, void *user_data)
 
 Lisp_Object
 window_from_coordinates (struct frame *f, int x, int y,
-			 enum window_part *part, bool tab_bar_p, bool tool_bar_p)
+			 enum window_part *part, bool menu_bar_p,
+			 bool tab_bar_p, bool tool_bar_p)
 {
   Lisp_Object window;
   struct check_window_data cw;
@@ -1693,6 +1694,21 @@ window_from_coordinates (struct frame *f, int x, int y,
   cw.window = &window, cw.x = x, cw.y = y; cw.part = part;
   foreach_window (f, check_window_containing, &cw);
 
+#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_MENU_BAR)
+  /* If not found above, see if it's in the menu bar window, if a menu
+     bar exists.  */
+  if (NILP (window)
+      && menu_bar_p
+      && WINDOWP (f->menu_bar_window)
+      && WINDOW_TOTAL_LINES (XWINDOW (f->menu_bar_window)) > 0
+      && (coordinates_in_window (XWINDOW (f->menu_bar_window), x, y)
+	  != ON_NOTHING))
+    {
+      *part = ON_TEXT;
+      window = f->menu_bar_window;
+    }
+#endif
+
 #if defined (HAVE_WINDOW_SYSTEM)
   /* If not found above, see if it's in the tab bar window, if a tab
      bar exists.  */
@@ -1746,7 +1762,7 @@ DEFUN ("window-at", Fwindow_at, Swindow_at, 2, 3, 0,
 				   + FRAME_INTERNAL_BORDER_WIDTH (f)),
 				  (FRAME_PIXEL_Y_FROM_CANON_Y (f, y)
 				   + FRAME_INTERNAL_BORDER_WIDTH (f)),
-				  0, false, false);
+				  0, false, false, false);
 }
 
 ptrdiff_t
diff --git a/src/window.h b/src/window.h
index 2f793ebe438..4f4a0a79216 100644
--- a/src/window.h
+++ b/src/window.h
@@ -1092,7 +1092,7 @@ #define WINDOW_TEXT_TO_FRAME_PIXEL_X(W, X)	\
 
 extern Lisp_Object make_window (void);
 extern Lisp_Object window_from_coordinates (struct frame *, int, int,
-                                            enum window_part *, bool, bool);
+                                            enum window_part *, bool, bool, bool);
 extern void resize_frame_windows (struct frame *, int, bool);
 extern void restore_window_configuration (Lisp_Object);
 extern void delete_all_child_windows (Lisp_Object);
diff --git a/src/xdisp.c b/src/xdisp.c
index 85ece901111..4c02a217cef 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2739,7 +2739,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
       goto virtual_glyph;
     }
   else if (!f->glyphs_initialized_p
-	   || (window = window_from_coordinates (f, gx, gy, &part, false, false),
+	   || (window = window_from_coordinates (f, gx, gy, &part, false, false, false),
 	       NILP (window)))
     {
       width = FRAME_SMALLEST_CHAR_WIDTH (f);
@@ -35124,7 +35124,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
     return;
 
   /* Which window is that in?  */
-  window = window_from_coordinates (f, x, y, &part, true, true);
+  window = window_from_coordinates (f, x, y, &part, true, true, true);
 
   /* If displaying active text in another window, clear that.  */
   if (! EQ (window, hlinfo->mouse_face_window)
diff --git a/src/xterm.c b/src/xterm.c
index f84eaeb8cbd..e58e6276aae 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -20925,7 +20925,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 		  }
 
 		Lisp_Object window = window_from_coordinates
-		  (f, xmotion.x, xmotion.y, 0, false, false);
+		  (f, xmotion.x, xmotion.y, 0, false, false, false);
 
 		/* A window will be autoselected only when it is not
 		   selected now and the last mouse movement event was
@@ -21656,7 +21656,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 int x = event->xbutton.x;
                 int y = event->xbutton.y;
 
-                window = window_from_coordinates (f, x, y, 0, true, true);
+                window = window_from_coordinates (f, x, y, 0, true, true, true);
                 tab_bar_p = EQ (window, f->tab_bar_window);
 
                 if (tab_bar_p)
@@ -21677,7 +21677,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 int x = event->xbutton.x;
                 int y = event->xbutton.y;
 
-                window = window_from_coordinates (f, x, y, 0, true, true);
+                window = window_from_coordinates (f, x, y, 0, true, true, true);
                 tool_bar_p = (EQ (window, f->tool_bar_window)
 			      && (event->xbutton.type != ButtonRelease
 				  || f->last_tool_bar_item != -1));
@@ -22398,7 +22398,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 			    continue;
 
 			  window = window_from_coordinates (f, real_x, real_y, NULL,
-							    false, false);
+							    false, false, false);
 
 			  if (WINDOWP (window))
 			    scroll_height = XWINDOW (window)->pixel_height;
@@ -22841,7 +22841,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 			  || !NILP (focus_follows_mouse)))
 		    {
 		      static Lisp_Object last_mouse_window;
-		      Lisp_Object window = window_from_coordinates (f, ev.x, ev.y, 0, false, false);
+		      Lisp_Object window = window_from_coordinates (f, ev.x, ev.y, 0, false, false,
+								    false);
 
 		      /* A window will be autoselected only when it is not
 			 selected now and the last mouse movement event was
@@ -23419,7 +23420,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 		      int x = bv.x;
 		      int y = bv.y;
 
-		      window = window_from_coordinates (f, x, y, 0, true, true);
+		      window = window_from_coordinates (f, x, y, 0, true, true, true);
 		      tab_bar_p = EQ (window, f->tab_bar_window);
 
 		      if (tab_bar_p)
@@ -23440,7 +23441,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 		      int x = bv.x;
 		      int y = bv.y;
 
-		      window = window_from_coordinates (f, x, y, 0, true, true);
+		      window = window_from_coordinates (f, x, y, 0, true, true, true);
 		      /* Ignore button release events if the mouse
 			 wasn't previously pressed on the tool bar.
 			 We do this because otherwise selecting some
@@ -24300,7 +24301,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 		  int x = xev->event_x;
 		  int y = xev->event_y;
 
-		  window = window_from_coordinates (f, x, y, 0, true, true);
+		  window = window_from_coordinates (f, x, y, 0, true, true, true);
 		  /* Ignore button release events if the mouse
 		     wasn't previously pressed on the tool bar.
 		     We do this because otherwise selecting some
-- 
2.40.0

>From d5ddf8e04d06730917f94cb7d2fcb026c4437788 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Mon, 3 Jul 2023 17:35:06 +0200
Subject: [PATCH 2/4] Highlight on non toolkit menu bar items

* src/xdisp.c (get_menu_bar_item, note_menu_bar_highlight): New
functions to highlight item in the menu-bar.
(note_mouse_highlight): Use it.
---
 src/xdisp.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/src/xdisp.c b/src/xdisp.c
index 4c02a217cef..25b33e3a8c4 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13859,6 +13859,122 @@ update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run)
   return hooks_run;
 }
 
+/* Get information about the menu-bar item at position X/Y on frame F.
+   Return menu-bar's item char position in H_START/H_END and pixel
+   position in X_START/X_END.  Value is
+
+   -1	if X/Y is not on a menu-bar item
+   0	if X/Y is on the same item that was highlighted before.
+   1	otherwise.  */
+
+static int
+get_menu_bar_item (struct frame *f, int x, int y, int *h_start, int *h_end,
+		   int *x_start, int *x_end, int *vpos)
+{
+  Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+  struct window *w = XWINDOW (f->menu_bar_window);
+  struct glyph_row *row;
+  int dummy;
+  Lisp_Object items;
+  int i;
+
+  /* Find glyph's hpos and vpos under X/Y.  */
+  if (x_y_to_hpos_vpos (w, x, y, h_start, vpos, NULL, NULL, &dummy) == NULL)
+    return -1;
+
+  /* Compute h_start and h_end for this menu bar item.  */
+  items = FRAME_MENU_BAR_ITEMS (f);
+  for (i = 0; i < ASIZE (items); i += 4)
+    {
+      Lisp_Object pos, string;
+      string = AREF (items, i + 1);
+      pos = AREF (items, i + 3);
+      if (NILP (string))
+	return -1;
+      if (*h_start >= XFIXNUM (pos)
+	  && *h_start < XFIXNUM (pos) + SCHARS (string))
+	{
+	  *h_start = XFIXNUM (pos);
+	  *h_end = *h_start + SCHARS (string);
+	  break;
+	}
+    }
+
+  /* Convert to pixels bounds.  */
+  row = MATRIX_ROW (w->current_matrix, *vpos);
+  *x_start = 0;
+  for (i = 0; i < *h_start; ++i)
+    *x_start += row->glyphs[TEXT_AREA][i].pixel_width;
+
+  *x_end = *x_start;
+  for (i = *h_start; i < *h_end; ++i)
+    *x_end += row->glyphs[TEXT_AREA][i].pixel_width;
+
+  /* Is mouse on the highlighted item?  */
+  if (EQ (f->menu_bar_window, hlinfo->mouse_face_window)
+      && *vpos >= hlinfo->mouse_face_beg_row
+      && *vpos <= hlinfo->mouse_face_end_row
+      && (*vpos > hlinfo->mouse_face_beg_row
+	  || *h_start >= hlinfo->mouse_face_beg_col)
+      && (*vpos < hlinfo->mouse_face_end_row
+	  || *h_end < hlinfo->mouse_face_end_col
+	  || hlinfo->mouse_face_past_end))
+    return 0;
+
+  return 1;
+}
+
+/* Possibly highlight a menu-bar item on frame F when mouse moves to
+   menu-bar window-relative coordinates X/Y.  Called from
+   note_mouse_highlight.  */
+
+static void
+note_menu_bar_highlight (struct frame *f, int x, int y)
+{
+  Lisp_Object window = f->menu_bar_window;
+  Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+  int h_start, h_end, vpos, x_start, x_end;
+  int rc;
+
+  /* Function note_mouse_highlight is called with negative X/Y
+     values when mouse moves outside of the frame.  */
+  if (x <= 0 || y <= 0)
+    {
+      clear_mouse_face (hlinfo);
+      return;
+    }
+
+  h_start = h_end = 0;
+  rc = get_menu_bar_item (f, x, y, &h_start, &h_end, &x_start, &x_end, &vpos);
+  if (rc < 0)
+    {
+      /* Not on menu-bar item.  */
+      clear_mouse_face (hlinfo);
+      return;
+    }
+  else if (rc == 0)
+    /* On same menu-bar item as before.  */
+    return;
+
+  if (!NILP (Vmouse_highlight))
+    {
+      /* Record this as the current active region.  */
+      hlinfo->mouse_face_beg_col = h_start;
+      hlinfo->mouse_face_beg_row = vpos;
+      hlinfo->mouse_face_beg_x = x_start;
+      hlinfo->mouse_face_past_end = false;
+
+      hlinfo->mouse_face_end_col = h_end;
+      hlinfo->mouse_face_end_row = vpos;
+      hlinfo->mouse_face_end_x = x_end;
+      hlinfo->mouse_face_window = window;
+      hlinfo->mouse_face_face_id = MENU_FACE_ID;
+
+      /* Display it as active.  */
+      show_mouse_face (hlinfo, DRAW_MOUSE_FACE);
+    }
+}
+
 
 
 /***********************************************************************
@@ -35223,6 +35339,16 @@ note_mouse_highlight (struct frame *f, int x, int y)
   w = XWINDOW (window);
   frame_to_window_pixel_xy (w, &x, &y);
 
+#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_MENU_BAR)
+  /* Handle menu-bar window differently since it doesn't display a
+     buffer.  */
+  if (EQ (window, f->menu_bar_window))
+    {
+      note_menu_bar_highlight (f, x, y);
+      return;
+    }
+#endif
+
 #if defined (HAVE_WINDOW_SYSTEM)
   /* Handle tab-bar window differently since it doesn't display a
      buffer.  */
-- 
2.40.0

>From 08279c0754c49b567fda0adedc3ecfd4e11a7ec5 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Tue, 4 Jul 2023 17:48:42 +0200
Subject: [PATCH 3/4] nontext cursor on the menu bar by default

* src/xdisp.c (show_mouse_face): Arrow for DRAW_NORMAL_TEXT on the
menu bar
---
 src/xdisp.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/xdisp.c b/src/xdisp.c
index 25b33e3a8c4..e1c4c9ee7b9 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -33878,6 +33878,9 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw)
   if (FRAME_WINDOW_P (f) && NILP (track_mouse))
     {
       if (draw == DRAW_NORMAL_TEXT
+#ifndef HAVE_EXT_MENU_BAR
+	  && !EQ (hlinfo->mouse_face_window, f->menu_bar_window)
+#endif
 #ifndef HAVE_EXT_TOOL_BAR
 	  && !EQ (hlinfo->mouse_face_window, f->tool_bar_window)
 #endif
-- 
2.40.0

>From 159ae74ed2ec452902913690f6d462767aa3a96d Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Tue, 4 Jul 2023 17:57:39 +0200
Subject: [PATCH 4/4] Avoid mouse cursor flicker

* src/dispextern.h (Mouse_HLInfo): Introduce a mouse_cursor_update
set to true by default.
* src/xdisp.c (show_mouse_face): Take it into account.
(note_menu_bar_highlight, note_tab_bar_highlight)
(note_tool_bar_highlight): Don't update the mouse cursor from
here.
---
 src/dispextern.h |  5 +++++
 src/xdisp.c      | 13 ++++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index ece128949f5..ed03a7c244e 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2876,6 +2876,10 @@ #define PRODUCE_GLYPHS(IT)                              \
 
   /* True means that the mouse highlight should not be shown.  */
   bool_bf mouse_face_hidden : 1;
+
+  /* True means that the mouse highlight should update the mouse
+     cursor.  */
+  bool_bf mouse_cursor_update : 1;
 } Mouse_HLInfo;
 
 INLINE void
@@ -2892,6 +2896,7 @@ reset_mouse_highlight (Mouse_HLInfo *hlinfo)
   hlinfo->mouse_face_past_end = false;
   hlinfo->mouse_face_hidden = false;
   hlinfo->mouse_face_defer = false;
+  hlinfo->mouse_cursor_update = true;
 }
 
 /***********************************************************************
diff --git a/src/xdisp.c b/src/xdisp.c
index e1c4c9ee7b9..86bb1be9240 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13956,6 +13956,11 @@ note_menu_bar_highlight (struct frame *f, int x, int y)
     /* On same menu-bar item as before.  */
     return;
 
+  /* Clear mouse face but the mouse cursor.  */
+  hlinfo->mouse_cursor_update = false;
+  clear_mouse_face (hlinfo);
+  hlinfo->mouse_cursor_update = true;
+
   if (!NILP (Vmouse_highlight))
     {
       /* Record this as the current active region.  */
@@ -14838,7 +14843,10 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
     /* On same tab-bar item as before.  */
     goto set_help_echo;
 
+  /* Clear mouse face but the mouse cursor.  */
+  hlinfo->mouse_cursor_update = false;
   clear_mouse_face (hlinfo);
+  hlinfo->mouse_cursor_update = true;
 
   bool mouse_down_p = false;
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
@@ -15793,7 +15801,10 @@ note_tool_bar_highlight (struct frame *f, int x, int y)
     /* On same tool-bar item as before.  */
     goto set_help_echo;
 
+  /* Clear mouse face but the mouse cursor.  */
+  hlinfo->mouse_cursor_update = false;
   clear_mouse_face (hlinfo);
+  hlinfo->mouse_cursor_update = true;
 
   /* Mouse is down, but on different tool-bar item?  */
   mouse_down_p = (gui_mouse_grabbed (dpyinfo)
@@ -33875,7 +33886,7 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw)
 
 #ifdef HAVE_WINDOW_SYSTEM
   /* Change the mouse cursor.  */
-  if (FRAME_WINDOW_P (f) && NILP (track_mouse))
+  if (FRAME_WINDOW_P (f) && NILP (track_mouse) && hlinfo->mouse_cursor_update)
     {
       if (draw == DRAW_NORMAL_TEXT
 #ifndef HAVE_EXT_MENU_BAR
-- 
2.40.0