///////////////////////////////////////////////////////////////////////// // $Id: rfb.cc,v 1.14 2001-12-13 18:36:29 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 #include #include "rfb.h" #else #include #include #include #include #include #include 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); } } // ::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) { }