Subject: Re: bug#74590: 31.0.50 [scratch/igc branch]; key input sometimes skip fcitx input method preedit box

From: Pip Cet <[email-masked]>
Date: Mon, 17 Feb 2025 19:02:03 +0000

"Yikai Zhao" <[email-masked]> writes:

> I'm sorry to tell you that the issue still happens with your patch. I
> don't see anything abnormal about the printed timestamps when the
> issue happens (no unordered timestamps found).

Oh no!  It seems I missed some cases, which might explain it, but it's
also possible it's an entirely separate issue.  Did it become less
frequent, at least?

> Now I think maybe what you described is not the same issue that I
> encountered. :(

Maybe.  If you still have the time, can you try this extended patch
instead?

diff --git a/src/xterm.c b/src/xterm.c
index c3137945ac5..52052fcc9c3 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13040,7 +13040,10 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
 		  != event_display->xi2_opcode))
 	    {
 #endif
-	      if (!x_filter_event (event_display, &next_event))
+	      XEvent event_copy = next_event;
+	      if (event_copy.type == KeyPress || event_copy.type == KeyRelease)
+		event_copy.xkey.time = CurrentTime;
+	      if (!x_filter_event (event_display, &event_copy))
 		handle_one_xevent (event_display,
 				   &next_event, &finish, &hold_quit);
 #ifdef HAVE_XINPUT2
@@ -17892,8 +17895,12 @@ x_filter_event (struct x_display_info *dpyinfo, XEvent *event)
    XFilterEvent because that's the one for which the IC
    was created.  */
 
+  //usleep (10000);
   struct frame *f1;
 
+  if (event->type == KeyPress || event->type == KeyRelease)
+    if (event->xkey.time > 1)
+      emacs_abort();
 #if defined HAVE_XINPUT2 && defined USE_GTK
   bool xinput_event = false;
   if (dpyinfo->supports_xi2
@@ -17976,9 +17983,13 @@ event_handler_gdk (GdkXEvent *gxev, GdkEvent *ev, gpointer data)
       /* Filter events for the current X input method.
          GTK calls XFilterEvent but not for key press and release,
          so we do it here.  */
+      XEvent xev_copy;
+      xev_copy = *xev;
+      fprintf (stderr, "ignoring time %ld\n", (long) xev_copy.xkey.time);
+      xev_copy.xkey.time = CurrentTime;
       if ((xev->type == KeyPress || xev->type == KeyRelease)
 	  && dpyinfo
-	  && x_filter_event (dpyinfo, xev))
+	  && x_filter_event (dpyinfo, &xev_copy))
 	{
 	  unblock_input ();
 	  return GDK_FILTER_REMOVE;
@@ -24131,7 +24142,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 		  copy.xkey.window = xev->event;
 		  copy.xkey.root = xev->root;
 		  copy.xkey.subwindow = xev->child;
-		  copy.xkey.time = xev->time;
+		  fprintf (stderr, "ignoring time %ld\n", (long) xev->time);
+		  copy.xkey.time = CurrentTime;
 		  copy.xkey.state = state;
 		  xi_convert_button_state (&xev->buttons, &copy.xkey.state);
 
@@ -24189,7 +24201,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 	      xkey.window = xev->event;
 	      xkey.root = xev->root;
 	      xkey.subwindow = xev->child;
-	      xkey.time = xev->time;
+	      fprintf (stderr, "ignoring time %ld\n", (long) xev->time);
+	      xkey.time = CurrentTime;
 	      xkey.state = state;
 	      xkey.x = lrint (xev->event_x);
 	      xkey.y = lrint (xev->event_y);
@@ -24631,7 +24644,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 	      xkey.window = xev->event;
 	      xkey.root = xev->root;
 	      xkey.subwindow = xev->child;
-	      xkey.time = xev->time;
+	      fprintf (stderr, "ignoring time %ld\n", (long) xev->time);
+	      xkey.time = CurrentTime;
 	      xkey.state = xi_convert_event_keyboard_state (xev);
 	      xkey.x = lrint (xev->event_x);
 	      xkey.y = lrint (xev->event_y);
@@ -25765,7 +25779,14 @@ XTread_socket (struct terminal *terminal, struct input_event *hold_quit)
 	  /* Input extension key events are filtered inside
 	     handle_one_xevent.  */
 #endif
-	  if (x_filter_event (dpyinfo, &event))
+	  XEvent event_copy = event;
+	  if (event_copy.type == KeyPress || event_copy.type == KeyRelease)
+	    {
+	      fprintf (stderr, "ignoring time %ld\n", (long) event.xkey.time);
+	      event_copy.xkey.time = CurrentTime;
+	    }
+
+	  if (x_filter_event (dpyinfo, &event_copy))
 	    continue;
 #ifdef HAVE_XINPUT2
 	}

> To summarize: for feature/igc at 2025/02/10 with or without the patch,
> the issue happens but at a much lower probability (compared to
> scratch/igc at 2024/11/24)

Did you ever see this message?

	fprintf(stderr,
	        "%s,%d: The application disposed a key event with %ld serial.\n",
	        __FILE__, __LINE__,
	        im->private.proto.fabricated_serial);

It should be printed on stderr when an out-of-order event arrives in
XFilterEvent.

> I think I will try bisecting between the two versions and see what affects this.

That sounds like a lot of work!  Sorry I couldn't come up with anything
but the patch above, and I understand if you don't want to try it, but
if you do, please let me know how it goes!

And if you can find a better recipe for reproducing this, please let us
know, too.

Thanks again for the report.

Pip