diff options
Diffstat (limited to 'nxcomp/src/ClientChannel.cpp')
-rw-r--r-- | nxcomp/src/ClientChannel.cpp | 7881 |
1 files changed, 7881 insertions, 0 deletions
diff --git a/nxcomp/src/ClientChannel.cpp b/nxcomp/src/ClientChannel.cpp new file mode 100644 index 000000000..2a182e49f --- /dev/null +++ b/nxcomp/src/ClientChannel.cpp @@ -0,0 +1,7881 @@ +/**************************************************************************/ +/* */ +/* 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 explaination. + // + + 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 issueing + // 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; +} |