Bochs/bochs/gui/rfb.cc
2009-11-15 19:53:56 +00:00

1682 lines
50 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id: rfb.cc,v 1.66 2009-11-15 19:53:56 sshwarts 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., 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 "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
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(int element, bx_bool active, bx_bool w=0);
#if BX_SHOW_IPS
void show_ips(Bit32u ips_count);
#endif
};
void rfbSetStatusText(int element, const char *text, bx_bool active, bx_bool w=0);
static Bit32u convertStringToRfbKey(const char *string);
// 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->
#include "rfb.h"
#include "rfbkeys.h"
#ifdef WIN32
#include <winsock.h>
#include <process.h>
#else
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <unistd.h>
#ifndef __QNXNTO__
#include <sys/errno.h>
#else
#include <errno.h>
#endif
#include <pthread.h>
typedef int SOCKET;
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#endif
static bool keep_alive;
static bool client_connected;
static bool desktop_resizable;
#define BX_RFB_PORT_MIN 5900
#define BX_RFB_PORT_MAX 5949
static unsigned short rfbPort;
// Headerbar stuff
unsigned rfbBitmapCount = 0;
static struct {
char *bmap;
unsigned xdim;
unsigned ydim;
} rfbBitmaps[BX_MAX_PIXMAPS];
static unsigned rfbHeaderbarBitmapCount = 0;
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 true
#define MOUSE false
#define MAX_KEY_EVENTS 512
static 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
static struct {
unsigned int x;
unsigned int y;
unsigned int width;
unsigned int height;
bool updated;
} rfbUpdateRegion;
#define BX_RFB_MAX_XDIM 1024
#define BX_RFB_MAX_YDIM 768
#define BX_RFB_DEF_XDIM 720
#define BX_RFB_DEF_YDIM 480
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 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 unsigned long ServerThread = 0;
//static unsigned long ServerThreadID = 0;
static SOCKET sGlobal;
static Bit32u clientEncodingsCount = 0;
static Bit32u *clientEncodings = NULL;
void CDECL 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 DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char color, bx_bool gfxchar);
void UpdateScreen(unsigned char *newBits, int x, int y, int width, int height, bool update_client);
void SendUpdate(int x, int y, int width, int height, Bit32u encoding);
void StartThread();
void rfbKeyPressed(Bit32u key, int press_release);
void rfbMouseMove(int x, int y, int bmask);
void DrawColorPalette();
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))))
// ::SPECIFIC_INIT()
//
// Called from gui.cc, once upon program startup, to allow for the
// specific GUI code (X11, BeOS, ...) to be initialized.
//
// 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_rfb_gui_c::specific_init(int argc, char **argv, unsigned tilewidth, unsigned tileheight, 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 = tilewidth;
rfbTileY = tileheight;
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;
}
}
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 = false;
clientEncodingsCount=0;
clientEncodings=NULL;
keep_alive = true;
client_connected = false;
desktop_resizable = false;
StartThread();
#ifdef WIN32
Sleep(1000);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
#endif
if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) {
BX_ERROR(("private_colormap option ignored."));
}
// load keymap for sdl
if (SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) {
bx_keymap.loadKeymap(convertStringToRfbKey);
}
// parse rfb specific options
if (argc > 1) {
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "timeout=", 8)) {
timeout = atoi(&argv[i][8]);
} else {
BX_PANIC(("Unknown rfb option '%s'", argv[i]));
}
}
}
// the ask menu doesn't work on the client side
io->set_log_action(LOGLEV_PANIC, ACT_FATAL);
while ((!client_connected) && (timeout--)) {
#ifdef WIN32
Sleep(1000);
#else
sleep(1);
#endif
}
if (timeout < 0) BX_PANIC(("timeout! no client present"));
new_gfx_api = 1;
dialog_caps = 0;
}
void rfbSetStatusText(int element, const char *text, bx_bool active, bx_bool w)
{
char *newBits;
unsigned xleft, xsize, color, 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;
}
if (element > 0) {
color = active?(w?0xc0:0xa0):0xf7;
} else {
color = 0xf0;
}
DrawBitmap(xleft, rfbWindowY - rfbStatusbarY + 1, xsize, rfbStatusbarY - 2, newBits, color, false);
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], color, 0);
}
if (xleft < rfbUpdateRegion.x) rfbUpdateRegion.x = xleft;
if ((rfbWindowY - rfbStatusbarY + 1) < rfbUpdateRegion.y) rfbUpdateRegion.y = rfbWindowY - rfbStatusbarY + 1;
if (((xleft + xsize) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((xleft + xsize) - rfbUpdateRegion.x);
if (((rfbWindowY - 2) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((rfbWindowY - 2) - rfbUpdateRegion.y);
rfbUpdateRegion.updated = true;
}
void bx_rfb_gui_c::statusbar_setitem(int element, bx_bool active, bx_bool w)
{
if (element < 0) {
for (unsigned i = 0; i < statusitem_count; i++) {
rfbSetStatusText(i+1, statusitem_text[i], active, w);
}
} else if ((unsigned)element < statusitem_count) {
rfbSetStatusText(element+1, statusitem_text[element], active, w);
}
}
#ifdef WIN32
bool InitWinsock()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return false;
return true;
}
#endif
#ifdef WIN32
bool StopWinsock()
{
WSACleanup();
return true;
}
#endif
void CDECL ServerThreadInit(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 == -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 HandleRfbClient(SOCKET sClient)
{
char rfbName[] = "Bochs-RFB";
rfbProtocolVersionMessage pv;
int one = 1;
U32 auth;
rfbClientInitMessage cim;
rfbServerInitMessage sim;
client_connected = true;
setsockopt(sClient, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one));
BX_INFO(("accepted client connection."));
snprintf(pv, rfbProtocolVersionMessageSize,
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;
}
sGlobal = sClient;
while(keep_alive) {
U8 msgType;
int n;
if((n = recv(sClient, (char *)&msgType, 1, MSG_PEEK)) <= 0) {
if(n == 0) {
BX_ERROR(("client closed connection."));
} else {
BX_ERROR(("error receiving data."));
}
return;
}
switch(msgType) {
case rfbSetPixelFormat:
{
rfbSetPixelFormatMessage spf;
ReadExact(sClient, (char *)&spf, sizeof(rfbSetPixelFormatMessage));
spf.pixelFormat.bitsPerPixel = spf.pixelFormat.bitsPerPixel;
spf.pixelFormat.depth = spf.pixelFormat.depth;
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);
spf.pixelFormat.redShift = spf.pixelFormat.redShift;
spf.pixelFormat.greenShift = spf.pixelFormat.greenShift;
spf.pixelFormat.blueShift = spf.pixelFormat.blueShift;
if (!PF_EQ(spf.pixelFormat, BGR233Format)) {
BX_ERROR(("client has wrong pixel format (%d %d %d %d %d %d %d %d %d)",
spf.pixelFormat.bitsPerPixel,spf.pixelFormat.depth,spf.pixelFormat.trueColourFlag,
spf.pixelFormat.bigEndianFlag,spf.pixelFormat.redMax,spf.pixelFormat.greenMax,
spf.pixelFormat.blueMax,spf.pixelFormat.redShift,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) {
BX_ERROR(("client closed connection."));
} 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 = true;
}
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 = 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:
{
rfbKeyEventMessage ke;
ReadExact(sClient, (char *)&ke, sizeof(rfbKeyEventMessage));
ke.key = ntohl(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.downFlag;
rfbKeyboardEvents++;
bKeyboardInUse = false;
break;
}
case rfbPointerEvent:
{
rfbPointerEventMessage pe;
ReadExact(sClient, (char *)&pe, sizeof(rfbPointerEventMessage));
while(bKeyboardInUse);
bKeyboardInUse = true;
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 = false;
break;
}
case rfbClientCutText:
{
rfbClientCutTextMessage cct;
ReadExact(sClient, (char *)&cct, sizeof(rfbClientCutTextMessage));
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_rfb_gui_c::handle_events(void)
{
while(bKeyboardInUse);
bKeyboardInUse = true;
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 = false;
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 = false;
}
// ::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;
bx_bool force_update=0, gfxchar, blink_state, blink_mode;
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];
}
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], cAttr, gfxchar);
if(yc < rfbUpdateRegion.y) rfbUpdateRegion.y = yc;
if((yc + font_height - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = (yc + font_height - rfbUpdateRegion.y);
if(xc < rfbUpdateRegion.x) rfbUpdateRegion.x = xc;
if((xc + font_width - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = (xc + font_width - rfbUpdateRegion.x);
rfbUpdateRegion.updated = true;
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], cAttr, 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(unsigned index, unsigned red, unsigned green, unsigned 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 '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_rfb_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
{
UpdateScreen(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);
if ((rfbUpdateRegion.x + rfbUpdateRegion.width) > rfbWindowX) {
rfbUpdateRegion.width = rfbWindowX - rfbUpdateRegion.x;
}
rfbUpdateRegion.updated = true;
}
bx_svga_tileinfo_t *bx_rfb_gui_c::graphics_tile_info(bx_svga_tileinfo_t *info)
{
if (!info) {
info = (bx_svga_tileinfo_t *)malloc(sizeof(bx_svga_tileinfo_t));
if (!info) {
return NULL;
}
}
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)
{
if(x0 < rfbUpdateRegion.x) rfbUpdateRegion.x = x0;
if((y0 + rfbHeaderbarY) < rfbUpdateRegion.y) rfbUpdateRegion.y = y0 + rfbHeaderbarY;
if(((y0 + rfbHeaderbarY + h) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((y0 + rfbHeaderbarY + h) - rfbUpdateRegion.y);
if(((x0 + w) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((x0 + h) - rfbUpdateRegion.x);
if ((rfbUpdateRegion.x + rfbUpdateRegion.width) > rfbWindowX) {
rfbUpdateRegion.width = rfbWindowX - 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().
// 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) {
BX_PANIC(("%d bpp graphics mode not supported yet", bpp));
}
if (fheight > 0) {
font_height = fheight;
font_width = fwidth;
text_cols = x / fwidth;
text_rows = y / fheight;
}
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));
} else if ((x != rfbDimensionX) || (x != rfbDimensionY)) {
if (desktop_resizable) {
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 {
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, (char)0xf0, false);
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, (char)0xf0, false);
}
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, (char)0xf0, false);
free(newBits);
for (i = 1; i <= statusitem_count; i++) {
rfbSetStatusText(i, statusitem_text[i-1], 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, (char)0xf0, true);
}
// ::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 = false;
#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()"));
}
/*
* 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 color, bool update_client)
{
unsigned char *newBits;
char fgcolor, bgcolor;
static char vgaPalette[] = {
(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 = vgaPalette[(color >> 4) & 0xF];
fgcolor = vgaPalette[color & 0xF];
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);
//DrawColorPalette();
free(newBits);
}
void DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char color, bx_bool gfxchar)
{
static unsigned char newBits[9 * 32];
unsigned char mask;
int bytes = width * height;
char fgcolor, bgcolor;
static char vgaPalette[] = {
(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 = vgaPalette[(color >> 4) & 0xF];
fgcolor = vgaPalette[color & 0xF];
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, false);
//DrawColorPalette();
}
void DrawColorPalette()
{
unsigned char bits[100];
int x = 0, y = 0, c;
for(c = 0; c < 256; c++) {
memset(&bits, rfbPalette[c], 100);
UpdateScreen(bits, x, y, 10, 10, false);
x += 10;
if(x > 70) {
y += 10;
x = 0;
}
}
}
void UpdateScreen(unsigned 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] = rfbPalette[newBits[(i * width) + c]];
}
memcpy(&rfbScreen[y * rfbWindowX + x], &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(x);
furh.r.yPosition = htons((y - i));
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 StartThread()
{
#ifdef WIN32
_beginthread(ServerThreadInit, 0, NULL);
#else
pthread_t thread;
pthread_create(&thread, NULL, (void *(*)(void *))&ServerThreadInit, NULL);
#endif
}
// 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;
}
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 xorigin;
if ((oldx == 1) && (oldy == -1)) {
oldx = x;
oldy = y;
return;
}
if(y > rfbHeaderbarY) {
DEV_mouse_motion(x - oldx, oldy - y, bmask);
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;
}
}
}
}
}
void bx_rfb_gui_c::mouse_enabled_changed_specific(bx_bool val)
{
}
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;
}
#if BX_SHOW_IPS
void bx_rfb_gui_c::show_ips(Bit32u ips_count)
{
char ips_text[40];
sprintf(ips_text, "IPS: %9u", ips_count);
rfbSetStatusText(0, ips_text, 1);
}
#endif
#endif /* if BX_WITH_RFB */