Subject: bug#75219: 31.0.50; mouse-2 mode-line binding overridden by mouse-1-click-follows-link

From: Eli Zaretskii <[email-masked]>
Date: Sun, 05 Jan 2025 17:17:45 +0200

> From: Visuwesh <[email-masked]>
> Date: Tue, 31 Dec 2024 11:07:05 +0530
> 
> When the point is over a character with a keymap that has `follow-link',
> mode-line mouse-1 binding is translated to mode-line mouse-2 binding.
> 
> To reproduce,
> 
>     1. emacs -Q
>     2. M-: (define-key mode-line-buffer-identification-keymap [mode-line mouse-2] #'mouse-buffer-menu) RET
>     3. M-s M-w something RET
>     4. Move the point over to any link
>     5. Click the buffer name in the mode-line with mouse-1
>     6. Observe how mouse-buffer-menu is executed instead of changing the
>        buffer

This happens because key-binding considers the local keymap at point
even if its POSITION argument specifies a mouse click in a completely
different location (in this case: on the mode line).  For example, try

  M-: (key-binding [follow-link] nil t (list (selected-window) 'mode-line '(82 . 549) 10000)) RET

Evaluate this in EWW buffer, once with point on a link and another
time with point on a regular character.  In the second case this will
return nil, but in the first case it will return 'mouse-face', an
indication that the click should follow a link.  The result of this
is that mouse-on-link-p returns non-nil, and
mouse--click-1-maybe-follows-link decides we need to follow the link.

The reason why key-binding considers the local keymap at point is this
part of current-active-maps (which key-binding calls):

      ptrdiff_t pt = click_position (position);  <<<<<<<<<<<<<<<<<<<<<
      /* This usually returns the buffer's local map,
	 but that can be overridden by a `local-map' property.  */
      Lisp_Object local_map = get_local_map (pt, current_buffer, Qlocal_map);
      /* This returns nil unless there is a `keymap' property.  */
      Lisp_Object keymap = get_local_map (pt, current_buffer, Qkeymap);
      Lisp_Object otlp = KVAR (current_kboard, Voverriding_terminal_local_map);

and click_position does this:

  static ptrdiff_t
  click_position (Lisp_Object position)
  {
    EMACS_INT pos = (FIXNUMP (position) ? XFIXNUM (position)
		     : MARKERP (position) ? marker_position (position)
		     : PT);
    if (! (BEGV <= pos && pos <= ZV))
      args_out_of_range (Fcurrent_buffer (), position);
    return pos;
  }

So if POSITION is a mouse click event, click_position will always
return the position of point.

It is easy to prevent this strange result from click_position by
ignoring local map at POSITION which is a list, but I don't know how
much code out there relies on this "fallback" to point.

Stefan, any ideas or suggestions?