https://gitlab.freedesktop.org/xorg/lib/libx11/-/commit/a9e845809bcaae22496bc8aa3ca252b410d5f39b
https://bugs.gentoo.org/862115

From a9e845809bcaae22496bc8aa3ca252b410d5f39b Mon Sep 17 00:00:00 2001
From: Matthieu Herrb <matthieu@herrb.eu>
Date: Fri, 11 Nov 2022 18:55:23 +0100
Subject: [PATCH] Fix 797755 Allow X*IfEvent() to reenter libX11

- the activation logic is reversed
- there is also _XInternalLockDisplay() that needs protection
- I've found cases (in fvwm2) where the callback calls XCheckIfEvent()
  recursively. So the flag needs to be a counter.

Reviewed-by: Adam Jackson <ajax@redhat.com>
--- a/include/X11/Xlibint.h
+++ b/include/X11/Xlibint.h
@@ -207,7 +207,7 @@ struct _XDisplay
 
 	XIOErrorExitHandler exit_handler;
 	void *exit_handler_data;
-        Bool in_ifevent;
+        CARD32 in_ifevent;
 };
 
 #define XAllocIDs(dpy,ids,n) (*(dpy)->idlist_alloc)(dpy,ids,n)
--- a/src/ChkIfEv.c
+++ b/src/ChkIfEv.c
@@ -49,8 +49,8 @@ Bool XCheckIfEvent (
 	unsigned long qe_serial = 0;
 	int n;			/* time through count */
 
+        dpy->in_ifevent++;
         LockDisplay(dpy);
-        dpy->in_ifevent = True;
 	prev = NULL;
 	for (n = 3; --n >= 0;) {
 	    for (qelt = prev ? prev->next : dpy->head;
@@ -80,7 +80,7 @@ Bool XCheckIfEvent (
 		/* another thread has snatched this event */
 		prev = NULL;
 	}
-        dpy->in_ifevent = False;
+        dpy->in_ifevent--;
 	UnlockDisplay(dpy);
 	return False;
 }
--- a/src/IfEvent.c
+++ b/src/IfEvent.c
@@ -48,8 +48,8 @@ XIfEvent (
 	register _XQEvent *qelt, *prev;
 	unsigned long qe_serial = 0;
 
+        dpy->in_ifevent++;
         LockDisplay(dpy);
-        dpy->in_ifevent = True;
 	prev = NULL;
 	while (1) {
 	    for (qelt = prev ? prev->next : dpy->head;
@@ -60,7 +60,7 @@ XIfEvent (
 		    *event = qelt->event;
 		    _XDeq(dpy, prev, qelt);
 		    _XStoreEventCookie(dpy, event);
-                    dpy->in_ifevent = False;
+                    dpy->in_ifevent--;
 		    UnlockDisplay(dpy);
 		    return 0;
 		}
--- a/src/OpenDis.c
+++ b/src/OpenDis.c
@@ -189,7 +189,7 @@ XOpenDisplay (
 	dpy->xcmisc_opcode	= 0;
 	dpy->xkb_info		= NULL;
 	dpy->exit_handler_data	= NULL;
-        dpy->in_ifevent         = False;
+        dpy->in_ifevent         = 0;
 
 /*
  * Setup other information in this display structure.
--- a/src/PeekIfEv.c
+++ b/src/PeekIfEv.c
@@ -49,8 +49,8 @@ XPeekIfEvent (
 	register _XQEvent *prev, *qelt;
 	unsigned long qe_serial = 0;
 
+        dpy->in_ifevent++;
 	LockDisplay(dpy);
-        dpy->in_ifevent = True;
 	prev = NULL;
 	while (1) {
 	    for (qelt = prev ? prev->next : dpy->head;
@@ -64,7 +64,7 @@ XPeekIfEvent (
 			_XStoreEventCookie(dpy, &copy);
 			*event = copy;
 		    }
-                    dpy->in_ifevent = False;
+                    dpy->in_ifevent--;
 		    UnlockDisplay(dpy);
 		    return 0;
 		}
--- a/src/locking.c
+++ b/src/locking.c
@@ -465,17 +465,33 @@ static void _XIfEventLockDisplay(
     /* assert(dpy->in_ifevent); */
 }
 
+static void _XInternalLockDisplay(
+    Display *dpy,
+    Bool wskip
+    XTHREADS_FILE_LINE_ARGS
+    );
+
+static void _XIfEventInternalLockDisplay(
+    Display *dpy,
+    Bool wskip
+    XTHREADS_FILE_LINE_ARGS
+    )
+{
+    /* assert(dpy->in_ifevent); */
+}
+
 static void _XIfEventUnlockDisplay(
     Display *dpy
     XTHREADS_FILE_LINE_ARGS
     )
 {
-    if (dpy->in_ifevent)
+    if (dpy->in_ifevent == 0) {
+        dpy->lock_fns->lock_display = _XLockDisplay;
+        dpy->lock_fns->unlock_display = _XUnlockDisplay;
+        dpy->lock->internal_lock_display = _XInternalLockDisplay;
+        UnlockDisplay(dpy);
+    } else
         return;
-
-    dpy->lock_fns->lock_display = _XLockDisplay;
-    dpy->lock_fns->unlock_display = _XUnlockDisplay;
-    UnlockDisplay(dpy);
 }
 
 static void _XLockDisplay(
@@ -507,6 +523,7 @@ static void _XLockDisplay(
     if (dpy->in_ifevent) {
         dpy->lock_fns->lock_display = _XIfEventLockDisplay;
         dpy->lock_fns->unlock_display = _XIfEventUnlockDisplay;
+        dpy->lock->internal_lock_display = _XIfEventInternalLockDisplay;
     }
 }
 
GitLab
