/* $Id$ */
/** @file
* Shared Clipboard: Mac OS X host.
*/
/*
* Copyright (C) 2008-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
*/
#include <VBox/HostServices/VBoxClipboardSvc.h>
#include <iprt/assert.h>
#include <iprt/asm.h>
#include <iprt/thread.h>
#include "VBoxClipboard.h"
#include "darwin-pasteboard.h"
/** Global clipboard context information */
struct _VBOXCLIPBOARDCONTEXT
{
/** We have a separate thread to poll for new clipboard content */
RTTHREAD thread;
bool volatile fTerminate;
/** The reference to the current pasteboard */
PasteboardRef pasteboard;
VBOXCLIPBOARDCLIENTDATA *pClient;
};
/** Only one client is supported. There seems to be no need for more clients. */
static VBOXCLIPBOARDCONTEXT g_ctx;
/**
* Checks if something is present on the clipboard and calls vboxSvcClipboardReportMsg.
*
* @returns IPRT status code (ignored).
* @param pCtx The context.
*/
static int vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
{
if (pCtx->pClient == NULL)
return VINF_SUCCESS;
uint32_t fFormats = 0;
bool fChanged = false;
/* Retrieve the formats currently in the clipboard and supported by vbox */
int rc = queryNewPasteboardFormats (pCtx->pasteboard, &fFormats, &fChanged);
if (RT_SUCCESS (rc) && fChanged)
{
vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, fFormats);
Log (("vboxClipboardChanged fFormats %02X\n", fFormats));
}
return rc;
}
/**
* The poller thread.
*
* This thread will check for the arrival of new data on the clipboard.
*
* @returns VINF_SUCCESS (not used).
* @param Thread Our thread handle.
* @param pvUser Pointer to the VBOXCLIPBOARDCONTEXT structure.
*
*/
static int vboxClipboardThread (RTTHREAD ThreadSelf, void *pvUser)
{
Log (("vboxClipboardThread: starting clipboard thread\n"));
AssertPtrReturn (pvUser, VERR_INVALID_PARAMETER);
VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *) pvUser;
while (!pCtx->fTerminate)
{
/* call this behind the lock because we don't know if the api is
thread safe and in any case we're calling several methods. */
vboxSvcClipboardLock();
vboxClipboardChanged (pCtx);
vboxSvcClipboardUnlock();
/* Sleep for 200 msecs before next poll */
RTThreadUserWait (ThreadSelf, 200);
}
Log (("vboxClipboardThread: clipboard thread terminated successfully with return code %Rrc\n", VINF_SUCCESS));
return VINF_SUCCESS;
}
/*
* Public platform dependent functions.
*/
/** Initialise the host side of the shared clipboard - called by the hgcm layer. */
int vboxClipboardInit (void)
{
Log (("vboxClipboardInit\n"));
g_ctx.fTerminate = false;
int rc = initPasteboard (&g_ctx.pasteboard);
AssertRCReturn (rc, rc);
rc = RTThreadCreate (&g_ctx.thread, vboxClipboardThread, &g_ctx, 0,
RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
if (RT_FAILURE (rc))
{
g_ctx.thread = NIL_RTTHREAD;
destroyPasteboard (&g_ctx.pasteboard);
}
return rc;
}
/** Terminate the host side of the shared clipboard - called by the hgcm layer. */
void vboxClipboardDestroy (void)
{
Log (("vboxClipboardDestroy\n"));
/*
* Signal the termination of the polling thread and wait for it to respond.
*/
ASMAtomicWriteBool (&g_ctx.fTerminate, true);
int rc = RTThreadUserSignal (g_ctx.thread);
AssertRC (rc);
rc = RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
AssertRC (rc);
/*
* Destroy the pasteboard and uninitialize the global context record.
*/
destroyPasteboard (&g_ctx.pasteboard);
g_ctx.thread = NIL_RTTHREAD;
g_ctx.pClient = NULL;
}
/**
* Enable the shared clipboard - called by the hgcm clipboard subsystem.
*
* @param pClient Structure containing context information about the guest system
* @returns RT status code
*/
int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool)
{
if (g_ctx.pClient != NULL)
{
/* One client only. */
return VERR_NOT_SUPPORTED;
}
vboxSvcClipboardLock();
pClient->pCtx = &g_ctx;
pClient->pCtx->pClient = pClient;
/* Initially sync the host clipboard content with the client. */
int rc = vboxClipboardSync (pClient);
vboxSvcClipboardUnlock();
return rc;
}
/**
* Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
* after a save and restore of the guest.
*/
int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
{
/* Sync the host clipboard content with the client. */
vboxSvcClipboardLock();
int rc = vboxClipboardChanged (pClient->pCtx);
vboxSvcClipboardUnlock();
return rc;
}
/**
* Shut down the shared clipboard subsystem and "disconnect" the guest.
*/
void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
{
Log (("vboxClipboardDisconnect\n"));
vboxSvcClipboardLock();
pClient->pCtx->pClient = NULL;
vboxSvcClipboardUnlock();
}
/**
* The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
* subsystem.
*
* @param pClient Context data for the guest system
* @param u32Formats Clipboard formats the guest is offering
*/
void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
{
Log (("vboxClipboardFormatAnnounce u32Formats %02X\n", u32Formats));
if (u32Formats == 0)
{
/* This is just an automatism, not a genuine announcement */
return;
}
vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
u32Formats);
}
/**
* Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
*
* @param pClient Context information about the guest VM
* @param u32Format The format that the guest would like to receive the data in
* @param pv Where to write the data to
* @param cb The size of the buffer to write the data to
* @param pcbActual Where to write the actual size of the written data
*/
int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format,
void *pv, uint32_t cb, uint32_t * pcbActual)
{
vboxSvcClipboardLock();
/* Default to no data available. */
*pcbActual = 0;
int rc = readFromPasteboard (pClient->pCtx->pasteboard, u32Format, pv, cb, pcbActual);
vboxSvcClipboardUnlock();
return rc;
}
/**
* Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
*
* @param pClient Context information about the guest VM
* @param pv Buffer to which the data was written
* @param cb The size of the data written
* @param u32Format The format of the data written
*/
void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv,
uint32_t cb, uint32_t u32Format)
{
vboxSvcClipboardLock();
writeToPasteboard (pClient->pCtx->pasteboard, pv, cb, u32Format);
vboxSvcClipboardUnlock();
}