I am experimenting a feature to implement VNC protocol server based on VRDE
interface.
Due to lack of document, i am not sure if my understanding to the interface
is correct. Especially, i am not sure the state transition is
understood correctly. If someone in this list can give me a review, it will
help me moving forward.
Current, the patch can work with OSE version as a "Built-in-VBoxVRDP". I
can successfully connect to VRDP with mouse support.
--
-Howard
#define LOG_GROUP LOG_GROUP_VRDP
#include <iprt/log.h>
#include <iprt/asm.h>
#include <iprt/alloca.h>
#include <iprt/ldr.h>
#include <iprt/param.h>
#include <iprt/path.h>
#include <iprt/mem.h>
#include <iprt/stream.h>
#include <iprt/thread.h>
#include <iprt/cpp/utils.h>
#include <VBox/err.h>
#include <VBox/RemoteDesktop/VRDEOrders.h>
#include <VBox/RemoteDesktop/VRDE.h>
#include <rfb/rfb.h>
class VNCServerImpl
{
public:
VNCServerImpl(){};
int Init(const VRDEINTERFACEHDR *pCallbacks, void *pvCallback);
VRDEINTERFACEHDR* GetInterface() { return &Entries.header; }
private:
RTTHREAD mVncThread;
rfbScreenInfoPtr mVncServer;
VRDECALLBACKS_3 *mCallbacks;
void *mCallback;
static VRDEENTRYPOINTS_3 Entries;
static enum rfbNewClientAction rfbNewClientEvent(struct _rfbClientRec* cl);
static void vncMouseEvent(int buttonMask, int x, int y, rfbClientPtr cl);
static DECLCALLBACK(void) VRDEDestroy(HVRDESERVER hServer);
static DECLCALLBACK(int) VRDEEnableConnections(HVRDESERVER hServer, bool fEnable);
static DECLCALLBACK(void) VRDEDisconnect(HVRDESERVER hServer, uint32_t u32ClientId,
bool fReconnect);
static DECLCALLBACK(void) VRDEResize(HVRDESERVER hServer);
static DECLCALLBACK(void) VRDEUpdate(HVRDESERVER hServer, unsigned uScreenId,
void *pvUpdate,uint32_t cbUpdate);
static DECLCALLBACK(void) VRDEColorPointer(HVRDESERVER hServer,
const VRDECOLORPOINTER *pPointer);
static DECLCALLBACK(void) VRDEHidePointer(HVRDESERVER hServer);
static DECLCALLBACK(void) VRDEAudioSamples(HVRDESERVER hServer,
const void *pvSamples,
uint32_t cSamples,
VRDEAUDIOFORMAT format);
static DECLCALLBACK(void) VRDEAudioVolume(HVRDESERVER hServer,
uint16_t u16Left,
uint16_t u16Right);
static DECLCALLBACK(void) VRDEUSBRequest(HVRDESERVER hServer,
uint32_t u32ClientId,
void *pvParm,
uint32_t cbParm);
static DECLCALLBACK(void) VRDEClipboard(HVRDESERVER hServer,
uint32_t u32Function,
uint32_t u32Format,
void *pvData,
uint32_t cbData,
uint32_t *pcbActualRead);
static DECLCALLBACK(void) VRDEQueryInfo(HVRDESERVER hServer,
uint32_t index,
void *pvBuffer,
uint32_t cbBuffer,
uint32_t *pcbOut);
static DECLCALLBACK(void) VRDERedirect(HVRDESERVER hServer,
uint32_t u32ClientId,
const char *pszServer,
const char *pszUser,
const char *pszDomain,
const char *pszPassword,
uint32_t u32SessionId,
const char *pszCookie);
static DECLCALLBACK(void) VRDEAudioInOpen(HVRDESERVER hServer,
void *pvCtx,
uint32_t u32ClientId,
VRDEAUDIOFORMAT audioFormat,
uint32_t u32SamplesPerBlock);
static DECLCALLBACK(void) VRDEAudioInClose(HVRDESERVER hServer,
uint32_t u32ClientId);
static DECLCALLBACK(int) vncThreadFn(RTTHREAD hThreadSelf, void *pvUser);
};
VRDEENTRYPOINTS_3 VNCServerImpl::Entries = {
{ VRDE_INTERFACE_VERSION_3, sizeof(VRDEENTRYPOINTS_3) },
VNCServerImpl::VRDEDestroy,
VNCServerImpl::VRDEEnableConnections,
VNCServerImpl::VRDEDisconnect,
VNCServerImpl::VRDEResize,
VNCServerImpl::VRDEUpdate,
VNCServerImpl::VRDEColorPointer,
VNCServerImpl::VRDEHidePointer,
VNCServerImpl::VRDEAudioSamples,
VNCServerImpl::VRDEAudioVolume,
VNCServerImpl::VRDEUSBRequest,
VNCServerImpl::VRDEClipboard,
VNCServerImpl::VRDEQueryInfo,
VNCServerImpl::VRDERedirect,
VNCServerImpl::VRDEAudioInOpen,
VNCServerImpl::VRDEAudioInClose
};
/** Destroy the server instance.
*
* @param hServer The server instance handle.
*
* @return IPRT status code.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEDestroy(HVRDESERVER hServer)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEDestroy\n");
return;
}
/** The server should start to accept clients connections.
*
* @param hServer The server instance handle.
* @param fEnable Whether to enable or disable client connections.
* When is false, all existing clients are disconnected.
*
* @return IPRT status code.
*/
DECLCALLBACK(int) VNCServerImpl::VRDEEnableConnections(HVRDESERVER hServer, bool fEnable)
{
VNCServerImpl *instance = (VNCServerImpl*)(hServer);
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEEnableConnections\n");
RTThreadCreate(&instance->mVncThread, VNCServerImpl::vncThreadFn, instance,
0 /*cbStack*/, RTTHREADTYPE_GUI, 0 /*fFlags*/, "VNC");
instance->mCallbacks->VRDECallbackProperty(
instance->mCallback, VRDE_SP_NETWORK_BIND_PORT, &(instance->mVncServer->port), sizeof(int), NULL);
return VINF_SUCCESS;
}
/** The server should disconnect the client.
*
* @param hServer The server instance handle.
* @param u32ClientId The client identifier.
* @param fReconnect Whether to send a "REDIRECT to the same server" packet to the
* client before disconnecting.
*
* @return IPRT status code.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEDisconnect(HVRDESERVER hServer, uint32_t u32ClientId,
bool fReconnect)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEDisconnect\n");
}
/**
* Inform the server that the display was resized.
* The server will query information about display
* from the application via callbacks.
*
* @param hServer Handle of VRDE server instance.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEResize(HVRDESERVER hServer)
{
VNCServerImpl *instance = (VNCServerImpl*)(hServer);
VRDEFRAMEBUFFERINFO info;
int rc = instance->mCallbacks->VRDECallbackFramebufferQuery(instance->mCallback, 0, &info);
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEResize to %dx%dx%dbpp\n", info.cWidth, info.cHeight, info.cBitsPerPixel);
if (info.cBitsPerPixel == 32)
{
instance->mVncServer->serverFormat.depth = 32;
instance->mVncServer->serverFormat.redMax = 255;
instance->mVncServer->serverFormat.greenMax = 255;
instance->mVncServer->serverFormat.blueMax = 255;
instance->mVncServer->serverFormat.redShift = 16;
instance->mVncServer->serverFormat.greenShift = 8;
instance->mVncServer->serverFormat.blueShift = 0;
rfbNewFramebuffer(instance->mVncServer, (char*)info.pu8Bits, info.cWidth, info.cHeight, 8, 3, 4);
}
/*
else if (info.cBitsPerPixel == 24)
{
instance->mVncServer->serverFormat.depth = 24;
instance->mVncServer->serverFormat.redMax = 255;
instance->mVncServer->serverFormat.greenMax = 255;
instance->mVncServer->serverFormat.blueMax = 255;
instance->mVncServer->serverFormat.redShift = 16;
instance->mVncServer->serverFormat.greenShift = 8;
instance->mVncServer->serverFormat.blueShift = 0;
rfbNewFramebuffer(instance->mVncServer, (char*)info.pu8Bits, info.cWidth, info.cHeight, 8, 3, 3);
}
*/
}
/**
* Send a update.
*
* @param hServer Handle of VRDE server instance.
* @param uScreenId The screen index.
* @param pvUpdate Pointer to VBoxGuest.h::VRDEORDERHDR structure with extra data.
* @param cbUpdate Size of the update data.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEUpdate(HVRDESERVER hServer, unsigned uScreenId,
void *pvUpdate,uint32_t cbUpdate)
{
VNCServerImpl *instance = (VNCServerImpl*)(hServer);
VRDEORDERHDR* order = static_cast<VRDEORDERHDR*>(pvUpdate);
if (order == NULL)
{
/* Inform the VRDP server that the current display update sequence is
* completed. At this moment the framebuffer memory contains a definite
* image, that is synchronized with the orders already sent to VRDP client.
* The server can now process redraw requests from clients or initial
* fullscreen updates for new clients.
*/
}
else
{
rfbMarkRectAsModified(instance->mVncServer, order->x, order->y, order->x+order->w, order->y+order->h);
}
}
/**
* Set the mouse pointer shape.
*
* @param hServer Handle of VRDE server instance.
* @param pPointer The pointer shape information.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEColorPointer(HVRDESERVER hServer,
const VRDECOLORPOINTER *pPointer)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEColorPointer\n");
}
/**
* Hide the mouse pointer.
*
* @param hServer Handle of VRDE server instance.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEHidePointer(HVRDESERVER hServer)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEHidePointer\n");
}
/**
* Queues the samples to be sent to clients.
*
* @param hServer Handle of VRDE server instance.
* @param pvSamples Address of samples to be sent.
* @param cSamples Number of samples.
* @param format Encoded audio format for these samples.
*
* @note Initialized to NULL when the application audio callbacks are NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEAudioSamples(HVRDESERVER hServer,
const void *pvSamples,
uint32_t cSamples,
VRDEAUDIOFORMAT format)
{
}
/**
* Sets the sound volume on clients.
*
* @param hServer Handle of VRDE server instance.
* @param left 0..0xFFFF volume level for left channel.
* @param right 0..0xFFFF volume level for right channel.
*
* @note Initialized to NULL when the application audio callbacks are NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEAudioVolume(HVRDESERVER hServer,
uint16_t u16Left,
uint16_t u16Right)
{
}
/**
* Sends a USB request.
*
* @param hServer Handle of VRDE server instance.
* @param u32ClientId An identifier that allows the server to find the corresponding client.
* The identifier is always passed by the server as a parameter
* of the FNVRDEUSBCALLBACK. Note that the value is the same as
* in the VRDESERVERCALLBACK functions.
* @param pvParm Function specific parameters buffer.
* @param cbParm Size of the buffer.
*
* @note Initialized to NULL when the application USB callbacks are NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEUSBRequest(HVRDESERVER hServer,
uint32_t u32ClientId,
void *pvParm,
uint32_t cbParm)
{
}
/**
* Called by the application when (VRDE_CLIPBOARD_FUNCTION_*):
* - (0) guest announces available clipboard formats;
* - (1) guest requests clipboard data;
* - (2) guest responds to the client's request for clipboard data.
*
* @param hServer The VRDE server handle.
* @param u32Function The cause of the call.
* @param u32Format Bitmask of announced formats or the format of data.
* @param pvData Points to: (1) buffer to be filled with clients data;
* (2) data from the host.
* @param cbData Size of 'pvData' buffer in bytes.
* @param pcbActualRead Size of the copied data in bytes.
*
* @note Initialized to NULL when the application clipboard callbacks are NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEClipboard(HVRDESERVER hServer,
uint32_t u32Function,
uint32_t u32Format,
void *pvData,
uint32_t cbData,
uint32_t *pcbActualRead)
{
}
/**
* Query various information from the VRDE server.
*
* @param hServer The VRDE server handle.
* @param index VRDE_QI_* identifier of information to be returned.
* @param pvBuffer Address of memory buffer to which the information must be written.
* @param cbBuffer Size of the memory buffer in bytes.
* @param pcbOut Size in bytes of returned information value.
*
* @remark The caller must check the *pcbOut. 0 there means no information was returned.
* A value greater than cbBuffer means that information is too big to fit in the
* buffer, in that case no information was placed to the buffer.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEQueryInfo(HVRDESERVER hServer,
uint32_t index,
void *pvBuffer,
uint32_t cbBuffer,
uint32_t *pcbOut)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::VRDEQueryInfo\n");
}
/**
* The server should redirect the client to the specified server.
*
* @param hServer The server instance handle.
* @param u32ClientId The client identifier.
* @param pszServer The server to redirect the client to.
* @param pszUser The username to use for the redirection.
* Can be NULL.
* @param pszDomain The domain. Can be NULL.
* @param pszPassword The password. Can be NULL.
* @param u32SessionId The ID of the session to redirect to.
* @param pszCookie The routing token used by a load balancer to
* route the redirection. Can be NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDERedirect(HVRDESERVER hServer,
uint32_t u32ClientId,
const char *pszServer,
const char *pszUser,
const char *pszDomain,
const char *pszPassword,
uint32_t u32SessionId,
const char *pszCookie)
{
}
/**
* Audio input open request.
*
* @param hServer Handle of VRDE server instance.
* @param pvCtx To be used in VRDECallbackAudioIn.
* @param u32ClientId An identifier that allows the server to find the corresponding client.
* @param audioFormat Preferred format of audio data.
* @param u32SamplesPerBlock Preferred number of samples in one block of audio input data.
*
* @note Initialized to NULL when the VRDECallbackAudioIn callback is NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEAudioInOpen(HVRDESERVER hServer,
void *pvCtx,
uint32_t u32ClientId,
VRDEAUDIOFORMAT audioFormat,
uint32_t u32SamplesPerBlock)
{
}
/**
* Audio input close request.
*
* @param hServer Handle of VRDE server instance.
* @param u32ClientId An identifier that allows the server to find the corresponding client.
*
* @note Initialized to NULL when the VRDECallbackAudioIn callback is NULL.
*/
DECLCALLBACK(void) VNCServerImpl::VRDEAudioInClose(HVRDESERVER hServer,
uint32_t u32ClientId)
{
}
int VNCServerImpl::Init(const VRDEINTERFACEHDR *pCallbacks,
void *pvCallback)
{
if (pCallbacks->u64Version == VRDE_INTERFACE_VERSION_3)
{
mCallbacks = (VRDECALLBACKS_3*)pCallbacks;
mCallback = pvCallback;
}
else if (pCallbacks->u64Version == VRDE_INTERFACE_VERSION_1)
{
// @todo: this is incorrect and it will cause crash if client call unsupport func.
mCallbacks = (VRDECALLBACKS_3*)pCallbacks;
mCallback = pvCallback;
// since they are same in order, let's just change header
Entries.header.u64Version = VRDE_INTERFACE_VERSION_1;
Entries.header.u64Size = sizeof(VRDEENTRYPOINTS_1);
}
else
return VERR_VERSION_MISMATCH;
// query server for the framebuffer
VRDEFRAMEBUFFERINFO info;
int rc = mCallbacks->VRDECallbackFramebufferQuery(mCallback, 0, &info);
mVncServer = rfbGetScreen(0, NULL, info.cWidth, info.cHeight, 8, 3, 4);
mVncServer->serverFormat.redShift = 16;
mVncServer->serverFormat.greenShift = 8;
mVncServer->serverFormat.blueShift = 0;
mVncServer->screenData = (void*)this;
mVncServer->port = 5900;
mVncServer->desktopName = "VBOXVNC";
rfbInitServer(mVncServer);
mVncServer->newClientHook = rfbNewClientEvent;
// mVncServer->kbdAddEvent = vncKeyboardEvent;
// mVncServer->kbdReleaseAllKeys = vncReleaseKeysEvent;
mVncServer->ptrAddEvent = vncMouseEvent;
return VINF_SUCCESS;
}
void VNCServerImpl::vncMouseEvent(int buttonMask, int x, int y, rfbClientPtr cl)
{
VNCServerImpl *instance = static_cast<VNCServerImpl*>(cl->screen->screenData);
VRDEINPUTPOINT point;
point.uButtons = buttonMask;
point.x = x;
point.y = y;
instance->mCallbacks->VRDECallbackInput(instance->mCallback, VRDE_INPUT_POINT, &point, sizeof(point));
rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
}
enum rfbNewClientAction VNCServerImpl::rfbNewClientEvent(struct _rfbClientRec* cl)
{
RTStrmPrintf(g_pStdOut, "VNCServerImpl::rfbNewClientEvent\n");
VNCServerImpl *instance = static_cast<VNCServerImpl*>(cl->screen->screenData);
instance->mCallbacks->VRDECallbackClientConnect(instance->mCallback, (int)cl->sock);
}
DECLCALLBACK(int) VNCServerImpl::vncThreadFn(RTTHREAD hThreadSelf, void *pvUser)
{
VNCServerImpl *instance = static_cast<VNCServerImpl*>(pvUser);
rfbRunEventLoop(instance->mVncServer, -1, FALSE);
return VINF_SUCCESS;
}
VNCServerImpl *g_RDPServer = 0;
DECLEXPORT(int) VRDECreateServer (const VRDEINTERFACEHDR *pCallbacks,
void *pvCallback,
VRDEINTERFACEHDR **ppEntryPoints,
HVRDESERVER *phServer)
{
RTStrmPrintf(g_pStdOut, "VRDECreateServer In\n");
if (!g_RDPServer)
{
g_RDPServer = new VNCServerImpl();
}
int rc = g_RDPServer->Init(pCallbacks, pvCallback);
if (RT_SUCCESS(rc))
{
*ppEntryPoints = g_RDPServer->GetInterface();
*phServer = (HVRDESERVER)g_RDPServer;
}
RTStrmPrintf(g_pStdOut, "VRDECreateServer Out\n");
return rc;
}
_______________________________________________
vbox-dev mailing list
[email protected]
http://vbox.innotek.de/mailman/listinfo/vbox-dev