From a6cfd3c49af9432627dc8b66afe28f0cdcbe3530 Mon Sep 17 00:00:00 2001 From: David Fort Date: Wed, 6 Dec 2017 13:51:45 +0100 Subject: [PATCH] geometry: a skeleton for the MS-RDPEGT channel The base for implementing the MS-RDPEGT client channel that allows to track window geometry. --- channels/geometry/CMakeLists.txt | 22 ++ channels/geometry/ChannelOptions.cmake | 12 + channels/geometry/client/CMakeLists.txt | 39 +++ channels/geometry/client/geometry_main.c | 365 +++++++++++++++++++++++ channels/geometry/client/geometry_main.h | 34 +++ client/common/cmdline.c | 16 + include/freerdp/channels/geometry.h | 63 ++++ include/freerdp/client/geometry.h | 42 +++ 8 files changed, 593 insertions(+) create mode 100644 channels/geometry/CMakeLists.txt create mode 100644 channels/geometry/ChannelOptions.cmake create mode 100644 channels/geometry/client/CMakeLists.txt create mode 100755 channels/geometry/client/geometry_main.c create mode 100644 channels/geometry/client/geometry_main.h create mode 100644 include/freerdp/channels/geometry.h create mode 100644 include/freerdp/client/geometry.h diff --git a/channels/geometry/CMakeLists.txt b/channels/geometry/CMakeLists.txt new file mode 100644 index 000000000..7ddea6d5e --- /dev/null +++ b/channels/geometry/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2017 David Fort +# +# 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. + +define_channel("geometry") + +if(WITH_CLIENT_CHANNELS) + add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/geometry/ChannelOptions.cmake b/channels/geometry/ChannelOptions.cmake new file mode 100644 index 000000000..6a09d147f --- /dev/null +++ b/channels/geometry/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT OFF) +set(OPTION_CLIENT_DEFAULT ON) +set(OPTION_SERVER_DEFAULT OFF) + +define_channel_options(NAME "geometry" TYPE "dynamic" + DESCRIPTION "Geometry tracking Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPEGT]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_client_options(${OPTION_CLIENT_DEFAULT}) +define_channel_server_options(${OPTION_SERVER_DEFAULT}) diff --git a/channels/geometry/client/CMakeLists.txt b/channels/geometry/client/CMakeLists.txt new file mode 100644 index 000000000..ea28bff9d --- /dev/null +++ b/channels/geometry/client/CMakeLists.txt @@ -0,0 +1,39 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2017 David Fort +# +# 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. + +define_channel_client("geometry") + +set(${MODULE_PREFIX}_SRCS + geometry_main.c + geometry_main.h) + +include_directories(..) + +add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") + + + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + + +if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS) + install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") diff --git a/channels/geometry/client/geometry_main.c b/channels/geometry/client/geometry_main.c new file mode 100755 index 000000000..f5a728aad --- /dev/null +++ b/channels/geometry/client/geometry_main.c @@ -0,0 +1,365 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking Virtual Channel Extension + * + * Copyright 2017 David Fort + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TAG CHANNELS_TAG("geometry.client") + +#include "geometry_main.h" + +struct _GEOMETRY_CHANNEL_CALLBACK +{ + IWTSVirtualChannelCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; + IWTSVirtualChannel* channel; +}; +typedef struct _GEOMETRY_CHANNEL_CALLBACK GEOMETRY_CHANNEL_CALLBACK; + +struct _GEOMETRY_LISTENER_CALLBACK +{ + IWTSListenerCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; + GEOMETRY_CHANNEL_CALLBACK* channel_callback; +}; +typedef struct _GEOMETRY_LISTENER_CALLBACK GEOMETRY_LISTENER_CALLBACK; + +struct _GEOMETRY_PLUGIN +{ + IWTSPlugin iface; + + IWTSListener* listener; + GEOMETRY_LISTENER_CALLBACK* listener_callback; + + GeometryClientContext *context; +}; +typedef struct _GEOMETRY_PLUGIN GEOMETRY_PLUGIN; + + +static UINT32 geometry_read_RGNDATA(wStream *s, UINT32 len, FREERDP_RGNDATA *rgndata) +{ + UINT32 dwSize, iType; + INT32 right, bottom; + + if (len < 32) + { + WLog_ERR(TAG, "invalid RGNDATA"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, dwSize); + if (dwSize != 32) { + WLog_ERR(TAG, "invalid RGNDATA dwSize"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, iType); + if (iType != RDH_RECTANGLE) { + WLog_ERR(TAG, "iType %"PRIu32" for RGNDATA is not supported", iType); + return ERROR_UNSUPPORTED_TYPE; + } + + Stream_Read_UINT32(s, rgndata->nRectCount); + Stream_Seek_UINT32(s); /* nRgnSize IGNORED */ + Stream_Read_INT32(s, rgndata->boundingRect.x); + Stream_Read_INT32(s, rgndata->boundingRect.y); + Stream_Read_INT32(s, right); + Stream_Read_INT32(s, bottom); + + rgndata->boundingRect.width = right - rgndata->boundingRect.x; + rgndata->boundingRect.height = bottom - rgndata->boundingRect.y; + + len -= 32; + if (len / (4 * 4) < rgndata->nRectCount) + { + WLog_ERR(TAG, "not enough data for region rectangles"); + } + + if (rgndata->nRectCount) + { + int i; + + if (!(rgndata->rects = calloc(rgndata->nRectCount, sizeof(RDP_RECT)))) + { + WLog_ERR(TAG, "unable to allocate memory for %"PRIu32" RECTs", rgndata->nRectCount); + return CHANNEL_RC_NO_MEMORY; + } + + for (i = 0; i < rgndata->nRectCount; i++) + { + Stream_Read_INT32(s, rgndata->rects[i].x); + Stream_Read_INT32(s, rgndata->rects[i].y); + Stream_Read_INT32(s, right); + Stream_Read_INT32(s, bottom); + + rgndata->rects[i].width = right - rgndata->rects[i].x; + rgndata->rects[i].height = bottom - rgndata->rects[i].y; + } + } + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_recv_pdu(GEOMETRY_CHANNEL_CALLBACK* callback, wStream* s) +{ + UINT32 length, cbGeometryBuffer; + MAPPED_GEOMETRY_PACKET packet; + GEOMETRY_PLUGIN* geometry; + GeometryClientContext *context; + UINT ret; + + geometry = (GEOMETRY_PLUGIN*) callback->plugin; + context = (GeometryClientContext *)geometry->iface.pInterface; + + if (Stream_GetRemainingLength(s) < 4) + { + WLog_ERR(TAG, "not enough remaining data"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + if (length < 73 || Stream_GetRemainingLength(s) < (length-4)) + { + WLog_ERR(TAG, "invalid packet length"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, packet.version); + Stream_Read_UINT64(s, packet.mappingId); + Stream_Read_UINT32(s, packet.updateType); + Stream_Seek_UINT32(s); /* flags */ + Stream_Read_UINT64(s, packet.topLevelId); + + Stream_Read_INT32(s, packet.left); + Stream_Read_INT32(s, packet.top); + Stream_Read_INT32(s, packet.right); + Stream_Read_INT32(s, packet.bottom); + + Stream_Read_INT32(s, packet.topLevelLeft); + Stream_Read_INT32(s, packet.topLevelTop); + Stream_Read_INT32(s, packet.topLevelRight); + Stream_Read_INT32(s, packet.topLevelBottom); + + Stream_Read_UINT32(s, packet.geometryType); + + Stream_Read_UINT32(s, cbGeometryBuffer); + if (Stream_GetRemainingLength(s) < cbGeometryBuffer) + { + WLog_ERR(TAG, "invalid packet length"); + return ERROR_INVALID_DATA; + } + + ZeroMemory(&packet.geometry, sizeof(packet.geometry)); + if (cbGeometryBuffer) + { + ret = geometry_read_RGNDATA(s, cbGeometryBuffer, &packet.geometry); + if (ret != CHANNEL_RC_OK) + return ret; + } + + if (context->MappedGeometryPacket) + ret = context->MappedGeometryPacket(context, &packet); + + free(packet.geometry.rects); + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data) +{ + GEOMETRY_CHANNEL_CALLBACK* callback = (GEOMETRY_CHANNEL_CALLBACK*) pChannelCallback; + + return geometry_recv_pdu(callback, data); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback) +{ + free(pChannelCallback); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, + IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept, + IWTSVirtualChannelCallback** ppCallback) +{ + GEOMETRY_CHANNEL_CALLBACK* callback; + GEOMETRY_LISTENER_CALLBACK* listener_callback = (GEOMETRY_LISTENER_CALLBACK*) pListenerCallback; + + callback = (GEOMETRY_CHANNEL_CALLBACK*) calloc(1, sizeof(GEOMETRY_CHANNEL_CALLBACK)); + + if (!callback) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + callback->iface.OnDataReceived = geometry_on_data_received; + callback->iface.OnClose = geometry_on_close; + callback->plugin = listener_callback->plugin; + callback->channel_mgr = listener_callback->channel_mgr; + callback->channel = pChannel; + listener_callback->channel_callback = callback; + + *ppCallback = (IWTSVirtualChannelCallback*) callback; + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr) +{ + UINT status; + GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin; + + geometry->listener_callback = (GEOMETRY_LISTENER_CALLBACK*) calloc(1, sizeof(GEOMETRY_LISTENER_CALLBACK)); + if (!geometry->listener_callback) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + geometry->listener_callback->iface.OnNewChannelConnection = geometry_on_new_channel_connection; + geometry->listener_callback->plugin = pPlugin; + geometry->listener_callback->channel_mgr = pChannelMgr; + + status = pChannelMgr->CreateListener(pChannelMgr, GEOMETRY_DVC_CHANNEL_NAME, 0, + (IWTSListenerCallback*) geometry->listener_callback, &(geometry->listener)); + + geometry->listener->pInterface = geometry->iface.pInterface; + + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT geometry_plugin_terminated(IWTSPlugin* pPlugin) +{ + GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*) pPlugin; + free(geometry->listener_callback); + free(geometry->iface.pInterface); + free(pPlugin); + return CHANNEL_RC_OK; +} + +/** + * Channel Client Interface + */ + + +#ifdef BUILTIN_CHANNELS +#define DVCPluginEntry geometry_DVCPluginEntry +#else +#define DVCPluginEntry FREERDP_API DVCPluginEntry +#endif + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) +{ + UINT error = CHANNEL_RC_OK; + GEOMETRY_PLUGIN* geometry; + GeometryClientContext* context; + + geometry = (GEOMETRY_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "geometry"); + if (!geometry) + { + geometry = (GEOMETRY_PLUGIN*) calloc(1, sizeof(GEOMETRY_PLUGIN)); + if (!geometry) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + geometry->iface.Initialize = geometry_plugin_initialize; + geometry->iface.Connected = NULL; + geometry->iface.Disconnected = NULL; + geometry->iface.Terminated = geometry_plugin_terminated; + + context = (GeometryClientContext*) calloc(1, sizeof(GeometryClientContext)); + if (!context) + { + WLog_ERR(TAG, "calloc failed!"); + free(geometry); + return CHANNEL_RC_NO_MEMORY; + } + + context->handle = (void*) geometry; + + geometry->iface.pInterface = (void*) context; + geometry->context = context; + + error = pEntryPoints->RegisterPlugin(pEntryPoints, "geometry", (IWTSPlugin*) geometry); + } + else + { + WLog_ERR(TAG, "could not get geometry Plugin."); + return CHANNEL_RC_BAD_CHANNEL; + } + + return error; +} diff --git a/channels/geometry/client/geometry_main.h b/channels/geometry/client/geometry_main.h new file mode 100644 index 000000000..1fb5321a2 --- /dev/null +++ b/channels/geometry/client/geometry_main.h @@ -0,0 +1,34 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking virtual channel extension + * + * Copyright 2017 David Fort + * + * 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_CHANNEL_GEOMETRY_CLIENT_MAIN_H +#define FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + + +#endif /* FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H */ + diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 29075ff13..6db75b97f 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -101,6 +101,7 @@ static COMMAND_LINE_ARGUMENT_A args[] = { "gateway-usage-method", COMMAND_LINE_VALUE_REQUIRED, "direct|detect", NULL, NULL, -1, "gum", "Gateway usage method" }, { "gd", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Gateway domain" }, { "gdi", COMMAND_LINE_VALUE_REQUIRED, "sw|hw", NULL, NULL, -1, NULL, "GDI rendering" }, + { "geometry", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Geometry tracking channel" }, { "gestures", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Consume multitouch input locally" }, #ifdef WITH_GFX_H264 { "gfx", COMMAND_LINE_VALUE_OPTIONAL, "RFX|AVC420|AVC444", NULL, NULL, -1, NULL, "RDP8 graphics pipeline (experimental)" }, @@ -931,6 +932,10 @@ static int freerdp_client_command_line_post_filter(void* context, { settings->SupportDisplayControl = TRUE; } + CommandLineSwitchCase(arg, "geometry") + { + settings->SupportGeometryTracking = TRUE; + } CommandLineSwitchCase(arg, "sound") { char** p; @@ -3151,6 +3156,17 @@ BOOL freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings) return FALSE; } + if (settings->SupportGeometryTracking) + { + char* p[1]; + int count; + count = 1; + p[0] = "geometry"; + + if (!freerdp_client_add_dynamic_channel(settings, count, p)) + return FALSE; + } + if (settings->DynamicChannelCount) settings->SupportDynamicChannels = TRUE; diff --git a/include/freerdp/channels/geometry.h b/include/freerdp/channels/geometry.h new file mode 100644 index 000000000..ec4e02a37 --- /dev/null +++ b/include/freerdp/channels/geometry.h @@ -0,0 +1,63 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking Virtual Channel Extension + * + * Copyright 2017 David Fort + * + * 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_CHANNEL_GEOMETRY_H +#define FREERDP_CHANNEL_GEOMETRY_H + +#include +#include + +#define GEOMETRY_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::Geometry::v08.01" + +enum +{ + GEOMETRY_UPDATE = 1, + GEOMETRY_CLEAR = 2 +}; + +enum +{ + RDH_RECTANGLE = 1 +}; + +struct _FREERDP_RGNDATA +{ + RDP_RECT boundingRect; + UINT32 nRectCount; + RDP_RECT *rects; +}; + +typedef struct _FREERDP_RGNDATA FREERDP_RGNDATA; + +struct _MAPPED_GEOMETRY_PACKET +{ + UINT32 version; + UINT64 mappingId; + UINT32 updateType; + UINT64 topLevelId; + INT32 left, top, right, bottom; + INT32 topLevelLeft, topLevelTop, topLevelRight, topLevelBottom; + UINT32 geometryType; + + FREERDP_RGNDATA geometry; +}; + +typedef struct _MAPPED_GEOMETRY_PACKET MAPPED_GEOMETRY_PACKET; + +#endif /* FREERDP_CHANNEL_GEOMETRY_H */ diff --git a/include/freerdp/client/geometry.h b/include/freerdp/client/geometry.h new file mode 100644 index 000000000..000d5bac1 --- /dev/null +++ b/include/freerdp/client/geometry.h @@ -0,0 +1,42 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Geometry tracking Virtual Channel Extension + * + * Copyright 2017 David Fort + * + * 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_CHANNELS_CLIENT_GEOMETRY_H +#define FREERDP_CHANNELS_CLIENT_GEOMETRY_H + +#include + +/** + * Client Interface + */ +typedef struct _geometry_client_context GeometryClientContext; + + +typedef UINT (*pcMappedGeometryPacket)(GeometryClientContext* context, MAPPED_GEOMETRY_PACKET *packet); + +struct _geometry_client_context +{ + void* handle; + void* custom; + + pcMappedGeometryPacket MappedGeometryPacket; +}; + +#endif /* FREERDP_CHANNELS_CLIENT_GEOMETRY_H */ +