If localloop if false, there's no event checking or calls to serverEventProc.
This is why no PropertyChange event was being seen, so there should be no
reason to call serverEventProc(dpy, NULL). This new loop will would call
XCheckWindowEvent every ~1/2 sec in any case, and I don't think any change
events are actually missing.

With that said, the reason I've changed this from EventsQueued to
XCheckWindowEvent is because I'm thinking that the call to XSelectInput
when the window is created is actually registering the window to receive
PropertyChange events on the dpy given. i.e. not just events that are
only for that window. The X11 docs are a little unclear, but they seem
to suggest this. XCheckWindowEvent will only remove those events that are
for the commWindow, so there's no danger of removing events another window
might be looking for.

The other difference is that if no events are found, it will flush the
output buffer. Using QueuedAfterReading with EventsQueued didn't do this,
but I don't think this is a big deal.

Side note:
  This uses a while loop, since some events would be PropertyDelete events.
  You could use an 'if' here, since serverEventProc will process the whole
  property. But, this would leave those AND could leave others, causing the
  next call to immediately process the property due to an older event.
  Of course, endCond keeps the loop going until we actually get what we
  want from the property, so this may not be a big deal.

  And, since serverEventProc is doing this:

        if (eventPtr->xproperty.atom != commProperty
                || eventPtr->xproperty.state != PropertyNewValue)
            return;

  You could use XCheckIfEvent with a predicate function to check this
  and the event's type and window, then just call serverEventProc
  if everything is correct. However, this too would leave the PropertyDelete
  events to build up in the queue, as they would never be removed.



For select/poll, even though the delay was set for 50msec, having the setup
for this within the loop was causing more than this. And of course, that
time spent is fixed; meaning it's not waiting for the fd to be ready.
Once it's moved out of the loop, the loop processes much faster. So much,
that the delay really needs to be increased. Since the timeout is based on
whole seconds, I've used a 1/2 sec delay. I've seen no problems with this,
as select/poll will release as soon as the fd is ready. In fact, X11 (XCB)
is using select against this same fd when EventsQueued is called.
(I haven't checked the X11/XCB code since changing this to XCheckWindowEvent,
but surely it's the same).

Also, this new loop will call XCheckWindowEvent before any polling is done.
So, if the fd has already been read by X11/XCB and would cause select/poll
to block until it times out, this eliminates that possible delay, since
serverEventProc should immediately find what we're waiting for.

I'm not sure about ui_delay() and if that could/should be changed.

Also, I've used FD_SETSIZE with select(), although this could probably
just be '1' from what I understand, since there's only one fd to check.
The point was really not to call ConnectionNumber inside the loop.


Other:

I've seen calls to WindowValid in several places, but I'm not sure
why this would need to be constantly checked. At least, not when the
window in question is commWindow. Would this not mean the X server
crashed or something? In which case, select/poll would break here.
Also worth noting, if this fd became invalid, X would raise XIOError,
so you _could_ wrap this with calls to XSetIOErrorHandler.

ServerWait does not use the Window (w) passed to it.
The window passed from serverSendToVim is the server's window,
which you wouldn't want to use here with XCheckWindowEvent.
The other call is from serverReadReply, which has a window passed in to it.
That same window is also passed to serverReplyFind.
I haven't followed all that code, or other places this may be called.
But, it seems this would always be the same as commWindow,
in which case it wouldn't really need this parameter?

Also, I didn't want to move too much around, but if the
above shown checks for the xproperty.atom and .state were placed
within the XCheckWindowEvent loop, then the eventPtr parameter
could be removed from serverEventProc. You might even be able to use a
static Display *commDisplay and avoid passing dpy around everywhere,
but I could be missing something :)


Results:

As an example of these changes, these are benchmarks from the tests
within this project: https://github.com/burns/vim_highlighting

Before:
                          user     system      total        real
server_one:           0.050000   0.130000   1.600000 (  8.188443)
server_two:           0.040000   0.090000   0.730000 ( 20.040763)
server_three:         0.010000   0.030000   0.320000 (  6.009709)

After:
                          user     system      total        real
server_one:           0.040000   0.120000   1.590000 (  7.225723)
server_two:           0.020000   0.060000   0.680000 (  7.348854)
server_three:         0.020000   0.030000   0.360000 (  6.119608)

The 'server_two' method here is making 20 calls using:

html = %x{
    gvim -fes -u NONE                         \
  +'call remote_send("#{ SERVER_NAME }",      \
    '\\'':bfirst!                           | \
    exe bufnr("%").",".bufnr("$")."bd!"     | \
    e #{ tempfile.path }                    | \
    setlocal filetype=#{ lang }             | \
    syn on                                  | \
    run! syntax/2html.vim                   | \
    call server2client(expand("<client>"),    \
      join(getline(1,"$"),                    \
    "\\n"))<cr>'\\'',                         \
    "sid")'                                   \
  +'redir >> /dev/stdout                    | \
    echo remote_read(sid)                   | \
    redir END                               | \
    q'
}

The sources for this and the other benchmark methods are here:
https://github.com/burns/vim_highlighting/blob/master/lib/highlighters.rb

--- a/src/if_xcmdsrv.c	Thu Jul 05 02:20:43 2012 -0400
+++ b/src/if_xcmdsrv.c	Fri Jul 06 17:36:09 2012 -0400
@@ -572,63 +572,58 @@
 {
     time_t	    start;
     time_t	    now;
-    time_t	    lastChk = 0;
     XEvent	    event;
-    XPropertyEvent *e = (XPropertyEvent *)&event;
-#   define SEND_MSEC_POLL 50
+
+#   define UI_MSEC_DELAY 50
+#   define SEND_MSEC_POLL 500
+#ifndef HAVE_SELECT
+    struct pollfd   fds;
+
+    fds.fd = ConnectionNumber(dpy);
+    fds.events = POLLIN;
+#else
+    fd_set	    fds;
+    struct timeval  tv;
+
+    tv.tv_sec = 0;
+    tv.tv_usec =  SEND_MSEC_POLL * 1000;
+    FD_ZERO(&fds);
+    FD_SET(ConnectionNumber(dpy), &fds);
+#endif

     time(&start);
-    while (endCond(endData) == 0)
+    while (TRUE)
     {
-	time(&now);
-	if (seconds >= 0 && (now - start) >= seconds)
-	    break;
-	if (now != lastChk)
-	{
-	    lastChk = now;
-	    if (!WindowValid(dpy, w))
-		break;
-	    /*
-	     * Sometimes the PropertyChange event doesn't come.
-	     * This can be seen in eg: vim -c 'echo remote_expr("gvim", "3+2")'
-	     */
-	    serverEventProc(dpy, NULL);
-	}
-	if (localLoop)
-	{
+        while (XCheckWindowEvent(dpy, commWindow, PropertyChangeMask, &event))
+            serverEventProc(dpy, &event);
+
+        if (endCond(endData) != 0)
+            break;
+        else
+        {
+            time(&now);
+            if (seconds >= 0 && (now - start) >= seconds)
+                break;
+
 	    /* Just look out for the answer without calling back into Vim */
+            if (localLoop)
+            {
 #ifndef HAVE_SELECT
-	    struct pollfd   fds;
-
-	    fds.fd = ConnectionNumber(dpy);
-	    fds.events = POLLIN;
-	    if (poll(&fds, 1, SEND_MSEC_POLL) < 0)
-		break;
+                if (poll(&fds, 1, SEND_MSEC_POLL) < 0)
+                    break;
 #else
-	    fd_set	    fds;
-	    struct timeval  tv;
-
-	    tv.tv_sec = 0;
-	    tv.tv_usec =  SEND_MSEC_POLL * 1000;
-	    FD_ZERO(&fds);
-	    FD_SET(ConnectionNumber(dpy), &fds);
-	    if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0)
-		break;
+                if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 0)
+                    break;
 #endif
-	    while (XEventsQueued(dpy, QueuedAfterReading) > 0)
-	    {
-		XNextEvent(dpy, &event);
-		if (event.type == PropertyNotify && e->window == commWindow)
-		    serverEventProc(dpy, &event);
-	    }
-	}
-	else
-	{
-	    if (got_int)
-		break;
-	    ui_delay((long)SEND_MSEC_POLL, TRUE);
-	    ui_breakcheck();
-	}
+            }
+            else
+            {
+                if (got_int)
+                    break;
+                ui_delay((long)UI_MSEC_DELAY, TRUE);
+                ui_breakcheck();
+            }
+        }
     }
 }

