DirectX-XShm now waits for the X server to finish the previous frame
before sending another frame down its pipe, avoiding the X server
overload and resulting slowness that used to be.

diff --git a/graphics/ddraw.c b/graphics/ddraw.c
index da0bd41..8fee409 100644
--- a/graphics/ddraw.c
+++ b/graphics/ddraw.c
@@ -708,7 +708,11 @@
 				   This->s.palette);
 
 #ifdef HAVE_LIBXXSHM
-    if (This->s.ddraw->e.xlib.xshm_active)
+    if (This->s.ddraw->e.xlib.xshm_active) {
+      int compl = This->s.ddraw->e.xlib.xshm_compl;
+      if (compl)
+        X11DRV_EVENT_WaitShmCompletion( compl );
+      This->s.ddraw->e.xlib.xshm_compl = X11DRV_EVENT_PrepareShmCompletion( This->s.ddraw->d.drawable );
       TSXShmPutImage(display,
 		     This->s.ddraw->d.drawable,
 		     DefaultGCOfScreen(X11DRV_GetXScreen()),
@@ -716,7 +720,8 @@
 		     0, 0, 0, 0,
 		     This->t.xlib.image->width,
 		     This->t.xlib.image->height,
-		     False);
+		     True);
+    }
     else
 #endif
 	TSXPutImage(		display,
@@ -5027,8 +5032,10 @@
 
 #ifdef HAVE_LIBXXSHM
 	/* Test if XShm is available. */
-	if (((*ilplpDD)->e.xlib.xshm_active = DDRAW_XSHM_Available()))
+	if (((*ilplpDD)->e.xlib.xshm_active = DDRAW_XSHM_Available())) {
+	  (*ilplpDD)->e.xlib.xshm_compl = 0;
 	  TRACE("Using XShm extension.\n");
+	}
 #endif
 	
 	return DD_OK;
diff --git a/graphics/ddraw_private.h b/graphics/ddraw_private.h
index e55fbb0..48e70e8 100644
--- a/graphics/ddraw_private.h
+++ b/graphics/ddraw_private.h
@@ -81,7 +81,7 @@
 struct _xlib_directdrawdata
 {
 #ifdef HAVE_LIBXXSHM
-    int xshm_active;
+    int xshm_active, xshm_compl;
 #endif /* defined(HAVE_LIBXXSHM) */
     
     /* are these needed for anything? (draw_surf is the active surface)
diff --git a/include/ts_xlib.h b/include/ts_xlib.h
index 6709455..9e4303d 100644
--- a/include/ts_xlib.h
+++ b/include/ts_xlib.h
@@ -53,6 +53,7 @@
 extern int  TSXChangeKeyboardControl(Display*, unsigned long, XKeyboardControl*);
 extern int  TSXChangeProperty(Display*, Window, Atom, Atom, int, int, const  unsigned char*, int);
 extern int  TSXChangeWindowAttributes(Display*, Window, unsigned long, XSetWindowAttributes*);
+extern int   TSXCheckTypedEvent(Display*, int, XEvent*);
 extern int   TSXCheckTypedWindowEvent(Display*, Window, int, XEvent*);
 extern int   TSXCheckWindowEvent(Display*, Window, long, XEvent*);
 extern int  TSXConvertSelection(Display*, Atom, Atom, Atom, Window, Time);
diff --git a/include/x11drv.h b/include/x11drv.h
index 1034f9c..0293b35 100644
--- a/include/x11drv.h
+++ b/include/x11drv.h
@@ -469,4 +469,7 @@
 extern BOOL X11DRV_WND_SetHostAttr(struct tagWND *wndPtr, INT haKey, INT value);
 extern BOOL X11DRV_WND_IsSelfClipping(struct tagWND *wndPtr);
 
+extern int X11DRV_EVENT_PrepareShmCompletion( Drawable dw );
+extern void X11DRV_EVENT_WaitShmCompletion( int compl );
+
 #endif  /* __WINE_X11DRV_H */
diff --git a/tsx11/X11_calls b/tsx11/X11_calls
index 65c747b..b872337 100644
--- a/tsx11/X11_calls
+++ b/tsx11/X11_calls
@@ -16,6 +16,7 @@
 XChangeKeyboardControl
 XChangeProperty
 XChangeWindowAttributes
+XCheckTypedEvent
 XCheckTypedWindowEvent
 XCheckWindowEvent
 XClipBox
diff --git a/tsx11/ts_xlib.c b/tsx11/ts_xlib.c
index c336483..2ec5a35 100644
--- a/tsx11/ts_xlib.c
+++ b/tsx11/ts_xlib.c
@@ -422,6 +422,17 @@
   return r;
 }
 
+int   TSXCheckTypedEvent(Display* a0, int a1, XEvent* a2)
+{
+  int   r;
+  TRACE("Call XCheckTypedEvent\n");
+  EnterCriticalSection( &X11DRV_CritSection );
+  r = XCheckTypedEvent(a0, a1, a2);
+  LeaveCriticalSection( &X11DRV_CritSection );
+  TRACE("Ret XCheckTypedEvent\n");
+  return r;
+}
+
 int   TSXCheckTypedWindowEvent(Display* a0, Window a1, int a2, XEvent* a3)
 {
   int   r;
diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c
index 4977625..2c3f40f 100644
--- a/windows/x11drv/event.c
+++ b/windows/x11drv/event.c
@@ -14,6 +14,9 @@
 #include "ts_xlib.h"
 #include "ts_xresource.h"
 #include "ts_xutil.h"
+#ifdef HAVE_LIBXXSHM
+#include "ts_xshm.h"
+#endif
 
 #include <assert.h>
 #include <string.h>
@@ -102,6 +105,11 @@
 static void EVENT_MapNotify( HWND pWnd, XMapEvent *event );
 static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
 
+#ifdef HAVE_LIBXXSHM
+static void EVENT_ShmCompletion( XShmCompletionEvent *event );
+static int ShmCompletionType;
+#endif
+
 /* Usable only with OLVWM - compile option perhaps?
 static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event );
 */
@@ -121,6 +129,10 @@
  */
 BOOL X11DRV_EVENT_Init(void)
 {
+#ifdef HAVE_LIBXXSHM
+    ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
+#endif
+
     /* Install the X event processing callback */
     SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display), 
                                            GENERIC_READ | SYNCHRONIZE ),
@@ -150,7 +162,7 @@
 {
     XEvent event;
   
-    TRACE_(event)( "called.\n" );
+    TRACE_(event)( "called (thread %lx).\n", GetCurrentThreadId() );
 
     EnterCriticalSection( &X11DRV_CritSection );
     while ( XPending( display ) )
@@ -211,6 +223,13 @@
     case ReparentNotify:
       return;
   }
+
+#ifdef HAVE_LIBXXSHM
+  if (event->type == ShmCompletionType) {
+    EVENT_ShmCompletion( (XShmCompletionEvent*)event );
+    return;
+  }
+#endif
       
   if ( TSXFindContext( display, event->xany.window, winContext,
 		       (char **)&hWnd ) != 0) {
@@ -373,6 +392,7 @@
 	   event_names[event->type], hWnd );
       break;
     }
+    TRACE_(event)( "returns.\n" );
 }
 
 /***********************************************************************
@@ -1780,4 +1800,96 @@
   return prev;
 }
 
+#ifdef HAVE_LIBXXSHM
+
+/*
+Normal XShm operation:
+
+X11           service thread    app thread
+------------- ----------------- ------------------------
+              (idle)            ddraw calls XShmPutImage
+(copies data)                   (waiting for shm_event)
+ShmCompletion ->                (waiting for shm_event)
+(idle)        signal shm_event ->
+              (idle)            returns to app
+
+However, this situation can occur for some reason:
+
+X11           service thread    app thread
+------------- ----------------- ------------------------
+Expose ->
+              WM_ERASEBKGND? ->
+              (waiting for app) ddraw calls XShmPutImage
+(copies data) (waiting for app) (waiting for shm_event)
+ShmCompletion (waiting for app) (waiting for shm_event)
+(idle)        DEADLOCK          DEADLOCK
+
+which is why I also wait for shm_read and do XCheckTypedEvent()
+calls in the wait loop. This results in:
+
+X11           service thread    app thread
+------------- ----------------- ------------------------
+ShmCompletion (waiting for app) waking up on shm_read
+(idle)        (waiting for app) XCheckTypedEvent() -> signal shm_event
+              (waiting for app) returns
+              (idle)
+*/
+   
+/* FIXME: this is not pretty */
+static Drawable shm_draw = 0;
+static HANDLE shm_event = 0, shm_read = 0;
+
+static void EVENT_ShmCompletion( XShmCompletionEvent *event )
+{
+  TRACE_(event)("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() );
+  if (event->drawable == shm_draw) {
+    HANDLE event = shm_event;
+    shm_draw = 0;
+    shm_event = 0;
+    SetEvent(event);
+    TRACE_(event)("Event object triggered\n" );
+  } else ERR_(event)("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
+}
+
+int X11DRV_EVENT_PrepareShmCompletion( Drawable dw )
+{
+  if (shm_draw) {
+    ERR_(event)("Multiple ShmCompletion requests not implemented\n");
+    return 0;
+  }
+  TRACE_(event)("Preparing ShmCompletion (%d) wait for drawable %ld (time %ld)\n", ShmCompletionType, dw, GetTickCount() );
+  shm_draw = dw;
+  if (!shm_event)
+    /* use manual reset just in case */
+    shm_event = ConvertToGlobalHandle( CreateEventA( NULL, TRUE, FALSE, NULL ) );
+  if (!shm_read)
+    shm_read = ConvertToGlobalHandle( FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE ) );
+  return shm_event;
+}
+
+void X11DRV_EVENT_WaitShmCompletion( int compl )
+{
+  if (!compl) return;
+  TRACE_(event)("Waiting for ShmCompletion (%d) (thread %lx) (time %ld)\n", ShmCompletionType, GetCurrentThreadId(), GetTickCount() );
+  /* already triggered? */
+  if ( WaitForSingleObject( compl, 0 ) != WAIT_OBJECT_0 ) {
+    /* nope, may need to poll X event queue, in case the service thread is blocked */
+    XEvent event;
+    HANDLE hnd[2];
+
+    hnd[0] = compl;
+    hnd[1] = shm_read;
+    do {
+      /* check X event queue */
+      if (TSXCheckTypedEvent( display, ShmCompletionType, &event)) {
+        EVENT_ProcessEvent( &event );
+      }
+    } while ( WaitForMultipleObjects(2, hnd, FALSE, INFINITE) > WAIT_OBJECT_0 );
+  }
+  ResetEvent(compl); /* manual reset */
+  TRACE_(event)("Wait complete (time %ld)\n", GetTickCount() );
+}
+
+#endif
+
 #endif /* !defined(X_DISPLAY_MISSING) */