FreeRDP
Loading...
Searching...
No Matches
update.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/print.h>
26#include <winpr/synch.h>
27#include <winpr/thread.h>
28#include <winpr/collections.h>
29#include <winpr/assert.h>
30#include <winpr/cast.h>
31
32#include "settings.h"
33#include "update.h"
34#include "surface.h"
35#include "message.h"
36#include "info.h"
37#include "window.h"
38
39#include <freerdp/log.h>
40#include <freerdp/peer.h>
41#include <freerdp/codec/bitmap.h>
42
43#include "../cache/pointer.h"
44#include "../cache/palette.h"
45#include "../cache/bitmap.h"
46
47#define TAG FREERDP_TAG("core.update")
48
49#define FORCE_ASYNC_UPDATE_OFF
50
51#define RDP_STATS_COUNT sizeof(rdp_stats) / sizeof(uint64_t)
52#define bufferlen 64
53
54static INIT_ONCE stats_names_once = INIT_ONCE_STATIC_INIT;
55static char stats_names[RDP_STATS_COUNT][bufferlen];
56
57static BOOL stats_names_generate(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce,
58 WINPR_ATTR_UNUSED PVOID Parameter,
59 WINPR_ATTR_UNUSED PVOID* Context)
60{
61
62 for (size_t index = 0; index < RDP_STATS_COUNT; index++)
63 {
64 char* buffer = stats_names[index];
65
66 const rdp_stats stats = WINPR_C_ARRAY_INIT;
67 size_t limit = ARRAYSIZE(stats.primary);
68 size_t offset = 0;
69 if (index < limit)
70 {
71 const char* str =
72 primary_order_string(WINPR_ASSERTING_INT_CAST(UINT32, index), buffer, bufferlen);
73 if (!str)
74 return FALSE;
75 WINPR_ASSERT(strnlen(buffer, 2) > 0);
76 continue;
77 }
78
79 offset = limit;
80 limit += ARRAYSIZE(stats.secondary);
81 if (index < limit)
82 {
83 const char* str = secondary_order_string(
84 WINPR_ASSERTING_INT_CAST(UINT32, index - offset), buffer, bufferlen);
85 if (!str)
86 return FALSE;
87 WINPR_ASSERT(strnlen(buffer, 2) > 0);
88 continue;
89 }
90
91 offset = limit;
92 limit += ARRAYSIZE(stats.altsec);
93 if (index < limit)
94 {
95 const char* str = altsec_order_string(WINPR_ASSERTING_INT_CAST(BYTE, index - offset),
96 buffer, bufferlen);
97 if (!str)
98 return FALSE;
99 WINPR_ASSERT(strnlen(buffer, 2) > 0);
100 continue;
101 }
102
103 offset = limit;
104 limit += ARRAYSIZE(stats.base);
105 if (index < limit)
106 {
107#define EVCASE(x) \
108 case x: \
109 (void)_snprintf(buffer, bufferlen, "%s", #x); \
110 break
111
112 switch (index - offset)
113 {
114 EVCASE(RDP_STATS_SURFACE_BITS);
115 EVCASE(RDP_STATS_SURFACE_BITS_NSC);
116 EVCASE(RDP_STATS_SURFACE_BITS_RFX);
117 EVCASE(RDP_STATS_SURFACE_BITS_RFX_IMAGE);
118 EVCASE(RDP_STATS_SURFACE_BITS_NONE);
119 EVCASE(RDP_STATS_SURFACE_BITS_UNKNOWN);
120 EVCASE(RDP_STATS_BEGIN_PAINT);
121 EVCASE(RDP_STATS_END_PAINT);
122 EVCASE(RDP_STATS_SET_BOUNDS);
123 EVCASE(RDP_STATS_SYNC);
124 EVCASE(RDP_STATS_RESIZE);
125 EVCASE(RDP_STATS_BITMAP_UPDATE);
126 EVCASE(RDP_STATS_PALETTE);
127 EVCASE(RDP_STATS_REFRESH_RECT);
128 EVCASE(RDP_STATS_SUPPRESS_OUTPUT);
129 EVCASE(RDP_STATS_SURFACE_COMMAND);
130 EVCASE(RDP_STATS_SURFACE_FRAME_MARKER);
131 EVCASE(RDP_STATS_SURFACE_FRAME_ACK);
132 EVCASE(RDP_STATS_POINTER_SYSTEM);
133 EVCASE(RDP_STATS_POINTER_DEFAULT);
134 EVCASE(RDP_STATS_POINTER_POSITION);
135 EVCASE(RDP_STATS_POINTER_COLOR);
136 EVCASE(RDP_STATS_POINTER_CACHED);
137 EVCASE(RDP_STATS_POINTER_NEW);
138 EVCASE(RDP_STATS_POINTER_LARGE);
139 default:
140 (void)_snprintf(buffer, bufferlen, "RDP_STATS_UNUSED");
141 break;
142 }
143#undef EVCASE
144 }
145 else
146 (void)_snprintf(buffer, bufferlen, "RDP_STATS_UNUSED");
147 }
148
149 return TRUE;
150}
151static const char* const UPDATE_TYPE_STRINGS[] = { "Orders", "Bitmap", "Palette", "Synchronize" };
152
153static const char* update_type_to_string(UINT16 updateType)
154{
155 if (updateType >= ARRAYSIZE(UPDATE_TYPE_STRINGS))
156 return "UNKNOWN";
157
158 return UPDATE_TYPE_STRINGS[updateType];
159}
160
161static BOOL update_recv_orders(rdpUpdate* update, wStream* s)
162{
163 UINT16 numberOrders = 0;
164
165 WINPR_ASSERT(update);
166
167 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
168 return FALSE;
169
170 Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
171 Stream_Read_UINT16(s, numberOrders); /* numberOrders (2 bytes) */
172 Stream_Seek_UINT16(s); /* pad2OctetsB (2 bytes) */
173
174 while (numberOrders > 0)
175 {
176 if (!update_recv_order(update, s))
177 {
178 WLog_ERR(TAG, "update_recv_order() failed");
179 return FALSE;
180 }
181
182 numberOrders--;
183 }
184
185 return TRUE;
186}
187
188static BOOL update_read_bitmap_data(rdpUpdate* update, wStream* s, BITMAP_DATA* bitmapData)
189{
190 WINPR_UNUSED(update);
191 WINPR_ASSERT(bitmapData);
192
193 if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
194 return FALSE;
195
196 Stream_Read_UINT16(s, bitmapData->destLeft);
197 Stream_Read_UINT16(s, bitmapData->destTop);
198 Stream_Read_UINT16(s, bitmapData->destRight);
199 Stream_Read_UINT16(s, bitmapData->destBottom);
200 Stream_Read_UINT16(s, bitmapData->width);
201 Stream_Read_UINT16(s, bitmapData->height);
202 Stream_Read_UINT16(s, bitmapData->bitsPerPixel);
203 Stream_Read_UINT16(s, bitmapData->flags);
204 Stream_Read_UINT16(s, bitmapData->bitmapLength);
205
206 if ((bitmapData->width == 0) || (bitmapData->height == 0))
207 {
208 WLog_ERR(TAG, "Invalid BITMAP_DATA: width=%" PRIu16 ", height=%" PRIu16, bitmapData->width,
209 bitmapData->height);
210 return FALSE;
211 }
212
213 if (bitmapData->flags & BITMAP_COMPRESSION)
214 {
215 if (!(bitmapData->flags & NO_BITMAP_COMPRESSION_HDR))
216 {
217 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
218 return FALSE;
219
220 Stream_Read_UINT16(s,
221 bitmapData->cbCompFirstRowSize); /* cbCompFirstRowSize (2 bytes) */
222 Stream_Read_UINT16(s,
223 bitmapData->cbCompMainBodySize); /* cbCompMainBodySize (2 bytes) */
224 Stream_Read_UINT16(s, bitmapData->cbScanWidth); /* cbScanWidth (2 bytes) */
225 Stream_Read_UINT16(s,
226 bitmapData->cbUncompressedSize); /* cbUncompressedSize (2 bytes) */
227 bitmapData->bitmapLength = bitmapData->cbCompMainBodySize;
228 }
229
230 bitmapData->compressed = TRUE;
231 }
232 else
233 bitmapData->compressed = FALSE;
234
235 if (!Stream_CheckAndLogRequiredLength(TAG, s, bitmapData->bitmapLength))
236 return FALSE;
237
238 if (bitmapData->bitmapLength > 0)
239 {
240 bitmapData->bitmapDataStream = malloc(bitmapData->bitmapLength);
241
242 if (!bitmapData->bitmapDataStream)
243 return FALSE;
244
245 memcpy(bitmapData->bitmapDataStream, Stream_ConstPointer(s), bitmapData->bitmapLength);
246 Stream_Seek(s, bitmapData->bitmapLength);
247 }
248
249 return TRUE;
250}
251
252static BOOL update_write_bitmap_data_header(const BITMAP_DATA* bitmapData, wStream* s)
253{
254 WINPR_ASSERT(bitmapData);
255 if (!Stream_EnsureRemainingCapacity(s, 18))
256 return FALSE;
257 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->destLeft));
258 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->destTop));
259 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->destRight));
260 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->destBottom));
261 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->width));
262 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->height));
263 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->bitsPerPixel));
264 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->flags));
265 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->bitmapLength));
266 return TRUE;
267}
268
269static BOOL update_write_bitmap_data_no_comp_header(const BITMAP_DATA* bitmapData, wStream* s)
270{
271 WINPR_ASSERT(bitmapData);
272 if (!Stream_EnsureRemainingCapacity(s, 8))
273 return FALSE;
274
275 Stream_Write_UINT16(
276 s, WINPR_ASSERTING_INT_CAST(
277 uint16_t, bitmapData->cbCompFirstRowSize)); /* cbCompFirstRowSize (2 bytes) */
278 Stream_Write_UINT16(
279 s, WINPR_ASSERTING_INT_CAST(
280 uint16_t, bitmapData->cbCompMainBodySize)); /* cbCompMainBodySize (2 bytes) */
281 Stream_Write_UINT16(
282 s, WINPR_ASSERTING_INT_CAST(uint16_t, bitmapData->cbScanWidth)); /* cbScanWidth (2 bytes) */
283 Stream_Write_UINT16(
284 s, WINPR_ASSERTING_INT_CAST(
285 uint16_t, bitmapData->cbUncompressedSize)); /* cbUncompressedSize (2 bytes) */
286 return TRUE;
287}
288
289static BOOL update_write_bitmap_data(rdpUpdate* update_pub, wStream* s, BITMAP_DATA* bitmapData)
290{
291 rdp_update_internal* update = update_cast(update_pub);
292
293 WINPR_ASSERT(bitmapData);
294
295 if (!Stream_EnsureRemainingCapacity(s, 64 + bitmapData->bitmapLength))
296 return FALSE;
297
298 if (update->common.autoCalculateBitmapData)
299 {
300 bitmapData->flags = 0;
301 bitmapData->cbCompFirstRowSize = 0;
302
303 if (bitmapData->compressed)
304 bitmapData->flags |= BITMAP_COMPRESSION;
305
306 if (update->common.context->settings->NoBitmapCompressionHeader)
307 {
308 bitmapData->flags |= NO_BITMAP_COMPRESSION_HDR;
309 bitmapData->cbCompMainBodySize = bitmapData->bitmapLength;
310 }
311 }
312
313 if (!update_write_bitmap_data_header(bitmapData, s))
314 return FALSE;
315
316 if (bitmapData->flags & BITMAP_COMPRESSION)
317 {
318 if ((bitmapData->flags & NO_BITMAP_COMPRESSION_HDR) == 0)
319 {
320 if (!update_write_bitmap_data_no_comp_header(bitmapData, s))
321 return FALSE;
322 }
323 }
324
325 if (!Stream_EnsureRemainingCapacity(s, bitmapData->bitmapLength))
326 return FALSE;
327 Stream_Write(s, bitmapData->bitmapDataStream, bitmapData->bitmapLength);
328
329 return TRUE;
330}
331
332BITMAP_UPDATE* update_read_bitmap_update(rdpUpdate* update, wStream* s)
333{
334 BITMAP_UPDATE* bitmapUpdate = calloc(1, sizeof(BITMAP_UPDATE));
335 rdp_update_internal* up = update_cast(update);
336
337 if (!bitmapUpdate)
338 goto fail;
339
340 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
341 goto fail;
342
343 Stream_Read_UINT16(s, bitmapUpdate->number); /* numberRectangles (2 bytes) */
344 WLog_Print(up->log, WLOG_TRACE, "BitmapUpdate: %" PRIu32 "", bitmapUpdate->number);
345
346 bitmapUpdate->rectangles = (BITMAP_DATA*)calloc(bitmapUpdate->number, sizeof(BITMAP_DATA));
347
348 if (!bitmapUpdate->rectangles)
349 goto fail;
350
351 /* rectangles */
352 for (UINT32 i = 0; i < bitmapUpdate->number; i++)
353 {
354 if (!update_read_bitmap_data(update, s, &bitmapUpdate->rectangles[i]))
355 goto fail;
356 }
357
358 return bitmapUpdate;
359fail:
360 WINPR_PRAGMA_DIAG_PUSH
361 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
362 free_bitmap_update(update->context, bitmapUpdate);
363 WINPR_PRAGMA_DIAG_POP
364 return nullptr;
365}
366
367static BOOL update_write_bitmap_update(rdpUpdate* update, wStream* s,
368 const BITMAP_UPDATE* bitmapUpdate)
369{
370 WINPR_ASSERT(update);
371 WINPR_ASSERT(bitmapUpdate);
372
373 if (!Stream_EnsureRemainingCapacity(s, 32))
374 return FALSE;
375
376 Stream_Write_UINT16(s, UPDATE_TYPE_BITMAP); /* updateType */
377 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(
378 uint16_t, bitmapUpdate->number)); /* numberRectangles (2 bytes) */
379
380 /* rectangles */
381 for (UINT32 i = 0; i < bitmapUpdate->number; i++)
382 {
383 if (!update_write_bitmap_data(update, s, &bitmapUpdate->rectangles[i]))
384 return FALSE;
385 }
386
387 return TRUE;
388}
389
390PALETTE_UPDATE* update_read_palette(rdpUpdate* update, wStream* s)
391{
392 PALETTE_UPDATE* palette_update = calloc(1, sizeof(PALETTE_UPDATE));
393
394 if (!palette_update)
395 goto fail;
396
397 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
398 goto fail;
399
400 Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
401 Stream_Read_UINT32(s, palette_update->number); /* numberColors (4 bytes), must be set to 256 */
402
403 if (palette_update->number > 256)
404 palette_update->number = 256;
405
406 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, palette_update->number, 3ull))
407 goto fail;
408
409 /* paletteEntries */
410 for (UINT32 i = 0; i < palette_update->number; i++)
411 {
412 PALETTE_ENTRY* entry = &palette_update->entries[i];
413 Stream_Read_UINT8(s, entry->red);
414 Stream_Read_UINT8(s, entry->green);
415 Stream_Read_UINT8(s, entry->blue);
416 }
417
418 return palette_update;
419fail:
420 WINPR_PRAGMA_DIAG_PUSH
421 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
422 free_palette_update(update->context, palette_update);
423 WINPR_PRAGMA_DIAG_POP
424 return nullptr;
425}
426
427static BOOL update_read_synchronize(rdpUpdate* update, wStream* s)
428{
429 WINPR_UNUSED(update);
430 return Stream_SafeSeek(s, 2); /* pad2Octets (2 bytes) */
435}
436
437static BOOL update_read_play_sound(wStream* s, PLAY_SOUND_UPDATE* play_sound)
438{
439 WINPR_ASSERT(play_sound);
440
441 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
442 return FALSE;
443
444 Stream_Read_UINT32(s, play_sound->duration); /* duration (4 bytes) */
445 Stream_Read_UINT32(s, play_sound->frequency); /* frequency (4 bytes) */
446 return TRUE;
447}
448
449BOOL update_recv_play_sound(rdpUpdate* update, wStream* s)
450{
451 PLAY_SOUND_UPDATE play_sound = WINPR_C_ARRAY_INIT;
452
453 WINPR_ASSERT(update);
454
455 if (!update_read_play_sound(s, &play_sound))
456 return FALSE;
457
458 return IFCALLRESULT(TRUE, update->PlaySound, update->context, &play_sound);
459}
460
461POINTER_POSITION_UPDATE* update_read_pointer_position(rdpUpdate* update, wStream* s)
462{
463 POINTER_POSITION_UPDATE* pointer_position = calloc(1, sizeof(POINTER_POSITION_UPDATE));
464
465 WINPR_ASSERT(update);
466
467 if (!pointer_position)
468 goto fail;
469
470 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
471 goto fail;
472
473 Stream_Read_UINT16(s, pointer_position->xPos); /* xPos (2 bytes) */
474 Stream_Read_UINT16(s, pointer_position->yPos); /* yPos (2 bytes) */
475 return pointer_position;
476fail:
477 WINPR_PRAGMA_DIAG_PUSH
478 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
479 free_pointer_position_update(update->context, pointer_position);
480 WINPR_PRAGMA_DIAG_POP
481 return nullptr;
482}
483
484POINTER_SYSTEM_UPDATE* update_read_pointer_system(rdpUpdate* update, wStream* s)
485{
486 POINTER_SYSTEM_UPDATE* pointer_system = calloc(1, sizeof(POINTER_SYSTEM_UPDATE));
487
488 WINPR_ASSERT(update);
489
490 if (!pointer_system)
491 goto fail;
492
493 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
494 goto fail;
495
496 Stream_Read_UINT32(s, pointer_system->type); /* systemPointerType (4 bytes) */
497 return pointer_system;
498fail:
499 WINPR_PRAGMA_DIAG_PUSH
500 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
501 free_pointer_system_update(update->context, pointer_system);
502 WINPR_PRAGMA_DIAG_POP
503 return nullptr;
504}
505
506static BOOL s_update_read_pointer_color(wStream* s, POINTER_COLOR_UPDATE* pointer_color,
507 BYTE xorBpp, UINT32 flags)
508{
509 BYTE* newMask = nullptr;
510 UINT32 scanlineSize = 0;
511 UINT32 max = 32;
512
513 WINPR_ASSERT(pointer_color);
514
515 if (flags & LARGE_POINTER_FLAG_96x96)
516 max = 96;
517
518 if (!pointer_color)
519 goto fail;
520
521 if (!Stream_CheckAndLogRequiredLength(TAG, s, 14))
522 goto fail;
523
524 Stream_Read_UINT16(s, pointer_color->cacheIndex); /* cacheIndex (2 bytes) */
525 Stream_Read_UINT16(s, pointer_color->hotSpotX); /* hotSpot.xPos (2 bytes) */
526 Stream_Read_UINT16(s, pointer_color->hotSpotY); /* hotSpot.yPos (2 bytes) */
536 Stream_Read_UINT16(s, pointer_color->width); /* width (2 bytes) */
537 Stream_Read_UINT16(s, pointer_color->height); /* height (2 bytes) */
538
539 if ((pointer_color->width > max) || (pointer_color->height > max))
540 goto fail;
541
542 Stream_Read_UINT16(s, pointer_color->lengthAndMask); /* lengthAndMask (2 bytes) */
543 Stream_Read_UINT16(s, pointer_color->lengthXorMask); /* lengthXorMask (2 bytes) */
544
551 if (pointer_color->hotSpotX >= pointer_color->width)
552 pointer_color->hotSpotX = 0;
553
554 if (pointer_color->hotSpotY >= pointer_color->height)
555 pointer_color->hotSpotY = 0;
556
557 if (pointer_color->lengthXorMask > 0)
558 {
570 if (!Stream_CheckAndLogRequiredLength(TAG, s, pointer_color->lengthXorMask))
571 goto fail;
572
573 scanlineSize = (7 + xorBpp * pointer_color->width) / 8;
574 scanlineSize = ((scanlineSize + 1) / 2) * 2;
575
576 if (scanlineSize * pointer_color->height != pointer_color->lengthXorMask)
577 {
578 WLog_ERR(TAG,
579 "invalid lengthXorMask: width=%" PRIu32 " height=%" PRIu32 ", %" PRIu32
580 " instead of %" PRIu32 "",
581 pointer_color->width, pointer_color->height, pointer_color->lengthXorMask,
582 scanlineSize * pointer_color->height);
583 goto fail;
584 }
585
586 newMask = realloc(pointer_color->xorMaskData, pointer_color->lengthXorMask);
587
588 if (!newMask)
589 goto fail;
590
591 pointer_color->xorMaskData = newMask;
592 Stream_Read(s, pointer_color->xorMaskData, pointer_color->lengthXorMask);
593 }
594
595 if (pointer_color->lengthAndMask > 0)
596 {
604 if (!Stream_CheckAndLogRequiredLength(TAG, s, pointer_color->lengthAndMask))
605 goto fail;
606
607 scanlineSize = ((7 + pointer_color->width) / 8);
608 scanlineSize = ((1 + scanlineSize) / 2) * 2;
609
610 if (scanlineSize * pointer_color->height != pointer_color->lengthAndMask)
611 {
612 WLog_ERR(TAG, "invalid lengthAndMask: %" PRIu32 " instead of %" PRIu32 "",
613 pointer_color->lengthAndMask, scanlineSize * pointer_color->height);
614 goto fail;
615 }
616
617 newMask = realloc(pointer_color->andMaskData, pointer_color->lengthAndMask);
618
619 if (!newMask)
620 goto fail;
621
622 pointer_color->andMaskData = newMask;
623 Stream_Read(s, pointer_color->andMaskData, pointer_color->lengthAndMask);
624 }
625
626 if (Stream_GetRemainingLength(s) > 0)
627 Stream_Seek_UINT8(s); /* pad (1 byte) */
628
629 return TRUE;
630fail:
631 return FALSE;
632}
633
634POINTER_COLOR_UPDATE* update_read_pointer_color(rdpUpdate* update, wStream* s, BYTE xorBpp)
635{
636 POINTER_COLOR_UPDATE* pointer_color = calloc(1, sizeof(POINTER_COLOR_UPDATE));
637
638 WINPR_ASSERT(update);
639
640 if (!pointer_color)
641 goto fail;
642
643 if (!s_update_read_pointer_color(s, pointer_color, xorBpp,
644 update->context->settings->LargePointerFlag))
645 goto fail;
646
647 return pointer_color;
648fail:
649 WINPR_PRAGMA_DIAG_PUSH
650 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
651 free_pointer_color_update(update->context, pointer_color);
652 WINPR_PRAGMA_DIAG_POP
653 return nullptr;
654}
655
656static BOOL s_update_read_pointer_large(wStream* s, POINTER_LARGE_UPDATE* pointer)
657{
658 BYTE* newMask = nullptr;
659 UINT32 scanlineSize = 0;
660
661 if (!pointer)
662 goto fail;
663
664 if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
665 goto fail;
666
667 Stream_Read_UINT16(s, pointer->xorBpp);
668 Stream_Read_UINT16(s, pointer->cacheIndex); /* cacheIndex (2 bytes) */
669 Stream_Read_UINT16(s, pointer->hotSpotX); /* hotSpot.xPos (2 bytes) */
670 Stream_Read_UINT16(s, pointer->hotSpotY); /* hotSpot.yPos (2 bytes) */
671
672 Stream_Read_UINT16(s, pointer->width); /* width (2 bytes) */
673 Stream_Read_UINT16(s, pointer->height); /* height (2 bytes) */
674
675 if ((pointer->width > 384) || (pointer->height > 384))
676 goto fail;
677
678 Stream_Read_UINT32(s, pointer->lengthAndMask); /* lengthAndMask (4 bytes) */
679 Stream_Read_UINT32(s, pointer->lengthXorMask); /* lengthXorMask (4 bytes) */
680
681 if (pointer->hotSpotX >= pointer->width)
682 pointer->hotSpotX = 0;
683
684 if (pointer->hotSpotY >= pointer->height)
685 pointer->hotSpotY = 0;
686
687 if (pointer->lengthXorMask > 0)
688 {
700 if (!Stream_CheckAndLogRequiredLength(TAG, s, pointer->lengthXorMask))
701 goto fail;
702
703 scanlineSize = (7 + pointer->xorBpp * pointer->width) / 8;
704 scanlineSize = ((scanlineSize + 1) / 2) * 2;
705
706 if (scanlineSize * pointer->height != pointer->lengthXorMask)
707 {
708 WLog_ERR(TAG,
709 "invalid lengthXorMask: width=%" PRIu32 " height=%" PRIu32 ", %" PRIu32
710 " instead of %" PRIu32 "",
711 pointer->width, pointer->height, pointer->lengthXorMask,
712 scanlineSize * pointer->height);
713 goto fail;
714 }
715
716 newMask = realloc(pointer->xorMaskData, pointer->lengthXorMask);
717
718 if (!newMask)
719 goto fail;
720
721 pointer->xorMaskData = newMask;
722 Stream_Read(s, pointer->xorMaskData, pointer->lengthXorMask);
723 }
724
725 if (pointer->lengthAndMask > 0)
726 {
734 if (!Stream_CheckAndLogRequiredLength(TAG, s, pointer->lengthAndMask))
735 goto fail;
736
737 scanlineSize = ((7 + pointer->width) / 8);
738 scanlineSize = ((1 + scanlineSize) / 2) * 2;
739
740 if (scanlineSize * pointer->height != pointer->lengthAndMask)
741 {
742 WLog_ERR(TAG, "invalid lengthAndMask: %" PRIu32 " instead of %" PRIu32 "",
743 pointer->lengthAndMask, scanlineSize * pointer->height);
744 goto fail;
745 }
746
747 newMask = realloc(pointer->andMaskData, pointer->lengthAndMask);
748
749 if (!newMask)
750 goto fail;
751
752 pointer->andMaskData = newMask;
753 Stream_Read(s, pointer->andMaskData, pointer->lengthAndMask);
754 }
755
756 if (Stream_GetRemainingLength(s) > 0)
757 Stream_Seek_UINT8(s); /* pad (1 byte) */
758
759 return TRUE;
760fail:
761 return FALSE;
762}
763
764POINTER_LARGE_UPDATE* update_read_pointer_large(rdpUpdate* update, wStream* s)
765{
766 POINTER_LARGE_UPDATE* pointer = calloc(1, sizeof(POINTER_LARGE_UPDATE));
767
768 WINPR_ASSERT(update);
769
770 if (!pointer)
771 goto fail;
772
773 if (!s_update_read_pointer_large(s, pointer))
774 goto fail;
775
776 return pointer;
777fail:
778 WINPR_PRAGMA_DIAG_PUSH
779 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
780 free_pointer_large_update(update->context, pointer);
781 WINPR_PRAGMA_DIAG_POP
782 return nullptr;
783}
784
785POINTER_NEW_UPDATE* update_read_pointer_new(rdpUpdate* update, wStream* s)
786{
787 POINTER_NEW_UPDATE* pointer_new = calloc(1, sizeof(POINTER_NEW_UPDATE));
788
789 WINPR_ASSERT(update);
790
791 if (!pointer_new)
792 goto fail;
793
794 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
795 goto fail;
796
797 Stream_Read_UINT16(s, pointer_new->xorBpp); /* xorBpp (2 bytes) */
798
799 if ((pointer_new->xorBpp < 1) || (pointer_new->xorBpp > 32))
800 {
801 WLog_ERR(TAG, "invalid xorBpp %" PRIu32 "", pointer_new->xorBpp);
802 goto fail;
803 }
804
805 WINPR_ASSERT(pointer_new->xorBpp <= UINT8_MAX);
806 if (!s_update_read_pointer_color(
807 s, &pointer_new->colorPtrAttr, (UINT8)pointer_new->xorBpp,
808 update->context->settings->LargePointerFlag)) /* colorPtrAttr */
809 goto fail;
810
811 return pointer_new;
812fail:
813 WINPR_PRAGMA_DIAG_PUSH
814 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
815 free_pointer_new_update(update->context, pointer_new);
816 WINPR_PRAGMA_DIAG_POP
817 return nullptr;
818}
819
820POINTER_CACHED_UPDATE* update_read_pointer_cached(rdpUpdate* update, wStream* s)
821{
822 POINTER_CACHED_UPDATE* pointer = calloc(1, sizeof(POINTER_CACHED_UPDATE));
823
824 WINPR_ASSERT(update);
825
826 if (!pointer)
827 goto fail;
828
829 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
830 goto fail;
831
832 Stream_Read_UINT16(s, pointer->cacheIndex); /* cacheIndex (2 bytes) */
833 return pointer;
834fail:
835 WINPR_PRAGMA_DIAG_PUSH
836 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
837 free_pointer_cached_update(update->context, pointer);
838 WINPR_PRAGMA_DIAG_POP
839 return nullptr;
840}
841
842BOOL update_recv_pointer(rdpUpdate* update, wStream* s)
843{
844 BOOL rc = FALSE;
845 UINT16 messageType = 0;
846
847 WINPR_ASSERT(update);
848
849 rdpContext* context = update->context;
850 rdpPointerUpdate* pointer = update->pointer;
851
852 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2 + 2))
853 return FALSE;
854
855 Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
856 Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
857
858 switch (messageType)
859 {
860 case PTR_MSG_TYPE_POSITION:
861 {
862 POINTER_POSITION_UPDATE* pointer_position = update_read_pointer_position(update, s);
863
864 if (pointer_position)
865 {
866 rc = IFCALLRESULT(FALSE, pointer->PointerPosition, context, pointer_position);
867 free_pointer_position_update(context, pointer_position);
868 }
869 }
870 break;
871
872 case PTR_MSG_TYPE_SYSTEM:
873 {
874 POINTER_SYSTEM_UPDATE* pointer_system = update_read_pointer_system(update, s);
875
876 if (pointer_system)
877 {
878 rc = IFCALLRESULT(FALSE, pointer->PointerSystem, context, pointer_system);
879 free_pointer_system_update(context, pointer_system);
880 }
881 }
882 break;
883
884 case PTR_MSG_TYPE_COLOR:
885 {
886 POINTER_COLOR_UPDATE* pointer_color = update_read_pointer_color(update, s, 24);
887
888 if (pointer_color)
889 {
890 rc = IFCALLRESULT(FALSE, pointer->PointerColor, context, pointer_color);
891 free_pointer_color_update(context, pointer_color);
892 }
893 }
894 break;
895
896 case PTR_MSG_TYPE_POINTER_LARGE:
897 {
898 POINTER_LARGE_UPDATE* pointer_large = update_read_pointer_large(update, s);
899
900 if (pointer_large)
901 {
902 rc = IFCALLRESULT(FALSE, pointer->PointerLarge, context, pointer_large);
903 free_pointer_large_update(context, pointer_large);
904 }
905 }
906 break;
907
908 case PTR_MSG_TYPE_POINTER:
909 {
910 POINTER_NEW_UPDATE* pointer_new = update_read_pointer_new(update, s);
911
912 if (pointer_new)
913 {
914 rc = IFCALLRESULT(FALSE, pointer->PointerNew, context, pointer_new);
915 free_pointer_new_update(context, pointer_new);
916 }
917 }
918 break;
919
920 case PTR_MSG_TYPE_CACHED:
921 {
922 POINTER_CACHED_UPDATE* pointer_cached = update_read_pointer_cached(update, s);
923
924 if (pointer_cached)
925 {
926 rc = IFCALLRESULT(FALSE, pointer->PointerCached, context, pointer_cached);
927 free_pointer_cached_update(context, pointer_cached);
928 }
929 }
930 break;
931
932 default:
933 break;
934 }
935
936 return rc;
937}
938
939BOOL update_recv(rdpUpdate* update, wStream* s)
940{
941 BOOL rc = FALSE;
942 UINT16 updateType = 0;
943 rdp_update_internal* up = update_cast(update);
944 rdpContext* context = update->context;
945
946 WINPR_ASSERT(context);
947
948 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
949 return FALSE;
950
951 Stream_Read_UINT16(s, updateType); /* updateType (2 bytes) */
952 WLog_Print(up->log, WLOG_TRACE, "%s Update Data PDU", update_type_to_string(updateType));
953
954 if (!update_begin_paint(update))
955 goto fail;
956
957 switch (updateType)
958 {
959 case UPDATE_TYPE_ORDERS:
960 rc = update_recv_orders(update, s);
961 break;
962
963 case UPDATE_TYPE_BITMAP:
964 {
965 BITMAP_UPDATE* bitmap_update = update_read_bitmap_update(update, s);
966
967 if (!bitmap_update)
968 {
969 WLog_ERR(TAG, "UPDATE_TYPE_BITMAP - update_read_bitmap_update() failed");
970 goto fail;
971 }
972
973 up->stats.base[RDP_STATS_BITMAP_UPDATE]++;
974 rc = IFCALLRESULT(FALSE, update->BitmapUpdate, context, bitmap_update);
975 free_bitmap_update(context, bitmap_update);
976 }
977 break;
978
979 case UPDATE_TYPE_PALETTE:
980 {
981 PALETTE_UPDATE* palette_update = update_read_palette(update, s);
982
983 if (!palette_update)
984 {
985 WLog_ERR(TAG, "UPDATE_TYPE_PALETTE - update_read_palette() failed");
986 goto fail;
987 }
988
989 up->stats.base[RDP_STATS_PALETTE]++;
990 rc = IFCALLRESULT(FALSE, update->Palette, context, palette_update);
991 free_palette_update(context, palette_update);
992 }
993 break;
994
995 case UPDATE_TYPE_SYNCHRONIZE:
996 if (!update_read_synchronize(update, s))
997 goto fail;
998 up->stats.base[RDP_STATS_SYNC]++;
999 rc = IFCALLRESULT(TRUE, update->Synchronize, context);
1000 break;
1001
1002 default:
1003 break;
1004 }
1005
1006fail:
1007
1008 if (!update_end_paint(update))
1009 rc = FALSE;
1010
1011 if (!rc)
1012 {
1013 WLog_ERR(TAG, "UPDATE_TYPE %s [%" PRIu16 "] failed", update_type_to_string(updateType),
1014 updateType);
1015 return FALSE;
1016 }
1017
1018 return TRUE;
1019}
1020
1021void update_reset_state(rdpUpdate* update)
1022{
1023 rdp_update_internal* up = update_cast(update);
1024 rdp_primary_update_internal* primary = primary_update_cast(update->primary);
1025
1026 WINPR_ASSERT(primary);
1027
1028 ZeroMemory(&primary->order_info, sizeof(ORDER_INFO));
1029 ZeroMemory(&primary->dstblt, sizeof(DSTBLT_ORDER));
1030 ZeroMemory(&primary->patblt, sizeof(PATBLT_ORDER));
1031 ZeroMemory(&primary->scrblt, sizeof(SCRBLT_ORDER));
1032 ZeroMemory(&primary->opaque_rect, sizeof(OPAQUE_RECT_ORDER));
1033 ZeroMemory(&primary->draw_nine_grid, sizeof(DRAW_NINE_GRID_ORDER));
1034 ZeroMemory(&primary->multi_dstblt, sizeof(MULTI_DSTBLT_ORDER));
1035 ZeroMemory(&primary->multi_patblt, sizeof(MULTI_PATBLT_ORDER));
1036 ZeroMemory(&primary->multi_scrblt, sizeof(MULTI_SCRBLT_ORDER));
1037 ZeroMemory(&primary->multi_opaque_rect, sizeof(MULTI_OPAQUE_RECT_ORDER));
1038 ZeroMemory(&primary->multi_draw_nine_grid, sizeof(MULTI_DRAW_NINE_GRID_ORDER));
1039 ZeroMemory(&primary->line_to, sizeof(LINE_TO_ORDER));
1040
1041 free(primary->polyline.points);
1042 ZeroMemory(&primary->polyline, sizeof(POLYLINE_ORDER));
1043
1044 ZeroMemory(&primary->memblt, sizeof(MEMBLT_ORDER));
1045 ZeroMemory(&primary->mem3blt, sizeof(MEM3BLT_ORDER));
1046 ZeroMemory(&primary->save_bitmap, sizeof(SAVE_BITMAP_ORDER));
1047 ZeroMemory(&primary->glyph_index, sizeof(GLYPH_INDEX_ORDER));
1048 ZeroMemory(&primary->fast_index, sizeof(FAST_INDEX_ORDER));
1049
1050 free(primary->fast_glyph.glyphData.aj);
1051 ZeroMemory(&primary->fast_glyph, sizeof(FAST_GLYPH_ORDER));
1052
1053 free(primary->polygon_sc.points);
1054 ZeroMemory(&primary->polygon_sc, sizeof(POLYGON_SC_ORDER));
1055
1056 free(primary->polygon_cb.points);
1057 ZeroMemory(&primary->polygon_cb, sizeof(POLYGON_CB_ORDER));
1058
1059 ZeroMemory(&primary->ellipse_sc, sizeof(ELLIPSE_SC_ORDER));
1060 ZeroMemory(&primary->ellipse_cb, sizeof(ELLIPSE_CB_ORDER));
1061 primary->order_info.orderType = ORDER_TYPE_PATBLT;
1062
1063 if (!up->initialState)
1064 {
1065 rdp_altsec_update_internal* altsec = altsec_update_cast(update->altsec);
1066 WINPR_ASSERT(altsec);
1067
1068 altsec->switch_surface.bitmapId = SCREEN_BITMAP_SURFACE;
1069 if (altsec->common.SwitchSurface)
1070 {
1071 if (!altsec->common.SwitchSurface(update->context, &(altsec->switch_surface)))
1072 WLog_Print(up->log, WLOG_WARN, "altsec->common.SwitchSurface failed");
1073 }
1074 }
1075}
1076
1077BOOL update_post_connect(rdpUpdate* update)
1078{
1079 rdp_update_internal* up = update_cast(update);
1080 rdp_altsec_update_internal* altsec = altsec_update_cast(update->altsec);
1081
1082 WINPR_ASSERT(update->context);
1083 WINPR_ASSERT(update->context->settings);
1084 up->asynchronous = update->context->settings->AsyncUpdate;
1085
1086 if (up->asynchronous)
1087 {
1088#if defined(FORCE_ASYNC_UPDATE_OFF)
1089 WLog_WARN(TAG, "AsyncUpdate requested, but forced deactivated");
1090 WLog_WARN(TAG, "see https://github.com/FreeRDP/FreeRDP/issues/10153 for details");
1091#else
1092 if (!(up->proxy = update_message_proxy_new(update)))
1093 return FALSE;
1094#endif
1095 }
1096
1097 altsec->switch_surface.bitmapId = SCREEN_BITMAP_SURFACE;
1098 const BOOL rc = IFCALLRESULT(TRUE, update->altsec->SwitchSurface, update->context,
1099 &(altsec->switch_surface));
1100 up->initialState = FALSE;
1101 return rc;
1102}
1103
1104void update_post_disconnect(rdpUpdate* update)
1105{
1106 rdp_update_internal* up = update_cast(update);
1107
1108 WINPR_ASSERT(update->context);
1109 WINPR_ASSERT(update->context->settings);
1110
1111 up->asynchronous = update->context->settings->AsyncUpdate;
1112
1113 if (up->asynchronous)
1114 {
1115#if !defined(FORCE_ASYNC_UPDATE_OFF)
1116 update_message_proxy_free(up->proxy);
1117#endif
1118 }
1119
1120 up->initialState = TRUE;
1121}
1122
1123static BOOL s_update_begin_paint(rdpContext* context)
1124{
1125 wStream* s = nullptr;
1126 WINPR_ASSERT(context);
1127 rdp_update_internal* update = update_cast(context->update);
1128
1129 if (update->us)
1130 {
1131 if (!update_end_paint(&update->common))
1132 return FALSE;
1133 }
1134
1135 WINPR_ASSERT(context->rdp);
1136 s = fastpath_update_pdu_init_new(context->rdp->fastpath);
1137
1138 if (!s)
1139 return FALSE;
1140
1141 Stream_SealLength(s);
1142 Stream_GetLength(s, update->offsetOrders);
1143 Stream_Seek(s, 2); /* numberOrders (2 bytes) */
1144 update->combineUpdates = TRUE;
1145 update->numberOrders = 0;
1146 update->us = s;
1147 return TRUE;
1148}
1149
1150static BOOL s_update_end_paint(rdpContext* context)
1151{
1152 BOOL rc = FALSE;
1153
1154 WINPR_ASSERT(context);
1155 rdp_update_internal* update = update_cast(context->update);
1156
1157 if (!update->us)
1158 return FALSE;
1159
1160 wStream* s = update->us;
1161 update->us = nullptr;
1162
1163 Stream_SealLength(s);
1164 if (!Stream_SetPosition(s, update->offsetOrders))
1165 goto fail;
1166 Stream_Write_UINT16(s, update->numberOrders); /* numberOrders (2 bytes) */
1167 if (!Stream_SetPosition(s, Stream_Length(s)))
1168 goto fail;
1169
1170 if (update->numberOrders > 0)
1171 {
1172 WLog_DBG(TAG, "sending %" PRIu16 " orders", update->numberOrders);
1173 if (!fastpath_send_update_pdu(context->rdp->fastpath, FASTPATH_UPDATETYPE_ORDERS, s, FALSE))
1174 goto fail;
1175 }
1176
1177 update->combineUpdates = FALSE;
1178 update->numberOrders = 0;
1179 update->offsetOrders = 0;
1180
1181 rc = TRUE;
1182fail:
1183 Stream_Free(s, TRUE);
1184 return rc;
1185}
1186
1187static BOOL update_flush(rdpContext* context)
1188{
1189 rdp_update_internal* update = nullptr;
1190
1191 WINPR_ASSERT(context);
1192 update = update_cast(context->update);
1193
1194 if (update->numberOrders > 0)
1195 {
1196 if (!update_end_paint(&update->common))
1197 return FALSE;
1198
1199 if (!update_begin_paint(&update->common))
1200 return FALSE;
1201 }
1202 return TRUE;
1203}
1204
1205static BOOL update_force_flush(rdpContext* context)
1206{
1207 return update_flush(context);
1208}
1209
1210static BOOL update_check_flush(rdpContext* context, size_t size)
1211{
1212 WINPR_ASSERT(context);
1213 rdp_update_internal* update = update_cast(context->update);
1214
1215 wStream* s = update->us;
1216
1217 if (!s)
1218 {
1219 if (!update_begin_paint(&update->common))
1220 return FALSE;
1221 s = update->us;
1222 }
1223
1224 if (Stream_GetPosition(s) + size + 64 >= FASTPATH_MAX_PACKET_SIZE)
1225 {
1226 // Too big for the current packet. Flush first
1227 if (!update_flush(context))
1228 return FALSE;
1229 }
1230
1231 return TRUE;
1232}
1233
1234static BOOL update_set_bounds(rdpContext* context, const rdpBounds* bounds)
1235{
1236 rdp_update_internal* update = nullptr;
1237
1238 WINPR_ASSERT(context);
1239
1240 update = update_cast(context->update);
1241
1242 CopyMemory(&update->previousBounds, &update->currentBounds, sizeof(rdpBounds));
1243
1244 if (!bounds)
1245 ZeroMemory(&update->currentBounds, sizeof(rdpBounds));
1246 else
1247 CopyMemory(&update->currentBounds, bounds, sizeof(rdpBounds));
1248
1249 return TRUE;
1250}
1251
1252static BOOL update_bounds_is_null(rdpBounds* bounds)
1253{
1254 WINPR_ASSERT(bounds);
1255 return ((bounds->left == 0) && (bounds->top == 0) && (bounds->right == 0) &&
1256 (bounds->bottom == 0));
1257}
1258
1259static BOOL update_bounds_equals(rdpBounds* bounds1, rdpBounds* bounds2)
1260{
1261 WINPR_ASSERT(bounds1);
1262 WINPR_ASSERT(bounds2);
1263
1264 return ((bounds1->left == bounds2->left) && (bounds1->top == bounds2->top) &&
1265 (bounds1->right == bounds2->right) && (bounds1->bottom == bounds2->bottom));
1266}
1267
1268static size_t update_prepare_bounds(rdpContext* context, ORDER_INFO* orderInfo)
1269{
1270 size_t length = 0;
1271 rdp_update_internal* update = nullptr;
1272
1273 WINPR_ASSERT(context);
1274 WINPR_ASSERT(orderInfo);
1275
1276 update = update_cast(context->update);
1277
1278 orderInfo->boundsFlags = 0;
1279
1280 if (update_bounds_is_null(&update->currentBounds))
1281 return 0;
1282
1283 orderInfo->controlFlags |= ORDER_BOUNDS;
1284
1285 if (update_bounds_equals(&update->previousBounds, &update->currentBounds))
1286 {
1287 orderInfo->controlFlags |= ORDER_ZERO_BOUNDS_DELTAS;
1288 return 0;
1289 }
1290 else
1291 {
1292 length += 1;
1293
1294 if (update->previousBounds.left != update->currentBounds.left)
1295 {
1296 orderInfo->bounds.left = update->currentBounds.left;
1297 orderInfo->boundsFlags |= BOUND_LEFT;
1298 length += 2;
1299 }
1300
1301 if (update->previousBounds.top != update->currentBounds.top)
1302 {
1303 orderInfo->bounds.top = update->currentBounds.top;
1304 orderInfo->boundsFlags |= BOUND_TOP;
1305 length += 2;
1306 }
1307
1308 if (update->previousBounds.right != update->currentBounds.right)
1309 {
1310 orderInfo->bounds.right = update->currentBounds.right;
1311 orderInfo->boundsFlags |= BOUND_RIGHT;
1312 length += 2;
1313 }
1314
1315 if (update->previousBounds.bottom != update->currentBounds.bottom)
1316 {
1317 orderInfo->bounds.bottom = update->currentBounds.bottom;
1318 orderInfo->boundsFlags |= BOUND_BOTTOM;
1319 length += 2;
1320 }
1321 }
1322
1323 return length;
1324}
1325
1326static size_t update_prepare_order_info(rdpContext* context, ORDER_INFO* orderInfo,
1327 UINT32 orderType)
1328{
1329 WINPR_ASSERT(context);
1330 WINPR_ASSERT(orderInfo);
1331
1332 orderInfo->fieldFlags = 0;
1333 orderInfo->orderType = orderType;
1334 orderInfo->controlFlags = ORDER_STANDARD;
1335 orderInfo->controlFlags |= ORDER_TYPE_CHANGE;
1336 size_t length = 2;
1337 length += get_primary_drawing_order_field_bytes(orderInfo->orderType, nullptr);
1338 length += update_prepare_bounds(context, orderInfo);
1339 return length;
1340}
1341
1342WINPR_ATTR_NODISCARD
1343static int update_write_order_info(rdpContext* context, wStream* s, const ORDER_INFO* orderInfo,
1344 size_t offset)
1345{
1346 WINPR_UNUSED(context);
1347 WINPR_ASSERT(orderInfo);
1348 WINPR_ASSERT(orderInfo->controlFlags <= UINT8_MAX);
1349
1350 const size_t position = Stream_GetPosition(s);
1351 const UINT8 controlFlags = (UINT8)orderInfo->controlFlags;
1352
1353 if (!Stream_SetPosition(s, offset))
1354 return -1;
1355
1356 Stream_Write_UINT8(s, controlFlags); /* controlFlags (1 byte) */
1357
1358 if (orderInfo->controlFlags & ORDER_TYPE_CHANGE)
1359 Stream_Write_UINT8(
1360 s, WINPR_ASSERTING_INT_CAST(uint8_t, orderInfo->orderType)); /* orderType (1 byte) */
1361
1362 if (!update_write_field_flags(
1363 s, orderInfo->fieldFlags, controlFlags,
1364 get_primary_drawing_order_field_bytes(orderInfo->orderType, nullptr)))
1365 return -1;
1366 if (!update_write_bounds(s, orderInfo))
1367 return -1;
1368 if (!Stream_SetPosition(s, position))
1369 return -1;
1370 return 0;
1371}
1372
1373static void update_write_refresh_rect(wStream* s, BYTE count, const RECTANGLE_16* areas)
1374{
1375 WINPR_ASSERT(s);
1376 WINPR_ASSERT(areas || (count == 0));
1377
1378 Stream_Write_UINT8(s, count); /* numberOfAreas (1 byte) */
1379 Stream_Seek(s, 3); /* pad3Octets (3 bytes) */
1380
1381 for (BYTE i = 0; i < count; i++)
1382 {
1383 Stream_Write_UINT16(s, areas[i].left); /* left (2 bytes) */
1384 Stream_Write_UINT16(s, areas[i].top); /* top (2 bytes) */
1385 Stream_Write_UINT16(s, areas[i].right); /* right (2 bytes) */
1386 Stream_Write_UINT16(s, areas[i].bottom); /* bottom (2 bytes) */
1387 }
1388}
1389
1390static BOOL update_send_refresh_rect(rdpContext* context, BYTE count, const RECTANGLE_16* areas)
1391{
1392 WINPR_ASSERT(context);
1393 rdpRdp* rdp = context->rdp;
1394
1395 WINPR_ASSERT(rdp->settings);
1396 if (rdp->settings->RefreshRect)
1397 {
1398 UINT16 sec_flags = 0;
1399 wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
1400
1401 if (!s)
1402 return FALSE;
1403
1404 update_write_refresh_rect(s, count, areas);
1405 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_REFRESH_RECT, rdp->mcs->userId, sec_flags);
1406 }
1407
1408 return TRUE;
1409}
1410
1411static void update_write_suppress_output(wStream* s, BYTE allow, const RECTANGLE_16* area)
1412{
1413 WINPR_ASSERT(s);
1414
1415 Stream_Write_UINT8(s, allow); /* allowDisplayUpdates (1 byte) */
1416 /* Use zeros for padding (like mstsc) for compatibility with legacy servers */
1417 Stream_Zero(s, 3); /* pad3Octets (3 bytes) */
1418
1419 if (allow > 0)
1420 {
1421 WINPR_ASSERT(area);
1422 Stream_Write_UINT16(s, area->left); /* left (2 bytes) */
1423 Stream_Write_UINT16(s, area->top); /* top (2 bytes) */
1424 Stream_Write_UINT16(s, area->right); /* right (2 bytes) */
1425 Stream_Write_UINT16(s, area->bottom); /* bottom (2 bytes) */
1426 }
1427}
1428
1429static BOOL update_send_suppress_output(rdpContext* context, BYTE allow, const RECTANGLE_16* area)
1430{
1431 WINPR_ASSERT(context);
1432 rdpRdp* rdp = context->rdp;
1433
1434 WINPR_ASSERT(rdp);
1435 WINPR_ASSERT(rdp->settings);
1436 if (rdp->settings->SuppressOutput)
1437 {
1438 UINT16 sec_flags = 0;
1439 wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
1440
1441 if (!s)
1442 return FALSE;
1443
1444 update_write_suppress_output(s, allow, area);
1445 WINPR_ASSERT(rdp->mcs);
1446 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SUPPRESS_OUTPUT, rdp->mcs->userId,
1447 sec_flags);
1448 }
1449
1450 return TRUE;
1451}
1452
1453static BOOL update_send_surface_command(rdpContext* context, wStream* s)
1454{
1455 wStream* update = nullptr;
1456 WINPR_ASSERT(context);
1457 rdpRdp* rdp = context->rdp;
1458 BOOL ret = 0;
1459
1460 WINPR_ASSERT(rdp);
1461 update = fastpath_update_pdu_init(rdp->fastpath);
1462
1463 if (!update)
1464 return FALSE;
1465
1466 if (!Stream_EnsureRemainingCapacity(update, Stream_GetPosition(s)))
1467 {
1468 ret = FALSE;
1469 goto out;
1470 }
1471
1472 Stream_Write(update, Stream_Buffer(s), Stream_GetPosition(s));
1473 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_SURFCMDS, update, FALSE);
1474out:
1475 Stream_Release(update);
1476 return ret;
1477}
1478
1479static BOOL update_send_surface_bits(rdpContext* context,
1480 const SURFACE_BITS_COMMAND* surfaceBitsCommand)
1481{
1482 wStream* s = nullptr;
1483 WINPR_ASSERT(context);
1484 rdpRdp* rdp = context->rdp;
1485 BOOL ret = FALSE;
1486
1487 WINPR_ASSERT(surfaceBitsCommand);
1488 WINPR_ASSERT(rdp);
1489
1490 if (!update_force_flush(context))
1491 return FALSE;
1492 s = fastpath_update_pdu_init(rdp->fastpath);
1493
1494 if (!s)
1495 return FALSE;
1496
1497 if (!update_write_surfcmd_surface_bits(s, surfaceBitsCommand))
1498 goto out_fail;
1499
1500 if (!fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_SURFCMDS, s,
1501 surfaceBitsCommand->skipCompression))
1502 goto out_fail;
1503
1504 ret = update_force_flush(context);
1505out_fail:
1506 Stream_Release(s);
1507 return ret;
1508}
1509
1510static BOOL update_send_surface_frame_marker(rdpContext* context,
1511 const SURFACE_FRAME_MARKER* surfaceFrameMarker)
1512{
1513 wStream* s = nullptr;
1514 WINPR_ASSERT(context);
1515 rdpRdp* rdp = context->rdp;
1516 BOOL ret = FALSE;
1517 if (!update_force_flush(context))
1518 return FALSE;
1519
1520 WINPR_ASSERT(rdp);
1521 s = fastpath_update_pdu_init(rdp->fastpath);
1522
1523 if (!s)
1524 return FALSE;
1525
1526 WINPR_ASSERT(surfaceFrameMarker->frameAction <= UINT16_MAX);
1527 if (!update_write_surfcmd_frame_marker(s, (UINT16)surfaceFrameMarker->frameAction,
1528 surfaceFrameMarker->frameId) ||
1529 !fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_SURFCMDS, s, FALSE))
1530 goto out_fail;
1531
1532 ret = update_force_flush(context);
1533out_fail:
1534 Stream_Release(s);
1535 return ret;
1536}
1537
1538static BOOL update_send_surface_frame_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd,
1539 BOOL first, BOOL last, UINT32 frameId)
1540{
1541 wStream* s = nullptr;
1542
1543 WINPR_ASSERT(context);
1544 rdpRdp* rdp = context->rdp;
1545 BOOL ret = FALSE;
1546
1547 if (!update_force_flush(context))
1548 return FALSE;
1549
1550 WINPR_ASSERT(rdp);
1551 s = fastpath_update_pdu_init(rdp->fastpath);
1552
1553 if (!s)
1554 return FALSE;
1555
1556 if (first)
1557 {
1558 if (!update_write_surfcmd_frame_marker(s, SURFACECMD_FRAMEACTION_BEGIN, frameId))
1559 goto out_fail;
1560 }
1561
1562 if (!update_write_surfcmd_surface_bits(s, cmd))
1563 goto out_fail;
1564
1565 if (last)
1566 {
1567 if (!update_write_surfcmd_frame_marker(s, SURFACECMD_FRAMEACTION_END, frameId))
1568 goto out_fail;
1569 }
1570
1571 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_SURFCMDS, s,
1572 cmd->skipCompression);
1573 if (!ret)
1574 goto out_fail;
1575
1576 ret = update_force_flush(context);
1577out_fail:
1578 Stream_Release(s);
1579 return ret;
1580}
1581
1582static BOOL update_send_frame_acknowledge(rdpContext* context, UINT32 frameId)
1583{
1584 WINPR_ASSERT(context);
1585 rdpRdp* rdp = context->rdp;
1586
1587 WINPR_ASSERT(rdp);
1588 WINPR_ASSERT(rdp->settings);
1589 WINPR_ASSERT(rdp->settings->ReceivedCapabilities);
1590 WINPR_ASSERT(rdp->settings->ReceivedCapabilitiesSize > CAPSET_TYPE_FRAME_ACKNOWLEDGE);
1591 if (rdp->settings->ReceivedCapabilities[CAPSET_TYPE_FRAME_ACKNOWLEDGE])
1592 {
1593 UINT16 sec_flags = 0;
1594 wStream* s = rdp_data_pdu_init(rdp, &sec_flags);
1595
1596 if (!s)
1597 return FALSE;
1598
1599 Stream_Write_UINT32(s, frameId);
1600 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FRAME_ACKNOWLEDGE, rdp->mcs->userId,
1601 sec_flags);
1602 }
1603
1604 return TRUE;
1605}
1606
1607static BOOL update_send_synchronize(rdpContext* context)
1608{
1609 wStream* s = nullptr;
1610 WINPR_ASSERT(context);
1611 rdpRdp* rdp = context->rdp;
1612 BOOL ret = 0;
1613
1614 WINPR_ASSERT(rdp);
1615 s = fastpath_update_pdu_init(rdp->fastpath);
1616
1617 if (!s)
1618 return FALSE;
1619
1620 Stream_Zero(s, 2); /* pad2Octets (2 bytes) */
1621 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_SYNCHRONIZE, s, FALSE);
1622 Stream_Release(s);
1623 return ret;
1624}
1625
1626static BOOL update_send_desktop_resize(rdpContext* context)
1627{
1628 WINPR_ASSERT(context);
1629 return rdp_server_reactivate(context->rdp);
1630}
1631
1632static BOOL update_send_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate)
1633{
1634 wStream* s = nullptr;
1635 WINPR_ASSERT(context);
1636 rdpRdp* rdp = context->rdp;
1637 rdpUpdate* update = context->update;
1638 BOOL ret = TRUE;
1639
1640 if (!update_force_flush(context))
1641 return FALSE;
1642
1643 WINPR_ASSERT(rdp);
1644 s = fastpath_update_pdu_init(rdp->fastpath);
1645
1646 if (!s)
1647 return FALSE;
1648
1649 if (!update_write_bitmap_update(update, s, bitmapUpdate) ||
1650 !fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_BITMAP, s,
1651 bitmapUpdate->skipCompression))
1652 {
1653 ret = FALSE;
1654 goto out_fail;
1655 }
1656
1657 ret = update_force_flush(context);
1658
1659out_fail:
1660 Stream_Release(s);
1661 return ret;
1662}
1663
1664static BOOL update_send_play_sound(rdpContext* context, const PLAY_SOUND_UPDATE* play_sound)
1665{
1666 UINT16 sec_flags = 0;
1667 wStream* s = nullptr;
1668 WINPR_ASSERT(context);
1669 rdpRdp* rdp = context->rdp;
1670
1671 WINPR_ASSERT(rdp);
1672 WINPR_ASSERT(rdp->settings);
1673 WINPR_ASSERT(play_sound);
1674 WINPR_ASSERT(rdp->settings->ReceivedCapabilities);
1675 WINPR_ASSERT(rdp->settings->ReceivedCapabilitiesSize > CAPSET_TYPE_SOUND);
1676 if (!rdp->settings->ReceivedCapabilities[CAPSET_TYPE_SOUND])
1677 {
1678 return TRUE;
1679 }
1680
1681 s = rdp_data_pdu_init(rdp, &sec_flags);
1682
1683 if (!s)
1684 return FALSE;
1685
1686 Stream_Write_UINT32(s, play_sound->duration);
1687 Stream_Write_UINT32(s, play_sound->frequency);
1688 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_PLAY_SOUND, rdp->mcs->userId, sec_flags);
1689}
1690
1695static BOOL update_send_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt)
1696{
1697 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1698
1699 WINPR_ASSERT(context);
1700 WINPR_ASSERT(dstblt);
1701
1702 rdp_update_internal* update = update_cast(context->update);
1703
1704 const size_t headerLength = update_prepare_order_info(context, &orderInfo, ORDER_TYPE_DSTBLT);
1705 const size_t inf = update_approximate_dstblt_order(&orderInfo, dstblt);
1706 if (!update_check_flush(context, headerLength + inf))
1707 return FALSE;
1708
1709 wStream* s = update->us;
1710
1711 if (!s)
1712 return FALSE;
1713
1714 const size_t offset = Stream_GetPosition(s);
1715
1716 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1717 return FALSE;
1718
1719 Stream_Seek(s, headerLength);
1720
1721 if (!update_write_dstblt_order(s, &orderInfo, dstblt))
1722 return FALSE;
1723
1724 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1725 return FALSE;
1726 update->numberOrders++;
1727 return TRUE;
1728}
1729
1730static BOOL update_send_patblt(rdpContext* context, PATBLT_ORDER* patblt)
1731{
1732 size_t offset = 0;
1733 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1734
1735 WINPR_ASSERT(context);
1736 WINPR_ASSERT(patblt);
1737 rdp_update_internal* update = update_cast(context->update);
1738
1739 const size_t headerLength = update_prepare_order_info(context, &orderInfo, ORDER_TYPE_PATBLT);
1740 if (!update_check_flush(context,
1741 headerLength + update_approximate_patblt_order(&orderInfo, patblt)))
1742 return FALSE;
1743
1744 wStream* s = update->us;
1745
1746 if (!s)
1747 return FALSE;
1748
1749 offset = Stream_GetPosition(s);
1750
1751 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1752 return FALSE;
1753
1754 Stream_Seek(s, headerLength);
1755 if (!update_write_patblt_order(s, &orderInfo, patblt))
1756 return FALSE;
1757 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1758 return FALSE;
1759 update->numberOrders++;
1760 return TRUE;
1761}
1762
1763static BOOL update_send_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt)
1764{
1765 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1766
1767 WINPR_ASSERT(context);
1768 WINPR_ASSERT(scrblt);
1769 rdp_update_internal* update = update_cast(context->update);
1770
1771 const size_t headerLength = update_prepare_order_info(context, &orderInfo, ORDER_TYPE_SCRBLT);
1772 const size_t inf = update_approximate_scrblt_order(&orderInfo, scrblt);
1773 if (!update_check_flush(context, headerLength + inf))
1774 return FALSE;
1775
1776 wStream* s = update->us;
1777
1778 if (!s)
1779 return TRUE;
1780
1781 const size_t offset = Stream_GetPosition(s);
1782
1783 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1784 return FALSE;
1785
1786 Stream_Seek(s, headerLength);
1787 if (!update_write_scrblt_order(s, &orderInfo, scrblt))
1788 return FALSE;
1789 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1790 return FALSE;
1791 update->numberOrders++;
1792 return TRUE;
1793}
1794
1795static BOOL update_send_opaque_rect(rdpContext* context, const OPAQUE_RECT_ORDER* opaque_rect)
1796{
1797 size_t offset = 0;
1798 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1799
1800 WINPR_ASSERT(context);
1801 WINPR_ASSERT(opaque_rect);
1802 rdp_update_internal* update = update_cast(context->update);
1803
1804 const size_t headerLength =
1805 update_prepare_order_info(context, &orderInfo, ORDER_TYPE_OPAQUE_RECT);
1806 if (!update_check_flush(
1807 context, headerLength + update_approximate_opaque_rect_order(&orderInfo, opaque_rect)))
1808 return FALSE;
1809
1810 wStream* s = update->us;
1811
1812 if (!s)
1813 return FALSE;
1814
1815 offset = Stream_GetPosition(s);
1816
1817 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1818 return FALSE;
1819
1820 Stream_Seek(s, headerLength);
1821 if (!update_write_opaque_rect_order(s, &orderInfo, opaque_rect))
1822 return FALSE;
1823 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1824 return FALSE;
1825 update->numberOrders++;
1826 return TRUE;
1827}
1828
1829static BOOL update_send_line_to(rdpContext* context, const LINE_TO_ORDER* line_to)
1830{
1831 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1832
1833 WINPR_ASSERT(context);
1834 WINPR_ASSERT(line_to);
1835 rdp_update_internal* update = update_cast(context->update);
1836 const size_t headerLength = update_prepare_order_info(context, &orderInfo, ORDER_TYPE_LINE_TO);
1837 const size_t inf = update_approximate_line_to_order(&orderInfo, line_to);
1838 if (!update_check_flush(context, headerLength + inf))
1839 return FALSE;
1840
1841 wStream* s = update->us;
1842
1843 if (!s)
1844 return FALSE;
1845
1846 const size_t offset = Stream_GetPosition(s);
1847
1848 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1849 return FALSE;
1850
1851 Stream_Seek(s, headerLength);
1852 if (!update_write_line_to_order(s, &orderInfo, line_to))
1853 return FALSE;
1854 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1855 return FALSE;
1856 update->numberOrders++;
1857 return TRUE;
1858}
1859
1860static BOOL update_send_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
1861{
1862 size_t offset = 0;
1863 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1864
1865 WINPR_ASSERT(context);
1866 WINPR_ASSERT(memblt);
1867 rdp_update_internal* update = update_cast(context->update);
1868 const size_t headerLength = update_prepare_order_info(context, &orderInfo, ORDER_TYPE_MEMBLT);
1869 if (!update_check_flush(context,
1870 headerLength + update_approximate_memblt_order(&orderInfo, memblt)))
1871 return FALSE;
1872
1873 wStream* s = update->us;
1874
1875 if (!s)
1876 return FALSE;
1877
1878 offset = Stream_GetPosition(s);
1879
1880 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1881 return FALSE;
1882
1883 Stream_Seek(s, headerLength);
1884 if (!update_write_memblt_order(s, &orderInfo, memblt))
1885 return FALSE;
1886 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1887 return FALSE;
1888 update->numberOrders++;
1889 return TRUE;
1890}
1891
1892static BOOL update_send_glyph_index(rdpContext* context, GLYPH_INDEX_ORDER* glyph_index)
1893{
1894 ORDER_INFO orderInfo = WINPR_C_ARRAY_INIT;
1895
1896 WINPR_ASSERT(context);
1897 WINPR_ASSERT(glyph_index);
1898 rdp_update_internal* update = update_cast(context->update);
1899
1900 const size_t headerLength =
1901 update_prepare_order_info(context, &orderInfo, ORDER_TYPE_GLYPH_INDEX);
1902 const size_t inf = update_approximate_glyph_index_order(&orderInfo, glyph_index);
1903 if (!update_check_flush(context, headerLength + inf))
1904 return FALSE;
1905
1906 wStream* s = update->us;
1907
1908 if (!s)
1909 return FALSE;
1910
1911 const size_t offset = Stream_GetPosition(s);
1912
1913 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1914 return FALSE;
1915
1916 Stream_Seek(s, headerLength);
1917 if (!update_write_glyph_index_order(s, &orderInfo, glyph_index))
1918 return FALSE;
1919 if (update_write_order_info(context, s, &orderInfo, offset) < 0)
1920 return FALSE;
1921 update->numberOrders++;
1922 return TRUE;
1923}
1924
1925/*
1926 * Secondary Drawing Orders
1927 */
1928
1929static BOOL update_send_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cache_bitmap)
1930{
1931 const size_t headerLength = 6;
1932 UINT16 extraFlags = 0;
1933
1934 WINPR_ASSERT(context);
1935 WINPR_ASSERT(cache_bitmap);
1936 rdp_update_internal* update = update_cast(context->update);
1937
1938 const BYTE orderType = cache_bitmap->compressed ? ORDER_TYPE_CACHE_BITMAP_COMPRESSED
1939 : ORDER_TYPE_BITMAP_UNCOMPRESSED;
1940 const size_t inf =
1941 update_approximate_cache_bitmap_order(cache_bitmap, cache_bitmap->compressed, &extraFlags);
1942 if (!update_check_flush(context, headerLength + inf))
1943 return FALSE;
1944
1945 wStream* s = update->us;
1946
1947 if (!s)
1948 return FALSE;
1949
1950 const size_t bm = Stream_GetPosition(s);
1951
1952 if (!Stream_EnsureRemainingCapacity(s, headerLength))
1953 return FALSE;
1954
1955 Stream_Seek(s, headerLength);
1956
1957 if (!update_write_cache_bitmap_order(s, cache_bitmap, cache_bitmap->compressed, &extraFlags))
1958 return FALSE;
1959
1960 const size_t em = Stream_GetPosition(s);
1961 WINPR_ASSERT(em >= bm + 13);
1962 const size_t orderLength = (em - bm) - 13;
1963 WINPR_ASSERT(orderLength <= UINT16_MAX);
1964
1965 if (!Stream_SetPosition(s, bm))
1966 return FALSE;
1967 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
1968 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
1969 Stream_Write_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
1970 Stream_Write_UINT8(s, orderType); /* orderType (1 byte) */
1971 update->numberOrders++;
1972 return Stream_SetPosition(s, em);
1973}
1974
1975static BOOL update_send_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cache_bitmap_v2)
1976{
1977 const size_t headerLength = 6;
1978 UINT16 extraFlags = 0;
1979
1980 WINPR_ASSERT(context);
1981 WINPR_ASSERT(cache_bitmap_v2);
1982 rdp_update_internal* update = update_cast(context->update);
1983
1984 const BYTE orderType = cache_bitmap_v2->compressed ? ORDER_TYPE_BITMAP_COMPRESSED_V2
1985 : ORDER_TYPE_BITMAP_UNCOMPRESSED_V2;
1986
1987 if (context->settings->NoBitmapCompressionHeader)
1988 cache_bitmap_v2->flags |= CBR2_NO_BITMAP_COMPRESSION_HDR;
1989
1990 if (!update_check_flush(
1991 context, headerLength + update_approximate_cache_bitmap_v2_order(
1992 cache_bitmap_v2, cache_bitmap_v2->compressed, &extraFlags)))
1993 return FALSE;
1994
1995 wStream* s = update->us;
1996
1997 if (!s)
1998 return FALSE;
1999
2000 const size_t bm = Stream_GetPosition(s);
2001
2002 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2003 return FALSE;
2004
2005 Stream_Seek(s, headerLength);
2006
2007 if (!update_write_cache_bitmap_v2_order(s, cache_bitmap_v2, cache_bitmap_v2->compressed,
2008 &extraFlags))
2009 return FALSE;
2010
2011 const size_t em = Stream_GetPosition(s);
2012 WINPR_ASSERT(em >= bm + 13);
2013 const size_t orderLength = (em - bm) - 13;
2014 WINPR_ASSERT(orderLength <= UINT16_MAX);
2015
2016 if (!Stream_SetPosition(s, bm))
2017 return FALSE;
2018 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2019 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2020 Stream_Write_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
2021 Stream_Write_UINT8(s, orderType); /* orderType (1 byte) */
2022 update->numberOrders++;
2023 return Stream_SetPosition(s, em);
2024}
2025
2026static BOOL update_send_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cache_bitmap_v3)
2027{
2028 const size_t headerLength = 6;
2029 UINT16 extraFlags = 0;
2030
2031 WINPR_ASSERT(context);
2032 WINPR_ASSERT(cache_bitmap_v3);
2033 rdp_update_internal* update = update_cast(context->update);
2034
2035 const BYTE orderType = ORDER_TYPE_BITMAP_COMPRESSED_V3;
2036 if (!update_check_flush(context, headerLength + update_approximate_cache_bitmap_v3_order(
2037 cache_bitmap_v3, &extraFlags)))
2038 return FALSE;
2039
2040 wStream* s = update->us;
2041
2042 if (!s)
2043 return FALSE;
2044
2045 const size_t bm = Stream_GetPosition(s);
2046
2047 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2048 return FALSE;
2049
2050 Stream_Seek(s, headerLength);
2051
2052 if (!update_write_cache_bitmap_v3_order(s, cache_bitmap_v3, &extraFlags))
2053 return FALSE;
2054
2055 const size_t em = Stream_GetPosition(s);
2056 WINPR_ASSERT(em >= bm + 13);
2057 const size_t orderLength = (em - bm) - 13;
2058 WINPR_ASSERT(orderLength <= UINT16_MAX);
2059
2060 if (!Stream_SetPosition(s, bm))
2061 return FALSE;
2062 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2063 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2064 Stream_Write_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
2065 Stream_Write_UINT8(s, orderType); /* orderType (1 byte) */
2066 update->numberOrders++;
2067 return Stream_SetPosition(s, em);
2068}
2069
2070static BOOL update_send_cache_color_table(rdpContext* context,
2071 const CACHE_COLOR_TABLE_ORDER* cache_color_table)
2072{
2073 UINT16 flags = 0;
2074 size_t headerLength = 6;
2075
2076 WINPR_ASSERT(context);
2077 WINPR_ASSERT(cache_color_table);
2078 rdp_update_internal* update = update_cast(context->update);
2079
2080 const size_t inf = update_approximate_cache_color_table_order(cache_color_table, &flags);
2081 if (!update_check_flush(context, headerLength + inf))
2082 return FALSE;
2083
2084 wStream* s = update->us;
2085
2086 if (!s)
2087 return FALSE;
2088
2089 const size_t bm = Stream_GetPosition(s);
2090
2091 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2092 return FALSE;
2093
2094 Stream_Seek(s, headerLength);
2095
2096 if (!update_write_cache_color_table_order(s, cache_color_table, &flags))
2097 return FALSE;
2098
2099 const size_t em = Stream_GetPosition(s);
2100 WINPR_ASSERT(em >= bm + 13);
2101 const size_t orderLength = (em - bm) - 13;
2102 WINPR_ASSERT(orderLength <= UINT16_MAX);
2103 if (!Stream_SetPosition(s, bm))
2104 return FALSE;
2105 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2106 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2107 Stream_Write_UINT16(s, flags); /* extraFlags (2 bytes) */
2108 Stream_Write_UINT8(s, ORDER_TYPE_CACHE_COLOR_TABLE); /* orderType (1 byte) */
2109 update->numberOrders++;
2110 return Stream_SetPosition(s, em);
2111}
2112
2113static BOOL update_send_cache_glyph(rdpContext* context, const CACHE_GLYPH_ORDER* cache_glyph)
2114{
2115 UINT16 flags = 0;
2116 const size_t headerLength = 6;
2117
2118 WINPR_ASSERT(context);
2119 WINPR_ASSERT(cache_glyph);
2120 rdp_update_internal* update = update_cast(context->update);
2121
2122 const size_t inf = update_approximate_cache_glyph_order(cache_glyph, &flags);
2123 if (!update_check_flush(context, headerLength + inf))
2124 return FALSE;
2125
2126 wStream* s = update->us;
2127
2128 if (!s)
2129 return FALSE;
2130
2131 const size_t bm = Stream_GetPosition(s);
2132
2133 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2134 return FALSE;
2135
2136 Stream_Seek(s, headerLength);
2137
2138 if (!update_write_cache_glyph_order(s, cache_glyph, &flags))
2139 return FALSE;
2140
2141 const size_t em = Stream_GetPosition(s);
2142 WINPR_ASSERT(em >= bm + 13);
2143 const size_t orderLength = (em - bm) - 13;
2144 WINPR_ASSERT(orderLength <= UINT16_MAX);
2145 if (!Stream_SetPosition(s, bm))
2146 return FALSE;
2147 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2148 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2149 Stream_Write_UINT16(s, flags); /* extraFlags (2 bytes) */
2150 Stream_Write_UINT8(s, ORDER_TYPE_CACHE_GLYPH); /* orderType (1 byte) */
2151 update->numberOrders++;
2152 return Stream_SetPosition(s, em);
2153}
2154
2155static BOOL update_send_cache_glyph_v2(rdpContext* context,
2156 const CACHE_GLYPH_V2_ORDER* cache_glyph_v2)
2157{
2158 UINT16 flags = 0;
2159 const size_t headerLength = 6;
2160
2161 WINPR_ASSERT(context);
2162 WINPR_ASSERT(cache_glyph_v2);
2163 rdp_update_internal* update = update_cast(context->update);
2164
2165 const size_t inf = update_approximate_cache_glyph_v2_order(cache_glyph_v2, &flags);
2166 if (!update_check_flush(context, headerLength + inf))
2167 return FALSE;
2168
2169 wStream* s = update->us;
2170
2171 if (!s)
2172 return FALSE;
2173
2174 const size_t bm = Stream_GetPosition(s);
2175
2176 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2177 return FALSE;
2178
2179 Stream_Seek(s, headerLength);
2180
2181 if (!update_write_cache_glyph_v2_order(s, cache_glyph_v2, &flags))
2182 return FALSE;
2183
2184 const size_t em = Stream_GetPosition(s);
2185 WINPR_ASSERT(em >= bm + 13);
2186 const size_t orderLength = (em - bm) - 13;
2187 WINPR_ASSERT(orderLength <= UINT16_MAX);
2188 if (!Stream_SetPosition(s, bm))
2189 return FALSE;
2190 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2191 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2192 Stream_Write_UINT16(s, flags); /* extraFlags (2 bytes) */
2193 Stream_Write_UINT8(s, ORDER_TYPE_CACHE_GLYPH); /* orderType (1 byte) */
2194 update->numberOrders++;
2195 return Stream_SetPosition(s, em);
2196}
2197
2198static BOOL update_send_cache_brush(rdpContext* context, const CACHE_BRUSH_ORDER* cache_brush)
2199{
2200 UINT16 flags = 0;
2201 const size_t headerLength = 6;
2202
2203 WINPR_ASSERT(context);
2204 WINPR_ASSERT(cache_brush);
2205 rdp_update_internal* update = update_cast(context->update);
2206
2207 const size_t inf = update_approximate_cache_brush_order(cache_brush, &flags);
2208 if (!update_check_flush(context, headerLength + inf))
2209 return FALSE;
2210
2211 wStream* s = update->us;
2212
2213 if (!s)
2214 return FALSE;
2215
2216 const size_t bm = Stream_GetPosition(s);
2217
2218 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2219 return FALSE;
2220
2221 Stream_Seek(s, headerLength);
2222
2223 if (!update_write_cache_brush_order(s, cache_brush, &flags))
2224 return FALSE;
2225
2226 const size_t em = Stream_GetPosition(s);
2227 if (em <= bm + 13)
2228 return FALSE;
2229
2230 const size_t orderLength = (em - bm) - 13;
2231 WINPR_ASSERT(orderLength <= UINT16_MAX);
2232 if (!Stream_SetPosition(s, bm))
2233 return FALSE;
2234 Stream_Write_UINT8(s, ORDER_STANDARD | ORDER_SECONDARY); /* controlFlags (1 byte) */
2235 Stream_Write_UINT16(s, (UINT16)orderLength); /* orderLength (2 bytes) */
2236 Stream_Write_UINT16(s, flags); /* extraFlags (2 bytes) */
2237 Stream_Write_UINT8(s, ORDER_TYPE_CACHE_BRUSH); /* orderType (1 byte) */
2238 update->numberOrders++;
2239 return Stream_SetPosition(s, em);
2240}
2241
2246static BOOL update_send_create_offscreen_bitmap_order(
2247 rdpContext* context, const CREATE_OFFSCREEN_BITMAP_ORDER* create_offscreen_bitmap)
2248{
2249 WINPR_ASSERT(context);
2250 WINPR_ASSERT(create_offscreen_bitmap);
2251 rdp_update_internal* update = update_cast(context->update);
2252
2253 const size_t headerLength = 1;
2254 const size_t orderType = ORDER_TYPE_CREATE_OFFSCREEN_BITMAP;
2255 const size_t controlFlags = ORDER_SECONDARY | (orderType << 2);
2256 const size_t inf = update_approximate_create_offscreen_bitmap_order(create_offscreen_bitmap);
2257 if (!update_check_flush(context, headerLength + inf))
2258 return FALSE;
2259
2260 wStream* s = update->us;
2261
2262 if (!s)
2263 return FALSE;
2264
2265 const size_t bm = Stream_GetPosition(s);
2266
2267 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2268 return FALSE;
2269
2270 Stream_Seek(s, headerLength);
2271
2272 if (!update_write_create_offscreen_bitmap_order(s, create_offscreen_bitmap))
2273 return FALSE;
2274
2275 const size_t em = Stream_GetPosition(s);
2276 if (!Stream_SetPosition(s, bm))
2277 return FALSE;
2278 Stream_Write_UINT8(s,
2279 WINPR_ASSERTING_INT_CAST(uint8_t, controlFlags)); /* controlFlags (1 byte) */
2280 update->numberOrders++;
2281 return Stream_SetPosition(s, em);
2282}
2283
2284static BOOL update_send_switch_surface_order(rdpContext* context,
2285 const SWITCH_SURFACE_ORDER* switch_surface)
2286{
2287 WINPR_ASSERT(context);
2288 WINPR_ASSERT(switch_surface);
2289 rdp_update_internal* update = update_cast(context->update);
2290
2291 const size_t headerLength = 1;
2292 const size_t orderType = ORDER_TYPE_SWITCH_SURFACE;
2293 const size_t controlFlags = ORDER_SECONDARY | (orderType << 2);
2294 const size_t inf = update_approximate_switch_surface_order(switch_surface);
2295 if (!update_check_flush(context, headerLength + inf))
2296 return FALSE;
2297
2298 wStream* s = update->us;
2299
2300 if (!s)
2301 return FALSE;
2302
2303 const size_t bm = Stream_GetPosition(s);
2304
2305 if (!Stream_EnsureRemainingCapacity(s, headerLength))
2306 return FALSE;
2307
2308 Stream_Seek(s, headerLength);
2309
2310 if (!update_write_switch_surface_order(s, switch_surface))
2311 return FALSE;
2312
2313 const size_t em = Stream_GetPosition(s);
2314 if (!Stream_SetPosition(s, bm))
2315 return FALSE;
2316 Stream_Write_UINT8(s,
2317 WINPR_ASSERTING_INT_CAST(uint8_t, controlFlags)); /* controlFlags (1 byte) */
2318 update->numberOrders++;
2319 return Stream_SetPosition(s, em);
2320}
2321
2322static BOOL update_send_pointer_system(rdpContext* context,
2323 const POINTER_SYSTEM_UPDATE* pointer_system)
2324{
2325 wStream* s = nullptr;
2326 BYTE updateCode = 0;
2327
2328 WINPR_ASSERT(context);
2329 rdpRdp* rdp = context->rdp;
2330 BOOL ret = 0;
2331
2332 WINPR_ASSERT(rdp);
2333 s = fastpath_update_pdu_init(rdp->fastpath);
2334
2335 if (!s)
2336 return FALSE;
2337
2338 if (pointer_system->type == SYSPTR_NULL)
2339 updateCode = FASTPATH_UPDATETYPE_PTR_NULL;
2340 else
2341 updateCode = FASTPATH_UPDATETYPE_PTR_DEFAULT;
2342
2343 ret = fastpath_send_update_pdu(rdp->fastpath, updateCode, s, FALSE);
2344 Stream_Release(s);
2345 return ret;
2346}
2347
2348static BOOL update_send_pointer_position(rdpContext* context,
2349 const POINTER_POSITION_UPDATE* pointerPosition)
2350{
2351 wStream* s = nullptr;
2352 WINPR_ASSERT(context);
2353 rdpRdp* rdp = context->rdp;
2354 BOOL ret = FALSE;
2355
2356 WINPR_ASSERT(rdp);
2357 s = fastpath_update_pdu_init(rdp->fastpath);
2358
2359 if (!s)
2360 return FALSE;
2361
2362 if (!Stream_EnsureRemainingCapacity(s, 16))
2363 goto out_fail;
2364
2365 Stream_Write_UINT16(
2366 s, WINPR_ASSERTING_INT_CAST(uint16_t, pointerPosition->xPos)); /* xPos (2 bytes) */
2367 Stream_Write_UINT16(
2368 s, WINPR_ASSERTING_INT_CAST(uint16_t, pointerPosition->yPos)); /* yPos (2 bytes) */
2369 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_PTR_POSITION, s, FALSE);
2370out_fail:
2371 Stream_Release(s);
2372 return ret;
2373}
2374
2375static BOOL update_write_pointer_color(wStream* s, const POINTER_COLOR_UPDATE* pointer_color)
2376{
2377 WINPR_ASSERT(pointer_color);
2378 if (!Stream_EnsureRemainingCapacity(s, 32 + pointer_color->lengthAndMask +
2379 pointer_color->lengthXorMask))
2380 return FALSE;
2381
2382 Stream_Write_UINT16(s, pointer_color->cacheIndex);
2383 Stream_Write_UINT16(s, pointer_color->hotSpotX);
2384 Stream_Write_UINT16(s, pointer_color->hotSpotY);
2385 Stream_Write_UINT16(s, pointer_color->width);
2386 Stream_Write_UINT16(s, pointer_color->height);
2387 Stream_Write_UINT16(s, pointer_color->lengthAndMask);
2388 Stream_Write_UINT16(s, pointer_color->lengthXorMask);
2389
2390 if (pointer_color->lengthXorMask > 0)
2391 Stream_Write(s, pointer_color->xorMaskData, pointer_color->lengthXorMask);
2392
2393 if (pointer_color->lengthAndMask > 0)
2394 Stream_Write(s, pointer_color->andMaskData, pointer_color->lengthAndMask);
2395
2396 Stream_Write_UINT8(s, 0); /* pad (1 byte) */
2397 return TRUE;
2398}
2399
2400static BOOL update_send_pointer_color(rdpContext* context,
2401 const POINTER_COLOR_UPDATE* pointer_color)
2402{
2403 wStream* s = nullptr;
2404
2405 WINPR_ASSERT(context);
2406 rdpRdp* rdp = context->rdp;
2407 BOOL ret = FALSE;
2408
2409 WINPR_ASSERT(rdp);
2410 WINPR_ASSERT(pointer_color);
2411 s = fastpath_update_pdu_init(rdp->fastpath);
2412
2413 if (!s)
2414 return FALSE;
2415
2416 if (!update_write_pointer_color(s, pointer_color))
2417 goto out_fail;
2418
2419 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_COLOR, s, FALSE);
2420out_fail:
2421 Stream_Release(s);
2422 return ret;
2423}
2424
2425static BOOL update_write_pointer_large(wStream* s, const POINTER_LARGE_UPDATE* pointer)
2426{
2427 WINPR_ASSERT(pointer);
2428
2429 if (!Stream_EnsureRemainingCapacity(s, 32 + pointer->lengthAndMask + pointer->lengthXorMask))
2430 return FALSE;
2431
2432 Stream_Write_UINT16(s, pointer->xorBpp);
2433 Stream_Write_UINT16(s, pointer->cacheIndex);
2434 Stream_Write_UINT16(s, pointer->hotSpotX);
2435 Stream_Write_UINT16(s, pointer->hotSpotY);
2436 Stream_Write_UINT16(s, pointer->width);
2437 Stream_Write_UINT16(s, pointer->height);
2438 Stream_Write_UINT32(s, pointer->lengthAndMask);
2439 Stream_Write_UINT32(s, pointer->lengthXorMask);
2440 Stream_Write(s, pointer->xorMaskData, pointer->lengthXorMask);
2441 Stream_Write(s, pointer->andMaskData, pointer->lengthAndMask);
2442 Stream_Write_UINT8(s, 0); /* pad (1 byte) */
2443 return TRUE;
2444}
2445
2446static BOOL update_send_pointer_large(rdpContext* context, const POINTER_LARGE_UPDATE* pointer)
2447{
2448 wStream* s = nullptr;
2449 WINPR_ASSERT(context);
2450 rdpRdp* rdp = context->rdp;
2451 BOOL ret = FALSE;
2452
2453 WINPR_ASSERT(rdp);
2454 WINPR_ASSERT(pointer);
2455 s = fastpath_update_pdu_init(rdp->fastpath);
2456
2457 if (!s)
2458 return FALSE;
2459
2460 if (!update_write_pointer_large(s, pointer))
2461 goto out_fail;
2462
2463 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_LARGE_POINTER, s, FALSE);
2464out_fail:
2465 Stream_Release(s);
2466 return ret;
2467}
2468
2469static BOOL update_send_pointer_new(rdpContext* context, const POINTER_NEW_UPDATE* pointer_new)
2470{
2471 wStream* s = nullptr;
2472
2473 WINPR_ASSERT(context);
2474 rdpRdp* rdp = context->rdp;
2475 BOOL ret = FALSE;
2476
2477 WINPR_ASSERT(rdp);
2478 WINPR_ASSERT(pointer_new);
2479 s = fastpath_update_pdu_init(rdp->fastpath);
2480
2481 if (!s)
2482 return FALSE;
2483
2484 if (!Stream_EnsureRemainingCapacity(s, 16))
2485 goto out_fail;
2486
2487 Stream_Write_UINT16(
2488 s, WINPR_ASSERTING_INT_CAST(uint16_t, pointer_new->xorBpp)); /* xorBpp (2 bytes) */
2489 update_write_pointer_color(s, &pointer_new->colorPtrAttr);
2490 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_POINTER, s, FALSE);
2491out_fail:
2492 Stream_Release(s);
2493 return ret;
2494}
2495
2496static BOOL update_send_pointer_cached(rdpContext* context,
2497 const POINTER_CACHED_UPDATE* pointer_cached)
2498{
2499 wStream* s = nullptr;
2500
2501 WINPR_ASSERT(context);
2502 rdpRdp* rdp = context->rdp;
2503 BOOL ret = 0;
2504
2505 WINPR_ASSERT(rdp);
2506 WINPR_ASSERT(pointer_cached);
2507 s = fastpath_update_pdu_init(rdp->fastpath);
2508
2509 if (!s)
2510 return FALSE;
2511
2512 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(
2513 uint16_t, pointer_cached->cacheIndex)); /* cacheIndex (2 bytes) */
2514 ret = fastpath_send_update_pdu(rdp->fastpath, FASTPATH_UPDATETYPE_CACHED, s, FALSE);
2515 Stream_Release(s);
2516 return ret;
2517}
2518
2519BOOL update_read_refresh_rect(rdpUpdate* update, wStream* s)
2520{
2521 BYTE numberOfAreas = 0;
2522 RECTANGLE_16 areas[256] = WINPR_C_ARRAY_INIT;
2523 rdp_update_internal* up = update_cast(update);
2524
2525 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
2526 return FALSE;
2527
2528 Stream_Read_UINT8(s, numberOfAreas);
2529 Stream_Seek(s, 3); /* pad3Octects */
2530
2531 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, numberOfAreas, 8ull))
2532 return FALSE;
2533
2534 for (BYTE index = 0; index < numberOfAreas; index++)
2535 {
2536 RECTANGLE_16* area = &areas[index];
2537
2538 Stream_Read_UINT16(s, area->left);
2539 Stream_Read_UINT16(s, area->top);
2540 Stream_Read_UINT16(s, area->right);
2541 Stream_Read_UINT16(s, area->bottom);
2542 }
2543
2544 WINPR_ASSERT(update->context);
2545 WINPR_ASSERT(update->context->settings);
2546 if (update->context->settings->RefreshRect)
2547 IFCALL(update->RefreshRect, update->context, numberOfAreas, areas);
2548 else
2549 WLog_Print(up->log, WLOG_WARN, "ignoring refresh rect request from client");
2550
2551 return TRUE;
2552}
2553
2554BOOL update_read_suppress_output(rdpUpdate* update, wStream* s)
2555{
2556 rdp_update_internal* up = update_cast(update);
2557 RECTANGLE_16* prect = nullptr;
2558 RECTANGLE_16 rect = WINPR_C_ARRAY_INIT;
2559 BYTE allowDisplayUpdates = 0;
2560
2561 WINPR_ASSERT(up);
2562 WINPR_ASSERT(s);
2563
2564 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
2565 return FALSE;
2566
2567 Stream_Read_UINT8(s, allowDisplayUpdates);
2568 Stream_Seek(s, 3); /* pad3Octects */
2569
2570 if (allowDisplayUpdates > 0)
2571 {
2572 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(RECTANGLE_16)))
2573 return FALSE;
2574
2575 Stream_Read_UINT16(s, rect.left);
2576 Stream_Read_UINT16(s, rect.top);
2577 Stream_Read_UINT16(s, rect.right);
2578 Stream_Read_UINT16(s, rect.bottom);
2579
2580 prect = &rect;
2581 }
2582
2583 WINPR_ASSERT(update->context);
2584 WINPR_ASSERT(update->context->settings);
2585 if (update->context->settings->SuppressOutput)
2586 IFCALL(update->SuppressOutput, update->context, allowDisplayUpdates, prect);
2587 else
2588 WLog_Print(up->log, WLOG_WARN, "ignoring suppress output request from client");
2589
2590 return TRUE;
2591}
2592
2593static BOOL update_send_set_keyboard_indicators(rdpContext* context, UINT16 led_flags)
2594{
2595 UINT16 sec_flags = 0;
2596 wStream* s = nullptr;
2597
2598 WINPR_ASSERT(context);
2599 rdpRdp* rdp = context->rdp;
2600 s = rdp_data_pdu_init(rdp, &sec_flags);
2601
2602 if (!s)
2603 return FALSE;
2604
2605 Stream_Write_UINT16(s, 0); /* unitId should be 0 according to MS-RDPBCGR 2.2.8.2.1.1 */
2606 Stream_Write_UINT16(s, led_flags); /* ledFlags (2 bytes) */
2607
2608 WINPR_ASSERT(rdp->mcs);
2609 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SET_KEYBOARD_INDICATORS, rdp->mcs->userId,
2610 sec_flags);
2611}
2612
2613static BOOL update_send_set_keyboard_ime_status(rdpContext* context, UINT16 imeId, UINT32 imeState,
2614 UINT32 imeConvMode)
2615{
2616 UINT16 sec_flags = 0;
2617 wStream* s = nullptr;
2618
2619 WINPR_ASSERT(context);
2620 rdpRdp* rdp = context->rdp;
2621 s = rdp_data_pdu_init(rdp, &sec_flags);
2622
2623 if (!s)
2624 return FALSE;
2625
2626 /* unitId should be 0 according to MS-RDPBCGR 2.2.8.2.2.1 */
2627 Stream_Write_UINT16(s, imeId);
2628 Stream_Write_UINT32(s, imeState);
2629 Stream_Write_UINT32(s, imeConvMode);
2630
2631 WINPR_ASSERT(rdp->mcs);
2632 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SET_KEYBOARD_IME_STATUS, rdp->mcs->userId,
2633 sec_flags);
2634}
2635
2636static UINT16 update_calculate_new_or_existing_window(const WINDOW_ORDER_INFO* orderInfo,
2637 const WINDOW_STATE_ORDER* stateOrder)
2638{
2639 size_t orderSize = 11;
2640
2641 WINPR_ASSERT(orderInfo);
2642 WINPR_ASSERT(stateOrder);
2643
2644 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_OWNER) != 0)
2645 orderSize += 4;
2646
2647 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_STYLE) != 0)
2648 orderSize += 8;
2649
2650 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_SHOW) != 0)
2651 orderSize += 1;
2652
2653 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_TITLE) != 0)
2654 orderSize += 2 + stateOrder->titleInfo.length;
2655
2656 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET) != 0)
2657 orderSize += 8;
2658
2659 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE) != 0)
2660 orderSize += 8;
2661
2662 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X) != 0)
2663 orderSize += 8;
2664
2665 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y) != 0)
2666 orderSize += 8;
2667
2668 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_RP_CONTENT) != 0)
2669 orderSize += 1;
2670
2671 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_ROOT_PARENT) != 0)
2672 orderSize += 4;
2673
2674 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) != 0)
2675 orderSize += 8;
2676
2677 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA) != 0)
2678 orderSize += 8;
2679
2680 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE) != 0)
2681 orderSize += 8;
2682
2683 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS) != 0)
2684 {
2685 const size_t len = 2ULL + stateOrder->numWindowRects * sizeof(RECTANGLE_16);
2686 orderSize += len;
2687 }
2688
2689 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET) != 0)
2690 orderSize += 8;
2691
2692 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY) != 0)
2693 {
2694
2695 const size_t len = 2ULL + stateOrder->numVisibilityRects * sizeof(RECTANGLE_16);
2696 orderSize += len;
2697 }
2698
2699 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_OVERLAY_DESCRIPTION) != 0)
2700 orderSize += 2 + stateOrder->OverlayDescription.length;
2701
2702 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_TASKBAR_BUTTON) != 0)
2703 orderSize += 1;
2704
2705 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_ENFORCE_SERVER_ZORDER) != 0)
2706 orderSize += 1;
2707
2708 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_APPBAR_STATE) != 0)
2709 orderSize += 1;
2710
2711 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_APPBAR_EDGE) != 0)
2712 orderSize += 1;
2713
2714 return WINPR_ASSERTING_INT_CAST(uint16_t, orderSize);
2715}
2716
2717static BOOL update_write_order_field_flags(UINT32 fieldFlags, const WINDOW_STATE_ORDER* stateOrder,
2718 wStream* s)
2719{
2720 WINPR_ASSERT(stateOrder);
2721
2722 if ((fieldFlags & WINDOW_ORDER_FIELD_OWNER) != 0)
2723 Stream_Write_UINT32(s, stateOrder->ownerWindowId);
2724
2725 if ((fieldFlags & WINDOW_ORDER_FIELD_STYLE) != 0)
2726 {
2727 Stream_Write_UINT32(s, stateOrder->style);
2728 Stream_Write_UINT32(s, stateOrder->extendedStyle);
2729 }
2730
2731 if ((fieldFlags & WINDOW_ORDER_FIELD_SHOW) != 0)
2732 {
2733 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, stateOrder->showState));
2734 }
2735
2736 if ((fieldFlags & WINDOW_ORDER_FIELD_TITLE) != 0)
2737 {
2738 Stream_Write_UINT16(s, stateOrder->titleInfo.length);
2739 Stream_Write(s, stateOrder->titleInfo.string, stateOrder->titleInfo.length);
2740 }
2741
2742 if ((fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET) != 0)
2743 {
2744 Stream_Write_INT32(s, stateOrder->clientOffsetX);
2745 Stream_Write_INT32(s, stateOrder->clientOffsetY);
2746 }
2747
2748 if ((fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE) != 0)
2749 {
2750 Stream_Write_UINT32(s, stateOrder->clientAreaWidth);
2751 Stream_Write_UINT32(s, stateOrder->clientAreaHeight);
2752 }
2753
2754 if ((fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X) != 0)
2755 {
2756 Stream_Write_UINT32(s, stateOrder->resizeMarginLeft);
2757 Stream_Write_UINT32(s, stateOrder->resizeMarginRight);
2758 }
2759
2760 if ((fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y) != 0)
2761 {
2762 Stream_Write_UINT32(s, stateOrder->resizeMarginTop);
2763 Stream_Write_UINT32(s, stateOrder->resizeMarginBottom);
2764 }
2765
2766 if ((fieldFlags & WINDOW_ORDER_FIELD_RP_CONTENT) != 0)
2767 {
2768 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, stateOrder->RPContent));
2769 }
2770
2771 if ((fieldFlags & WINDOW_ORDER_FIELD_ROOT_PARENT) != 0)
2772 {
2773 Stream_Write_UINT32(s, stateOrder->rootParentHandle);
2774 }
2775
2776 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) != 0)
2777 {
2778 Stream_Write_INT32(s, stateOrder->windowOffsetX);
2779 Stream_Write_INT32(s, stateOrder->windowOffsetY);
2780 }
2781
2782 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA) != 0)
2783 {
2784 Stream_Write_INT32(s, stateOrder->windowClientDeltaX);
2785 Stream_Write_INT32(s, stateOrder->windowClientDeltaY);
2786 }
2787
2788 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE) != 0)
2789 {
2790 Stream_Write_UINT32(s, stateOrder->windowWidth);
2791 Stream_Write_UINT32(s, stateOrder->windowHeight);
2792 }
2793
2794 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS) != 0)
2795 {
2796 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, stateOrder->numWindowRects));
2797 Stream_Write(s, stateOrder->windowRects, stateOrder->numWindowRects * sizeof(RECTANGLE_16));
2798 }
2799
2800 if ((fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET) != 0)
2801 {
2802 Stream_Write_INT32(s, stateOrder->visibleOffsetX);
2803 Stream_Write_INT32(s, stateOrder->visibleOffsetY);
2804 }
2805
2806 if ((fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY) != 0)
2807 {
2808 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, stateOrder->numVisibilityRects));
2809 Stream_Write(s, stateOrder->visibilityRects,
2810 stateOrder->numVisibilityRects * sizeof(RECTANGLE_16));
2811 }
2812
2813 if ((fieldFlags & WINDOW_ORDER_FIELD_OVERLAY_DESCRIPTION) != 0)
2814 {
2815 Stream_Write_UINT16(s, stateOrder->OverlayDescription.length);
2816 Stream_Write(s, stateOrder->OverlayDescription.string,
2817 stateOrder->OverlayDescription.length);
2818 }
2819
2820 if ((fieldFlags & WINDOW_ORDER_FIELD_TASKBAR_BUTTON) != 0)
2821 {
2822 Stream_Write_UINT8(s, stateOrder->TaskbarButton);
2823 }
2824
2825 if ((fieldFlags & WINDOW_ORDER_FIELD_ENFORCE_SERVER_ZORDER) != 0)
2826 {
2827 Stream_Write_UINT8(s, stateOrder->EnforceServerZOrder);
2828 }
2829
2830 if ((fieldFlags & WINDOW_ORDER_FIELD_APPBAR_STATE) != 0)
2831 {
2832 Stream_Write_UINT8(s, stateOrder->AppBarState);
2833 }
2834
2835 if ((fieldFlags & WINDOW_ORDER_FIELD_APPBAR_EDGE) != 0)
2836 {
2837 Stream_Write_UINT8(s, stateOrder->AppBarEdge);
2838 }
2839
2840 return TRUE;
2841}
2842
2843static BOOL update_send_new_or_existing_window(rdpContext* context,
2844 const WINDOW_ORDER_INFO* orderInfo,
2845 const WINDOW_STATE_ORDER* stateOrder)
2846{
2847 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
2848 UINT16 orderSize = update_calculate_new_or_existing_window(orderInfo, stateOrder);
2849
2850 WINPR_ASSERT(context);
2851 WINPR_ASSERT(orderInfo);
2852 WINPR_ASSERT(stateOrder);
2853
2854 rdp_update_internal* update = update_cast(context->update);
2855
2856 if (!update_check_flush(context, orderSize))
2857 return FALSE;
2858
2859 wStream* s = update->us;
2860
2861 if (!s)
2862 return FALSE;
2863
2864 if (!Stream_EnsureRemainingCapacity(s, orderSize))
2865 return FALSE;
2866
2867 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
2868 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
2869 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
2870 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
2871
2872 if (!update_write_order_field_flags(orderInfo->fieldFlags, stateOrder, s))
2873 return FALSE;
2874
2875 update->numberOrders++;
2876 return TRUE;
2877}
2878
2879static BOOL update_send_window_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
2880 const WINDOW_STATE_ORDER* stateOrder)
2881{
2882 return update_send_new_or_existing_window(context, orderInfo, stateOrder);
2883}
2884
2885static BOOL update_send_window_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
2886 const WINDOW_STATE_ORDER* stateOrder)
2887{
2888 return update_send_new_or_existing_window(context, orderInfo, stateOrder);
2889}
2890
2891static UINT16
2892update_calculate_window_icon_order(WINPR_ATTR_UNUSED const WINDOW_ORDER_INFO* orderInfo,
2893 const WINDOW_ICON_ORDER* iconOrder)
2894{
2895 UINT16 orderSize = 23;
2896
2897 WINPR_ASSERT(iconOrder);
2898 ICON_INFO* iconInfo = iconOrder->iconInfo;
2899 WINPR_ASSERT(iconInfo);
2900
2901 orderSize += iconInfo->cbBitsColor + iconInfo->cbBitsMask;
2902
2903 if (iconInfo->bpp <= 8)
2904 orderSize += 2 + iconInfo->cbColorTable;
2905
2906 return orderSize;
2907}
2908
2909static BOOL update_send_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
2910 const WINDOW_ICON_ORDER* iconOrder)
2911{
2912 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
2913
2914 WINPR_ASSERT(iconOrder);
2915 ICON_INFO* iconInfo = iconOrder->iconInfo;
2916 UINT16 orderSize = update_calculate_window_icon_order(orderInfo, iconOrder);
2917
2918 WINPR_ASSERT(context);
2919 WINPR_ASSERT(orderInfo);
2920 WINPR_ASSERT(iconInfo);
2921
2922 rdp_update_internal* update = update_cast(context->update);
2923
2924 if (!update_check_flush(context, orderSize))
2925 return FALSE;
2926
2927 wStream* s = update->us;
2928
2929 if (!s || !iconInfo)
2930 return FALSE;
2931
2932 if (!Stream_EnsureRemainingCapacity(s, orderSize))
2933 return FALSE;
2934
2935 /* Write Hdr */
2936 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
2937 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
2938 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
2939 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
2940 /* Write body */
2941 Stream_Write_UINT16(
2942 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cacheEntry)); /* CacheEntry (2 bytes) */
2943 Stream_Write_UINT8(s,
2944 WINPR_ASSERTING_INT_CAST(uint8_t, iconInfo->cacheId)); /* CacheId (1 byte) */
2945 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, iconInfo->bpp)); /* Bpp (1 byte) */
2946 Stream_Write_UINT16(s,
2947 WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->width)); /* Width (2 bytes) */
2948 Stream_Write_UINT16(
2949 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->height)); /* Height (2 bytes) */
2950
2951 if (iconInfo->bpp <= 8)
2952 {
2953 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(
2954 uint16_t, iconInfo->cbColorTable)); /* CbColorTable (2 bytes) */
2955 }
2956
2957 Stream_Write_UINT16(
2958 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cbBitsMask)); /* CbBitsMask (2 bytes) */
2959 Stream_Write_UINT16(
2960 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cbBitsColor)); /* CbBitsColor (2 bytes) */
2961 Stream_Write(s, iconInfo->bitsMask, iconInfo->cbBitsMask); /* BitsMask (variable) */
2962
2963 if (iconInfo->bpp <= 8)
2964 {
2965 Stream_Write(s, iconInfo->colorTable, iconInfo->cbColorTable); /* ColorTable (variable) */
2966 }
2967
2968 Stream_Write(s, iconInfo->bitsColor, iconInfo->cbBitsColor); /* BitsColor (variable) */
2969
2970 update->numberOrders++;
2971 return TRUE;
2972}
2973
2974static BOOL update_send_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
2975 const WINDOW_CACHED_ICON_ORDER* cachedIconOrder)
2976{
2977 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
2978 UINT16 orderSize = 14;
2979
2980 WINPR_ASSERT(cachedIconOrder);
2981 const CACHED_ICON_INFO* cachedIcon = &cachedIconOrder->cachedIcon;
2982
2983 WINPR_ASSERT(context);
2984 WINPR_ASSERT(orderInfo);
2985 WINPR_ASSERT(cachedIcon);
2986
2987 rdp_update_internal* update = update_cast(context->update);
2988
2989 if (!update_check_flush(context, orderSize))
2990 return FALSE;
2991
2992 wStream* s = update->us;
2993 if (!s)
2994 return FALSE;
2995
2996 if (!Stream_EnsureRemainingCapacity(s, orderSize))
2997 return FALSE;
2998
2999 /* Write Hdr */
3000 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3001 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3002 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3003 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
3004 /* Write body */
3005 Stream_Write_UINT16(
3006 s, WINPR_ASSERTING_INT_CAST(uint16_t, cachedIcon->cacheEntry)); /* CacheEntry (2 bytes) */
3007 Stream_Write_UINT8(
3008 s, WINPR_ASSERTING_INT_CAST(uint8_t, cachedIcon->cacheId)); /* CacheId (1 byte) */
3009 update->numberOrders++;
3010 return TRUE;
3011}
3012
3013static BOOL update_send_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
3014{
3015 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
3016 UINT16 orderSize = 11;
3017
3018 WINPR_ASSERT(context);
3019 WINPR_ASSERT(orderInfo);
3020 rdp_update_internal* update = update_cast(context->update);
3021
3022 if (!update_check_flush(context, orderSize))
3023 return FALSE;
3024
3025 wStream* s = update->us;
3026
3027 if (!s)
3028 return FALSE;
3029
3030 if (!Stream_EnsureRemainingCapacity(s, orderSize))
3031 return FALSE;
3032
3033 /* Write Hdr */
3034 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3035 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3036 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3037 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
3038 update->numberOrders++;
3039 return TRUE;
3040}
3041
3042static UINT16 update_calculate_new_or_existing_notification_icons_order(
3043 const WINDOW_ORDER_INFO* orderInfo, const NOTIFY_ICON_STATE_ORDER* iconStateOrder)
3044{
3045 UINT16 orderSize = 15;
3046
3047 WINPR_ASSERT(orderInfo);
3048 WINPR_ASSERT(iconStateOrder);
3049
3050 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION) != 0)
3051 orderSize += 4;
3052
3053 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_TIP) != 0)
3054 {
3055 orderSize += 2 + iconStateOrder->toolTip.length;
3056 }
3057
3058 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_INFO_TIP) != 0)
3059 {
3060 NOTIFY_ICON_INFOTIP infoTip = iconStateOrder->infoTip;
3061 orderSize += 12 + infoTip.text.length + infoTip.title.length;
3062 }
3063
3064 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_STATE) != 0)
3065 {
3066 orderSize += 4;
3067 }
3068
3069 if ((orderInfo->fieldFlags & WINDOW_ORDER_ICON) != 0)
3070 {
3071 ICON_INFO iconInfo = iconStateOrder->icon;
3072 orderSize += 12;
3073
3074 if (iconInfo.bpp <= 8)
3075 orderSize += 2 + iconInfo.cbColorTable;
3076
3077 orderSize += iconInfo.cbBitsMask + iconInfo.cbBitsColor;
3078 }
3079 else if ((orderInfo->fieldFlags & WINDOW_ORDER_CACHED_ICON) != 0)
3080 {
3081 orderSize += 3;
3082 }
3083
3084 return orderSize;
3085}
3086
3087static BOOL update_send_new_or_existing_order_icon(const ICON_INFO* iconInfo, wStream* s)
3088{
3089 WINPR_ASSERT(iconInfo);
3090
3091 if (!Stream_EnsureRemainingCapacity(s, 8))
3092 return FALSE;
3093
3094 Stream_Write_UINT16(
3095 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cacheEntry)); /* CacheEntry (2 bytes) */
3096 Stream_Write_UINT8(s,
3097 WINPR_ASSERTING_INT_CAST(uint8_t, iconInfo->cacheId)); /* CacheId (1 byte) */
3098 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(uint8_t, iconInfo->bpp)); /* Bpp (1 byte) */
3099 Stream_Write_UINT16(s,
3100 WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->width)); /* Width (2 bytes) */
3101 Stream_Write_UINT16(
3102 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->height)); /* Height (2 bytes) */
3103
3104 if (iconInfo->bpp <= 8)
3105 {
3106 if (!Stream_EnsureRemainingCapacity(s, 2))
3107 return FALSE;
3108 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(
3109 uint16_t, iconInfo->cbColorTable)); /* CbColorTable (2 bytes) */
3110 }
3111
3112 if (!Stream_EnsureRemainingCapacity(s, 4ULL + iconInfo->cbBitsMask))
3113 return FALSE;
3114 Stream_Write_UINT16(
3115 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cbBitsMask)); /* CbBitsMask (2 bytes) */
3116 Stream_Write_UINT16(
3117 s, WINPR_ASSERTING_INT_CAST(uint16_t, iconInfo->cbBitsColor)); /* CbBitsColor (2 bytes) */
3118 Stream_Write(s, iconInfo->bitsMask, iconInfo->cbBitsMask); /* BitsMask (variable) */
3119
3120 if (iconInfo->bpp <= 8)
3121 {
3122 if (!Stream_EnsureRemainingCapacity(s, iconInfo->cbColorTable))
3123 return FALSE;
3124 Stream_Write(s, iconInfo->colorTable, iconInfo->cbColorTable); /* ColorTable (variable) */
3125 }
3126
3127 if (!Stream_EnsureRemainingCapacity(s, iconInfo->cbBitsColor))
3128 return FALSE;
3129 Stream_Write(s, iconInfo->bitsColor, iconInfo->cbBitsColor); /* BitsColor (variable) */
3130 return TRUE;
3131}
3132
3133static BOOL
3134update_send_new_or_existing_notification_icons(rdpContext* context,
3135 const WINDOW_ORDER_INFO* orderInfo,
3136 const NOTIFY_ICON_STATE_ORDER* iconStateOrder)
3137{
3138 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
3139 BOOL versionFieldPresent = FALSE;
3140 const UINT16 orderSize =
3141 update_calculate_new_or_existing_notification_icons_order(orderInfo, iconStateOrder);
3142
3143 WINPR_ASSERT(context);
3144 WINPR_ASSERT(orderInfo);
3145 WINPR_ASSERT(iconStateOrder);
3146 rdp_update_internal* update = update_cast(context->update);
3147
3148 if (!update_check_flush(context, orderSize))
3149 return FALSE;
3150
3151 wStream* s = update->us;
3152 if (!s)
3153 return FALSE;
3154
3155 if (!Stream_EnsureRemainingCapacity(s, orderSize))
3156 return FALSE;
3157
3158 /* Write Hdr */
3159 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3160 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3161 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3162 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
3163 Stream_Write_UINT32(s, orderInfo->notifyIconId); /* NotifyIconId (4 bytes) */
3164
3165 /* Write body */
3166 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION) != 0)
3167 {
3168 versionFieldPresent = TRUE;
3169 Stream_Write_UINT32(s, iconStateOrder->version);
3170 }
3171
3172 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_TIP) != 0)
3173 {
3174 Stream_Write_UINT16(s, iconStateOrder->toolTip.length);
3175 Stream_Write(s, iconStateOrder->toolTip.string, iconStateOrder->toolTip.length);
3176 }
3177
3178 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_INFO_TIP) != 0)
3179 {
3180 NOTIFY_ICON_INFOTIP infoTip = iconStateOrder->infoTip;
3181
3182 /* info tip should not be sent when version is 0 */
3183 if (versionFieldPresent && iconStateOrder->version == 0)
3184 return FALSE;
3185
3186 Stream_Write_UINT32(s, infoTip.timeout); /* Timeout (4 bytes) */
3187 Stream_Write_UINT32(s, infoTip.flags); /* InfoFlags (4 bytes) */
3188 Stream_Write_UINT16(s, infoTip.text.length); /* InfoTipText (variable) */
3189 Stream_Write(s, infoTip.text.string, infoTip.text.length);
3190 Stream_Write_UINT16(s, infoTip.title.length); /* Title (variable) */
3191 Stream_Write(s, infoTip.title.string, infoTip.title.length);
3192 }
3193
3194 if ((orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_STATE) != 0)
3195 {
3196 /* notify state should not be sent when version is 0 */
3197 if (versionFieldPresent && iconStateOrder->version == 0)
3198 return FALSE;
3199
3200 Stream_Write_UINT32(s, iconStateOrder->state);
3201 }
3202
3203 if ((orderInfo->fieldFlags & WINDOW_ORDER_ICON) != 0)
3204 {
3205 const ICON_INFO* iconInfo = &iconStateOrder->icon;
3206
3207 if (!update_send_new_or_existing_order_icon(iconInfo, s))
3208 return FALSE;
3209 }
3210 else if ((orderInfo->fieldFlags & WINDOW_ORDER_CACHED_ICON) != 0)
3211 {
3212 const CACHED_ICON_INFO cachedIcon = iconStateOrder->cachedIcon;
3213 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(
3214 uint16_t, cachedIcon.cacheEntry)); /* CacheEntry (2 bytes) */
3215 Stream_Write_UINT8(
3216 s, WINPR_ASSERTING_INT_CAST(uint8_t, cachedIcon.cacheId)); /* CacheId (1 byte) */
3217 }
3218
3219 update->numberOrders++;
3220 return TRUE;
3221}
3222
3223static BOOL update_send_notify_icon_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
3224 const NOTIFY_ICON_STATE_ORDER* iconStateOrder)
3225{
3226 return update_send_new_or_existing_notification_icons(context, orderInfo, iconStateOrder);
3227}
3228
3229static BOOL update_send_notify_icon_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
3230 const NOTIFY_ICON_STATE_ORDER* iconStateOrder)
3231{
3232 return update_send_new_or_existing_notification_icons(context, orderInfo, iconStateOrder);
3233}
3234
3235static BOOL update_send_notify_icon_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
3236{
3237 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
3238 UINT16 orderSize = 15;
3239
3240 WINPR_ASSERT(context);
3241 WINPR_ASSERT(orderInfo);
3242 rdp_update_internal* update = update_cast(context->update);
3243
3244 if (!update_check_flush(context, orderSize))
3245 return FALSE;
3246
3247 wStream* s = update->us;
3248
3249 if (!s)
3250 return FALSE;
3251
3252 /* Write Hdr */
3253 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3254 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3255 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3256 Stream_Write_UINT32(s, orderInfo->windowId); /* WindowID (4 bytes) */
3257 Stream_Write_UINT32(s, orderInfo->notifyIconId); /* NotifyIconId (4 bytes) */
3258 update->numberOrders++;
3259 return TRUE;
3260}
3261
3262static UINT16 update_calculate_monitored_desktop(const WINDOW_ORDER_INFO* orderInfo,
3263 const MONITORED_DESKTOP_ORDER* monitoredDesktop)
3264{
3265 UINT16 orderSize = 7;
3266
3267 WINPR_ASSERT(orderInfo);
3268 WINPR_ASSERT(monitoredDesktop);
3269
3270 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND)
3271 {
3272 orderSize += 4;
3273 }
3274
3275 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ZORDER)
3276 {
3277 orderSize += 1 + (4 * monitoredDesktop->numWindowIds);
3278 }
3279
3280 return orderSize;
3281}
3282
3283static BOOL update_send_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
3284 const MONITORED_DESKTOP_ORDER* monitoredDesktop)
3285{
3286 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
3287 UINT16 orderSize = update_calculate_monitored_desktop(orderInfo, monitoredDesktop);
3288
3289 WINPR_ASSERT(context);
3290 WINPR_ASSERT(orderInfo);
3291 WINPR_ASSERT(monitoredDesktop);
3292
3293 rdp_update_internal* update = update_cast(context->update);
3294
3295 if (!update_check_flush(context, orderSize))
3296 return FALSE;
3297
3298 wStream* s = update->us;
3299
3300 if (!s)
3301 return FALSE;
3302
3303 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3304 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3305 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3306
3307 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ACTIVE_WND)
3308 {
3309 Stream_Write_UINT32(s, monitoredDesktop->activeWindowId); /* activeWindowId (4 bytes) */
3310 }
3311
3312 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_DESKTOP_ZORDER)
3313 {
3314 Stream_Write_UINT8(
3315 s, WINPR_ASSERTING_INT_CAST(
3316 uint8_t, monitoredDesktop->numWindowIds)); /* numWindowIds (1 byte) */
3317
3318 /* windowIds */
3319 for (UINT32 i = 0; i < monitoredDesktop->numWindowIds; i++)
3320 {
3321 Stream_Write_UINT32(s,
3322 WINPR_ASSERTING_INT_CAST(uint32_t, monitoredDesktop->windowIds[i]));
3323 }
3324 }
3325
3326 update->numberOrders++;
3327 return TRUE;
3328}
3329
3330static BOOL update_send_non_monitored_desktop(rdpContext* context,
3331 const WINDOW_ORDER_INFO* orderInfo)
3332{
3333 BYTE controlFlags = ORDER_SECONDARY | (ORDER_TYPE_WINDOW << 2);
3334 UINT16 orderSize = 7;
3335
3336 WINPR_ASSERT(context);
3337 WINPR_ASSERT(orderInfo);
3338 rdp_update_internal* update = update_cast(context->update);
3339
3340 if (!update_check_flush(context, orderSize))
3341 return FALSE;
3342
3343 wStream* s = update->us;
3344
3345 if (!s)
3346 return FALSE;
3347
3348 Stream_Write_UINT8(s, controlFlags); /* Header (1 byte) */
3349 Stream_Write_UINT16(s, orderSize); /* OrderSize (2 bytes) */
3350 Stream_Write_UINT32(s, orderInfo->fieldFlags); /* FieldsPresentFlags (4 bytes) */
3351 update->numberOrders++;
3352 return TRUE;
3353}
3354
3355void update_register_server_callbacks(rdpUpdate* update)
3356{
3357 WINPR_ASSERT(update);
3358
3359 update->BeginPaint = s_update_begin_paint;
3360 update->EndPaint = s_update_end_paint;
3361 update->SetBounds = update_set_bounds;
3362 update->Synchronize = update_send_synchronize;
3363 update->DesktopResize = update_send_desktop_resize;
3364 update->BitmapUpdate = update_send_bitmap_update;
3365 update->SurfaceBits = update_send_surface_bits;
3366 update->SurfaceFrameMarker = update_send_surface_frame_marker;
3367 update->SurfaceCommand = update_send_surface_command;
3368 update->SurfaceFrameBits = update_send_surface_frame_bits;
3369 update->PlaySound = update_send_play_sound;
3370 update->SetKeyboardIndicators = update_send_set_keyboard_indicators;
3371 update->SetKeyboardImeStatus = update_send_set_keyboard_ime_status;
3372 update->SaveSessionInfo = rdp_send_save_session_info;
3373 update->ServerStatusInfo = rdp_send_server_status_info;
3374 update->primary->DstBlt = update_send_dstblt;
3375 update->primary->PatBlt = update_send_patblt;
3376 update->primary->ScrBlt = update_send_scrblt;
3377 update->primary->OpaqueRect = update_send_opaque_rect;
3378 update->primary->LineTo = update_send_line_to;
3379 update->primary->MemBlt = update_send_memblt;
3380 update->primary->GlyphIndex = update_send_glyph_index;
3381 update->secondary->CacheBitmap = update_send_cache_bitmap;
3382 update->secondary->CacheBitmapV2 = update_send_cache_bitmap_v2;
3383 update->secondary->CacheBitmapV3 = update_send_cache_bitmap_v3;
3384 update->secondary->CacheColorTable = update_send_cache_color_table;
3385 update->secondary->CacheGlyph = update_send_cache_glyph;
3386 update->secondary->CacheGlyphV2 = update_send_cache_glyph_v2;
3387 update->secondary->CacheBrush = update_send_cache_brush;
3388 update->altsec->CreateOffscreenBitmap = update_send_create_offscreen_bitmap_order;
3389 update->altsec->SwitchSurface = update_send_switch_surface_order;
3390 update->pointer->PointerSystem = update_send_pointer_system;
3391 update->pointer->PointerPosition = update_send_pointer_position;
3392 update->pointer->PointerColor = update_send_pointer_color;
3393 update->pointer->PointerLarge = update_send_pointer_large;
3394 update->pointer->PointerNew = update_send_pointer_new;
3395 update->pointer->PointerCached = update_send_pointer_cached;
3396 update->window->WindowCreate = update_send_window_create;
3397 update->window->WindowUpdate = update_send_window_update;
3398 update->window->WindowIcon = update_send_window_icon;
3399 update->window->WindowCachedIcon = update_send_window_cached_icon;
3400 update->window->WindowDelete = update_send_window_delete;
3401 update->window->NotifyIconCreate = update_send_notify_icon_create;
3402 update->window->NotifyIconUpdate = update_send_notify_icon_update;
3403 update->window->NotifyIconDelete = update_send_notify_icon_delete;
3404 update->window->MonitoredDesktop = update_send_monitored_desktop;
3405 update->window->NonMonitoredDesktop = update_send_non_monitored_desktop;
3406}
3407
3408void update_register_client_callbacks(rdpUpdate* update)
3409{
3410 WINPR_ASSERT(update);
3411
3412 update->RefreshRect = update_send_refresh_rect;
3413 update->SuppressOutput = update_send_suppress_output;
3414 update->SurfaceFrameAcknowledge = update_send_frame_acknowledge;
3415}
3416
3417int update_process_messages(rdpUpdate* update)
3418{
3419 return update_message_queue_process_pending_messages(update);
3420}
3421
3422static void update_free_queued_message(void* obj)
3423{
3424 wMessage* msg = (wMessage*)obj;
3425 update_message_queue_free_message(msg);
3426}
3427
3428void update_free_window_state(WINDOW_STATE_ORDER* window_state)
3429{
3430 if (!window_state)
3431 return;
3432
3433 free(window_state->OverlayDescription.string);
3434 free(window_state->titleInfo.string);
3435 free(window_state->windowRects);
3436 free(window_state->visibilityRects);
3437 memset(window_state, 0, sizeof(WINDOW_STATE_ORDER));
3438}
3439
3440rdpUpdate* update_new(rdpRdp* rdp)
3441{
3442 const wObject cb = { nullptr, nullptr, nullptr, update_free_queued_message, nullptr };
3443
3444 WINPR_ASSERT(rdp);
3445 WINPR_ASSERT(rdp->context);
3446
3447 rdp_update_internal* update = (rdp_update_internal*)calloc(1, sizeof(rdp_update_internal));
3448
3449 if (!update)
3450 return nullptr;
3451
3452 update->common.context = rdp->context;
3453 update->log = WLog_Get("com.freerdp.core.update");
3454 InitializeCriticalSection(&(update->mux));
3455 update->common.pointer = (rdpPointerUpdate*)calloc(1, sizeof(rdpPointerUpdate));
3456
3457 if (!update->common.pointer)
3458 goto fail;
3459
3460 {
3463
3464 if (!primary)
3465 goto fail;
3466 update->common.primary = &primary->common;
3467 }
3468
3469 {
3472
3473 if (!secondary)
3474 goto fail;
3475 update->common.secondary = &secondary->common;
3476 }
3477
3478 {
3481
3482 if (!altsec)
3483 goto fail;
3484
3485 update->common.altsec = &altsec->common;
3486
3487 update->common.window = (rdpWindowUpdate*)calloc(1, sizeof(rdpWindowUpdate));
3488
3489 if (!update->common.window)
3490 goto fail;
3491
3492 {
3493 OFFSCREEN_DELETE_LIST* deleteList = &(altsec->create_offscreen_bitmap.deleteList);
3494 deleteList->sIndices = 64;
3495 deleteList->indices = calloc(deleteList->sIndices, 2);
3496
3497 if (!deleteList->indices)
3498 goto fail;
3499
3500 deleteList->cIndices = 0;
3501 }
3502 }
3503
3504 update->common.SuppressOutput = update_send_suppress_output;
3505 update->initialState = TRUE;
3506 update->common.autoCalculateBitmapData = TRUE;
3507 update->queue = MessageQueue_New(&cb);
3508
3509 if (!update->queue)
3510 goto fail;
3511
3512 return &update->common;
3513fail:
3514 WINPR_PRAGMA_DIAG_PUSH
3515 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
3516 update_free(&update->common);
3517 WINPR_PRAGMA_DIAG_POP
3518 return nullptr;
3519}
3520
3521void update_free(rdpUpdate* update)
3522{
3523 if (update != nullptr)
3524 {
3525 rdp_update_internal* up = update_cast(update);
3526 rdp_altsec_update_internal* altsec = altsec_update_cast(update->altsec);
3527 OFFSCREEN_DELETE_LIST* deleteList = &(altsec->create_offscreen_bitmap.deleteList);
3528
3529 if (deleteList)
3530 free(deleteList->indices);
3531
3532 free(update->pointer);
3533
3534 if (update->primary)
3535 {
3536 rdp_primary_update_internal* primary = primary_update_cast(update->primary);
3537
3538 free(primary->polygon_cb.points);
3539 free(primary->polyline.points);
3540 free(primary->polygon_sc.points);
3541 free(primary->fast_glyph.glyphData.aj);
3542 free(primary);
3543 }
3544
3545 free(update->secondary);
3546 free(altsec);
3547
3548 if (update->window)
3549 free(update->window);
3550
3551 MessageQueue_Free(up->queue);
3552 DeleteCriticalSection(&up->mux);
3553
3554 if (up->us)
3555 Stream_Free(up->us, TRUE);
3556 free(update);
3557 }
3558}
3559
3560void rdp_update_lock(rdpUpdate* update)
3561{
3562 rdp_update_internal* up = update_cast(update);
3563 EnterCriticalSection(&up->mux);
3564}
3565
3566void rdp_update_unlock(rdpUpdate* update)
3567{
3568 rdp_update_internal* up = update_cast(update);
3569 LeaveCriticalSection(&up->mux);
3570}
3571
3572BOOL update_begin_paint(rdpUpdate* update)
3573{
3574 rdp_update_internal* up = update_cast(update);
3575 WINPR_ASSERT(update);
3576 rdp_update_lock(update);
3577
3578 up->withinBeginEndPaint = TRUE;
3579
3580 WINPR_ASSERT(update->context);
3581
3582 up->stats.base[RDP_STATS_BEGIN_PAINT]++;
3583 BOOL rc = IFCALLRESULT(TRUE, update->BeginPaint, update->context);
3584 if (!rc)
3585 WLog_WARN(TAG, "BeginPaint call failed");
3586
3587 /* Reset the invalid regions, we start a new frame here. */
3588 rdpGdi* gdi = update->context->gdi;
3589 if (!gdi)
3590 return rc;
3591
3592 if (gdi->hdc && gdi->primary && gdi->primary->hdc)
3593 {
3594 HGDI_WND hwnd = gdi->primary->hdc->hwnd;
3595 WINPR_ASSERT(hwnd);
3596 WINPR_ASSERT(hwnd->invalid);
3597
3598 hwnd->invalid->null = TRUE;
3599 hwnd->ninvalid = 0;
3600 }
3601
3602 return rc;
3603}
3604
3605BOOL update_end_paint(rdpUpdate* update)
3606{
3607 rdp_update_internal* up = update_cast(update);
3608 BOOL rc = TRUE;
3609
3610 WINPR_ASSERT(update);
3611 up->stats.base[RDP_STATS_END_PAINT]++;
3612
3613 IFCALLRET(update->EndPaint, rc, update->context);
3614 if (!rc)
3615 WLog_WARN(TAG, "EndPaint call failed");
3616
3617 if (!up->withinBeginEndPaint)
3618 return rc;
3619 up->withinBeginEndPaint = FALSE;
3620
3621 rdp_update_unlock(update);
3622 return rc;
3623}
3624
3625uint64_t rdp_stats_value_for_index(rdpUpdate* context, size_t index)
3626{
3627 rdp_update_internal* up = update_cast(context);
3628 WINPR_ASSERT(up);
3629
3630 size_t limit = ARRAYSIZE(up->stats.primary);
3631 size_t offset = 0;
3632 if (index < limit)
3633 return up->stats.primary[index];
3634
3635 offset = limit;
3636 limit += ARRAYSIZE(up->stats.secondary);
3637 if (index < limit)
3638 return up->stats.secondary[index - offset];
3639
3640 offset = limit;
3641 limit += ARRAYSIZE(up->stats.altsec);
3642 if (index < limit)
3643 return up->stats.altsec[index - offset];
3644
3645 offset = limit;
3646 limit += ARRAYSIZE(up->stats.base);
3647 if (index < limit)
3648 return up->stats.base[index - offset];
3649
3650 return 0;
3651}
3652
3653const char* rdp_stats_name_for_index(size_t index)
3654{
3655 if (!InitOnceExecuteOnce(&stats_names_once, stats_names_generate, nullptr, nullptr))
3656 return "RDP_STATS_UNUSED";
3657 if (index < rdp_stats_max_index())
3658 return stats_names[index];
3659 return "RDP_STATS_UNUSED";
3660}
3661
3662size_t rdp_stats_max_index(void)
3663{
3664 return RDP_STATS_COUNT;
3665}
3666
3667void update_dump_stats(rdpUpdate* update)
3668{
3669 rdp_update_internal* up = update_cast(update);
3670 WINPR_ASSERT(up);
3671
3672 wLog* log = up->log;
3673 const DWORD level = WLOG_TRACE;
3674 if (!WLog_IsLevelActive(log, level))
3675 return;
3676
3677 WLog_Print(log, level, "RdpCodecStats");
3678 for (size_t x = 0; x < rdp_stats_max_index(); x++)
3679 {
3680 const char* name = rdp_stats_name_for_index(x);
3681 const uint64_t val = rdp_stats_value_for_index(update, x);
3682 WINPR_ASSERT(name && strnlen(name, 2) > 0);
3683 const bool unknown = strstr(name, " UNKNOWN") != nullptr;
3684 const bool unused = strstr(name, "UNUSED") != nullptr;
3685 const bool sunused = strcmp("RDP_STATS_UNUSED", name) == 0;
3686 if ((val != 0) || (!unknown && !sunused && !unused))
3687 WLog_Print(log, level, "%s: %" PRIu64, name, val);
3688 }
3689}
Definition types.h:82
This struct contains function pointer to initialize/free objects.
Definition collections.h:52