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

Reply via email to