///////////////////////////////////////////////////////////////////////// // $Id$ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2000 Psyon.Org! // // Donald Becker // http://www.psyon.org // // Copyright (C) 2001-2013 The Bochs Project // // 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // RFB still to do : // - properly handle SetPixelFormat, including big/little-endian flag // - depth > 8bpp support // - optional compression support // Define BX_PLUGGABLE in files that can be compiled into plugins. For // platforms that require a special tag on exported symbols, BX_PLUGGABLE // is used to know when we are exporting symbols and when we are importing. #define BX_PLUGGABLE #include "param_names.h" #include "iodev.h" #include "keymap.h" #if BX_WITH_RFB #include "icon_bochs.h" #include "font/vga.bitmap.h" #if BX_WITH_SDL && !BX_PLUGINS extern unsigned char sdl_font8x8[256][8]; #else #include "sdl.h" // 8x8 font for status text #endif #include "rfb.h" #include "rfbkeys.h" class bx_rfb_gui_c : public bx_gui_c { public: bx_rfb_gui_c (void) {} DECLARE_GUI_VIRTUAL_METHODS() DECLARE_GUI_NEW_VIRTUAL_METHODS() void get_capabilities(Bit16u *xres, Bit16u *yres, Bit16u *bpp); void statusbar_setitem_specific(int element, bx_bool active, bx_bool w); virtual void set_mouse_mode_absxy(bx_bool mode); #if BX_SHOW_IPS void show_ips(Bit32u ips_count); #endif }; // declare one instance of the gui object and call macro to insert the // plugin code static bx_rfb_gui_c *theGui = NULL; IMPLEMENT_GUI_PLUGIN_CODE(rfb) #define LOG_THIS theGui-> #ifdef WIN32 #include #include #else #include #include #include #include #ifndef __QNXNTO__ #include #else #include #endif #include typedef int SOCKET; #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif #endif static bx_bool keep_alive; static bx_bool client_connected; static bx_bool desktop_resizable; #if BX_SHOW_IPS static bx_bool rfbHideIPS = 0; static bx_bool rfbIPSupdate = 0; static char rfbIPStext[40]; #endif static unsigned short rfbPort; // Headerbar stuff static unsigned rfbBitmapCount = 0; static struct _rfbBitmaps { char *bmap; unsigned xdim; unsigned ydim; } rfbBitmaps[BX_MAX_PIXMAPS]; static unsigned rfbHeaderbarBitmapCount = 0; static struct _rfbHeaderbarBitmaps { unsigned int index; unsigned int xorigin; unsigned int yorigin; unsigned int alignment; void (*f)(void); } rfbHeaderbarBitmaps[BX_MAX_HEADERBAR_ENTRIES]; //Keyboard stuff #define KEYBOARD 1 #define MOUSE 0 #define MAX_KEY_EVENTS 512 static struct _rfbKeyboardEvent { bx_bool type; int key; int down; int x; int y; } rfbKeyboardEvent[MAX_KEY_EVENTS]; static unsigned long rfbKeyboardEvents = 0; static bx_bool bKeyboardInUse = 0; // Misc Stuff static struct _rfbUpdateRegion { unsigned int x; unsigned int y; unsigned int width; unsigned int height; bx_bool updated; } rfbUpdateRegion; #define BX_RFB_MAX_XDIM 1280 #define BX_RFB_MAX_YDIM 1024 #define BX_RFB_DEF_XDIM 720 #define BX_RFB_DEF_YDIM 480 const unsigned char status_led_green = 0x38; const unsigned char status_gray_text = 0xa4; const unsigned char status_led_red = 0x07; const unsigned char headerbar_bg = 0xff; const unsigned char headerbar_fg = 0x00; static char *rfbScreen; static char rfbPalette[256]; static unsigned rfbWindowX, rfbWindowY; static unsigned rfbDimensionX, rfbDimensionY; 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 bx_bool rfbMouseModeAbsXY = 0; static unsigned rfbStatusbarY = 18; static unsigned rfbStatusitemPos[12] = { 0, 170, 210, 250, 290, 330, 370, 410, 450, 490, 530, 570 }; static bx_bool rfbStatusitemActive[12]; static unsigned int text_rows = 25, text_cols = 80; static unsigned int font_height = 16, font_width = 8; static SOCKET sGlobal; static Bit32u clientEncodingsCount = 0; static Bit32u *clientEncodings = NULL; #ifdef WIN32 bool StopWinsock(); #endif void rfbStartThread(); 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 fg, char bg, bx_bool update_client); void DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char fg, char bg, bx_bool gfxchar); void UpdateScreen(unsigned char *newBits, int x, int y, int width, int height, bx_bool update_client); void SendUpdate(int x, int y, int width, int height, Bit32u encoding); void rfbAddUpdateRegion(unsigned x0, unsigned y0, unsigned w, unsigned h); void rfbSetStatusText(int element, const char *text, bx_bool active, bx_bool w = 0); static Bit32u convertStringToRfbKey(const char *string); void rfbKeyPressed(Bit32u key, int press_release); void rfbMouseMove(int x, int y, int bmask); #if BX_SHOW_IPS && defined(WIN32) DWORD WINAPI rfbShowIPSthread(LPVOID); #endif static const rfbPixelFormat BGR233Format = { 8, 8, 1, 1, 7, 7, 3, 0, 3, 6 }; // VNCViewer code to be replaced #define PF_EQ(x,y) ((x.bitsPerPixel == y.bitsPerPixel) && (x.depth == y.depth) && (x.trueColourFlag == y.trueColourFlag) && ((x.bigEndianFlag == y.bigEndianFlag) || (x.bitsPerPixel == 8)) && (!x.trueColourFlag || ((x.redMax == y.redMax) && (x.greenMax == y.greenMax) && (x.blueMax == y.blueMax) && (x.redShift == y.redShift) && (x.greenShift == y.greenShift) && (x.blueShift == y.blueShift)))) // RFB implementation of the bx_gui_c methods // ::SPECIFIC_INIT() // // Called from gui.cc, once upon program startup, to allow for the // specific GUI code (X11, Win32, ...) to be initialized. // // argc, argv: used to pass display library specific options to the init code // (X11 options, Win32 options,...) // // 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_rfb_gui_c::specific_init(int argc, char **argv, unsigned headerbar_y) { unsigned char fc, vc; int i, timeout = 30; put("RFB"); UNUSED(bochs_icon_bits); rfbHeaderbarY = headerbar_y; rfbDimensionX = BX_RFB_DEF_XDIM; rfbDimensionY = BX_RFB_DEF_YDIM; rfbWindowX = rfbDimensionX; rfbWindowY = rfbDimensionY + rfbHeaderbarY + rfbStatusbarY; rfbTileX = x_tilesize; rfbTileY = y_tilesize; for (i = 0; i < 256; i++) { for (int j = 0; j < 16; j++) { vc = bx_vgafont[i].data[j]; fc = 0; for (int b = 0; b < 8; b++) { fc |= (vc & 0x01) << (7 - b); vc >>= 1; } vga_charmap[i * 32 + j] = fc; } } // parse rfb specific options if (argc > 1) { for (i = 1; i < argc; i++) { if (!strncmp(argv[i], "timeout=", 8)) { timeout = atoi(&argv[i][8]); if (timeout < 0) { BX_PANIC(("invalid timeout value: %d", timeout)); } else { BX_INFO(("connection timeout set to %d", timeout)); } #if BX_SHOW_IPS } else if (!strcmp(argv[i], "hideIPS")) { BX_INFO(("hide IPS display in status bar")); rfbHideIPS = 1; #endif } else { BX_PANIC(("Unknown rfb option '%s'", argv[i])); } } } if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) { BX_ERROR(("private_colormap option ignored.")); } rfbScreen = (char *)malloc(rfbWindowX * rfbWindowY); memset(&rfbPalette, 0, sizeof(rfbPalette)); rfbPalette[7] = (char)0xAD; rfbPalette[63] = (char)0xFF; rfbUpdateRegion.x = rfbWindowX; rfbUpdateRegion.y = rfbWindowY; rfbUpdateRegion.width = 0; rfbUpdateRegion.height = 0; rfbUpdateRegion.updated = 0; clientEncodingsCount=0; clientEncodings=NULL; keep_alive = 1; client_connected = 0; desktop_resizable = 0; rfbStartThread(); #ifdef WIN32 Sleep(1000); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); #endif // load keymap for rfb if (SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { bx_keymap.loadKeymap(convertStringToRfbKey); } // the ask menu doesn't work on the client side io->set_log_action(LOGLEV_PANIC, ACT_FATAL); if (timeout > 0) { while ((!client_connected) && (timeout--)) { fprintf(stderr, "Bochs RFB server waiting for client: %2d\r", timeout+1); #ifdef WIN32 Sleep(1000); #else sleep(1); #endif } if ((timeout < 0) && (!client_connected)) { BX_PANIC(("timeout! no client present")); } else { fprintf(stderr, "RFB client connected \r"); } } #if BX_SHOW_IPS && defined(WIN32) if (!rfbHideIPS) { DWORD threadID; CreateThread(NULL, 0, rfbShowIPSthread, NULL, 0, &threadID); } #endif new_gfx_api = 1; dialog_caps = 0; } // ::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_rfb_gui_c::handle_events(void) { while (bKeyboardInUse) ; bKeyboardInUse = 1; if (rfbKeyboardEvents > 0) { for (unsigned 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 = 0; if (rfbUpdateRegion.updated) { SendUpdate(rfbUpdateRegion.x, rfbUpdateRegion.y, rfbUpdateRegion.width, rfbUpdateRegion.height, rfbEncodingRaw); rfbUpdateRegion.x = rfbWindowX; rfbUpdateRegion.y = rfbWindowY; rfbUpdateRegion.width = 0; rfbUpdateRegion.height = 0; rfbUpdateRegion.updated = 0; } #if BX_SHOW_IPS if (rfbIPSupdate) { rfbIPSupdate = 0; rfbSetStatusText(0, rfbIPStext, 1); } #endif } // ::FLUSH() // // Called periodically, requesting that the gui code flush all pending // screen update requests. void bx_rfb_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_rfb_gui_c::clear_screen(void) { memset(&rfbScreen[rfbWindowX * rfbHeaderbarY], 0, rfbWindowX * rfbDimensionY); } // ::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 tm_info->line_offset*text_rows // bytes long. Each character consists of 2 bytes. The first by is // the character value, the second is the attribute byte. // // cursor_x: new x location of cursor // cursor_y: new y location of cursor // tm_info: this structure contains information for additional // features in text mode (cursor shape, line offset,...) void bx_rfb_gui_c::text_update(Bit8u *old_text, Bit8u *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t *tm_info) { Bit8u *old_line, *new_line; Bit8u cAttr, cChar; unsigned int curs, hchars, offset, rows, x, y, xc, yc, i; bx_bool force_update = 0, gfxchar, blink_state, blink_mode; char text_palette[16]; char fgcolor, bgcolor; for (i = 0; i < 16; i++) { text_palette[i] = rfbPalette[tm_info->actl_palette[i]]; } blink_mode = (tm_info->blink_flags & BX_TEXT_BLINK_MODE) > 0; blink_state = (tm_info->blink_flags & BX_TEXT_BLINK_STATE) > 0; if (blink_mode) { if (tm_info->blink_flags & BX_TEXT_BLINK_TOGGLE) force_update = 1; } if (charmap_updated) { force_update = 1; charmap_updated = 0; } // first invalidate character at previous and new cursor location if ((rfbCursorY < text_rows) && (rfbCursorX < text_cols)) { curs = rfbCursorY * tm_info->line_offset + rfbCursorX * 2; old_text[curs] = ~new_text[curs]; } if ((tm_info->cs_start <= tm_info->cs_end) && (tm_info->cs_start < font_height) && (cursor_y < text_rows) && (cursor_x < text_cols)) { curs = cursor_y * tm_info->line_offset + cursor_x * 2; old_text[curs] = ~new_text[curs]; } else { curs = 0xffff; } rows = text_rows; y = 0; do { hchars = text_cols; new_line = new_text; old_line = old_text; offset = y * tm_info->line_offset; yc = y * font_height + rfbHeaderbarY; x = 0; do { if (force_update || (old_text[0] != new_text[0]) || (old_text[1] != new_text[1])) { cChar = new_text[0]; if (blink_mode) { cAttr = new_text[1] & 0x7F; if (!blink_state && (new_text[1] & 0x80)) cAttr = (cAttr & 0x70) | (cAttr >> 4); } else { cAttr = new_text[1]; } fgcolor = text_palette[cAttr & 0x0F]; bgcolor = text_palette[cAttr >> 4]; gfxchar = tm_info->line_graphics && ((cChar & 0xE0) == 0xC0); xc = x * font_width; DrawChar(xc, yc, font_width, font_height, 0, (char *)&vga_charmap[cChar<<5], fgcolor, bgcolor, gfxchar); rfbAddUpdateRegion(xc, yc, font_width, font_height); if (offset == curs) { cAttr = ((cAttr >> 4) & 0xF) + ((cAttr & 0xF) << 4); DrawChar(xc, yc + tm_info->cs_start, font_width, tm_info->cs_end - tm_info->cs_start + 1, tm_info->cs_start, (char *)&vga_charmap[cChar<<5], bgcolor, fgcolor, gfxchar); } } x++; new_text += 2; old_text += 2; offset += 2; } while (--hchars); y++; new_text = new_line + tm_info->line_offset; old_text = old_line + tm_info->line_offset; } while (--rows); rfbCursorX = cursor_x; rfbCursorY = cursor_y; } int bx_rfb_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes) { return 0; } int bx_rfb_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) bx_bool bx_rfb_gui_c::palette_change(Bit8u index, Bit8u red, Bit8u green, Bit8u blue) { rfbPalette[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 'x_tilesize' & 'y_tilesize' members. // 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_rfb_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0) { unsigned c, i, h, y; switch (guest_bpp) { case 8: /* 8 bpp */ y = y0 + rfbHeaderbarY; if ((y0 + rfbTileY) > rfbDimensionY) { h = rfbDimensionY - y0; } else { h = rfbTileY; } for (i = 0; i < h; i++) { for (c = 0; c < rfbTileX; c++) { tile[(i * rfbTileX) + c] = rfbPalette[tile[(i * rfbTileX) + c]]; } memcpy(&rfbScreen[y * rfbWindowX + x0], &tile[i * rfbTileX], rfbTileX); y++; } break; default: BX_PANIC(("%u bpp modes handled by new graphics API", guest_bpp)); return; } rfbAddUpdateRegion(x0, y0 + rfbHeaderbarY, rfbTileX, h); } // ::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(). // fheight: new VGA character height in text mode // fwidth : new VGA character width in text mode // bpp : bits per pixel in graphics mode void bx_rfb_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp) { if (bpp == 8) { guest_bpp = bpp; } else { BX_PANIC(("%d bpp graphics mode not supported yet", bpp)); } guest_textmode = (fheight > 0); guest_xres = x; guest_yres = y; if (guest_textmode) { font_height = fheight; font_width = fwidth; text_cols = x / fwidth; text_rows = y / fheight; } if ((x != rfbDimensionX) || (y != rfbDimensionY)) { if (desktop_resizable) { if ((x > BX_RFB_MAX_XDIM) || (y > BX_RFB_MAX_YDIM)) { BX_PANIC(("dimension_update(): RFB doesn't support graphics mode %dx%d", x, y)); } rfbDimensionX = x; rfbDimensionY = y; rfbWindowX = rfbDimensionX; rfbWindowY = rfbDimensionY + rfbHeaderbarY + rfbStatusbarY; rfbScreen = (char *)realloc(rfbScreen, rfbWindowX * rfbWindowY); SendUpdate(0, 0, rfbWindowX, rfbWindowY, rfbEncodingDesktopSize); bx_gui->show_headerbar(); } else { if ((x > BX_RFB_DEF_XDIM) || (y > BX_RFB_DEF_YDIM)) { BX_PANIC(("dimension_update(): RFB doesn't support graphics mode %dx%d", x, y)); } clear_screen(); SendUpdate(0, rfbHeaderbarY, rfbDimensionX, rfbDimensionY, rfbEncodingRaw); rfbDimensionX = x; rfbDimensionY = 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_rfb_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim) { if (rfbBitmapCount >= BX_MAX_PIXMAPS) { BX_ERROR(("too many pixmaps.")); 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_rfb_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_rfb_gui_c::show_headerbar(void) { char *newBits, value; unsigned int i, xorigin, addr; newBits = (char *) malloc(rfbWindowX * rfbHeaderbarY); memset(newBits, 0, (rfbWindowX * rfbHeaderbarY)); DrawBitmap(0, 0, rfbWindowX, rfbHeaderbarY, newBits, headerbar_fg, headerbar_bg, 0); for (i = 0; i < rfbHeaderbarBitmapCount; i++) { if (rfbHeaderbarBitmaps[i].alignment == BX_GRAVITY_LEFT) { xorigin = rfbHeaderbarBitmaps[i].xorigin; } else { xorigin = rfbWindowX - rfbHeaderbarBitmaps[i].xorigin; } DrawBitmap(xorigin, 0, rfbBitmaps[rfbHeaderbarBitmaps[i].index].xdim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].ydim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].bmap, headerbar_fg, headerbar_bg, 0); } free(newBits); newBits = (char *) malloc(rfbWindowX * rfbStatusbarY / 8); memset(newBits, 0, (rfbWindowX * rfbStatusbarY / 8)); for (i = 1; i < 12; i++) { addr = rfbStatusitemPos[i] / 8; value = 1 << (rfbStatusitemPos[i] % 8); for (unsigned j = 1; j < rfbStatusbarY; j++) { newBits[(rfbWindowX * j / 8) + addr] = value; } } DrawBitmap(0, rfbWindowY - rfbStatusbarY, rfbWindowX, rfbStatusbarY, newBits, headerbar_fg, headerbar_bg, 0); free(newBits); for (i = 1; i <= statusitem_count; i++) { rfbSetStatusText(i, statusitem[i - 1].text, rfbStatusitemActive[i]); } } // ::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_rfb_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id) { unsigned int xorigin; if (bmap_id == rfbHeaderbarBitmaps[hbar_id].index) return; rfbHeaderbarBitmaps[hbar_id].index = bmap_id; if (rfbHeaderbarBitmaps[hbar_id].alignment == BX_GRAVITY_LEFT) { xorigin = rfbHeaderbarBitmaps[hbar_id].xorigin; } else { xorigin = rfbWindowX - rfbHeaderbarBitmaps[hbar_id].xorigin; } DrawBitmap(xorigin, 0, rfbBitmaps[rfbHeaderbarBitmaps[hbar_id].index].xdim, rfbBitmaps[rfbHeaderbarBitmaps[hbar_id].index].ydim, rfbBitmaps[rfbHeaderbarBitmaps[hbar_id].index].bmap, headerbar_fg, headerbar_bg, 1); } // ::EXIT() // // Called before bochs terminates, to allow for a graceful // exit from the native GUI mechanism. void bx_rfb_gui_c::exit(void) { unsigned int i; keep_alive = 0; #ifdef WIN32 StopWinsock(); #endif free(rfbScreen); for(i = 0; i < rfbBitmapCount; i++) { free(rfbBitmaps[i].bmap); } // Clear supported encodings if (clientEncodings != NULL) { delete [] clientEncodings; clientEncodingsCount = 0; } BX_DEBUG(("bx_rfb_gui_c::exit()")); } void bx_rfb_gui_c::mouse_enabled_changed_specific(bx_bool val) { } bx_svga_tileinfo_t *bx_rfb_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info) { info->bpp = 8; info->pitch = rfbWindowX; info->red_shift = 3; info->green_shift = 6; info->blue_shift = 8; info->red_mask = 0x07; info->green_mask = 0x38; info->blue_mask = 0xc0; info->is_indexed = 0; #ifdef BX_LITTLE_ENDIAN info->is_little_endian = 1; #else info->is_little_endian = 0; #endif return info; } Bit8u *bx_rfb_gui_c::graphics_tile_get(unsigned x0, unsigned y0, unsigned *w, unsigned *h) { if (x0 + rfbTileX > rfbDimensionX) { *w = rfbDimensionX - x0; } else { *w = rfbTileX; } if (y0 + rfbTileY > rfbDimensionY) { *h = rfbDimensionY - y0; } else { *h = rfbTileY; } return (Bit8u *)rfbScreen + (rfbHeaderbarY + y0) * rfbWindowX + x0; } void bx_rfb_gui_c::graphics_tile_update_in_place(unsigned x0, unsigned y0, unsigned w, unsigned h) { rfbAddUpdateRegion(x0, y0 + rfbHeaderbarY, w, h); } void bx_rfb_gui_c::get_capabilities(Bit16u *xres, Bit16u *yres, Bit16u *bpp) { if (desktop_resizable) { *xres = BX_RFB_MAX_XDIM; *yres = BX_RFB_MAX_YDIM; } else { *xres = BX_RFB_DEF_XDIM; *yres = BX_RFB_DEF_YDIM; } *bpp = 8; } void bx_rfb_gui_c::statusbar_setitem_specific(int element, bx_bool active, bx_bool w) { rfbSetStatusText(element+1, statusitem[element].text, active, w); } void bx_rfb_gui_c::set_mouse_mode_absxy(bx_bool mode) { rfbMouseModeAbsXY = mode; } #if BX_SHOW_IPS void bx_rfb_gui_c::show_ips(Bit32u ips_count) { if (!rfbIPSupdate && !rfbHideIPS) { ips_count /= 1000; sprintf(rfbIPStext, "IPS: %u.%3.3uM", ips_count / 1000, ips_count % 1000); rfbIPSupdate = 1; } } #endif // RFB specific functions #ifdef WIN32 bool InitWinsock() { WSADATA wsaData; if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return false; return true; } bool StopWinsock() { WSACleanup(); return true; } #endif void CDECL rfbServerThreadInit(void *indata) { SOCKET sServer; SOCKET sClient; struct sockaddr_in sai; unsigned int sai_size; int port_ok = 0; int one=1; #ifdef WIN32 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); if(!InitWinsock()) { BX_PANIC(("could not initialize winsock.")); goto end_of_thread; } #endif sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sServer == (SOCKET) -1) { BX_PANIC(("could not create socket.")); goto end_of_thread; } if (setsockopt(sServer, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(int)) == -1) { BX_PANIC(("could not set socket option.")); 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", BX_RFB_PORT_MIN, BX_RFB_PORT_MAX)); goto end_of_thread; } BX_INFO (("listening for connections on port %i", rfbPort)); sai_size = sizeof(sai); while (keep_alive) { sClient = accept(sServer, (struct sockaddr *)&sai, (socklen_t*)&sai_size); if(sClient != INVALID_SOCKET) { HandleRfbClient(sClient); sGlobal = INVALID_SOCKET; close(sClient); } else { close(sClient); } } end_of_thread: #ifdef WIN32 StopWinsock(); #endif return; } void rfbStartThread() { #ifdef WIN32 _beginthread(rfbServerThreadInit, 0, NULL); #else pthread_t thread; pthread_create(&thread, NULL, (void *(*)(void *))&rfbServerThreadInit, NULL); #endif } void HandleRfbClient(SOCKET sClient) { char rfbName[] = "Bochs-RFB"; rfbProtocolVersionMessage pv; int one = 1; U32 auth; rfbClientInitMessage cim; rfbServerInitMessage sim; bx_bool mouse_toggle = 0; setsockopt(sClient, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one)); BX_INFO(("accepted client connection.")); snprintf(pv, rfbProtocolVersionMessageSize + 1, rfbProtocolVersionFormat, rfbServerProtocolMajorVersion, rfbServerProtocolMinorVersion); if(WriteExact(sClient, pv, rfbProtocolVersionMessageSize) < 0) { BX_ERROR(("could not send protocol version.")); return; } if(ReadExact(sClient, pv, rfbProtocolVersionMessageSize) < 0) { BX_ERROR(("could not receive client protocol version.")); return; } pv[rfbProtocolVersionMessageSize-1]=0; // Drop last character BX_INFO(("Client protocol version is '%s'", pv)); // FIXME should check for version number auth = htonl(rfbSecurityNone); if(WriteExact(sClient, (char *)&auth, sizeof(auth)) < 0) { BX_ERROR(("could not send authorization method.")); return; } if(ReadExact(sClient, (char *)&cim, rfbClientInitMessageSize) < 0) { BX_ERROR(("could not receive client initialization message.")); return; } sim.framebufferWidth = htons((short)rfbWindowX); sim.framebufferHeight = htons((short)rfbWindowY); sim.serverPixelFormat = BGR233Format; sim.serverPixelFormat.redMax = htons(sim.serverPixelFormat.redMax); sim.serverPixelFormat.greenMax = htons(sim.serverPixelFormat.greenMax); sim.serverPixelFormat.blueMax = htons(sim.serverPixelFormat.blueMax); sim.nameLength = strlen(rfbName); sim.nameLength = htonl(sim.nameLength); if(WriteExact(sClient, (char *)&sim, rfbServerInitMessageSize) < 0) { BX_ERROR(("could send server initialization message.")); return; } if(WriteExact(sClient, rfbName, strlen(rfbName)) < 0) { BX_ERROR (("could not send server name.")); return; } client_connected = 1; sGlobal = sClient; while (keep_alive) { U8 msgType; int n; if ((n = recv(sClient, (char *)&msgType, 1, MSG_PEEK)) <= 0) { if (n == 0) { // client closed connection client_connected = 0; } else { if (errno == EINTR) continue; BX_ERROR(("error receiving data.")); } return; } switch (msgType) { case rfbSetPixelFormat: { rfbSetPixelFormatMessage spf; ReadExact(sClient, (char *)&spf, sizeof(rfbSetPixelFormatMessage)); spf.pixelFormat.trueColourFlag = (spf.pixelFormat.trueColourFlag ? 1 : 0); spf.pixelFormat.bigEndianFlag = (spf.pixelFormat.bigEndianFlag ? 1 : 0); spf.pixelFormat.redMax = ntohs(spf.pixelFormat.redMax); spf.pixelFormat.greenMax = ntohs(spf.pixelFormat.greenMax); spf.pixelFormat.blueMax = ntohs(spf.pixelFormat.blueMax); if (!PF_EQ(spf.pixelFormat, BGR233Format)) { BX_ERROR(("client has wrong pixel format (%d %d %d %d %d %d %d %d %d %d)", spf.pixelFormat.bitsPerPixel,spf.pixelFormat.depth,spf.pixelFormat.bigEndianFlag, spf.pixelFormat.trueColourFlag,spf.pixelFormat.redMax,spf.pixelFormat.greenMax, spf.pixelFormat.blueMax,spf.pixelFormat.redShift,spf.pixelFormat.greenShift, spf.pixelFormat.blueShift)); //return; } break; } case rfbFixColourMapEntries: { rfbFixColourMapEntriesMessage fcme; ReadExact(sClient, (char *)&fcme, sizeof(rfbFixColourMapEntriesMessage)); break; } case rfbSetEncodings: { rfbSetEncodingsMessage se; Bit32u i; U32 enc; // free previously registered encodings if (clientEncodings != NULL) { delete [] clientEncodings; clientEncodingsCount = 0; } ReadExact(sClient, (char *)&se, sizeof(rfbSetEncodingsMessage)); // Alloc new clientEncodings clientEncodingsCount = ntohs(se.numberOfEncodings); clientEncodings = new Bit32u[clientEncodingsCount]; for (i = 0; i < clientEncodingsCount; i++) { if ((n = ReadExact(sClient, (char *)&enc, sizeof(U32))) <= 0) { if (n == 0) { // client closed connection client_connected = 0; } else { BX_ERROR(("error receiving data.")); } return; } clientEncodings[i]=ntohl(enc); } // print supported encodings BX_INFO(("rfbSetEncodings : client supported encodings:")); for (i = 0; i < clientEncodingsCount; i++) { Bit32u j; bx_bool found = 0; for (j=0; j < rfbEncodingsCount; j ++) { if (clientEncodings[i] == rfbEncodings[j].id) { BX_INFO(("%08x %s", rfbEncodings[j].id, rfbEncodings[j].name)); found=1; if (clientEncodings[i] == rfbEncodingDesktopSize) { desktop_resizable = 1; } break; } } if (!found) BX_INFO(("%08x Unknown", clientEncodings[i])); } break; } case rfbFramebufferUpdateRequest: { rfbFramebufferUpdateRequestMessage fur; ReadExact(sClient, (char *)&fur, sizeof(rfbFramebufferUpdateRequestMessage)); if(!fur.incremental) { rfbUpdateRegion.x = 0; rfbUpdateRegion.y = 0; rfbUpdateRegion.width = rfbWindowX; rfbUpdateRegion.height = rfbWindowY; rfbUpdateRegion.updated = 1; } //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 = 1; break; } case rfbKeyEvent: { rfbKeyEventMessage ke; ReadExact(sClient, (char *)&ke, sizeof(rfbKeyEventMessage)); ke.key = ntohl(ke.key); while (bKeyboardInUse) ; if ((ke.key == XK_Control_L) || (ke.key == XK_Control_R)) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_CTRL, ke.downFlag); } else if (ke.key == XK_Alt_L) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_ALT, ke.downFlag); } else if (ke.key == XK_F10) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_F10, ke.downFlag); } else if (ke.key == XK_F12) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_F12, ke.downFlag); } if (mouse_toggle) { bx_gui->toggle_mouse_enable(); } else { bKeyboardInUse = 1; if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break; rfbKeyboardEvent[rfbKeyboardEvents].type = KEYBOARD; rfbKeyboardEvent[rfbKeyboardEvents].key = ke.key; rfbKeyboardEvent[rfbKeyboardEvents].down = ke.downFlag; rfbKeyboardEvents++; bKeyboardInUse = 0; } break; } case rfbPointerEvent: { rfbPointerEventMessage pe; ReadExact(sClient, (char *)&pe, sizeof(rfbPointerEventMessage)); while (bKeyboardInUse) ; if (bx_gui->mouse_toggle_check(BX_MT_MBUTTON, (pe.buttonMask & 0x02) > 0)) { bx_gui->toggle_mouse_enable(); } else { bKeyboardInUse = 1; if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break; rfbKeyboardEvent[rfbKeyboardEvents].type = MOUSE; rfbKeyboardEvent[rfbKeyboardEvents].x = ntohs(pe.xPosition); rfbKeyboardEvent[rfbKeyboardEvents].y = ntohs(pe.yPosition); rfbKeyboardEvent[rfbKeyboardEvents].down = (pe.buttonMask & 0x01) | ((pe.buttonMask>>1) & 0x02) | ((pe.buttonMask<<1) & 0x04); rfbKeyboardEvents++; bKeyboardInUse = 0; } break; } case rfbClientCutText: { rfbClientCutTextMessage cct; ReadExact(sClient, (char *)&cct, sizeof(rfbClientCutTextMessage)); break; } } } } /* * 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) { while (len > 0) { int 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) { while (len > 0) { int n = send(sock, buf, len,0); if (n > 0) { buf += n; len -= n; } else { if (n == 0) BX_ERROR(("WriteExact: write returned 0?")); return n; } } return 1; } void DrawBitmap(int x, int y, int width, int height, char *bmap, char fgcolor, char bgcolor, bx_bool update_client) { unsigned char *newBits; newBits = (unsigned char *)malloc(width * height); memset(newBits, 0, (width * height)); for (int 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); free(newBits); } void DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char fgcolor, char bgcolor, bx_bool gfxchar) { static unsigned char newBits[9 * 32]; unsigned char mask; int bytes = width * height; for (int i = 0; i < bytes; i+=width) { mask = 0x80; for (int j = 0; j < width; j++) { if (mask > 0) { newBits[i + j] = (bmap[fonty] & mask) ? fgcolor : bgcolor; } else { if (gfxchar) { newBits[i + j] = (bmap[fonty] & 0x01) ? fgcolor : bgcolor; } else { newBits[i + j] = bgcolor; } } mask >>= 1; } fonty++; } UpdateScreen(newBits, x, y, width, height, 0); } void UpdateScreen(unsigned char *newBits, int x, int y, int width, int height, bx_bool update_client) { int i, x0, y0; x0 = x; y0 = y; if ((x + width - 1) >= rfbWindowX) { width = rfbWindowX - x + 1; } if ((y + height - 1) >= rfbWindowY) { height = rfbWindowY - y + 1; } for (i = 0; i < height; i++) { memcpy(&rfbScreen[y * rfbWindowX + x0], &newBits[i * width], width); y++; } if (update_client) { if(sGlobal == INVALID_SOCKET) return; rfbFramebufferUpdateMessage fum; rfbFramebufferUpdateRectHeader furh; fum.messageType = rfbFramebufferUpdate; fum.numberOfRectangles = htons(1); WriteExact(sGlobal, (char *)&fum, rfbFramebufferUpdateMessageSize); furh.r.xPosition = htons(x0); furh.r.yPosition = htons(y0); furh.r.width = htons((short)width); furh.r.height = htons((short)height); furh.r.encodingType = htonl(rfbEncodingRaw); WriteExact(sGlobal, (char *)&furh, rfbFramebufferUpdateRectHeaderSize); WriteExact(sGlobal, (char *)newBits, width * height); } } void SendUpdate(int x, int y, int width, int height, Bit32u encoding) { char *newBits; if(x < 0 || y < 0 || (x + width) > (int)rfbWindowX || (y + height) > (int)rfbWindowY) { BX_ERROR(("Dimensions out of bounds. x=%i y=%i w=%i h=%i", x, y, width, height)); } if(sGlobal != INVALID_SOCKET) { rfbFramebufferUpdateMessage fum; rfbFramebufferUpdateRectHeader furh; fum.messageType = rfbFramebufferUpdate; fum.numberOfRectangles = htons(1); furh.r.xPosition = htons(x); furh.r.yPosition = htons(y); furh.r.width = htons((short)width); furh.r.height = htons((short)height); furh.r.encodingType = htonl(encoding); WriteExact(sGlobal, (char *)&fum, rfbFramebufferUpdateMessageSize); WriteExact(sGlobal, (char *)&furh, rfbFramebufferUpdateRectHeaderSize); if (encoding == rfbEncodingRaw) { newBits = (char *)malloc(width * height); for(int i = 0; i < height; i++) { memcpy(&newBits[i * width], &rfbScreen[y * rfbWindowX + x], width); y++; } WriteExact(sGlobal, (char *)newBits, width * height); free(newBits); } } } void rfbAddUpdateRegion(unsigned x0, unsigned y0, unsigned w, unsigned h) { if (x0 < rfbUpdateRegion.x) rfbUpdateRegion.x = x0; if (y0 < rfbUpdateRegion.y) rfbUpdateRegion.y = y0; if ((y0 + h - rfbUpdateRegion.y) > rfbUpdateRegion.height) { rfbUpdateRegion.height = y0 + h - rfbUpdateRegion.y; } if ((x0 + w - rfbUpdateRegion.x) > rfbUpdateRegion.width) { rfbUpdateRegion.width = x0 + w - rfbUpdateRegion.x; } if ((rfbUpdateRegion.x + rfbUpdateRegion.width) > rfbWindowX) { rfbUpdateRegion.width = rfbWindowX - rfbUpdateRegion.x; } rfbUpdateRegion.updated = 1; } void rfbSetStatusText(int element, const char *text, bx_bool active, bx_bool w) { char *newBits; unsigned xleft, xsize, i, len; rfbStatusitemActive[element] = active; xleft = rfbStatusitemPos[element] + 2; xsize = rfbStatusitemPos[element + 1] - xleft - 1; newBits = (char *) malloc(((xsize / 8) + 1) * (rfbStatusbarY - 2)); memset(newBits, 0, ((xsize / 8) + 1) * (rfbStatusbarY - 2)); for (i = 0; i < (rfbStatusbarY - 2); i++) { newBits[((xsize / 8) + 1) * i] = 0; } unsigned char fgcolor = active ? headerbar_fg : status_gray_text; unsigned char bgcolor = 0; if (element > 0) { bgcolor = active ? (w ? status_led_red : status_led_green) : headerbar_bg; } else { bgcolor = headerbar_bg; } DrawBitmap(xleft, rfbWindowY - rfbStatusbarY + 1, xsize, rfbStatusbarY - 2, newBits, fgcolor, bgcolor, 0); free(newBits); len = ((element > 0) && (strlen(text) > 4)) ? 4 : strlen(text); for (i = 0; i < len; i++) { DrawChar(xleft + i * 8 + 2, rfbWindowY - rfbStatusbarY + 5, 8, 8, 0, (char *) &sdl_font8x8[(unsigned) text[i]][0], fgcolor, bgcolor, 0); } rfbAddUpdateRegion(xleft, rfbWindowY - rfbStatusbarY + 1, xsize, rfbStatusbarY - 2); } // function to convert key names into rfb key values. // This first try will be horribly inefficient, but it only has // to be done while loading a keymap. Once the simulation starts, // this function won't be called. static Bit32u convertStringToRfbKey(const char *string) { rfbKeyTabEntry *ptr; for (ptr = &rfb_keytable[0]; ptr->name != NULL; ptr++) { if (!strcmp(string, ptr->name)) return ptr->value; } return BX_KEYMAP_UNKNOWN; } static Bit32u rfb_ascii_to_key_event[0x5f] = { // !"#$%&' BX_KEY_SPACE, BX_KEY_1, BX_KEY_SINGLE_QUOTE, BX_KEY_3, BX_KEY_4, BX_KEY_5, BX_KEY_7, BX_KEY_SINGLE_QUOTE, // ()*+,-./ BX_KEY_9, BX_KEY_0, BX_KEY_8, BX_KEY_EQUALS, BX_KEY_COMMA, BX_KEY_MINUS, BX_KEY_PERIOD, BX_KEY_SLASH, // 01234567 BX_KEY_0, BX_KEY_1, BX_KEY_2, BX_KEY_3, BX_KEY_4, BX_KEY_5, BX_KEY_6, BX_KEY_7, // 89:;<=>? BX_KEY_8, BX_KEY_9, BX_KEY_SEMICOLON, BX_KEY_SEMICOLON, BX_KEY_COMMA, BX_KEY_EQUALS, BX_KEY_PERIOD, BX_KEY_SLASH, // @ABCDEFG BX_KEY_2, BX_KEY_A, BX_KEY_B, BX_KEY_C, BX_KEY_D, BX_KEY_E, BX_KEY_F, BX_KEY_G, // HIJKLMNO BX_KEY_H, BX_KEY_I, BX_KEY_J, BX_KEY_K, BX_KEY_L, BX_KEY_M, BX_KEY_N, BX_KEY_O, // PQRSTUVW BX_KEY_P, BX_KEY_Q, BX_KEY_R, BX_KEY_S, BX_KEY_T, BX_KEY_U, BX_KEY_V, BX_KEY_W, // XYZ[\]^_ BX_KEY_X, BX_KEY_Y, BX_KEY_Z, BX_KEY_LEFT_BRACKET, BX_KEY_BACKSLASH, BX_KEY_RIGHT_BRACKET, BX_KEY_6, BX_KEY_MINUS, // `abcdefg BX_KEY_GRAVE, BX_KEY_A, BX_KEY_B, BX_KEY_C, BX_KEY_D, BX_KEY_E, BX_KEY_F, BX_KEY_G, // hijklmno BX_KEY_H, BX_KEY_I, BX_KEY_J, BX_KEY_K, BX_KEY_L, BX_KEY_M, BX_KEY_N, BX_KEY_O, // pqrstuvw BX_KEY_P, BX_KEY_Q, BX_KEY_R, BX_KEY_S, BX_KEY_T, BX_KEY_U, BX_KEY_V, BX_KEY_W, // xyz{|}~ BX_KEY_X, BX_KEY_Y, BX_KEY_Z, BX_KEY_LEFT_BRACKET, BX_KEY_BACKSLASH, BX_KEY_RIGHT_BRACKET, BX_KEY_GRAVE }; void rfbKeyPressed(Bit32u key, int press_release) { Bit32u key_event; if (!SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { if ((key >= XK_space) && (key <= XK_asciitilde)) { key_event = rfb_ascii_to_key_event[key - XK_space]; } else { switch (key) { case XK_KP_1: #ifdef XK_KP_End case XK_KP_End: #endif key_event = BX_KEY_KP_END; break; case XK_KP_2: #ifdef XK_KP_Down case XK_KP_Down: #endif key_event = BX_KEY_KP_DOWN; break; case XK_KP_3: #ifdef XK_KP_Page_Down case XK_KP_Page_Down: #endif key_event = BX_KEY_KP_PAGE_DOWN; break; case XK_KP_4: #ifdef XK_KP_Left case XK_KP_Left: #endif key_event = BX_KEY_KP_LEFT; break; case XK_KP_5: #ifdef XK_KP_Begin case XK_KP_Begin: #endif key_event = BX_KEY_KP_5; break; case XK_KP_6: #ifdef XK_KP_Right case XK_KP_Right: #endif key_event = BX_KEY_KP_RIGHT; break; case XK_KP_7: #ifdef XK_KP_Home case XK_KP_Home: #endif key_event = BX_KEY_KP_HOME; break; case XK_KP_8: #ifdef XK_KP_Up case XK_KP_Up: #endif key_event = BX_KEY_KP_UP; break; case XK_KP_9: #ifdef XK_KP_Page_Up case XK_KP_Page_Up: #endif key_event = BX_KEY_KP_PAGE_UP; break; case XK_KP_0: #ifdef XK_KP_Insert case XK_KP_Insert: #endif key_event = BX_KEY_KP_INSERT; break; case XK_KP_Decimal: #ifdef XK_KP_Delete case XK_KP_Delete: #endif key_event = BX_KEY_KP_DELETE; break; #ifdef XK_KP_Enter case XK_KP_Enter: key_event = BX_KEY_KP_ENTER; break; #endif case XK_KP_Subtract: key_event = BX_KEY_KP_SUBTRACT; break; case XK_KP_Add: key_event = BX_KEY_KP_ADD; break; case XK_KP_Multiply: key_event = BX_KEY_KP_MULTIPLY; break; case XK_KP_Divide: key_event = BX_KEY_KP_DIVIDE; break; case XK_Up: key_event = BX_KEY_UP; break; case XK_Down: key_event = BX_KEY_DOWN; break; case XK_Left: key_event = BX_KEY_LEFT; break; case XK_Right: key_event = BX_KEY_RIGHT; break; case XK_Delete: key_event = BX_KEY_DELETE; break; case XK_BackSpace: key_event = BX_KEY_BACKSPACE; break; case XK_Tab: key_event = BX_KEY_TAB; break; #ifdef XK_ISO_Left_Tab case XK_ISO_Left_Tab: key_event = BX_KEY_TAB; break; #endif case XK_Return: key_event = BX_KEY_ENTER; break; case XK_Escape: key_event = BX_KEY_ESC; break; case XK_F1: key_event = BX_KEY_F1; break; case XK_F2: key_event = BX_KEY_F2; break; case XK_F3: key_event = BX_KEY_F3; break; case XK_F4: key_event = BX_KEY_F4; break; case XK_F5: key_event = BX_KEY_F5; break; case XK_F6: key_event = BX_KEY_F6; break; case XK_F7: key_event = BX_KEY_F7; break; case XK_F8: key_event = BX_KEY_F8; break; case XK_F9: key_event = BX_KEY_F9; break; case XK_F10: key_event = BX_KEY_F10; break; case XK_F11: key_event = BX_KEY_F11; break; case XK_F12: key_event = BX_KEY_F12; break; case XK_Control_L: key_event = BX_KEY_CTRL_L; break; #ifdef XK_Control_R case XK_Control_R: key_event = BX_KEY_CTRL_R; break; #endif case XK_Shift_L: key_event = BX_KEY_SHIFT_L; break; case XK_Shift_R: key_event = BX_KEY_SHIFT_R; break; case XK_Alt_L: key_event = BX_KEY_ALT_L; break; #ifdef XK_Alt_R case XK_Alt_R: key_event = BX_KEY_ALT_R; break; #endif case XK_Caps_Lock: key_event = BX_KEY_CAPS_LOCK; break; case XK_Num_Lock: key_event = BX_KEY_NUM_LOCK; break; #ifdef XK_Scroll_Lock case XK_Scroll_Lock: key_event = BX_KEY_SCRL_LOCK; break; #endif #ifdef XK_Print case XK_Print: key_event = BX_KEY_PRINT; break; #endif #ifdef XK_Pause case XK_Pause: key_event = BX_KEY_PAUSE; break; #endif case XK_Insert: key_event = BX_KEY_INSERT; break; case XK_Home: key_event = BX_KEY_HOME; break; case XK_End: key_event = BX_KEY_END; break; case XK_Page_Up: key_event = BX_KEY_PAGE_UP; break; case XK_Page_Down: key_event = BX_KEY_PAGE_DOWN; break; default: BX_ERROR(("rfbKeyPress(): key %04x unhandled!", key)); return; } } } else { BXKeyEntry *entry = bx_keymap.findHostKey(key); if (!entry) { BX_ERROR(("rfbKeyPressed(): key %x unhandled!", (unsigned) key)); return; } key_event = entry->baseKey; } if (!press_release) key_event |= BX_KEY_RELEASED; DEV_kbd_gen_scancode(key_event); } void rfbMouseMove(int x, int y, int bmask) { static int oldx = -1; static int oldy = -1; int dx, dy, xorigin; if ((oldx == 1) && (oldy == -1)) { oldx = x; oldy = y; return; } if (y > rfbHeaderbarY) { if (rfbMouseModeAbsXY) { if ((y >= rfbHeaderbarY) && (y < (int)(rfbDimensionY + rfbHeaderbarY))) { dx = x * 0x7fff / rfbDimensionX; dy = (y - rfbHeaderbarY) * 0x7fff / rfbDimensionY; DEV_mouse_motion(dx, dy, 0, bmask, 1); } } else { DEV_mouse_motion(x - oldx, oldy - y, 0, bmask, 0); } oldx = x; oldy = y; } else { if (bmask == 1) { for (unsigned i = 0; i < rfbHeaderbarBitmapCount; i++) { if (rfbHeaderbarBitmaps[i].alignment == BX_GRAVITY_LEFT) xorigin = rfbHeaderbarBitmaps[i].xorigin; else xorigin = rfbWindowX - rfbHeaderbarBitmaps[i].xorigin; if ((x >= xorigin) && (x < (xorigin + int(rfbBitmaps[rfbHeaderbarBitmaps[i].index].xdim)))) { rfbHeaderbarBitmaps[i].f(); return; } } } } } #if BX_SHOW_IPS && defined(WIN32) VOID CALLBACK IPSTimerProc(HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime) { if (keep_alive) { bx_show_ips_handler(); } } DWORD WINAPI rfbShowIPSthread(LPVOID) { MSG msg; UINT TimerId = SetTimer(NULL, 0, 1000, &IPSTimerProc); while (keep_alive && GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } KillTimer(NULL, TimerId); return 0; } #endif #endif /* if BX_WITH_RFB */