From: "Adrián Medraño Calvo" <adrian@medranocalvo.com>
To: Po Lu <luangruo@yahoo.com>
Cc: 61241@debbugs.gnu.org, Eli Zaretskii <eliz@gnu.org>
Subject: bug#61241: 29.0.60; Incoherent last_mouse_window (xterm.c) between XInput and XInput 2
Date: Tue, 22 Aug 2023 07:45:53 +0200 [thread overview]
Message-ID: <E867A8FB-64E8-467C-B02E-DA04BEC4F2E4@medranocalvo.com> (raw)
In-Reply-To: <87cz6n8tb1.fsf@yahoo.com>
[-- Attachment #1: Type: text/plain, Size: 5307 bytes --]
Dear Po Lu,
> On 6. Feb 2023, at 02:40, Po Lu <luangruo@yahoo.com> wrote:
>
> Adrián Medraño Calvo <adrian@medranocalvo.com> writes:
>
>> Thank you. I thought already about fixing this in EXWM by sending the
>> XI_Motion event when Emacs uses XInput 2 (as you propose below), but
>> thought that other users might be in a similar situation. For example,
>> a user using xdotool (I must say that I don't know whether xdotool
>> relies on XInput, XInput 2, XTest or something else, but for the sake
>> of this example please assume the former) would observe the same
>> behaviour as us.
>
> These tools should use the record or test extensions, which will not
> have these problems.
I’ve tried implementing your suggestion to send an XI_Motion event to Emacs when the mouse enters the X window managed by EXWM—to no avail:
1. Using SendEvent: XI2 events are GenericEvents, Xorg disallows sending GenericEvents within SendEvent. The following seem to be the cause:
- https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/dix/events.c#L5517, and especially
- https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/dix/events.c#L5541
2. Using XTest: although undocumented (as far as I could see), XTest does support sending XI device events. The issue is that EXWM wants to, when entering a managed X window, send XI_Motion events to Emacs as well so as to trick it into thinking that the pointer entered the Emacs window corresponding to the mentioned X window; which marks this Emacs window the last Emacs window the pointer was over. The last think is critical for focus-follows-mouse to work on EXWM. The obstacle I find with XTest is that XTest just mimics moving the pointer so, when moving the pointer to an X window and then using XTest to simulate moving the pointer there (again), nothing changes: the XI_Motion events are again sent to this topmost X Window. I couldn’t think of a way of having the XTest events lead to XI_Motion reaching Emacs in such a situation.
3. I also tried using XWrapPointer which of course does not help as it generates no motion events.
Can you think of a way forward?
>> If I understand correctly, an XInput 2 enabled Emacs must handle
>> regular events as well because some of its frames might be in
>> terminals supporting only Xinput 1. If that's the case, would it be
>> possible to drop/reject corresponding regular events
>> (e.g. MotionNotifiy) on terminals supporting XInput 2? If that's not
>> possible, I’d say it’s better applying the patch (or similar) so that,
>> even while unsupported, we do a best effort in reducing inconsistency.
>
> XInput 1.x is not supported by Emacs. You probably mean the X11 core
> protocol.
>
> It is ok for MotionNotify events to arrive on a display that has not
> selected for input extension events, since Emacs does not keep track of
> much state globally across displays.
>
> But once they do on a display that has, then a lot can go wrong.
>
>> I'll implement your suggestion in EXWM shortly, in any case before
>> Emacs 29 is released. How can I detect whether Emacs uses XInput 2 in
>> a particular terminal?
>
> The function `x-server-input-extension-version'.
>
>> (I’m not sure whether I understand your question, please clarify if
>> you think I missed it.) Just focusing the X window (and selecting the
>> related Emacs window) is not enough for integrating
>> `mouse-autoselect-window', precisely because `last_mouse_window' gets
>> out-of-sync, leading to the user being unable to select back the last
>> Emacs window by moving the mouse over it. A possible solution to this
>> would be to expose `last_mouse_window' to Lisp
>> (e.g. `mouse-autoselect-last-window'); presumably EXWM could then set
>> its value as part of the above steps and have focus follow the
>> pointer. I’d say this is my preferred solution, what do you think
>> about this?
>
> Sounds reasonable, except it is too late in the Emacs 29 release cycle
> to implement this there.
I prepared a patch (attached below) introducing a new lisp variable holding the last "mouse-autoselected” window. With this patch EXWM can simply select the Emacs window corresponding to the managed X window and set it as last “mouse-autoselected” window; mouse-autoselect works normally after that. No more sending Motion events.
Using a single variable introduces a significant change, though: when `mouse-autoselect-window’ is enabled and emacs runs in different terminals, “mouse-autoselecting” a window on a terminal changes the last “mouse-autoselected” window for all other terminals. Slightly moving the mouse in a different terminal (within a single Emacs window) will “mouse-autoselect” that window, whereas that would not have been the case before this patch, as the last “mouse-autoselected” window was terminal-specific. It’s not clear to me whether this behaviour will be beneficial, neutral or unacceptable. Moreover, this behaviour is consistent with the behaviour introduced in the first patch I sent, where I proposed to merge the last “mouse-autoselected” window for Motion and XI_Motion events (other terminals were not affected).
Please let me know your thoughts.
Regards,
Adrián.
[-- Attachment #2: 0001-Expose-last-window-under-pointer-to-Lisp.patch --]
[-- Type: application/octet-stream, Size: 12723 bytes --]
From d845f06a8155a35fe93769e600b79b951f723863 Mon Sep 17 00:00:00 2001
Message-ID: <d845f06a8155a35fe93769e600b79b951f723863.1692630504.git.adrian@medranocalvo.com>
From: =?UTF-8?q?Adri=C3=A1n=20Medra=C3=B1o=20Calvo?=
<adrian@medranocalvo.com>
Date: Tue, 15 Aug 2023 00:00:00 +0000
Subject: [PATCH] Expose last window under pointer to Lisp
* src/xdisp.c (syms_of_xdisp): Add defvar
`Vmouse_autoselect_window_last_window'.
* src/haikuterm.c (haiku_read_socket):
* src/msdos.c (dos_rawgetc):
* src/nsterm.m ([EmacsView mouseMoved:]):
* src/pgtkterm.c (motion_notify_event):
* src/w32inevt.c (do_mouse_event):
* src/w32term.c (w32_read_socket):
* src/xterm.c (handle_one_xevent): Use it replacing static
`last_mouse_window'.
* lisp/window.el (mouse-autoselect-window-window)
(mouse-autoselect-window-previous-window): Rename variable.
---
lisp/window.el | 8 +++++---
src/haikuterm.c | 5 ++---
src/msdos.c | 6 ++----
src/nsterm.m | 5 ++---
src/pgtkterm.c | 5 ++---
src/w32inevt.c | 7 ++-----
src/w32term.c | 5 ++---
src/xdisp.c | 7 +++++++
src/xterm.c | 11 ++++-------
9 files changed, 28 insertions(+), 31 deletions(-)
diff --git a/lisp/window.el b/lisp/window.el
index 16f16a75418..b2677ea99f3 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -10391,8 +10391,10 @@ mouse-autoselect-window-position-1
(defvar mouse-autoselect-window-position nil
"Last mouse position recorded by delayed window autoselection.")
-(defvar mouse-autoselect-window-window nil
+(defvar mouse-autoselect-window-previous-window nil
"Last window recorded by delayed window autoselection.")
+(define-obsolete-variable-alias 'mouse-autoselect-window-window
+ 'mouse-autoselect-window-previous-window "29.1")
(defvar mouse-autoselect-window-state nil
"When non-nil, special state of delayed window autoselection.
@@ -10425,7 +10427,7 @@ mouse-autoselect-window-start
means suspend autoselection."
;; Record values for MOUSE-POSITION, WINDOW, and SUSPEND.
(setq mouse-autoselect-window-position mouse-position)
- (when window (setq mouse-autoselect-window-window window))
+ (when window (setq mouse-autoselect-window-previous-window window))
(setq mouse-autoselect-window-state (when suspend 'suspend))
;; Install timer which runs `mouse-autoselect-window-select' after
;; `mouse-autoselect-window' seconds.
@@ -10473,7 +10475,7 @@ mouse-autoselect-window-select
(and (>= mouse-autoselect-window 0)
;; If `mouse-autoselect-window' is non-negative,
;; select window if it's the same as before.
- (eq window mouse-autoselect-window-window))
+ (eq window mouse-autoselect-window-previous-window))
;; Otherwise select window iff the mouse is at the same
;; position as before. Observe that the first test
;; after starting autoselection usually fails since the
diff --git a/src/haikuterm.c b/src/haikuterm.c
index 8733b82fb2b..8341fd4cc53 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -3471,11 +3471,10 @@ 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);
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window)
&& !popup_activated_p
&& !MINI_WINDOW_P (XWINDOW (selected_window))
@@ -3486,7 +3485,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
inev2.frame_or_window = window;
}
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
if (f->auto_raise)
diff --git a/src/msdos.c b/src/msdos.c
index 75a39045cee..333fd9d59e6 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -2652,8 +2652,6 @@ dos_rawgetc (void)
/* Generate SELECT_WINDOW_EVENTs when needed. */
if (!NILP (Vmouse_autoselect_window))
{
- static Lisp_Object last_mouse_window;
-
mouse_window = window_from_coordinates
(SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0);
/* A window will be selected only when it is not
@@ -2661,7 +2659,7 @@ dos_rawgetc (void)
not in it. A minibuffer window will be selected iff
it is active. */
if (WINDOWP (mouse_window)
- && !EQ (mouse_window, last_mouse_window)
+ && !EQ (mouse_window, Vmouse_autoselect_window_last_window)
&& !EQ (mouse_window, selected_window))
{
event.kind = SELECT_WINDOW_EVENT;
@@ -2671,7 +2669,7 @@ dos_rawgetc (void)
kbd_buffer_store_event (&event);
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = mouse_window;
+ Vmouse_autoselect_window_last_window = mouse_window;
}
previous_help_echo_string = help_echo_string;
diff --git a/src/nsterm.m b/src/nsterm.m
index c809c0b824a..060898bb842 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7530,12 +7530,11 @@ - (void)mouseMoved: (NSEvent *)e
if (!NILP (Vmouse_autoselect_window))
{
NSTRACE_MSG ("mouse_autoselect_window");
- static Lisp_Object last_mouse_window;
Lisp_Object window
= window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0);
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window)
&& !MINI_WINDOW_P (XWINDOW (selected_window))
&& (!NILP (focus_follows_mouse)
@@ -7548,7 +7547,7 @@ - (void)mouseMoved: (NSEvent *)e
EV_TRAILER2 (e);
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
dragging = (e.type == NSEventTypeLeftMouseDragged);
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index e767e15cc07..b343bd99fbe 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -5889,7 +5889,6 @@ motion_notify_event (GtkWidget *widget, GdkEvent *event,
also when the target window is on another frame. */
&& (f == XFRAME (selected_frame) || !NILP (focus_follows_mouse)))
{
- static Lisp_Object last_mouse_window;
Lisp_Object window = window_from_coordinates
(f, event->motion.x, event->motion.y, 0, false, false);
@@ -5902,7 +5901,7 @@ motion_notify_event (GtkWidget *widget, GdkEvent *event,
autoselection the window is usually the window's text
area including the margins. */
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window))
{
inev.ie.kind = SELECT_WINDOW_EVENT;
@@ -5910,7 +5909,7 @@ motion_notify_event (GtkWidget *widget, GdkEvent *event,
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
if (!note_mouse_movement (f, &event->motion))
diff --git a/src/w32inevt.c b/src/w32inevt.c
index 29717954cfd..c800291c2f8 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -467,7 +467,6 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
struct input_event *emacs_ev)
{
static DWORD button_state = 0;
- static Lisp_Object last_mouse_window;
DWORD but_change, mask, flags = event->dwEventFlags;
int i;
@@ -502,7 +501,7 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
not in it. A minibuffer window will be selected iff
it is active. */
if (WINDOWP (mouse_window)
- && !EQ (mouse_window, last_mouse_window)
+ && !EQ (mouse_window, Vmouse_autoselect_window_last_window)
&& !EQ (mouse_window, selected_window))
{
struct input_event event;
@@ -514,10 +513,8 @@ do_mouse_event (MOUSE_EVENT_RECORD *event,
event.timestamp = movement_time;
kbd_buffer_store_event (&event);
}
- last_mouse_window = mouse_window;
+ Vmouse_autoselect_window_last_window = mouse_window;
}
- else
- last_mouse_window = Qnil;
previous_help_echo_string = help_echo_string;
help_echo_string = help_echo_object = help_echo_window = Qnil;
diff --git a/src/w32term.c b/src/w32term.c
index 2899e82b295..f9d86637024 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -5333,7 +5333,6 @@ w32_read_socket (struct terminal *terminal,
|| (!NILP (focus_follows_mouse)
&& !FRAME_NO_ACCEPT_FOCUS (f))))
{
- static Lisp_Object last_mouse_window;
Lisp_Object window = window_from_coordinates
(f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0);
@@ -5342,7 +5341,7 @@ w32_read_socket (struct terminal *terminal,
not in it. Minibuffer window will be selected
only when it is active. */
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window))
{
inev.kind = SELECT_WINDOW_EVENT;
@@ -5350,7 +5349,7 @@ w32_read_socket (struct terminal *terminal,
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
if (!w32_note_mouse_movement (f, &msg.msg))
diff --git a/src/xdisp.c b/src/xdisp.c
index 9cddcfeda27..dc0cbb658f6 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -36854,6 +36854,13 @@ syms_of_xdisp (void)
of your window manager. */);
Vmouse_autoselect_window = Qnil;
+ DEFVAR_LISP ("mouse-autoselect-window-last-window",
+ Vmouse_autoselect_window_last_window,
+ doc: /* Last window entered by the the mouse pointer.
+This variable is tracked only when both `mouse-autoselect-window' and
+`focus-follows-mouse' are non-nil. */);
+ Vmouse_autoselect_window_last_window = Qnil;
+
DEFVAR_LISP ("auto-resize-tab-bars", Vauto_resize_tab_bars,
doc: /* Non-nil means automatically resize tab-bars.
This dynamically changes the tab-bar's height to the minimum height
diff --git a/src/xterm.c b/src/xterm.c
index 5840b15bcb7..36ba3103a70 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -20681,8 +20681,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
&& (f == XFRAME (selected_frame)
|| !NILP (focus_follows_mouse)))
{
- static Lisp_Object last_mouse_window;
-
if (xmotion.window != FRAME_X_WINDOW (f))
{
x_translate_coordinates (f, xmotion.x_root, xmotion.y_root,
@@ -20702,7 +20700,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
autoselection the window is usually the window's text
area including the margins. */
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window))
{
inev.ie.kind = SELECT_WINDOW_EVENT;
@@ -20710,7 +20708,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
if (!x_note_mouse_movement (f, &xmotion, Qnil))
@@ -22593,7 +22591,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
&& (f == XFRAME (selected_frame)
|| !NILP (focus_follows_mouse)))
{
- static Lisp_Object last_mouse_window;
Lisp_Object window = window_from_coordinates (f, ev.x, ev.y, 0, false, false);
/* A window will be autoselected only when it is not
@@ -22605,7 +22602,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
autoselection the window is usually the window's text
area including the margins. */
if (WINDOWP (window)
- && !EQ (window, last_mouse_window)
+ && !EQ (window, Vmouse_autoselect_window_last_window)
&& !EQ (window, selected_window))
{
inev.ie.kind = SELECT_WINDOW_EVENT;
@@ -22616,7 +22613,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
/* Remember the last window where we saw the mouse. */
- last_mouse_window = window;
+ Vmouse_autoselect_window_last_window = window;
}
if (!x_note_mouse_movement (f, &ev, source ? source->name : Qnil))
--
2.41.0
next prev parent reply other threads:[~2023-08-22 5:45 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-02 22:28 bug#61241: 29.0.60; Incoherent last_mouse_window (xterm.c) between XInput and XInput 2 Adrián Medraño Calvo
2023-02-04 8:25 ` Eli Zaretskii
2023-02-04 8:43 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-02-05 19:04 ` Adrián Medraño Calvo
2023-02-06 1:40 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-08-22 5:45 ` Adrián Medraño Calvo [this message]
2023-08-22 6:59 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=E867A8FB-64E8-467C-B02E-DA04BEC4F2E4@medranocalvo.com \
--to=adrian@medranocalvo.com \
--cc=61241@debbugs.gnu.org \
--cc=eliz@gnu.org \
--cc=luangruo@yahoo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).