Implemented wayland clipboard.

This commit is contained in:
Armin Novak 2018-12-19 16:18:13 +01:00
parent 0cba9edc99
commit ca2e8e4bc2
6 changed files with 924 additions and 0 deletions

View File

@ -29,6 +29,8 @@ set(${MODULE_PREFIX}_SRCS
wlf_disp.h wlf_disp.h
wlf_input.c wlf_input.c
wlf_input.h wlf_input.h
wlf_cliprdr.c
wlf_cliprdr.h
wlf_channels.c wlf_channels.c
wlf_channels.h wlf_channels.h
) )

View File

@ -24,6 +24,7 @@
#include <freerdp/gdi/gfx.h> #include <freerdp/gdi/gfx.h>
#include "wlf_channels.h" #include "wlf_channels.h"
#include "wlf_cliprdr.h"
#include "wlf_disp.h" #include "wlf_disp.h"
#include "wlfreerdp.h" #include "wlfreerdp.h"
@ -81,6 +82,7 @@ void wlf_OnChannelConnectedEventHandler(void* context,
} }
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
{ {
wlf_cliprdr_init(wlf->clipboard, (CliprdrClientContext*)e->pInterface);
} }
else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0) else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
{ {
@ -116,6 +118,7 @@ void wlf_OnChannelDisconnectedEventHandler(void* context,
} }
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
{ {
wlf_cliprdr_uninit(wlf->clipboard, (CliprdrClientContext*)e->pInterface);
} }
else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0) else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
{ {

View File

@ -0,0 +1,866 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Wayland Clipboard Redirection
*
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
* Copyright 2018 Thincast Technologies GmbH
*
* 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 <stdlib.h>
#include <winpr/crt.h>
#include <winpr/image.h>
#include <winpr/stream.h>
#include <winpr/clipboard.h>
#include <freerdp/log.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/channels/channels.h>
#include <freerdp/channels/cliprdr.h>
#include "wlf_cliprdr.h"
#define MAX_CLIPBOARD_FORMATS 255
static const char* mime_text[] =
{
"text/plain",
"text/plain;charset=utf-8",
"UTF8_STRING",
"COMPOUND_TEXT",
"TEXT",
"STRING"
};
static const char* mime_image[] =
{
"image/png",
"image/bmp",
"image/x-bmp",
"image/x-MS-bmp",
"image/x-icon",
"image/x-ico",
"image/x-win-bitmap",
"image/vmd.microsoft.icon",
"application/ico",
"image/ico",
"image/icon",
"image/jpeg",
"image/tiff"
};
static const char* mime_html[] =
{
"text/html"
};
struct wlf_clipboard
{
wlfContext* wfc;
rdpChannels* channels;
CliprdrClientContext* context;
wLog* log;
UwacSeat* seat;
wClipboard* system;
wClipboardDelegate* delegate;
size_t numClientFormats;
CLIPRDR_FORMAT* clientFormats;
size_t numServerFormats;
CLIPRDR_FORMAT* serverFormats;
BOOL sync;
/* File clipping */
BOOL streams_supported;
BOOL file_formats_registered;
/* Server response stuff */
FILE* responseFile;
UINT32 responseFormat;
const char* responseMime;
};
static BOOL wlf_mime_is_text(const char* mime)
{
size_t x;
for (x=0; x<ARRAYSIZE(mime_text); x++)
{
if (strcmp(mime, mime_text[x]) == 0)
return TRUE;
}
return FALSE;
}
static BOOL wlf_mime_is_image(const char* mime)
{
size_t x;
for (x=0; x<ARRAYSIZE(mime_image); x++)
{
if (strcmp(mime, mime_image[x]) == 0)
return TRUE;
}
return FALSE;
}
static BOOL wlf_mime_is_html(const char* mime)
{
size_t x;
for (x=0; x<ARRAYSIZE(mime_html); x++)
{
if (strcmp(mime, mime_html[x]) == 0)
return TRUE;
}
return FALSE;
}
static void wlf_cliprdr_free_server_formats(wfClipboard* clipboard)
{
if (clipboard && clipboard->serverFormats)
{
size_t j;
for (j = 0; j < clipboard->numServerFormats; j++)
{
CLIPRDR_FORMAT* format = &clipboard->serverFormats[j];
free(format->formatName);
}
free(clipboard->serverFormats);
clipboard->serverFormats = NULL;
clipboard->numServerFormats = 0;
}
UwacClipboardOfferDestroy(clipboard->seat);
}
static void wlf_cliprdr_free_client_formats(wfClipboard* clipboard)
{
if (clipboard && clipboard->numClientFormats)
{
size_t j;
for (j = 0; j < clipboard->numClientFormats; j++)
{
CLIPRDR_FORMAT* format = &clipboard->clientFormats[j];
free(format->formatName);
}
free(clipboard->clientFormats);
clipboard->clientFormats = NULL;
clipboard->numClientFormats = 0;
}
UwacClipboardOfferDestroy(clipboard->seat);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_send_client_format_list(wfClipboard* clipboard)
{
CLIPRDR_FORMAT_LIST formatList = { 0 };
formatList.msgFlags = CB_RESPONSE_OK;
formatList.numFormats = (UINT32)clipboard->numClientFormats;
formatList.formats = clipboard->clientFormats;
return clipboard->context->ClientFormatList(clipboard->context, &formatList);
}
static void wfl_cliprdr_add_client_format_id(wfClipboard* clipboard, UINT32 formatId)
{
size_t x;
CLIPRDR_FORMAT* format;
const char* name = ClipboardGetFormatName(clipboard->system, formatId);
for (x=0; x<clipboard->numClientFormats; x++)
{
format = &clipboard->clientFormats[x];
if (format->formatId == formatId)
return;
}
format = realloc(clipboard->clientFormats, (clipboard->numClientFormats + 1) * sizeof(CLIPRDR_FORMAT));
if (!format)
return;
clipboard->clientFormats = format;
format = &clipboard->clientFormats[clipboard->numClientFormats++];
format->formatId = formatId;
format->formatName = NULL;
if (name && (formatId >= CF_MAX))
format->formatName = _strdup(name);
}
static void wlf_cliprdr_add_client_format(wfClipboard* clipboard, const char* mime)
{
if (wlf_mime_is_html(mime))
{
UINT32 formatId = ClipboardGetFormatId(clipboard->system, "HTML Format");
wfl_cliprdr_add_client_format_id(clipboard, formatId);
}
else if (wlf_mime_is_text(mime))
{
wfl_cliprdr_add_client_format_id(clipboard, CF_TEXT);
wfl_cliprdr_add_client_format_id(clipboard, CF_OEMTEXT);
wfl_cliprdr_add_client_format_id(clipboard, CF_UNICODETEXT);
}
else if (wlf_mime_is_image(mime))
{
UINT32 formatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
wfl_cliprdr_add_client_format_id(clipboard, formatId);
wfl_cliprdr_add_client_format_id(clipboard, CF_DIB);
}
wlf_cliprdr_send_client_format_list(clipboard);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_send_data_request(wfClipboard* clipboard,
UINT32 formatId)
{
CLIPRDR_FORMAT_DATA_REQUEST request;
ZeroMemory(&request, sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
request.requestedFormatId = formatId;
return clipboard->context->ClientFormatDataRequest(clipboard->context,
&request);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_send_data_response(wfClipboard* clipboard, const BYTE* data,
size_t size)
{
CLIPRDR_FORMAT_DATA_RESPONSE response;
ZeroMemory(&response, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE));
response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
response.dataLen = size;
response.requestedFormatData = data;
return clipboard->context->ClientFormatDataResponse(clipboard->context,
&response);
}
BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* event)
{
if (!clipboard || !event)
return FALSE;
if (!clipboard)
return FALSE;
switch (event->type)
{
case UWAC_EVENT_CLIPBOARD_AVAILABLE:
clipboard->seat = event->seat;
return TRUE;
case UWAC_EVENT_CLIPBOARD_OFFER:
WLog_Print(clipboard->log, WLOG_INFO, "client announces mime %s", event->mime);
wlf_cliprdr_add_client_format(clipboard, event->mime);
return TRUE;
case UWAC_EVENT_CLIPBOARD_SELECT:
WLog_Print(clipboard->log, WLOG_DEBUG, "client announces new data");
wlf_cliprdr_free_client_formats(clipboard);
return TRUE;
default:
return FALSE;
}
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
{
CLIPRDR_CAPABILITIES capabilities;
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
capabilities.cCapabilitiesSets = 1;
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*) &
(generalCapabilitySet);
generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
generalCapabilitySet.capabilitySetLength = 12;
generalCapabilitySet.version = CB_CAPS_VERSION_2;
generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
if (clipboard->streams_supported && clipboard->file_formats_registered)
generalCapabilitySet.generalFlags |=
CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
return clipboard->context->ClientCapabilities(clipboard->context,
&capabilities);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_send_client_format_list_response(wfClipboard* clipboard,
BOOL status)
{
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
formatListResponse.msgFlags = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
formatListResponse.dataLen = 0;
return clipboard->context->ClientFormatListResponse(clipboard->context,
&formatListResponse);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_monitor_ready(CliprdrClientContext* context,
const CLIPRDR_MONITOR_READY* monitorReady)
{
wfClipboard* clipboard = (wfClipboard*) context->custom;
UINT ret;
if ((ret = wlf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK)
return ret;
if ((ret = wlf_cliprdr_send_client_format_list(clipboard)) != CHANNEL_RC_OK)
return ret;
clipboard->sync = TRUE;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_server_capabilities(CliprdrClientContext* context,
const CLIPRDR_CAPABILITIES* capabilities)
{
UINT32 i;
const BYTE* capsPtr = (const BYTE*) capabilities->capabilitySets;
wfClipboard* clipboard = (wfClipboard*) context->custom;
clipboard->streams_supported = FALSE;
for (i = 0; i < capabilities->cCapabilitiesSets; i++)
{
const CLIPRDR_CAPABILITY_SET* caps = (const CLIPRDR_CAPABILITY_SET*) capsPtr;
if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
{
const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps = (const CLIPRDR_GENERAL_CAPABILITY_SET*) caps;
if (generalCaps->generalFlags & CB_STREAM_FILECLIP_ENABLED)
{
clipboard->streams_supported = TRUE;
}
}
capsPtr += caps->capabilitySetLength;
}
return CHANNEL_RC_OK;
}
static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* mime, int fd)
{
wfClipboard* clipboard = (wfClipboard*)context;
size_t x;
WINPR_UNUSED(seat);
clipboard->responseMime = NULL;
for (x = 0; x < ARRAYSIZE(mime_html); x++)
{
const char* mime_cur = mime_html[x];
if (strcmp(mime_cur, mime) == 0)
{
clipboard->responseMime = mime_cur;
clipboard->responseFormat = ClipboardGetFormatId(clipboard->system, "HTML Format");
break;
}
}
for (x = 0; x < ARRAYSIZE(mime_text); x++)
{
const char* mime_cur = mime_text[x];
if (strcmp(mime_cur, mime) == 0)
{
clipboard->responseMime = mime_cur;
clipboard->responseFormat = CF_UNICODETEXT;
break;
}
}
for (x = 0; x < ARRAYSIZE(mime_image); x++)
{
const char* mime_cur = mime_image[x];
if (strcmp(mime_cur, mime) == 0)
{
clipboard->responseMime = mime_cur;
clipboard->responseFormat = CF_DIB;
break;
}
}
if (clipboard->responseMime != NULL)
{
clipboard->responseFile = fdopen(fd, "w");
if (clipboard->responseFile)
wlf_cliprdr_send_data_request(clipboard, clipboard->responseFormat);
}
}
static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
{
WINPR_UNUSED(seat);
WINPR_UNUSED(context);
}
/**
* Called when the clipboard changes server side.
*
* Clear the local clipboard offer and replace it with a new one
* that announces the formats we get listed here.
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_server_format_list(CliprdrClientContext* context,
const CLIPRDR_FORMAT_LIST* formatList)
{
UINT32 i;
wfClipboard* clipboard;
BOOL html = FALSE;
BOOL text = FALSE;
BOOL image = FALSE;
if (!context || !context->custom)
return ERROR_INVALID_PARAMETER;
clipboard = (wfClipboard*) context->custom;
wlf_cliprdr_free_server_formats(clipboard);
if (!(clipboard->serverFormats = (CLIPRDR_FORMAT*) calloc(
formatList->numFormats, sizeof(CLIPRDR_FORMAT))))
{
WLog_Print(clipboard->log, WLOG_ERROR, "failed to allocate %"PRIuz" CLIPRDR_FORMAT structs",
clipboard->numServerFormats);
return CHANNEL_RC_NO_MEMORY;
}
clipboard->numServerFormats = formatList->numFormats;
if (!clipboard->seat)
return ERROR_INTERNAL_ERROR;
for (i = 0; i < formatList->numFormats; i++)
{
const CLIPRDR_FORMAT* format = &formatList->formats[i];
CLIPRDR_FORMAT* srvFormat = &clipboard->serverFormats[i];
srvFormat->formatId = format->formatId;
if (format->formatName)
{
srvFormat->formatName = _strdup(format->formatName);
if (!srvFormat->formatName)
{
wlf_cliprdr_free_server_formats(clipboard);
return CHANNEL_RC_NO_MEMORY;
}
}
if (format->formatName)
{
if (strcmp(format->formatName, "HTML Format") == 0)
{
text = TRUE;
html = TRUE;
}
}
else
{
switch (format->formatId)
{
case CF_TEXT:
case CF_OEMTEXT:
case CF_UNICODETEXT:
text = TRUE;
break;
case CF_DIB:
image = TRUE;
break;
default:
break;
}
}
}
if (html)
{
size_t x;
for (x = 0; x < ARRAYSIZE(mime_html); x++)
UwacClipboardOfferCreate(clipboard->seat, mime_html[x]);
}
if (text)
{
size_t x;
for (x = 0; x < ARRAYSIZE(mime_text); x++)
UwacClipboardOfferCreate(clipboard->seat, mime_text[x]);
}
if (image)
{
size_t x;
for (x = 0; x < ARRAYSIZE(mime_image); x++)
UwacClipboardOfferCreate(clipboard->seat, mime_image[x]);
}
UwacClipboardOfferAnnounce(clipboard->seat, clipboard, wlf_cliprdr_transfer_data,
wlf_cliprdr_cancel_data);
return wlf_cliprdr_send_client_format_list_response(clipboard, TRUE);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_server_format_list_response(CliprdrClientContext*
context, const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
{
//wfClipboard* clipboard = (wfClipboard*) context->custom;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_server_format_data_request(CliprdrClientContext* context,
const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
{
UINT rc;
char* data;
LPWSTR cdata;
size_t size;
const char* mime;
UINT32 formatId = formatDataRequest->requestedFormatId;
wfClipboard* clipboard = (wfClipboard*) context->custom;
switch(formatId)
{
case CF_TEXT:
case CF_OEMTEXT:
case CF_UNICODETEXT:
mime = "text/plain;charset=utf-8";
break;
case CF_DIB:
case CF_DIBV5:
mime = "image/bmp";
break;
default:
if (formatId == ClipboardGetFormatId(clipboard->system, "HTML Format"))
mime = "text/html";
else if (formatId == ClipboardGetFormatId(clipboard->system, "image/bmp"))
mime = "image/bmp";
else
mime = ClipboardGetFormatName(clipboard->system, formatId);
break;
}
data = UwacClipboardDataGet(clipboard->seat, mime, &size);
if (!data)
return ERROR_INTERNAL_ERROR;
switch (formatId)
{
case CF_UNICODETEXT:
size = ConvertToUnicode(CP_UTF8, 0, data, size, &cdata, 0);
free(data);
data = cdata;
size *= sizeof(WCHAR);
break;
default:
// TODO: Image conversions
break;
}
rc = wlf_cliprdr_send_data_response(clipboard, data, size);
free(data);
return rc;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT wlf_cliprdr_server_format_data_response(CliprdrClientContext*
context, const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
{
UINT rc = ERROR_INTERNAL_ERROR;
BOOL freedata = FALSE;
UINT32 size = formatDataResponse->dataLen;
LPSTR data = formatDataResponse->requestedFormatData;
const WCHAR* wdata = (WCHAR*)formatDataResponse->requestedFormatData;
wfClipboard* clipboard = (wfClipboard*) context->custom;
switch (clipboard->responseFormat)
{
case CF_UNICODETEXT:
size = ConvertFromUnicode(CP_UTF8, 0, wdata, size / sizeof(WCHAR), &data, 0, NULL, NULL);
freedata = TRUE;
break;
default:
// TODO: Image conversions
break;
}
fwrite(data, 1, size, clipboard->responseFile);
fclose(clipboard->responseFile);
rc = CHANNEL_RC_OK;
fail:
if (freedata)
free(data);
return rc;
}
static UINT wlf_cliprdr_server_file_size_request(wfClipboard* clipboard,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
wClipboardFileSizeRequest request;
ZeroMemory(&request, sizeof(request));
request.streamId = fileContentsRequest->streamId;
request.listIndex = fileContentsRequest->listIndex;
if (fileContentsRequest->cbRequested != sizeof(UINT64))
{
WLog_Print(clipboard->log, WLOG_WARN, "unexpected FILECONTENTS_SIZE request: %"PRIu32" bytes",
fileContentsRequest->cbRequested);
}
return clipboard->delegate->ClientRequestFileSize(clipboard->delegate, &request);
}
static UINT wlf_cliprdr_server_file_range_request(wfClipboard* clipboard,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
wClipboardFileRangeRequest request;
ZeroMemory(&request, sizeof(request));
request.streamId = fileContentsRequest->streamId;
request.listIndex = fileContentsRequest->listIndex;
request.nPositionLow = fileContentsRequest->nPositionLow;
request.nPositionHigh = fileContentsRequest->nPositionHigh;
request.cbRequested = fileContentsRequest->cbRequested;
return clipboard->delegate->ClientRequestFileRange(clipboard->delegate, &request);
}
static UINT wlf_cliprdr_send_file_contents_failure(CliprdrClientContext* context,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response;
ZeroMemory(&response, sizeof(response));
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = fileContentsRequest->streamId;
response.dwFlags = fileContentsRequest->dwFlags;
return context->ClientFileContentsResponse(context, &response);
}
static UINT wlf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
UINT error = NO_ERROR;
wfClipboard* clipboard = context->custom;
/*
* MS-RDPECLIP 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST):
* The FILECONTENTS_SIZE and FILECONTENTS_RANGE flags MUST NOT be set at the same time.
*/
if ((fileContentsRequest->dwFlags & (FILECONTENTS_SIZE | FILECONTENTS_RANGE)) ==
(FILECONTENTS_SIZE | FILECONTENTS_RANGE))
{
WLog_Print(clipboard->log, WLOG_ERROR, "invalid CLIPRDR_FILECONTENTS_REQUEST.dwFlags");
return wlf_cliprdr_send_file_contents_failure(context, fileContentsRequest);
}
if (fileContentsRequest->dwFlags & FILECONTENTS_SIZE)
error = wlf_cliprdr_server_file_size_request(clipboard, fileContentsRequest);
if (fileContentsRequest->dwFlags & FILECONTENTS_RANGE)
error = wlf_cliprdr_server_file_range_request(clipboard, fileContentsRequest);
if (error)
{
WLog_Print(clipboard->log, WLOG_ERROR, "failed to handle CLIPRDR_FILECONTENTS_REQUEST: 0x%08X",
error);
return wlf_cliprdr_send_file_contents_failure(context, fileContentsRequest);
}
return CHANNEL_RC_OK;
}
static UINT wlf_cliprdr_clipboard_file_size_success(wClipboardDelegate* delegate,
const wClipboardFileSizeRequest* request, UINT64 fileSize)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response;
wfClipboard* clipboard = delegate->custom;
ZeroMemory(&response, sizeof(response));
response.msgFlags = CB_RESPONSE_OK;
response.streamId = request->streamId;
response.dwFlags = FILECONTENTS_SIZE;
response.cbRequested = sizeof(UINT64);
response.requestedData = (BYTE*) &fileSize;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT wlf_cliprdr_clipboard_file_size_failure(wClipboardDelegate* delegate,
const wClipboardFileSizeRequest* request, UINT errorCode)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response;
wfClipboard* clipboard = delegate->custom;
ZeroMemory(&response, sizeof(response));
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = request->streamId;
response.dwFlags = FILECONTENTS_SIZE;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT wlf_cliprdr_clipboard_file_range_success(wClipboardDelegate* delegate,
const wClipboardFileRangeRequest* request, const BYTE* data, UINT32 size)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response;
wfClipboard* clipboard = delegate->custom;
ZeroMemory(&response, sizeof(response));
response.msgFlags = CB_RESPONSE_OK;
response.streamId = request->streamId;
response.dwFlags = FILECONTENTS_RANGE;
response.cbRequested = size;
response.requestedData = (BYTE*) data;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
static UINT wlf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate,
const wClipboardFileRangeRequest* request, UINT errorCode)
{
CLIPRDR_FILE_CONTENTS_RESPONSE response;
wfClipboard* clipboard = delegate->custom;
ZeroMemory(&response, sizeof(response));
response.msgFlags = CB_RESPONSE_FAIL;
response.streamId = request->streamId;
response.dwFlags = FILECONTENTS_RANGE;
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
wfClipboard* wlf_clipboard_new(wlfContext* wfc)
{
rdpChannels* channels;
wfClipboard* clipboard;
if (!(clipboard = (wfClipboard*) calloc(1, sizeof(wfClipboard))))
{
WLog_Print(clipboard->log, WLOG_ERROR, "failed to allocate wfClipboard data");
return NULL;
}
clipboard->wfc = wfc;
channels = wfc->context.channels;
clipboard->log = WLog_Get(TAG);
clipboard->channels = channels;
clipboard->system = ClipboardCreate();
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
clipboard->delegate->custom = clipboard;
clipboard->delegate->ClipboardFileSizeSuccess = wlf_cliprdr_clipboard_file_size_success;
clipboard->delegate->ClipboardFileSizeFailure = wlf_cliprdr_clipboard_file_size_failure;
clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success;
clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure;
return clipboard;
error:
wlf_clipboard_free(clipboard);
return NULL;
}
void wlf_clipboard_free(wfClipboard* clipboard)
{
if (!clipboard)
return;
wlf_cliprdr_free_server_formats(clipboard);
wlf_cliprdr_free_client_formats(clipboard);
ClipboardDestroy(clipboard->system);
free(clipboard);
}
BOOL wlf_cliprdr_init(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
{
if (!cliprdr || !clipboard)
return FALSE;
clipboard->context = cliprdr;
cliprdr->custom = (void*) clipboard;
cliprdr->MonitorReady = wlf_cliprdr_monitor_ready;
cliprdr->ServerCapabilities = wlf_cliprdr_server_capabilities;
cliprdr->ServerFormatList = wlf_cliprdr_server_format_list;
cliprdr->ServerFormatListResponse = wlf_cliprdr_server_format_list_response;
cliprdr->ServerFormatDataRequest = wlf_cliprdr_server_format_data_request;
cliprdr->ServerFormatDataResponse = wlf_cliprdr_server_format_data_response;
cliprdr->ServerFileContentsRequest = wlf_cliprdr_server_file_contents_request;
return TRUE;
}
BOOL wlf_cliprdr_uninit(wfClipboard* clipboard, CliprdrClientContext* cliprdr)
{
if (cliprdr)
cliprdr->custom = NULL;
if (clipboard)
clipboard->context = NULL;
return TRUE;
}

View File

@ -0,0 +1,36 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Wayland Clipboard Redirection
*
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
* Copyright 2018 Thincast Technologies GmbH
*
* 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.
*/
#ifndef FREERDP_CLIENT_WAYLAND_CLIPRDR_H
#define FREERDP_CLIENT_WAYLAND_CLIPRDR_H
#include "wlfreerdp.h"
#include <freerdp/client/cliprdr.h>
wfClipboard* wlf_clipboard_new(wlfContext* wlc);
void wlf_clipboard_free(wfClipboard* clipboard);
BOOL wlf_cliprdr_init(wfClipboard* clipboard, CliprdrClientContext* cliprdr);
BOOL wlf_cliprdr_uninit(wfClipboard* clipboard, CliprdrClientContext* cliprdr);
BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* event);
#endif /* FREERDP_CLIENT_WAYLAND_CLIPRDR_H */

View File

@ -36,6 +36,7 @@
#include "wlfreerdp.h" #include "wlfreerdp.h"
#include "wlf_input.h" #include "wlf_input.h"
#include "wlf_cliprdr.h"
#include "wlf_disp.h" #include "wlf_disp.h"
#include "wlf_channels.h" #include "wlf_channels.h"
@ -232,6 +233,11 @@ static BOOL wl_post_connect(freerdp* instance)
if (!(context->disp = wlf_disp_new(context))) if (!(context->disp = wlf_disp_new(context)))
return FALSE; return FALSE;
context->clipboard = wlf_clipboard_new(context);
if (!context->clipboard)
return FALSE;
return wl_update_buffer(context, 0, 0, gdi->width, gdi->height); return wl_update_buffer(context, 0, 0, gdi->width, gdi->height);
} }
@ -247,6 +253,7 @@ static void wl_post_disconnect(freerdp* instance)
context = (wlfContext*) instance->context; context = (wlfContext*) instance->context;
gdi_free(instance); gdi_free(instance);
wlf_clipboard_free(context->clipboard);
wlf_disp_free(context->disp); wlf_disp_free(context->disp);
if (context->window) if (context->window)
@ -329,6 +336,14 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
break; break;
case UWAC_EVENT_CLIPBOARD_AVAILABLE:
case UWAC_EVENT_CLIPBOARD_OFFER:
case UWAC_EVENT_CLIPBOARD_SELECT:
if (!wlf_cliprdr_handle_event(context->clipboard, &event.clipboard))
return FALSE;
break;
default: default:
break; break;
} }

View File

@ -31,6 +31,7 @@
#define TAG CLIENT_TAG("wayland") #define TAG CLIENT_TAG("wayland")
typedef struct wlf_context wlfContext; typedef struct wlf_context wlfContext;
typedef struct wlf_clipboard wfClipboard;
typedef struct _wlfDispContext wlfDispContext; typedef struct _wlfDispContext wlfDispContext;
struct wlf_context struct wlf_context
@ -49,6 +50,7 @@ struct wlf_context
RdpeiClientContext* rdpei; RdpeiClientContext* rdpei;
RdpgfxClientContext* gfx; RdpgfxClientContext* gfx;
EncomspClientContext* encomsp; EncomspClientContext* encomsp;
wfClipboard* clipboard;
wlfDispContext* disp; wlfDispContext* disp;
wLog* log; wLog* log;
}; };