diff --git a/graphics/ddraw.c b/graphics/ddraw.c
index 4c88786b4c80beb0844335cddd288a0a68062219..804df3f0286a596eb4422368803f80d22b9dbbc4 100644
--- a/graphics/ddraw.c
+++ b/graphics/ddraw.c
@@ -737,6 +737,18 @@ static HRESULT WINAPI IDirectDrawSurface4Impl_Lock(
 	} else {
 		assert(This->s.surface_desc.u1.lpSurface);
 	}
+
+	/* wait for any previous operations to complete */
+#ifdef HAVE_LIBXXSHM
+        if (This->t.xlib.image && (SDDSCAPS(This) & DDSCAPS_VISIBLE) &&
+	    This->s.ddraw->e.xlib.xshm_active) {
+/*
+	  int compl = InterlockedExchange( &This->s.ddraw->e.xlib.xshm_compl, 0 );
+          if (compl) X11DRV_EVENT_WaitShmCompletion( compl );
+*/
+          X11DRV_EVENT_WaitShmCompletions( This->s.ddraw->d.drawable );
+	}
+#endif
 	return DD_OK;
 }
 
@@ -761,10 +773,13 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) {
 
 #ifdef HAVE_LIBXXSHM
     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 );
+/*
+      X11DRV_EVENT_WaitReplaceShmCompletion( &This->s.ddraw->e.xlib.xshm_compl, This->s.ddraw->d.drawable );
+*/
+      /* let WaitShmCompletions track 'em for now */
+      /* (you may want to track it again whenever you implement DX7's partial surface locking,
+          where threads have concurrent access) */
+      X11DRV_EVENT_PrepareShmCompletion( This->s.ddraw->d.drawable );
       TSXShmPutImage(display,
 		     This->s.ddraw->d.drawable,
 		     DefaultGCOfScreen(X11DRV_GetXScreen()),
@@ -773,6 +788,8 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) {
 		     This->t.xlib.image->width,
 		     This->t.xlib.image->height,
 		     True);
+      /* make sure the image is transferred ASAP */
+      TSXFlush(display);
     }
     else
 #endif
diff --git a/include/x11drv.h b/include/x11drv.h
index 33810ea0b6f81bc62b5c1a55b3d56fca5d61e1ea..2644ea023c72377d5661565cdc1fc45e52315b12 100644
--- a/include/x11drv.h
+++ b/include/x11drv.h
@@ -479,5 +479,7 @@ extern void X11DRV_WND_DockWindow(struct tagWND *wndPtr);
 
 extern int X11DRV_EVENT_PrepareShmCompletion( Drawable dw );
 extern void X11DRV_EVENT_WaitShmCompletion( int compl );
+extern void X11DRV_EVENT_WaitShmCompletions( Drawable dw );
+extern void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw );
 
 #endif  /* __WINE_X11DRV_H */
diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c
index 590ee1d11d558e9a4a07abc40856764f1be4e10d..a54fb6ac8d16fbe62fc349d38e8757c8c0380c47 100644
--- a/windows/x11drv/event.c
+++ b/windows/x11drv/event.c
@@ -112,7 +112,7 @@ static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
 
 #ifdef HAVE_LIBXXSHM
 static void EVENT_ShmCompletion( XShmCompletionEvent *event );
-static int ShmCompletionType;
+static int ShmAvailable, ShmCompletionType;
 extern int XShmGetEventBase( Display * );/* Missing prototype for function in libXext. */
 #endif
 
@@ -151,7 +151,10 @@ static BOOL in_transition = FALSE; /* This is not used as for today */
 BOOL X11DRV_EVENT_Init(void)
 {
 #ifdef HAVE_LIBXXSHM
-    ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
+    ShmAvailable = XShmQueryExtension( display );
+    if (ShmAvailable) {
+        ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
+    }
 #endif
 
     /* Install the X event processing callback */
@@ -246,7 +249,7 @@ static void EVENT_ProcessEvent( XEvent *event )
   }
 
 #ifdef HAVE_LIBXXSHM
-  if (event->type == ShmCompletionType) {
+  if (ShmAvailable && (event->type == ShmCompletionType)) {
     EVENT_ShmCompletion( (XShmCompletionEvent*)event );
     return;
   }
@@ -2002,59 +2005,155 @@ ShmCompletion (waiting for app) waking up on shm_read
               (waiting for app) returns
               (idle)
 */
+
+typedef struct {
+  Drawable draw;
+  LONG state, waiter;
+  HANDLE sema;
+} shm_qs;
    
 /* FIXME: this is not pretty */
-static Drawable shm_draw = 0;
-static HANDLE shm_event = 0, shm_read = 0;
+static HANDLE shm_read = 0;
+
+#define SHM_MAX_Q 4
+static volatile shm_qs shm_q[SHM_MAX_Q];
 
 static void EVENT_ShmCompletion( XShmCompletionEvent *event )
 {
+  int n;
+
   TRACE("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() );
-  if (event->drawable == shm_draw) {
-    HANDLE event = shm_event;
-    shm_draw = 0;
-    SetEvent(event);
-    TRACE("Event object triggered\n" );
-  } else ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
+
+  for (n=0; n<SHM_MAX_Q; n++)
+    if ((shm_q[n].draw == event->drawable) && (shm_q[n].state == 0)) {
+      HANDLE sema = shm_q[n].sema;
+      if (!InterlockedCompareExchange((PVOID*)&shm_q[n].state, (PVOID)1, (PVOID)0)) {
+        ReleaseSemaphore(sema, 1, NULL);
+        TRACE("Signaling ShmCompletion (#%d) (semaphore %x)\n", n, sema);
+      }
+      return;
+    }
+
+  ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
 }
 
 int X11DRV_EVENT_PrepareShmCompletion( Drawable dw )
 {
-  if (shm_draw) {
-    ERR("Multiple ShmCompletion requests not implemented\n");
-    return 0;
-  }
-  TRACE("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 ) );
+  int n;
+
   if (!shm_read)
     shm_read = ConvertToGlobalHandle( FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE ) );
-  return shm_event;
+
+  for (n=0; n<SHM_MAX_Q; n++)
+    if (!shm_q[n].draw)
+      if (!InterlockedCompareExchange((PVOID*)&shm_q[n].draw, (PVOID)dw, (PVOID)0))
+        break;
+
+  if (n>=SHM_MAX_Q) {
+    ERR("Maximum number of outstanding ShmCompletions exceeded!\n");
+    return 0;
+  }
+
+  shm_q[n].state = 0;
+  if (!shm_q[n].sema) {
+    shm_q[n].sema = ConvertToGlobalHandle( CreateSemaphoreA( NULL, 0, 256, NULL ) );
+    TRACE("Allocated ShmCompletion slots have been increased to %d, new semaphore is %x\n", n+1, shm_q[n].sema);
+  }
+
+  TRACE("Prepared ShmCompletion (#%d) wait for drawable %ld (thread %lx) (time %ld)\n", n, dw, GetCurrentThreadId(), GetTickCount() );
+  return n+1;
+}
+
+static void X11DRV_EVENT_WaitReplaceShmCompletionInternal( int *compl, Drawable dw, int creat )
+{
+  int n = *compl;
+  LONG nn, st;
+  HANDLE sema;
+
+  if ((!n) || (creat && (!shm_q[n-1].draw))) {
+    nn = X11DRV_EVENT_PrepareShmCompletion(dw);
+    if (!(n=(LONG)InterlockedCompareExchange((PVOID*)compl, (PVOID)nn, (PVOID)n)))
+      return;
+    /* race for compl lost, clear slot */
+    shm_q[nn-1].draw = 0;
+    return;
+  }
+
+  if (dw && (shm_q[n-1].draw != dw)) {
+    /* this shouldn't happen with the current ddraw implementation */
+    FIXME("ShmCompletion replace with different drawable!\n");
+    return;
+  }
+
+  sema = shm_q[n-1].sema;
+  if (!sema) {
+    /* nothing to wait on (PrepareShmCompletion not done yet?), so probably nothing to wait for */
+    return;
+  }
+
+  nn = InterlockedExchangeAdd((PLONG)&shm_q[n-1].waiter, 1);
+  if ((!shm_q[n-1].draw) || (shm_q[n-1].state == 2)) {
+    /* too late, the wait was just cleared (wait complete) */
+    TRACE("Wait skip for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
+  } else {
+    TRACE("Waiting for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
+    if (nn) {
+      /* another thread is already waiting, let the primary waiter do the dirty work
+       * (to avoid TSX critical section contention - that could get really slow) */
+      WaitForSingleObject( sema, INFINITE );
+    } else
+    /* we're primary waiter - first check if it's already triggered */
+    if ( WaitForSingleObject( sema, 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] = sema;
+      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 );
+    }
+    TRACE("Wait complete (thread %lx) (time %ld)\n", GetCurrentThreadId(), GetTickCount() );
+
+    /* clear wait */
+    st = InterlockedExchange(&shm_q[n-1].state, 2);
+    if (st != 2) {
+      /* first waiter to return, release all other waiters */
+      nn = shm_q[n-1].waiter;
+      TRACE("Signaling %ld additional ShmCompletion (#%d) waiter(s), semaphore %x\n", nn-1, n-1, sema);
+      ReleaseSemaphore(sema, nn-1, NULL);
+    }
+  }
+  nn = InterlockedDecrement((LPLONG)&shm_q[n-1].waiter);
+  if (!nn) {
+    /* last waiter to return, replace drawable and prepare new wait */
+    shm_q[n-1].draw = dw;
+    shm_q[n-1].state = 0;
+  }
+}
+
+void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw )
+{
+  X11DRV_EVENT_WaitReplaceShmCompletionInternal( compl, dw, 1 );
 }
 
 void X11DRV_EVENT_WaitShmCompletion( int compl )
 {
   if (!compl) return;
-  TRACE("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];
+  X11DRV_EVENT_WaitReplaceShmCompletionInternal( &compl, 0, 0 );
+}
 
-    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("Wait complete (time %ld)\n", GetTickCount() );
+void X11DRV_EVENT_WaitShmCompletions( Drawable dw )
+{
+  int n;
+
+  for (n=0; n<SHM_MAX_Q; n++)
+    if (shm_q[n].draw == dw)
+      X11DRV_EVENT_WaitShmCompletion( n+1 );
 }
 
 #endif /* defined(HAVE_LIBXXSHM) */