channels/cliprdr: remove dependency on deprecated eventing system, svc_plugin.c
This commit is contained in:
parent
769eb38217
commit
24a752a708
@ -22,10 +22,6 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
@ -36,93 +32,6 @@
|
||||
#include "cliprdr_main.h"
|
||||
#include "cliprdr_format.h"
|
||||
|
||||
#define CFSTR_HTML "H\0T\0M\0L\0 \0F\0o\0r\0m\0a\0t\0\0"
|
||||
#define CFSTR_PNG "P\0N\0G\0\0"
|
||||
#define CFSTR_JPEG "J\0F\0I\0F\0\0"
|
||||
#define CFSTR_GIF "G\0I\0F\0\0"
|
||||
|
||||
void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event)
|
||||
{
|
||||
int i;
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending Clipboard Format List");
|
||||
|
||||
if (cb_event->raw_format_data)
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, cb_event->raw_format_data_size);
|
||||
Stream_Write(s, cb_event->raw_format_data, cb_event->raw_format_data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
wStream* body = Stream_New(NULL, 64);
|
||||
|
||||
for (i = 0; i < cb_event->num_formats; i++)
|
||||
{
|
||||
const char* name;
|
||||
int name_length, short_name_length = 32, x;
|
||||
|
||||
switch (cb_event->formats[i])
|
||||
{
|
||||
case CB_FORMAT_HTML:
|
||||
name = CFSTR_HTML;
|
||||
name_length = sizeof(CFSTR_HTML);
|
||||
break;
|
||||
case CB_FORMAT_PNG:
|
||||
name = CFSTR_PNG;
|
||||
name_length = sizeof(CFSTR_PNG);
|
||||
break;
|
||||
case CB_FORMAT_JPEG:
|
||||
name = CFSTR_JPEG;
|
||||
name_length = sizeof(CFSTR_JPEG);
|
||||
break;
|
||||
case CB_FORMAT_GIF:
|
||||
name = CFSTR_GIF;
|
||||
name_length = sizeof(CFSTR_GIF);
|
||||
break;
|
||||
default:
|
||||
name = "\0\0";
|
||||
name_length = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cliprdr->use_long_format_names)
|
||||
{
|
||||
x = (name_length > short_name_length) ?
|
||||
name_length : short_name_length;
|
||||
|
||||
Stream_EnsureRemainingCapacity(body, 4 + short_name_length);
|
||||
Stream_Write_UINT32(body, cb_event->formats[i]);
|
||||
Stream_Write(body, name, x);
|
||||
|
||||
while (x++ < short_name_length)
|
||||
Stream_Write(body, "\0", 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_EnsureRemainingCapacity(body, 4 + name_length);
|
||||
Stream_Write_UINT32(body, cb_event->formats[i]);
|
||||
Stream_Write(body, name, name_length);
|
||||
}
|
||||
}
|
||||
|
||||
Stream_SealLength(body);
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, Stream_Length(body));
|
||||
Stream_Write(s, Stream_Buffer(body), Stream_Length(body));
|
||||
Stream_Free(body, TRUE);
|
||||
}
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
static void cliprdr_send_format_list_response(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending Clipboard Format List Response");
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, CB_RESPONSE_OK, 0);
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
void cliprdr_process_short_format_names(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
|
||||
{
|
||||
int i;
|
||||
@ -281,101 +190,6 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
||||
|
||||
free(formats);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
UINT32 format;
|
||||
BOOL supported;
|
||||
CLIPRDR_FORMAT_NAME* format_name;
|
||||
RDP_CB_FORMAT_LIST_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FormatList, NULL, NULL);
|
||||
|
||||
if (dataLen > 0)
|
||||
{
|
||||
cb_event->raw_format_data = (BYTE*) malloc(dataLen);
|
||||
memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen);
|
||||
cb_event->raw_format_data_size = dataLen;
|
||||
cb_event->raw_format_unicode = (msgFlags & CB_ASCII_NAMES) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
if (cliprdr->use_long_format_names)
|
||||
cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
else
|
||||
cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
|
||||
if (cliprdr->num_format_names > 0)
|
||||
cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names);
|
||||
|
||||
cb_event->num_formats = 0;
|
||||
|
||||
for (i = 0; i < cliprdr->num_format_names; i++)
|
||||
{
|
||||
supported = TRUE;
|
||||
format_name = &cliprdr->format_names[i];
|
||||
format = format_name->id;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case CF_TEXT:
|
||||
case CF_DIB:
|
||||
case CF_UNICODETEXT:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (format_name->length > 0)
|
||||
{
|
||||
DEBUG_CLIPRDR("format: %s", format_name->name);
|
||||
|
||||
if (strcmp(format_name->name, "HTML Format") == 0)
|
||||
{
|
||||
format = CB_FORMAT_HTML;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "PNG") == 0)
|
||||
{
|
||||
format = CB_FORMAT_PNG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "JFIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_JPEG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "GIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_GIF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
supported = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (supported)
|
||||
cb_event->formats[cb_event->num_formats++] = format;
|
||||
|
||||
if (format_name->length > 0)
|
||||
free(format_name->name);
|
||||
}
|
||||
|
||||
free(cliprdr->format_names);
|
||||
cliprdr->format_names = NULL;
|
||||
|
||||
cliprdr->num_format_names = 0;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %d",
|
||||
cb_event->num_formats);
|
||||
|
||||
cliprdr_send_format_list_response(cliprdr);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
@ -384,8 +198,6 @@ void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/hh872154.aspx */
|
||||
|
||||
if (context->custom)
|
||||
{
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
|
||||
@ -397,17 +209,6 @@ void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
if (context->ServerFormatListResponse)
|
||||
context->ServerFormatListResponse(context, &formatListResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
|
||||
{
|
||||
/* In case of an error the clipboard will not be synchronized with the server.
|
||||
* Post this event to restart format negotiation and data transfer. */
|
||||
|
||||
wMessage* event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
@ -429,46 +230,6 @@ void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN
|
||||
if (context->ServerFormatDataRequest)
|
||||
context->ServerFormatDataRequest(context, &formatDataRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_DATA_REQUEST_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataRequest, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->format);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending Format Data Response");
|
||||
|
||||
if (cb_event->size > 0)
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, cb_event->size);
|
||||
Stream_Write(s, cb_event->data, cb_event->size);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_FAIL, 0);
|
||||
}
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending Format Data Request");
|
||||
|
||||
s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
|
||||
Stream_Write_UINT32(s, cb_event->format);
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
@ -497,20 +258,4 @@ void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
||||
|
||||
free(formatDataResponse.requestedFormatData);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_DATA_RESPONSE_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataResponse, NULL, NULL);
|
||||
|
||||
if (dataLen > 0)
|
||||
{
|
||||
cb_event->size = dataLen;
|
||||
cb_event->data = (BYTE*) malloc(dataLen);
|
||||
CopyMemory(cb_event->data, Stream_Pointer(s), dataLen);
|
||||
}
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,9 @@
|
||||
#ifndef __CLIPRDR_FORMAT_H
|
||||
#define __CLIPRDR_FORMAT_H
|
||||
|
||||
void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event);
|
||||
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
|
||||
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event);
|
||||
|
||||
void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event);
|
||||
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
|
||||
#endif /* __CLIPRDR_FORMAT_H */
|
||||
|
@ -22,10 +22,6 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
@ -55,19 +51,22 @@ static const char* const CB_MSG_TYPE_STRINGS[] =
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
CliprdrClientContext* pInterface;
|
||||
rdpSvcPlugin* plugin = (rdpSvcPlugin*) cliprdr;
|
||||
pInterface = (CliprdrClientContext*) plugin->channel_entry_points.pInterface;
|
||||
pInterface = (CliprdrClientContext*) cliprdr->channelEntryPoints.pInterface;
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
s = Stream_New(NULL, dataLen + 8);
|
||||
|
||||
Stream_Write_UINT16(s, msgType);
|
||||
Stream_Write_UINT16(s, msgFlags);
|
||||
|
||||
/* Write actual length after the entire packet has been constructed. */
|
||||
Stream_Seek(s, 4);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -75,21 +74,36 @@ void cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* s)
|
||||
{
|
||||
int pos;
|
||||
UINT32 dataLen;
|
||||
UINT32 status = 0;
|
||||
|
||||
pos = Stream_GetPosition(s);
|
||||
|
||||
dataLen = pos - 8;
|
||||
|
||||
Stream_SetPosition(s, 4);
|
||||
Stream_Write_UINT32(s, dataLen);
|
||||
Stream_SetPosition(s, pos);
|
||||
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
WLog_DBG(TAG, "Cliprdr Sending (%d bytes)", dataLen + 8);
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), dataLen + 8);
|
||||
#endif
|
||||
svc_plugin_send((rdpSvcPlugin*) cliprdr, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_connect(rdpSvcPlugin* plugin)
|
||||
{
|
||||
DEBUG_CLIPRDR("connecting");
|
||||
if (!cliprdr)
|
||||
{
|
||||
status = CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = cliprdr->channelEntryPoints.pVirtualChannelWrite(cliprdr->OpenHandle,
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
WLog_ERR(TAG, "cliprdr_packet_send: VirtualChannelWrite failed %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_print_general_capability_flags(UINT32 flags)
|
||||
@ -152,13 +166,6 @@ static int cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream* s
|
||||
if (context->ServerCapabilities)
|
||||
context->ServerCapabilities(context, &capabilities);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_CLIP_CAPS* caps_event;
|
||||
caps_event = (RDP_CB_CLIP_CAPS*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_ClipCaps, NULL, NULL);
|
||||
caps_event->capabilities = generalFlags;
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) caps_event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -193,27 +200,6 @@ static int cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s, UINT16
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
wStream* s;
|
||||
UINT32 flags;
|
||||
s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
|
||||
DEBUG_CLIPRDR("Sending Capabilities");
|
||||
flags = CB_USE_LONG_FORMAT_NAMES
|
||||
#ifdef _WIN32
|
||||
| CB_STREAM_FILECLIP_ENABLED
|
||||
| CB_FILECLIP_NO_FILE_PATHS
|
||||
#endif
|
||||
;
|
||||
Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */
|
||||
Stream_Write_UINT16(s, 0); /* pad1 */
|
||||
Stream_Write_UINT16(s, CB_CAPSTYPE_GENERAL); /* capabilitySetType */
|
||||
Stream_Write_UINT16(s, CB_CAPSTYPE_GENERAL_LEN); /* lengthCapability */
|
||||
Stream_Write_UINT32(s, CB_CAPS_VERSION_2); /* version */
|
||||
Stream_Write_UINT32(s, flags); /* generalFlags */
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
static int cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UINT16 length, UINT16 flags)
|
||||
{
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
@ -230,16 +216,6 @@ static int cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UIN
|
||||
if (context->MonitorReady)
|
||||
context->MonitorReady(context, &monitorReady);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_MONITOR_READY_EVENT* event;
|
||||
|
||||
if (cliprdr->received_caps)
|
||||
cliprdr_send_clip_caps(cliprdr);
|
||||
|
||||
event = (RDP_CB_MONITOR_READY_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -272,23 +248,6 @@ static int cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream*
|
||||
if (context->ServerFileContentsRequest)
|
||||
context->ServerFileContentsRequest(context, &request);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_FILECONTENTS_REQUEST_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_FILECONTENTS_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FilecontentsRequest, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->streamId);
|
||||
Stream_Read_UINT32(s, cb_event->lindex);
|
||||
Stream_Read_UINT32(s, cb_event->dwFlags);
|
||||
Stream_Read_UINT32(s, cb_event->nPositionLow);
|
||||
Stream_Read_UINT32(s, cb_event->nPositionHigh);
|
||||
Stream_Read_UINT32(s, cb_event->cbRequested);
|
||||
//Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -318,24 +277,6 @@ static int cliprdr_process_filecontents_response(cliprdrPlugin* cliprdr, wStream
|
||||
if (context->ServerFileContentsResponse)
|
||||
context->ServerFileContentsResponse(context, &response);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_FILECONTENTS_RESPONSE_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_FILECONTENTS_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FilecontentsResponse, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->streamId);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
cb_event->size = length - 4;
|
||||
cb_event->data = (BYTE*) malloc(cb_event->size);
|
||||
CopyMemory(cb_event->data, Stream_Pointer(s), cb_event->size);
|
||||
}
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -362,17 +303,6 @@ static int cliprdr_process_lock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UIN
|
||||
if (context->ServerLockClipboardData)
|
||||
context->ServerLockClipboardData(context, &lockClipboardData);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_LOCK_CLIPDATA_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_LOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_LockClipdata, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -399,27 +329,16 @@ static int cliprdr_process_unlock_clipdata(cliprdrPlugin* cliprdr, wStream* s, U
|
||||
if (context->ServerUnlockClipboardData)
|
||||
context->ServerUnlockClipboardData(context, &unlockClipboardData);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_UNLOCK_CLIPDATA_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_UNLOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_UnLockClipdata, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
static void cliprdr_order_recv(cliprdrPlugin* cliprdr, wStream* s)
|
||||
{
|
||||
UINT16 msgType;
|
||||
UINT16 msgFlags;
|
||||
UINT32 dataLen;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) plugin;
|
||||
|
||||
Stream_Read_UINT16(s, msgType);
|
||||
Stream_Read_UINT16(s, msgFlags);
|
||||
Stream_Read_UINT32(s, dataLen);
|
||||
@ -435,142 +354,49 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
case CB_CLIP_CAPS:
|
||||
cliprdr_process_clip_caps(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_MONITOR_READY:
|
||||
cliprdr_process_monitor_ready(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FORMAT_LIST:
|
||||
cliprdr_process_format_list(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FORMAT_LIST_RESPONSE:
|
||||
cliprdr_process_format_list_response(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FORMAT_DATA_REQUEST:
|
||||
cliprdr_process_format_data_request(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FORMAT_DATA_RESPONSE:
|
||||
cliprdr_process_format_data_response(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FILECONTENTS_REQUEST:
|
||||
cliprdr_process_filecontents_request(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FILECONTENTS_RESPONSE:
|
||||
cliprdr_process_filecontents_response(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_LOCK_CLIPDATA:
|
||||
cliprdr_process_lock_clipdata(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_UNLOCK_CLIPDATA:
|
||||
cliprdr_process_unlock_clipdata(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown msgType %d", msgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_request_event(cliprdrPlugin* plugin, RDP_CB_FILECONTENTS_REQUEST_EVENT* event)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending File Contents Request.");
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0, 24);
|
||||
Stream_Write_UINT32(s, event->streamId);
|
||||
Stream_Write_UINT32(s, event->lindex);
|
||||
Stream_Write_UINT32(s, event->dwFlags);
|
||||
Stream_Write_UINT32(s, event->nPositionLow);
|
||||
Stream_Write_UINT32(s, event->nPositionHigh);
|
||||
Stream_Write_UINT32(s, event->cbRequested);
|
||||
//Stream_Write_UINT32(s, event->clipDataId);
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_response_event(cliprdrPlugin* plugin, RDP_CB_FILECONTENTS_RESPONSE_EVENT* event)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending file contents response with size = %d", event->size);
|
||||
|
||||
if (event->size > 0)
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_OK, event->size + 4);
|
||||
Stream_Write_UINT32(s, event->streamId);
|
||||
Stream_Write(s, event->data, event->size);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_FAIL, 0);
|
||||
}
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_lock_clipdata_event(cliprdrPlugin* plugin, RDP_CB_LOCK_CLIPDATA_EVENT* event)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending Lock Request");
|
||||
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
|
||||
Stream_Write_UINT32(s, event->clipDataId);
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_unlock_clipdata_event(cliprdrPlugin* plugin, RDP_CB_UNLOCK_CLIPDATA_EVENT* event)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending UnLock Request");
|
||||
s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
|
||||
Stream_Write_UINT32(s, event->clipDataId);
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_tempdir_event(cliprdrPlugin* plugin, RDP_CB_TEMPDIR_EVENT* event)
|
||||
{
|
||||
wStream* s;
|
||||
DEBUG_CLIPRDR("Sending Temporary Directory.");
|
||||
s = cliprdr_packet_new(CB_TEMP_DIRECTORY, 0, 520);
|
||||
Stream_Write(s, event->dirname, 520);
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_event(rdpSvcPlugin* plugin, wMessage* event)
|
||||
{
|
||||
switch (GetMessageType(event->id))
|
||||
{
|
||||
case CliprdrChannel_FormatList:
|
||||
cliprdr_process_format_list_event((cliprdrPlugin*) plugin, (RDP_CB_FORMAT_LIST_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_DataRequest:
|
||||
cliprdr_process_format_data_request_event((cliprdrPlugin*) plugin, (RDP_CB_DATA_REQUEST_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_DataResponse:
|
||||
cliprdr_process_format_data_response_event((cliprdrPlugin*) plugin, (RDP_CB_DATA_RESPONSE_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_FilecontentsRequest:
|
||||
cliprdr_process_filecontents_request_event((cliprdrPlugin*) plugin, (RDP_CB_FILECONTENTS_REQUEST_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_FilecontentsResponse:
|
||||
cliprdr_process_filecontents_response_event((cliprdrPlugin*) plugin, (RDP_CB_FILECONTENTS_RESPONSE_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_LockClipdata:
|
||||
cliprdr_process_lock_clipdata_event((cliprdrPlugin*) plugin, (RDP_CB_LOCK_CLIPDATA_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_UnLockClipdata:
|
||||
cliprdr_process_unlock_clipdata_event((cliprdrPlugin*) plugin, (RDP_CB_UNLOCK_CLIPDATA_EVENT*) event);
|
||||
break;
|
||||
case CliprdrChannel_TemporaryDirectory:
|
||||
cliprdr_process_tempdir_event((cliprdrPlugin*) plugin, (RDP_CB_TEMPDIR_EVENT*) event);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown event type %d", GetMessageType(event->id));
|
||||
break;
|
||||
}
|
||||
|
||||
freerdp_event_free(event);
|
||||
}
|
||||
|
||||
static void cliprdr_process_terminate(rdpSvcPlugin* plugin)
|
||||
{
|
||||
svc_plugin_terminate(plugin);
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback Interface
|
||||
*/
|
||||
@ -816,6 +642,216 @@ int cliprdr_client_file_contents_response(CliprdrClientContext* context, CLIPRDR
|
||||
return 1;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
static wListDictionary* g_InitHandles;
|
||||
static wListDictionary* g_OpenHandles;
|
||||
|
||||
void cliprdr_add_init_handle_data(void* pInitHandle, void* pUserData)
|
||||
{
|
||||
if (!g_InitHandles)
|
||||
g_InitHandles = ListDictionary_New(TRUE);
|
||||
|
||||
ListDictionary_Add(g_InitHandles, pInitHandle, pUserData);
|
||||
}
|
||||
|
||||
void* cliprdr_get_init_handle_data(void* pInitHandle)
|
||||
{
|
||||
void* pUserData = NULL;
|
||||
pUserData = ListDictionary_GetItemValue(g_InitHandles, pInitHandle);
|
||||
return pUserData;
|
||||
}
|
||||
|
||||
void cliprdr_remove_init_handle_data(void* pInitHandle)
|
||||
{
|
||||
ListDictionary_Remove(g_InitHandles, pInitHandle);
|
||||
}
|
||||
|
||||
void cliprdr_add_open_handle_data(DWORD openHandle, void* pUserData)
|
||||
{
|
||||
void* pOpenHandle = (void*) (size_t) openHandle;
|
||||
|
||||
if (!g_OpenHandles)
|
||||
g_OpenHandles = ListDictionary_New(TRUE);
|
||||
|
||||
ListDictionary_Add(g_OpenHandles, pOpenHandle, pUserData);
|
||||
}
|
||||
|
||||
void* cliprdr_get_open_handle_data(DWORD openHandle)
|
||||
{
|
||||
void* pUserData = NULL;
|
||||
void* pOpenHandle = (void*) (size_t) openHandle;
|
||||
pUserData = ListDictionary_GetItemValue(g_OpenHandles, pOpenHandle);
|
||||
return pUserData;
|
||||
}
|
||||
|
||||
void cliprdr_remove_open_handle_data(DWORD openHandle)
|
||||
{
|
||||
void* pOpenHandle = (void*) (size_t) openHandle;
|
||||
ListDictionary_Remove(g_OpenHandles, pOpenHandle);
|
||||
}
|
||||
|
||||
static void cliprdr_virtual_channel_event_data_received(cliprdrPlugin* cliprdr,
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
wStream* data_in;
|
||||
|
||||
if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (cliprdr->data_in)
|
||||
Stream_Free(cliprdr->data_in, TRUE);
|
||||
|
||||
cliprdr->data_in = Stream_New(NULL, totalLength);
|
||||
}
|
||||
|
||||
data_in = cliprdr->data_in;
|
||||
Stream_EnsureRemainingCapacity(data_in, (int) dataLength);
|
||||
Stream_Write(data_in, pData, dataLength);
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_LAST)
|
||||
{
|
||||
if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_plugin_process_received: read error");
|
||||
}
|
||||
|
||||
cliprdr->data_in = NULL;
|
||||
Stream_SealLength(data_in);
|
||||
Stream_SetPosition(data_in, 0);
|
||||
|
||||
MessageQueue_Post(cliprdr->MsgPipe->In, NULL, 0, (void*) data_in, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE cliprdr_virtual_channel_open_event(DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
cliprdrPlugin* cliprdr;
|
||||
|
||||
cliprdr = (cliprdrPlugin*) cliprdr_get_open_handle_data(openHandle);
|
||||
|
||||
if (!cliprdr)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_virtual_channel_open_event: error no match");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
cliprdr_virtual_channel_event_data_received(cliprdr, pData, dataLength, totalLength, dataFlags);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void* cliprdr_virtual_channel_client_thread(void* arg)
|
||||
{
|
||||
wStream* data;
|
||||
wMessage message;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) arg;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!MessageQueue_Wait(cliprdr->MsgPipe->In))
|
||||
break;
|
||||
|
||||
if (MessageQueue_Peek(cliprdr->MsgPipe->In, &message, TRUE))
|
||||
{
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
if (message.id == 0)
|
||||
{
|
||||
data = (wStream*) message.wParam;
|
||||
cliprdr_order_recv(cliprdr, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cliprdr_virtual_channel_event_connected(cliprdrPlugin* cliprdr, LPVOID pData, UINT32 dataLength)
|
||||
{
|
||||
UINT32 status;
|
||||
|
||||
status = cliprdr->channelEntryPoints.pVirtualChannelOpen(cliprdr->InitHandle,
|
||||
&cliprdr->OpenHandle, cliprdr->channelDef.name, cliprdr_virtual_channel_open_event);
|
||||
|
||||
cliprdr_add_open_handle_data(cliprdr->OpenHandle, cliprdr);
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_virtual_channel_event_connected: open failed: status: %d", status);
|
||||
return;
|
||||
}
|
||||
|
||||
cliprdr->MsgPipe = MessagePipe_New();
|
||||
|
||||
cliprdr->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) cliprdr_virtual_channel_client_thread, (void*) cliprdr, 0, NULL);
|
||||
}
|
||||
|
||||
static void cliprdr_virtual_channel_event_terminated(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
MessagePipe_PostQuit(cliprdr->MsgPipe, 0);
|
||||
WaitForSingleObject(cliprdr->thread, INFINITE);
|
||||
|
||||
MessagePipe_Free(cliprdr->MsgPipe);
|
||||
CloseHandle(cliprdr->thread);
|
||||
|
||||
cliprdr->channelEntryPoints.pVirtualChannelClose(cliprdr->OpenHandle);
|
||||
|
||||
if (cliprdr->data_in)
|
||||
{
|
||||
Stream_Free(cliprdr->data_in, TRUE);
|
||||
cliprdr->data_in = NULL;
|
||||
}
|
||||
|
||||
cliprdr_remove_open_handle_data(cliprdr->OpenHandle);
|
||||
cliprdr_remove_init_handle_data(cliprdr->InitHandle);
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE cliprdr_virtual_channel_init_event(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength)
|
||||
{
|
||||
cliprdrPlugin* cliprdr;
|
||||
|
||||
cliprdr = (cliprdrPlugin*) cliprdr_get_init_handle_data(pInitHandle);
|
||||
|
||||
if (!cliprdr)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_virtual_channel_init_event: error no match");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
cliprdr_virtual_channel_event_connected(cliprdr, pData, dataLength);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
cliprdr_virtual_channel_event_terminated(cliprdr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* cliprdr is always built-in */
|
||||
#define VirtualChannelEntry cliprdr_VirtualChannelEntry
|
||||
|
||||
@ -827,20 +863,13 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
cliprdr = (cliprdrPlugin*) calloc(1, sizeof(cliprdrPlugin));
|
||||
|
||||
cliprdr->plugin.channel_def.options =
|
||||
cliprdr->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
|
||||
strcpy(cliprdr->plugin.channel_def.name, "cliprdr");
|
||||
|
||||
cliprdr->log = WLog_Get("com.freerdp.channels.cliprdr.client");
|
||||
|
||||
cliprdr->plugin.connect_callback = cliprdr_process_connect;
|
||||
cliprdr->plugin.receive_callback = cliprdr_process_receive;
|
||||
cliprdr->plugin.event_callback = cliprdr_process_event;
|
||||
cliprdr->plugin.terminate_callback = cliprdr_process_terminate;
|
||||
strcpy(cliprdr->channelDef.name, "cliprdr");
|
||||
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
|
||||
|
||||
@ -848,7 +877,10 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||
{
|
||||
context = (CliprdrClientContext*) calloc(1, sizeof(CliprdrClientContext));
|
||||
|
||||
context->handle = (void*) cliprdr;
|
||||
context->custom = NULL;
|
||||
|
||||
context->ClientCapabilities = cliprdr_client_capabilities;
|
||||
context->TempDirectory = cliprdr_temp_directory;
|
||||
context->ClientFormatList = cliprdr_client_format_list;
|
||||
@ -859,9 +891,23 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
context->ClientFormatDataResponse = cliprdr_client_format_data_response;
|
||||
context->ClientFileContentsRequest = cliprdr_client_file_contents_request;
|
||||
context->ClientFileContentsResponse = cliprdr_client_file_contents_response;
|
||||
|
||||
*(pEntryPointsEx->ppInterface) = (void*) context;
|
||||
}
|
||||
|
||||
svc_plugin_init((rdpSvcPlugin*) cliprdr, pEntryPoints);
|
||||
cliprdr->log = WLog_Get("com.freerdp.channels.cliprdr.client");
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "VirtualChannelEntry");
|
||||
|
||||
CopyMemory(&(cliprdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP));
|
||||
|
||||
cliprdr->channelEntryPoints.pVirtualChannelInit(&cliprdr->InitHandle,
|
||||
&cliprdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, cliprdr_virtual_channel_init_event);
|
||||
|
||||
cliprdr->channelEntryPoints.pInterface = *(cliprdr->channelEntryPoints.ppInterface);
|
||||
cliprdr->channelEntryPoints.ppInterface = &(cliprdr->channelEntryPoints.pInterface);
|
||||
|
||||
cliprdr_add_init_handle_data(cliprdr->InitHandle, (void*) cliprdr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -23,23 +23,32 @@
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("cliprdr.client")
|
||||
|
||||
struct cliprdr_plugin
|
||||
{
|
||||
rdpSvcPlugin plugin;
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP channelEntryPoints;
|
||||
|
||||
wLog* log;
|
||||
HANDLE thread;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessagePipe* MsgPipe;
|
||||
|
||||
int num_format_names;
|
||||
CLIPRDR_FORMAT_NAME* format_names;
|
||||
|
||||
BOOL received_caps;
|
||||
BOOL use_long_format_names;
|
||||
BOOL stream_fileclip_enabled;
|
||||
BOOL fileclip_no_file_paths;
|
||||
BOOL can_lock_clipdata;
|
||||
CLIPRDR_FORMAT_NAME* format_names;
|
||||
int num_format_names;
|
||||
};
|
||||
typedef struct cliprdr_plugin cliprdrPlugin;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user