/* Adapted from palmvnc - original notice below */


/*
 *  Version 1.40 Copyright (C) 2001 Harakan Software
 *
 *  http://www.harakan.btinternet.co.uk/PalmVNC
 *
 *
 *  Copyright (C) 1998 International Computer Science Institute (ICSI)
 *
 *  Author: Vladimir Minenko, minenko@icsi.berkeley.edu
 *
 *  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.
 *
 */


#include <pspkernel.h>
#include <psppower.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/fd_set.h>
#include <sys/select.h>
//LUDO: #include "libnet/pspnet.h"
#include "libvnc/vnc.h"
#include "menu.h"

#include "pg.h"
#include "vncdbg.h"
#include "dialog.h"

int vpUpdate = 0;
int vncHomeMenu(int *padmode, int *disconnect);

extern u8 mouseDS, mouseAS;
extern u8 UpdatePreventsPowersave;
extern int bQuit;

#define INVALID_PIXEL       0xffffffff

#define COLORMAP_SIZE       256

// switch defines for scrollbars
// we have our own scrollbars because the native scrollbars are too large
#define HORIZ           1
#define HORIZ_UP        2
#define HORIZ_DOWN      3
#define HORIZ_TOP       4
#define HORIZ_BUTTOM    5

#define VERT            6
#define VERT_UP         7
#define VERT_DOWN       8
#define VERT_TOP        9
#define VERT_BUTTOM     10

#define MAIN_PRESSED    11
#define MAIN_MOVED      12
#define MAIN_ONCE       13
#define MAIN_DOUBLE     14

// modifiers and special Windows keys
#define TAB_KEY         0xff09
#define ENTER_KEY       0xff0d
#define ESC_KEY         0xff1b
#define SHIFT_KEY       0xffe1
#define CTRL_KEY        0xffe3
#define ALT_KEY         0xffe9
#define F4_KEY          0xffc1
#define DEL_KEY         0xffff

#define F1_KEY          0xffbe

#if 0
unsigned long lastPenTime = 0;
#endif

unsigned short lastX=0, lastY=0;
unsigned short currentMouseButton = 0;

PRFBViewport pspFB;

short mouseX = 240, mouseY=136;

u16 *screenData = (u16 *)0x4198000; //[VIEWPORT_SKEW*VIEWPORT_H];

DB_ServerConnection server;

// pen event processing
int downX, downY;
unsigned short buttonState = 0;

int viewportX = 0, viewportY = 0;

int pen_zooming = false;

RectangleType vtSb = {VIEWPORT_W - SCROLLBAR_SIZE, 0,
				SCROLLBAR_SIZE, VIEWPORT_H};
RectangleType hzSb = {0, VIEWPORT_H - SCROLLBAR_SIZE,
				VIEWPORT_H - SCROLLBAR_SIZE, SCROLLBAR_SIZE};
RectangleType vtSl = {VIEWPORT_W - SCROLLBAR_SIZE, 0,
				SCROLLBAR_SIZE, SCROLLBAR_SIZE};
RectangleType hzSl = {0, VIEWPORT_H - SCROLLBAR_SIZE,
				SCROLLBAR_SIZE, SCROLLBAR_SIZE};
RectangleType updateRect = {0, 0, 0, 0};

RectangleType viewableBounds = {0, 0, VIEWPORT_W, VIEWPORT_H};
RectangleType virtualBounds = {0, 0, VIEWPORT_W, VIEWPORT_H};

int errno;

Bool OpenConnection (void);
void ShutdownConnection (void);
void SendKeySequence (unsigned short mod1, unsigned short mod2, unsigned short mod3,
			  unsigned short *keys, int n, Bool ascii);
void PositionViewport (void);
void ProcessPenEvent ();
void PositionScrollbars (void);
void UpdateScrollbars (void);
int UpdateViewport (int padmode, int doctl);
void UpdateProgress (char *msg, int part, int whole);

/******************************************************************
 * mySetForeColor
 *
 * Sets the foregroung ink to the specified BGR233 color,
 * regardless of mode.
 *****************************************************************/
void mySetForeColor (u16 color)
{
}

/******************************************************************
 * GetUpdateRectAtPos
 *
 * Re-aasigns the coordinates of the update rect. and returns the
 * pointer to it.
 *****************************************************************/
RectangleType * GetUpdateRectAtPos (unsigned short x, unsigned short y)
{
  updateRect.x = x;
  updateRect.y = y;
  return &updateRect;
}



/******************************************************************
 * DrawVirtualFilledRectangle
 *
 * Draws a filled rectangle in the virtual window If adjust is true
 * x and y coordinates are translatds to the origin in the virtual
 *****************************************************************/
void DrawVirtualFilledRectangle (unsigned short color,
				short x, short y,
				unsigned short width, unsigned short height,
				int adjust)
{
	short row = 0;
	short column = 0;
	short start_column = 0;
	short end_column = 0;
	short start_row = 0;
	short end_row = 0;

	// align to real coordinates
	if (adjust)
	{
		x -= pspFB.virtual.x;
		y -= pspFB.virtual.y;
	}

	// Check top-left corner
	if ((x >= pspFB.virtual.w) || (y >= pspFB.virtual.h))
	{
		return;
	}

	// Check top and bottom boundaries
	if (y < 0)
		start_row = -y;
	else
		start_row = 0;
	if ((y + height) >= pspFB.virtual.h)
		end_row = (pspFB.virtual.h - y)-1;
	else
		end_row = height-1;
	if (end_row < start_row)
		return;

	// Check left and right boundaries
	if (x < 0)
		start_column = -x;
	else
		start_column = 0;
	if ((x + width) >= pspFB.virtual.w)
		end_column = (pspFB.virtual.w - x)-1;
	else
		end_column = width-1;
	if (end_column < start_column)
		return;

	/* Do whole region */
	for (row = start_row; row <= end_row; row++)
	{
		for (column = start_column; column <= end_column; column ++)
		{
			screenData[(((row + y) * VIEWPORT_SKEW) + column + x)] = color;
		}
	}
}


/******************************************************************
 * CopyDataToScreen
 *
 * Copies a 16bit pixel array into the data area off screen
 *****************************************************************/
void CopyDataToScreen (u16 *buf, short x, short y, unsigned short width, unsigned short height)
{
	short row = 0;
	short column = 0;
	short start_column = 0;
	short end_column = 0;
	short start_row = 0;
	short end_row = 0;

        if(UpdatePreventsPowersave)
	  scePowerTick(0);

//	short x_off = 0;
//	unsigned short color_temp = 0;
//	unsigned short color_mask = 0;

	// align to real coordinates
	x -= pspFB.virtual.x;
	y -= pspFB.virtual.y;

	// Check top-left corner
	if ((x >= pspFB.virtual.w) || (y >= pspFB.virtual.h))
	{
		return;
	}

	// Check top and bottom boundaries
	if (y < 0)
		start_row = -y;
	else
		start_row = 0;
	if ((y + height) >= pspFB.virtual.h)
		end_row = (pspFB.virtual.h - y)-1;
	else
		end_row = height-1;
	if (end_row < start_row)
		return;

	// Check left and right boundaries
	if (x < 0)
		start_column = -x;
	else
		start_column = 0;
	if ((x + width) >= pspFB.virtual.w)
		end_column = (pspFB.virtual.w - x)-1;
	else
		end_column = width-1;
	if (end_column < start_column)
		return;

	for (row = start_row; row <= end_row; row++)
	{
		for (column = start_column; column <= end_column; column ++)
		{
			screenData[(((row + y) * VIEWPORT_SKEW) + column + x)] = buf[(row * width) + column];
		}
	}
}

/******************************************************************
 * CreateWindows
 *
 * Creates the off-screen panning window and inits global variables.
 * Called only once wenn the MainFrom is loaded and opened.
 *****************************************************************/
void vncInitFormat ()
{
  /* this is a set-up for BRG565 format. 
   */

  myFormat.bitsPerPixel = 16;
  myFormat.depth = 16;
  myFormat.trueColour = 1;
  myFormat.bigEndian = 0;
  myFormat.redMax = 31;
  myFormat.greenMax = 63;
  myFormat.blueMax = 31;
  myFormat.redShift = 0;
  myFormat.greenShift = 5;
  myFormat.blueShift = 11;

  viewportX = 0;
  viewportY = 0;

  /* set up the size of the update rectangle */
  updateRect.w = pspFB.viewable.w;
  updateRect.h = pspFB.viewable.h;

  return;
}



/******************************************************************
 * OpenConnection
 *
 * Opens a connection to the server
 *****************************************************************/
Bool OpenConnection (void)
{
  int err = false;

  if (rfbsock > 0) {
	MsgPs0 (0, "Already connected\n");
	return true;
  }

  viewportX = pspFB.viewable.x = pspFB.virtual.x = server.beginX;
  viewportY = pspFB.viewable.y = pspFB.virtual.y = server.beginY;

  PostProgressMessage ("\n\tConnecting...", 0, 0);

  if (!ConnectToRFBServer (server.address, server.port)) {
	MsgPs0 (0, "\n\tERROR: cannot connect\n");
	err = true;
  } else if (!InitialiseRFBConnection(rfbsock)) {
	MsgPs0 (0, "\n\tERROR: cannot initialize\n");
	err = true;
  } else if (!SetFormatAndEncodings()) {
	err = true;
	MsgPs0 (0, "\n\tERROR: cannot set format\n");
  } else if (server.scaleFactor != 1) {
	if (!SetScaleFactor(server.scaleFactor)) {
		err = true;
		MsgPs0 (0, "\n\tERROR: cannot set scale\n");
		LogPs0 ("ERROR: cannot set scale\n");
	}
  }
  if (server.scaleFactor == 1)
  {
	  if ((err == false) && (!SendFramebufferUpdateRequest(pspFB.virtual.x, pspFB.virtual.y,
			 pspFB.virtual.w, pspFB.virtual.h, False))) {
		MsgPs0 (0, "\n\tERROR: cannot send buffer update\n");
		err = true;
	  }
  }

  if (err) {
	ShutdownConnection ();
	rfbsock = 0;
	return false;
  }

  if(pspFB.remote.w && pspFB.remote.h) {
    /* set up the size of scrollbar slides */
    hzSl.w =
    	(s16)((pspFB.viewable.w * pspFB.viewable.w) /
		pspFB.remote.w);
    vtSl.h =
	(s16)((pspFB.viewable.h * pspFB.viewable.h) /
		pspFB.remote.h);
  } else {
    hzSl.w = 64;
    vtSl.h = 64;
  }
//  PostProgressMessage ("\n\tConnected!\n", 0, 0);

  return true;
}


/******************************************************************
 * ShutdownConnection
 *
 * Closes the current connection to the VNC server
 *****************************************************************/
void ShutdownConnection ()
{
  if (rfbsock > 0) {
	sceNetInetClose(rfbsock);
	rfbsock = 0;

	PrintSentRecvStat ();

	LogPs0 ("Connection closed\n\n");

	/* set up the size of scrollbar slides */
	hzSl.w = 64;
	vtSl.h = 64;

	viewportX = pspFB.viewable.x = pspFB.virtual.x = 0;
	viewportY = pspFB.viewable.y = pspFB.virtual.y = 0;

	pspFB.remote.h = pspFB.remote.w = 0;

	/*InitVirtual ();*/

	PositionScrollbars ();
	UpdateProgress("Server connection closed", 0, 0);
  }
}

/******************************************************************
 * SendKeySequence
 *
 * Sends a sequence of n characters as a keyboard input to the VNC
 * server. Works with unsigned short data because the server works
 * with 16bit KeySyms from X11. mod1 and mod2 are two available
 * modifiers. If ascii is true, convert the data into a sequence of
 * unsigned short
 *****************************************************************/
void SendKeySequence (unsigned short mod1, unsigned short mod2, unsigned short mod3,
			  unsigned short *keys, int n, Bool ascii)
{
  int i;
  unsigned short key;

  if (keys == 0) {
	return;
  }

  if (mod1 > 0)
	SendKeyEvent(mod1, true); // modifier 1 pressed

  if (mod2 > 0)
	SendKeyEvent(mod2, true); // modifier 2 pressed

  if (mod3 > 0)
	SendKeyEvent(mod3, true); // modifier 3 pressed

  for (i = 0; i < n; i++) {
	if (ascii)
	  key = ((unsigned char *)keys)[i];
	else
	  key = keys[i];

  if (key == 10) key = 13; // Override for carriage return


	SendKeyEvent(key, true); // pressed
	SendKeyEvent(key, false); // released
  }

  if (mod3 > 0)
	SendKeyEvent(mod3, false); // released

  if (mod2 > 0)
	SendKeyEvent(mod2, false); // released

  if (mod1 > 0)
	SendKeyEvent(mod1, false); // released

  sendUpdateRequest = true;
}

/******************************************************************
 * PositionViewport
 *
 * Moves visible area over the virtual panning window. If the
 * the boundaries of the visible area is beyond the panning window,
 * the panning window will be moved and a new portion of the screen
 * data will be requested
 *****************************************************************/
void PositionViewport ()
{
  int deltaX=0, deltaY=0;
  int updateViewport = False;

  if (viewportX < 0)
	viewportX = 0;

  if (viewportX > (pspFB.remote.w - pspFB.viewable.w))
	viewportX = pspFB.remote.w - pspFB.viewable.w;

  if (viewportY < 0)
	viewportY = 0;

  if (viewportY > (pspFB.remote.h - pspFB.viewable.h))
	viewportY = pspFB.remote.h - pspFB.viewable.h;

  if (pspFB.viewable.x != viewportX) {
	if (viewportX < pspFB.virtual.x ||
	viewportX + pspFB.viewable.w > pspFB.virtual.w) {
	  deltaX = (int)pspFB.viewable.x - viewportX;
	}
	updateViewport = True;
	pspFB.viewable.x = viewportX;
  }

  if ((int)pspFB.virtual.x - deltaX < 0)
	 deltaX = pspFB.virtual.x;

  if (pspFB.viewable.y != viewportY) {
	if (viewportY < pspFB.virtual.y ||
	viewportY + pspFB.viewable.h > pspFB.virtual.h) {
	  deltaY = (int)pspFB.viewable.y - viewportY;
	}
	updateViewport = True;
	pspFB.viewable.y = viewportY;
  }

  if ((int)pspFB.virtual.y - deltaY < 0)
	deltaY = pspFB.virtual.y;

  if (deltaX != 0 || deltaY != 0) {
	int obscuredRectX=0, obscuredRectY=0;
	int sx, sy, sw, sh, dx, dy;

	sx = sy = 0;
	sw = pspFB.virtual.w;
	sh = pspFB.virtual.h;

        dx = dy = 0;

	if (deltaX > 0) { sw -= deltaX; dx = deltaX; }
	if (deltaY > 0) { sh -= deltaY; dy = deltaY; }

	if (deltaX < 0) { sx -= deltaX; sw += deltaX; }
	if (deltaY < 0) { sy -= deltaY; sh += deltaY; }

        pgBitBltD(screenData, VIEWPORT_SKEW, dx, dy,
                  screenData, VIEWPORT_SKEW, sx, sy, sw, sh);
        
	if (deltaX < 0)
	  obscuredRectX = (int)pspFB.virtual.w + deltaX;

	if (deltaX != 0) {

	  DrawVirtualFilledRectangle (RGB565(64,64,64), obscuredRectX, 0,
		abs(deltaX), pspFB.virtual.h, false);

	  pspFB.virtual.x -= deltaX;
	  deltaX = abs(deltaX);
	  if (deltaX > VIEWPORT_W-8) deltaX = VIEWPORT_W-8;

	  (void)SendFramebufferUpdateRequest (pspFB.virtual.x + obscuredRectX,
					pspFB.virtual.y,
					deltaX,
					pspFB.virtual.h,
					False);
	}

	if (deltaY < 0)
	  obscuredRectY = (int)pspFB.virtual.h + deltaY;

	if (deltaY != 0) {

	  DrawVirtualFilledRectangle (RGB565(64,64,64), 0, obscuredRectY,
		pspFB.virtual.w, abs(deltaY), false);

	  pspFB.virtual.y -= deltaY;
	  deltaY = abs(deltaY);
	  if (deltaY > VIEWPORT_H - 8) deltaY = VIEWPORT_H - 8;

	  (void)SendFramebufferUpdateRequest (pspFB.virtual.x,
					pspFB.virtual.y + obscuredRectY,
					pspFB.virtual.w,
					deltaY,
					False);
	}

	sendUpdateRequest = true;
	updateViewport = true;
  }

  if (updateViewport)
	UpdateViewport (0, 0);
}

/******************************************************************
 * UpdateViewport
 *
 * Copies the viewable portion of the off-screen window into the
 * visible window
 *****************************************************************/
static int Vx=0, Vy=0, Vs=0, Vd=-1, Vc=0, Va=0;
int UpdateViewport (int padmode, int doctl)
{
//  RectangleType rect;
//  int i;

  if (rfbsock == 0)
	return padmode;
  
  pgStartFrame();

  bltrect sr = {
    pspFB.viewable.x - pspFB.virtual.x,
    pspFB.viewable.y - pspFB.virtual.y,
    VIEWPORT_W,
    VIEWPORT_H,
    VIEWPORT_SKEW,
    VIEWPORT_SKEW,
    screenData,
    1.003f,
    GE_TPSM_5650
  };
  pgBitBltR(&sr, 0, 0); 

  
  if(padmode==1) {
    pgFillBox(0, 0, 480-8, 12, 0x7f000000);
    mh_print(209, 1, "SCROLL MODE", 0x7f000000);
    mh_print(208, 0, "SCROLL MODE", 0x7fffffff);
    UpdateScrollbars();
  } else if(padmode==3) {
    pgFillBox(0, 272-12, 480, 272, 0x3f000000);
    mh_print(218, 272-10, "GAME MODE", 0x3f000000);
    mh_print(217, 272-11, "GAME MODE", 0x3fffffff);
  } else if(padmode==2) { 
    unsigned short newkey = 0;
    u32 dn = now_pad;
    if(!doctl) now_pad = 0;
    newkey = VKB_Overlay(24,24, &Vx, &Vy, &Vs, &Vd, Vc, Va);
    now_pad = dn;
    switch(newkey) {
    case 0xf0:
      Va = (!Va)*0xffe9;
      break;
    case 0xfd:
      Vc = (!Vc)*0xffe3;
      break;
    case 255:
      padmode = 0;
      break;
    case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6:
    case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc:
      newkey += 0xfecd;
      SendKeySequence(Vc,Va,0, &newkey, 1, 0);
      break;
    case 27:
      newkey = 0xff1b;
      SendKeySequence(Vc,Va,0, &newkey, 1, 0);
      break;
    case 13:
      newkey = 0xff0d;
      SendKeySequence(Vc,Va,0, &newkey, 1, 0);
      break;
    case 8:
      newkey = 0xff08;
      SendKeySequence(Vc,Va,0, &newkey, 1, 0);
      break;
    case 127:
      newkey = 0xffff;
      SendKeySequence(Vc,Va,0, &newkey, 1, 0);
      break;
    default:
      if(newkey) SendKeySequence(Vc,Va,0, &newkey, 1, 1);
    }
  }

  pgEndFrame();
  
  return padmode;
}

/******************************************************************
 * UpdateProgress
 *
 *****************************************************************/
void UpdateProgress (char *msg, int part, int whole)
{
#if 0
  bltrect sr = {
    0,
    0,
    VIEWPORT_W,
    VIEWPORT_H,
    VIEWPORT_SKEW,
    screenData,
  };

  if (rfbsock == 0)
	return;

  pgBitBltR(&sr, 0, 0); 

  pgFillBox(0, 254, 479, 271, BLACK);

  mh_print(4,256,msg,WHITE);  

  if(whole) {
    pgFillBox(242, 256, 478, 270, WHITE);
    pgFillBox(244, 258, 476, 268, BLACK);
    pgFillBox(246, 260, 246+(228*part)/whole, 266, WHITE);
  }

  pgScreenFlip();
#endif
}


/******************************************************************
 * UpdateScrollbars
 *
 * Repaints scrollbars
 *****************************************************************/
void UpdateScrollbars (void)
{
  // Outlines
  pgDrawFrame(0, 271-8, 479-9, 271, WHITE);
  pgDrawFrame(479-8, 0, 479, 271-9, WHITE);

  // Track
  pgFillBox(1, 271-7, 479-10, 271-1, BLACK);
  pgFillBox(479-7, 1, 479-1, 271-10, BLACK);

  // Button
  pgFillBox(hzSl.x+2, 271-6, hzSl.x+hzSl.w, 271-2, WHITE);
  pgFillBox(479-6, vtSl.y+2, 479-2, vtSl.y+vtSl.h, WHITE);
}


/******************************************************************
 * PositionScrollbars
 *
 * re-computes scrollbar rectangles and re-paints scrollbars
 *****************************************************************/
void PositionScrollbars ()
{
  if (pspFB.remote.w == 0)
	hzSl.x = 0;
  else
	hzSl.x = (((u32)viewportX * hzSl.w) / pspFB.remote.w);

  if (pspFB.remote.h == 0)
	vtSl.y = 0;
  else
	vtSl.y = ((u32)(viewportY * vtSl.h) / pspFB.remote.h);
	
}

/******************************************************************
 * ProcessPenEvent
 *****************************************************************/
void ProcessPenEvent ()
{
//	unsigned long penTime = 0;
	unsigned short serverX, serverY;

        if(mouseX < 0) {
          viewportX += mouseX;
          vpUpdate=1;
        }
        if(mouseX > pspFB.viewable.w) {
          viewportX += mouseX - pspFB.viewable.w;
          vpUpdate=1;
        }

        if(mouseY < 0) {
          viewportY += mouseY;
          vpUpdate=1;
        }
        if(mouseY > pspFB.viewable.h) {
          viewportY += mouseY - pspFB.viewable.h;
          vpUpdate=1;
        }

        if(vpUpdate) {
          vpUpdate=0;
          PositionViewport ();
          PositionScrollbars ();
        }

        if(mouseX < 0)
          mouseX = 0;
        if(mouseX > pspFB.viewable.w)
          mouseX = pspFB.viewable.w;

        if(mouseY < 0)
          mouseY = 0;
        if(mouseY > pspFB.viewable.h)
          mouseY = pspFB.viewable.h;

	serverX = mouseX + pspFB.viewable.x;
	serverY = mouseY + pspFB.viewable.y;

	if (lastX == 0)
		lastX = serverX;
	if (lastY == 0)
		lastY = serverY;

        if(now_pad & PSP_CTRL_CROSS) currentMouseButton |= rfbButton1Mask;
        else currentMouseButton &= ~rfbButton1Mask;
        if(now_pad & PSP_CTRL_CIRCLE) currentMouseButton |= rfbButton2Mask;
        else currentMouseButton &= ~rfbButton2Mask;
        if(now_pad & PSP_CTRL_SQUARE) currentMouseButton |= rfbButton3Mask;
        else currentMouseButton &= ~rfbButton3Mask;
        if(now_pad & PSP_CTRL_LTRIGGER) currentMouseButton |= rfbButton6Mask;
        else currentMouseButton &= ~rfbButton6Mask;
        if(now_pad & PSP_CTRL_RTRIGGER) currentMouseButton |= rfbButton7Mask;
        else currentMouseButton &= ~rfbButton7Mask;

	// sync the intial mouse position first
	(void)SendPointerEvent (serverX, serverY, currentMouseButton);

	lastX = serverX;
	lastY = serverY;

	sendUpdateRequest = true;

	// Allow the masked off key to now send keyDownEvents.
}

/******************************************************************
 * StartApplication
 *
 * Get preferences, open (or create) app database
 *****************************************************************/
void vncStart (void)
{
  pspFB.viewable.x = pspFB.viewable.y = 0;
  pspFB.viewable.h = VIEWPORT_H - SCROLLBAR_SIZE;
  pspFB.viewable.w = VIEWPORT_W - SCROLLBAR_SIZE;
  pspFB.virtual.x = pspFB.virtual.y = 0;
  pspFB.zoomarea_ena = false;

  pspFB.virtual.h = VIEWPORT_H;
  pspFB.virtual.w = VIEWPORT_W;
  virtualBounds.w = VIEWPORT_W;
  virtualBounds.h = VIEWPORT_H;

  pspFB.remote.x = pspFB.remote.y = pspFB.remote.h = pspFB.remote.w = 0;

  pen_zooming = false;
  
  vncInitFormat();
}



/******************************************************************
 * EventLoop
 *
 * The main event loop
 *****************************************************************/
void vncLoop (void)
{
//  Word err;
//  long eventTimeout;
//  Bool passSystem = false;
  int penUpdate = 0, padmode = 0;
  fd_set fds;
  struct timeval to = { 0, 0 };

  while(rfbsock) {
        readpad(1);

        padmode = UpdateViewport(padmode, 1);

        switch(padmode) {
        case 0:
          if(new_pad & PSP_CTRL_START) padmode = 3;
          if(new_pad & PSP_CTRL_SELECT) padmode = 1;
          if(new_pad & PSP_CTRL_TRIANGLE) padmode = 2;
          if(old_pad != now_pad) penUpdate = 1;
          if(now_pad & PSP_CTRL_DOWN) { mouseY += (mouseDS+1)*2; penUpdate = 1; }
          if(now_pad & PSP_CTRL_UP) { mouseY -= (mouseDS+1)*2; penUpdate = 1; }
          if(now_pad & PSP_CTRL_RIGHT) { mouseX += (mouseDS+1)*2; penUpdate = 1; }
          if(now_pad & PSP_CTRL_LEFT) { mouseX -= (mouseDS+1)*2; penUpdate = 1; }
          if(Ly > UPPER_THRESHOLD) { mouseY += (Ly - UPPER_THRESHOLD)/(6-mouseAS); penUpdate = 1; }
          if(Ly < LOWER_THRESHOLD) { mouseY += (Ly - LOWER_THRESHOLD)/(6-mouseAS); penUpdate = 1; }
          if(Lx > UPPER_THRESHOLD) { mouseX += (Lx - UPPER_THRESHOLD)/(6-mouseAS); penUpdate = 1; }
          if(Lx < LOWER_THRESHOLD) { mouseX += (Lx - LOWER_THRESHOLD)/(6-mouseAS); penUpdate = 1; }
          if((new_pad & 0xf300) != (old_pad & 0xf300)) penUpdate = 1;
          break;
        case 2:
          if(new_pad & PSP_CTRL_TRIANGLE) padmode = 0;
          if(new_pad & PSP_CTRL_RTRIGGER) SendKeyEvent(0xff53, true);
          else if(old_pad & PSP_CTRL_RTRIGGER) SendKeyEvent(0xff53, false);
          if(new_pad & PSP_CTRL_LTRIGGER) SendKeyEvent(0xff51, true);
          else if(old_pad & PSP_CTRL_LTRIGGER) SendKeyEvent(0xff51, false);
          break;
        case 1:
          if(new_pad & (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_SELECT)) padmode = 0;
          if(new_pad & PSP_CTRL_START) padmode = 3;
          if(new_pad & PSP_CTRL_TRIANGLE) padmode = 2;
          if(now_pad & PSP_CTRL_DOWN) { viewportY += (mouseDS+1)*2; vpUpdate = 1; }
          if(now_pad & PSP_CTRL_UP) { viewportY -= (mouseDS+1)*2; vpUpdate = 1; }
          if(now_pad & PSP_CTRL_RIGHT) { viewportX += (mouseDS+1)*2; vpUpdate = 1; }
          if(now_pad & PSP_CTRL_LEFT) { viewportX -= (mouseDS+1)*2; vpUpdate = 1; }
          if(Ly > UPPER_THRESHOLD) { viewportY += (Ly - UPPER_THRESHOLD)/(6-mouseAS); vpUpdate = 1; }
          if(Ly < LOWER_THRESHOLD) { viewportY += (Ly - LOWER_THRESHOLD)/(6-mouseAS); vpUpdate = 1; }
          if(Lx > UPPER_THRESHOLD) { viewportX += (Lx - UPPER_THRESHOLD)/(6-mouseAS); vpUpdate = 1; }
          if(Lx < LOWER_THRESHOLD) { viewportX += (Lx - LOWER_THRESHOLD)/(6-mouseAS); vpUpdate = 1; }
          break;
        case 3: {
            u16 newkey;
            if(new_pad & PSP_CTRL_DOWN) SendKeyEvent(0xff54, true);
            else if(old_pad & PSP_CTRL_DOWN) SendKeyEvent(0xff54, false);
            if(new_pad & PSP_CTRL_UP) SendKeyEvent(0xff52, true);
            else if(old_pad & PSP_CTRL_UP) SendKeyEvent(0xff52, false);
            if(new_pad & PSP_CTRL_RIGHT) SendKeyEvent(0xff53, true);
            else if(old_pad & PSP_CTRL_RIGHT) SendKeyEvent(0xff53, false);
            if(new_pad & PSP_CTRL_LEFT) SendKeyEvent(0xff51, true);
            else if(old_pad & PSP_CTRL_LEFT) SendKeyEvent(0xff51, false);
            newkey = 0;
            if(now_pad & PSP_CTRL_CROSS) newkey |= 1;
            if(now_pad & PSP_CTRL_CIRCLE) newkey |= 2;
            if(now_pad & PSP_CTRL_SQUARE) newkey |= 4;
            if(now_pad & PSP_CTRL_TRIANGLE) newkey |= 8;
            if(now_pad & PSP_CTRL_LTRIGGER) newkey |= 16;
            if(now_pad & PSP_CTRL_RTRIGGER) newkey |= 32;
            if(now_pad & PSP_CTRL_SELECT) newkey |= 64;
            if(now_pad & PSP_CTRL_START) newkey |= 128;
            if(Lx < 0x9f && Lx > 0x5f) Lx=127;
            if(Ly < 0x9f && Ly > 0x5f) Ly=127;
            SendPointerEvent (112+Lx, 8+Ly, newkey);
            break;
          }
        }

        if (new_pad & PSP_CTRL_HOME) {
          int disconnect = 0;
          vncHomeMenu(&padmode, &disconnect);
          if(disconnect) {
            ShutdownConnection();
          }
        }

        if(penUpdate) {
          ProcessPenEvent();
          penUpdate = 0;
        }

        new_pad = 0;

        if(vpUpdate) {
          vpUpdate=0;
          PositionViewport ();
          PositionScrollbars ();
        }

	if (rfbsock <= 0)
	  break;

	if (updateViewportSize)
	{

	  viewportX = pspFB.virtual.x;
	  viewportY = pspFB.virtual.y;

	  PositionScrollbars();

		(void)SendFramebufferUpdateRequest((int)pspFB.virtual.x,
		(int)pspFB.virtual.y,
		(int)pspFB.virtual.w,
		(int)pspFB.virtual.h,
		false);

		PositionScrollbars();
		updateViewportSize = false;
		sendUpdateRequest = false;
	}

	if (sendUpdateRequest) {
	  if (!SendIncrementalFramebufferUpdateRequest())
	    ShutdownConnection();
	  sendUpdateRequest = false;
	}
	
        FD_ZERO(&fds);
        FD_SET(rfbsock, &fds);

	if (sceNetInetSelect (rfbsock+1, &fds, NULL, NULL, &to) < 0) {
	  MsgPrintf (0, "select returns error %i\n", sceNetInetGetErrno());
	  ShutdownConnection ();
	  return;
	}

	if(bQuit) {
	  ShutdownConnection ();
	  return;
	}

	if (FD_ISSET(rfbsock, &fds)) {
	  if (!HandleRFBServerMessage()) {
		ShutdownConnection ();
		PostProgressMessage ("Connection Lost!", 0, 0);
	  }
          UpdateViewport (padmode, 0);
	}
  };
}

int reconnectable = 0;
void vncDisconnect() {
  if(rfbsock) reconnectable = 1;
  ShutdownConnection();
}

void vncConnect() {
  if(reconnectable) OpenConnection();
}

void vncMain (DB_ServerConnection *srv) {
  if(!srv) return;
  memcpy(&server, srv, sizeof(DB_ServerConnection));

  memset(screenData, 0, sizeof(screenData));

  vncStart();
  OpenConnection();
  vncLoop();

  return;
}
