service.cpp revision 74e1a1eff6baaf6d676441963c74a15ea46e9284
/** @file
*
* Shared Clipboard:
* Host service entry points.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/** @page pg_hostclip The Shared Clipboard Host Service
*
* The shared clipboard host service provides a proxy between the host's
* clipboard and a similar proxy running on a guest. The service is split
* into a platform-independant core and platform-specific backends. The
* service defines two communication protocols - one to communicate with the
* clipboard service running on the guest, and one to communicate with the
* backend. These will be described in a very skeletal fashion here.
*
* @section sec_hostclip_guest_proto The guest communication protocol
*
* The guest clipboard service communicates with the host service via HGCM
* (the host service runs as an HGCM service). The guest clipboard must
* connect to the host service before all else (Windows hosts currently only
* support one simultaneous connection). Once it has connected, it can send
* HGCM messages to the host services, some of which will receive replies from
* the host. The host can only reply to a guest message, it cannot initiate
* any communication. The guest can in theory send any number of messages in
* parallel (see the descriptions of the messages for the practice), and the
* host will receive these in sequence, and may reply to them at once
* (releasing the caller in the guest) or defer the reply until later.
*
* There are currently four messages defined. The first is
* VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the
* host. Host messages currently defined are
* VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused),
* VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the
* contents of its clipboard to the host) and
* VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS (to notify the guest that new
* clipboard data is available). If a host message is sent while the guest is
* not waiting, it will be queued until the guest requests it. At most one
* host message of each type will be kept in the queue. The host code only
* supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call
* from the guest.
*
* The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells
* the host that the guest has new clipboard data available. The third is
* VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its
* clipboard data and waits until it arrives. The host supports at most one
* simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a
* second call is made before the first has returned, the first will be
* aborted.
*
* The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is
* used to send the contents of the guest clipboard to the host. This call
* should be used after the host has requested data from the guest.
*
* @section sec_hostclip_backend_proto The communication protocol with the
* platform-specific backend
*
* This section may be written in the future :)
*/
#include <iprt/critsect.h>
#include "VBoxClipboard.h"
{
}
{
{
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
#if 0
{
}
#endif
{
{
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
static PVBOXHGCMSVCHELPERS g_pHelpers;
static RTCRITSECT critsect;
static PFNHGCMSVCEXT g_pfnExtension;
static void *g_pvExtension;
static VBOXCLIPBOARDCLIENTDATA *g_pClient;
/* Serialization of data reading and format announcements from the RDP client. */
static bool g_fReadingData = false;
static bool g_fDelayedAnnouncement = false;
static uint32_t g_u32DelayedFormats = 0;
static uint32_t vboxSvcClipboardMode (void)
{
return g_u32Mode;
}
{
switch (u32Mode)
{
break;
default:
}
}
bool vboxSvcClipboardLock (void)
{
}
void vboxSvcClipboardUnlock (void)
{
}
/* Set the HGCM parameters according to pending messages.
* Executed under the clipboard lock.
*/
{
/* Message priority is taken into account. */
{
LogRelFlow(("vboxSvcClipboardReturnMsg: Quit\n"));
}
else if (pClient->fMsgReadData)
{
pClient->fMsgReadData = false;
}
else if (pClient->fMsgFormats)
{
pClient->fMsgFormats = false;
}
else
{
/* No pending messages. */
LogRelFlow(("vboxSvcClipboardReturnMsg: no message\n"));
return false;
}
/* Message information assigned. */
return true;
}
void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
{
if (vboxSvcClipboardLock ())
{
switch (u32Msg)
{
{
LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n"));
} break;
{
{
/* Skip the message. */
break;
}
pClient->fMsgReadData = true;
} break;
{
{
/* Skip the message. */
break;
}
pClient->fMsgFormats = true;
} break;
default:
{
/* Invalid message. */
} break;
}
{
/* The client waits for a responce. */
/* Make a copy of the handle. */
if (fMessageReturned)
{
/* There is a responce. */
}
if (fMessageReturned)
{
LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n"));
}
}
else
{
}
}
}
static int svcInit (void)
{
if (RT_SUCCESS (rc))
{
rc = vboxClipboardInit ();
/* Clean up on failure, because 'svnUnload' will not be called
* if the 'svcInit' returns an error.
*/
if (RT_FAILURE (rc))
{
}
}
return rc;
}
static DECLCALLBACK(int) svcUnload (void *)
{
return VINF_SUCCESS;
}
/**
* Disconnect the host side of the shared clipboard and send a "host disconnected" message
* to the guest side.
*/
{
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
/* If there is already a client connected then we want to release it first. */
{
/* And free the resources in the hgcm subsystem. */
}
/* Register the client. */
if (RT_SUCCESS (rc))
{
}
return rc;
}
static DECLCALLBACK(void) svcCall (void *,
void *pvClient,
{
int rc = VINF_SUCCESS;
LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
bool fAsynchronousProcessing = false;
#ifdef DEBUG
uint32_t i;
for (i = 0; i < cParms; i++)
{
/** @todo parameters other than 32 bit */
}
#endif
switch (u32Function)
{
{
/* The quest requests a host message. */
LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
{
}
)
{
}
else
{
/* Atomically verify the client's state. */
if (vboxSvcClipboardLock ())
{
if (fMessageReturned)
{
/* Just return to the caller. */
}
else
{
/* No event available at the time. Process asynchronously. */
fAsynchronousProcessing = true;
LogRel2(("svcCall: async.\n"));
}
}
else
{
}
}
} break;
{
/* The guest reports that some formats are available. */
LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
{
}
)
{
}
else
{
if (RT_SUCCESS (rc))
{
{
break;
}
if (g_pfnExtension)
{
}
else
{
}
}
}
} break;
{
/* The guest wants to read data in the given format. */
LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
{
}
)
{
}
else
{
void *pv;
if (RT_SUCCESS (rc))
{
if (RT_SUCCESS (rc))
{
{
break;
}
if (g_pfnExtension)
{
g_fReadingData = true;
LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
{
g_fDelayedAnnouncement = false;
g_u32DelayedFormats = 0;
}
g_fReadingData = false;
if (RT_SUCCESS (rc))
{
}
}
else
{
/* Release any other pending read, as we only
* support one pending read at one time. */
}
/* Remember our read request until it is completed.
* See the protocol description above for more
* information. */
if (rc == VINF_HGCM_ASYNC_EXECUTE)
{
if (vboxSvcClipboardLock())
{
pClient->fReadPending = true;
fAsynchronousProcessing = true;
}
else
}
else if (RT_SUCCESS (rc))
{
}
}
}
}
} break;
{
/* The guest writes the requested data. */
LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
{
}
)
{
}
else
{
void *pv;
if (RT_SUCCESS (rc))
{
if (RT_SUCCESS (rc))
{
{
break;
}
if (g_pfnExtension)
{
}
else
{
}
}
}
}
} break;
default:
{
}
}
if (!fAsynchronousProcessing)
{
}
}
/** If the client in the guest is waiting for a read operation to complete
* then complete it, otherwise return. See the protocol description in the
* shared clipboard module description. */
{
bool fReadPending = false;
if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
{
pClient->fReadPending = false;
}
if (fReadPending)
{
}
}
/*
* We differentiate between a function handler for the guest and one for the host.
*/
static DECLCALLBACK(int) svcHostCall (void *,
{
int rc = VINF_SUCCESS;
LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
switch (u32Function)
{
{
LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
if (cParms != 1)
{
}
)
{
}
else
{
/* The setter takes care of invalid values. */
}
} break;
default:
break;
}
return rc;
}
/**
* SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
*/
static SSMFIELD const g_aClipboardClientDataFields[] =
{
};
static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
{
/* If there are any pending requests, they must be completed here. Since
* the service is single threaded, there could be only requests
* which the service itself has postponed.
*
* HGCM knows that the state is being saved and that the pfnComplete
* calls are just clean ups. These requests are saved by the VMMDev.
*
* When the state will be restored, these requests will be reissued
* by VMMDev. The service therefore must save state as if there were no
* pending request.
*/
/* This field used to be the length. We're using it as a version field
with the high bit set. */
int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
{
g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
{
/* Existing client can not be in async state yet. */
/* Save the client ID for data validation. */
/** @todo isn't this the same as u32ClientID? Playing safe for now... */
/* Restore the client data. */
{
rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
}
{
/**
* This structure corresponds to the original layout of the
* VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
* when saving state, we need to remember it forever in order to preserve
* compatibility.
*
* (Starting with 3.1 this is no longer used.)
*/
typedef struct _CLIPSAVEDSTATEDATA
{
struct _CLIPSAVEDSTATEDATA *pNext;
struct _CLIPSAVEDSTATEDATA *pPrev;
bool fMsgQuit: 1;
bool fMsgReadData: 1;
bool fMsgFormats: 1;
struct {
} async;
struct {
void *pv;
} data;
/**
* SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
*/
static SSMFIELD const s_aClipSavedStateDataFields30[] =
{
SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
};
}
else
{
}
/* Verify the client ID. */
{
}
/* Actual host data are to be reported to guest (SYNC). */
return VINF_SUCCESS;
}
static DECLCALLBACK(int) extCallback (uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
{
{
switch (u32Function)
{
{
if (g_fReadingData)
{
g_fDelayedAnnouncement = true;
}
else
{
}
} break;
{
} break;
default:
return VERR_NOT_SUPPORTED;
}
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
{
if (pfnExtension)
{
/* Install extension. */
}
else
{
if (g_pfnExtension)
{
}
/* Uninstall extension. */
}
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
if (!ptable)
{
}
else
{
LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
{
}
else
{
/* Service specific initialization. */
}
}
return rc;
}