/*
 *  Copyright (C) 1997, 1998 Olivetti & Oracle Research Laboratory
 *
 *  Copyright (C) 1998 International Computer Science Institute (ICSI)
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */

/*
 * rfbproto.c - functions to deal with client side of RFB protocol.
 */

#include <pspkernel.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include "vnc.h"
#include "vncauth.h"
#include "../dialog.h"
#include "../vncdbg.h"
#include "../libnet/pspnet.h"

//#define DEBUG_VNC_CODING
#define printf LogPrintf

static Bool HandleHextileEncoding16(int x, int y, int w, int h);
extern Word AppNetRefNum;

extern CARD8 IgnoreServerVersion;

int rfbsock = 0;
char *desktopName;
int requestedDepth = 0;
Bool debug = False;

rfbPixelFormat myFormat;
rfbServerInitMsg si;
Bool sendUpdateRequest;
Bool updateViewportSize;

PRFBViewport pspFB;

int endianTest = 1;


/* note that the CoRRE encoding uses this buffer and assumes it is big enough
   to hold 255 * 255 * 32 bits -> 260100 bytes.  640*480 = 307200 bytes */
/* also hextile assumes it is big enough to hold 16 * 16 * 32 bits */

#define BUFFER_SIZE (16*16)
static CARD16 buffer[BUFFER_SIZE];

/******************************************************************
 * ConnectToRFBServer
 *
 * connects on the networkl level
 *****************************************************************/
Bool ConnectToRFBServer (CARD32 host, int port)
{
//    unsigned long host = 0;
	
//	if (!StringToIPAddr (hostname, &host)) {
//	MsgPrintf(0, "\n\tCannot resolve %s", hostname);
//	return False;
//    }

    rfbsock = ConnectToTcpAddr (host, port);

    if (rfbsock < 0) {
//	MsgPrintf (0, "\n\tUnable to connect to\n\n   %i.%i.%i.%i:%i\n", (host>>24), (host>>16)&0xff, (host>>8)&0xff, host&0xff, port);
	LogPrintf("Unable to connect to %i.%i.%i.%i:%i\n", (host>>24), (host>>16)&0xff, (host>>8)&0xff, host&0xff, port);
	return False;
    }

    LogPrintf ("Connected to %i.%i.%i.%i:%i\n", (host>>24), (host>>16)&0xff, (host>>8)&0xff, host&0xff, port);
    return True;
}


/******************************************************************
 * InitialiseRFBConnection.
 *
 * connects on the VNC protocol level, i.e. handshake, authentication, etc
 *****************************************************************/

Bool InitialiseRFBConnection (int sock)
{
    rfbProtocolVersionMsg pv;
    int majorV,minorV;
    Bool authWillWork = True;
    CARD32 authScheme, reasonLen;
    char *reason;
    CARD32 authResult;
    CARD8 challenge[CHALLENGESIZE];
    rfbClientInitMsg ci;
    char testVer[8];
    char mypass[32];

    if (!ReadExact(sock, (CARD8*)pv, sz_rfbProtocolVersionMsg))
      return False;

    UpdateProgress ("\tHandshaking...", 0, 0);

    errorMessageFromReadExact = True;

    pv[sz_rfbProtocolVersionMsg] = 0;

    majorV = rfbProtocolMajorVersion;
    minorV = rfbProtocolMinorVersion;

    sprintf (testVer, "00%d.00%d\n", majorV, minorV);

    if (strstr (pv, "RFB") == 0 && !IgnoreServerVersion) {
	MsgPrintf(0, "\n\tNot a valid VNC server\n\n%s", pv);
	return False;
    }

    LogPrintf("VNC server supports protocol %s\n", pv);
    LogPrintf("Viewer supports %i.%i\n", majorV, minorV);

    if (strstr (pv, testVer) == 0 && !IgnoreServerVersion) {
       // if server is before 3.3 authentication won't work
      LogPrintf("VNC server does not support authentication");
      authWillWork = False;
    }

    sprintf (pv, rfbProtocolVersionFormat, majorV, minorV);

    if (!WriteExact(sock, (CARD8*)pv, sz_rfbProtocolVersionMsg)) return False;

    if (!ReadExact(sock, (CARD8*)&authScheme, 4)) return False;

    authScheme = Swap32IfLE(authScheme);

    switch (authScheme) {

    case rfbConnFailed:
	if (!ReadExact(sock, (CARD8*)&reasonLen, 4)) return False;
	reasonLen = Swap32IfLE(reasonLen);

	if ((reason = malloc (reasonLen)) == 0) {
	  LogPrintf("ERROR: Memory Exhausted\n");
	  MsgPrintf(0, "\n\tERROR:\n\n   Memory Exhausted\n");
	  return false;
 	}

	if (!ReadExact(sock, (CARD8*)reason, (int)reasonLen)) return False;

	LogPrintf("\nVNC connection failed: %i", reason);

	free (reason);

	return False;

    case rfbNoAuth:
	LogPrintf ("No authentication needed\n");
	break;

    case rfbVncAuth:
	if (!authWillWork) {
	    MsgPrintf(0, "\n\tVNC versions incompatible.\n");
	    return False;
	}

	if (!ReadExact(sock, (CARD8*)challenge, CHALLENGESIZE)) return False;

	if (strlen(server.password) == 0)
	{
		memset(mypass, 0, 32);
		VKB_InputText("\tPassword?", mypass, 32);
	} else {
		strcpy(mypass, server.password);
	}
	vncEncryptBytes(challenge, mypass);

	if (!WriteExact(sock, (CARD8*)challenge, CHALLENGESIZE)) return False;

	if (!ReadExact(sock, (CARD8*)&authResult, 4)) return False;

	authResult = Swap32IfLE(authResult);

	switch (authResult) {
	case rfbVncAuthOK:
	    LogPrintf("Authenticated\n");
	    break;
	case rfbVncAuthFailed:
	    MsgPrintf(0, "\n\tAuthentication failed\n\n");
	    return False;
	case rfbVncAuthTooMany:
	    MsgPrintf(0, "\n\tAuthentication failed\n\n   too many tries\n");
	    return False;
	default:
	    MsgPrintf(0, "\n\tAuthentication failed\n\n   code %i", (int)authResult);
	    return False;
	}
	break;
    default:
	MsgPrintf (0, "\n\tUnknown authentication:\n\n  %i ", (int)authScheme);
	return False;
    }

    ci.shared = (server.sharedDesktop ? 1 : 0);

    if (!WriteExact(sock, (CARD8*)&ci, sz_rfbClientInitMsg)) return False;

    if (!ReadExact(sock, (CARD8*)&si, sz_rfbServerInitMsg)) return False;

    si.framebufferWidth = Swap16IfLE(si.framebufferWidth);
    si.framebufferHeight = Swap16IfLE(si.framebufferHeight);
    si.format.redMax = Swap16IfLE(si.format.redMax);
    si.format.greenMax = Swap16IfLE(si.format.greenMax);
    si.format.blueMax = Swap16IfLE(si.format.blueMax);
    si.nameLength = Swap32IfLE(si.nameLength);

    pspFB.remote.w = si.framebufferWidth;
    pspFB.remote.h = si.framebufferHeight;
    pspFB.desktop.w = si.framebufferWidth;
    pspFB.desktop.h = si.framebufferHeight;

    if ((desktopName = malloc (si.nameLength + 1)) == 0) {
      MsgPrintf(0, "\n\tERROR:\n\n   mem ptr\n");
      LogPrintf ("ERROR: mem ptr\n");
      return false;
    }

    if (!ReadExact(sock, (CARD8*)desktopName, (int)si.nameLength)) return False;

    desktopName[si.nameLength] = 0;

    LogPrintf ("Desktop size: %i x %i\n", si.framebufferWidth, si.framebufferHeight);
    LogPrintf ("Desktop name '%s'\n", desktopName);

    free (desktopName);

    LogPrintf ("Connected using protocol %i.%i\n", rfbProtocolMajorVersion, rfbProtocolMinorVersion);

    return True;
}


/******************************************************************
 * SetFormatAndEncodings
 *****************************************************************/
Bool SetFormatAndEncodings()
{
    rfbSetPixelFormatMsg spf;
    CARD8 buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
    rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf;
    CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]);
    int len = 0;

    spf.type = rfbSetPixelFormat;
    spf.format = myFormat;
    spf.format.redMax = Swap16IfLE(spf.format.redMax);
    spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
    spf.format.blueMax = Swap16IfLE(spf.format.blueMax);

    if (!WriteExact(rfbsock, (CARD8*)&spf, sz_rfbSetPixelFormatMsg))
	return False;

    se->type = rfbSetEncodings;
    se->nEncodings = 0;

    // VM PalmVNC currently supports only Hextile encoding */
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingUltra);
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingUltraZip);
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE);
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE);
    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw);

    len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;

    se->nEncodings = Swap16IfLE(se->nEncodings);

    if (!WriteExact(rfbsock, buf, len)) return False;

    return True;
}

/******************************************************************
 * SetScaleFactor
 *****************************************************************/
Bool SetScaleFactor(unsigned short scaleFactor)
{
    rfbSetScaleFactorMsg ssf;
//    int len = 0;

    ssf.type = rfbSetScaleFactor;
    ssf.scale = Swap16IfLE(scaleFactor);

    if (!WriteExact(rfbsock, (CARD8*)&ssf, sz_rfbSetScaleFactorMsg))
		return False;

    return True;
}


/******************************************************************
 * SendIncrementalFramebufferUpdateRequest
 *****************************************************************/
Bool SendIncrementalFramebufferUpdateRequest()
{

  return SendFramebufferUpdateRequest((int)pspFB.virtual.x,
		(int)pspFB.virtual.y,
		(int)pspFB.virtual.w,
		(int)pspFB.virtual.h,
		true);
}


/******************************************************************
 * SendFramebufferUpdateRequest.
 *****************************************************************/
Bool SendFramebufferUpdateRequest(int x, int y, int w, int h, int incremental)
{
    rfbFramebufferUpdateRequestMsg fur;

	if (x < 0) { w = w + x; x = 0; }
	if (y < 0) { h = h + y; y = 0; }
	if (x > pspFB.desktop.w) return True;
	if (y > pspFB.desktop.h) return True;
	if ((x + w) > pspFB.desktop.w) w = pspFB.desktop.w - x;
	if ((y + h) > pspFB.desktop.h) h = pspFB.desktop.h - y;

    fur.type = rfbFramebufferUpdateRequest;
    fur.incremental = incremental ? 1 : 0;
    fur.x = Swap16IfLE(x);
    fur.y = Swap16IfLE(y);
    fur.w = Swap16IfLE(w);
    fur.h = Swap16IfLE(h);

#ifdef DEBUG_VNC_CODING
    	printf ("updt=%d,%d %d,%d,%d\n", x, y, w, h, incremental);
#endif
    if (!WriteExact(rfbsock, (CARD8*)&fur,
			sz_rfbFramebufferUpdateRequestMsg))
	return False;

    // we do this in the event loop, it was: sendUpdateRequest = false;

    return True;
}


/******************************************************************
 * SendPointerEvent.
 *****************************************************************/
Bool SendPointerEvent(int x, int y, int buttonMask)
{
    rfbPointerEventMsg pe;

    pe.type = rfbPointerEvent;
    pe.buttonMask = buttonMask;
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    pe.x = Swap16IfLE(x);
    pe.y = Swap16IfLE(y);
    return WriteExact(rfbsock, (CARD8*)&pe, sz_rfbPointerEventMsg);
}


/******************************************************************
 * SendKeyEvent.
 *****************************************************************/
Bool SendKeyEvent(CARD32 key, int down)
{
    rfbKeyEventMsg ke;

    ke.type = rfbKeyEvent;
    ke.down = down ? 1 : 0;
    ke.key = Swap32IfLE(key);
    return WriteExact(rfbsock, (CARD8*)&ke, sz_rfbKeyEventMsg);
}


/******************************************************************
 * SendClientCutText.
 *****************************************************************/
Bool SendClientCutText(char *str, int len)
{
    rfbClientCutTextMsg cct;

    cct.type = rfbClientCutText;
    cct.length = Swap32IfLE (len);

    return  (WriteExact(rfbsock, (CARD8*)&cct,
			sz_rfbClientCutTextMsg) &&
	     WriteExact(rfbsock, (CARD8*)str, len));
}


/******************************************************************
 * HandleRFBServerMessage.
 *****************************************************************/
Bool HandleRFBServerMessage()
{
    rfbServerToClientMsg msg;
	unsigned short old_w, old_h, old_x, old_y;
	signed short new_x, new_y;

    if (!ReadExact(rfbsock, (CARD8*)&msg, 1)) {
        LogPrintf("HandleRFBServerMessage(): ERROR ReadExact(msg) failed\n");
	return False;
    }

    switch (msg.type) {

    case rfbSetColourMapEntries:
    {
	if (!ReadExact(rfbsock, ((CARD8*)&msg) + 1,
			 sz_rfbSetColourMapEntriesMsg - 1)) {
            LogPrintf("HandleRFBServerMessage(): ERROR ReadExact(colormap) failed\n");
	    return False;
        }

	break;
    }

    case rfbFramebufferUpdate:
    {
	rfbFramebufferUpdateRectHeader rect;
	int i;

	if (!ReadExact(rfbsock, ((CARD8*)&msg.fu) + 1,
			 sz_rfbFramebufferUpdateMsg - 1)) {
            LogPrintf("HandleRFBServerMessage(): ERROR ReadExact(rectheader) failed\n");
	    return False;
        }

	msg.fu.nRects = Swap16IfLE(msg.fu.nRects);

	for (i = 0; i < msg.fu.nRects; i++) {

	    if (!ReadExact(rfbsock, (CARD8*)&rect,
			     sz_rfbFramebufferUpdateRectHeader)) {
            LogPrintf("HandleRFBServerMessage(): ERROR ReadExact(rect) failed\n");
		return False;
            }

	    rect.r.x = Swap16IfLE(rect.r.x);
	    rect.r.y = Swap16IfLE(rect.r.y);
	    rect.r.w = Swap16IfLE(rect.r.w);
	    rect.r.h = Swap16IfLE(rect.r.h);

	    rect.encoding = Swap32IfLE(rect.encoding);

#ifdef DEBUG_VNC_CODING
    	printf ("Rxd rect=%d,%d %d,%d\n", rect.r.x, rect.r.y, rect.r.w, rect.r.h);
#endif
    	
	    if ((rect.r.x + rect.r.w > si.framebufferWidth) ||
		(rect.r.y + rect.r.h > si.framebufferHeight))
	    {
#ifdef DEBUG_VNC_CODING
		printf ("rect too large: %dx%d at (%d, %d)\n",
		       rect.r.w, rect.r.h, rect.r.x, rect.r.y);
#endif
		return False;
	    }

	    if ((rect.r.h * rect.r.w) == 0) {
#ifdef DEBUG_VNC_CODING
		printf ("zero size rect - ignoring\n");
#endif
		continue;
	    }

	    switch (rect.encoding) {
            case rfbEncodingUltra:
                ReadUltraRect(&rect);
                break;

            case rfbEncodingUltraZip:
                ReadUltraZip(&rect);
                break;

	    case rfbEncodingRaw:
	        ReadRawRect(&rect);
		break;

            case rfbEncodingSolidColor:
                ReadSolidRect(&rect);
                break;

	    case rfbEncodingRRE:
	        ReadRRERect(&rect);
	        break;

	    case rfbEncodingCoRRE:
	        ReadCoRRERect(&rect);
	        break;

#ifdef TIGHTVNC
	    case rfbEncodingLastRect:
	        msg.fu.nRects = 0;
	        break;

            case rfbEncodingTight:
                ReadTightRect(&rect);
                break;
#endif
	    case rfbEncodingHextile:
  	        if (!HandleHextileEncoding16((int)rect.r.x, (int)rect.r.y,
						(int)rect.r.w, (int)rect.r.h))
                    return False;
                break;

	    default:
#ifdef DEBUG_VNC_CODING
			printf ("unknown encoding %d\n", (int)rect.encoding);
#endif
			return False;
	    }
	}

	sendUpdateRequest = True;

	break;
    }

    case rfbBell:
//	SndPlaySystemSound ((SndSysBeepType)sndInfo);
	break;

    case rfbServerCutText:
    {
		char *str;
//		Word length = 0;
//		Word buttonID = 0;

		if (!ReadExact(rfbsock, ((CARD8*)&msg) + 1, sz_rfbServerCutTextMsg - 1))
		    return False;

		msg.sct.length = Swap32IfLE(msg.sct.length);

		str = malloc (msg.sct.length);
		memset (str, msg.sct.length, 0);

		if (!ReadExact(rfbsock, (CARD8*)str, msg.sct.length))
	    	return False;

#if 0
#ifdef DEBUG_VNC
        printf ("%d bytes in CutText received\n", (Word)msg.sct.length);
        printf ("\t\"%s\"\n", str);
#endif
		if (msg.sct.length > cbdMaxTextLength)
	  		length = cbdMaxTextLength;
		else
	  		length = msg.sct.length;

		if ((buttonID = FrmCustomAlert (ID_ALERT_CLIPBOARD, str, "", "")) == 0)
		{ // the firt button, "OK"
	  		ClipboardAddItem (clipboardText, str, length);
#ifdef DEBUG_VNC
        	printf ("CutText copied into clipboard\n");
#endif
		}
#endif
		free (str);
		break;
    }

	case rfbReSizeFrameBuffer:
	{
		if (!ReadExact(rfbsock, ((CARD8*)&msg) + 1, sz_rfbReSizeFrameBufferMsg - 1))
			return False;

		old_w = pspFB.remote.w;
		old_h = pspFB.remote.h;
		old_x = pspFB.viewable.x;
		old_y = pspFB.viewable.y;
		pspFB.desktop.w = Swap16IfLE(msg.rsfb.desktop_w);
		pspFB.desktop.h = Swap16IfLE(msg.rsfb.desktop_h);
		pspFB.remote.w = Swap16IfLE(msg.rsfb.buffer_w);
		pspFB.remote.h = Swap16IfLE(msg.rsfb.buffer_h);

#ifdef DEBUG_VNC_CODING
    	printf ("Got resize=%d,%d %d,%d\n", pspFB.desktop.w, pspFB.desktop.h, pspFB.remote.w, pspFB.remote.h);
#endif

//		if (pspFB.zoomarea_ena)
//		{
			// Centre on specified coordinates - assumes full screen before
			new_x = ((pspFB.zoomarea.x * (long)pspFB.remote.w) / (long)old_w) - (pspFB.viewable.w / 2);
			new_y = ((pspFB.zoomarea.y * (long)pspFB.remote.h) / (long)old_h) - (pspFB.viewable.h / 2);
//		}
//		else
//		{
			// Try to keep the top-left corner in the same place
//			new_x = ((long)pspFB.viewable.x * (long)pspFB.remote.w) / (long)old_w;
//			new_y = ((long)pspFB.viewable.y * (long)pspFB.remote.h) / (long)old_h;
//		}
		pspFB.zoomarea_ena = false;

		if ((new_x + pspFB.virtual.w) >= pspFB.remote.w)
		{
			if (((signed)pspFB.remote.w - (signed)pspFB.virtual.w) > 0)
				new_x = (pspFB.remote.w - pspFB.virtual.w);
			else
				new_x = 0;
		}		

		if ((new_y + pspFB.virtual.h) > pspFB.remote.h)
		{
			if (((signed)pspFB.remote.h - (signed)pspFB.virtual.h) > 0)
				new_y = (pspFB.remote.h - pspFB.virtual.h);
			else
				new_y = 0;
		}

		if (new_x < 0) new_x = 0;
		if (new_y < 0) new_y = 0;

		pspFB.viewable.x = pspFB.virtual.x = new_x;
		pspFB.viewable.y = pspFB.virtual.y = new_y;
			
		updateViewportSize = True;
		break;
	}

    default:
	LogPrintf("unknown message %i\n", msg.type);
	return false;
    }

    return true;
}


#define GET_PIXEL16(pix,ptr) ((pix)=*(ptr)++)

static Bool HandleHextileEncoding16(int rx, int ry, int rw, int rh)
{
    CARD16 bg, fg;
    int i;
    CARD16 *ptr;
    short x, y;
    unsigned short w, h;
    short sx, sy;
    unsigned short sw, sh;
    CARD8 subencoding;
    CARD8 nSubrects;

#ifdef DEBUG_VNC_CODING
    printf ("hextile %d, %d (%d by %d)\n", rx, ry, rw, rh);
#endif

    for (y = ry; y < ry+rh; y += 16)
	{
		if ((rh * rw) > 2000)
			 UpdateProgress ("\tReceiving Data...", y-ry, rh);
        for (x = rx; x < rx+rw; x += 16)
		{
			if ((rh < 32) && (rw > 64))
				UpdateProgress ("\tReceiving Data...", x-rx, rw);
            w = h = 16;
            if (rx+rw - x < 16)
                w = rx+rw - x;
            if (ry+rh - y < 16)
                h = ry+rh - y;

            if (!ReadExact(rfbsock, (CARD8*)&subencoding, 1))
                return False;

            if (subencoding & rfbHextileRaw)
			{
                if (!ReadExact(rfbsock, (CARD8*)buffer, (int)(w * h * 2)))
                    return False;

                CopyDataToScreen ((CARD16 *)buffer, x, y, w, h);
                continue;
            }

            if (subencoding & rfbHextileBackgroundSpecified)
                if (!ReadExact(rfbsock, (CARD8*)&bg, 2))
                    return False;

#ifdef DEBUG_VNC_CODING
	    	printf (" spec bg %d r %dx%d (%d,%d)\n", bg, w, h, x, y);
#endif
		    DrawVirtualFilledRectangle (bg, x, y, w, h, true);

            if (subencoding & rfbHextileForegroundSpecified)
                if (!ReadExact(rfbsock, (CARD8*)&fg, 2))
                    return False;

            if (!(subencoding & rfbHextileAnySubrects))
			{
                continue;
            }

            if (!ReadExact(rfbsock, (CARD8*)&nSubrects, 1))
                return False;

            ptr = (CARD16 *)buffer;

            if (subencoding & rfbHextileSubrectsColoured)
			{
                if (!ReadExact(rfbsock, (CARD8*)buffer, nSubrects * 4))
                    return False;

#ifdef DEBUG_VNC_CODING
	        	printf (" %d subr. colored\n", nSubrects);
#endif

                for (i = 0; i < nSubrects; i++)
				{
                    GET_PIXEL16(fg, ptr);
                    sx = rfbHextileExtractX(*ptr);
                    sy = rfbHextileExtractY(*ptr);
                    sw = rfbHextileExtractW(*ptr);
                    sh = rfbHextileExtractH(*ptr);
                    ptr++;
#ifdef DEBUG_VNC_CODING
	            	printf (" color %d\n", fg);
#endif
	            	DrawVirtualFilledRectangle (fg, x+sx, y+sy, sw, sh, true);
                }

            }
			else
			{
                if (!ReadExact(rfbsock, (CARD8*)buffer, nSubrects * 2))
                    return False;

                for (i = 0; i < nSubrects; i++)
				{
                    sx = rfbHextileExtractX(*ptr);
                    sy = rfbHextileExtractY(*ptr);
                    sw = rfbHextileExtractW(*ptr);
                    sh = rfbHextileExtractH(*ptr);
                    ptr++;
#ifdef DEBUG_VNC_CODING
	            	printf (" color %d\n", fg);
#endif
		    		DrawVirtualFilledRectangle (fg, x+sx, y+sy, sw, sh, true);
                }
            }
        }
    }

    return True;
}
