/////////////////////////////////////////////////////////////////////////
// $Id: rfb.cc,v 1.16 2002/03/16 11:30:06 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2000  Psyon.Org!
//
//    Donald Becker
//    http://www.psyon.org
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

#include "bochs.h"
#include "icon_bochs.h"
#include "font/vga.bitmap.h"
#define LOG_THIS bx_gui.

#ifdef WIN32

#include <winsock.h>
#include <process.h>
#include "rfb.h"

#else

#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/errno.h>
#include <pthread.h>
typedef unsigned long CARD32;
typedef unsigned short CARD16;
typedef short INT16;
typedef unsigned char  CARD8;
typedef int SOCKET;

#endif

#include "rfbproto.h"

static bool keep_alive;

#define BX_RFB_PORT_MIN 5900
#define BX_RFB_PORT_MAX 5949
static unsigned short rfbPort;

// Headerbar stuff
unsigned rfbBitmapCount = 0;
struct {
	char     *bmap;
	unsigned xdim;
	unsigned ydim;
} rfbBitmaps[BX_MAX_PIXMAPS];

unsigned rfbHeaderbarBitmapCount = 0;
struct {
	unsigned int index;
	unsigned int xorigin;
	unsigned int yorigin;
	unsigned int alignment;
	void (*f)(void);
} rfbHeaderbarBitmaps[BX_MAX_HEADERBAR_ENTRIES];

//Keyboard stuff
#define KEYBOARD true
#define MOUSE    false
#define MAX_KEY_EVENTS 512
struct {
	bool type;
	int  key;
	int  down;
	int  x;
	int  y;
} rfbKeyboardEvent[MAX_KEY_EVENTS];
static unsigned long rfbKeyboardEvents = 0;
static bool          bKeyboardInUse = false;

// Misc Stuff
struct {
	unsigned int x;
	unsigned int y;
	unsigned int width;
	unsigned int height;
	bool updated;
} rfbUpdateRegion;

static char  *rfbScreen;
static char  rfbPallet[256];

static long  rfbDimensionX, rfbDimensionY;
static long  rfbStretchedX, rfbStretchedY;
static long  rfbHeaderbarY;
static long  rfbTileX = 0;
static long  rfbTileY = 0;
static unsigned long  rfbCursorX = 0;
static unsigned long  rfbCursorY = 0;
static unsigned long  rfbOriginLeft  = 0;
static unsigned long  rfbOriginRight = 0;

static unsigned long ServerThread   = 0;
static unsigned long ServerThreadID = 0;

static SOCKET sGlobal;

void ServerThreadInit(void *indata);
void HandleRfbClient(SOCKET sClient);
int  ReadExact(int sock, char *buf, int len);
int  WriteExact(int sock, char *buf, int len);
void DrawBitmap(int x, int y, int width, int height, char *bmap, char color, bool update_client);
void UpdateScreen(char *newBits, int x, int y, int width, int height, bool update_client);
void SendUpdate(int x, int y, int width, int height);
void StartThread();
void rfbKeyPressed(Bit32u key, int press_release);
void rfbMouseMove(int x, int y, int bmask);
void DrawColorPallet();

static const rfbPixelFormat BGR233Format = {
    8, 8, 1, 1, 7, 7, 3, 0, 3, 6
};

// Set this for the endian of your machine. 0 = big, 1 = little 
static const int rfbEndianTest = 1;

#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
#define Swap32(l) (((l) >> 24) | (((l) & 0x00ff0000) >> 8)  | (((l) & 0x0000ff00) << 8)  | ((l) << 24))
#define Swap16IfLE(s) (*(const char *)&rfbEndianTest ? Swap16(s) : (s))
#define Swap32IfLE(l) (*(const char *)&rfbEndianTest ? Swap32(l) : (l))
#define PF_EQ(x,y) ((x.bitsPerPixel == y.bitsPerPixel) && (x.depth == y.depth) && (x.trueColour == y.trueColour) &&	((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && (!x.trueColour || ((x.redMax == y.redMax) &&	(x.greenMax == y.greenMax) && (x.blueMax == y.blueMax) && (x.redShift == y.redShift) && (x.greenShift == y.greenShift) && (x.blueShift == y.blueShift))))

// This file defines stubs for the GUI interface, which is a
// place to start if you want to port bochs to a platform, for
// which there is no support for your native GUI, or if you want to compile
// bochs without any native GUI support (no output window or
// keyboard input will be possible).
// Look in 'x.cc', 'beos.cc', and 'win32.cc' for specific
// implementations of this interface.  -Kevin


// ::SPECIFIC_INIT()
//
// Called from gui.cc, once upon program startup, to allow for the
// specific GUI code (X11, BeOS, ...) to be initialized.
//
// th: a 'this' pointer to the gui class.  If a function external to the
//     class needs access, store this pointer and use later.
// argc, argv: not used right now, but the intention is to pass native GUI
//     specific options from the command line.  (X11 options, BeOS options,...)
//
// tilewidth, tileheight: for optimization, graphics_tile_update() passes
//     only updated regions of the screen to the gui code to be redrawn.
//     These define the dimensions of a region (tile).
// headerbar_y:  A headerbar (toolbar) is display on the top of the
//     VGA window, showing floppy status, and other information.  It
//     always assumes the width of the current VGA mode width, but
//     it's height is defined by this parameter.

void bx_gui_c::specific_init(bx_gui_c *th, int argc, char **argv, unsigned tilewidth, unsigned tileheight, unsigned headerbar_y)
{
	rfbHeaderbarY = headerbar_y;
	rfbDimensionX = 640;
	rfbDimensionY = 480 + rfbHeaderbarY;
	rfbStretchedX = rfbDimensionX;
	rfbStretchedY = rfbDimensionY;
	rfbTileX      = tilewidth;
	rfbTileY      = tileheight;

	rfbScreen = (char *)malloc(rfbDimensionX * rfbDimensionY); 
	memset(&rfbPallet, 0, sizeof(rfbPallet));
	rfbPallet[63] = (char)0xFF;

	rfbUpdateRegion.x = rfbDimensionX;
	rfbUpdateRegion.y = rfbDimensionY;
	rfbUpdateRegion.width  = 0;
	rfbUpdateRegion.height = 0;
	rfbUpdateRegion.updated = false;
	
	keep_alive = true;
	StartThread();
	
#ifdef WIN32
	Sleep(1000);
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
#endif
	if (bx_options.Oprivate_colormap->get ()) {
		BX_ERROR(( "private_colormap option ignored." ));
	}
}

bool InitWinsock()
{
#ifdef WIN32
	WSADATA wsaData;
	if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return false;
#endif
	return true;
}

bool StopWinsock()
{
#ifdef WIN32
	WSACleanup();
#endif
	return true;
}

void ServerThreadInit(void *indata) 
{
	SOCKET             sServer;
	SOCKET             sClient;
	struct sockaddr_in sai;
	unsigned int       sai_size;
	int port_ok = 0;

#ifdef WIN32
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
#endif
	if(!InitWinsock()) {
		BX_PANIC(( "could not initialize winsock."));
		goto end_of_thread;
	}

	sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sServer == -1) { 
		BX_PANIC(( "could not create socket." ));
		goto end_of_thread;
	}
	for (rfbPort = BX_RFB_PORT_MIN; rfbPort <= BX_RFB_PORT_MAX; rfbPort++) {
	  sai.sin_addr.s_addr = INADDR_ANY;
	  sai.sin_family      = AF_INET;
	  sai.sin_port        = htons(rfbPort);
	  BX_INFO (("Trying port %d", rfbPort));
	  if(bind(sServer, (struct sockaddr *)&sai, sizeof(sai)) == -1) {
		  BX_INFO(( "Could not bind socket."));
		  continue;
	  }
	  if(listen(sServer, SOMAXCONN) == -1) {
		  BX_INFO(( "Could not listen on socket."));
		  continue;
	  }
	  // success
	  port_ok = 1;
	  break;
	}
	if (!port_ok) {
	  BX_PANIC (("RFB could not bind any port between %d and %d\n", 
		BX_RFB_PORT_MIN,
		BX_RFB_PORT_MAX));
	  goto end_of_thread;
	}
	BX_INFO (("listening for connections on port %i", rfbPort));
	fprintf (stderr, "RFB: listening for connections on port %i\n", rfbPort);
	sai_size = sizeof(sai);
	while(keep_alive) {
		sClient = accept(sServer, (struct sockaddr *)&sai, (socklen_t*)&sai_size);
		if(sClient != -1) {
			HandleRfbClient(sClient);
			sGlobal = -1;
			close(sClient);
		} else {
			close(sClient);
		}
	}

end_of_thread:
	StopWinsock();
}

void HandleRfbClient(SOCKET sClient) 
{
	char rfbName[] = "Bochs-RFB";
	rfbProtocolVersionMsg pv;
	int one = 1;
	CARD32 auth;
	rfbClientInitMsg cim;
	rfbServerInitMsg sim;

	setsockopt(sClient, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one));
	fprintf(stderr, "# RFB: accepted client connection.\n");
	sprintf(pv, rfbProtocolVersionFormat, rfbProtocolMajorVersion, rfbProtocolMinorVersion);

	if(WriteExact(sClient, pv, sz_rfbProtocolVersionMsg) < 0) {
		fprintf(stderr, "# ERROR: RFB: could not send protocol version.\n");
		return;
	}
	if(ReadExact(sClient, pv, sz_rfbProtocolVersionMsg) < 0) {
		fprintf(stderr, "# ERROR: RFB: could not recieve client protocol version.\n");
		return;
	}

	auth = Swap32IfLE(rfbNoAuth);
	if(WriteExact(sClient, (char *)&auth, sizeof(auth)) < 0) {
		fprintf(stderr, "# ERROR: RFB: could not send authorization method.\n");
		return;
	}

	if(ReadExact(sClient, (char *)&cim, sz_rfbClientInitMsg) < 0) {
		fprintf(stderr, "# ERROR: RFB: could not recieve client initialization message.\n");
		return;
	}

	sim.framebufferWidth  = Swap16IfLE((short)rfbDimensionX);
	sim.framebufferHeight = Swap16IfLE((short)rfbDimensionY);
	sim.format            = BGR233Format;
	sim.format.redMax     = Swap16IfLE(sim.format.redMax);
	sim.format.greenMax   = Swap16IfLE(sim.format.greenMax);
	sim.format.blueMax    = Swap16IfLE(sim.format.blueMax);
	sim.nameLength = strlen(rfbName);
	sim.nameLength = Swap32IfLE(sim.nameLength);
	if(WriteExact(sClient, (char *)&sim, sz_rfbServerInitMsg) < 0) {
		fprintf(stderr, "# ERROR: RFB: could send server initialization message.\n");
		return;
	}
	if(WriteExact(sClient, rfbName, strlen(rfbName)) < 0) {
		fprintf(stderr, "# ERROR: RFB: could not send server name.\n");
		return;
	}

	sGlobal = sClient;
	while(keep_alive) {
		CARD8 msgType;
		int n;

		if((n = recv(sClient, (char *)&msgType, 1, MSG_PEEK)) <= 0) {
			if(n == 0) {
				fprintf(stderr, "# RFB: client closed connection.\n");
			} else {
				fprintf(stderr, "# RFB: error recieving data.\n");
			}
			return;
		}
		
		switch(msgType) {
		case rfbSetPixelFormat:
			{
				rfbSetPixelFormatMsg spf;
				ReadExact(sClient, (char *)&spf, sizeof(rfbSetPixelFormatMsg));

				spf.format.bitsPerPixel = spf.format.bitsPerPixel;
				spf.format.depth = spf.format.depth;
				spf.format.trueColour = (spf.format.trueColour ? 1 : 0);
				spf.format.bigEndian = (spf.format.bigEndian ? 1 : 0);
				spf.format.redMax = Swap16IfLE(spf.format.redMax);
				spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
				spf.format.blueMax = Swap16IfLE(spf.format.blueMax);
				spf.format.redShift = spf.format.redShift;
				spf.format.greenShift = spf.format.greenShift;
				spf.format.blueShift = spf.format.blueShift;
				
				if (!PF_EQ(spf.format, BGR233Format)) {
					fprintf(stderr,"# ERROR: RFB: client has wrong pixel format\n");
					//return;
				}
				break;
			}
		case rfbFixColourMapEntries:
			{
				rfbFixColourMapEntriesMsg fcme;
				ReadExact(sClient, (char *)&fcme, sizeof(rfbFixColourMapEntriesMsg));
				break;
			}
		case rfbSetEncodings:
			{
				rfbSetEncodingsMsg se;
				int                i;
				CARD32             enc;
				ReadExact(sClient, (char *)&se, sizeof(rfbSetEncodingsMsg));
				se.nEncodings = Swap16IfLE(se.nEncodings);
				for(i = 0; i < se.nEncodings; i++) {
					if((n = ReadExact(sClient, (char *)&enc, sizeof(CARD32))) <= 0) {
						if(n == 0) {
							fprintf(stderr, "# RFB: client closed connection.\n");
						} else {
							fprintf(stderr, "# RFB: error recieving data.\n");
						}
						return;
					}
				}
				break;
			}
		case rfbFramebufferUpdateRequest:
			{
				rfbFramebufferUpdateRequestMsg fur;

				ReadExact(sClient, (char *)&fur, sizeof(rfbFramebufferUpdateRequestMsg));
				if(!fur.incremental) {
					rfbUpdateRegion.x = 0;
					rfbUpdateRegion.y = 0;
					rfbUpdateRegion.width  = rfbDimensionX;
					rfbUpdateRegion.height = rfbDimensionY;
					rfbUpdateRegion.updated = true;
				} //else {
				//	if(fur.x < rfbUpdateRegion.x) rfbUpdateRegion.x = fur.x;
				//	if(fur.y < rfbUpdateRegion.x) rfbUpdateRegion.y = fur.y;
				//	if(((fur.x + fur.w) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((fur.x + fur.w) - rfbUpdateRegion.x);
				//	if(((fur.y + fur.h) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((fur.y + fur.h) - rfbUpdateRegion.y);
				//}
				//rfbUpdateRegion.updated = true;
				break;
			}
		case rfbKeyEvent:
			{
				rfbKeyEventMsg ke;
				ReadExact(sClient, (char *)&ke, sizeof(rfbKeyEventMsg));
				ke.key = Swap32IfLE(ke.key);
				while(bKeyboardInUse);
				bKeyboardInUse = true;
				if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break;
				rfbKeyboardEvent[rfbKeyboardEvents].type = KEYBOARD;
				rfbKeyboardEvent[rfbKeyboardEvents].key  = ke.key;
				rfbKeyboardEvent[rfbKeyboardEvents].down = ke.down;
				rfbKeyboardEvents++;
				bKeyboardInUse = false;
				break;
			}
		case rfbPointerEvent:
			{	
				rfbPointerEventMsg pe;
				ReadExact(sClient, (char *)&pe, sizeof(rfbPointerEventMsg));
				while(bKeyboardInUse);
				bKeyboardInUse = true;
				if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break;
				rfbKeyboardEvent[rfbKeyboardEvents].type = MOUSE;
				rfbKeyboardEvent[rfbKeyboardEvents].x    = Swap16IfLE(pe.x);
				rfbKeyboardEvent[rfbKeyboardEvents].y    = Swap16IfLE(pe.y);
				rfbKeyboardEvent[rfbKeyboardEvents].down = pe.buttonMask;
				rfbKeyboardEvents++;
				bKeyboardInUse = false;
				break;
			}
		case rfbClientCutText:
			{
				rfbClientCutTextMsg cct;
				ReadExact(sClient, (char *)&cct, sizeof(rfbClientCutTextMsg));
				break;
			}
		}
	}
}
// ::HANDLE_EVENTS()
//
// Called periodically (vga_update_interval in .bochsrc) so the
// the gui code can poll for keyboard, mouse, and other
// relevant events.

void bx_gui_c::handle_events(void)
{
	unsigned int i = 0;
	while(bKeyboardInUse);
	bKeyboardInUse = true;
	if(rfbKeyboardEvents > 0) {
		for(i = 0; i < rfbKeyboardEvents; i++) {
			if(rfbKeyboardEvent[i].type == KEYBOARD) {
				rfbKeyPressed(rfbKeyboardEvent[i].key, rfbKeyboardEvent[i].down);
			} else { //type == MOUSE;
				rfbMouseMove(rfbKeyboardEvent[i].x, rfbKeyboardEvent[i].y, rfbKeyboardEvent[i].down);
			}
		}
		rfbKeyboardEvents = 0;
	}
	bKeyboardInUse = false;

	if(rfbUpdateRegion.updated) {
		SendUpdate(rfbUpdateRegion.x, rfbUpdateRegion.y, rfbUpdateRegion.width, rfbUpdateRegion.height);
		rfbUpdateRegion.x = rfbDimensionX;
		rfbUpdateRegion.y = rfbDimensionY;
		rfbUpdateRegion.width  = 0;
		rfbUpdateRegion.height = 0;
	}
	rfbUpdateRegion.updated = false;
}


// ::FLUSH()
//
// Called periodically, requesting that the gui code flush all pending
// screen update requests.

void bx_gui_c::flush(void)
{
}


// ::CLEAR_SCREEN()
//
// Called to request that the VGA region is cleared.  Don't
// clear the area that defines the headerbar.
void bx_gui_c::clear_screen(void)
{
	memset(&rfbScreen[rfbDimensionX * rfbHeaderbarY], 0, rfbDimensionX * (rfbDimensionY - rfbHeaderbarY));
}



// ::TEXT_UPDATE()
//
// Called in a VGA text mode, to update the screen with
// new content.
//
// old_text: array of character/attributes making up the contents
//           of the screen from the last call.  See below
// new_text: array of character/attributes making up the current
//           contents, which should now be displayed.  See below
//
// format of old_text & new_text: each is 4000 bytes long.
//     This represents 80 characters wide by 25 high, with
//     each character being 2 bytes.  The first by is the
//     character value, the second is the attribute byte.
//     I currently don't handle the attribute byte.
//
// cursor_x: new x location of cursor
// cursor_y: new y location of cursor

void bx_gui_c::text_update(Bit8u *old_text, Bit8u *new_text, unsigned long cursor_x, unsigned long cursor_y, Bit16u cursor_state, unsigned nrows)
{
	unsigned char cChar;
	unsigned int  nchars;
	unsigned int  i, x, y;
	bool updated = false;

	nchars = 80 * nrows;
	if((rfbCursorY * 80 + rfbCursorX) < nchars) {
		cChar = new_text[(rfbCursorY * 80 + rfbCursorX) * 2];
		DrawBitmap(rfbCursorX * 8, rfbCursorY * 16 + rfbHeaderbarY, 8, 16, (char *)&bx_vgafont[cChar].data, new_text[((rfbCursorY * 80 + rfbCursorX) * 2) + 1], false);
	}

	for(i = 0; i < nchars * 2; i += 2) {
		if((old_text[i] != new_text[i]) || (old_text[i+1] != new_text[i+1])) {
			cChar = new_text[i];
			x = (i / 2) % 80;
			y = (i / 2) / 80;
			DrawBitmap(x * 8, y * 16 + rfbHeaderbarY, 8, 16, (char *)&bx_vgafont[cChar].data, new_text[i + 1], false);
			if((y * 16 + rfbHeaderbarY) < rfbUpdateRegion.y) rfbUpdateRegion.y = y * 16 + rfbHeaderbarY;
			if(((y * 16 + rfbHeaderbarY + 16) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((y * 16 + rfbHeaderbarY + 16) - rfbUpdateRegion.y);
			if((x * 8) < rfbUpdateRegion.x) rfbUpdateRegion.x = x * 8;
			if(((x * 8 + 8) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((x * 8 + 8) - rfbUpdateRegion.x);
			rfbUpdateRegion.updated = true;
		}
	}

	rfbCursorX = cursor_x;
	rfbCursorY = cursor_y;

	if((cursor_y * 80 + cursor_x) < nchars) {
		char cAttr = new_text[((cursor_y * 80 + cursor_x) * 2) + 1];
		cChar = new_text[(cursor_y * 80 + cursor_x) * 2];
		//cAttr = ((cAttr >> 4) & 0xF) + ((cAttr & 0xF) << 4);
		DrawBitmap(rfbCursorX * 8, rfbCursorY * 16 + rfbHeaderbarY, 8, 16, (char *)&bx_vgafont[cChar].data, cAttr, false);
	}
}

  int
bx_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
{
  return 0;
}

  int
bx_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
{
  return 0;
}


// ::PALETTE_CHANGE()
//
// Allocate a color in the native GUI, for this color, and put
// it in the colormap location 'index'.
// returns: 0=no screen update needed (color map change has direct effect)
//          1=screen updated needed (redraw using current colormap)

Boolean bx_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
{
	rfbPallet[index] = (((red * 7 + 127) / 255) << 0) | (((green * 7 + 127) / 255) << 3) | (((blue * 3 + 127) / 255) << 6);
	return(1);
}


// ::GRAPHICS_TILE_UPDATE()
//
// Called to request that a tile of graphics be drawn to the
// screen, since info in this region has changed.
//
// tile: array of 8bit values representing a block of pixels with
//       dimension equal to the 'tilewidth' & 'tileheight' parameters to
//       ::specific_init().  Each value specifies an index into the
//       array of colors you allocated for ::palette_change()
// x0: x origin of tile
// y0: y origin of tile
//
// note: origin of tile and of window based on (0,0) being in the upper
//       left of the window.
void bx_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
{
	UpdateScreen((char *)tile, x0, y0 + rfbHeaderbarY, rfbTileX, rfbTileY, false);
	if(x0 < rfbUpdateRegion.x) rfbUpdateRegion.x = x0;
	if((y0 + rfbHeaderbarY) < rfbUpdateRegion.y) rfbUpdateRegion.y = y0 + rfbHeaderbarY;
	if(((y0 + rfbHeaderbarY + rfbTileY) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height =  ((y0 + rfbHeaderbarY + rfbTileY) - rfbUpdateRegion.y);
	if(((x0 + rfbTileX) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((x0 + rfbTileX) - rfbUpdateRegion.x);
	rfbUpdateRegion.updated = true;
}



// ::DIMENSION_UPDATE()
//
// Called when the VGA mode changes it's X,Y dimensions.
// Resize the window to this size, but you need to add on
// the height of the headerbar to the Y value.
//
// x: new VGA x size
// y: new VGA y size (add headerbar_y parameter from ::specific_init().

  void
bx_gui_c::dimension_update(unsigned x, unsigned y)
{
  UNUSED(x);
  UNUSED(y);
}


// ::CREATE_BITMAP()
//
// Create a monochrome bitmap of size 'xdim' by 'ydim', which will
// be drawn in the headerbar.  Return an integer ID to the bitmap,
// with which the bitmap can be referenced later.
//
// bmap: packed 8 pixels-per-byte bitmap.  The pixel order is:
//       bit0 is the left most pixel, bit7 is the right most pixel.
// xdim: x dimension of bitmap
// ydim: y dimension of bitmap

unsigned bx_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
{
	if(rfbBitmapCount >= BX_MAX_PIXMAPS) {
		fprintf(stderr, "# RFB: too many pixmaps.\n");
		return 0;
	}
	rfbBitmaps[rfbBitmapCount].bmap = (char *)malloc((xdim * ydim) / 8);
	rfbBitmaps[rfbBitmapCount].xdim = xdim;
	rfbBitmaps[rfbBitmapCount].ydim = ydim;
	memcpy(rfbBitmaps[rfbBitmapCount].bmap, bmap, (xdim * ydim) / 8);
	
	rfbBitmapCount++;
	return(rfbBitmapCount - 1);
}


// ::HEADERBAR_BITMAP()
//
// Called to install a bitmap in the bochs headerbar (toolbar).
//
// bmap_id: will correspond to an ID returned from
//     ::create_bitmap().  'alignment' is either BX_GRAVITY_LEFT
//     or BX_GRAVITY_RIGHT, meaning install the bitmap in the next
//     available leftmost or rightmost space.
// alignment: is either BX_GRAVITY_LEFT or BX_GRAVITY_RIGHT,
//     meaning install the bitmap in the next
//     available leftmost or rightmost space.
// f: a 'C' function pointer to callback when the mouse is clicked in
//     the boundaries of this bitmap.

unsigned bx_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void))
{
	int hb_index;

	if((rfbHeaderbarBitmapCount + 1) > BX_MAX_HEADERBAR_ENTRIES) {
		return 0;
	}

	rfbHeaderbarBitmapCount++;
	hb_index = rfbHeaderbarBitmapCount - 1;
	rfbHeaderbarBitmaps[hb_index].index     = bmap_id;
	rfbHeaderbarBitmaps[hb_index].alignment = alignment;
	rfbHeaderbarBitmaps[hb_index].f = f;
	if (alignment == BX_GRAVITY_LEFT) {
		rfbHeaderbarBitmaps[hb_index].xorigin = rfbOriginLeft;
		rfbHeaderbarBitmaps[hb_index].yorigin = 0;
		rfbOriginLeft += rfbBitmaps[bmap_id].xdim;
	} else { // BX_GRAVITY_RIGHT
		rfbOriginRight += rfbBitmaps[bmap_id].xdim;
		rfbHeaderbarBitmaps[hb_index].xorigin = rfbOriginRight;
		rfbHeaderbarBitmaps[hb_index].yorigin = 0;
	}
	return hb_index;
}


// ::SHOW_HEADERBAR()
//
// Show (redraw) the current headerbar, which is composed of
// currently installed bitmaps.

void bx_gui_c::show_headerbar(void)
{
	unsigned int i, xorigin;

	for(i = 0; i < rfbHeaderbarBitmapCount; i++) {
		if(rfbHeaderbarBitmaps[i].alignment == BX_GRAVITY_LEFT) {
			xorigin = rfbHeaderbarBitmaps[i].xorigin;
		} else {
			xorigin = rfbDimensionX - rfbHeaderbarBitmaps[i].xorigin;
		}
		DrawBitmap(xorigin, 0, rfbBitmaps[rfbHeaderbarBitmaps[i].index].xdim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].ydim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].bmap, (char)0x0F, false);
	}
}


// ::REPLACE_BITMAP()
//
// Replace the bitmap installed in the headerbar ID slot 'hbar_id',
// with the one specified by 'bmap_id'.  'bmap_id' will have
// been generated by ::create_bitmap().  The old and new bitmap
// must be of the same size.  This allows the bitmap the user
// sees to change, when some action occurs.  For example when
// the user presses on the floppy icon, it then displays
// the ejected status.
//
// hbar_id: headerbar slot ID
// bmap_id: bitmap ID

void bx_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
{
	rfbHeaderbarBitmaps[hbar_id].index = bmap_id;
}


// ::EXIT()
//
// Called before bochs terminates, to allow for a graceful
// exit from the native GUI mechanism.
void bx_gui_c::exit(void)
{
	unsigned int i;
	keep_alive = false;
	StopWinsock();
	free(rfbScreen);
	for(i = 0; i < rfbBitmapCount; i++) {
		free(rfbBitmaps[i].bmap);
	}
	fprintf(stderr, "# RFB: bx_gui_c::exit()\n");
}

/*
* ReadExact reads an exact number of bytes on a TCP socket.  Returns 1 if
* those bytes have been read, 0 if the other end has closed, or -1 if an error
* occurred (errno is set to ETIMEDOUT if it timed out).
*/

int ReadExact(int sock, char *buf, int len)
{
    int n;
 
    while (len > 0) {
	n = recv(sock, buf, len, 0);
	if (n > 0) {
	    buf += n;
	    len -= n;
        } else {
            return n;
	}
    }
    return 1;
}

/*
* WriteExact writes an exact number of bytes on a TCP socket.  Returns 1 if
* those bytes have been written, or -1 if an error occurred (errno is set to
* ETIMEDOUT if it timed out).
*/

int WriteExact(int sock, char *buf, int len)
{
    int n;
	
    while (len > 0) {
	n = send(sock, buf, len,0);
		
	if (n > 0) {
	    buf += n;
	    len -= n;
	} else if (n == 0) {
      	    fprintf(stderr,"WriteExact: write returned 0?\n");
	    return n;
        } else {
            return n;
	}
    }
    return 1;
}

void DrawBitmap(int x, int y, int width, int height, char *bmap, char color, bool update_client)
{
	int  i;
	char *newBits;
	char fgcolor, bgcolor;
	char vgaPallet[] = { (char)0x00, //Black 
						 (char)0x01, //Dark Blue
						 (char)0x02, //Dark Green
						 (char)0x03, //Dark Cyan
						 (char)0x04, //Dark Red
						 (char)0x05, //Dark Magenta
						 (char)0x06, //Brown
						 (char)0x07, //Light Gray
						 (char)0x38, //Dark Gray
						 (char)0x09, //Light Blue
						 (char)0x12, //Green
						 (char)0x1B, //Cyan
						 (char)0x24, //Light Red
						 (char)0x2D, //Magenta
						 (char)0x36, //Yellow
						 (char)0x3F  //White
						};

	bgcolor = vgaPallet[(color >> 4) & 0xF];
	fgcolor = vgaPallet[color & 0xF];
	newBits = (char *)malloc(width * height);
	memset(newBits, 0, (width * height));
	for(i = 0; i < (width * height) / 8; i++) {
		newBits[i * 8 + 0] = (bmap[i] & 0x01) ? fgcolor : bgcolor;
		newBits[i * 8 + 1] = (bmap[i] & 0x02) ? fgcolor : bgcolor;
		newBits[i * 8 + 2] = (bmap[i] & 0x04) ? fgcolor : bgcolor;
		newBits[i * 8 + 3] = (bmap[i] & 0x08) ? fgcolor : bgcolor;
		newBits[i * 8 + 4] = (bmap[i] & 0x10) ? fgcolor : bgcolor;
		newBits[i * 8 + 5] = (bmap[i] & 0x20) ? fgcolor : bgcolor;
		newBits[i * 8 + 6] = (bmap[i] & 0x40) ? fgcolor : bgcolor;
		newBits[i * 8 + 7] = (bmap[i] & 0x80) ? fgcolor : bgcolor;
	}
	UpdateScreen(newBits, x, y, width, height, update_client);
	//DrawColorPallet();
	free(newBits);
}

void DrawColorPallet()
{
	char bits[100];
	int x = 0, y = 0, c;
	for(c = 0; c < 256; c++) {
		memset(&bits, rfbPallet[c], 100);
		UpdateScreen(bits, x, y, 10, 10, false);
		x += 10;
		if(x > 70) {
			y += 10;
			x = 0;
		}
	}
}

void UpdateScreen(char *newBits, int x, int y, int width, int height, bool update_client)
{
	int i, c;
	for(i = 0; i < height; i++) {
		for(c = 0; c < width; c++) {
			newBits[(i * width) + c] = rfbPallet[newBits[(i * width) + c]];
		}
		memcpy(&rfbScreen[y * rfbDimensionX + x], &newBits[i * width], width);
		y++;
	}
	if(update_client) {
		if(sGlobal == -1) return;
		rfbFramebufferUpdateMsg fum;
		rfbFramebufferUpdateRectHeader furh;
		fum.type = rfbFramebufferUpdate;
		fum.nRects = Swap16IfLE(1);
		WriteExact(sGlobal, (char *)&fum, sz_rfbFramebufferUpdateMsg);
		furh.r.x = Swap16IfLE(x);
		furh.r.y = Swap16IfLE((y - i));
		furh.r.w = Swap16IfLE((short)width);
		furh.r.h = Swap16IfLE((short)height);
		furh.encoding = Swap32IfLE(rfbEncodingRaw);
		WriteExact(sGlobal, (char *)&furh, sz_rfbFramebufferUpdateRectHeader);
		WriteExact(sGlobal, (char *)newBits, width * height);
	}
}

void SendUpdate(int x, int y, int width, int height)
{
	char *newBits;
	int  i;

	if(x < 0 || y < 0 || (x + width) > rfbDimensionX || (y + height) > rfbDimensionY) {
		fprintf(stderr, "# RFB: Dimensions out of bounds.  x=%i y=%i w=%i h=%i\n", x, y, width, height);
	}
	if(sGlobal != -1) {
		rfbFramebufferUpdateMsg fum;
		rfbFramebufferUpdateRectHeader furh;

		fum.type = rfbFramebufferUpdate;
		fum.nRects = Swap16IfLE(1);

		furh.r.x = Swap16IfLE(x);
		furh.r.y = Swap16IfLE(y);
		furh.r.w = Swap16IfLE((short)width);
		furh.r.h = Swap16IfLE((short)height);
		furh.encoding = Swap32IfLE(rfbEncodingRaw);

		newBits = (char *)malloc(width * height);
		for(i = 0; i < height; i++) {
			memcpy(&newBits[i * width], &rfbScreen[y * rfbDimensionX + x], width);
			y++;
		}

		WriteExact(sGlobal, (char *)&fum, sz_rfbFramebufferUpdateMsg);
		WriteExact(sGlobal, (char *)&furh, sz_rfbFramebufferUpdateRectHeader);
		WriteExact(sGlobal, (char *)newBits, width * height);

		free(newBits);
	}
}

void StartThread()
{
#ifdef WIN32
	_beginthread(ServerThreadInit, 0, NULL);
#else
	pthread_t      thread;
	pthread_create(&thread, NULL, (void *(*)(void *))&ServerThreadInit, NULL);
#endif
}

/***********************/
/* Keyboard Definitons */
/*        And          */
/*     Functions       */
/***********************/

#define XK_space            0x020
#define XK_asciitilde       0x07e

#define XK_dead_grave       0xFE50
#define XK_dead_acute       0xFE51
#define XK_dead_circumflex  0xFE52
#define XK_dead_tilde       0xFE53

#define XK_BackSpace		0xFF08	
#define XK_Tab			    0xFF09
#define XK_Linefeed		    0xFF0A
#define XK_Clear		    0xFF0B
#define XK_Return		    0xFF0D
#define XK_Pause		    0xFF13	
#define XK_Scroll_Lock		0xFF14
#define XK_Sys_Req		    0xFF15
#define XK_Escape		    0xFF1B

#define XK_Delete		    0xFFFF

#define XK_Home			0xFF50
#define XK_Left			0xFF51	
#define XK_Up			0xFF52	
#define XK_Right		0xFF53
#define XK_Down			0xFF54
#define XK_Page_Up		0xFF55
#define XK_Page_Down	0xFF56
#define XK_End			0xFF57	
#define XK_Begin		0xFF58	

#define XK_Select		0xFF60	
#define XK_Print		0xFF61
#define XK_Execute		0xFF62	
#define XK_Insert		0xFF63	

#define XK_Cancel		0xFF69	
#define XK_Help			0xFF6A
#define XK_Break		0xFF6B
#define XK_Num_Lock		0xFF7F

#define XK_KP_Space		0xFF80
#define XK_KP_Tab		0xFF89
#define XK_KP_Enter		0xFF8D	

#define XK_KP_Home		0xFF95
#define XK_KP_Left		0xFF96
#define XK_KP_Up		0xFF97
#define XK_KP_Right		0xFF98
#define XK_KP_Down		0xFF99
#define XK_KP_Prior		0xFF9A
#define XK_KP_Page_Up	0xFF9A
#define XK_KP_Next		0xFF9B
#define XK_KP_Page_Down	0xFF9B
#define XK_KP_End		0xFF9C
#define XK_KP_Begin		0xFF9D
#define XK_KP_Insert	0xFF9E
#define XK_KP_Delete	0xFF9F
#define XK_KP_Equal		0xFFBD
#define XK_KP_Multiply	0xFFAA
#define XK_KP_Add		0xFFAB
#define XK_KP_Separator	0xFFAC	
#define XK_KP_Subtract	0xFFAD
#define XK_KP_Decimal	0xFFAE
#define XK_KP_Divide	0xFFAF

#define XK_KP_F1		0xFF91
#define XK_KP_F2		0xFF92
#define XK_KP_F3		0xFF93
#define XK_KP_F4		0xFF94

#define XK_KP_0			0xFFB0
#define XK_KP_1			0xFFB1
#define XK_KP_2			0xFFB2
#define XK_KP_3			0xFFB3
#define XK_KP_4			0xFFB4
#define XK_KP_5			0xFFB5
#define XK_KP_6			0xFFB6
#define XK_KP_7			0xFFB7
#define XK_KP_8			0xFFB8
#define XK_KP_9			0xFFB9

#define XK_F1			0xFFBE
#define XK_F2			0xFFBF
#define XK_F3			0xFFC0
#define XK_F4			0xFFC1
#define XK_F5			0xFFC2
#define XK_F6			0xFFC3
#define XK_F7			0xFFC4
#define XK_F8			0xFFC5
#define XK_F9			0xFFC6
#define XK_F10			0xFFC7
#define XK_F11			0xFFC8
#define XK_F12			0xFFC9
#define XK_F13			0xFFCA
#define XK_F14			0xFFCB
#define XK_F15			0xFFCC
#define XK_F16			0xFFCD
#define XK_F17			0xFFCE
#define XK_F18			0xFFCF
#define XK_F19			0xFFD0
#define XK_F20			0xFFD1
#define XK_F21			0xFFD2
#define XK_F22			0xFFD3
#define XK_F23			0xFFD4
#define XK_F24			0xFFD5


#define XK_Shift_L		0xFFE1	
#define XK_Shift_R		0xFFE2	
#define XK_Control_L	0xFFE3	
#define XK_Control_R	0xFFE4	
#define XK_Caps_Lock	0xFFE5	
#define XK_Shift_Lock	0xFFE6	
#define XK_Meta_L		0xFFE7	
#define XK_Meta_R		0xFFE8
#define XK_Alt_L		0xFFE9
#define XK_Alt_R		0xFFEA

char rfbAsciiKey[0x5f] = {
  //  !"#$%&'
  0x39,
  0x02,
  BX_KEY_SINGLE_QUOTE,
  0x04,
  0x05,
  0x06,
  0x07,
  0x08,

  // ()*+,-./
  0x0A,
  0x0B,
  0x09,
  0x0D,
  0x33,
  0x0C,
  0x34,
  0x35,

  // 01234567
  0x0B,
  0x02,
  0x03,
  0x04,
  0x05,
  0x06,
  0x07,
  0x08,

  // 89:;<=>?
  0x09,
  0x0A,
  0x27,
  0x27,
  0x33,
  0x0D,
  0x34,
  0x35,

  // @ABCDEFG
  0x03,
  0x1E,
  0x30,
  0x2E,
  0x20,
  0x12,
  0x21,
  0x22,


  // HIJKLMNO
  0x23,
  0x17,
  0x24,
  0x25,
  0x26,
  0x32,
  0x31,
  0x18,


  // PQRSTUVW
  0x19,
  0x10,
  0x13,
  0x1F,
  0x14,
  0x16,
  0x2F,
  0x11,

  // XYZ[\]^_
  0x2D,
  0x15,
  0x2C,
  0x1A,
  0x2B,
  0x1B,
  0x07,
  0x29,

  // `abcdefg
  0x29,
  0x1E,
  0x30,
  0x2E,
  0x20,
  0x12,
  0x21,
  0x22,

  // hijklmno
  0x23,
  0x17,
  0x24,
  0x25,
  0x26,
  0x32,
  0x31,
  0x18,

  // pqrstuvw
  0x19,
  0x10,
  0x13,
  0x1F,
  0x14,
  0x16,
  0x2F,
  0x11,

  // xyz{|}~
  0x2D,
  0x15,
  0x2C,
  0x1A,
  0x2B,
  0x1B,
  0x29
};

void rfbKeyPressed(Bit32u key, int press_release)
{
	unsigned char key_event;

	if((key >= XK_space) && (key <= XK_asciitilde)) {
		key_event = rfbAsciiKey[key - XK_space];
	} else switch (key) {
		case XK_KP_1:
#ifdef XK_KP_End
		case XK_KP_End:
#endif
		key_event = 0x4F; break;

		case XK_KP_2:
#ifdef XK_KP_Down
		case XK_KP_Down:
#endif
		key_event = 0x50; break;
		
		case XK_KP_3:
#ifdef XK_KP_Page_Down
		case XK_KP_Page_Down:
#endif
		key_event = 0x51; break;
		
		case XK_KP_4:
#ifdef XK_KP_Left
		case XK_KP_Left:
#endif
		key_event = 0x4B; break;
		
		case XK_KP_5:
		key_event = 0x4C; break;
		
		case XK_KP_6:
#ifdef XK_KP_Right
		case XK_KP_Right:
#endif
		key_event = 0x4D; break;

		case XK_KP_7:
#ifdef XK_KP_Home
		case XK_KP_Home:
#endif
		key_event = 0x47; break;

		case XK_KP_8:
#ifdef XK_KP_Up
		case XK_KP_Up:
#endif
		key_event = 0x48; break;

		case XK_KP_9:
#ifdef XK_KP_Page_Up
		case XK_KP_Page_Up:
#endif
		key_event = 0x49; break;

		case XK_KP_0:
#ifdef XK_KP_Insert
		case XK_KP_Insert:
		key_event = 0x52; break;
#endif

		case XK_KP_Decimal:
#ifdef XK_KP_Delete
		case XK_KP_Delete:
		key_event = 0x53; break;
#endif

		case XK_KP_Subtract: key_event = 0x4A; break;
		case XK_KP_Add:      key_event = 0x4E; break;

		case XK_Up:          key_event = 0x48; break;
		case XK_Down:        key_event = 0x50; break;
		case XK_Left:        key_event = 0x4B; break;
	    case XK_Right:       key_event = 0x4D; break;

		case XK_BackSpace:   key_event = 0x0E; break;
		case XK_Tab:         key_event = 0x0F; break;
		case XK_Return:      key_event = 0x1C; break;
		case XK_Escape:      key_event = 0x01; break;
		case XK_F1:          key_event = 0x3B; break;
		case XK_F2:          key_event = 0x3C; break;
		case XK_F3:          key_event = 0x3D; break;
		case XK_F4:          key_event = 0x3E; break;
		case XK_F5:          key_event = 0x3F; break;
		case XK_F6:          key_event = 0x40; break;
		case XK_F7:          key_event = 0x41; break;
		case XK_F8:          key_event = 0x42; break;
		case XK_F9:          key_event = 0x43; break;
		case XK_F10:         key_event = 0x44; break;
		case XK_Control_L:   key_event = 0x1D; break;
		case XK_Control_R:   key_event = 0x1D; break;
		case XK_Shift_L:     key_event = 0x2A; break;
		case XK_Shift_R:     key_event = 0x36; break;
		case XK_Num_Lock:    key_event = 0x45; break;
		case XK_Alt_L:       key_event = 0x38; break;
		case XK_Alt_R:       key_event = 0x38; break;
	
		default:
			fprintf(stderr, "# RFB: rfbKeyPress(): key %x unhandled!\n", (unsigned) key);
			return;
			break;
	}

	if (press_release) key_event |= 0x80;
	bx_devices.keyboard->put_scancode((unsigned char *)&key_event, 1);
}

void rfbMouseMove(int x, int y, int bmask)
{
	static int oldx = -1;
	static int oldy = -1;
	int buttons = 0;

	if (oldx == oldy == -1) {
		oldx = x;
		oldy = y;
		return;
	}
	if(y > rfbHeaderbarY) {
		//bx_devices.keyboard->mouse_motion(x, y - rfbHeaderbarY, buttons);
		bx_devices.keyboard->mouse_motion(x - oldx, oldy - y, bmask);
		oldx = x;
		oldy = y;
	}
}

  void
bx_gui_c::mouse_enabled_changed_specific (Boolean val)
{
}
