20#include <freerdp/config.h>
27#include <winpr/synch.h>
28#include <winpr/print.h>
29#include <winpr/stream.h>
30#include <winpr/cmdline.h>
31#include <winpr/collections.h>
33#include <freerdp/addin.h>
34#include <freerdp/client/channels.h>
35#include <freerdp/client/geometry.h>
36#include <freerdp/channels/log.h>
38#define TAG CHANNELS_TAG("geometry.client")
40#include "geometry_main.h"
45 GeometryClientContext* context;
48static UINT32 mappedGeometryHash(
const void* v)
50 const UINT64* g = (
const UINT64*)v;
51 return (UINT32)((*g >> 32) + (*g & 0xffffffff));
54static BOOL mappedGeometryKeyCompare(
const void* v1,
const void* v2)
56 const UINT64* g1 = (
const UINT64*)v1;
57 const UINT64* g2 = (
const UINT64*)v2;
68 WINPR_ASSERT(rgndata);
72 WLog_Print(logger, WLOG_ERROR,
"invalid RGNDATA");
73 return ERROR_INVALID_DATA;
76 const UINT32 dwSize = Stream_Get_UINT32(s);
80 WLog_Print(logger, WLOG_ERROR,
"invalid RGNDATA dwSize");
81 return ERROR_INVALID_DATA;
84 const UINT32 iType = Stream_Get_UINT32(s);
86 if (iType != RDH_RECTANGLE)
88 WLog_Print(logger, WLOG_ERROR,
"iType %" PRIu32
" for RGNDATA is not supported", iType);
89 return ERROR_UNSUPPORTED_TYPE;
92 rgndata->nRectCount = Stream_Get_UINT32(s);
93 Stream_Seek_UINT32(s);
95 const INT32 x = Stream_Get_INT32(s);
96 const INT32 y = Stream_Get_INT32(s);
97 const INT32 right = Stream_Get_INT32(s);
98 const INT32 bottom = Stream_Get_INT32(s);
99 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
101 WLog_Print(logger, WLOG_ERROR,
"x=%" PRId32
" or y=%" PRId32
" does not fit into INT16",
103 return ERROR_INVALID_DATA;
105 const INT32 w = right - x;
106 const INT32 h = bottom - y;
107 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
109 WLog_Print(logger, WLOG_ERROR,
"w=%" PRId32
" or h=%" PRId32
" does not fit into INT16",
111 return ERROR_INVALID_DATA;
113 if ((x > right) || (y > bottom))
115 WLog_Print(logger, WLOG_ERROR,
116 "One of x=%" PRId32
" > right=%" PRId32
" or y=%" PRId32
117 " > bottom=%" PRId32,
118 x, right, y, bottom);
119 return ERROR_INVALID_DATA;
121 rgndata->boundingRect.x = (INT16)x;
122 rgndata->boundingRect.y = (INT16)y;
123 rgndata->boundingRect.width = (INT16)w;
124 rgndata->boundingRect.height = (INT16)h;
128 if (len / (4 * 4) < rgndata->nRectCount)
130 WLog_Print(logger, WLOG_ERROR,
"not enough data for region rectangles");
131 return ERROR_INVALID_DATA;
134 if (rgndata->nRectCount)
136 RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount *
sizeof(
RDP_RECT));
140 WLog_Print(logger, WLOG_ERROR,
"unable to allocate memory for %" PRIu32
" RECTs",
141 rgndata->nRectCount);
142 return CHANNEL_RC_NO_MEMORY;
144 rgndata->rects = tmp;
146 for (UINT32 i = 0; i < rgndata->nRectCount; i++)
148 RDP_RECT* rect = &rgndata->rects[i];
150 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 16))
151 return CHANNEL_RC_NULL_DATA;
153 const INT32 x = Stream_Get_INT32(s);
154 const INT32 y = Stream_Get_INT32(s);
155 const INT32 right = Stream_Get_INT32(s);
156 const INT32 bottom = Stream_Get_INT32(s);
157 if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
159 WLog_Print(logger, WLOG_ERROR,
"x=%" PRId32
" or y=%" PRId32
" does not fit INT16",
161 return ERROR_INVALID_DATA;
163 const INT32 w = right - x;
164 const INT32 h = bottom - y;
165 if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
166 return ERROR_INVALID_DATA;
168 if ((x > right) || (y > bottom))
170 WLog_Print(logger, WLOG_ERROR,
171 "One of x=%" PRId32
" > right=%" PRId32
", y=%" PRId32
172 " > bottom=%" PRId32,
173 x, right, y, bottom);
174 return ERROR_INVALID_DATA;
178 rect->width = (INT16)w;
179 rect->height = (INT16)h;
183 return CHANNEL_RC_OK;
193 UINT ret = CHANNEL_RC_OK;
195 WINPR_ASSERT(callback);
196 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)callback->plugin;
197 WINPR_ASSERT(geometry);
199 wLog* logger = geometry->base.log;
200 GeometryClientContext* context = (GeometryClientContext*)geometry->base.iface.pInterface;
201 WINPR_ASSERT(context);
203 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 4))
204 return ERROR_INVALID_DATA;
206 const UINT32 length = Stream_Get_UINT32(s);
208 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, (length - 4)))
210 WLog_Print(logger, WLOG_ERROR,
"invalid packet length");
211 return ERROR_INVALID_DATA;
214 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 20))
215 return ERROR_INVALID_DATA;
217 context->remoteVersion = Stream_Get_UINT32(s);
218 const UINT64
id = Stream_Get_UINT64(s);
219 const UINT32 updateType = Stream_Get_UINT32(s);
220 Stream_Seek_UINT32(s);
222 MAPPED_GEOMETRY* mappedGeometry = HashTable_GetItemValue(context->geometries, &
id);
224 if (updateType == GEOMETRY_CLEAR)
228 WLog_Print(logger, WLOG_ERROR,
229 "geometry 0x%" PRIx64
" not found here, ignoring clear command",
id);
230 return CHANNEL_RC_OK;
233 WLog_Print(logger, WLOG_DEBUG,
"clearing geometry 0x%" PRIx64
"",
id);
235 if (mappedGeometry->MappedGeometryClear &&
236 !mappedGeometry->MappedGeometryClear(mappedGeometry))
237 return ERROR_INTERNAL_ERROR;
239 if (!HashTable_Remove(context->geometries, &
id))
240 WLog_Print(logger, WLOG_ERROR,
"geometry not removed from geometries");
242 else if (updateType == GEOMETRY_UPDATE)
249 WLog_Print(logger, WLOG_DEBUG,
"creating geometry 0x%" PRIx64
"",
id);
250 mappedGeometry = calloc(1,
sizeof(MAPPED_GEOMETRY));
252 return CHANNEL_RC_NO_MEMORY;
254 mappedGeometry->refCounter = 1;
255 mappedGeometry->mappingId = id;
257 if (!HashTable_Insert(context->geometries, &(mappedGeometry->mappingId),
260 WLog_Print(logger, WLOG_ERROR,
261 "unable to register geometry 0x%" PRIx64
" in the table",
id);
262 free(mappedGeometry);
263 return CHANNEL_RC_NO_MEMORY;
268 WLog_Print(logger, WLOG_DEBUG,
"updating geometry 0x%" PRIx64
"",
id);
271 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 48))
274 return ERROR_INVALID_DATA;
277 mappedGeometry->topLevelId = Stream_Get_UINT64(s);
279 mappedGeometry->left = Stream_Get_INT32(s);
280 mappedGeometry->top = Stream_Get_INT32(s);
281 mappedGeometry->right = Stream_Get_INT32(s);
282 mappedGeometry->bottom = Stream_Get_INT32(s);
284 if (mappedGeometry->left > mappedGeometry->right)
286 WLog_Print(logger, WLOG_ERROR,
"invalid left=%" PRId32
" > right=%" PRId32,
287 mappedGeometry->left, mappedGeometry->right);
288 return ERROR_INVALID_DATA;
290 if (mappedGeometry->top > mappedGeometry->bottom)
292 WLog_Print(logger, WLOG_ERROR,
"invalid top=%" PRId32
" > bottom=%" PRId32,
293 mappedGeometry->top, mappedGeometry->bottom);
294 return ERROR_INVALID_DATA;
297 mappedGeometry->topLevelLeft = Stream_Get_INT32(s);
298 mappedGeometry->topLevelTop = Stream_Get_INT32(s);
299 mappedGeometry->topLevelRight = Stream_Get_INT32(s);
300 mappedGeometry->topLevelBottom = Stream_Get_INT32(s);
302 if (mappedGeometry->topLevelLeft > mappedGeometry->topLevelRight)
304 WLog_Print(logger, WLOG_ERROR,
305 "invalid topLevelLeft=%" PRId32
" > topLevelRight=%" PRId32,
306 mappedGeometry->topLevelLeft, mappedGeometry->topLevelRight);
307 return ERROR_INVALID_DATA;
309 if (mappedGeometry->topLevelTop > mappedGeometry->topLevelBottom)
311 WLog_Print(logger, WLOG_ERROR,
312 "invalid topLevelTop=%" PRId32
" > topLevelBottom=%" PRId32,
313 mappedGeometry->topLevelTop, mappedGeometry->topLevelBottom);
314 return ERROR_INVALID_DATA;
317 const UINT32 geometryType = Stream_Get_UINT32(s);
318 if (geometryType != 0x02)
320 WLog_Print(logger, WLOG_DEBUG,
"geometryType should be set to 0x02 and is 0x%" PRIx32,
322 return ERROR_INVALID_DATA;
325 const UINT32 cbGeometryBuffer = Stream_Get_UINT32(s);
326 if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, cbGeometryBuffer))
329 return ERROR_INVALID_DATA;
332 if (cbGeometryBuffer > 0)
334 ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry);
335 if (ret != CHANNEL_RC_OK)
340 freerdp_rgndata_reset(&mappedGeometry->geometry);
345 if (context->MappedGeometryAdded &&
346 !context->MappedGeometryAdded(context, mappedGeometry))
348 WLog_Print(logger, WLOG_ERROR,
"geometry added callback failed");
349 ret = ERROR_INTERNAL_ERROR;
354 if (mappedGeometry->MappedGeometryUpdate &&
355 !mappedGeometry->MappedGeometryUpdate(mappedGeometry))
357 WLog_Print(logger, WLOG_ERROR,
"geometry update callback failed");
358 ret = ERROR_INTERNAL_ERROR;
364 WLog_Print(logger, WLOG_ERROR,
"unknown updateType=%" PRIu32
"", updateType);
376static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
379 return geometry_recv_pdu(callback, data);
387static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
389 free(pChannelCallback);
390 return CHANNEL_RC_OK;
393static void mappedGeometryUnref_void(
void* arg)
395 MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg;
396 mappedGeometryUnref(g);
403static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received,
405 geometry_on_close,
nullptr };
407static void geometry_plugin_free(GeometryClientContext* context)
411 HashTable_Free(context->geometries);
416 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
421 geometry_plugin_free(geometry->context);
425 rdpSettings* settings)
427 GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
430 WINPR_UNUSED(settings);
432 GeometryClientContext* context =
433 (GeometryClientContext*)calloc(1,
sizeof(GeometryClientContext));
437 context->geometries = HashTable_New(FALSE);
438 if (!context->geometries)
441 if (!HashTable_SetHashFunction(context->geometries, mappedGeometryHash))
445 wObject* obj = HashTable_KeyObject(context->geometries);
449 wObject* obj = HashTable_ValueObject(context->geometries);
452 context->handle = (
void*)geometry;
454 geometry->context = context;
455 geometry->base.iface.pInterface = (
void*)context;
457 return CHANNEL_RC_OK;
460 geometry_plugin_free(context);
461 return CHANNEL_RC_NO_MEMORY;
469FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
471 return freerdp_generic_DVCPluginEntryWithOptions(
472 pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME,
sizeof(GEOMETRY_PLUGIN),
474 GENERIC_CHANNEL_ENTRY_OPTION_MULTI_INSTANCE);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals