Subject: 27.1; tab-bar missed mouse clicks on MS-Windows

From: Ioannis Kappas <[email-masked]>
Date: Sat, 3 Apr 2021 12:26:53 +0100

The issue appears to be due to the wrong calculation performed by the
src/xdisp.c:remember_mouse_glyph fn to identify the RECTangle location
of the glyph under the mouse cursor, when the mouse is over a tab.

The fn calls src/windows.c:window_from_coordinates to retrieve the
window where the mouse is over, but this returns nil, resulting to a
`virtual_glyph' position calculation, which appears to dictate that
glyphs are fixed width having their width and height equal to the
smallest char width and font height of the frame respectively.

But the tab bar glyphs are of variable size, and thus the glyph
position rectangle returned by the above function will not necessarily
correspond to the actual position of the glyph under the mouse cursor.

Consider the following example of glyph position in a tab-bar with a
single *scratch* tab on MS-Windows. The src/xdisp.c:x_y_to_hpos_vpos
fn used elsewhere in the tab logic correctly calculates the column
HPOS as per below (e.g. the ?t glyph corresponds to column no. 7, it
is 6px wide and occupies pixels 61 to 67 in the tab row):

| column no.   |   1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |  10 |
| glyph        | ?\s | ?* | ?s | ?c | ?r | ?a | ?t | ?c | ?h |  ?* |
| width (px)   |   6 | 10 | 12 | 12 |  8 | 13 |  6 | 12 | 12 |   9 |
| end pos (px) |   6 | 16 | 28 | 40 | 48 | 61 | 67 | 79 | 91 | 100 |

and the corresponding glyph positions returned by the
src/xdisp.c:remember_mouse_glyph fn RECT on windows 10 (the fixed
width of a glyph is 9px wide):

| column no.   |   1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |  10 |
| glyph        | ?\s | ?* | ?s | ?c | ?r | ?a | ?t | ?c | ?h |  ?* |
| width (px)   |   9 |  9 |  9 |  9 |  9 |  9 |  9 |  9 |  9 |   9 |
| end pos (px) |   9 | 18 | 27 | 36 | 45 | 54 | 63 | 82 | 91 | 100 |

The above miscalculation is used by
src/w32term.c:w32_note_mouse_movement, to check if the mouse has moved
over the last glyph and thus update the last glyph position
accordingly. However, because the last glyph position check is based
on the glyph coordinates performed by src/xdisp.c:remember_mouse_glyph
`virtual_glyph' method, the glyph position does not correspond to the
actual tab glyph, and updates can be missed.

Consider the following example. Mouse X coord is at position 62px,
both methodologies identify the mouse is over column 7; the mouse then
moves to X coord 60px i.e. the mouse is over the glyph at column 6,
but the `virtual_glyph' fixed width methodology thinks it is still on
column 7 (i.e. in 54-63), thus the update described earlier is missed,
resulting to a miss-activation of the tab as per this bug report.

Assuming the above analysis to be correct, there appears to be an easy
solution
(which seems rather to be a bug fix) to instruct
src/xdisp.c:remember_mouse_glyph
to call src/window.c:window_from_coordinates with a TAB_BAR_P argument of
true, thus letting it proceed with the correct RECT identification of
the glyph (the logic falls in the pseudo-window-p if block, and jumps
over to the `text_glyph' calculation):

diff --git a/src/xdisp.c b/src/xdisp.c
index 77c9af747c..c6d5f8b789 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2609,7 +2609,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, true, false),
         NILP (window)))
     {
       width = FRAME_SMALLEST_CHAR_WIDTH (f);

Thanks