/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com)          */
/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>  */
/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>                 */
/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>                */
/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)           */
/*                                                                        */
/* NXCOMP, NX protocol compression and NX extensions to this software     */
/* are copyright of the aforementioned persons and companies.             */
/*                                                                        */
/* Redistribution and use of the present software is allowed according    */
/* to terms specified in the file LICENSE.nxcomp which comes in the       */
/* source distribution.                                                   */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/* NOTE: This software has received contributions from various other      */
/* contributors, only the core maintainers and supporters are listed as   */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well.                                          */
/*                                                                        */
/**************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <X11/X.h>
#include <X11/Xatom.h>

#include "NXproto.h"
#include "NXrender.h"

#include "ClientChannel.h"

#include "EncodeBuffer.h"
#include "DecodeBuffer.h"

#include "StaticCompressor.h"

#include "Statistics.h"
#include "Proxy.h"

#include "PutImage.h"
#include "PutPackedImage.h"

extern Proxy *proxy;

//
// Set the verbosity level. You also
// need to define OPCODES in Misc.cpp
// if you want literals instead of
// opcodes' numbers.
//

#define PANIC
#define WARNING
#undef  OPCODES
#undef  TEST
#undef  DEBUG
#undef  DUMP

//
// Log the important tracepoints related
// to writing packets to the peer proxy.
//

#undef  FLUSH

//
// Log the operations related to splits.
//

#undef  SPLIT

//
// Define this to trace the invocations
// of the agent's callbacks.
//

#undef  CALLBACK

//
// By defining this, a simple procedure is activated at
// startup which just allocates and deallocates plenty
// of cache objects. This is used to help determine the
// current memory requirements.
//

#undef  MEMORY

//
// Inspects target of common X operations.
//

#undef  TARGETS

#ifdef TARGETS

#include <set>
#include <map>

typedef set < unsigned int, less<unsigned int> > T_windows;
typedef set < unsigned int, less<unsigned int> > T_pixmaps;
typedef map < unsigned int, unsigned int, less<unsigned int> > T_gcontexts;

T_windows   windows;
T_pixmaps   pixmaps;
T_gcontexts gcontexts;

#endif

//
// Define this to log when a channel
// is created or destroyed.
//

#undef  REFERENCES

//
// Here are the static members.
//

#ifdef REFERENCES

int ClientChannel::references_ = 0;

#endif

ClientChannel::ClientChannel(Transport *transport, StaticCompressor *compressor)

  : Channel(transport, compressor), readBuffer_(transport_, this)
{
  //
  // Sequence number of the next message
  // being encoded or decoded.
  //

  clientSequence_ = 0;
  serverSequence_ = 0;

  //
  // Current sequence known by NX agent.
  //

  lastSequence_ = 0;

  //
  // This is used to test the synchronous
  // flush in the proxy.
  //

  lastRequest_ = 0;

  //
  // Store information about the images
  // being streamed.
  //

  splitState_.resource = nothing;
  splitState_.pending  = 0;
  splitState_.commit   = 0;
  splitState_.mode     = split_none;

  //
  // Number of outstanding tainted replies.
  //

  taintCounter_ = 0;

  #ifdef MEMORY

  *logofs << "ClientChannel: Created 1 ClientCache and 1 ServerCache. "
          << "You have 30 seconds to check the allocated size.\n"
          << logofs_flush;

  sleep(30);

  ClientCache *clientCacheTestArray[100];
  ServerCache *serverCacheTestArray[100];

  for (int i = 0; i < 100; i++)
  {
    clientCacheTestArray[i] = new ClientCache();
  }

  *logofs << "ClientChannel: Created further 100 ClientCache. "
          << "You have 30 seconds to check the allocated size.\n"
          << logofs_flush;

  sleep(30);

  for (int i = 0; i < 100; i++)
  {
    serverCacheTestArray[i] = new ServerCache();
  }

  *logofs << "ClientChannel: Created further 100 ServerCache. "
          << "You have 30 seconds to check the allocated size.\n"
          << logofs_flush;

  sleep(30);

  for (int i = 0; i < 100; i++)
  {
    delete clientCacheTestArray[i];
    delete serverCacheTestArray[i];
  }

  *logofs << "ClientChannel: Deleted 100 ClientCache and 100 ServerCache. "
          << "You have 30 seconds to check the allocated size.\n"
          << logofs_flush;

  sleep(30);

  #endif

  #ifdef REFERENCES
  *logofs << "ClientChannel: Created new object at " 
          << this << " for FD#" << fd_ << " out of " 
          << ++references_ << " allocated channels.\n"
          << logofs_flush;
  #endif
}

ClientChannel::~ClientChannel()
{
  #ifdef REFERENCES
  *logofs << "ClientChannel: Deleted object at " 
          << this << " for FD#" << fd_ << " out of "
          << --references_ << " allocated channels.\n"
          << logofs_flush;
  #endif
}

//
// Beginning of handleRead().
//

int ClientChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
                                  unsigned int length)
{
  #ifdef TEST
  *logofs << "handleRead: Called for FD#" << fd_
          << " with " << encodeBuffer.getLength()
          << " bytes already encoded.\n"
          << logofs_flush;
  #endif

  //
  // Pointer to located message and
  // its size in bytes.
  //

  const unsigned char *inputMessage;
  unsigned int inputLength;

  //
  // Set when message is found in
  // cache.
  //

  int hit;

  //
  // Check if we can borrow the buffer
  // from the caller.
  //

  if (message != NULL && length != 0)
  {
    readBuffer_.readMessage(message, length);
  }
  else
  {
    //
    // Get the data from the transport.
    //

    #if defined(TEST) || defined(INFO)
    *logofs << "handleRead: Trying to read from FD#"
            << fd_ << " at " << strMsTimestamp() << ".\n"
            << logofs_flush;
    #endif

    int result = readBuffer_.readMessage();

    #ifdef DEBUG
    *logofs << "handleRead: Read result on FD#" << fd_
            << " is " << result << ".\n"
            << logofs_flush;
    #endif

    if (result < 0)
    {
      //
      // Let the proxy close the channel.
      //

      return -1;
    }
    else if (result == 0)
    {
      #if defined(TEST) || defined(INFO)

      *logofs << "handleRead: PANIC! No data read from FD#"
              << fd_ << " while encoding messages.\n"
              << logofs_flush;

      HandleCleanup();

      #endif

      return 0;
    }
  }

  #if defined(TEST) || defined(INFO) || defined(FLUSH)
  *logofs << "handleRead: Encoding messages for FD#" << fd_
          << " with " << readBuffer_.getLength() << " bytes "
          << "in the buffer.\n" << logofs_flush;
  #endif

  //
  // Extract any complete message which 
  // is available in the buffer.
  //

  if (proxy -> handleAsyncSwitch(fd_) < 0)
  {
    return -1;
  }

  while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
  {
    hit = 0;
 
    if (firstRequest_)
    {
      //
      // Need to add the length of the first
      // request as it was not present in
      // previous versions.
      //

      // Since ProtoStep7 (#issue 108)
      encodeBuffer.encodeValue(inputLength, 8);

      for (unsigned int i = 0; i < inputLength; i++)
      {
        encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8);
      }

      firstRequest_ = 0;

      #if defined(TEST) || defined(OPCODES)

      int bits = encodeBuffer.diffBits();

      *logofs << "handleRead: Handled first request. " << inputLength
              << " bytes in, " << bits << " bits (" << ((float) bits) / 8
              << " bytes) out.\n" << logofs_flush;
      #endif

      priority_++;
    }
    else
    {
      //
      // First of all we get the opcode.
      //

      unsigned char inputOpcode = *inputMessage;

      #if defined(TEST) || defined(INFO)

      //
      // This is used to test the synchronous
      // flush in the parent proxy.
      //

      lastRequest_ = inputOpcode;

      #endif

      //
      // Check if the request is supported by the
      // remote. If not, only handle it locally and
      // taint the opcode as a X_NoOperation. Also
      // try to short-circuit some replies at this
      // side. XSync requests, for example, weight
      // for half of the total round-trips.
      //

      if (handleTaintRequest(inputOpcode, inputMessage,
                                 inputLength) < 0)
      {
        return -1;
      }

      encodeBuffer.encodeOpcodeValue(inputOpcode, clientCache_ -> opcodeCache);

      //
      // Update the current sequence.
      //

      clientSequence_++;
      clientSequence_ &= 0xffff;

      #ifdef DEBUG
      *logofs << "handleRead: Last client sequence number for FD#" 
              << fd_ << " is " << clientSequence_ << ".\n"
              << logofs_flush;
      #endif

      //
      // If differential compression is disabled
      // then use the most simple encoding.
      //

      if (control -> LocalDeltaCompression == 0)
      {
        int result = handleFastReadRequest(encodeBuffer, inputOpcode,
                                               inputMessage, inputLength);
        if (result < 0)
        {
          return -1;
        }
        else if (result > 0)
        {
          continue;
        }
      }

      //
      // Go to the message's specific encoding.
      //

      switch (inputOpcode)
      {
      case X_AllocColor:
        {
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                             clientCache_ -> colormapCache);
          const unsigned char *nextSrc = inputMessage + 8;
          unsigned int colorData[3];
          for (unsigned int i = 0; i < 3; i++)
          {
            unsigned int value = GetUINT(nextSrc, bigEndian_);
            encodeBuffer.encodeCachedValue(value, 16,
                               *(clientCache_ -> allocColorRGBCache[i]), 4);
            colorData[i] = value;
            nextSrc += 2;
          }

          sequenceQueue_.push(clientSequence_, inputOpcode,
                                  colorData[0], colorData[1], colorData[2]);

          priority_++;
        }
        break;
      case X_ReparentWindow:
        {
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeValue(GetUINT(inputMessage + 12, bigEndian_), 16, 11);
          encodeBuffer.encodeValue(GetUINT(inputMessage + 14, bigEndian_), 16, 11);
        }
        break;
      case X_ChangeProperty:
        {
          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ChangeProperty);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          unsigned char format = inputMessage[16];
          encodeBuffer.encodeCachedValue(format, 8,
                             clientCache_ -> changePropertyFormatCache);
          unsigned int dataLength = GetULONG(inputMessage + 20, bigEndian_);
          encodeBuffer.encodeValue(dataLength, 32, 6);
          encodeBuffer.encodeValue(inputMessage[1], 2);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
                             clientCache_ -> changePropertyPropertyCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
                             clientCache_ -> changePropertyTypeCache, 9);
          const unsigned char *nextSrc = inputMessage + 24;
          if (format == 8)
          {
            // Since ProtoStep7 (#issue 108)
            encodeBuffer.encodeTextData(nextSrc, dataLength);
          }
          else if (format == 32)
          {
            for (unsigned int i = 0; i < dataLength; i++)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
                                 clientCache_ -> changePropertyData32Cache);
              nextSrc += 4;
            }
          }
          else
          {
            for (unsigned int i = 0; i < dataLength; i++)
            {
              encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16);
              nextSrc += 2;
            }
          }
        }
        break;
      case X_SendEvent:
        {
          //
          // TODO: This can be improved. In the worst
          // cases, it appears to provide a poor 1.6:1
          // ratio.
          //

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_SendEvent);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          unsigned int window = GetULONG(inputMessage + 4, bigEndian_);

          if (window == 0 || window == 1)
          {
            encodeBuffer.encodeBoolValue(1);
            encodeBuffer.encodeBoolValue(window);
          }
          else
          {
            encodeBuffer.encodeBoolValue(0);
            encodeBuffer.encodeXidValue(window, clientCache_ -> windowCache);
          }

          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 32,
                             clientCache_ -> sendEventMaskCache, 9);
          encodeBuffer.encodeCachedValue(*(inputMessage + 12), 8, 
                             clientCache_ -> sendEventCodeCache);
          encodeBuffer.encodeCachedValue(*(inputMessage + 13), 8, 
                             clientCache_ -> sendEventByteDataCache);

          unsigned int newSeq = GetUINT(inputMessage + 14, bigEndian_);
          unsigned int diffSeq = newSeq - clientCache_ -> sendEventLastSequence;
          clientCache_ -> sendEventLastSequence = newSeq;
          encodeBuffer.encodeValue(diffSeq, 16, 4);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
                             clientCache_ -> sendEventIntDataCache);

          for (unsigned int i = 20; i < 44; i++)
          {
            encodeBuffer.encodeCachedValue((unsigned int) inputMessage[i], 8,
                               clientCache_ -> sendEventEventCache);
          }
        }
        break;
      case X_ChangeWindowAttributes:
        {
          encodeBuffer.encodeValue((inputLength - 12) >> 2, 4);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          unsigned int bitmask = GetULONG(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeCachedValue(bitmask, 15,
                             clientCache_ -> createWindowBitmaskCache);
          const unsigned char *nextSrc = inputMessage + 12;
          unsigned int mask = 0x1;
          for (unsigned int j = 0; j < 15; j++)
          {
            if (bitmask & mask)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
                                 *clientCache_ -> createWindowAttrCache[j]);
              nextSrc += 4;
            }
            mask <<= 1;
          }
        }
        break;
      case X_ClearArea:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_ClearArea target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_ClearArea target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_ClearArea target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ClearArea);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          const unsigned char *nextSrc = inputMessage + 8;
          for (unsigned int i = 0; i < 4; i++)
          {
            encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                               *clientCache_ -> clearAreaGeomCache[i], 8);
            nextSrc += 2;
          }
        }
        break;
      case X_CloseFont:
        {
          unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
          encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
          clientCache_ -> lastFont = font;
        }
        break;
      case X_ConfigureWindow:
        {
          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ConfigureWindow);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          unsigned int bitmask = GetUINT(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeCachedValue(bitmask, 7,
                                  clientCache_ -> configureWindowBitmaskCache);
          unsigned int mask = 0x1;
          const unsigned char *nextSrc = inputMessage + 12;
          for (unsigned int i = 0; i < 7; i++)
          {
            if (bitmask & mask)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_),
                                 CONFIGUREWINDOW_FIELD_WIDTH[i],
                                     *clientCache_ -> configureWindowAttrCache[i], 8);
              nextSrc += 4;
            }
            mask <<= 1;
          }
        }
        break;
      case X_ConvertSelection:
        {
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                             clientCache_ -> convertSelectionRequestorCache, 9);
          const unsigned char* nextSrc = inputMessage + 8;
          for (unsigned int i = 0; i < 3; i++)
          {
            encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
                               *(clientCache_ -> convertSelectionAtomCache[i]), 9);
            nextSrc += 4;
          }
          unsigned int _timestamp = GetULONG(nextSrc, bigEndian_);
          encodeBuffer.encodeValue(_timestamp -
                             clientCache_ -> convertSelectionLastTimestamp, 32, 4);
          clientCache_ -> convertSelectionLastTimestamp = _timestamp;
        }
        break;
      case X_CopyArea:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_CopyArea source id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_CopyArea source id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_CopyArea source id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          t_id = GetULONG(inputMessage + 8, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_CopyArea target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_CopyArea target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_CopyArea target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_CopyArea);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *nextSrc = inputMessage + 16;
          for (unsigned int i = 0; i < 6; i++)
          {
            encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                               *clientCache_ -> copyAreaGeomCache[i], 8);
            nextSrc += 2;
          }
        }
        break;
      case X_CopyGC:
        {
          #ifdef TARGETS

          unsigned int s_g_id = GetULONG(inputMessage + 4, bigEndian_);
          unsigned int d_g_id = GetULONG(inputMessage + 8, bigEndian_);

          *logofs << "handleRead: X_CopyGC source gcontext id is " << s_g_id  
                  << " destination gcontext id is " << d_g_id << ".\n" 
                  << logofs_flush;

          #endif

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> gcCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
                             bigEndian_), 23, clientCache_ -> createGCBitmaskCache);
        }
        break;
      case X_CopyPlane:
        {
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *nextSrc = inputMessage + 16;
          for (unsigned int i = 0; i < 6; i++)
          {
            encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                               *clientCache_ -> copyPlaneGeomCache[i], 8);
            nextSrc += 2;
          }
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), 32,
                                         clientCache_ -> copyPlaneBitPlaneCache, 10);
        }
        break;
      case X_CreateGC:
        {
          #ifdef TARGETS

          unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
          unsigned int t_id = GetULONG(inputMessage + 8, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_CreateGC id " << g_id
                    << " target id is pixmap " << t_id
                    << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_CreateGC id " << g_id
                    << " target id is window " << t_id
                    << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_CreateGC id " << g_id
                    << " target id is unrecognized.\n"
                    << logofs_flush;
          }

          gcontexts.insert(T_gcontexts::value_type(g_id, t_id));

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_CreateGC);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> lastId, clientCache_ -> lastIdCache,
                                 clientCache_ -> gcCache,
                                     clientCache_ -> freeGCCache);

          const unsigned char *nextSrc = inputMessage + 8;
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> drawableCache);
          nextSrc += 4;
          unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
          nextSrc += 4;
          encodeBuffer.encodeCachedValue(bitmask, 23,
                             clientCache_ -> createGCBitmaskCache);
          unsigned int mask = 0x1;
          for (unsigned int i = 0; i < 23; i++)
          {
            if (bitmask & mask)
            {
              unsigned int value = GetULONG(nextSrc, bigEndian_);
              nextSrc += 4;
              unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
              if (fieldWidth <= 4)
              {
                encodeBuffer.encodeValue(value, fieldWidth);
              }
              else
              {
                encodeBuffer.encodeCachedValue(value, fieldWidth,
                                   *clientCache_ -> createGCAttrCache[i]);
              }
            }
            mask <<= 1;
          }
        }
        break;
      case X_ChangeGC:
        {
          #ifdef TARGETS

          unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);

          T_gcontexts::iterator i = gcontexts.find(g_id);

          if (i != gcontexts.end())
          {
            unsigned int t_id = i -> second;

            if (pixmaps.find(t_id) != pixmaps.end())
            {
              *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id  
                      << " target id is pixmap " << t_id << ".\n" 
                      << logofs_flush;
            }
            else if (windows.find(t_id) != windows.end())
            {
              *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id  
                      << " target id is window " << t_id << ".\n" 
                      << logofs_flush;
            }
            else
            {
              *logofs << "handleRead: X_ChangeGC gcontext is " << g_id  
                      << " target id is unrecognized.\n" 
                      << logofs_flush;
            }
          }
          else
          {
            *logofs << "handleRead: X_ChangeGC gcontext id " << g_id  
                    << " is unrecognized.\n" << logofs_flush;
          }

          gcontexts.erase(g_id);

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ChangeGC);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *nextSrc = inputMessage + 8;
          unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
          nextSrc += 4;
          encodeBuffer.encodeCachedValue(bitmask, 23,
                             clientCache_ -> createGCBitmaskCache);
          unsigned int mask = 0x1;
          for (unsigned int i = 0; i < 23; i++)
          {
            if (bitmask & mask)
            {
              unsigned int value = GetULONG(nextSrc, bigEndian_);
              nextSrc += 4;
              unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
              if (fieldWidth <= 4)
              {
                encodeBuffer.encodeValue(value, fieldWidth);
              }
              else
              {
                encodeBuffer.encodeCachedValue(value, fieldWidth,
                                   *clientCache_ -> createGCAttrCache[i]);
              }
            }
            mask <<= 1;
          }
        }
        break;
      case X_CreatePixmap:
        {
          #ifdef TARGETS

          *logofs << "handleRead: X_CreatePixmap depth " << (unsigned) inputMessage[1]
                  << ", pixmap id " << GetULONG(inputMessage + 4, bigEndian_)
                  << ", drawable " << GetULONG(inputMessage + 8, bigEndian_)
                  << ", width " << GetUINT(inputMessage + 12, bigEndian_)
                  << ", height " << GetUINT(inputMessage + 14, bigEndian_)
                  << ", size " << GetUINT(inputMessage + 2, bigEndian_) << 2
                  << ".\n" << logofs_flush;

          unsigned int   p_id = GetULONG(inputMessage + 4, bigEndian_);
          unsigned short p_sx = GetUINT(inputMessage + 12, bigEndian_);
          unsigned short p_sy = GetUINT(inputMessage + 14, bigEndian_);

          *logofs << "handleRead: X_CreatePixmap id is " << p_id 
                  << " width is " << p_sx << " height is " << p_sy 
                  << ".\n" << logofs_flush;

          if (p_sx * p_sy <= 64 * 64)
          {
            *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
                    << p_sx << "x" << p_sy << "=" << p_sx * p_sy 
                    << " will be painted at client side.\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
                    << p_sx << "x" << p_sy << "=" << p_sx * p_sy 
                    << " will be painted at server side.\n" << logofs_flush;
          }

          pixmaps.insert(p_id);

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_CreatePixmap);

          hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                 inputOpcode, inputMessage, inputLength);
        }
        break;
      case X_CreateWindow:
        {
          #ifdef TARGETS

          unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);

          *logofs << "handleRead: X_CreateWindow id is " << w_id
                  << ".\n" << logofs_flush;

          windows.insert(w_id);

          #endif

          unsigned bitmask = GetULONG(inputMessage + 28, bigEndian_);
          encodeBuffer.encodeCachedValue((unsigned int) inputMessage[1], 8,
                                         clientCache_ -> depthCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
                             clientCache_ -> windowCache);

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> lastId, clientCache_ -> lastIdCache,
                                 clientCache_ -> windowCache,
                                     clientCache_ -> freeWindowCache);

          const unsigned char *nextSrc = inputMessage + 12;
          for (unsigned int i = 0; i < 6; i++)
          {
            encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                               *clientCache_ -> createWindowGeomCache[i], 8);
            nextSrc += 2;
          }
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24,
                                 bigEndian_), 29, clientCache_ -> visualCache);
          encodeBuffer.encodeCachedValue(bitmask, 15,
                                     clientCache_ -> createWindowBitmaskCache);
          nextSrc = inputMessage + 32;
          unsigned int mask = 0x1;
          for (unsigned int j = 0; j < 15; j++)
          {
            if (bitmask & mask)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
                                 *clientCache_ -> createWindowAttrCache[j]);
              nextSrc += 4;
            }
            mask <<= 1;
          }
        }
        break;
      case X_DeleteProperty:
        {
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9);
        }
        break;
      case X_FillPoly:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_FillPoly target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_FillPoly target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_FillPoly target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_FillPoly);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          unsigned int numPoints = ((inputLength - 16) >> 2);

          // Since ProtoStep10 (#issue 108)
          encodeBuffer.encodeCachedValue(numPoints, 16,
                             clientCache_ -> fillPolyNumPointsCache, 4);

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
                             clientCache_ -> gcCache);
          encodeBuffer.encodeValue((unsigned int) inputMessage[12], 2);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
          int relativeCoordMode = (inputMessage[13] != 0);
          const unsigned char *nextSrc = inputMessage + 16;
          unsigned int pointIndex = 0;

          for (unsigned int i = 0; i < numPoints; i++)
          {
            if (relativeCoordMode)
            {
              encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                                 *clientCache_ -> fillPolyXRelCache[pointIndex], 8);
              nextSrc += 2;
              encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                                 *clientCache_ -> fillPolyYRelCache[pointIndex], 8);
              nextSrc += 2;
            }
            else
            {
              unsigned int x = GetUINT(nextSrc, bigEndian_);
              nextSrc += 2;
              unsigned int y = GetUINT(nextSrc, bigEndian_);
              nextSrc += 2;
              unsigned int j;
              for (j = 0; j < 8; j++)
                if ((x == clientCache_ -> fillPolyRecentX[j]) &&
                    (y == clientCache_ -> fillPolyRecentY[j]))
                  break;
              if (j < 8)
              {
                encodeBuffer.encodeBoolValue(1);
                encodeBuffer.encodeValue(j, 3);
              }
              else
              {
                encodeBuffer.encodeBoolValue(0);
                encodeBuffer.encodeCachedValue(x, 16,
                             *clientCache_ -> fillPolyXAbsCache[pointIndex], 8);
                encodeBuffer.encodeCachedValue(y, 16,
                             *clientCache_ -> fillPolyYAbsCache[pointIndex], 8);
                clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x;
                clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y;
                clientCache_ -> fillPolyIndex++;
                if (clientCache_ -> fillPolyIndex == 8)
                  clientCache_ -> fillPolyIndex = 0;
              }
            }

            if (++pointIndex == 10) pointIndex = 0;
          }
        }
        break;
      case X_FreeColors:
        {
          unsigned int numPixels = GetUINT(inputMessage + 2, bigEndian_) - 3;
          encodeBuffer.encodeValue(numPixels, 16, 4);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                                         clientCache_ -> colormapCache);
          encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 32, 4);
          const unsigned char *nextSrc = inputMessage + 12;
          while (numPixels)
          {
            encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 8);
            nextSrc += 4;
            numPixels--;
          }
        }
        break;
      case X_FreeCursor:
        {
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
                                         29, clientCache_ -> cursorCache, 9);
        }
        break;
      case X_FreeGC:
        {
          #ifdef TARGETS

          unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);

          T_gcontexts::iterator i = gcontexts.find(g_id);

          if (i != gcontexts.end())
          {
            unsigned int t_id = i -> second;

            if (pixmaps.find(t_id) != pixmaps.end())
            {
              *logofs << "handleRead: X_FreeGC gcontext id is " << g_id  
                      << " target id is pixmap " << t_id << ".\n" 
                      << logofs_flush;
            }
            else if (windows.find(t_id) != windows.end())
            {
              *logofs << "handleRead: X_FreeGC gcontext id is " << g_id  
                      << " target id is window " << t_id << ".\n" 
                      << logofs_flush;
            }
            else
            {
              *logofs << "handleRead: X_FreeGC gcontext id is " << g_id  
                      << " target id is unrecognized.\n" 
                      << logofs_flush;
            }
          }
          else
          {
            *logofs << "handleRead: X_FreeGC gcontext id " << g_id  
                    << " is unrecognized.\n" << logofs_flush;
          }

          gcontexts.erase(g_id);

          #endif

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> freeGCCache);
        }
        break;
      case X_FreePixmap:
        {
          #ifdef TARGETS

          unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_);

          *logofs << "handleRead: X_FreePixmap id is " << p_id << ".\n" << logofs_flush;

          pixmaps.erase(p_id);

          #endif

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> freeDrawableCache);
        }
        break;
      case X_GetAtomName:
        {
          encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 29, 9);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetGeometry:
        {
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> drawableCache);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetInputFocus:
        {
          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetModifierMapping:
        {
          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetKeyboardMapping:
        {
          encodeBuffer.encodeValue((unsigned int) inputMessage[4], 8);
          encodeBuffer.encodeValue((unsigned int) inputMessage[5], 8);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetProperty:
        {
          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_GetProperty);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            unsigned int property = GetULONG(inputMessage + 8, bigEndian_);

            sequenceQueue_.push(clientSequence_, inputOpcode, property);

            priority_++;

            hit = 1;

            break;
          }

          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          unsigned int property = GetULONG(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeValue(property, 29, 9);
          encodeBuffer.encodeValue(GetULONG(inputMessage + 12, bigEndian_), 29, 9);
          encodeBuffer.encodeValue(GetULONG(inputMessage + 16, bigEndian_), 32, 2);
          encodeBuffer.encodeValue(GetULONG(inputMessage + 20, bigEndian_), 32, 8);

          sequenceQueue_.push(clientSequence_, inputOpcode, property);

          priority_++;
        }
        break;
      case X_GetSelectionOwner:
        {
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                             clientCache_ -> getSelectionOwnerSelectionCache, 9);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GrabButton:
        {
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
                             clientCache_ -> grabButtonEventMaskCache);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
                             clientCache_ -> grabButtonConfineCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29,
                             clientCache_ -> cursorCache, 9);
          encodeBuffer.encodeCachedValue(inputMessage[20], 8,
                             clientCache_ -> grabButtonButtonCache);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 22, bigEndian_), 16,
                             clientCache_ -> grabButtonModifierCache);
        }
        break;
      case X_GrabPointer:
        {
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
                             clientCache_ -> grabButtonEventMaskCache);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
                           bigEndian_), 29,
                               clientCache_ -> grabButtonConfineCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16,
                           bigEndian_), 29, clientCache_ -> cursorCache, 9);

          unsigned int _timestamp = GetULONG(inputMessage + 20, bigEndian_);
          encodeBuffer.encodeValue(_timestamp -
                           clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
          clientCache_ -> grabKeyboardLastTimestamp = _timestamp;

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GrabKeyboard:
        {
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> windowCache);
          unsigned int _timestamp = GetULONG(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeValue(_timestamp -
                             clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
          clientCache_ -> grabKeyboardLastTimestamp = _timestamp;
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GrabServer:
      case X_UngrabServer:
      case X_NoOperation:
        {
        }
        break;
      case X_PolyText8:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyText8 target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyText8 target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyText8 target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyText8);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
          int xDiff = x - clientCache_ -> polyTextLastX;
          clientCache_ -> polyTextLastX = x;
          encodeBuffer.encodeCachedValue(xDiff, 16,
                             clientCache_ -> polyTextCacheX);
          unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
          int yDiff = y - clientCache_ -> polyTextLastY;
          clientCache_ -> polyTextLastY = y;
          encodeBuffer.encodeCachedValue(yDiff, 16,
                             clientCache_ -> polyTextCacheY);
          const unsigned char *end = inputMessage + inputLength - 1;
          const unsigned char *nextSrc = inputMessage + 16;
          while (nextSrc < end)
          {
            unsigned int textLength = (unsigned int) *nextSrc++;
            encodeBuffer.encodeBoolValue(1);
            encodeBuffer.encodeValue(textLength, 8);
            if (textLength == 255)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
                                 clientCache_ -> polyTextFontCache);
              nextSrc += 4;
            }
            else
            {
              encodeBuffer.encodeCachedValue(*nextSrc++, 8,
                                 clientCache_ -> polyTextDeltaCache);

              // Since ProtoStep7 (#issue 108)
              encodeBuffer.encodeTextData(nextSrc, textLength);
              nextSrc += textLength;
            }
          }
          encodeBuffer.encodeBoolValue(0);
        }
        break;
      case X_PolyText16:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyText16 target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyText16 target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyText16 target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyText16);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
          int xDiff = x - clientCache_ -> polyTextLastX;
          clientCache_ -> polyTextLastX = x;
          encodeBuffer.encodeCachedValue(xDiff, 16,
                             clientCache_ -> polyTextCacheX);
          unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
          int yDiff = y - clientCache_ -> polyTextLastY;
          clientCache_ -> polyTextLastY = y;
          encodeBuffer.encodeCachedValue(yDiff, 16,
                             clientCache_ -> polyTextCacheY);
          const unsigned char *end = inputMessage + inputLength - 1;
          const unsigned char *nextSrc = inputMessage + 16;
          while (nextSrc < end)
          {
            unsigned int textLength = (unsigned int) *nextSrc++;
            encodeBuffer.encodeBoolValue(1);
            encodeBuffer.encodeValue(textLength, 8);
            if (textLength == 255)
            {
              encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
                                 clientCache_ -> polyTextFontCache);
              nextSrc += 4;
            }
            else
            {
              encodeBuffer.encodeCachedValue(*nextSrc++, 8,
                                 clientCache_ -> polyTextDeltaCache);

              // Since ProtoStep7 (#issue 108)
              encodeBuffer.encodeTextData(nextSrc, textLength * 2);
              nextSrc += textLength * 2;
            }
          }
          encodeBuffer.encodeBoolValue(0);
        }
        break;
      case X_ImageText8:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_ImageText8 target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_ImageText8 target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_ImageText8 target id "
                    << t_id << " is unrecognized.\n"
                    << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ImageText8);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          unsigned int textLength = (unsigned int) inputMessage[1];
          encodeBuffer.encodeCachedValue(textLength, 8,
                             clientCache_ -> imageTextLengthCache, 4);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
          int xDiff = x - clientCache_ -> imageTextLastX;
          clientCache_ -> imageTextLastX = x;
          encodeBuffer.encodeCachedValue(xDiff, 16,
                             clientCache_ -> imageTextCacheX);
          unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
          int yDiff = y - clientCache_ -> imageTextLastY;
          clientCache_ -> imageTextLastY = y;
          encodeBuffer.encodeCachedValue(yDiff, 16,
                             clientCache_ -> imageTextCacheY);
          const unsigned char *nextSrc = inputMessage + 16;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, textLength);
        }
        break;
      case X_ImageText16:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_ImageText16 target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_ImageText16 target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_ImageText16 target id "
                    << t_id << " is unrecognized.\n"
                    << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_ImageText16);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          unsigned int textLength = (unsigned int) inputMessage[1];
          encodeBuffer.encodeCachedValue(textLength, 8,
                             clientCache_ -> imageTextLengthCache, 4);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
          int xDiff = x - clientCache_ -> imageTextLastX;
          clientCache_ -> imageTextLastX = x;
          encodeBuffer.encodeCachedValue(xDiff, 16,
                             clientCache_ -> imageTextCacheX);
          unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
          int yDiff = y - clientCache_ -> imageTextLastY;
          clientCache_ -> imageTextLastY = y;
          encodeBuffer.encodeCachedValue(yDiff, 16,
                             clientCache_ -> imageTextCacheY);
          const unsigned char *nextSrc = inputMessage + 16;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, textLength * 2);
        }
        break;
      case X_InternAtom:
        {
          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_InternAtom);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            sequenceQueue_.push(clientSequence_, inputOpcode);

            //
            // Set the priority, also if doing so will
            // penalize all the well written clients
            // using XInternAtoms() to pipeline multi-
            // ple replies.
            //

            priority_++;

            hit = 1;

            break;
          }

          unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
          encodeBuffer.encodeValue(nameLength, 16, 6);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          const unsigned char *nextSrc = inputMessage + 8;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, nameLength);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_ListExtensions:
        {
          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_ListFonts:
        {
          unsigned int textLength = GetUINT(inputMessage + 6, bigEndian_);
          encodeBuffer.encodeValue(textLength, 16, 6);
          encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16, 6);
          const unsigned char* nextSrc = inputMessage + 8;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, textLength);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_LookupColor:
      case X_AllocNamedColor:
        {
          unsigned int textLength = GetUINT(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeValue(textLength, 16, 6);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
                                         29, clientCache_ -> colormapCache);
          const unsigned char *nextSrc = inputMessage + 12;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, textLength);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_MapWindow:
      case X_UnmapWindow:
      case X_MapSubwindows:
      case X_GetWindowAttributes:
      case X_DestroyWindow:
      case X_DestroySubwindows:
      case X_QueryPointer:
      case X_QueryTree:
        {
          #ifdef TARGETS

          if (inputOpcode == X_DestroyWindow)
          {
            unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);

            *logofs << "handleRead: X_DestroyWindow id is "
                    << w_id << ".\n" << logofs_flush;

            windows.erase(w_id);
          }

          #endif

          if (inputOpcode == X_DestroyWindow)
          {
            // Since ProtoStep7 (#issue 108)
            encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                               clientCache_ -> freeWindowCache);
          }
          else
          {
            encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                               clientCache_ -> windowCache);
          }

          if ((inputOpcode == X_QueryPointer) ||
              (inputOpcode == X_GetWindowAttributes) ||
              (inputOpcode == X_QueryTree))
          {
            sequenceQueue_.push(clientSequence_, inputOpcode);

            priority_++;
          }
        }
        break;
      case X_OpenFont:
        {
          unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_);
          encodeBuffer.encodeValue(nameLength, 16, 7);
          unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
          encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
          clientCache_ -> lastFont = font;
          const unsigned char *nextSrc = inputMessage + 12;

          // Since ProtoStep7 (#issue 108)
          encodeBuffer.encodeTextData(nextSrc, nameLength);
        }
        break;
      case X_PolyFillRectangle:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyFillRectangle target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyFillRectangle target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyFillRectangle target id "
                    << t_id << " is unrecognized.\n"
                    << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyFillRectangle);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);

          unsigned int index = 0;
          unsigned int lastX = 0, lastY = 0;
          unsigned int lastWidth = 0, lastHeight = 0;

          //
          // TODO: Could send the size at the beginning
          // instead of a bool at each iteration.
          //

          for (unsigned int i = 12; i < inputLength;)
          {
            unsigned int x = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newX = x;
            x -= lastX;
            lastX = newX;
            encodeBuffer.encodeCachedValue(x, 16,
                         *clientCache_ -> polyFillRectangleCacheX[index], 8);
            i += 2;
            unsigned int y = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newY = y;
            y -= lastY;
            lastY = newY;
            encodeBuffer.encodeCachedValue(y, 16,
                         *clientCache_ -> polyFillRectangleCacheY[index], 8);
            i += 2;
            unsigned int width = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newWidth = width;
            width -= lastWidth;
            lastWidth = newWidth;
            encodeBuffer.encodeCachedValue(width, 16,
                         *clientCache_ -> polyFillRectangleCacheWidth[index], 8);
            i += 2;
            unsigned int height = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newHeight = height;
            height -= lastHeight;
            lastHeight = newHeight;
            encodeBuffer.encodeCachedValue(height, 16,
                         *clientCache_ -> polyFillRectangleCacheHeight[index], 8);
            i += 2;

            if (++index == 4) index = 0;

            encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
          }
        }
        break;
      case X_PolyFillArc:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead:  X_PolyFillArc target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead:  X_PolyFillArc target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead:  X_PolyFillArc target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyFillArc);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);

          unsigned int index = 0;
          unsigned int lastX = 0, lastY = 0;
          unsigned int lastWidth = 0, lastHeight = 0;
          unsigned int lastAngle1 = 0, lastAngle2 = 0;

          //
          // TODO: Could send the size at the beginning
          // instead of a bool at each iteration.
          //

          for (unsigned int i = 12; i < inputLength;)
          {
            unsigned int x = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newX = x;
            x -= lastX;
            lastX = newX;
            encodeBuffer.encodeCachedValue(x, 16,
                         *clientCache_ -> polyFillArcCacheX[index], 8);
            i += 2;
            unsigned int y = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newY = y;
            y -= lastY;
            lastY = newY;
            encodeBuffer.encodeCachedValue(y, 16,
                         *clientCache_ -> polyFillArcCacheY[index], 8);
            i += 2;
            unsigned int width = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newWidth = width;
            width -= lastWidth;
            lastWidth = newWidth;
            encodeBuffer.encodeCachedValue(width, 16,
                         *clientCache_ -> polyFillArcCacheWidth[index], 8);
            i += 2;
            unsigned int height = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newHeight = height;
            height -= lastHeight;
            lastHeight = newHeight;
            encodeBuffer.encodeCachedValue(height, 16,
                         *clientCache_ -> polyFillArcCacheHeight[index], 8);
            i += 2;
            unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newAngle1 = angle1;
            angle1 -= lastAngle1;
            lastAngle1 = newAngle1;
            encodeBuffer.encodeCachedValue(angle1, 16,
                         *clientCache_ -> polyFillArcCacheAngle1[index], 8);
            i += 2;
            unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newAngle2 = angle2;
            angle2 -= lastAngle2;
            lastAngle2 = newAngle2;
            encodeBuffer.encodeCachedValue(angle2, 16,
                         *clientCache_ -> polyFillArcCacheAngle2[index], 8);
            i += 2;

            if (++index == 2) index = 0;

            encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
          }
        }
        break;
      case X_PolyArc:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyArc target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyArc target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyArc target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyArc);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);

          unsigned int index = 0;
          unsigned int lastX = 0, lastY = 0;
          unsigned int lastWidth = 0, lastHeight = 0;
          unsigned int lastAngle1 = 0, lastAngle2 = 0;

          //
          // TODO: Could send the size at the beginning
          // instead of a bool at each iteration.
          //

          for (unsigned int i = 12; i < inputLength;)
          {
            unsigned int x = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newX = x;
            x -= lastX;
            lastX = newX;
            encodeBuffer.encodeCachedValue(x, 16,
                         *clientCache_ -> polyArcCacheX[index], 8);
            i += 2;
            unsigned int y = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newY = y;
            y -= lastY;
            lastY = newY;
            encodeBuffer.encodeCachedValue(y, 16,
                         *clientCache_ -> polyArcCacheY[index], 8);
            i += 2;
            unsigned int width = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newWidth = width;
            width -= lastWidth;
            lastWidth = newWidth;
            encodeBuffer.encodeCachedValue(width, 16,
                         *clientCache_ -> polyArcCacheWidth[index], 8);
            i += 2;
            unsigned int height = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newHeight = height;
            height -= lastHeight;
            lastHeight = newHeight;
            encodeBuffer.encodeCachedValue(height, 16,
                         *clientCache_ -> polyArcCacheHeight[index], 8);
            i += 2;
            unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newAngle1 = angle1;
            angle1 -= lastAngle1;
            lastAngle1 = newAngle1;
            encodeBuffer.encodeCachedValue(angle1, 16,
                         *clientCache_ -> polyArcCacheAngle1[index], 8);
            i += 2;
            unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
            unsigned int newAngle2 = angle2;
            angle2 -= lastAngle2;
            lastAngle2 = newAngle2;
            encodeBuffer.encodeCachedValue(angle2, 16,
                         *clientCache_ -> polyArcCacheAngle2[index], 8);
            i += 2;

            if (++index == 2) index = 0;

            encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
          }
        }
        break;
      case X_PolyPoint:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyPoint target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyPoint target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyPoint target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyPoint);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
                             clientCache_ -> gcCache);
          const unsigned char *nextSrc = inputMessage + 12;

          unsigned int index = 0;
          unsigned int lastX = 0, lastY = 0;

          for (unsigned int i = 12; i < inputLength; i += 4)
          {
            unsigned int x = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            unsigned int tmp = x;
            x -= lastX;
            lastX = tmp;
            encodeBuffer.encodeCachedValue(x, 16,
                               *clientCache_ -> polyPointCacheX[index], 8);
            unsigned int y = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            tmp = y;
            y -= lastY;
            lastY = tmp;
            encodeBuffer.encodeCachedValue(y, 16,
                               *clientCache_ -> polyPointCacheY[index], 8);

            if (++index == 2) index = 0;
          }
        }
        break;
      case X_PolyLine:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolyLine target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolyLine target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolyLine target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolyLine);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
          encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *nextSrc = inputMessage + 12;

          unsigned int index = 0;
          unsigned int lastX = 0, lastY = 0;

          for (unsigned int i = 12; i < inputLength; i += 4)
          {
            unsigned int x = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            unsigned int tmp = x;
            x -= lastX;
            lastX = tmp;
            encodeBuffer.encodeCachedValue(x, 16,
                               *clientCache_ -> polyLineCacheX[index], 8);
            unsigned int y = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            tmp = y;
            y -= lastY;
            lastY = tmp;
            encodeBuffer.encodeCachedValue(y, 16,
                               *clientCache_ -> polyLineCacheY[index], 8);

            if (++index == 2) index = 0;
          }
        }
        break;
      case X_PolyRectangle:
        {
          encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
                                            bigEndian_) - 3) >> 1, 16, 3);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *end = inputMessage + inputLength;
          const unsigned char *nextSrc = inputMessage + 12;
          while (nextSrc < end)
          {
            for (unsigned int i = 0; i < 4; i++)
            {
              encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                                 *clientCache_ -> polyRectangleGeomCache[i], 8);
              nextSrc += 2;
            }
          }
        }
        break;
      case X_PolySegment:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PolySegment target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PolySegment target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PolySegment target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PolySegment);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
                                            bigEndian_) - 3) >> 1, 16, 4);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
                             bigEndian_), clientCache_ -> gcCache);
          const unsigned char *end = inputMessage + inputLength;
          const unsigned char *nextSrc = inputMessage + 12;
          // unsigned int index = 0;
          // unsigned int lastX1, lastY1, lastX2, lastY2;
          while (nextSrc < end)
          {
            unsigned int x = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            unsigned int xDiff0 =
            x - clientCache_ -> polySegmentLastX[0];
            unsigned int xDiff1 =
            x - clientCache_ -> polySegmentLastX[1];
            int xDiff0Abs = (int) xDiff0;
            if (xDiff0Abs < 0)
              xDiff0Abs = -xDiff0Abs;
            int xDiff1Abs = (int) xDiff1;
            if (xDiff1Abs < 0)
              xDiff1Abs = -xDiff1Abs;

            unsigned int y = GetUINT(nextSrc, bigEndian_);
            nextSrc += 2;
            unsigned int yDiff0 =
            y - clientCache_ -> polySegmentLastY[0];
            unsigned int yDiff1 =
            y - clientCache_ -> polySegmentLastY[1];
            int yDiff0Abs = (int) yDiff0;
            if (yDiff0Abs < 0)
              yDiff0Abs = -yDiff0Abs;
            int yDiff1Abs = (int) yDiff1;
            if (yDiff1Abs < 0)
              yDiff1Abs = -yDiff1Abs;

            int diff0 = xDiff0Abs + yDiff0Abs;
            int diff1 = xDiff1Abs + yDiff1Abs;
            if (diff0 < diff1)
            {
              encodeBuffer.encodeBoolValue(0);
              encodeBuffer.encodeCachedValue(xDiff0, 16,
                                         clientCache_ -> polySegmentCacheX, 6);
              encodeBuffer.encodeCachedValue(yDiff0, 16,
                                         clientCache_ -> polySegmentCacheY, 6);
            }
            else
            {
              encodeBuffer.encodeBoolValue(1);
              encodeBuffer.encodeCachedValue(xDiff1, 16,
                                         clientCache_ -> polySegmentCacheX, 6);
              encodeBuffer.encodeCachedValue(yDiff1, 16,
                                         clientCache_ -> polySegmentCacheY, 6);
            }

            clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x;
            clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y;

            clientCache_ -> polySegmentCacheIndex =
                  clientCache_ -> polySegmentCacheIndex == 1 ? 0 : 1;
          }
        }
        break;
      case X_PutImage:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_PutImage target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_PutImage target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_PutImage target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_PutImage);

          hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                 inputOpcode, inputMessage, inputLength);
        }
        break;
      case X_QueryBestSize:
        {
          encodeBuffer.encodeValue((unsigned int)inputMessage[1], 2);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8);
          encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_QueryColors:
        {
          // Differential encoding.
          encodeBuffer.encodeBoolValue(1);

          unsigned int numColors = ((inputLength - 8) >> 2);
          encodeBuffer.encodeValue(numColors, 16, 5);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                                         clientCache_ -> colormapCache);
          const unsigned char *nextSrc = inputMessage + 8;
          unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel;
          for (unsigned int i = 0; i < numColors; i++)
          {
            unsigned int pixel = GetULONG(nextSrc, bigEndian_);
            nextSrc += 4;
            if (pixel == predictedPixel)
              encodeBuffer.encodeBoolValue(1);
            else
            {
              encodeBuffer.encodeBoolValue(0);
              encodeBuffer.encodeValue(pixel, 32, 9);
            }
            if (i == 0)
              clientCache_ -> queryColorsLastPixel = pixel;
            predictedPixel = pixel + 1;
          }

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_QueryExtension:
        {
          #ifdef TEST

          char data[256];

          int length = GetUINT(inputMessage + 4, bigEndian_);

          if (length > 256)
          {
            length = 256;
          }

          strncpy(data, (char *) inputMessage + 8, length);

          *(data + length) = '\0';

          *logofs << "handleRead: Going to query extension '"
                  << data << "' for FD#" << fd_ << ".\n"
                  << logofs_flush;
          #endif

          unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
          encodeBuffer.encodeValue(nameLength, 16, 6);
          const unsigned char *nextSrc = inputMessage + 8;

          for (; nameLength; nameLength--)
          {
            encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8);
          }

          unsigned int extension = 0;

          if (strncmp((char *) inputMessage + 8, "SHAPE", 5) == 0)
          {
            extension = X_NXInternalShapeExtension;
          }
          else if (strncmp((char *) inputMessage + 8, "RENDER", 6) == 0)
          {
            extension = X_NXInternalRenderExtension;
          }

          sequenceQueue_.push(clientSequence_, inputOpcode, extension);

          priority_++;
        }
        break;
      case X_QueryFont:
        {
          unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
          encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
          clientCache_ -> lastFont = font;

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_SetClipRectangles:
        {
          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_SetClipRectangles);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            hit = 1;

            break;
          }

          unsigned int numRectangles = ((inputLength - 12) >> 3);

          // Since ProtoStep9 (#issue 108)
          encodeBuffer.encodeValue(numRectangles, 15, 4);

          encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> gcCache);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
                             clientCache_ -> setClipRectanglesXCache, 8);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 10, bigEndian_), 16,
                             clientCache_ -> setClipRectanglesYCache, 8);
          const unsigned char *nextSrc = inputMessage + 12;
          for (unsigned int i = 0; i < numRectangles; i++)
          {
            for (unsigned int j = 0; j < 4; j++)
            {
              encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
                                 *clientCache_ -> setClipRectanglesGeomCache[j], 8);
              nextSrc += 2;
            }
          }
        }
        break;
      case X_SetDashes:
        {
          unsigned int numDashes = GetUINT(inputMessage + 10, bigEndian_);
          encodeBuffer.encodeCachedValue(numDashes, 16,
                             clientCache_ -> setDashesLengthCache, 5);
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
                             clientCache_ -> gcCache);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
                             clientCache_ -> setDashesOffsetCache, 5);
          const unsigned char *nextSrc = inputMessage + 12;
          for (unsigned int i = 0; i < numDashes; i++)
            encodeBuffer.encodeCachedValue(*nextSrc++, 8,
                                clientCache_ -> setDashesDashCache_[i & 1], 5);
        }
        break;
      case X_SetSelectionOwner:
        {
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                                    clientCache_ -> setSelectionOwnerCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
                           clientCache_ -> getSelectionOwnerSelectionCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 32,
                           clientCache_ -> setSelectionOwnerTimestampCache, 9);
        }
        break;
      case X_TranslateCoords:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_TranslateCoords source id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_TranslateCoords source id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_TranslateCoords source id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          t_id = GetULONG(inputMessage + 8, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_TranslateCoords target id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_TranslateCoords target id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_TranslateCoords target id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_TranslateCoords);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            sequenceQueue_.push(clientSequence_, inputOpcode);

            priority_++;

            hit = 1;

            break;
          }

          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
                                   clientCache_ -> translateCoordsSrcCache, 9);
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
                                   clientCache_ -> translateCoordsDstCache, 9);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), 16,
                                   clientCache_ -> translateCoordsXCache, 8);
          encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), 16,
                                   clientCache_ -> translateCoordsYCache, 8);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetImage:
        {
          #ifdef TARGETS

          unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

          if (pixmaps.find(t_id) != pixmaps.end())
          {
            *logofs << "handleRead: X_GetImage source id is pixmap "
                    << t_id << ".\n" << logofs_flush;
          }
          else if (windows.find(t_id) != windows.end())
          {
            *logofs << "handleRead: X_GetImage source id is window "
                    << t_id << ".\n" << logofs_flush;
          }
          else
          {
            *logofs << "handleRead: X_GetImage source id " << t_id
                    << " is unrecognized.\n" << logofs_flush;
          }

          #endif

          MessageStore *messageStore = clientStore_ ->
                               getRequestStore(X_GetImage);

          if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
                                     inputMessage, inputLength))
          {
            sequenceQueue_.push(clientSequence_, inputOpcode);

            priority_++;

            hit = 1;

            break;
          }

          // Format.
          encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
          // Drawable.
          encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
                             bigEndian_), clientCache_ -> drawableCache);
          // X.
          unsigned int x = GetUINT(inputMessage + 8, bigEndian_);
          int xDiff = x - clientCache_ -> putImageLastX;
          clientCache_ -> putImageLastX = x;
          encodeBuffer.encodeCachedValue(xDiff, 16,
                             clientCache_ -> putImageXCache, 8);
          // Y.
          unsigned int y = GetUINT(inputMessage + 10, bigEndian_);
          int yDiff = y - clientCache_ -> putImageLastY;
          clientCache_ -> putImageLastY = y;
          encodeBuffer.encodeCachedValue(yDiff, 16,
                             clientCache_ -> putImageYCache, 8);
          // Width.
          unsigned int width = GetUINT(inputMessage + 12, bigEndian_);
          encodeBuffer.encodeCachedValue(width, 16,
                             clientCache_ -> putImageWidthCache, 8);
          // Height.
          unsigned int height = GetUINT(inputMessage + 14, bigEndian_);
          encodeBuffer.encodeCachedValue(height, 16,
                             clientCache_ -> putImageHeightCache, 8);
          // Plane mask.
          encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
                             clientCache_ -> getImagePlaneMaskCache, 5);

          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetPointerMapping:
        {
          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      case X_GetKeyboardControl:
        {
          sequenceQueue_.push(clientSequence_, inputOpcode);

          priority_++;
        }
        break;
      default:
        {
          if (inputOpcode == opcodeStore_ -> renderExtension)
          {
            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXInternalRenderExtension);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> shapeExtension)
          {
            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXInternalShapeExtension);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> putPackedImage)
          {
            #ifdef TARGETS

            unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);

            if (pixmaps.find(t_id) != pixmaps.end())
            {
              *logofs << "handleRead: X_NXPutPackedImage target id is pixmap "
                      << t_id << ".\n" << logofs_flush;
            }
            else if (windows.find(t_id) != windows.end())
            {
              *logofs << "handleRead: X_NXPutPackedImage target id is window "
                      << t_id << ".\n" << logofs_flush;
            }
            else
            {
              *logofs << "handleRead: X_NXPutPackedImage target id " << t_id
                      << " is unrecognized.\n" << logofs_flush;
            }

            #endif

            #ifdef DEBUG
            *logofs << "handleRead: Encoding packed image request for FD#"
                    << fd_ << ".\n" << logofs_flush;
            #endif

            //
            // The field carries the destination data
            // length. We add the request's size of
            // the final X_PutImage.
            //

            unsigned int outputLength = GetULONG(inputMessage + 20, bigEndian_) + 24;

            statistics -> addPackedBytesIn(inputLength);

            statistics -> addPackedBytesOut(outputLength);

            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXPutPackedImage);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> setUnpackColormap)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding set unpack colormap request "
                    << "for FD#" << fd_ << " with size " << inputLength
                    << ".\n" << logofs_flush;
            #endif

            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXSetUnpackColormap);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> setUnpackAlpha)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding set unpack alpha request "
                    << "for FD#" << fd_ << " with size " << inputLength
                    << ".\n" << logofs_flush;
            #endif

            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXSetUnpackAlpha);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> setUnpackGeometry)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding set unpack geometry request "
                    << "for FD#" << fd_ << " with size " << inputLength
                    << ".\n" << logofs_flush;
            #endif

            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXSetUnpackGeometry);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);
          }
          else if (inputOpcode == opcodeStore_ -> startSplit)
          {
            if (handleStartSplitRequest(encodeBuffer, inputOpcode,
                                            inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> endSplit)
          {
            if (handleEndSplitRequest(encodeBuffer, inputOpcode,
                                          inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> commitSplit)
          {
            if (handleCommitSplitRequest(encodeBuffer, inputOpcode,
                                             inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> abortSplit)
          {
            if (handleAbortSplitRequest(encodeBuffer, inputOpcode,
                                            inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> finishSplit)
          {
            if (handleFinishSplitRequest(encodeBuffer, inputOpcode,
                                          inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> freeSplit)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding free split request "
                    << "for FD#" << fd_ << " with size " << inputLength
                    << ".\n" << logofs_flush;
            #endif

            encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
                         clientCache_ -> resourceCache);
          }
          else if (inputOpcode == opcodeStore_ -> freeUnpack)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding free unpack request "
                    << "for FD#" << fd_ << " with size " << inputLength
                    << ".\n" << logofs_flush;
            #endif

            encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
                         clientCache_ -> resourceCache);
          }
          else if (inputOpcode == opcodeStore_ -> getControlParameters)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding get control parameters "
                    << "request for FD#" << fd_ << " with size "
                    << inputLength << ".\n" << logofs_flush;
            #endif

            //
            // Add the reply to the write buffer. If found
            // to contain a message, it it will be flushed
            // to the X client before leaving the loop.
            //

            unsigned char *reply = writeBuffer_.addMessage(32);

            *(reply + 0) = X_Reply;

            PutUINT(clientSequence_, reply + 2, bigEndian_);

            PutULONG(0, reply + 4, bigEndian_);

            //
            // Save the sequence number we used
            // to auto-generate this reply.
            //

            lastSequence_ = clientSequence_;

            #ifdef TEST
            *logofs << "handleRead: Registered " << lastSequence_
                    << " as last auto-generated sequence number.\n"
                    << logofs_flush;
            #endif

            *(reply + 1) = control -> LinkMode;

            *(reply + 8)  = control -> LocalVersionMajor;
            *(reply + 9)  = control -> LocalVersionMinor;
            *(reply + 10) = control -> LocalVersionPatch;

            *(reply + 11) = control -> RemoteVersionMajor;
            *(reply + 12) = control -> RemoteVersionMinor;
            *(reply + 13) = control -> RemoteVersionPatch;

            PutUINT(control -> SplitTimeout,  reply + 14, bigEndian_);
            PutUINT(control -> MotionTimeout, reply + 16, bigEndian_);

            *(reply + 18) = control -> SplitMode;

            PutULONG(control -> SplitDataThreshold, reply + 20, bigEndian_);

            *(reply + 24) = control -> PackMethod;
            *(reply + 25) = control -> PackQuality;

            *(reply + 26) = control -> LocalDataCompressionLevel;
            *(reply + 27) = control -> LocalStreamCompressionLevel;
            *(reply + 28) = control -> LocalDeltaCompression;

            *(reply + 29) = (control -> LocalDeltaCompression == 1 &&
                                 control -> PersistentCacheEnableLoad == 1);
            *(reply + 30) = (control -> LocalDeltaCompression == 1 &&
                                 control -> PersistentCacheEnableSave == 1);
            *(reply + 31) = (control -> LocalDeltaCompression == 1 &&
                                 control -> PersistentCacheEnableLoad == 1 &&
                                     control -> PersistentCacheName != NULL);

            if (handleFlush(flush_if_any) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> getCleanupParameters)
          {
            #ifdef WARNING
            *logofs << "handleRead: WARNING! Encoding fake get cleanup "
                    << "parameters request for FD#" << fd_ << " with size "
                    << inputLength << ".\n" << logofs_flush;
            #endif
          }
          else if (inputOpcode == opcodeStore_ -> getImageParameters)
          {
            #ifdef WARNING
            *logofs << "handleRead: WARNING! Encoding fake get cleanup "
                    << "parameters request for FD#" << fd_ << " with size "
                    << inputLength << ".\n" << logofs_flush;
            #endif
          }
          else if (inputOpcode == opcodeStore_ -> getUnpackParameters)
          {
            #ifdef DEBUG
            *logofs << "handleRead: Encoding get unpack parameters "
                    << "request for FD#" << fd_ << " with size "
                    << inputLength << ".\n" << logofs_flush;
            #endif

            sequenceQueue_.push(clientSequence_, inputOpcode);
          }
          else if (inputOpcode == opcodeStore_ -> getShmemParameters)
          {
            if (handleShmemRequest(encodeBuffer, inputOpcode,
                                       inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> setExposeParameters)
          {
            //
            // Enable or disable expose events
            // coming from the real server.
            //

            encodeBuffer.encodeBoolValue(*(inputMessage + 4));
            encodeBuffer.encodeBoolValue(*(inputMessage + 5));
            encodeBuffer.encodeBoolValue(*(inputMessage + 6));
          }
          else if (inputOpcode == opcodeStore_ -> setCacheParameters)
          {
            if (handleCacheRequest(encodeBuffer, inputOpcode,
                                       inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else if (inputOpcode == opcodeStore_ -> getFontParameters)
          {
            if (handleFontRequest(encodeBuffer, inputOpcode,
                                      inputMessage, inputLength) < 0)
            {
              return -1;
            }
          }
          else
          {
            MessageStore *messageStore = clientStore_ ->
                                 getRequestStore(X_NXInternalGenericRequest);

            hit = handleEncode(encodeBuffer, clientCache_, messageStore,
                                   inputOpcode, inputMessage, inputLength);

            //
            // Don't flush if the opcode is unrecognized.
            // We may optionally flush it is an extension
            // but would penalize the well written clients.
            //
            // if (inputOpcode > 127)
            // {
            //   priority_++;
            // }
            //
          }
        }
      } // End of switch on opcode.

      int bits = encodeBuffer.diffBits();

      #if defined(TEST) || defined(OPCODES)

      const char *cacheString = (hit ? "cached " : "");

      *logofs << "handleRead: Handled " << cacheString << "request OPCODE#" 
              << (unsigned int) inputOpcode << " (" << DumpOpcode(inputOpcode)
              << ")" << " for FD#" << fd_ << " sequence " << clientSequence_
              << ". " << inputLength  << " bytes in, " << bits << " bits ("
              << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;

      #endif

      if (hit)
      {
        statistics -> addCachedRequest(inputOpcode);
      }

      statistics -> addRequestBits(inputOpcode, inputLength << 3, bits);

      if (inputOpcode == opcodeStore_ -> renderExtension)
      {
        if (hit)
        {
          statistics -> addRenderCachedRequest(*(inputMessage + 1));
        }

        statistics -> addRenderRequestBits(*(inputMessage + 1), inputLength << 3, bits);
      }

    }  // End if (firstRequest_)... else ...

  } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ...

  //
  // Check if we need to flush because of
  // prioritized data.
  //

  if (priority_ > 0)
  {
    #if defined(TEST) || defined(INFO)
    *logofs << "handleRead: WARNING! Requesting flush "
            << "because of " << priority_ << " prioritized "
            << "messages for FD#" << fd_ << ".\n"
            << logofs_flush;
    #endif

    if (proxy -> handleAsyncPriority() < 0)
    {
      return -1;
    }

    //
    // Reset the priority flag.
    //

    priority_ = 0;
  }

  //
  // Flush if we exceeded the token length.
  //

  if (proxy -> canAsyncFlush() == 1)
  {
    #if defined(TEST) || defined(INFO)
    *logofs << "handleRead: WARNING! Requesting flush "
            << "because of token length exceeded.\n"
            << logofs_flush;
    #endif

    if (proxy -> handleAsyncFlush() < 0)
    {
      return -1;
    }
  }

  #if defined(TEST) || defined(INFO)

  if (transport_ -> pending() != 0 ||
          readBuffer_.checkMessage() != 0)
  {
    *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
            << fd_ << " has " << transport_ -> pending()
            << " bytes to read.\n" << logofs_flush;

    HandleCleanup();
  }

  #endif

  //
  // Reset the read buffer.
  //

  readBuffer_.fullReset();

  return 1;
}

//
// End of handleRead().
//

//
// Beginning of handleWrite().
//

int ClientChannel::handleWrite(const unsigned char *message, unsigned int length)
{
  #ifdef TEST
  *logofs << "handleWrite: Called for FD#" << fd_
          << ".\n" << logofs_flush;
  #endif

  //
  // Create the buffer from which to 
  // decode messages.
  //

  DecodeBuffer decodeBuffer(message, length);

  #if defined(TEST) || defined(INFO) || defined(FLUSH)
  *logofs << "handleWrite: Decoding messages for FD#" << fd_
          << " with " << length << " bytes in the buffer.\n"
          << logofs_flush;
  #endif

  if (firstReply_)
  {
    #ifdef TEST
    *logofs << "handleWrite: First reply detected.\n" << logofs_flush;
    #endif

    unsigned int outputOpcode;

    decodeBuffer.decodeValue(outputOpcode, 8);
    unsigned int secondByte;
    decodeBuffer.decodeValue(secondByte, 8);
    unsigned int major;
    decodeBuffer.decodeValue(major, 16);
    unsigned int minor;
    decodeBuffer.decodeValue(minor, 16);
    unsigned int extraLength;
    decodeBuffer.decodeValue(extraLength, 16);
    unsigned int outputLength = 8 + (extraLength << 2);

    unsigned char *outputMessage = writeBuffer_.addMessage(outputLength);
    *outputMessage = (unsigned char) outputOpcode;
    outputMessage[1] = (unsigned char) secondByte;
    PutUINT(major, outputMessage + 2, bigEndian_);
    PutUINT(minor, outputMessage + 4, bigEndian_);
    PutUINT(extraLength, outputMessage + 6, bigEndian_);
    unsigned char *nextDest = outputMessage + 8;
    unsigned int cached;
    decodeBuffer.decodeBoolValue(cached);

    if (cached)
    {
      memcpy(nextDest, ServerCache::lastInitReply.getData(), outputLength - 8);
    }
    else
    {
      for (unsigned i = 8; i < outputLength; i++)
      {
        unsigned int nextByte;
        decodeBuffer.decodeValue(nextByte, 8);
        *nextDest++ = (unsigned char) nextByte;
      }

      ServerCache::lastInitReply.set(outputLength - 8, outputMessage + 8);
    }

    imageByteOrder_ = outputMessage[30];
    bitmapBitOrder_ = outputMessage[31];
    scanlineUnit_   = outputMessage[32];
    scanlinePad_    = outputMessage[33];

    firstReply_ = 0;

  } // End of if (firstReply_)

  //
  // This was previously in a 'else' block.
  // Due to the way the first request was
  // handled, we could not decode multiple
  // messages in the first frame.
  //

  { // Start of the decoding block.

    #ifdef DEBUG
    *logofs << "handleWrite: Starting loop on opcodes.\n"
            << logofs_flush;
    #endif

    unsigned char outputOpcode;

    //
    // NX client needs this line to consider
    // the initialization phase successfully
    // completed.
    //

    if (firstClient_ == -1)
    {
      cerr << "Info" << ": Established X client connection.\n" ;

      firstClient_ = fd_;
    }

    while (decodeBuffer.decodeOpcodeValue(outputOpcode, serverCache_ -> opcodeCache, 1))
    {
      #ifdef DEBUG
      *logofs << "handleWrite: Decoded a new OPCODE#"
              << (unsigned int) outputOpcode << ".\n"
              << logofs_flush;
      #endif

      unsigned char *outputMessage = NULL;
      unsigned int outputLength    = 0;

      //
      // General-purpose temp variables 
      // for decoding ints and chars.
      //

      unsigned int value   = 0;
      unsigned char cValue = 0;

      //
      // Check first if we need to abort any split,
      // then if this is a reply, finally if it is
      // en event or error.
      //

      if (outputOpcode == opcodeStore_ -> splitEvent)
      {
        //
        // It's an abort split, not a normal
        // burst of proxy data.
        //

        handleSplitEvent(decodeBuffer);

        continue;
      }
      else if (outputOpcode == X_Reply)
      {
        #ifdef DEBUG
        *logofs << "handleWrite: Decoding sequence number of reply.\n"
                << logofs_flush;
        #endif

        unsigned int sequenceNum;
        unsigned int sequenceDiff;

        decodeBuffer.decodeCachedValue(sequenceDiff, 16,
                           serverCache_ -> replySequenceCache, 7);

        sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;

        serverSequence_ = sequenceNum;

        #ifdef DEBUG
        *logofs << "handleWrite: Last server sequence number for FD#" 
                << fd_ << " is " << serverSequence_ << " with "
                << "difference " << sequenceDiff << ".\n"
                << logofs_flush;
        #endif

        //
        // In case of reply we can follow the X server and
        // override any event's sequence number generated
        // by this side.
        //

        #ifdef TEST
        *logofs << "handleWrite: Updating last event's sequence "
                << lastSequence_ << " to reply's sequence number "
                << serverSequence_ << " for FD#" << fd_ << ".\n"
                << logofs_flush;
        #endif

        lastSequence_ = serverSequence_;

        unsigned short int requestSequenceNum;
        unsigned char requestOpcode;

        #ifdef DEBUG

        requestSequenceNum = 0;
        requestOpcode = 0;

        *logofs << "handleWrite: Peek of sequence number returns ";

        *logofs << sequenceQueue_.peek(requestSequenceNum, requestOpcode);

        *logofs << " with sequence " << requestSequenceNum << " and opcode "
                << (unsigned int) requestOpcode << ".\n" << logofs_flush;

        #endif

        if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) == 1 &&
                (requestSequenceNum == sequenceNum))
        {
          unsigned int requestData[3];

          sequenceQueue_.pop(requestSequenceNum, requestOpcode,
                                    requestData[0], requestData[1], requestData[2]);

          #ifdef DEBUG
          *logofs << "handleWrite: Identified reply to OPCODE#"
                  << (unsigned int) requestOpcode << ".\n"
                  << logofs_flush;
          #endif

          //
          // Is differential encoding disabled?
          //

          if (control -> RemoteDeltaCompression == 0)
          {
            int result = handleFastWriteReply(decodeBuffer, requestOpcode,
                                                  outputMessage, outputLength);
            if (result < 0)
            {
              return -1;
            }
            else if (result > 0)
            {
              continue;
            }
          }

          switch (requestOpcode)
          {
          case X_AllocColor:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              unsigned char *nextDest = outputMessage + 8;
              for (unsigned int i = 0; i < 3; i++)
              {
                decodeBuffer.decodeBoolValue(value);
                if (value)
                {
                  PutUINT(requestData[i], nextDest, bigEndian_);
                }
                else
                {
                  decodeBuffer.decodeValue(value, 16, 6);
                  PutUINT(requestData[i] + value, nextDest, bigEndian_);
                }
                nextDest += 2;
              }
              decodeBuffer.decodeValue(value, 32, 9);
              PutULONG(value, outputMessage + 16, bigEndian_);
            }
            break;
          case X_GetAtomName:
            {
              unsigned int nameLength;
              decodeBuffer.decodeValue(nameLength, 16, 6);
              outputLength = RoundUp4(nameLength) + 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              PutUINT(nameLength, outputMessage + 8, bigEndian_);
              unsigned char* nextDest = outputMessage + 32;

              // Since ProtoStep7 (#issue 108)
              decodeBuffer.decodeTextData(nextDest, nameLength);
            }
            break;
          case X_GetGeometry:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeCachedValue(cValue, 8,
                                 serverCache_ -> depthCache);
              outputMessage[1] = cValue;
              decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> getGeometryRootCache, 9);
              PutULONG(value, outputMessage + 8, bigEndian_);
              unsigned char *nextDest = outputMessage + 12;
              for (unsigned int i = 0; i < 5; i++)
              {
                decodeBuffer.decodeCachedValue(value, 16,
                                   *serverCache_ -> getGeometryGeomCache[i], 8);
                PutUINT(value, nextDest, bigEndian_);
                nextDest += 2;
              }
            }
            break;
          case X_GetInputFocus:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeValue(value, 2);
              outputMessage[1] = (unsigned char) value;
              decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> getInputFocusWindowCache);
              PutULONG(value, outputMessage + 8, bigEndian_);
            }
            break;
          case X_GetKeyboardMapping:
            {
              decodeBuffer.decodeBoolValue(value);
              if (value)
              {
                unsigned int dataLength =
                ServerCache::getKeyboardMappingLastMap.getLength();
                outputLength = 32 + dataLength;
                outputMessage = writeBuffer_.addMessage(outputLength);
                outputMessage[1] =
                ServerCache::getKeyboardMappingLastKeysymsPerKeycode;
                memcpy(outputMessage + 32,
                           ServerCache::getKeyboardMappingLastMap.getData(),
                               dataLength);
                break;
              }
              unsigned int numKeycodes;
              decodeBuffer.decodeValue(numKeycodes, 8);
              unsigned int keysymsPerKeycode;
              decodeBuffer.decodeValue(keysymsPerKeycode, 8, 4);
              ServerCache::getKeyboardMappingLastKeysymsPerKeycode =
                                  keysymsPerKeycode;
              outputLength = 32 + numKeycodes * keysymsPerKeycode * 4;
              outputMessage = writeBuffer_.addMessage(outputLength);
              outputMessage[1] = (unsigned char) keysymsPerKeycode;
              unsigned char *nextDest = outputMessage + 32;
              unsigned char previous = 0;
              for (unsigned int count = numKeycodes * keysymsPerKeycode;
                       count; --count)
              {
                decodeBuffer.decodeBoolValue(value);
                if (value)
                  PutULONG((unsigned int) NoSymbol, nextDest, bigEndian_);
                else
                {
                  unsigned int keysym;
                  decodeBuffer.decodeCachedValue(keysym, 24,
                             serverCache_ -> getKeyboardMappingKeysymCache, 9);
                  decodeBuffer.decodeCachedValue(cValue, 8,
                           serverCache_ -> getKeyboardMappingLastByteCache, 5);
                  previous += cValue;
                  PutULONG((keysym << 8) | previous, nextDest, bigEndian_);
                }
                nextDest += 4;
              }
              ServerCache::getKeyboardMappingLastMap.set(outputLength - 32,
                                                             outputMessage + 32);
            }
            break;
          case X_GetModifierMapping:
            {
              unsigned int keycodesPerModifier;
              decodeBuffer.decodeValue(keycodesPerModifier, 8);
              outputLength = 32 + (keycodesPerModifier << 3);
              outputMessage = writeBuffer_.addMessage(outputLength);
              outputMessage[1] = (unsigned char) keycodesPerModifier;
              unsigned char *nextDest = outputMessage + 32;
              decodeBuffer.decodeBoolValue(value);
              if (value)
              {
                memcpy(outputMessage + 32,
                           ServerCache::getModifierMappingLastMap.getData(),
                               ServerCache::getModifierMappingLastMap.getLength());
                break;
              }
              for (unsigned int count = outputLength - 32; count; count--)
              {
                decodeBuffer.decodeBoolValue(value);
                if (value)
                  *nextDest++ = 0;
                else
                {
                  decodeBuffer.decodeValue(value, 8);
                  *nextDest++ = value;
                }
              }
              ServerCache::getModifierMappingLastMap.set(outputLength - 32,
                                                             outputMessage + 32);
            }
            break;
          case X_GetProperty:
            {
              MessageStore *messageStore = serverStore_ ->
                                   getReplyStore(X_GetProperty);

              handleDecode(decodeBuffer, serverCache_, messageStore,
                               requestOpcode, outputMessage, outputLength);
            }
            break;
          case X_GetSelectionOwner:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeCachedValue(value, 29,
                                    serverCache_ -> getSelectionOwnerCache, 9);
              PutULONG(value, outputMessage + 8, bigEndian_);
            }
            break;
          case X_GetWindowAttributes:
            {
              outputLength = 44;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeValue(value, 2);
              outputMessage[1] = (unsigned char) value;
              decodeBuffer.decodeCachedValue(value, 29,
                                             serverCache_ -> visualCache);
              PutULONG(value, outputMessage + 8, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                             serverCache_ -> getWindowAttributesClassCache, 3);
              PutUINT(value, outputMessage + 12, bigEndian_);
              decodeBuffer.decodeCachedValue(cValue, 8,
                           serverCache_ -> getWindowAttributesBitGravityCache);
              outputMessage[14] = cValue;
              decodeBuffer.decodeCachedValue(cValue, 8,
                           serverCache_ -> getWindowAttributesWinGravityCache);
              outputMessage[15] = cValue;
              decodeBuffer.decodeCachedValue(value, 32,
                            serverCache_ -> getWindowAttributesPlanesCache, 9);
              PutULONG(value, outputMessage + 16, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 32,
                             serverCache_ -> getWindowAttributesPixelCache, 9);
              PutULONG(value, outputMessage + 20, bigEndian_);
              decodeBuffer.decodeBoolValue(value);
              outputMessage[24] = (unsigned char) value;
              decodeBuffer.decodeBoolValue(value);
              outputMessage[25] = (unsigned char) value;
              decodeBuffer.decodeValue(value, 2);
              outputMessage[26] = (unsigned char) value;
              decodeBuffer.decodeBoolValue(value);
              outputMessage[27] = (unsigned char) value;
              decodeBuffer.decodeCachedValue(value, 29,
                                             serverCache_ -> colormapCache, 9);
              PutULONG(value, outputMessage + 28, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 32,
                            serverCache_ -> getWindowAttributesAllEventsCache);
              PutULONG(value, outputMessage + 32, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 32,
                           serverCache_ -> getWindowAttributesYourEventsCache);
              PutULONG(value, outputMessage + 36, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                        serverCache_ -> getWindowAttributesDontPropagateCache);
              PutUINT(value, outputMessage + 40, bigEndian_);
            }
            break;
          case X_GrabKeyboard:
          case X_GrabPointer:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeValue(value, 3);
              outputMessage[1] = (unsigned char) value;
            }
            break;
          case X_InternAtom:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeValue(value, 29, 9);
              PutULONG(value, outputMessage + 8, bigEndian_);
            }
            break;
          case X_ListExtensions:
            {
              decodeBuffer.decodeValue(value, 32, 8);
              outputLength = 32 + (value << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);
              unsigned int numExtensions;
              decodeBuffer.decodeValue(numExtensions, 8);
              outputMessage[1] = (unsigned char) numExtensions;
              unsigned char *nextDest = outputMessage + 32;
              for (; numExtensions; numExtensions--)
              {
                unsigned int _length;
                decodeBuffer.decodeValue(_length, 8);
                *nextDest++ = (unsigned char) _length;
                for (; _length; _length--)
                {
                  decodeBuffer.decodeValue(value, 8);
                  *nextDest++ = value;
                }
              }
            }
            break;
          case X_ListFonts:
            {
              //
              // Differential compression can achieve a 12:1 to 14:1
              // ratio, while the best ZLIB compression can achieve
              // a mere 4:1 to 5:1. In the first case, though, the
              // huge amount of data constituting the message would
              // be stored uncompressed at the remote side. We need
              // to find a compromise. The solution is to use diffe-
              // rential compression at startup and ZLIB compression
              // later on.
              //

              MessageStore *messageStore = serverStore_ ->
                                   getReplyStore(X_ListFonts);

              if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
                                         outputMessage, outputLength))
              {
                break;
              }

              decodeBuffer.decodeValue(value, 32, 8);
              outputLength = 32 + (value << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);
              unsigned int numFonts;
              decodeBuffer.decodeValue(numFonts, 16, 6);
              PutUINT(numFonts, outputMessage + 8, bigEndian_);

              // Differential or plain data compression?
              decodeBuffer.decodeBoolValue(value);

              if (value)
              {
                unsigned char* nextDest = outputMessage + 32;
                for (; numFonts; numFonts--)
                {
                  unsigned int _length;
                  decodeBuffer.decodeValue(_length, 8);
                  *nextDest++ = (unsigned char)_length;

                  // Since ProtoStep7 (#issue 108)
                  decodeBuffer.decodeTextData(nextDest, _length);
                  nextDest += _length;
                }

                handleSave(messageStore, outputMessage, outputLength);
              }
              else
              {
                const unsigned char *compressedData = NULL;
                unsigned int compressedDataSize = 0;

                int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
                                                        outputMessage, outputLength, compressedData,
                                                            compressedDataSize);
                if (decompressed < 0)
                {
                  return -1;
                }
                else if (decompressed > 0)
                {
                  handleSave(messageStore, outputMessage, outputLength,
                                 compressedData, compressedDataSize);
                }
                else
                {
                  handleSave(messageStore, outputMessage, outputLength);
                }
              }
            }
            break;
          case X_LookupColor:
          case X_AllocNamedColor:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              unsigned char *nextDest = outputMessage + 8;
              if (requestOpcode == X_AllocNamedColor)
              {
                decodeBuffer.decodeValue(value, 32, 9);
                PutULONG(value, nextDest, bigEndian_);
                nextDest += 4;
              }
              unsigned int count = 3;
              do
              {
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, nextDest, bigEndian_);
                unsigned int visualColor;
                decodeBuffer.decodeValue(visualColor, 16, 5);
                visualColor += value;
                visualColor &= 0xffff;
                PutUINT(visualColor, nextDest + 6, bigEndian_);
                nextDest += 2;
              }
              while (--count);
            }
            break;
          case X_QueryBestSize:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeValue(value, 16, 8);
              PutUINT(value, outputMessage + 8, bigEndian_);
              decodeBuffer.decodeValue(value, 16, 8);
              PutUINT(value, outputMessage + 10, bigEndian_);
            }
            break;
          case X_QueryColors:
            {
              // Differential or plain data compression?
              decodeBuffer.decodeBoolValue(value);

              if (value)
              {
                decodeBuffer.decodeBoolValue(value);
                if (value)
                {
                  unsigned int numColors =
                  serverCache_ -> queryColorsLastReply.getLength() / 6;
                  outputLength = 32 + (numColors << 3);
                  outputMessage = writeBuffer_.addMessage(outputLength);
                  PutUINT(numColors, outputMessage + 8, bigEndian_);
                  const unsigned char *nextSrc =
                  serverCache_ -> queryColorsLastReply.getData();
                  unsigned char *nextDest = outputMessage + 32;
                  for (; numColors; numColors--)
                  {
                    for (unsigned int i = 0; i < 6; i++)
                      *nextDest++ = *nextSrc++;
                    nextDest += 2;
                  }
                }
                else
                {
                  unsigned int numColors;
                  decodeBuffer.decodeValue(numColors, 16, 5);
                  outputLength = 32 + (numColors << 3);
                  outputMessage = writeBuffer_.addMessage(outputLength);
                  PutUINT(numColors, outputMessage + 8, bigEndian_);
                  unsigned char *nextDest = outputMessage + 32;
                  for (unsigned int c = 0; c < numColors; c++)
                  {
                    for (unsigned int i = 0; i < 3; i++)
                    {
                      decodeBuffer.decodeValue(value, 16);
                      PutUINT(value, nextDest, bigEndian_);
                      nextDest += 2;
                    }
                  }
                  serverCache_ -> queryColorsLastReply.set(numColors * 6,
                                       outputMessage + 32);
                  const unsigned char *nextSrc = nextDest - 1;
                  nextDest = outputMessage + 32 + ((numColors - 1) << 3) + 5;
                  for (; numColors > 1; numColors--)
                  {
                    for (unsigned int i = 0; i < 6; i++)
                      *nextDest-- = *nextSrc--;
                    nextDest -= 2;
                  }
                }
              }
              else
              {
                // Reply length.
                unsigned int numColors;
                decodeBuffer.decodeValue(numColors, 16, 5);
                outputLength = 32 + (numColors << 3);
                outputMessage = writeBuffer_.addMessage(outputLength);
                PutUINT(numColors, outputMessage + 8, bigEndian_);

                const unsigned char *compressedData = NULL;
                unsigned int compressedDataSize = 0;

                int decompressed = handleDecompress(decodeBuffer, requestOpcode, 32,
                                                        outputMessage, outputLength, compressedData,
                                                            compressedDataSize);
                if (decompressed < 0)
                {
                  return -1;
                }
              }
            }
            break;
          case X_QueryExtension:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeBoolValue(value);
              outputMessage[8] = (unsigned char) value;
              decodeBuffer.decodeValue(value, 8);
              outputMessage[9] = (unsigned char) value;
              decodeBuffer.decodeValue(value, 8);
              outputMessage[10] = (unsigned char) value;
              decodeBuffer.decodeValue(value, 8);
              outputMessage[11] = (unsigned char) value;

              //
              // We use a predefined opcode to address
              // extensions' message stores, while real
              // opcodes are used for communication with
              // X server and clients.
              //

              if (requestData[0] == X_NXInternalShapeExtension)
              {
                opcodeStore_ -> shapeExtension = outputMessage[9];

                #ifdef TEST
                *logofs << "handleWrite: Shape extension opcode for FD#" << fd_
                        << " is " << (unsigned int) opcodeStore_ -> shapeExtension
                        << ".\n" << logofs_flush;
                #endif
              }
              else if (requestData[0] == X_NXInternalRenderExtension)
              {
                opcodeStore_ -> renderExtension = outputMessage[9];

                #ifdef TEST
                *logofs << "handleWrite: Render extension opcode for FD#" << fd_
                        << " is " << (unsigned int) opcodeStore_ -> renderExtension
                        << ".\n" << logofs_flush;
                #endif
              }
            }
            break;
          case X_QueryFont:
            {
              //
              // Use differential compression at startup and plain
              // data compression later. Check X_ListFonts message
              // for an explanation.
              //

              MessageStore *messageStore = serverStore_ ->
                                   getReplyStore(X_QueryFont);

              if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
                                         outputMessage, outputLength))
              {
                break;
              }

              // Differential or plain data compression?
              decodeBuffer.decodeBoolValue(value);

              if (value)
              {
                unsigned int numProperties;
                unsigned int numCharInfos;
                decodeBuffer.decodeValue(numProperties, 16, 8);
                decodeBuffer.decodeValue(numCharInfos, 32, 10);
                outputLength = 60 + numProperties * 8 + numCharInfos * 12;
                outputMessage = writeBuffer_.addMessage(outputLength);
                PutUINT(numProperties, outputMessage + 46, bigEndian_);
                PutULONG(numCharInfos, outputMessage + 56, bigEndian_);
                handleDecodeCharInfo(decodeBuffer, outputMessage + 8);
                handleDecodeCharInfo(decodeBuffer, outputMessage + 24);
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, outputMessage + 40, bigEndian_);
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, outputMessage + 42, bigEndian_);
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, outputMessage + 44, bigEndian_);
                decodeBuffer.decodeBoolValue(value);
                outputMessage[48] = (unsigned char) value;
                decodeBuffer.decodeValue(value, 8);
                outputMessage[49] = (unsigned char) value;
                decodeBuffer.decodeValue(value, 8);
                outputMessage[50] = (unsigned char) value;
                decodeBuffer.decodeBoolValue(value);
                outputMessage[51] = (unsigned char) value;
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, outputMessage + 52, bigEndian_);
                decodeBuffer.decodeValue(value, 16, 9);
                PutUINT(value, outputMessage + 54, bigEndian_);
                unsigned char *nextDest = outputMessage + 60;
                decodeBuffer.decodeBoolValue(value);

                int end = 0;

                if (value == 1)
                {
                  unsigned int index;
                  decodeBuffer.decodeValue(index, 4);
                  unsigned int _length;
                  const unsigned char *data;
                  ServerCache::queryFontFontCache.get(index, _length, data);
                  memcpy(nextDest, data, _length);

                  end = 1;
                }

                if (end == 0)
                {
                  unsigned char *saveDest = nextDest;
                  unsigned int _length = numProperties * 8 + numCharInfos * 12;
                  for (; numProperties; numProperties--)
                  {
                    decodeBuffer.decodeValue(value, 32, 9);
                    PutULONG(value, nextDest, bigEndian_);
                    decodeBuffer.decodeValue(value, 32, 9);
                    PutULONG(value, nextDest + 4, bigEndian_);
                    nextDest += 8;
                  }
                  for (; numCharInfos; numCharInfos--)
                  {
                    handleDecodeCharInfo(decodeBuffer, nextDest);

                    nextDest += 12;
                  }
                  ServerCache::queryFontFontCache.set(_length, saveDest);
                }

                handleSave(messageStore, outputMessage, outputLength);
              }
              else
              {
                // Reply length.
                unsigned int replyLength;
                decodeBuffer.decodeValue(replyLength, 32, 16);
                outputLength = 32 + (replyLength << 2);
                outputMessage = writeBuffer_.addMessage(outputLength);

                const unsigned char *compressedData = NULL;
                unsigned int compressedDataSize = 0;

                int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
                                                        outputMessage, outputLength, compressedData,
                                                            compressedDataSize);
                if (decompressed < 0)
                {
                  return -1;
                }
                else if (decompressed > 0)
                {
                  handleSave(messageStore, outputMessage, outputLength,
                                 compressedData, compressedDataSize);
                }
                else
                {
                  handleSave(messageStore, outputMessage, outputLength);
                }
              }
            }
            break;
          case X_QueryPointer:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeBoolValue(value);
              outputMessage[1] = (unsigned char) value;
              decodeBuffer.decodeCachedValue(value, 29,
                                     serverCache_ -> queryPointerRootCache, 9);
              PutULONG(value, outputMessage + 8, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 29,
                                    serverCache_ -> queryPointerChildCache, 9);
              PutULONG(value, outputMessage + 12, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                    serverCache_ -> motionNotifyRootXCache, 8);
              serverCache_ -> motionNotifyLastRootX += value;
              PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 16,
                      bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                    serverCache_ -> motionNotifyRootYCache, 8);
              serverCache_ -> motionNotifyLastRootY += value;
              PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 18,
                      bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                   serverCache_ -> motionNotifyEventXCache, 8);
              PutUINT(serverCache_ -> motionNotifyLastRootX + value,
                      outputMessage + 20, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                   serverCache_ -> motionNotifyEventYCache, 8);
              PutUINT(serverCache_ -> motionNotifyLastRootY + value,
                      outputMessage + 22, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                       serverCache_ -> motionNotifyStateCache);
              PutUINT(value, outputMessage + 24, bigEndian_);
            }
            break;
          case X_QueryTree:
            {
              unsigned int children;
              decodeBuffer.decodeValue(children, 16, 8);

              outputLength = 32 + (children << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);

              PutULONG(outputLength, outputMessage + 4, bigEndian_);

              decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> queryTreeWindowCache);

              PutULONG(value, outputMessage + 8, bigEndian_);

              decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> queryTreeWindowCache);

              PutULONG(value, outputMessage + 12, bigEndian_);

              unsigned char *next = outputMessage + 32;

              PutUINT(children, outputMessage + 16, bigEndian_);

              for (unsigned int i = 0; i < children; i++)
              {
                decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> queryTreeWindowCache);

                PutULONG(value, next + (i * 4), bigEndian_);
              }
            }
            break;
          case X_TranslateCoords:
            {
              outputLength = 32;
              outputMessage = writeBuffer_.addMessage(outputLength);
              decodeBuffer.decodeBoolValue(value);
              outputMessage[1] = (unsigned char) value;
              decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> translateCoordsChildCache, 9);
              PutULONG(value, outputMessage + 8, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                     serverCache_ -> translateCoordsXCache, 8);
              PutUINT(value, outputMessage + 12, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                     serverCache_ -> translateCoordsYCache, 8);
              PutUINT(value, outputMessage + 14, bigEndian_);
            }
            break;
          case X_GetImage:
            {
              MessageStore *messageStore = serverStore_ ->
                                   getReplyStore(X_GetImage);

              if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
                                         outputMessage, outputLength))
              {
                break;
              }

              // Depth.
              decodeBuffer.decodeCachedValue(cValue, 8,
                                 serverCache_ -> depthCache);
              // Reply length.
              unsigned int replyLength;
              decodeBuffer.decodeValue(replyLength, 32, 9);
              outputLength = 32 + (replyLength << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);
              outputMessage[1] = (unsigned char) cValue;
              // Visual.
              unsigned int visual;
              decodeBuffer.decodeCachedValue(visual, 29, 
                                 serverCache_ -> visualCache);
              PutULONG(visual, outputMessage + 8, bigEndian_);

              // Since ProtoStep8 (#issue 108)
              handleCopy(decodeBuffer, requestOpcode, messageStore ->
                             dataOffset, outputMessage, outputLength);

              handleSave(messageStore, outputMessage, outputLength);
            }
            break;
          case X_GetPointerMapping:
            {
              unsigned int nextByte;
              decodeBuffer.decodeValue(nextByte, 8, 4);
              unsigned int replyLength;
              decodeBuffer.decodeValue(replyLength, 32, 4);
              outputLength = 32 + (replyLength << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);
              outputMessage[1] = (unsigned char) nextByte;
              unsigned char *nextDest = outputMessage + 32;
              for (unsigned int i = 32; i < outputLength; i++)
              {
                decodeBuffer.decodeValue(nextByte, 8, 4);
                *nextDest++ = (unsigned char) nextByte;
              }
            }
            break;
          case X_GetKeyboardControl:
            {
              unsigned int nextByte;
              decodeBuffer.decodeValue(nextByte, 8, 2);
              unsigned int replyLength;
              decodeBuffer.decodeValue(replyLength, 32, 8);
              outputLength = 32 + (replyLength << 2);
              outputMessage = writeBuffer_.addMessage(outputLength);
              outputMessage[1] = (unsigned char) nextByte;
              unsigned char *nextDest = outputMessage + 8;
              for (unsigned int i = 8; i < outputLength; i++)
              {
                decodeBuffer.decodeValue(nextByte, 8, 4);
                *nextDest++ = (unsigned char) nextByte;
              }
            }
            break;
          default:
            {
              if (requestOpcode == opcodeStore_ -> getUnpackParameters)
              {
                #ifdef TEST
                *logofs << "handleWrite: Received get unpack parameters reply "
                        << "OPCODE#" << (unsigned int) opcodeStore_ -> getUnpackParameters
                        << ".\n" << logofs_flush;
                #endif

                outputLength = 32 + PACK_METHOD_LIMIT;

                outputMessage = writeBuffer_.addMessage(outputLength);

                unsigned int method;

                //
                // Let agent use only the unpack methods
                // implemented at both sides.
                //

                for (int i = 0; i < PACK_METHOD_LIMIT; i++)
                {
                  decodeBuffer.decodeBoolValue(method);

                  control -> RemoteUnpackMethods[i] = method;

                  *(outputMessage + 32 + i) =
                      (control -> LocalUnpackMethods[i] == 1 &&
                           method == 1);
                }
              }
              else if (requestOpcode == opcodeStore_ -> getShmemParameters)
              {
                if (handleShmemReply(decodeBuffer, requestOpcode,
                                         outputMessage, outputLength) < 0)
                {
                  return -1;
                }
              }
              else if (requestOpcode == opcodeStore_ -> getFontParameters)
              {
                if (handleFontReply(decodeBuffer, requestOpcode,
                                        outputMessage, outputLength) < 0)
                {
                  return -1;
                }
              }
              else
              {
                #ifdef PANIC
                *logofs << "handleWrite: PANIC! No matching request for "
                        << "reply with sequence number " << sequenceNum
                        << ".\n" << logofs_flush;
                #endif

                cerr << "Error" << ": No matching request for "
                     << "reply with sequence number " << sequenceNum
                     << ".\n";

                return -1;
              }
            }
          }

          #if defined(TEST) || defined(OPCODES)
          *logofs << "handleWrite: Handled reply to OPCODE#"
                  << (unsigned) requestOpcode << " (" << DumpOpcode(requestOpcode)
                  << ")" << " for FD#" << fd_ << " with sequence " << serverSequence_
                  << ". Output size is " << outputLength << ".\n" << logofs_flush;
          #endif

          statistics -> addRepliedRequest(requestOpcode);
        }
        else // End of if (sequenceQueue_.peek() && ...)
        {
          //
          // Reply didn't match any request opcode.
          // Check again if differential encoding
          // is disabled.
          //

          #ifdef DEBUG
          *logofs << "handleWrite: Identified generic reply.\n"
                  << logofs_flush;
          #endif

          requestOpcode = X_Reply;

          if (control -> RemoteDeltaCompression == 0)
          {
            int result = handleFastWriteReply(decodeBuffer, requestOpcode,
                                                  outputMessage, outputLength);
            if (result < 0)
            {
              return -1;
            }
            else if (result > 0)
            {
              continue;
            }
          }

          //
          // All replies whose opcode is not pushed in
          // sequence number queue are cached together.
          // Among such replies are those to extension
          // requests.
          //

          MessageStore *messageStore = serverStore_ ->
                               getReplyStore(X_NXInternalGenericReply);

          handleDecode(decodeBuffer, serverCache_, messageStore,
                           requestOpcode, outputMessage, outputLength);

          #if defined(TEST) || defined(OPCODES)
          *logofs << "handleWrite: Handled generic reply for FD#" << fd_
                  << " with sequence " << serverSequence_ << ". Output size is "
                  << outputLength << ".\n" << logofs_flush;
          #endif

          statistics -> addRepliedRequest(requestOpcode);

        } // End of if (sequenceQueue_.peek() && ...) else ...

        //
        // If any output was produced then write opcode,
        // sequence number and size to the buffer.
        //

        if (outputLength > 0)
        {
          *outputMessage = outputOpcode;

          PutUINT(serverSequence_, outputMessage + 2, bigEndian_);

          PutULONG((outputLength - 32) >> 2, outputMessage + 4, bigEndian_);
        }

      } // End of if (outputOpcode == 1)...
      else
      {
        //
        // It's an event or error.
        //

        unsigned int sequenceNum;
        unsigned int sequenceDiff;

        decodeBuffer.decodeCachedValue(sequenceDiff, 16,
                           serverCache_ -> eventSequenceCache, 7);

        sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;

        serverSequence_ = sequenceNum;

        #ifdef DEBUG
        *logofs << "handleWrite: Last server sequence number for FD#" 
                << fd_ << " is " << serverSequence_ << " with "
                << "difference " << sequenceDiff << ".\n"
                << logofs_flush;
        #endif

        //
        // Check if this is an error that matches
        // a sequence number for which we were
        // expecting a reply.
        //

        if (outputOpcode == X_Error)
        {
          unsigned short int errorSequenceNum;
          unsigned char errorOpcode;

          if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) &&
                  ((unsigned) errorSequenceNum == serverSequence_))
          {
            //
            // Remove the queued sequence of the reply.
            //

            #ifdef TEST
            *logofs << "handleWrite: WARNING! Removing reply to OPCODE#"
                    << (unsigned) errorOpcode << " sequence "
                    << errorSequenceNum << " for FD#" << fd_
                    << " due to error.\n" << logofs_flush;
            #endif

            sequenceQueue_.pop(errorSequenceNum, errorOpcode);

            //
            // Send to the client the current sequence
            // number, not the number that matched the
            // reply. Because we are generating replies
            // at our side, Xlib can incur in a sequence
            // lost if the error comes after the auto-
            // generated reply.
            //

            if (control -> SessionMode == session_proxy)
            {
              #ifdef TEST
              *logofs << "handleWrite: Updating last event's sequence "
                      << lastSequence_ << " to X server's error sequence "
                      << "number " << serverSequence_ << " for FD#"
                      << fd_ << ".\n" << logofs_flush;
              #endif

              lastSequence_ = serverSequence_;
            }
          }

          //
          // In case of errors always send to client the
          // original X server's sequence associated to
          // the failing request.
          //

          if (control -> SessionMode != session_proxy)
          {
            #ifdef TEST
            *logofs << "handleWrite: Updating last event's sequence "
                    << lastSequence_ << " to X server's error sequence "
                    << "number " << serverSequence_ << " for FD#"
                    << fd_ << ".\n" << logofs_flush;
            #endif

            lastSequence_ = serverSequence_;
          }
        }

        //
        // Check if by producing events at client side we
        // have modified the events' sequence numbering.
        // In this case taint the original sequence to
        // comply with the last one known by client.
        //

/*
FIXME: Recover the sequence number if the proxy
       is not connected to an agent.
*/
        if (serverSequence_ > lastSequence_ ||
                control -> SessionMode != session_proxy)
        {
          #ifdef DEBUG
          *logofs << "handleWrite: Updating last event's sequence "
                  << lastSequence_ << " to X server's sequence number "
                  << serverSequence_ << " for FD#" << fd_
                  << ".\n" << logofs_flush;
          #endif

          lastSequence_ = serverSequence_;
        }
        #ifdef DEBUG
        else if (serverSequence_ < lastSequence_)
        {
          //
          // Use our last auto-generated sequence.
          //

          *logofs << "handleWrite: Tainting sequence number "
                  << serverSequence_ << " to last event's sequence "
                  << lastSequence_ << " for FD#" << fd_ << ".\n"
                  << logofs_flush;
        }
        #endif

        //
        // Check if remote side used fast encoding.
        //

        if (control -> RemoteDeltaCompression == 0)
        {
          int result = handleFastWriteEvent(decodeBuffer, outputOpcode,
                                                outputMessage, outputLength);
          if (result < 0)
          {
            return -1;
          }
          else if (result > 0)
          {
            continue;
          }
        }

        //
        // Make space for message in the outgoing buffer
        // and write opcode and sequence number.
        //

        outputLength = 32;
        outputMessage = writeBuffer_.addMessage(outputLength);

        *outputMessage = outputOpcode;

        PutUINT(lastSequence_, outputMessage + 2, bigEndian_);

        #ifdef DEBUG
        *logofs << "handleWrite: Going to handle event or error OPCODE#"
                << (unsigned int) outputOpcode << " for FD#" << fd_
                << " sequence " << lastSequence_ << " (real was "
                << serverSequence_ << ").\n" << logofs_flush;
        #endif

        switch (outputOpcode)
        {
        case X_Error:
          {
            unsigned char code;
            decodeBuffer.decodeCachedValue(code, 8,
                               serverCache_ -> errorCodeCache);
            outputMessage[1] = code;

            #if defined(TEST) || defined(OPCODES)
            *logofs << "handleWrite: Handled error ERR_CODE#"
                    << (unsigned int) code << " for FD#" << fd_;
            #endif

            if ((code != 11) && (code != 8) &&
                    (code != 15) && (code != 1))
            {
              decodeBuffer.decodeValue(value, 32, 16);
              PutULONG(value, outputMessage + 4, bigEndian_);

              #if defined(TEST) || defined(OPCODES)
              *logofs << " RES_ID#" << value;
              #endif
            }

            if (code >= 18)
            {
              decodeBuffer.decodeCachedValue(value, 16,
                                 serverCache_ -> errorMinorCache);
              PutUINT(value, outputMessage + 8, bigEndian_);

              #if defined(TEST) || defined(OPCODES)
              *logofs << " MIN_OP#" << value;
              #endif
            }

            decodeBuffer.decodeCachedValue(cValue, 8,
                               serverCache_ -> errorMajorCache);
            outputMessage[10] = cValue;

            #if defined(TEST) || defined(OPCODES)
            *logofs << " MAJ_OP#" << (unsigned int) cValue;
            #endif

            if (code >= 18)
            {
              unsigned char *nextDest = outputMessage + 11;
              for (unsigned int i = 11; i < 32; i++)
              {
                decodeBuffer.decodeValue(value, 8);
                *nextDest++ = (unsigned char) cValue;
              }
            }

            #if defined(TEST) || defined(OPCODES)
            *logofs << " sequence " << lastSequence_ << " (real was "
                    << serverSequence_ << ") . Size is "
                    << (unsigned int) outputLength << ".\n"
                    << logofs_flush;
            #endif
          }
          break;
        case ButtonPress:
        case ButtonRelease:
        case KeyPress:
        case KeyRelease:
        case MotionNotify:
        case EnterNotify:
        case LeaveNotify:
          {
            if (outputOpcode == MotionNotify)
            {
              decodeBuffer.decodeBoolValue(value);
            }
            else if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
            {
              decodeBuffer.decodeValue(value, 3);
            }
            else if (outputOpcode == KeyRelease)
            {
              decodeBuffer.decodeBoolValue(value);
              if (value)
              {
                value = serverCache_ -> keyPressLastKey;
              }
              else
              {
                decodeBuffer.decodeValue(value, 8);
              }
            }
            else if (outputOpcode == ButtonPress || outputOpcode == ButtonRelease)
            {
              decodeBuffer.decodeCachedValue(cValue, 8,
                                 serverCache_ -> buttonCache);
              value = (unsigned int) cValue;
            }
            else
            {
              decodeBuffer.decodeValue(value, 8);
            }

            outputMessage[1] = (unsigned char) value;
            decodeBuffer.decodeCachedValue(value, 32,
                               serverCache_ -> motionNotifyTimestampCache, 9);
            serverCache_ -> lastTimestamp += value;
            PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
                     bigEndian_);
            unsigned char *nextDest = outputMessage + 8;
            int skipRest = 0;
            if (outputOpcode == KeyRelease)
            {
              decodeBuffer.decodeBoolValue(value);
              if (value)
              {
                for (unsigned int i = 0; i < 23; i++)
                {
                  *nextDest++ = serverCache_ -> keyPressCache[i];
                }
                skipRest = 1;
              }
            }

            if (!skipRest)
            {
              for (unsigned int i = 0; i < 3; i++)
              {
                decodeBuffer.decodeCachedValue(value, 29,
                                   *serverCache_ -> motionNotifyWindowCache[i], 6);
                PutULONG(value, nextDest, bigEndian_);
                nextDest += 4;
              }
              decodeBuffer.decodeCachedValue(value, 16,
                                    serverCache_ -> motionNotifyRootXCache, 6);
              serverCache_ -> motionNotifyLastRootX += value;
              PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 20,
                      bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                    serverCache_ -> motionNotifyRootYCache, 6);
              serverCache_ -> motionNotifyLastRootY += value;
              PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 22,
                      bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                   serverCache_ -> motionNotifyEventXCache, 6);
              PutUINT(serverCache_ -> motionNotifyLastRootX + value,
                      outputMessage + 24, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                   serverCache_ -> motionNotifyEventYCache, 6);
              PutUINT(serverCache_ -> motionNotifyLastRootY + value,
                      outputMessage + 26, bigEndian_);
              decodeBuffer.decodeCachedValue(value, 16,
                                       serverCache_ -> motionNotifyStateCache);
              PutUINT(value, outputMessage + 28, bigEndian_);
              if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
              {
                decodeBuffer.decodeValue(value, 2);
              }
              else
              {
                decodeBuffer.decodeBoolValue(value);
              }
              outputMessage[30] = (unsigned char) value;
              if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
              {
                decodeBuffer.decodeValue(value, 2);
                outputMessage[31] = (unsigned char) value;
              }
              else if (outputOpcode == KeyPress)
              {
                serverCache_ -> keyPressLastKey = outputMessage[1];
                for (unsigned int i = 8; i < 31; i++)
                {
                  serverCache_ -> keyPressCache[i - 8] = outputMessage[i];
                }
              }
            }
          }
          break;
        case ColormapNotify:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> colormapNotifyWindowCache, 8);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                               serverCache_ -> colormapNotifyColormapCache, 8);
            PutULONG(value, outputMessage + 8, bigEndian_);
            decodeBuffer.decodeBoolValue(value);
            outputMessage[12] = (unsigned char) value;
            decodeBuffer.decodeBoolValue(value);
            outputMessage[13] = (unsigned char) value;
          }
          break;
        case ConfigureNotify:
          {
            unsigned char *nextDest = outputMessage + 4;
            for (unsigned int i = 0; i < 3; i++)
            {
              decodeBuffer.decodeCachedValue(value, 29,
                                 *serverCache_ -> configureNotifyWindowCache[i], 9);
              PutULONG(value, nextDest, bigEndian_);
              nextDest += 4;
            }
            for (unsigned int j = 0; j < 5; j++)
            {
              decodeBuffer.decodeCachedValue(value, 16,
                                 *serverCache_ -> configureNotifyGeomCache[j], 8);
              PutUINT(value, nextDest, bigEndian_);
              nextDest += 2;
            }
            decodeBuffer.decodeBoolValue(value);
            *nextDest = value;
          }
          break;
        case CreateNotify:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> createNotifyWindowCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeValue(value, 29, 5);
            serverCache_ -> createNotifyLastWindow += value;
            serverCache_ -> createNotifyLastWindow &= 0x1fffffff;
            PutULONG(serverCache_ -> createNotifyLastWindow, outputMessage + 8,
                     bigEndian_);
            unsigned char* nextDest = outputMessage + 12;
            for (unsigned int i = 0; i < 5; i++)
            {
              decodeBuffer.decodeValue(value, 16, 9);
              PutUINT(value, nextDest, bigEndian_);
              nextDest += 2;
            }
            decodeBuffer.decodeBoolValue(value);
            *nextDest = (unsigned char) value;
          }
          break;
        case Expose:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                               serverCache_ -> exposeWindowCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            unsigned char *nextDest = outputMessage + 8;
            for (unsigned int i = 0; i < 5; i++)
            {
              decodeBuffer.decodeCachedValue(value, 16,
                                 *serverCache_ -> exposeGeomCache[i], 6);
              PutUINT(value, nextDest, bigEndian_);
              nextDest += 2;
            }
          }
          break;
        case FocusIn:
        case FocusOut:
          {
            decodeBuffer.decodeValue(value, 3);
            outputMessage[1] = (unsigned char) value;
            decodeBuffer.decodeCachedValue(value, 29,
                                        serverCache_ -> focusInWindowCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeValue(value, 2);
            outputMessage[8] = (unsigned char) value;
          }
          break;
        case KeymapNotify:
          {
            decodeBuffer.decodeBoolValue(value);
            if (value)
            memcpy(outputMessage + 1, ServerCache::lastKeymap.getData(), 31);
            else
            {
              unsigned char *nextDest = outputMessage + 1;
              for (unsigned int i = 1; i < 32; i++)
              {
                decodeBuffer.decodeValue(value, 8);
                *nextDest++ = (unsigned char) value;
              }
            ServerCache::lastKeymap.set(31, outputMessage + 1);
            }
          }
          break;
        case MapNotify:
        case UnmapNotify:
        case DestroyNotify:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                                       serverCache_ -> mapNotifyEventCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                      serverCache_ -> mapNotifyWindowCache, 9);
            PutULONG(value, outputMessage + 8, bigEndian_);
            if (outputOpcode == MapNotify || outputOpcode == UnmapNotify)
            {
              decodeBuffer.decodeBoolValue(value);
              outputMessage[12] = (unsigned char) value;
            }
          }
          break;
        case NoExpose:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                                     serverCache_ -> noExposeDrawableCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 16,
                                           serverCache_ -> noExposeMinorCache);
            PutUINT(value, outputMessage + 8, bigEndian_);
            decodeBuffer.decodeCachedValue(cValue, 8,
                                           serverCache_ -> noExposeMajorCache);
            outputMessage[10] = cValue;
          }
          break;
        case PropertyNotify:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                                 serverCache_ -> propertyNotifyWindowCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> propertyNotifyAtomCache, 9);
            PutULONG(value, outputMessage + 8, bigEndian_);
            decodeBuffer.decodeValue(value, 32, 9);
            serverCache_ -> lastTimestamp += value;
            PutULONG(serverCache_ -> lastTimestamp, outputMessage + 12,
                     bigEndian_);
            decodeBuffer.decodeBoolValue(value);
            outputMessage[16] = (unsigned char) value;
          }
          break;
        case ReparentNotify:
          {
            unsigned char* nextDest = outputMessage + 4;
            for (unsigned int i = 0; i < 3; i++)
            {
              decodeBuffer.decodeCachedValue(value, 29,
                             serverCache_ -> reparentNotifyWindowCache, 9);
              PutULONG(value, nextDest, bigEndian_);
              nextDest += 4;
            }
            decodeBuffer.decodeValue(value, 16, 6);
            PutUINT(value, nextDest, bigEndian_);
            decodeBuffer.decodeValue(value, 16, 6);
            PutUINT(value, nextDest + 2, bigEndian_);
            decodeBuffer.decodeBoolValue(value);
            outputMessage[20] = (unsigned char)value;
          }
          break;
        case SelectionClear:
          {
            decodeBuffer.decodeValue(value, 32, 9);
            serverCache_ -> lastTimestamp += value;
            PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
                     bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearWindowCache, 9);
            PutULONG(value, outputMessage + 8, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearAtomCache, 9);
            PutULONG(value, outputMessage + 12, bigEndian_);
          }
          break;
        case SelectionRequest:
          {
            decodeBuffer.decodeValue(value, 32, 9);
            serverCache_ -> lastTimestamp += value;
            PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
                     bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearWindowCache, 9);
            PutULONG(value, outputMessage + 8, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearWindowCache, 9);
            PutULONG(value, outputMessage + 12, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearAtomCache, 9);
            PutULONG(value, outputMessage + 16, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearAtomCache, 9);
            PutULONG(value, outputMessage + 20, bigEndian_);
            decodeBuffer.decodeCachedValue(value, 29,
                                   serverCache_ -> selectionClearAtomCache, 9);
            PutULONG(value, outputMessage + 24, bigEndian_);
          }
          break;
        case VisibilityNotify:
          {
            decodeBuffer.decodeCachedValue(value, 29,
                               serverCache_ -> visibilityNotifyWindowCache, 9);
            PutULONG(value, outputMessage + 4, bigEndian_);
            decodeBuffer.decodeValue(value, 2);
            outputMessage[8] = (unsigned char) value;
          }
          break;
        default:
          {
            #ifdef TEST
            *logofs << "handleWrite: Using generic event compression "
                    << "for OPCODE#" << (unsigned int) outputOpcode
                    << ".\n" << logofs_flush;
            #endif

            decodeBuffer.decodeCachedValue(*(outputMessage + 1), 8,
                         serverCache_ -> genericEventCharCache);

            for (unsigned int i = 0; i < 14; i++)
            {
              decodeBuffer.decodeCachedValue(value, 16,
                           *serverCache_ -> genericEventIntCache[i]);

              PutUINT(value, outputMessage + i * 2 + 4, bigEndian_);
            }
          }
        } // End of switch (outputOpcode)...

        #if defined(TEST) || defined(OPCODES)
        if (outputOpcode != X_Error)
        {
          *logofs << "handleWrite: Handled event OPCODE#"
                  << (unsigned int) outputOpcode << " for FD#"
                  << fd_ << " sequence " << lastSequence_ << " (real was "
                  << serverSequence_ << "). Size is " << outputLength
                  << ".\n" << logofs_flush;
        }
        #endif

        //
        // Check if we need to suppress the error.
        //

        if (outputOpcode == X_Error &&
                handleTaintSyncError(*(outputMessage + 10)) > 0)
        {
          #if defined(TEST) || defined(OPCODES)
          *logofs << "handleWrite: WARNING! Suppressed error OPCODE#"
                  << (unsigned int) outputOpcode << " for FD#"
                  << fd_ << " sequence " << lastSequence_ << ".\n"
                  << logofs_flush;
          #endif

          writeBuffer_.removeMessage(32);
        }

      } // End of if (outputOpcode == 1)... else ...

      //
      // Check if we produced enough data. We need to
      // decode all provided messages. Just update the
      // finish flag in case of failure.
      //

      handleFlush(flush_if_needed);

    } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ...

  } // End of the decoding block.

  //
  // Write any remaining data to the X connection.
  //

  if (handleFlush(flush_if_any) < 0)
  {
    return -1;
  }

  return 1;
}

//
// End of handleWrite().
//

//
// Other members.
//

int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
                                   T_store_action action, int position, const unsigned char opcode,
                                       const unsigned char *buffer, const unsigned int size)
{
  #if defined(TEST) || defined(SPLIT)

  // Since ProtoStep8 (#issue 108)
  *logofs << "handleSplit: PANIC! SPLIT! Split should "
          << "not be enabled for message " << "OPCODE#"
          << (unsigned int) store -> opcode() << ".\n"
          << logofs_flush;

  HandleCleanup();

  #endif

  //
  // Refuse the split if it is not introduced
  // by a start split.
  //

  // Since ProtoStep7 (#issue 108)
  if (splitState_.resource == nothing || enableSplit_ == 0)
  {
    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplit: SPLIT! Nothing to do for message "
            << "OPCODE#" << (unsigned int) store -> opcode()
            << " of size " << size << " position " << position
            << " with action [" << DumpAction(action) << "] at "
            << strMsTimestamp() << ".\n" << logofs_flush;
    #endif

    encodeBuffer.encodeBoolValue(0);

    return 0;
  }

  //
  // It's not advisable to allocate the store at
  // the time we receive the start-split because
  // we may process all the splits received and
  // deallocate the store even before we receive
  // the end split. Another message for the same
  // split sequence may then come and we would
  // have a null split store.
  //

  handleSplitStoreAlloc(&splitResources_, splitState_.resource);

  //
  // Check if the split was actually requested by
  // the agent and if the request was saved in the
  // message store. The split can also be refused
  // if the message is smaller than the threshold
  // or if the split store is already full.
  //

  if (mustSplitMessage(splitState_.resource) == 0)
  {
    if (action == IS_HIT || canSplitMessage(splitState_.mode, size) == 0)
    {
      #if defined(TEST) || defined(SPLIT)

      if (splitState_.mode == split_none)
      {
        #ifdef PANIC
        *logofs << "handleSplit: PANIC! SPLIT! Split state has "
                << "mode 'none'.\n" << logofs_flush;
        #endif

        HandleCleanup();
      }

      if (action != IS_HIT && (int) size >=
              control -> SplitDataThreshold)
      {
        #ifdef WARNING
        *logofs << "handleSplit: WARNING! SPLIT! Split stores have "
                << clientStore_ -> getSplitTotalSize() << " messages "
                << "and " << clientStore_ -> getSplitTotalStorageSize()
                << " allocated bytes.\n" << logofs_flush;
        #endif
      }

      #endif

      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleSplit: SPLIT! Message OPCODE#"
              << (unsigned int) store -> opcode() << " of size " << size
              << " [not split] with resource " << splitState_.resource
              << " mode " << splitState_.mode << " position " << position
              << " and action [" << DumpAction(action) << "] at "
              << strMsTimestamp() << ".\n" << logofs_flush;
      #endif

      encodeBuffer.encodeBoolValue(0);

      return 0;
    }
  }

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplit: SPLIT! Message OPCODE#"
          << (unsigned int) store -> opcode() << " of size " << size
          << " [split] with resource " << splitState_.resource
          << " mode " << splitState_.mode << " position " << position
          << " and action [" << DumpAction(action) << "] at "
          << strMsTimestamp() << ".\n" << logofs_flush;
  #endif

  encodeBuffer.encodeBoolValue(1);

  T_checksum checksum = NULL;

  if (action == IS_ADDED)
  {
    checksum = store -> getChecksum(position);
  }
  else if (action == is_discarded)
  {
    //
    // Generate the checksum on the fly.
    //

    checksum = store -> getChecksum(buffer, size, bigEndian_);
  }

  //
  // The method must abort the connection
  // if it can't allocate the split.
  //

  Split *splitMessage = clientStore_ -> getSplitStore(splitState_.resource) ->
                              add(store, splitState_.resource, splitState_.mode,
                                      position, action, checksum, buffer, size);

  //
  // Send the checksum. By using the checksum,
  // the remote end will try to locate the
  // message and load it from disk.
  //

  if (action == IS_HIT)
  {
    splitMessage -> setState(split_loaded);
  }
  else if (handleSplitChecksum(encodeBuffer, checksum) == 0)
  {
    //
    // If the checksum is not sent, for example
    // because loading of messages from disk is
    // disabled, then mark the split as missed.
    //

    #ifdef WARNING
    *logofs << "handleSplit: WARNING! Checksum not sent. "
            << "Marking the split as [missed].\n"
            << logofs_flush;
    #endif

    splitMessage -> setState(split_missed);
  }

  if (action == is_discarded)
  {
    delete [] checksum;
  }

  //
  // Check if we are ready to send a new split
  // for this store.
  //

  handleSplitPending(splitState_.resource);

  #if defined(TEST) || defined(SPLIT)

  *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
             getSplitTotalSize() << " messages and " << clientStore_ ->
             getSplitTotalStorageSize() << " bytes to send in "
          << "the split stores.\n" << logofs_flush;

  clientStore_ -> dumpSplitStore(splitState_.resource);

  #endif

  return 1;
}

int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer)
{
  //
  // Determine the maximum amount of bytes
  // we can write in this iteration.
  //

  int total = control -> SplitDataPacketLimit;

  int bytes  = total;
  int splits = 0;

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplit: SPLIT! Handling splits "
          << "for FD#" << fd_ << " with " << clientStore_ ->
             getSplitTotalSize() << " elements and " << total
          << " bytes to write at " << strMsTimestamp() << ".\n"
          << logofs_flush;
  #endif

  if (proxy -> handleAsyncSwitch(fd_) < 0)
  {
    return -1;
  }

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplit: SPLIT! Looping to find "
          << "if there is any split to send.\n"
          << logofs_flush;
  #endif

  SplitStore *splitStore;

  Split *splitMessage;

  //
  // Divide the available bandwidth among all the active
  // split stores by implementing a simple round-robin
  // mechanism. This can be extended by using an external
  // function returning the number of bytes to be written
  // based on the state of the split (splits which didn't
  // receive yet a confirmation event could be delayed),
  // the current bitrate, and by letting the agent asso-
  // ciate a priority to the resource in the start split
  // operation.
  //

  splitState_.pending = 0;

  splitResources_.rotate();

  //
  // Copy the list since elements can be removed
  // in the middle of the loop.
  //

  T_list splitList = splitResources_.copyList();

  for (T_list::iterator j = splitList.begin();
           j != splitList.end(); j++)
  {
    int resource = *j;

    #ifdef DEBUG
    *logofs << "handleSplit: SPLIT! Looping with current "
            << "resource " << resource << ".\n"
            << logofs_flush;
    #endif

    splitStore = clientStore_ -> getSplitStore(resource);

    if (splitStore != NULL)
    {
      //
      // Don't send more than the the packet size
      // bytes but ensure that we abort any split
      // found in the disk cache.
      //

      for (;;)
      {
        #if defined(TEST) || defined(SPLIT)

        clientStore_ -> dumpSplitStore(resource);

        #endif

        splitMessage = splitStore -> getFirstSplit();

        if (splitMessage == NULL)
        {
          //
          // We have created the store after a start
          // split but no message was added yet.
          //

          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplit: WARNING! SPLIT! The split store "
                  << "is still empty.\n" << logofs_flush;
          #endif

          break;
        }

        //
        // Splits already aborted can't be in the
        // split store.
        //

        #if defined(TEST) || defined(SPLIT)

        if (splitMessage -> getState() == split_aborted)
        {
          *logofs << "handleSplit: PANIC! SPLIT! Found an "
                  << "aborted split in store [" << resource
                  << "].\n" << logofs_flush;

          HandleCleanup();
        }

        #endif

        //
        // Check if there are more messages in the
        // store that can be aborted or if we have
        // exceeded the number of bytes we can send
        // for this iteration.
        //

        #if defined(TEST) || defined(SPLIT)
        *logofs << "handleSplit: SPLIT! Checking closure "
                << "of the inner loop with " << bytes
                << " bytes to write and split state ["
                << DumpState(splitMessage -> getState())
                << "].\n" << logofs_flush;
        #endif

        if ((splitMessage -> getMode() == split_sync &&
                splitMessage -> getState() == split_added) ||
                    (bytes <= 0 && splitMessage ->
                        getState() != split_loaded))
        {
          break;
        }

        //
        // If the split was loaded at the remote
        // side abort it immediately.
        //

        if (splitMessage -> getState() == split_loaded)
        {
          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplit: SPLIT! Sending more data "
                  << "for store [" << resource << "] with "
                  << "a split to be aborted.\n"
                  << logofs_flush;
          #endif

          if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
          {
            return -1;
          }
        }
        else if (bytes > 0)
        {
          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplit: SPLIT! Sending more data "
                  << "for store [" << resource << "] with "
                  << bytes << " bytes to send.\n"
                  << logofs_flush;
          #endif

          if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
          {
            return -1;
          }
        }

        //
        // Check if the split store was deleted.
        //

        splitStore = clientStore_ -> getSplitStore(resource);

        if (splitStore == NULL)
        {
          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplit: SPLIT! Exiting from the "
                  << "inner loop with split store [" << resource
                  << "] destroyed.\n" << logofs_flush;
          #endif

          break;
        }
      }

      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleSplit: SPLIT! Completed handling splits "
              << "for store [" << resource << "] with " << bytes
              << " bytes still to send.\n" << logofs_flush;
      #endif

      //
      // Check if there is still a split to
      // send for the store just processed.
      //

      handleSplitPending(resource);
    }
  }

  #if defined(TEST) || defined(SPLIT)

  if (splits == 0)
  {
    #ifdef PANIC
    *logofs << "handleSplit: PANIC! Function called but "
            << "no split message was sent.\n"
            << logofs_flush;
    #endif

    HandleCleanup();
  }

  *logofs << "handleSplit: SPLIT! Sent " << splits
          << " splits and " << total - bytes << " bytes for FD#" << fd_
          << " with " << clientStore_ -> getSplitTotalStorageSize()
          << " bytes and [" << clientStore_ -> getSplitTotalSize()
          << "] splits remaining.\n" << logofs_flush;

  *logofs << "handleSplit: SPLIT! The pending split flag is "
          << splitState_.pending << " with " << clientStore_ ->
             getSplitTotalSize() << " splits in the split stores.\n"
          << logofs_flush;

  clientStore_ -> dumpSplitStores();

  #endif

  return 1;
}

int ClientChannel::handleSplitSend(EncodeBuffer &encodeBuffer, int resource,
                                       int &splits, int &bytes)
{
  #if defined(TEST) || defined(SPLIT) 

  SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

  Split *splitMessage = splitStore -> getFirstSplit();

  if (splitStore -> getResource() != resource ||
          splitMessage -> getResource() != resource)
  {
    #ifdef PANIC
    *logofs << "handleSplitSend: PANIC! The resource doesn't "
            << "match the split store.\n" << logofs_flush;
    #endif

    HandleCleanup();
  }

  *logofs << "handleSplitSend: SPLIT! Sending message "
          << "OPCODE#" << (unsigned) opcodeStore_ -> splitData
          << " for resource " << splitMessage -> getResource()
          << " with request " << splitMessage -> getRequest()
          << " position " << splitMessage -> getPosition()
          << " and " << bytes << " bytes to write.\n"
          << logofs_flush;
  #endif

  //
  // Use a special opcode to signal the other
  // side this is part of a split and not a
  // new message.
  //

  encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitData,
                                     clientCache_ -> opcodeCache);

  encodeBuffer.encodeCachedValue(resource, 8,
                     clientCache_ -> resourceCache);

  int result = clientStore_ -> getSplitStore(resource) ->
                     send(encodeBuffer, bytes);

  if (result < 0)
  {
    #ifdef PANIC
    *logofs << "handleSplit: PANIC! Error sending splits for FD#"
            << fd_ << ".\n" << logofs_flush;
    #endif

    cerr << "Error" << ": Error sending splits for FD#"
           << fd_ << ".\n";

    return -1;
  }

  //
  // Get the bits written and update the
  // statistics for this special opcode.
  //

  int bits = encodeBuffer.diffBits();

  #if defined(TEST) || defined(SPLIT)|| defined(OPCODES)
  *logofs << "handleSplitSend: SPLIT! Handled request OPCODE#"
          << (unsigned int) opcodeStore_ -> splitData << " ("
          << DumpOpcode(opcodeStore_ -> splitData) << ")" << " for FD#"
          << fd_ << " sequence none. 0 bytes in, " << bits << " bits ("
          << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
  #endif

  statistics -> addRequestBits(opcodeStore_ -> splitData, 0, bits);

  bytes -= bits >> 3;

  splits++;

  if (result == 1)
  {
    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitSend: SPLIT! Split at the head "
            << "of the list was completely transferred.\n"
            << logofs_flush;
    #endif

    //
    // The split at the head of the list was
    // completely transferred.
    //

    handleRestart(sequence_deferred, resource);
  }
  #if defined(TEST) || defined(SPLIT)
  else
  {
    *logofs << "handleSplitSend: SPLIT! More data to send "
            << "for the split at the head of the list.\n"
            << logofs_flush;
  }
  #endif

  return result;
}

int ClientChannel::handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum)
{
  //
  // Send the checksum only if the loading
  // or the saving of the message to the
  // persistent image cache is enabled.
  //

  if ((control -> ImageCacheEnableLoad == 1 ||
          control -> ImageCacheEnableSave == 1) &&
              (enableLoad_ == 1 || enableSave_ == 1))
  {
    encodeBuffer.encodeBoolValue(1);

    for (unsigned int i = 0; i < MD5_LENGTH; i++)
    {
      encodeBuffer.encodeValue((unsigned int) checksum[i], 8);
    }

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitChecksum: SPLIT! Sent checksum "
            <<  "[" << DumpChecksum(checksum) << "].\n"
            << logofs_flush;
    #endif

    return 1;
  }
  else
  {
    encodeBuffer.encodeBoolValue(0);

    return 0;
  }
}

void ClientChannel::handleSplitPending()
{
  #if defined(TEST) || defined(SPLIT)

  int previous = splitState_.pending;

  #endif

  if (clientStore_ -> getSplitTotalSize() == 0)
  {
    splitState_.pending = 0;

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitPending: SPLIT! Set the pending "
            << "split flag to " << splitState_.pending
            << " with split stores empty.\n"
            << logofs_flush;
    #endif
  }
  else
  {
    //
    // Loop through the stores to find if
    // there is any split that has become
    // ready.
    //

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitPending: WARNING! SPLIT! Looping to "
            << "find if there is any split pending.\n"
            << logofs_flush;
    #endif

    splitState_.pending = 0;

    T_list &splitList = splitResources_.getList();

    for (T_list::iterator j = splitList.begin();
             j != splitList.end(); j++)
    {
      int resource = *j;

      SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

      if (splitStore != NULL)
      {
        #if defined(TEST) || defined(SPLIT)

        clientStore_ -> dumpSplitStore(resource);

        #endif

        Split *splitMessage = splitStore -> getFirstSplit();

        if (splitMessage != NULL && canSendSplit(splitMessage) == 1)
        {
          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplitPending: SPLIT! Found a pending "
                  << "split in store [" << resource << "].\n"
                  << logofs_flush;
          #endif

          splitState_.pending = 1;

          #if defined(TEST) || defined(SPLIT)

          if (splitMessage -> getState() == split_loaded)
          {
            *logofs << "handleSplitPending: PANIC! SPLIT! Found a "
                    << "loaded split in store [" << resource
                    << "].\n" << logofs_flush;

            HandleCleanup();
          }

          #endif

          break;
        }
      }
    }

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitPending: SPLIT! Set the pending "
            << "split flag to " << splitState_.pending
            << " with " << clientStore_ -> getSplitTotalSize()
            << " splits in the split stores.\n"
            << logofs_flush;
    #endif
  }

  #if defined(TEST) || defined(SPLIT)

  if (splitState_.pending != previous)
  {
    *logofs << "handleSplitPending: SPLIT! Pending state "
            << "changed from " << previous << " to "
            << splitState_.pending << ".\n"
            << logofs_flush;
  }

  #endif
}

int ClientChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage)
{
  SplitStore *splitStore;

  int resource = splitMessage -> getResource();

  #if defined(TEST) || defined(INFO)

  splitStore = clientStore_ -> getSplitStore(resource);

  if (splitStore == NULL)
  {
    #ifdef PANIC
    *logofs << "handleSplitEvent: PANIC! The split store can't "
            << "be NULL handling abort splits.\n"
            << logofs_flush;
    #endif

    HandleCleanup();
  }
  else if (splitMessage -> getState() != split_loaded)
  {
    *logofs << "handleSplitEvent: PANIC! Can't find the split "
            << "to be aborted.\n" << logofs_flush;

    HandleCleanup();
  }

  #endif

  //
  // Send any split that it is possible to
  // abort until the store is either empty
  // or the next split can't be aborted.
  //

  if (proxy -> handleAsyncSwitch(fd_) < 0)
  {
    return -1;
  }

  while ((splitStore = clientStore_ ->
             getSplitStore(resource)) != NULL &&
                 (splitMessage = splitStore -> getFirstSplit()) != NULL &&
                     splitMessage -> getState() == split_loaded)
  {
    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleSplitEvent: SPLIT! Aborting split with "
            << "checksum [" << DumpChecksum(splitMessage ->
               getChecksum()) << "] for resource " << resource
            << " at " << strMsTimestamp() << ".\n"
            << logofs_flush;
    #endif

    int any = 0;

    if (handleSplitSend(encodeBuffer, resource, any, any) < 0)
    {
      return -1;
    }
  }

  #if defined(TEST) || defined(SPLIT)

  if ((splitStore = clientStore_ ->
          getSplitStore(resource)) == NULL)
  {
    *logofs << "handleSplitEvent: SPLIT! The split store ["
            << resource << "] has been destroyed.\n"
            << logofs_flush;
  }
  else if ((splitMessage = splitStore ->
               getFirstSplit()) == NULL)
  {
    *logofs << "handleSplitEvent: SPLIT! The split store ["
            << resource << "] is empty.\n"
            << logofs_flush;
  }
  else if (splitMessage -> getState() != split_loaded)
  {
    *logofs << "handleSplitEvent: SPLIT! The split at the "
            << "head of store [" << resource << "] doesn't "
            << "need to be aborted.\n" << logofs_flush;
  }

  #endif

  return 1;
}

int ClientChannel::handleSplitEvent(DecodeBuffer &decodeBuffer)
{
  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplitEvent: SPLIT! Handling abort "
          << "split messages for FD#" << fd_ << " at "
          << strMsTimestamp() << ".\n" << logofs_flush;
  #endif

  // Since ProtoStep7 (#issue 108)

  //
  // Decode the information about the
  // message to be updated.
  //

  unsigned char resource;

  decodeBuffer.decodeCachedValue(resource, 8,
                     serverCache_ -> resourceCache);

  unsigned int loaded;

  decodeBuffer.decodeBoolValue(loaded);

  unsigned char request;
  unsigned int  size;

  if (loaded == 1)
  {
    decodeBuffer.decodeOpcodeValue(request, serverCache_ -> abortOpcodeCache);

    decodeBuffer.decodeValue(size, 32, 14);
  }
  else
  {
    request = 0;
    size    = 0;
  }

  unsigned int value;

  md5_byte_t checksum[MD5_LENGTH];

  for (unsigned int i = 0; i < MD5_LENGTH; i++)
  {
    decodeBuffer.decodeValue(value, 8);

    checksum[i] = (unsigned char) value;
  }

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplitEvent: SPLIT! Checking split "
          << "with checksum [" << DumpChecksum(checksum)
          << "] loaded " << loaded << " request " << (unsigned int)
             request << " compressed size " << size << " at "
          << strMsTimestamp() << ".\n" << logofs_flush;
  #endif

  Split *splitMessage = handleSplitFind(checksum, resource);

  if (splitMessage != NULL)
  {
    if (loaded == 1)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleSplitEvent: SPLIT! Marked split with "
              << "checksum [" << DumpChecksum(checksum) << "] "
              << "as [loaded] at " << strMsTimestamp() << ".\n"
              << logofs_flush;
      #endif

      splitMessage -> setState(split_loaded);

      #if defined(TEST) || defined(SPLIT)

      if (splitMessage -> compressedSize() != (int) size)
      {
        *logofs << "handleSplitEvent: WARNING! SPLIT! Updating "
                << "compressed data size from " << splitMessage ->
                   compressedSize() << " to " << size << ".\n"
                << logofs_flush;
      }

      #endif

      splitMessage -> compressedSize(size);

      //
      // The splits to be aborted are checked by the split
      // store at the time we are going to send a new chunk
      // of split data. The splits must be strictly handled
      // in the same order as they were added to the split
      // store and the split we want to abort here may be
      // not at the head of the list.
      //

      if (splitMessage == clientStore_ ->
              getSplitStore(resource) -> getFirstSplit())
      {
        //
        // We don't need to flush this packet immediately.
        // The abort can be sent at any time to the remote
        // proxy. What's important is that we restart the
        // agent resource as soon as possible.
        //

        #if defined(TEST) || defined(SPLIT)

        T_timestamp startTs = getTimestamp();

        *logofs << "handleSplitEvent: SPLIT! Encoding abort "
                << "split events for FD#" << fd_ << " with "
                << "resource " << (unsigned) resource << " at "
                << strMsTimestamp() << ".\n" << logofs_flush;
        #endif

        if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
        {
          return -1;
        }

        #if defined(TEST) || defined(SPLIT)
        *logofs << "handleSplitEvent: SPLIT! Spent "
                << diffTimestamp(startTs, getTimestamp()) << " ms "
                << "handling abort split events for FD#" << fd_
                << ".\n" << logofs_flush;
        #endif

        //
        // Check if we can clear the pending flag.
        //

        handleSplitPending();
      }
      #if defined(TEST) || defined(SPLIT)
      else
      {
        *logofs << "handleSplitEvent: WARNING! SPLIT! Abort split "
                << "event not sent because not at the head "
                << "of the list.\n" << logofs_flush;
      }
      #endif
    }
    else
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleSplitEvent: SPLIT! Marked split with "
              << "checksum [" << DumpChecksum(checksum) << "] "
              << "as [missed] at " << strMsTimestamp() << ".\n"
              << logofs_flush;
      #endif

      splitMessage -> setState(split_missed);

      //
      // Check if we can set the pending flag.
      //

      handleSplitPending(resource);
    }
  }
  else
  {
    //
    // The split report came after the split was already
    // sent or the split store deleted. If the message
    // had been loaded from disk by the remote side, we
    // need to update the compressed size in our message
    // store or the checksum will not match at the time
    // we will try to save the message store on disk.
    //

    if (loaded == 1 && size != 0)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleSplitEvent: WARNING! SPLIT! Can't find "
              << "the split. Updating in the message store.\n"
              << logofs_flush;
      #endif

      MessageStore *store = clientStore_ -> getRequestStore(request);

      if (store != NULL)
      {
        store -> updateData(checksum, size);
      }
      #if defined(TEST) || defined(SPLIT)
      else
      {
        #ifdef PANIC
        *logofs << "handleSplitEvent: PANIC! The message store "
                << "can't be null.\n" << logofs_flush;
        #endif

        HandleCleanup();
      }
      #endif
    }
    #if defined(TEST) || defined(SPLIT)
    else
    {
      *logofs << "handleSplitEvent: WARNING! SPLIT! No need to "
              << "update the store with loaded " << loaded
              << " and compressed size " << size << ".\n"
              << logofs_flush;
    }
    #endif
  }

  return 1;
}

Split *ClientChannel::handleSplitFind(T_checksum checksum, int resource)
{
  //
  // It can be that we handled all the splits,
  // restarted the resource and deleted the
  // store before the event could even reach
  // our side.
  //

  SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

  if (splitStore != NULL)
  {
    Split *splitMessage;

    T_splits *splitList = splitStore -> getSplits();

    for (T_splits::iterator i = splitList -> begin();
             i != splitList -> end(); i++)
    {
      splitMessage = (*i);

      if (splitMessage -> getChecksum() != NULL)
      {
        #if defined(TEST) || defined(SPLIT)
        *logofs << "handleSplitFind: SPLIT! Comparing with message ["
                << DumpChecksum(splitMessage -> getChecksum())
                << "].\n" << logofs_flush;
        #endif

        if (memcmp(checksum, splitMessage -> getChecksum(), MD5_LENGTH) == 0)
        {
          #if defined(TEST) || defined(SPLIT)
          *logofs << "handleSplitFind: SPLIT! Located split for "
                  << "checksum [" << DumpChecksum(checksum) << "] "
                  << "in store [" << splitStore -> getResource()
                  << "].\n" << logofs_flush;
          #endif

          return splitMessage;
        }
      }
    }
  }
  #if defined(TEST) || defined(SPLIT)
  else
  {
    *logofs << "handleSplitFind: WARNING! SPLIT! The split store "
            << "was already deleted.\n" << logofs_flush;
  }
  #endif

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleSplitFind: WARNING! SPLIT! Can't find the "
          << "split for checksum [" << DumpChecksum(checksum)
          << "].\n" << logofs_flush;
  #endif

  return NULL;
}

int ClientChannel::handleRestart(T_sequence_mode mode, int resource)
{
  //
  // The agent must send a start-split message, followed by the
  // X messages that may be optionally split by the proxy. Usu-
  // ally, in the middle of a start-split/end-split sequence is
  // a single PutImage() or PutPackedImage(), that, in turn,
  // can generate multiple partial requests, like a SetUnpack-
  // Colormap() and SetUnpackAlpha() followed by the image that
  // must be transferred. Multiple requests may be also genera-
  // ted because the maximum size of a X request has been exce-
  // eded, so that Xlib has divided the single image in multi-
  // ple sub-image requests. The agent doesn't need to take care
  // of that, except tracking the result of the split operation.
  //
  // By monitoring the notify events sent by the proxy, the
  // agent will have to implement its own strategy to deal with
  // its resources (for example its clients). For example:
  //
  // - It will issue a new image request and suspend a client
  //   if the image was not entirely sent in the main X oputput
  //   stream.
  //
  // - It will choose to commit or discard the messages after
  //   they are recomposed at the remote side. The set of mes-
  //   sages that will have to be committed will include all
  //   messages that were part of the split (the colormap, the
  //   alpha channel).
  //
  // - It will restart its own client, in the case it had been
  //   suspended.
  //
  // A more useful strategy would be to replace the original im-
  // age with a tiny 'placeholder' if a split took place, and
  // synchronize the content of the drawable at later time. This
  // is generally referred as 'lazy encoding'.
  //
  // The agent will be able to identify the original split ope-
  // ration (the one marked with the start-spit) by the small
  // integer number (0-255) referred to as the 'resource' field.
  //
  // Before the proxy will be able to report the status of the
  // split, the agent will have to close the sequence by issuing
  // an end-split. The proxy will then report the result of the
  // operation, so that the agent will have the option of suspend-
  // ing the client or marking the drawable as dirty and take
  // care of synchronizing it at later time.
  //
  // One of the following cases may be encountered:
  //
  // notify_no_split:     All messages were sent in the main out-
  //                      put stream, so that no split actually
  //                      took place.
  //
  // notify_start_split:  One or more messages were split, so,
  //                      at discrection of the agent, the client
  //                      may be suspended until the transferral
  //                      is completed.
  //
  // notify_commit_split: One of the requests that made up the
  //                      split was recomposed. The agent should
  //                      either commit the given request or tell
  //                      the proxy to discard it.
  //
  // notify_end_split:    The split was duly completed. The agent
  //                      can restart the client.
  //
  // notify_empty_split:  No more split operation are pending.
  //                      The agent can use this information to
  //                      implement specific strategies requiring
  //                      that all messages have been recomposed
  //                      at the remote end, like updating the
  //                      drawables that were not synchronized
  //                      because of the lazy encoding.
  //
  // By checking the split and commit store we can determine if we
  // need to send a new notification event to the agent. There can
  // be four different cases:
  //
  // - If the split store is not null and not empty, we are still
  //   in the middle of a split.
  //
  // - If the commit store is not empty, we completely recomposed
  //   a full message and can send a new commit notify.
  //
  // - If the split store has become empty, we recomposed all the
  //   messages added for the given resource, and so will be able
  //   to restart the resource.
  //
  // - If no more messages are in the split stores, we can notify
  //   an empty split event to the agent.
  //

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleRestart: SPLIT! Handling ["
          << (mode == sequence_immediate ? "immediate" : "deferred")
          << "] restart events for resource " << resource << " at "
          << strMsTimestamp() << ".\n" << logofs_flush;
  #endif

  SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

  if (mode == sequence_immediate)
  {
    //
    // We have received an end-split request. If the store
    // was not deleted already, we mark the last split added
    // as the one ending the row for this resource. If the
    // commit() function returns 0 it means that the split
    // store is either empty or that we did not add any split
    // for this resource. This is because when connected to
    // an old proxy version we only have a single store for
    // all the resources.   
    //
    // It can happen that all the split messages that were
    // originally appended to the list were completely sent
    // before our client had the chance of ending the split
    // sequence. In this case the split store will be empty
    // or already deleted and so we will be able to restart
    // the resource.
    //

    #if defined(TEST) || defined(SPLIT)

    if (splitStore == NULL)
    {
      *logofs << "handleRestart: WARNING! SPLIT! Split store ["
              << resource << "] was already deleted.\n"
              << logofs_flush;
    }
    else
    {
      clientStore_ -> dumpSplitStore(resource);
    }

    #endif

    if (splitStore == NULL || splitStore -> getSize() == 0)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleRestart: SPLIT! Immediate agent split event "
              << "TYPE#" << (unsigned) opcodeStore_ -> noSplitNotify
              << " [no split] with resource " << resource
              << " at " << strMsTimestamp() << ".\n"
              << logofs_flush;
      #endif

      if (handleNotify(notify_no_split, sequence_immediate,
                           resource, nothing, nothing) < 0)
      {
        return -1;
      }
    }
    else
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleRestart: SPLIT! Immediate agent split event "
              << "TYPE#" << (unsigned) opcodeStore_ -> startSplitNotify
              << " [start split] with resource " << resource
              << " at " << strMsTimestamp() << ".\n"
              << logofs_flush;
      #endif

      if (handleNotify(notify_start_split, sequence_immediate,
                           resource, nothing, nothing) < 0)
      {
        return -1;
      }
    }
  }
  else
  {
    //
    // We have completely transferred a message
    // that was put in the split store.
    //
    // The id of the resource can be different
    // than the index of the store if we are
    // connected to an old proxy.
    //

    #if defined(TEST) || defined(SPLIT)

    if (splitStore == NULL)
    {
      #ifdef PANIC
      *logofs << "handleRestart: PANIC! The split store can't "
              << "be NULL handling deferred restart events.\n"
              << logofs_flush;
      #endif

      HandleCleanup();
    }
    else
    {
      clientStore_ -> dumpSplitStore(resource);
    }

    #endif

    CommitStore *commitStore = clientStore_ -> getCommitStore();

    #if defined(TEST) || defined(SPLIT)

    clientStore_ -> dumpCommitStore();

    #endif

    //
    // Check if there is any commit to notify.
    //

    Split *split;

    T_splits *commitList = commitStore -> getSplits();

    for (T_splits::iterator i = commitList -> begin();
             i != commitList -> end(); i++)
    {
      split = *i;

      if (split -> getState() != split_notified)
      {
        #if defined(TEST) || defined(SPLIT) 

        if (split -> getResource() != resource)
        {
          #ifdef PANIC
          *logofs << "handleSplitSend: PANIC! The resource doesn't "
                  << "match the split store.\n" << logofs_flush;
          #endif

          HandleCleanup();
        }

        #endif

        int request  = split -> getRequest();
        int position = split -> getPosition();

        #if defined(TEST) || defined(SPLIT)
        *logofs << "handleRestart: SPLIT! Deferred agent split event "
                << "TYPE#" << (unsigned) opcodeStore_ -> commitSplitNotify
                << " [commit split] with resource " << resource << " request "
                << request << " position " << position << " at "
                << strMsTimestamp() << ".\n" << logofs_flush;
        #endif

        if (handleNotify(notify_commit_split, sequence_deferred,
                            resource, request, position) < 0)
        {
          return -1;
        }

        //
        // Don't send the notification again.
        //

        split -> setState(split_notified);
      }
      #if defined(TEST) || defined(SPLIT)
      else
      {
        *logofs << "handleRestart: SPLIT! Split for request "
                << split -> getRequest() << " and position "
                << split -> getPosition() << " was already "
                << "notified.\n" << logofs_flush;
      }
      #endif
    }

    //
    // Don't send the end split if we are still
    // in the middle of a start-split/end-split
    // sequence. We'll send a no-split at the
    // time the end-split is received.
    //

    if (splitStore -> getSize() == 0 &&
            splitStore -> getResource() != splitState_.resource)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleRestart: SPLIT! Deferred agent split event "
              << "TYPE#" << (unsigned) opcodeStore_ -> endSplitNotify
              << " [end split] with resource " << resource << " at "
              << strMsTimestamp() << ".\n" << logofs_flush;
      #endif

      if (handleNotify(notify_end_split, sequence_deferred,
                           resource, nothing, nothing) < 0)
      {
        return -1;
      }
    }
    #if defined(TEST) || defined(SPLIT)
    else if (splitStore -> getSize() == 0 &&
                splitStore -> getResource() == splitState_.resource)
    {
      *logofs << "handleRestart: SPLIT! WARNING! The split store "
              << "for resource " << resource << " was emptied in the "
              << "split sequence at " << strMsTimestamp() << ".\n"
              << logofs_flush;
    }
    #endif
  }

  //
  // Remove the split store if it's empty.
  //

  if (splitStore != NULL && splitStore -> getSize() == 0 &&
          splitStore -> getResource() != splitState_.resource)
  {
    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleRestart: SPLIT! Removing the split store ["
            << resource << "] at " << strMsTimestamp()
            << ".\n" << logofs_flush;
    #endif

    handleSplitStoreRemove(&splitResources_, resource);

    if (clientStore_ -> getSplitTotalSize() == 0)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleRestart: SPLIT! Deferred agent split event "
              << "TYPE#" << (unsigned) opcodeStore_ -> emptySplitNotify
              << " [empty split] for FD#" << fd_ << " at "
              << strMsTimestamp() << ".\n" << logofs_flush;
      #endif

      if (handleNotify(notify_empty_split, sequence_deferred,
                           nothing, nothing, nothing) < 0)
      {
        return -1;
      }
    }

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleRestart: SPLIT! There are " << clientStore_ ->
               getSplitTotalSize() << " messages and " << clientStore_ ->
               getSplitTotalStorageSize() << " bytes to send in "
            << "the split stores.\n" << logofs_flush;

    if ((clientStore_ -> getSplitTotalSize() != 0 &&
            clientStore_ -> getSplitTotalStorageSize() == 0) ||
                (clientStore_ -> getSplitTotalSize() == 0 &&
                    clientStore_ -> getSplitTotalStorageSize() != 0))
    {
      #ifdef PANIC
      *logofs << "handleRestart: PANIC! Inconsistency detected "
              << "while handling the split stores.\n"
              << logofs_flush;
      #endif

      HandleCleanup();
    }

    #endif
  }

  return 1;
}

int ClientChannel::handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer,
                                              unsigned int &size)
{
  //
  // Test the efficiency of the encoding
  // without these RENDER requests.
  //

  if (opcode == opcodeStore_ -> renderExtension &&
          (*(buffer + 1) == X_RenderCompositeGlyphs8 ||
               *(buffer + 1) == X_RenderCompositeGlyphs16 ||
                   *(buffer + 1) == X_RenderCompositeGlyphs32 ||
                       *(buffer + 1) == X_RenderAddGlyphs ||
                           *(buffer + 1) == X_RenderTrapezoids))
  {
    #ifdef TEST
    *logofs << "handleTaintLameRequest: Tainting request "
            << "OPCODE#" << (unsigned int) opcode << " MINOR#"
            << (unsigned int) *(buffer + 1) << " for FD#"
            << fd_ << ".\n" << logofs_flush;
    #endif

    opcode = X_NoOperation;

    return 1;
  }

  return 0;
}

int ClientChannel::handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer,
                                              unsigned int &size)
{
  //
  // Should short-circuit other common replies
  // whose values could be queried only once.
  // Examples are X_InterAtom, X_ListExtension
  // and X_QueryExtension.
  //

  if (taintCounter_ >= control -> TaintThreshold)
  {
    #ifdef DEBUG
    *logofs << "handleTaintSyncRequest: Reset taint counter after "
            << taintCounter_ << " replies managed.\n"
            << logofs_flush;
    #endif

    taintCounter_ = 0;

    return 0;
  }

  //
  // Check if we are rolling the counter.
  // The client sequence number has not
  // been incremented yet in the loop.
  //

  unsigned int sequence = (clientSequence_ + 1) & 0xffff;

  #ifdef DEBUG
  *logofs << "handleTaintSyncRequest: Opcode is " << (unsigned) opcode
          << " expected client sequence is " << sequence
          << ".\n" << logofs_flush;
  #endif

  if (sequence == 0xffff)
  {
    return 0;
  }

  unsigned short t1;
  unsigned char  t2;

  //
  // Check if there is a previous reply
  // pending.
  //

  if (sequenceQueue_.peek(t1, t2) != 0)
  {
    #ifdef DEBUG
    *logofs << "handleTaintSyncRequest: Skipping taint of reply due to "
            << "pending request OPCODE#" << t1 << " with sequence "
            << (unsigned int) t2 << ".\n" << logofs_flush;
    #endif

    return 0;
  }

  #ifdef DEBUG
  *logofs << "handleTaintSyncRequest: Suppressing get input focus "
          << "request for FD#" << fd_ << " with sequence "
          << sequence << ".\n" << logofs_flush;
  #endif

  unsigned char *reply = writeBuffer_.addMessage(32);

  *(reply + 0) = X_Reply;

  PutUINT(sequence, reply + 2, bigEndian_);

  PutULONG(0, reply + 4, bigEndian_);

  //
  // Set revert-to to none.
  //

  *(reply + 1) = 0;

  //
  // Set focus to none.
  //

  PutULONG(0, reply + 8, bigEndian_);

  //
  // Save the sequence number, not incremented
  // yet, we used to auto-generate this reply.
  //

  lastSequence_ = clientSequence_ + 1;

  #ifdef TEST
  *logofs << "handleTaintSyncRequest: Registered " << lastSequence_
          << " as last auto-generated sequence number.\n"
          << logofs_flush;
  #endif

  //
  // Taint the request to a X_NoOperation.
  //

  opcode = X_NoOperation;

  //
  // We may assume that the client has finished
  // drawing and flush immediately, even if this
  // seems to perceively affect the performance.
  //
  // priority_++;
  //

  if (handleFlush(flush_if_any) < 0)
  {
    return -1;
  }

  taintCounter_++;

  return 1;
}

int ClientChannel::handleTaintSyncError(unsigned char opcode)
{
  if (control -> TaintReplies > 0)
  {
    //
    // By enabling short-circuiting of replies
    // some window managers can get confused
    // by some otherwise innocuous X errors.
    //

    if (opcode == X_GrabKey || opcode == X_ReparentWindow ||
            opcode == X_ConfigureWindow)
    {
      #if defined(TEST) || defined(OPCODES)
      *logofs << "handleTaintSyncError: WARNING! Suppressed error "
              << "on OPCODE#" << (unsigned int) opcode << " for FD#"
              << fd_ << " sequence " << lastSequence_ << " (real was "
              << serverSequence_ << ").\n" << logofs_flush;
      #endif

      return 1;
    }
  }

  return 0;
}

int ClientChannel::handleNotify(T_notification_type type, T_sequence_mode mode,
                                    int resource, int request, int position)
{
  if (finish_ == 1)
  {
    #if defined(TEST) || defined(INFO)
    *logofs << "handleNotify: Discarding notification on "
            << "channel for FD#" << fd_ << ".\n"
            << logofs_flush;
    #endif

    return 0;
  }

  //
  // Add a new message to the write buffer.
  //

  unsigned char *event = writeBuffer_.addMessage(32);

  //
  // Event is ClientMessage, atom and
  // window are 0, format is 32.
  //

  *(event + 0) = ClientMessage;

  PutULONG(0, event + 4, bigEndian_);
  PutULONG(0, event + 8, bigEndian_);

  *(event + 1) = 32;

  //
  // If the event follows immediately the request (that is the
  // sequence mode is 'immediate') then the sequence number is
  // the one of the last request, else it should be the last
  // sequence number encoded by peer proxy but, as we are ins-
  // erting events in the stream, we must ensure that the se-
  // quence we send is not less than the last sequence we have
  // auto-generated.
  //

  if (mode == sequence_immediate)
  {
    //
    // Save the sequence number we used
    // to auto-generate this event.
    //

    lastSequence_ = clientSequence_;

    #if defined(TEST) || defined(INFO)
    *logofs << "handleNotify: Registered " << lastSequence_
            << " as last auto-generated sequence number.\n"
            << logofs_flush;
    #endif
  }
  else
  {
    if (serverSequence_ > lastSequence_)
    {
      #ifdef DEBUG
      *logofs << "handleNotify: Updating last event's sequence "
              << lastSequence_ << " to X server's sequence number "
              << serverSequence_ << " for FD#" << fd_ << ".\n"
              << logofs_flush;
      #endif

      lastSequence_ = serverSequence_;
    }
    #ifdef DEBUG
    else if (serverSequence_ < lastSequence_)
    {
      //
      // Use our last auto-generated sequence.
      //

      *logofs << "handleNotify: Tainting sequence number "
              << serverSequence_ << " to last event's sequence "
              << lastSequence_ << " for FD#" << fd_ << ".\n"
              << logofs_flush;
    }
    #endif
  }

  PutUINT(lastSequence_, event + 2, bigEndian_);

  //
  // Be sure we set to void the fields that
  // are not significant for the specific
  // notification message.
  //

  PutULONG(nothing, event + 16, bigEndian_);
  PutULONG(nothing, event + 20, bigEndian_);
  PutULONG(nothing, event + 24, bigEndian_);

  switch (type)
  {
    case notify_no_split:
    {
      PutULONG(opcodeStore_ -> noSplitNotify,
                   event + 12, bigEndian_);

      PutULONG(resource, event + 16, bigEndian_);

      break;
    }
    case notify_start_split:
    {
      PutULONG(opcodeStore_ -> startSplitNotify,
                   event + 12, bigEndian_);

      PutULONG(resource, event + 16, bigEndian_);

      break;
    }
    case notify_commit_split:
    {
      PutULONG(opcodeStore_ -> commitSplitNotify,
                   event + 12, bigEndian_);

      PutULONG(resource, event + 16, bigEndian_);

      PutULONG(request, event + 20, bigEndian_);

      PutULONG(position, event + 24, bigEndian_);

      break;
    }
    case notify_end_split:
    {
      PutULONG(opcodeStore_ -> endSplitNotify,
                   event + 12, bigEndian_);

      PutULONG(resource, event + 16, bigEndian_);

      break;
    }
    case notify_empty_split:
    {
      PutULONG(opcodeStore_ -> emptySplitNotify,
                   event + 12, bigEndian_);
      break;
    }
    default:
    {
      #ifdef PANIC
      *logofs << "handleNotify: PANIC! Unrecognized notify "
              << "TYPE#" << type << ".\n"
              << logofs_flush;
      #endif

      return -1;
    }
  }

  #if defined(TEST) || defined(INFO) || defined (SPLIT)

  *logofs << "handleNotify: Sending "
          << (mode ==  sequence_immediate ? "immediate " : "deferred ")
          << "agent notify event TYPE#" << GetULONG(event + 12, bigEndian_)
          << logofs_flush;

  if (resource != nothing)
  {
    *logofs << " with resource " << GetULONG(event + 16, bigEndian_)
            << logofs_flush;

    if (request != nothing && position != nothing)
    {
      *logofs << " request " << GetULONG(event + 20, bigEndian_)
              << " position " << GetULONG(event + 24, bigEndian_)
              << logofs_flush;
    }
  }

  *logofs << ".\n" << logofs_flush;

  #endif

  //
  // Send the notification now.
  //

  if (handleFlush(flush_if_any) < 0)
  {
    return -1;
  }

  return 1;
}

int ClientChannel::handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                                const unsigned char *buffer, const unsigned int size)
{
  //
  // Get the data of the request to be
  // committed.
  //

  unsigned char request = *(buffer + 5);

  MessageStore *store = clientStore_ -> getRequestStore(request);

  if (store == NULL)
  {
    #ifdef PANIC
    *logofs << "handleCommitSplitRequest: PANIC! Can't commit split for "
            << "request OPCODE#" << (unsigned int) request
            << ". No message store found.\n"
            << logofs_flush;
    #endif

    cerr << "Error" << ": Can't commit split for request "
         << "OPCODE#" << (unsigned int) request
         << ". No message store found.\n";

    return -1;
  }

  //
  // The position in cache of the message
  // to commit. Encode it as difference in
  // respect to the last encoded value.
  //

  unsigned int position = GetULONG(buffer + 8, bigEndian_);

  unsigned char resource = *(buffer + 1);
  unsigned int  commit   = *(buffer + 4);

  #if defined(TEST) || defined(SPLIT)

  if (commit == 1)
  {
    *logofs << "handleCommitSplitRequest: SPLIT! Committing request "
            << "OPCODE#" << (unsigned) request << " at position "
            << position << " for FD#" << fd_ << " with resource "
            << (unsigned) resource << ".\n" << logofs_flush;
  }
  else
  {
    *logofs << "handleCommitSplitRequest: SPLIT! Discarding request "
            << "OPCODE#" << (unsigned) request << " at position "
            << position << " for FD#" << fd_ << " with resource "
            << (unsigned) resource << ".\n" << logofs_flush;
  }

  #endif

  encodeBuffer.encodeOpcodeValue(request, clientCache_ -> opcodeCache);

  int diffCommit = position - splitState_.commit;

  splitState_.commit = position;

  encodeBuffer.encodeValue(diffCommit, 32, 5);

  //
  // Send the resource id and the commit
  // flag.
  //

  encodeBuffer.encodeCachedValue(resource, 8,
                     clientCache_ -> resourceCache);

  encodeBuffer.encodeBoolValue(commit);

  //
  // Remove the split from the split queue.
  //

  Split *split = handleSplitCommitRemove(request, resource, splitState_.commit);

  if (split == NULL)
  {
    return -1;
  }

  clientStore_ -> getCommitStore() -> update(split);

  //
  // Free the split.
  //

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the "
          << "committed split.\n" << logofs_flush;
  #endif

  delete split;

  return 1;
}

int ClientChannel::handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                               const unsigned char *buffer, const unsigned int size)
{
  unsigned char resource = *(buffer + 1);

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split "
          << "request for FD#"<< fd_ << " and resource "
          << (unsigned int) resource << ".\n"
          << logofs_flush;
  #endif

  encodeBuffer.encodeCachedValue(resource, 8,
                     clientCache_ -> resourceCache);

  SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

  if (splitStore == NULL)
  {
    #ifdef WARNING
    *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The split "
            << "store [" << (unsigned int) resource << "] "
            << "is already empty.\n" << logofs_flush;
    #endif

    return 0;
  }

  //
  // Loop through the messages in the split
  // store and discard from the memory cache
  // the messages that are still incomplete.
  // Then remove the message from the split
  // store.
  //

  #if defined(TEST) || defined(SPLIT)

  clientStore_ -> dumpSplitStore(resource);

  #endif

  int splits = 0;

  Split *splitMessage;

  for (;;)
  {
    splitMessage = splitStore -> getFirstSplit();

    if (splitMessage == NULL)
    {
      //
      // Check if we had created the store
      // but no message was added yet.
      //

      #ifdef WARNING

      if (splits == 0)
      {
        *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
                << "split store [" << (unsigned int) resource
                << "] is unexpectedly empty.\n"
                << logofs_flush;
      }

      #endif

      break;
    }

    //
    // Splits already aborted can't be in the
    // split store.
    //

    #if defined(TEST) || defined(SPLIT)

    if (splitMessage -> getState() == split_aborted)
    {
      *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an "
              << "aborted split in store [" << (unsigned int) resource
              << "].\n" << logofs_flush;

      HandleCleanup();
    }

    #endif

    if (splitMessage -> getAction() == IS_HIT)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
              << "split from the memory cache.\n"
              << logofs_flush;
      #endif

      splitMessage -> getStore() -> remove(splitMessage -> getPosition(),
                                               use_checksum, discard_data);
    }

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
            << "split from the split store.\n"
            << logofs_flush;
    #endif

    splitMessage = splitStore -> pop();

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the "
            << "aborted split.\n" << logofs_flush;
    #endif

    delete splitMessage;

    splits++;
  }

  //
  // If the start-split/end-split sequence
  // was closed, send the notification now,
  // else wait for the end-split.
  //

  if (resource != splitState_.resource)
  {
    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleAbortSplitRequest: SPLIT! Sending the "
            << "deferred [end split] event.\n"
           << logofs_flush;
    #endif

    handleRestart(sequence_deferred, resource);
  }
  #if defined(TEST) || defined(SPLIT)
  else
  {
    *logofs << "handleAbortSplitRequest: WARNING! SPLIT! Still "
            << "waiting for the closure of the split "
            << "sequence.\n" << logofs_flush;
  }
  #endif

  //
  // Check if there is any other store
  // having splits to send.
  //

  handleSplitPending();

  return (splits > 0);
}

int ClientChannel::handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                                const unsigned char *buffer, const unsigned int size)
{
  unsigned char resource = *(buffer + 1);

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleFinishSplitRequest: SPLIT! Handling finish split "
          << "request for FD#"<< fd_ << " and resource "
          << (unsigned int) resource << ".\n"
          << logofs_flush;
  #endif

  encodeBuffer.encodeCachedValue(resource, 8,
                     clientCache_ -> resourceCache);

  //
  // We need to get the protocol statistics
  // for the finish message we are handling
  // here because sending a new split will
  // reset the bits counter.
  //

  int bits = encodeBuffer.diffBits();

  statistics -> addRequestBits(opcode, size << 3, bits);

  SplitStore *splitStore = clientStore_ -> getSplitStore(resource);

  if (splitStore == NULL)
  {
    #ifdef WARNING
    *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The split "
            << "store [" << (unsigned int) resource << "] "
            << "is already empty.\n" << logofs_flush;
    #endif

    return 0;
  }

  //
  // Send all the split queued for the given
  // resource until the split store becomes
  // empty.
  //

  #if defined(TEST) || defined(SPLIT)

  clientStore_ -> dumpSplitStore(resource);

  #endif

  Split *splitMessage;

  int total = MESSAGE_DATA_LIMIT;

  int bytes  = total;
  int splits = 0;

  for (;;)
  {
    splitMessage = splitStore -> getFirstSplit();

    if (splitMessage == NULL)
    {
      //
      // We have presumably created the store
      // after a start split but no message
      // was added yet.
      //

      #ifdef WARNING
      *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The "
              << "split store [" << (unsigned int) resource
              << "] is unexpectedly empty.\n"
              << logofs_flush;
      #endif

      break;
    }

    //
    // Splits already aborted can't be in the
    // split store.
    //

    #if defined(TEST) || defined(SPLIT)

    if (splitMessage -> getState() == split_aborted)
    {
      *logofs << "handleFinishSplitRequest: PANIC! SPLIT! Found an "
              << "aborted split in store [" << (unsigned int) resource
              << "].\n" << logofs_flush;

      HandleCleanup();
    }

    *logofs << "handleFinishSplitRequest: SPLIT! Sending more "
            << "data for store [" << (unsigned int) resource
            << "].\n" << logofs_flush;
    #endif

    if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
    {
      return -1;
    }

    //
    // Check if the split store was deleted.
    //

    if (clientStore_ -> getSplitStore(resource) == NULL)
    {
      #if defined(TEST) || defined(SPLIT)
      *logofs << "handleFinishSplitRequest: SPLIT! Exiting "
              << "from the finish loop with split store ["
              << (unsigned int) resource << "] destroyed.\n"
              << logofs_flush;
      #endif

      break;
    }
  }

  //
  // Check if there is any other store
  // having splits to send.
  //

  handleSplitPending();

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleFinishSplitRequest: SPLIT! Sent " << splits
          << " splits and " << total - bytes << " bytes for FD#" << fd_
          << " with " << clientStore_ -> getSplitTotalStorageSize()
          << " bytes and [" << clientStore_ -> getSplitTotalSize()
          << "] splits remaining.\n" << logofs_flush;
  #endif

  return (splits > 0);
}

int ClientChannel::handleConfiguration()
{
  #ifdef TEST
  *logofs << "ClientChannel: Setting new buffer parameters.\n"
          << logofs_flush;
  #endif

  readBuffer_.setSize(control -> ClientInitialReadSize,
                          control -> ClientMaximumBufferSize);

  writeBuffer_.setSize(control -> TransportXBufferSize,
                           control -> TransportXBufferThreshold,
                               control -> TransportMaximumBufferSize);

  transport_ -> setSize(control -> TransportXBufferSize,
                            control -> TransportXBufferThreshold,
                                control -> TransportMaximumBufferSize);

  return 1;
}

int ClientChannel::handleFinish()
{
  #ifdef TEST
  *logofs << "ClientChannel: Finishing channel for FD#"
          << fd_ << ".\n" << logofs_flush;
  #endif

  congestion_ = 0;
  priority_   = 0;

  finish_ = 1;

  taintCounter_ = 0;

  splitState_.resource = nothing;
  splitState_.pending  = 0;
  splitState_.commit   = 0;
  splitState_.mode     = split_none;

  transport_ -> finish();

  return 1;
}

//
// If differential compression is disabled then use the
// most simple encoding but handle the image requests
// and the X_ListExtensions and X_QueryExtension messa-
// ges (needed to detect the opcode of the shape or the
// other extensions) in the usual way.
//

int ClientChannel::handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
                                             const unsigned char *&buffer, const unsigned int &size)
{
  //
  // All the NX requests are handled in the
  // main message loop. The X_PutImage can
  // be handled here only if the split was
  // not requested (since ProtoStep7 #issue 108).
  //

  if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) ||
           (opcode == X_PutImage && splitState_.resource != nothing) ||
               opcode == X_ListExtensions ||
                   opcode == X_QueryExtension)
  {
    return 0;
  }

  #ifdef DEBUG
  *logofs << "handleFastReadRequest: Encoding raw request OPCODE#"
          << (unsigned int) opcode << " for FD#" << fd_
          << " with size " << size << ".\n"
          << logofs_flush;
  #endif

  encodeBuffer.encodeMemory(buffer, size);

  //
  // Put request on the fast track
  // if it needs a reply.
  //

  switch (opcode)
  {
    case X_GetAtomName:
    case X_GetGeometry:
    case X_GetInputFocus:
    case X_GetModifierMapping:
    case X_GetKeyboardMapping:
    case X_GetProperty:
    case X_GetSelectionOwner:
    case X_GrabPointer:
    case X_GrabKeyboard:
    case X_ListExtensions:
    case X_ListFonts:
    case X_LookupColor:
    case X_AllocNamedColor:
    case X_QueryPointer:
    case X_GetWindowAttributes:
    case X_QueryTree:
    case X_QueryBestSize:
    case X_QueryColors:
    case X_QueryFont:
    case X_TranslateCoords:
    case X_GetImage:
    case X_GetPointerMapping:
    case X_GetKeyboardControl:
    case X_InternAtom:
    case X_AllocColor:
    {
      sequenceQueue_.push(clientSequence_, opcode);

      priority_++;

      break;
    }
    default:
    {
      break;
    }
  }

  int bits = encodeBuffer.diffBits();

  #if defined(TEST) || defined(OPCODES)

  *logofs << "handleFastReadRequest: Handled raw request OPCODE#" 
          << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ")"
          << " for FD#" << fd_ << " sequence " << clientSequence_
          << ". " << size << " bytes in, " << bits << " bits ("
          << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;

  #endif

  statistics -> addRequestBits(opcode, size << 3, bits);

  if (opcode == opcodeStore_ -> renderExtension)
  {
    statistics -> addRenderRequestBits(*(buffer + 1), size << 3, bits);
  }

  return 1;
}

int ClientChannel::handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
                                            unsigned char *&buffer, unsigned int &size)
{
  if ((opcode >= X_NXFirstOpcode &&
           opcode <= X_NXLastOpcode) ||
               opcode == X_ListExtensions ||
                   opcode == X_QueryExtension)
  {
    return 0;
  }

  #ifdef DEBUG
  *logofs << "handleFastWriteReply: Decoding raw reply OPCODE#"
          << (unsigned int) opcode << " for FD#" << fd_
          << ".\n" << logofs_flush;
  #endif

  buffer = writeBuffer_.addMessage(8);

  #ifndef __sun

  unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(8);

  *((unsigned int *) buffer)       = *next++;
  *((unsigned int *) (buffer + 4)) = *next;

  #else /* #ifndef __sun */

  memcpy(buffer, decodeBuffer.decodeMemory(8), 8);

  #endif /* #ifndef __sun */

  size = 32 + (GetULONG(buffer + 4, bigEndian_) << 2);

  writeBuffer_.registerPointer(&buffer);

  if (writeBuffer_.getAvailable() < size - 8 ||
          (int) size >= control -> TransportFlushBufferSize)
  {
    #ifdef DEBUG
    *logofs << "handleFastWriteReply: Using scratch buffer for OPCODE#"
            << (unsigned int) opcode << " with size " << size << " and "
            << writeBuffer_.getLength() << " bytes in buffer.\n"
            << logofs_flush;
    #endif

    writeBuffer_.removeMessage(8);

    buffer = writeBuffer_.addScratchMessage(((unsigned char *)
                             decodeBuffer.decodeMemory(size - 8)) - 8, size);
  }
  else
  {
    writeBuffer_.addMessage(size - 8);

    #ifndef __sun

    if (size == 32)
    {
      next = (unsigned int *) decodeBuffer.decodeMemory(size - 8);

      for (int i = 8; i < 32; i += sizeof(unsigned int))
      {
        *((unsigned int *) (buffer + i)) = *next++;
      }
    }
    else
    {
      memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);
    }

    #else /* #ifndef __sun */

    memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);

    #endif /* #ifndef __sun */
  }

  writeBuffer_.unregisterPointer();

  //
  // We don't need to write our local sequence
  // number. Replies are always sent with the
  // original X server's sequence number.
  //

  #if defined(TEST) || defined(OPCODES)
  *logofs << "handleFastWriteReply: Handled raw reply OPCODE#"
          << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
          << serverSequence_ << ". Output size is " << size << ".\n"
          << logofs_flush;
  #endif

  #ifdef DEBUG
  *logofs << "handleFastWriteReply: Length of sequence queue is "
          << sequenceQueue_.length() << ".\n" << logofs_flush;
  #endif

  statistics -> addRepliedRequest(opcode);

  handleFlush(flush_if_needed);

  return 1;
}

int ClientChannel::handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode,
                                            unsigned char *&buffer, unsigned int &size)
{
  #ifdef DEBUG
  *logofs << "handleFastWriteEvent: Decoding raw "
          << (opcode == X_Error ? "error" : "event") << " OPCODE#"
          << (unsigned int) opcode << " for FD#" << fd_
          << ".\n" << logofs_flush;
  #endif

  size = 32;

  buffer = writeBuffer_.addMessage(size);

  #ifndef __sun

  unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(size);

  for (int i = 0; i < 32; i += sizeof(unsigned int))
  {
    *((unsigned int *) (buffer + i)) = *next++;
  }

  #else /* #ifndef __sun */

  memcpy(buffer, decodeBuffer.decodeMemory(size), size);

  #endif /* #ifndef __sun */

  //
  // Use our local sequence number.
  //

  PutUINT(lastSequence_, buffer + 2, bigEndian_);

  #if defined(TEST) || defined(OPCODES)
  *logofs << "handleFastWriteEvent: Handled raw "
          << (opcode == X_Error ? "error" : "event") << " OPCODE#"
          << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
          << lastSequence_ << ". Output size is " << size << ".\n"
          << logofs_flush;
  #endif

  //
  // Check if we need to suppress the error.
  //

  if (opcode == X_Error && handleTaintSyncError(*(buffer + 10)) > 0)
  {
    #if defined(TEST) || defined(OPCODES)
    *logofs << "handleFastWriteEvent: WARNING! Suppressed error OPCODE#"
            << (unsigned int) opcode << " for FD#" << fd_
            << " with sequence " << lastSequence_ << ".\n"
            << logofs_flush;
    #endif

    writeBuffer_.removeMessage(32);
  }

  handleFlush(flush_if_needed);

  return 1;
}

int ClientChannel::handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                          const unsigned char *buffer, const unsigned int size)
{
  //
  // Will push sequence and set
  // priority according to stage.
  //

  unsigned int stage = *(buffer + 1);

  #ifdef TEST
  *logofs << "handleShmemRequest: Encoding shmem request "
          << "OPCODE#" << (unsigned int) opcode << " for FD#"
          << fd_ << " with size " << size << " at stage "
          << stage << ".\n" << logofs_flush;
  #endif

  encodeBuffer.encodeValue(stage, 2);

  if (stage == 0)
  {
    unsigned int enableClient = 0;
    unsigned int enableServer = 0;

    if (control -> ShmemClient == 1)
    {
      enableClient = *(buffer + 4);
    }

    if (control -> ShmemServer == 1)
    {
      enableServer = *(buffer + 5);
    }

    encodeBuffer.encodeBoolValue(enableClient);
    encodeBuffer.encodeBoolValue(enableServer);

    unsigned int clientSegment = GetULONG(buffer + 8,  bigEndian_);
    unsigned int serverSegment = GetULONG(buffer + 12, bigEndian_);

    encodeBuffer.encodeValue(clientSegment, 29, 9);
    encodeBuffer.encodeValue(serverSegment, 29, 9);

    #ifdef TEST
    *logofs << "handleShmemRequest: Enable client is "
            << enableClient << " enable server is " << enableServer
            << " client segment is " << (void *) clientSegment
            << " server segment is " << (void *) serverSegment
            << ".\n" << logofs_flush;
    #endif

    #ifdef TEST
    *logofs << "handleShmemRequest: Size of the shared memory "
            << "segment will be " << control -> ShmemServerSize
            << ".\n" << logofs_flush;
    #endif
  }

  if (stage != 1)
  {
    sequenceQueue_.push(clientSequence_, opcodeStore_ ->
                            getShmemParameters);

    priority_++;
  }

  return 1;
}

int ClientChannel::handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
                                        unsigned char *&buffer, unsigned int &size)
{
  #ifdef TEST
  *logofs << "handleShmemReply: Received shmem parameters "
          << "reply OPCODE#" << (unsigned int) opcode
          << ".\n" << logofs_flush;
  #endif

  size   = 32;
  buffer = writeBuffer_.addMessage(size);

  unsigned int stage;

  decodeBuffer.decodeValue(stage, 2);

  *(buffer + 1) = stage;

  if (stage == 2)
  {
    unsigned int clientEnabled;
    unsigned int serverEnabled;

    decodeBuffer.decodeBoolValue(clientEnabled);
    decodeBuffer.decodeBoolValue(serverEnabled);

    //
    // Client support is not implemented
    // and not useful. It is here only
    // for compatibility.
    //

    clientEnabled = 0;

    *(buffer + 8) = clientEnabled;
    *(buffer + 9) = serverEnabled;

    PutULONG(0, buffer + 12, bigEndian_);

    if (serverEnabled == 1)
    {
      #ifdef TEST
      *logofs << "handleShmemReply: Enabled shared memory "
              << "support in X server with segment size "
              << control -> ShmemServerSize << ".\n"
              << logofs_flush;
      #endif

      PutULONG(control -> ShmemServerSize, buffer + 16, bigEndian_);
    }
    else
    {
      PutULONG(0, buffer + 16, bigEndian_);
    }
  }
  else
  {
    *(buffer + 8) = 0;
    *(buffer + 9) = 0;

    PutULONG(0, buffer + 12, bigEndian_);
    PutULONG(0, buffer + 16, bigEndian_);
  }

  return 1;
}

int ClientChannel::handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                         const unsigned char *buffer, const unsigned int size)
{
  #ifdef TEST
  *logofs << "handleFontRequest: Encoding font request "
          << "OPCODE#" << (unsigned int) opcode << " for FD#"
          << fd_ << " with size " << size << ".\n"
          << logofs_flush;
  #endif

  sequenceQueue_.push(clientSequence_, opcodeStore_ ->
                          getFontParameters);

  return 1;
}

int ClientChannel::handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
                                       unsigned char *&buffer, unsigned int &size)
{
  #ifdef TEST
  *logofs << "handleFontReply: Received font operation "
          << "reply OPCODE#" << (unsigned int) opcode
          << ".\n" << logofs_flush;
  #endif

  unsigned int length;

  decodeBuffer.decodeValue(length, 8);

  size   = 32 + RoundUp4(length + 1);
  buffer = writeBuffer_.addMessage(size);

  unsigned char *next = buffer + 32;

  *next++ = length;

  decodeBuffer.decodeTextData(next, length);

  #ifdef TEST

  *logofs << "handleFontReply: Received tunneled font server "
          << "path '";

  for (unsigned int i = 0; i < length; i++)
  {
    *logofs << *(buffer + 32 + 1 + i);
  }

  *logofs << "' for FD#" << fd_ << ".\n" << logofs_flush;

  #endif

  if (fontPort_ == -1)
  {
    //
    // The local side is not going to forward
    // the font server connections.
    //

    #ifdef TEST
    *logofs << "handleFontReply: WARNING! Returning an empty "
            << "font server path.\n" << logofs_flush;
    #endif

    writeBuffer_.removeMessage(size);

    size   = 36;
    buffer = writeBuffer_.addMessage(size);

    //
    // Set the length of the returned
    // path to 0.
    //

    *(buffer + 32) = 0;
  }
  #ifdef TEST
  else
  {
    *logofs << "handleFontReply: Returning the received "
            << "font server path.\n" << logofs_flush;
  }
  #endif

  return 1;
}

int ClientChannel::handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                          const unsigned char *buffer, const unsigned int size)
{
  #ifdef TEST
  *logofs << "handleCacheRequest: Handling cache request "
          << "for FD#" << fd_ << ".\n" << logofs_flush;
  #endif

  enableCache_ = *(buffer + 4);
  enableSplit_ = *(buffer + 5);
  enableSave_  = *(buffer + 6);
  enableLoad_  = *(buffer + 7);

  #ifdef TEST
  *logofs << "handleCacheRequest: Set cache parameters to "
          << " cache " << enableCache_ << " split " << enableSplit_
          << " save " << enableSave_ << " load " << enableLoad_
          << ".\n" << logofs_flush;
  #endif

  //
  // Encode all the parameters as a
  // single unsigned int so we can
  // use an int cache.
  //

  unsigned int mask = enableSave_ << 8 | enableLoad_;

  encodeBuffer.encodeCachedValue(mask, 32, clientCache_ ->
                     setCacheParametersCache);
  return 0;
}

int ClientChannel::handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                               const unsigned char *buffer, const unsigned int size)
{
  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleStartSplitRequest: SPLIT! Handling start split "
          << "request for FD#"<< fd_ << ".\n" << logofs_flush;
  #endif

  if (splitState_.resource != nothing)
  {
    #ifdef PANIC
    *logofs << "handleStartSplitRequest: PANIC! SPLIT! Split requested "
            << "for resource id " << (unsigned int) *(buffer + 1)
            << " while handling resource " << splitState_.resource
            << ".\n" << logofs_flush;
    #endif

    cerr << "Error" << ": Split requested for "
         << "resource id " << (unsigned int) *(buffer + 1)
         << " while handling resource " << splitState_.resource
         << ".\n";

    return -1;
  }
  else if (fd_ != firstClient_)
  {
    //
    // It can be that an auxiliary channel is the
    // first to connect, then comes the agent that
    // is actually using the NX opcodes.
    //

    #ifdef WARNING
    *logofs << "handleStartSplitRequest: WARNING SPLIT! Split requested "
            << "on FD#" << fd_ << " while expecting FD#" << firstClient_
            << ".\n" << logofs_flush;
    #endif

    firstClient_ = fd_;
  }

  //
  // Set the agent's resource for which we are
  // going to split the request.
  //

  splitState_.resource = *(buffer + 1);

  #if defined(TEST) || defined(SPLIT)

  *logofs << "handleStartSplitRequest: SPLIT! Registered id "
          << splitState_.resource << " as resource "
          << "waiting for a split.\n" << logofs_flush;

  if (clientStore_ -> getSplitStore(splitState_.resource) != NULL)
  {
    *logofs << "handleStartSplitRequest: WARNING! SPLIT! A split "
            << "store for resource id " << splitState_.resource
            << " already exists.\n" << logofs_flush;

    clientStore_ -> dumpSplitStore(splitState_.resource);
  }

  #endif

  //
  // Send the selected resource to the remote.
  //

  // Since ProtoStep7 (#issue 108)
  encodeBuffer.encodeCachedValue(splitState_.resource, 8,
                     clientCache_ -> resourceCache);

  splitState_.mode = (T_split_mode) *(buffer + 4);

  if (splitState_.mode != NXSplitModeAsync &&
          splitState_.mode != NXSplitModeSync)
  {
    splitState_.mode = (T_split_mode) control -> SplitMode;

    #if defined(TEST) || defined(SPLIT)
    *logofs << "handleStartSplitRequest: SPLIT! Set split "
            << "mode to '" << splitState_.mode << "' with "
            << "provided value '" << (unsigned) *(buffer + 4)
            << "'.\n" << logofs_flush;
    #endif
  }

  #if defined(TEST) || defined(SPLIT)

  if (splitState_.mode == NXSplitModeAsync)
  {
    *logofs << "handleStartSplitRequest: SPLIT! Selected split "
            << "mode is [split_async].\n" << logofs_flush;
  }
  else if (splitState_.mode == NXSplitModeSync)
  {
    *logofs << "handleStartSplitRequest: SPLIT! Selected split "
            << "mode is [split_sync].\n" << logofs_flush;
  }

  clientStore_ -> dumpSplitStores();

  #endif

  return 1;
}

int ClientChannel::handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
                                             const unsigned char *buffer, const unsigned int size)
{
  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleEndSplitRequest: SPLIT! Handling end split "
          << "request for FD#"<< fd_ << ".\n" << logofs_flush;
  #endif

  //
  // Verify that the agent resource matches.
  //

  if (splitState_.resource == nothing)
  {
    #ifdef PANIC
    *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of "
            << "split for resource id " << (unsigned int) *(buffer + 1)
            << " without a previous start.\n"
            << logofs_flush;
    #endif

    cerr << "Error" << ": Received an end of split "
         << "for resource id " << (unsigned int) *(buffer + 1)
         << " without a previous start.\n";

    return -1;
  }
  else if (splitState_.resource != *(buffer + 1))
  {
    #ifdef PANIC
    *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id "
            << (unsigned int) *(buffer + 1) << " received while "
            << "waiting for resource id " << splitState_.resource
            << ".\n" << logofs_flush;
    #endif

    cerr << "Error" << ": Invalid resource id "
         << (unsigned int) *(buffer + 1) << " received while "
         << "waiting for resource id " << splitState_.resource
         << ".\n";

    return -1;
  }

  //
  // Send the selected resource to the remote.
  //

  // Since ProtoStep7 (#issue 108)
  encodeBuffer.encodeCachedValue(splitState_.resource, 8,
                     clientCache_ -> resourceCache);

  //
  // Send the split notification events
  // to the agent.
  //

  handleRestart(sequence_immediate, splitState_.resource);

  //
  // Check if we still have splits to send.
  //

  handleSplitPending();

  #if defined(TEST) || defined(SPLIT)
  *logofs << "handleEndSplitRequest: SPLIT! Reset id "
          << splitState_.resource << " as resource "
          << "selected for splits.\n" << logofs_flush;
  #endif

  splitState_.resource = nothing;
  splitState_.mode     = split_none;

  #if defined(TEST) || defined(SPLIT)

  clientStore_ -> dumpSplitStores();

  #endif

  return 1;
}

void ClientChannel::handleDecodeCharInfo(DecodeBuffer &decodeBuffer, unsigned char *nextDest)
{
  unsigned int value;

  decodeBuffer.decodeCachedValue(value, 32,
                     *serverCache_ -> queryFontCharInfoCache[0], 6);

  PutUINT(value & 0xffff, nextDest, bigEndian_);
  PutUINT(value >> 16, nextDest + 10, bigEndian_);

  nextDest += 2;

  for (unsigned int i = 1; i < 5; i++)
  {
    unsigned int _value;

    decodeBuffer.decodeCachedValue(_value, 16,
                       *serverCache_ -> queryFontCharInfoCache[i], 6);

    PutUINT(_value, nextDest, bigEndian_);

    nextDest += 2;
  }
}

int ClientChannel::setBigEndian(int flag)
{
  bigEndian_ = flag;

  return 1;
}

int ClientChannel::setReferences()
{
  #ifdef TEST
  *logofs << "ClientChannel: Initializing the static "
          << "members for the client channels.\n"
          << logofs_flush;
  #endif

  #ifdef REFERENCES

  references_ = 0;

  #endif

  return 1;
}
