xserver: Branch 'master' - 8 commits

Keith Packard keithp at kemper.freedesktop.org
Mon Apr 30 20:39:54 PDT 2012


 Xi/xiqueryversion.c                |   41 ++++++-------
 dix/devices.c                      |    1 
 dix/getevents.c                    |    3 
 dix/globals.c                      |    1 
 dix/touch.c                        |    5 +
 hw/dmx/dmxlog.c                    |   13 +++-
 hw/dmx/dmxlog.h                    |   19 +++---
 include/opaque.h                   |    1 
 man/Xserver.man                    |    7 ++
 os/WaitFor.c                       |   18 +++++
 os/connection.c                    |   68 +++++++++++++++-------
 os/utils.c                         |    9 ++
 test/Makefile.am                   |    2 
 test/xi2/protocol-xiqueryversion.c |  113 +++++++++++++++++++++++++++++++++++++
 14 files changed, 248 insertions(+), 53 deletions(-)

New commits:
commit f3410b97cf9b48a47bee3d15d232f8a88e75f4ef
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Apr 30 10:01:48 2012 +1000

    dix: when disabling a device, release all buttons and keys
    
    A suspend-induced device disable may happen before the device gets to see
    the button release event. On resume, the server's internal state still has
    some buttons pressed, causing inconsistent behaviour.
    
    Force the release and the matching events to be sent to the client.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/devices.c b/dix/devices.c
index 600f8b7..7f38865 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -437,6 +437,7 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent)
     if (*prev != dev)
         return FALSE;
 
+    ReleaseButtonsAndKeys(dev);
     SyncRemoveDeviceIdleTime(dev->idle_counter);
     dev->idle_counter = NULL;
 
commit af88b43f9e604157b74270d609c08bdfa256a792
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Apr 27 16:31:17 2012 +1000

    dix: don't emulate scroll events for non-existing axes (#47281)
    
    Test case:
    - create a device with REL_HWHEEL and ABS_X and ABS_Y. evdev 2.7.0 will set
      that up as device with 1 relative axis
    - move pointer to VGA1
    - xrandr --output VGA1 --off
    
    Warps the pointer to the new spot and calls GPE with the x/y mask bits set.
    When running through the loop to check for scroll event, this overruns the
    axes and may try to emulate scroll events based on random garbage in the
    memory. If that memory contained non-zero for the scroll type but near-zero
    for the increment field, the server would hang in an infinite loop.
    
    This was the trigger for this suggested, never-merged, patch here:
    http://patchwork.freedesktop.org/patch/9543/
    
    X.Org Bug 47281 <http://bugs.freedesktop.org/show_bug.cgi?id=47281>
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/getevents.c b/dix/getevents.c
index 23bbe06..c960d44 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -1598,6 +1598,9 @@ GetPointerEvents(InternalEvent *events, DeviceIntPtr pDev, int type,
     /* Now turn the smooth-scrolling axes back into emulated button presses
      * for legacy clients, based on the integer delta between before and now */
     for (i = 0; i < valuator_mask_size(&mask); i++) {
+        if (i >= pDev->valuator->numAxes)
+            break;
+
         if (!valuator_mask_isset(&mask, i))
             continue;
 
commit 08962951de969b9d8c870af8b6e47303dc0decfd
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Apr 27 10:52:39 2012 +1000

    os: make timers signal-safe
    
    If TimerSet() is called from a signal handler (synaptics tap handling code)
    may result in list corruption if we're currently inside TimerSet().
    
    See backtrace in
    https://bugzilla.redhat.com/show_bug.cgi?id=814869
    
    Block signals for all list manipulations in the timers.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/os/WaitFor.c b/os/WaitFor.c
index 95e64ba..393890f 100644
--- a/os/WaitFor.c
+++ b/os/WaitFor.c
@@ -382,6 +382,7 @@ CheckAllTimers(void)
     OsTimerPtr timer;
     CARD32 now;
 
+    OsBlockSignals();
  start:
     now = GetTimeInMillis();
 
@@ -391,6 +392,7 @@ CheckAllTimers(void)
             goto start;
         }
     }
+    OsReleaseSignals();
 }
 
 static void
@@ -398,11 +400,13 @@ DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev)
 {
     CARD32 newTime;
 
+    OsBlockSignals();
     *prev = timer->next;
     timer->next = NULL;
     newTime = (*timer->callback) (timer, now, timer->arg);
     if (newTime)
         TimerSet(timer, 0, newTime, timer->callback, timer->arg);
+    OsReleaseSignals();
 }
 
 OsTimerPtr
@@ -418,6 +422,7 @@ TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
             return NULL;
     }
     else {
+        OsBlockSignals();
         for (prev = &timers; *prev; prev = &(*prev)->next) {
             if (*prev == timer) {
                 *prev = timer->next;
@@ -426,6 +431,7 @@ TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
                 break;
             }
         }
+        OsReleaseSignals();
     }
     if (!millis)
         return timer;
@@ -445,26 +451,32 @@ TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
         if (!millis)
             return timer;
     }
+    OsBlockSignals();
     for (prev = &timers;
          *prev && (int) ((*prev)->expires - millis) <= 0;
          prev = &(*prev)->next);
     timer->next = *prev;
     *prev = timer;
+    OsReleaseSignals();
     return timer;
 }
 
 Bool
 TimerForce(OsTimerPtr timer)
 {
+    int rc = FALSE;
     OsTimerPtr *prev;
 
+    OsBlockSignals();
     for (prev = &timers; *prev; prev = &(*prev)->next) {
         if (*prev == timer) {
             DoTimer(timer, GetTimeInMillis(), prev);
-            return TRUE;
+            rc = TRUE;
+            break;
         }
     }
-    return FALSE;
+    OsReleaseSignals();
+    return rc;
 }
 
 void
@@ -474,12 +486,14 @@ TimerCancel(OsTimerPtr timer)
 
     if (!timer)
         return;
+    OsBlockSignals();
     for (prev = &timers; *prev; prev = &(*prev)->next) {
         if (*prev == timer) {
             *prev = timer->next;
             break;
         }
     }
+    OsReleaseSignals();
 }
 
 void
commit d662fa2450856777b59c4b62b912395a8bfd52fd
Author: Michal Suchanek <hramrach at gmail.com>
Date:   Thu Apr 26 15:11:20 2012 +0200

    dmx: Annotate dmxlog.c with _X_ATTRIBUTE_PRINTF and _X_NORETURN
    
    and fix resulting printf warning in dmxLogVisual
    
    Signed-off-by: Michal Suchanek <hramrach at gmail.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

diff --git a/hw/dmx/dmxlog.c b/hw/dmx/dmxlog.c
index 33aee59..3249c48 100644
--- a/hw/dmx/dmxlog.c
+++ b/hw/dmx/dmxlog.c
@@ -86,6 +86,8 @@ ErrorF(const char *format, ...)
 
 /** Provide an VFatalError function when used stand-alone. */
 static void
+VFatalError(const char *format, va_list args) _X_ATTRIBUTE_PRINTF(1, 0) _X_NORETURN;
+static void
 VFatalError(const char *format, va_list args)
 {
     vfprintf(stderr, format, args);     /* RATS: We assume the format string
@@ -104,7 +106,9 @@ VErrorF(const char *format, va_list args)
 }
 #else
 /** This function was removed between XFree86 4.3.0 and XFree86 4.4.0. */
-extern void AbortServer(void);
+extern void AbortServer(void) _X_NORETURN;
+static void
+VFatalError(const char *format, va_list args) _X_ATTRIBUTE_PRINTF(1, 0) _X_NORETURN;
 static void
 VFatalError(const char *format, va_list args)
 {
@@ -163,6 +167,8 @@ dmxHeader(dmxLogLevel logLevel, DMXInputInfo * dmxInput,
 /* Prints the error message with the appropriate low-level X output
  * routine. */
 static void
+dmxMessage(dmxLogLevel logLevel, const char *format, va_list args) _X_ATTRIBUTE_PRINTF(2, 0);
+static void
 dmxMessage(dmxLogLevel logLevel, const char *format, va_list args)
 {
     if (logLevel == dmxFatal || logLevel >= dmxCurrentLogLevel) {
@@ -300,10 +306,11 @@ dmxLogVisual(DMXScreenInfo * dmxScreen, XVisualInfo * vi, int defaultVisual)
         class = "DirectColor";
         break;
     }
+#define VisualLogFormat "0x%02lx %s %2db %db/rgb %3d 0x%04lx 0x%04lx 0x%04lx%s\n"
 
     if (dmxScreen) {
         dmxLogOutput(dmxScreen,
-                     "0x%02x %s %2db %db/rgb %3d 0x%04x 0x%04x 0x%04x%s\n",
+                     VisualLogFormat,
                      vi->visualid, class, vi->depth, vi->bits_per_rgb,
                      vi->colormap_size,
                      vi->red_mask, vi->green_mask, vi->blue_mask,
@@ -311,7 +318,7 @@ dmxLogVisual(DMXScreenInfo * dmxScreen, XVisualInfo * vi, int defaultVisual)
     }
     else {
         dmxLog(dmxInfo,
-               "  0x%02x %s %2db %db/rgb %3d 0x%04x 0x%04x 0x%04x%s\n",
+               "  " VisualLogFormat,
                vi->visualid, class, vi->depth, vi->bits_per_rgb,
                vi->colormap_size,
                vi->red_mask, vi->green_mask, vi->blue_mask,
diff --git a/hw/dmx/dmxlog.h b/hw/dmx/dmxlog.h
index 4d4cd26..162484b 100644
--- a/hw/dmx/dmxlog.h
+++ b/hw/dmx/dmxlog.h
@@ -55,18 +55,23 @@ typedef enum {
 /* Logging functions used by Xserver/hw/dmx routines. */
 extern dmxLogLevel dmxSetLogLevel(dmxLogLevel newLevel);
 extern dmxLogLevel dmxGetLogLevel(void);
-extern void dmxLog(dmxLogLevel logLevel, const char *format, ...);
-extern void dmxLogCont(dmxLogLevel logLevel, const char *format, ...);
+extern void dmxLog(dmxLogLevel logLevel, const char *format,
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
+extern void dmxLogCont(dmxLogLevel logLevel, const char *format,
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
 extern const char *dmxEventName(int type);
 
 #ifndef DMX_LOG_STANDALONE
-extern void dmxLogOutput(DMXScreenInfo * dmxScreen, const char *format, ...);
+extern void dmxLogOutput(DMXScreenInfo * dmxScreen, const char *format,
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
 extern void dmxLogOutputCont(DMXScreenInfo * dmxScreen, const char *format,
-                             ...);
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
 extern void dmxLogOutputWarning(DMXScreenInfo * dmxScreen, const char *format,
-                                ...);
-extern void dmxLogInput(DMXInputInfo * dmxInput, const char *format, ...);
-extern void dmxLogInputCont(DMXInputInfo * dmxInput, const char *format, ...);
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
+extern void dmxLogInput(DMXInputInfo * dmxInput, const char *format,
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
+extern void dmxLogInputCont(DMXInputInfo * dmxInput, const char *format,
+                             ...) _X_ATTRIBUTE_PRINTF(2, 3);
 extern void dmxLogArgs(dmxLogLevel logLevel, int argc, char **argv);
 extern void dmxLogVisual(DMXScreenInfo * dmxScreen, XVisualInfo * vi,
                          int defaultVisual);
commit 5c361d59c5031d9b3f7f9093a52d2b1ff4d9ae5f
Author: Chase Douglas <chase.douglas at canonical.com>
Date:   Fri Apr 20 11:08:15 2012 -0700

    TouchListenerAcceptReject: Warn and return early on bad listener index
    
    Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
    Reviewed-by: Bryce Harrington <bryce at canonical.com>
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

diff --git a/dix/touch.c b/dix/touch.c
index dd16367..401cb98 100644
--- a/dix/touch.c
+++ b/dix/touch.c
@@ -966,6 +966,11 @@ TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener,
     int nev;
     int i;
 
+    BUG_WARN(listener < 0);
+    BUG_WARN(listener >= ti->num_listeners);
+    if (listener < 0 || listener >= ti->num_listeners)
+        return BadMatch;
+
     if (listener > 0) {
         if (mode == XIRejectTouch)
             TouchRejected(dev, ti, ti->listeners[listener].listener, NULL);
commit 88bacc49f06da5927f716869f5a32672a8297ed0
Author: Chase Douglas <chase.douglas at canonical.com>
Date:   Wed Apr 4 15:29:42 2012 -0700

    os: Add -displayfd option
    
    This option specifies a file descriptor in the launching process.  X
    will scan for an available display number and write that number back to
    the launching process, at the same time as SIGUSR1 generation.  This
    means display managers don't need to guess at available display numbers.
    As a consequence, if X fails to start when using -displayfd, it's not
    because the display was in use, so there's no point in retrying the X
    launch on a higher display number.
    
    Signed-off-by: Adam Jackson <ajax at redhat.com>
    Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
    Reviewed-by: Julien Cristau <jcristau at debian.org>
    Tested-by: Julien Cristau <jcristau at debian.org>
    Reviewed-by: Alan Coopersmith <alan.coopersmith at oracle.com>
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

diff --git a/dix/globals.c b/dix/globals.c
index a564575..332b91f 100644
--- a/dix/globals.c
+++ b/dix/globals.c
@@ -128,6 +128,7 @@ int defaultColorVisualClass = -1;
 int monitorResolution = 0;
 
 char *display;
+int displayfd;
 char *ConnectionInfo;
 
 CARD32 TimeOutValue = DEFAULT_TIMEOUT * MILLI_PER_SECOND;
diff --git a/include/opaque.h b/include/opaque.h
index 9ca408a..b76ab6e 100644
--- a/include/opaque.h
+++ b/include/opaque.h
@@ -50,6 +50,7 @@ extern _X_EXPORT int ScreenSaverAllowExposures;
 extern _X_EXPORT int defaultScreenSaverBlanking;
 extern _X_EXPORT int defaultScreenSaverAllowExposures;
 extern _X_EXPORT char *display;
+extern _X_EXPORT int displayfd;
 
 extern _X_EXPORT int defaultBackingStore;
 extern _X_EXPORT Bool disableBackingStore;
diff --git a/man/Xserver.man b/man/Xserver.man
index 0cd9b94..8d243d6 100644
--- a/man/Xserver.man
+++ b/man/Xserver.man
@@ -127,6 +127,13 @@ Not obeyed by all servers.
 .B \-core
 causes the server to generate a core dump on fatal errors.
 .TP 8
+.B \-displayfd \fIfd\fP
+specifies a file descriptor in the launching process.  Rather than specify
+a display number, the X server will attempt to listen on successively higher
+display numbers, and upon finding a free one, will write the port number back
+on this file descriptor as a newline-terminated string.  The \-pn option is
+ignored when using \-displayfd.
+.TP 8
 .B \-deferglyphs \fIwhichfonts\fP
 specifies the types of fonts for which the server should attempt to use
 deferred glyph loading.  \fIwhichfonts\fP can be all (all fonts),
diff --git a/os/connection.c b/os/connection.c
index 1099752..039942f 100644
--- a/os/connection.c
+++ b/os/connection.c
@@ -142,6 +142,7 @@ Bool AnyClientsWriteBlocked;    /* true if some client blocked on write */
 static Bool RunFromSmartParent; /* send SIGUSR1 to parent process */
 Bool RunFromSigStopParent;      /* send SIGSTOP to our own process; Upstart (or
                                    equivalent) will send SIGCONT back. */
+static char dynamic_display[7]; /* display name */
 Bool PartialNetwork;            /* continue even if unable to bind all addrs */
 static Pid_t ParentProcess;
 
@@ -350,6 +351,10 @@ void
 NotifyParentProcess(void)
 {
 #if !defined(WIN32)
+    if (dynamic_display[0]) {
+        write(displayfd, dynamic_display, strlen(dynamic_display));
+        close(displayfd);
+    }
     if (RunFromSmartParent) {
         if (ParentProcess > 1) {
             kill(ParentProcess, SIGUSR1);
@@ -360,6 +365,18 @@ NotifyParentProcess(void)
 #endif
 }
 
+static Bool
+TryCreateSocket(int num, int *partial)
+{
+    char port[20];
+
+    snprintf(port, sizeof(port), "%d", num);
+
+    return (_XSERVTransMakeAllCOTSServerListeners(port, partial,
+                                                  &ListenTransCount,
+                                                  &ListenTransConns) >= 0);
+}
+
 /*****************
  * CreateWellKnownSockets
  *    At initialization, create the sockets to listen on for new clients.
@@ -370,7 +387,6 @@ CreateWellKnownSockets(void)
 {
     int i;
     int partial;
-    char port[20];
 
     FD_ZERO(&AllSockets);
     FD_ZERO(&AllClients);
@@ -386,29 +402,41 @@ CreateWellKnownSockets(void)
 
     FD_ZERO(&WellKnownConnections);
 
-    snprintf(port, sizeof(port), "%d", atoi(display));
-
-    if ((_XSERVTransMakeAllCOTSServerListeners(port, &partial,
-                                               &ListenTransCount,
-                                               &ListenTransConns) >= 0) &&
-        (ListenTransCount >= 1)) {
-        if (!PartialNetwork && partial) {
-            FatalError("Failed to establish all listening sockets");
+    /* display is initialized to "0" by main(). It is then set to the display
+     * number if specified on the command line, or to NULL when the -displayfd
+     * option is used. */
+    if (display) {
+        if (TryCreateSocket(atoi(display), &partial) &&
+            ListenTransCount >= 1)
+            if (!PartialNetwork && partial)
+                FatalError ("Failed to establish all listening sockets");
+    }
+    else { /* -displayfd */
+        Bool found = 0;
+        for (i = 0; i < 65535 - X_TCP_PORT; i++) {
+            if (TryCreateSocket(i, &partial) && !partial) {
+                found = 1;
+                break;
+            }
+            else
+                CloseWellKnownConnections();
         }
-        else {
-            ListenTransFds = malloc(ListenTransCount * sizeof(int));
+        if (!found)
+            FatalError("Failed to find a socket to listen on");
+        snprintf(dynamic_display, sizeof(dynamic_display), "%d", i);
+        display = dynamic_display;
+    }
 
-            for (i = 0; i < ListenTransCount; i++) {
-                int fd = _XSERVTransGetConnectionNumber(ListenTransConns[i]);
+    ListenTransFds = malloc(ListenTransCount * sizeof (int));
 
-                ListenTransFds[i] = fd;
-                FD_SET(fd, &WellKnownConnections);
+    for (i = 0; i < ListenTransCount; i++) {
+        int fd = _XSERVTransGetConnectionNumber(ListenTransConns[i]);
 
-                if (!_XSERVTransIsLocal(ListenTransConns[i])) {
-                    DefineSelf(fd);
-                }
-            }
-        }
+        ListenTransFds[i] = fd;
+        FD_SET(fd, &WellKnownConnections);
+
+        if (!_XSERVTransIsLocal(ListenTransConns[i]))
+            DefineSelf (fd);
     }
 
     if (!XFD_ANYSET(&WellKnownConnections))
diff --git a/os/utils.c b/os/utils.c
index 30592d2..3a1ef93 100644
--- a/os/utils.c
+++ b/os/utils.c
@@ -659,6 +659,15 @@ ProcessCommandLine(int argc, char *argv[])
             else
                 UseMsg();
         }
+        else if (strcmp(argv[i], "-displayfd") == 0) {
+            if (++i < argc) {
+                displayfd = atoi(argv[i]);
+                display = NULL;
+                nolock = TRUE;
+            }
+            else
+                UseMsg();
+        }
 #ifdef DPMSExtension
         else if (strcmp(argv[i], "dpms") == 0)
             /* ignored for compatibility */ ;
commit 1d82ec95942b88dd01f0ac6b883368360a0b5fe6
Author: Michal Suchanek <hramrach at gmail.com>
Date:   Mon Apr 23 13:52:40 2012 +0200

    xserver: Fix out-of-tree build
    
    Fixes regression caused by ccb3e78124fb05defd0c9b438746b79d84dfc3ae
    
    Signed-off-by: Michal Suchanek <hramrach at gmail.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

diff --git a/test/Makefile.am b/test/Makefile.am
index 8582397..b2a53aa 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -36,7 +36,7 @@ misc_LDADD=$(TEST_LDADD)
 fixes_LDADD=$(TEST_LDADD)
 xfree86_LDADD=$(TEST_LDADD)
 touch_LDADD=$(TEST_LDADD)
-hashtabletest_LDADD=$(TEST_LDADD) ../Xext/hashtable.c
+hashtabletest_LDADD=$(TEST_LDADD) $(top_srcdir)/Xext/hashtable.c
 
 libxservertest_la_LIBADD = $(XSERVER_LIBS)
 if XORG
commit ea51e9b2877df60135edaf2a8f88d0f2a2b41060
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Apr 23 10:35:53 2012 +1000

    Xi: return BadValue on XIQueryVersion if the version is less than first call
    
    Clients that use plugin systems may require multiple calls to
    XIQueryVersion from different plugins. The current error handling requires
    client-side synchronisation of version numbers.
    
    The first call to XIQueryVersion defines the server behaviour. Once cached,
    always return that version number to any clients. Unless a client requests a
    version lower than the first defined one, then a BadValue must be returned
    to be protocol-compatible.
    
    Introduced in 2c23ef83b0e03e163aeeb06133538606886f4e9c
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Jeremy Huddleston <jeremyhu at apple.com>

diff --git a/Xi/xiqueryversion.c b/Xi/xiqueryversion.c
index fc0ca75..6081c41 100644
--- a/Xi/xiqueryversion.c
+++ b/Xi/xiqueryversion.c
@@ -70,28 +70,29 @@ ProcXIQueryVersion(ClientPtr client)
 
     pXIClient = dixLookupPrivate(&client->devPrivates, XIClientPrivateKey);
 
-    if (pXIClient->major_version &&
-           (stuff->major_version != pXIClient->major_version ||
-            stuff->minor_version != pXIClient->minor_version))
-    {
-        client->errorValue = stuff->major_version;
-        return BadValue;
+    if (pXIClient->major_version) {
+        if (version_compare(stuff->major_version, stuff->minor_version,
+                            pXIClient->major_version, pXIClient->minor_version) < 0) {
+            client->errorValue = stuff->major_version;
+            return BadValue;
+        }
+        major = pXIClient->major_version;
+        minor = pXIClient->minor_version;
+    } else {
+        if (version_compare(XIVersion.major_version, XIVersion.minor_version,
+                    stuff->major_version, stuff->minor_version) > 0) {
+            major = stuff->major_version;
+            minor = stuff->minor_version;
+        }
+        else {
+            major = XIVersion.major_version;
+            minor = XIVersion.minor_version;
+        }
+
+        pXIClient->major_version = major;
+        pXIClient->minor_version = minor;
     }
 
-
-    if (version_compare(XIVersion.major_version, XIVersion.minor_version,
-                        stuff->major_version, stuff->minor_version) > 0) {
-        major = stuff->major_version;
-        minor = stuff->minor_version;
-    }
-    else {
-        major = XIVersion.major_version;
-        minor = XIVersion.minor_version;
-    }
-
-    pXIClient->major_version = major;
-    pXIClient->minor_version = minor;
-
     memset(&rep, 0, sizeof(xXIQueryVersionReply));
     rep.repType = X_Reply;
     rep.RepType = X_XIQueryVersion;
diff --git a/test/xi2/protocol-xiqueryversion.c b/test/xi2/protocol-xiqueryversion.c
index 2552307..1347e86 100644
--- a/test/xi2/protocol-xiqueryversion.c
+++ b/test/xi2/protocol-xiqueryversion.c
@@ -54,6 +54,8 @@ struct test_data {
     int minor_client;
     int major_server;
     int minor_server;
+    int major_cached;
+    int minor_cached;
 };
 
 static void
@@ -82,6 +84,24 @@ reply_XIQueryVersion(ClientPtr client, int len, char *data, void *userdata)
     assert((sver > cver) ? ver == cver : ver == sver);
 }
 
+static void
+reply_XIQueryVersion_multiple(ClientPtr client, int len, char *data, void *userdata)
+{
+    xXIQueryVersionReply *rep = (xXIQueryVersionReply *) data;
+    struct test_data *versions = (struct test_data *) userdata;
+
+    reply_check_defaults(rep, len, XIQueryVersion);
+    assert(rep->length == 0);
+
+    if (versions->major_cached == -1) {
+        versions->major_cached = rep->major_version;
+        versions->minor_cached = rep->minor_version;
+    }
+
+    assert(versions->major_cached == rep->major_version);
+    assert(versions->minor_cached == rep->minor_version);
+}
+
 /**
  * Run a single test with server version smaj.smin and client
  * version cmaj.cmin. Verify that return code is equal to 'error'.
@@ -173,12 +193,105 @@ test_XIQueryVersion(void)
     reply_handler = NULL;
 }
 
+
+static void
+test_XIQueryVersion_multiple(void)
+{
+    xXIQueryVersionReq request;
+    ClientRec client;
+    struct test_data versions;
+    int rc;
+
+    request_init(&request, XIQueryVersion);
+    client = init_client(request.length, &request);
+
+    /* Change the server to support 2.2 */
+    XIVersion.major_version = 2;
+    XIVersion.minor_version = 2;
+
+    reply_handler = reply_XIQueryVersion_multiple;
+    userdata = (void *) &versions;
+
+    /* run 1 */
+    versions.major_cached = -1;
+    versions.minor_cached = -1;
+
+    /* client is lower than server, noncached */
+    request.major_version = 2;
+    request.minor_version = 1;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    /* client is higher than server, cached */
+    request.major_version = 2;
+    request.minor_version = 3;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    /* client is equal, cached */
+    request.major_version = 2;
+    request.minor_version = 2;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    /* client is low than cached */
+    request.major_version = 2;
+    request.minor_version = 0;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == BadValue);
+
+    /* run 2 */
+    client = init_client(request.length, &request);
+    XIVersion.major_version = 2;
+    XIVersion.minor_version = 2;
+    versions.major_cached = -1;
+    versions.minor_cached = -1;
+
+    request.major_version = 2;
+    request.minor_version = 2;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    request.major_version = 2;
+    request.minor_version = 3;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    request.major_version = 2;
+    request.minor_version = 1;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == BadValue);
+
+    /* run 3 */
+    client = init_client(request.length, &request);
+    XIVersion.major_version = 2;
+    XIVersion.minor_version = 2;
+    versions.major_cached = -1;
+    versions.minor_cached = -1;
+
+    request.major_version = 2;
+    request.minor_version = 3;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    request.major_version = 2;
+    request.minor_version = 2;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == Success);
+
+    request.major_version = 2;
+    request.minor_version = 1;
+    rc = ProcXIQueryVersion(&client);
+    assert(rc == BadValue);
+}
+
 int
 main(int argc, char **argv)
 {
     init_simple();
 
     test_XIQueryVersion();
+    test_XIQueryVersion_multiple();
 
     return 0;
 }


More information about the xorg-commit mailing list