FreeRDP/winpr/libwinpr/clipboard/clipboard.c
2019-12-02 10:57:31 +01:00

611 lines
12 KiB
C

/**
* WinPR: Windows Portable Runtime
* Clipboard Functions
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/collections.h>
#include <winpr/wlog.h>
#include <winpr/clipboard.h>
#include "clipboard.h"
#ifdef WITH_WCLIPBOARD_POSIX
#include "posix.h"
#endif
#include "../log.h"
#define TAG WINPR_TAG("clipboard")
/**
* Clipboard (Windows):
* msdn.microsoft.com/en-us/library/windows/desktop/ms648709/
*
* W3C Clipboard API and events:
* http://www.w3.org/TR/clipboard-apis/
*/
static const char* CF_STANDARD_STRINGS[CF_MAX] = {
"CF_RAW", /* 0 */
"CF_TEXT", /* 1 */
"CF_BITMAP", /* 2 */
"CF_METAFILEPICT", /* 3 */
"CF_SYLK", /* 4 */
"CF_DIF", /* 5 */
"CF_TIFF", /* 6 */
"CF_OEMTEXT", /* 7 */
"CF_DIB", /* 8 */
"CF_PALETTE", /* 9 */
"CF_PENDATA", /* 10 */
"CF_RIFF", /* 11 */
"CF_WAVE", /* 12 */
"CF_UNICODETEXT", /* 13 */
"CF_ENHMETAFILE", /* 14 */
"CF_HDROP", /* 15 */
"CF_LOCALE", /* 16 */
"CF_DIBV5" /* 17 */
};
static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
const char* name)
{
UINT32 index;
wClipboardFormat* format = NULL;
if (!clipboard)
return NULL;
if (formatId)
{
for (index = 0; index < clipboard->numFormats; index++)
{
if (formatId == clipboard->formats[index].formatId)
{
format = &clipboard->formats[index];
break;
}
}
}
else if (name)
{
for (index = 0; index < clipboard->numFormats; index++)
{
if (strcmp(name, clipboard->formats[index].formatName) == 0)
{
format = &clipboard->formats[index];
break;
}
}
}
else
{
/* special "CF_RAW" case */
if (clipboard->numFormats > 0)
{
format = &clipboard->formats[0];
if (format->formatId)
return NULL;
if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
return format;
}
}
return format;
}
static wClipboardSynthesizer* ClipboardFindSynthesizer(wClipboardFormat* format, UINT32 formatId)
{
UINT32 index;
wClipboardSynthesizer* synthesizer;
if (!format)
return NULL;
if (format->numSynthesizers < 1)
return NULL;
for (index = 0; index < format->numSynthesizers; index++)
{
synthesizer = &(format->synthesizers[index]);
if (formatId == synthesizer->syntheticId)
return synthesizer;
}
return NULL;
}
void ClipboardLock(wClipboard* clipboard)
{
if (!clipboard)
return;
EnterCriticalSection(&(clipboard->lock));
}
void ClipboardUnlock(wClipboard* clipboard)
{
if (!clipboard)
return;
LeaveCriticalSection(&(clipboard->lock));
}
BOOL ClipboardEmpty(wClipboard* clipboard)
{
if (!clipboard)
return FALSE;
if (clipboard->data)
{
free((void*)clipboard->data);
clipboard->data = NULL;
}
clipboard->size = 0;
clipboard->formatId = 0;
clipboard->sequenceNumber++;
return TRUE;
}
UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
{
if (!clipboard)
return 0;
return clipboard->numFormats;
}
UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
{
UINT32 index;
UINT32* pFormatIds;
wClipboardFormat* format;
if (!clipboard)
return 0;
if (!ppFormatIds)
return 0;
pFormatIds = *ppFormatIds;
if (!pFormatIds)
{
pFormatIds = calloc(clipboard->numFormats, sizeof(UINT32));
if (!pFormatIds)
return 0;
*ppFormatIds = pFormatIds;
}
for (index = 0; index < clipboard->numFormats; index++)
{
format = &(clipboard->formats[index]);
pFormatIds[index] = format->formatId;
}
return clipboard->numFormats;
}
UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name)
{
wClipboardFormat* format;
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, 0, name);
if (format)
return format->formatId;
if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
{
UINT32 numFormats = clipboard->maxFormats * 2;
wClipboardFormat* tmpFormat;
tmpFormat =
(wClipboardFormat*)realloc(clipboard->formats, numFormats * sizeof(wClipboardFormat));
if (!tmpFormat)
return 0;
clipboard->formats = tmpFormat;
clipboard->maxFormats = numFormats;
}
format = &(clipboard->formats[clipboard->numFormats]);
ZeroMemory(format, sizeof(wClipboardFormat));
if (name)
{
format->formatName = _strdup(name);
if (!format->formatName)
return 0;
}
format->formatId = clipboard->nextFormatId++;
clipboard->numFormats++;
return format->formatId;
}
BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
{
UINT32 index;
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
if (!clipboard)
return FALSE;
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return FALSE;
if (format->formatId == syntheticId)
return FALSE;
synthesizer = ClipboardFindSynthesizer(format, formatId);
if (!synthesizer)
{
wClipboardSynthesizer* tmpSynthesizer;
UINT32 numSynthesizers = format->numSynthesizers + 1;
tmpSynthesizer = (wClipboardSynthesizer*)realloc(
format->synthesizers, numSynthesizers * sizeof(wClipboardSynthesizer));
if (!tmpSynthesizer)
return FALSE;
format->synthesizers = tmpSynthesizer;
format->numSynthesizers = numSynthesizers;
index = numSynthesizers - 1;
synthesizer = &(format->synthesizers[index]);
}
ZeroMemory(synthesizer, sizeof(wClipboardSynthesizer));
synthesizer->syntheticId = syntheticId;
synthesizer->pfnSynthesize = pfnSynthesize;
return TRUE;
}
UINT32 ClipboardCountFormats(wClipboard* clipboard)
{
UINT32 count;
wClipboardFormat* format;
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
if (!format)
return 0;
count = 1 + format->numSynthesizers;
return count;
}
UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
{
UINT32 index;
UINT32 count;
UINT32* pFormatIds;
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
if (!format)
return 0;
count = 1 + format->numSynthesizers;
if (!ppFormatIds)
return 0;
pFormatIds = *ppFormatIds;
if (!pFormatIds)
{
pFormatIds = calloc(count, sizeof(UINT32));
if (!pFormatIds)
return 0;
*ppFormatIds = pFormatIds;
}
pFormatIds[0] = format->formatId;
for (index = 1; index < count; index++)
{
synthesizer = &(format->synthesizers[index - 1]);
pFormatIds[index] = synthesizer->syntheticId;
}
return count;
}
static BOOL ClipboardInitFormats(wClipboard* clipboard)
{
UINT32 formatId = 0;
wClipboardFormat* format;
if (!clipboard)
return FALSE;
for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
{
format = &(clipboard->formats[clipboard->numFormats]);
ZeroMemory(format, sizeof(wClipboardFormat));
format->formatId = formatId;
format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
if (!format->formatName)
goto error;
}
if (!ClipboardInitSynthesizers(clipboard))
goto error;
return TRUE;
error:
for (formatId = 0; formatId < clipboard->numFormats; formatId++)
{
free(clipboard->formats[formatId].formatName);
free(clipboard->formats[formatId].synthesizers);
}
return FALSE;
}
UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name)
{
wClipboardFormat* format;
if (!clipboard)
return 0;
format = ClipboardFindFormat(clipboard, 0, name);
if (!format)
return 0;
return format->formatId;
}
const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
{
wClipboardFormat* format;
if (!clipboard)
return NULL;
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return NULL;
return format->formatName;
}
void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
{
UINT32 SrcSize = 0;
UINT32 DstSize = 0;
void* pSrcData = NULL;
void* pDstData = NULL;
wClipboardFormat* format;
wClipboardSynthesizer* synthesizer;
if (!clipboard)
return NULL;
if (!pSize)
return NULL;
*pSize = 0;
format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL);
if (!format)
return NULL;
SrcSize = clipboard->size;
pSrcData = (void*)clipboard->data;
if (formatId == format->formatId)
{
DstSize = SrcSize;
pDstData = malloc(DstSize);
if (!pDstData)
return NULL;
CopyMemory(pDstData, pSrcData, SrcSize);
*pSize = DstSize;
}
else
{
synthesizer = ClipboardFindSynthesizer(format, formatId);
if (!synthesizer || !synthesizer->pfnSynthesize)
return NULL;
DstSize = SrcSize;
pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
if (pDstData)
*pSize = DstSize;
}
return pDstData;
}
BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32 size)
{
wClipboardFormat* format;
if (!clipboard)
return FALSE;
format = ClipboardFindFormat(clipboard, formatId, NULL);
if (!format)
return FALSE;
free((void*)clipboard->data);
clipboard->data = malloc(size);
if (!clipboard->data)
return FALSE;
memcpy(clipboard->data, data, size);
clipboard->size = size;
clipboard->formatId = formatId;
clipboard->sequenceNumber++;
return TRUE;
}
UINT64 ClipboardGetOwner(wClipboard* clipboard)
{
if (!clipboard)
return 0;
return clipboard->ownerId;
}
void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
{
if (!clipboard)
return;
clipboard->ownerId = ownerId;
}
wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
{
if (!clipboard)
return NULL;
return &clipboard->delegate;
}
static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
{
/*
* There can be only one local file subsystem active.
* Return as soon as initialization succeeds.
*/
#ifdef WITH_WCLIPBOARD_POSIX
if (ClipboardInitPosixFileSubsystem(clipboard))
{
WLog_INFO(TAG, "initialized POSIX local file subsystem");
return;
}
else
{
WLog_WARN(TAG, "failed to initialize POSIX local file subsystem");
}
#endif
WLog_INFO(TAG, "failed to initialize local file subsystem, file transfer not available");
}
wClipboard* ClipboardCreate(void)
{
wClipboard* clipboard;
clipboard = (wClipboard*)calloc(1, sizeof(wClipboard));
if (!clipboard)
return NULL;
clipboard->nextFormatId = 0xC000;
clipboard->sequenceNumber = 0;
if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
goto error_free_clipboard;
clipboard->numFormats = 0;
clipboard->maxFormats = 64;
clipboard->formats = (wClipboardFormat*)calloc(clipboard->maxFormats, sizeof(wClipboardFormat));
if (!clipboard->formats)
goto error_free_lock;
if (!ClipboardInitFormats(clipboard))
goto error_free_formats;
clipboard->delegate.clipboard = clipboard;
ClipboardInitLocalFileSubsystem(clipboard);
return clipboard;
error_free_formats:
free(clipboard->formats);
error_free_lock:
DeleteCriticalSection(&(clipboard->lock));
error_free_clipboard:
free(clipboard);
return NULL;
}
void ClipboardDestroy(wClipboard* clipboard)
{
UINT32 index;
wClipboardFormat* format;
if (!clipboard)
return;
ArrayList_Free(clipboard->localFiles);
clipboard->localFiles = NULL;
for (index = 0; index < clipboard->numFormats; index++)
{
format = &(clipboard->formats[index]);
free((void*)format->formatName);
if (format->synthesizers)
{
free(format->synthesizers);
format->synthesizers = NULL;
format->numSynthesizers = 0;
}
}
free((void*)clipboard->data);
clipboard->data = NULL;
clipboard->size = 0;
clipboard->numFormats = 0;
free(clipboard->formats);
DeleteCriticalSection(&(clipboard->lock));
free(clipboard);
}