#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/sysinfo.h>
+#include <winpr/interlocked.h>
#include <freerdp/log.h>
#define TAG CLIENT_TAG("shadow")
-void shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
+static void shadow_client_free_queued_message(void *obj)
+{
+ wMessage *message = (wMessage*)obj;
+ if (message->Free)
+ {
+ message->Free(message);
+ message->Free = NULL;
+ }
+}
+
+BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
{
rdpSettings* settings;
rdpShadowServer* server;
+ const wObject cb = { NULL, NULL, NULL, shadow_client_free_queued_message, NULL };
server = (rdpShadowServer*) peer->ContextExtra;
client->server = server;
settings->BitmapCacheV3Enabled = TRUE;
settings->FrameMarkerCommandEnabled = TRUE;
settings->SurfaceFrameMarkerEnabled = TRUE;
+ settings->SupportGraphicsPipeline = FALSE;
+
+ settings->DrawAllowSkipAlpha = TRUE;
+ settings->DrawAllowColorSubsampling = TRUE;
+ settings->DrawAllowDynamicColorFidelity = TRUE;
+
+ settings->CompressionLevel = PACKET_COMPR_TYPE_RDP6;
settings->RdpSecurity = TRUE;
settings->TlsSecurity = TRUE;
settings->NlaSecurity = FALSE;
- settings->CertificateFile = _strdup(server->CertificateFile);
- settings->PrivateKeyFile = _strdup(server->PrivateKeyFile);
+ if (!(settings->CertificateFile = _strdup(server->CertificateFile)))
+ goto fail_cert_file;
+ if (!(settings->PrivateKeyFile = _strdup(server->PrivateKeyFile)))
+ goto fail_privkey_file;
+ if (!(settings->RdpKeyFile = _strdup(settings->PrivateKeyFile)))
+ goto fail_rdpkey_file;
+
- settings->RdpKeyFile = _strdup(settings->PrivateKeyFile);
+ if (server->ipcSocket)
+ {
+ settings->LyncRdpMode = TRUE;
+ settings->CompressionEnabled = FALSE;
+ }
client->inLobby = TRUE;
client->mayView = server->mayView;
client->mayInteract = server->mayInteract;
- InitializeCriticalSectionAndSpinCount(&(client->lock), 4000);
+ if (!InitializeCriticalSectionAndSpinCount(&(client->lock), 4000))
+ goto fail_client_lock;
region16_init(&(client->invalidRegion));
client->vcm = WTSOpenServerA((LPSTR) peer->context);
+ if (!client->vcm || client->vcm == INVALID_HANDLE_VALUE)
+ goto fail_open_server;
- client->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!(client->MsgQueue = MessageQueue_New(&cb)))
+ goto fail_message_queue;
- client->encoder = shadow_encoder_new(client);
+ if (!(client->encoder = shadow_encoder_new(client)))
+ goto fail_encoder_new;
- ArrayList_Add(server->clients, (void*) client);
+ if (ArrayList_Add(server->clients, (void*) client) >= 0)
+ return TRUE;
+
+ shadow_encoder_free(client->encoder);
+ client->encoder = NULL;
+fail_encoder_new:
+ MessageQueue_Free(client->MsgQueue);
+ client->MsgQueue = NULL;
+fail_message_queue:
+ WTSCloseServer((HANDLE) client->vcm);
+ client->vcm = NULL;
+fail_open_server:
+ DeleteCriticalSection(&(client->lock));
+fail_client_lock:
+ free(settings->RdpKeyFile);
+ settings->RdpKeyFile = NULL;
+fail_rdpkey_file:
+ free(settings->PrivateKeyFile);
+ settings->PrivateKeyFile = NULL;
+fail_privkey_file:
+ free(settings->CertificateFile);
+ settings->CertificateFile = NULL;
+fail_cert_file:
+
+ return FALSE;
}
void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
WTSCloseServer((HANDLE) client->vcm);
- CloseHandle(client->StopEvent);
+ /* Clear queued messages and free resource */
+ MessageQueue_Clear(client->MsgQueue);
+ MessageQueue_Free(client->MsgQueue);
if (client->lobby)
{
void shadow_client_message_free(wMessage* message)
{
- if (message->id == SHADOW_MSG_IN_REFRESH_OUTPUT_ID)
+ switch(message->id)
{
- SHADOW_MSG_IN_REFRESH_OUTPUT* wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam;
+ case SHADOW_MSG_IN_REFRESH_OUTPUT_ID:
+ free(((SHADOW_MSG_IN_REFRESH_OUTPUT*)message->wParam)->rects);
+ free(message->wParam);
+ break;
- free(wParam->rects);
- free(wParam);
- }
- else if (message->id == SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID)
- {
- SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam;
- free(wParam);
+ case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID:
+ free(message->wParam);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %u", message->id);
+ free(message->wParam);
+ break;
}
}
BOOL shadow_client_post_connect(freerdp_peer* peer)
{
+ int authStatus;
int width, height;
rdpSettings* settings;
rdpShadowClient* client;
- rdpShadowSurface* lobby;
rdpShadowServer* server;
RECTANGLE_16 invalidRect;
+ rdpShadowSubsystem* subsystem;
client = (rdpShadowClient*) peer->context;
settings = peer->settings;
server = client->server;
+ subsystem = server->subsystem;
if (!server->shareSubRect)
{
if (settings->ColorDepth == 24)
settings->ColorDepth = 16; /* disable 24bpp */
+ if (settings->MultifragMaxRequestSize < 0x3F0000)
+ settings->NSCodec = FALSE; /* NSCodec compressor does not support fragmentation yet */
+
WLog_ERR(TAG, "Client from %s is activated (%dx%d@%d)",
peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &invalidRect);
- lobby = client->lobby = shadow_surface_new(client->server, 0, 0, width, height);
+ shadow_client_init_lobby(client);
- if (!client->lobby)
- return FALSE;
+ authStatus = -1;
+
+ if (settings->Username && settings->Password)
+ settings->AutoLogonEnabled = TRUE;
- freerdp_image_fill(lobby->data, PIXEL_FORMAT_XRGB32, lobby->scanline,
- 0, 0, lobby->width, lobby->height, 0x3BB9FF);
+ if (settings->AutoLogonEnabled && server->authentication)
+ {
+ if (subsystem->Authenticate)
+ {
+ authStatus = subsystem->Authenticate(subsystem,
+ settings->Username, settings->Domain, settings->Password);
+ }
+ }
- region16_union_rect(&(lobby->invalidRegion), &(lobby->invalidRegion), &invalidRect);
+ if (server->authentication)
+ {
+ if (authStatus < 0)
+ {
+ WLog_ERR(TAG, "client authentication failure: %d", authStatus);
+ return FALSE;
+ }
+ }
return TRUE;
}
-void shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
+BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
{
wMessage message = { 0 };
SHADOW_MSG_IN_REFRESH_OUTPUT* wParam;
wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
- wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT));
+ if (count && !areas)
+ return FALSE;
- if (!wParam)
- return;
+ if (!(wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT))))
+ return FALSE;
wParam->numRects = (UINT32) count;
wParam->rects = (RECTANGLE_16*) calloc(wParam->numRects, sizeof(RECTANGLE_16));
if (!wParam->rects)
- return;
- }
+ {
+ free (wParam);
+ return FALSE;
+ }
- CopyMemory(wParam->rects, areas, wParam->numRects * sizeof(RECTANGLE_16));
+ CopyMemory(wParam->rects, areas, wParam->numRects * sizeof(RECTANGLE_16));
+ }
message.id = SHADOW_MSG_IN_REFRESH_OUTPUT_ID;
message.wParam = (void*) wParam;
message.context = (void*) client;
message.Free = shadow_client_message_free;
- MessageQueue_Dispatch(MsgPipe->In, &message);
+ return MessageQueue_Dispatch(MsgPipe->In, &message);
}
-void shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
+BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
{
wMessage message = { 0 };
SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam;
wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_SUPPRESS_OUTPUT));
-
if (!wParam)
- return;
+ return FALSE;
wParam->allow = (UINT32) allow;
message.context = (void*) client;
message.Free = shadow_client_message_free;
- MessageQueue_Dispatch(MsgPipe->In, &message);
+ return MessageQueue_Dispatch(MsgPipe->In, &message);
}
BOOL shadow_client_activate(freerdp_peer* peer)
{
- rdpShadowClient* client;
+ rdpSettings* settings = peer->settings;
+ rdpShadowClient* client = (rdpShadowClient*) peer->context;
- client = (rdpShadowClient*) peer->context;
+ if (settings->ClientDir && (strcmp(settings->ClientDir, "librdp") == 0))
+ {
+ /* Hack for Mac/iOS/Android Microsoft RDP clients */
+
+ settings->RemoteFxCodec = FALSE;
+
+ settings->NSCodec = FALSE;
+ settings->NSCodecAllowSubsampling = FALSE;
+
+ settings->SurfaceFrameMarkerEnabled = FALSE;
+ }
client->activated = TRUE;
client->inLobby = client->mayView ? FALSE : TRUE;
shadow_encoder_reset(client->encoder);
- shadow_client_refresh_rect(client, 0, NULL);
-
- return TRUE;
+ return shadow_client_refresh_rect(client, 0, NULL);
}
-void shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
+BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
{
SURFACE_FRAME* frame;
wListDictionary* frameList;
ListDictionary_Remove(frameList, (void*) (size_t) frameId);
free(frame);
}
+ return TRUE;
}
int shadow_client_send_surface_frame_marker(rdpShadowClient* client, UINT32 action, UINT32 id)
{
RFX_RECT rect;
RFX_MESSAGE* messages;
+ RFX_RECT *messageRects = NULL;
+
+ shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX);
s = encoder->bs;
rect.width = nWidth;
rect.height = nHeight;
- messages = rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
+ if (!(messages = rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
surface->width, surface->height, nSrcStep, &numMessages,
- settings->MultifragMaxRequestSize);
+ settings->MultifragMaxRequestSize)))
+ {
+ return 0;
+ }
cmd.codecID = settings->RemoteFxCodecId;
cmd.bpp = 32;
cmd.width = surface->width;
cmd.height = surface->height;
+ cmd.skipCompression = TRUE;
+
+ if (numMessages > 0)
+ messageRects = messages[0].rects;
for (i = 0; i < numMessages; i++)
{
Stream_SetPosition(s, 0);
- rfx_write_message(encoder->rfx, s, &messages[i]);
+ if (!rfx_write_message(encoder->rfx, s, &messages[i]))
+ {
+ while (i < numMessages)
+ {
+ rfx_message_free(encoder->rfx, &messages[i++]);
+ }
+ break;
+ }
rfx_message_free(encoder->rfx, &messages[i]);
cmd.bitmapDataLength = Stream_GetPosition(s);
IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
}
+ free(messageRects);
free(messages);
}
else if (settings->NSCodec)
{
- NSC_MESSAGE* messages;
+ shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC);
s = encoder->bs;
+ Stream_SetPosition(s, 0);
- messages = nsc_encode_messages(encoder->nsc, pSrcData,
- nXSrc, nYSrc, nWidth, nHeight, nSrcStep,
- &numMessages, settings->MultifragMaxRequestSize);
+ pSrcData = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
+
+ nsc_compose_message(encoder->nsc, s, pSrcData, nWidth, nHeight, nSrcStep);
cmd.bpp = 32;
cmd.codecID = settings->NSCodecId;
-
- for (i = 0; i < numMessages; i++)
- {
- Stream_SetPosition(s, 0);
-
- nsc_write_message(encoder->nsc, s, &messages[i]);
- nsc_message_free(encoder->nsc, &messages[i]);
-
- cmd.destLeft = messages[i].x;
- cmd.destTop = messages[i].y;
- cmd.destRight = messages[i].x + messages[i].width;
- cmd.destBottom = messages[i].y + messages[i].height;
- cmd.width = messages[i].width;
- cmd.height = messages[i].height;
-
- cmd.bitmapDataLength = Stream_GetPosition(s);
- cmd.bitmapData = Stream_Buffer(s);
-
- first = (i == 0) ? TRUE : FALSE;
- last = ((i + 1) == numMessages) ? TRUE : FALSE;
-
- if (!encoder->frameAck)
- IFCALL(update->SurfaceBits, update->context, &cmd);
- else
- IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
- }
-
- free(messages);
+ cmd.destLeft = nXSrc;
+ cmd.destTop = nYSrc;
+ cmd.destRight = cmd.destLeft + nWidth;
+ cmd.destBottom = cmd.destTop + nHeight;
+ cmd.width = nWidth;
+ cmd.height = nHeight;
+
+ cmd.bitmapDataLength = Stream_GetPosition(s);
+ cmd.bitmapData = Stream_Buffer(s);
+
+ first = TRUE;
+ last = TRUE;
+
+ if (!encoder->frameAck)
+ IFCALL(update->SurfaceBits, update->context, &cmd);
+ else
+ IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
}
return 1;
{
BYTE* data;
BYTE* buffer;
- int i, j, k;
- wStream* s;
- wStream* ts;
- int e, lines;
+ int yIdx, xIdx, k;
int rows, cols;
int nSrcStep;
BYTE* pSrcData;
+ UINT32 DstSize;
+ UINT32 SrcFormat;
+ BITMAP_DATA* bitmap;
rdpUpdate* update;
rdpContext* context;
rdpSettings* settings;
- int MaxRegionWidth;
- int MaxRegionHeight;
+ UINT32 maxUpdateSize;
+ UINT32 totalBitmapSize;
+ UINT32 updateSizeEstimate;
BITMAP_DATA* bitmapData;
BITMAP_UPDATE bitmapUpdate;
rdpShadowServer* server;
server = client->server;
encoder = client->encoder;
+ maxUpdateSize = settings->MultifragMaxRequestSize;
+
+ if (settings->ColorDepth < 32)
+ shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED);
+ else
+ shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR);
+
pSrcData = surface->data;
nSrcStep = surface->scanline;
-
- MaxRegionWidth = 64 * 4;
- MaxRegionHeight = 64 * 1;
+ SrcFormat = PIXEL_FORMAT_RGB32;
if ((nXSrc % 4) != 0)
{
nYSrc -= (nYSrc % 4);
}
- if ((nWidth * nHeight) > (MaxRegionWidth * MaxRegionHeight))
- {
- int nXSrcSub;
- int nYSrcSub;
- int nWidthSub;
- int nHeightSub;
- rows = (nWidth + (MaxRegionWidth - (nWidth % MaxRegionWidth))) / MaxRegionWidth;
- cols = (nHeight + (MaxRegionHeight - (nHeight % MaxRegionHeight))) / MaxRegionHeight;
-
- for (i = 0; i < rows; i++)
- {
- for (j = 0; j < cols; j++)
- {
- nXSrcSub = nXSrc + (i * MaxRegionWidth);
- nYSrcSub = nYSrc + (j * MaxRegionHeight);
-
- nWidthSub = (i < (rows - 1)) ? MaxRegionWidth : nWidth - (i * MaxRegionWidth);
- nHeightSub = (j < (cols - 1)) ? MaxRegionHeight : nHeight - (j * MaxRegionHeight);
-
- if ((nWidthSub * nHeightSub) > 0)
- {
- shadow_client_send_bitmap_update(client, surface, nXSrcSub, nYSrcSub, nWidthSub, nHeightSub);
- }
- }
- }
-
- return 1;
- }
-
- rows = (nWidth + (64 - (nWidth % 64))) / 64;
- cols = (nHeight + (64 - (nHeight % 64))) / 64;
+ rows = (nHeight / 64) + ((nHeight % 64) ? 1 : 0);
+ cols = (nWidth / 64) + ((nWidth % 64) ? 1 : 0);
k = 0;
+ totalBitmapSize = 0;
+
bitmapUpdate.count = bitmapUpdate.number = rows * cols;
bitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * bitmapUpdate.number);
bitmapUpdate.rectangles = bitmapData;
nHeight += (nHeight % 4);
}
- for (i = 0; i < rows; i++)
+ for (yIdx = 0; yIdx < rows; yIdx++)
{
- for (j = 0; j < cols; j++)
+ for (xIdx = 0; xIdx < cols; xIdx++)
{
- nWidth = (i < (rows - 1)) ? 64 : nWidth - (i * 64);
- nHeight = (j < (cols - 1)) ? 64 : nHeight - (j * 64);
-
- bitmapData[k].bitsPerPixel = 16;
- bitmapData[k].width = nWidth;
- bitmapData[k].height = nHeight;
- bitmapData[k].destLeft = nXSrc + (i * 64);
- bitmapData[k].destTop = nYSrc + (j * 64);
- bitmapData[k].destRight = bitmapData[k].destLeft + nWidth - 1;
- bitmapData[k].destBottom = bitmapData[k].destTop + nHeight - 1;
- bitmapData[k].compressed = TRUE;
-
- if (((nWidth * nHeight) > 0) && (nWidth >= 4) && (nHeight >= 4))
- {
- UINT32 srcFormat = PIXEL_FORMAT_RGB32;
+ bitmap = &bitmapData[k];
- e = nWidth % 4;
+ bitmap->width = 64;
+ bitmap->height = 64;
+ bitmap->destLeft = nXSrc + (xIdx * 64);
+ bitmap->destTop = nYSrc + (yIdx * 64);
- if (e != 0)
- e = 4 - e;
+ if ((bitmap->destLeft + bitmap->width) > (nXSrc + nWidth))
+ bitmap->width = (nXSrc + nWidth) - bitmap->destLeft;
- s = encoder->bs;
- ts = encoder->bts;
+ if ((bitmap->destTop + bitmap->height) > (nYSrc + nHeight))
+ bitmap->height = (nYSrc + nHeight) - bitmap->destTop;
- Stream_SetPosition(s, 0);
- Stream_SetPosition(ts, 0);
+ bitmap->destRight = bitmap->destLeft + bitmap->width - 1;
+ bitmap->destBottom = bitmap->destTop + bitmap->height - 1;
+ bitmap->compressed = TRUE;
- data = surface->data;
- data = &data[(bitmapData[k].destTop * nSrcStep) +
- (bitmapData[k].destLeft * 4)];
+ if ((bitmap->width < 4) || (bitmap->height < 4))
+ continue;
- srcFormat = PIXEL_FORMAT_RGB32;
+ if (settings->ColorDepth < 32)
+ {
+ int bitsPerPixel = settings->ColorDepth;
+ int bytesPerPixel = (bitsPerPixel + 7) / 8;
- if (settings->ColorDepth > 24)
- {
- int dstSize;
+ DstSize = 64 * 64 * 4;
+ buffer = encoder->grid[k];
- buffer = encoder->grid[k];
+ interleaved_compress(encoder->interleaved, buffer, &DstSize, bitmap->width, bitmap->height,
+ pSrcData, SrcFormat, nSrcStep, bitmap->destLeft, bitmap->destTop, NULL, bitsPerPixel);
- buffer = freerdp_bitmap_compress_planar(encoder->planar,
- data, srcFormat, nWidth, nHeight, nSrcStep, buffer, &dstSize);
+ bitmap->bitmapDataStream = buffer;
+ bitmap->bitmapLength = DstSize;
+ bitmap->bitsPerPixel = bitsPerPixel;
+ bitmap->cbScanWidth = bitmap->width * bytesPerPixel;
+ bitmap->cbUncompressedSize = bitmap->width * bitmap->height * bytesPerPixel;
+ }
+ else
+ {
+ int dstSize;
- bitmapData[k].bitmapDataStream = buffer;
- bitmapData[k].bitmapLength = dstSize;
+ buffer = encoder->grid[k];
+ data = &pSrcData[(bitmap->destTop * nSrcStep) + (bitmap->destLeft * 4)];
- bitmapData[k].bitsPerPixel = 32;
- bitmapData[k].cbScanWidth = nWidth * 4;
- bitmapData[k].cbUncompressedSize = nWidth * nHeight * 4;
- }
- else
- {
- int bytesPerPixel = 2;
- UINT32 dstFormat = PIXEL_FORMAT_RGB16;
+ buffer = freerdp_bitmap_compress_planar(encoder->planar, data, SrcFormat,
+ bitmap->width, bitmap->height, nSrcStep, buffer, &dstSize);
- if (settings->ColorDepth == 15)
- {
- bytesPerPixel = 2;
- dstFormat = PIXEL_FORMAT_RGB15;
- }
- else if (settings->ColorDepth == 24)
- {
- bytesPerPixel = 3;
- dstFormat = PIXEL_FORMAT_XRGB32;
- }
+ bitmap->bitmapDataStream = buffer;
+ bitmap->bitmapLength = dstSize;
+ bitmap->bitsPerPixel = 32;
+ bitmap->cbScanWidth = bitmap->width * 4;
+ bitmap->cbUncompressedSize = bitmap->width * bitmap->height * 4;
+ }
- buffer = encoder->grid[k];
+ bitmap->cbCompFirstRowSize = 0;
+ bitmap->cbCompMainBodySize = bitmap->bitmapLength;
- freerdp_image_copy(buffer, dstFormat, -1, 0, 0, nWidth, nHeight,
- data, srcFormat, nSrcStep, 0, 0, NULL);
+ totalBitmapSize += bitmap->bitmapLength;
+ k++;
+ }
+ }
- lines = freerdp_bitmap_compress((char*) buffer, nWidth, nHeight, s,
- settings->ColorDepth, 64 * 64 * 4, nHeight - 1, ts, e);
+ bitmapUpdate.count = bitmapUpdate.number = k;
- Stream_SealLength(s);
+ updateSizeEstimate = totalBitmapSize + (k * bitmapUpdate.count) + 16;
- bitmapData[k].bitmapDataStream = Stream_Buffer(s);
- bitmapData[k].bitmapLength = Stream_Length(s);
+ if (updateSizeEstimate > maxUpdateSize)
+ {
+ UINT32 i, j;
+ UINT32 updateSize;
+ UINT32 newUpdateSize;
+ BITMAP_DATA* fragBitmapData;
- buffer = encoder->grid[k];
- CopyMemory(buffer, bitmapData[k].bitmapDataStream, bitmapData[k].bitmapLength);
- bitmapData[k].bitmapDataStream = buffer;
+ fragBitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * k);
+ bitmapUpdate.rectangles = fragBitmapData;
- bitmapData[k].bitsPerPixel = settings->ColorDepth;
- bitmapData[k].cbScanWidth = nWidth * bytesPerPixel;
- bitmapData[k].cbUncompressedSize = nWidth * nHeight * bytesPerPixel;
- }
+ i = j = 0;
+ updateSize = 1024;
- bitmapData[k].cbCompFirstRowSize = 0;
- bitmapData[k].cbCompMainBodySize = bitmapData[k].bitmapLength;
+ while (i < k)
+ {
+ newUpdateSize = updateSize + (bitmapData[i].bitmapLength + 16);
- k++;
+ if ((newUpdateSize < maxUpdateSize) && ((i + 1) < k))
+ {
+ CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA));
+ updateSize = newUpdateSize;
+ }
+ else
+ {
+ if ((i + 1) >= k)
+ {
+ CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA));
+ updateSize = newUpdateSize;
+ }
+
+ bitmapUpdate.count = bitmapUpdate.number = j;
+ IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+ updateSize = 1024;
+ j = 0;
}
}
- }
- bitmapUpdate.count = bitmapUpdate.number = k;
-
- IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+ free(fragBitmapData);
+ }
+ else
+ {
+ IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+ }
free(bitmapData);
if (settings->RemoteFxCodec || settings->NSCodec)
{
- if (settings->RemoteFxCodec)
- shadow_encoder_prepare(encoder, SHADOW_CODEC_REMOTEFX);
- else if (settings->NSCodec)
- shadow_encoder_prepare(encoder, SHADOW_CODEC_NSCODEC);
-
status = shadow_client_send_surface_bits(client, surface, nXSrc, nYSrc, nWidth, nHeight);
}
else
{
- shadow_encoder_prepare(encoder, SHADOW_CODEC_BITMAP);
-
status = shadow_client_send_bitmap_update(client, surface, nXSrc, nYSrc, nWidth, nHeight);
}
return 1;
}
+int shadow_client_convert_alpha_pointer_data(BYTE* pixels, BOOL premultiplied,
+ UINT32 width, UINT32 height, POINTER_COLOR_UPDATE* pointerColor)
+{
+ UINT32 x, y;
+ BYTE* pSrc8;
+ BYTE* pDst8;
+ int xorStep;
+ int andStep;
+ UINT32 andBit;
+ BYTE* andBits;
+ UINT32 andPixel;
+ BYTE A, R, G, B;
+
+ xorStep = (width * 3);
+ xorStep += (xorStep % 2);
+
+ andStep = ((width + 7) / 8);
+ andStep += (andStep % 2);
+
+ pointerColor->lengthXorMask = height * xorStep;
+ pointerColor->xorMaskData = (BYTE*) calloc(1, pointerColor->lengthXorMask);
+
+ if (!pointerColor->xorMaskData)
+ return -1;
+
+ pointerColor->lengthAndMask = height * andStep;
+ pointerColor->andMaskData = (BYTE*) calloc(1, pointerColor->lengthAndMask);
+
+ if (!pointerColor->andMaskData)
+ return -1;
+
+ for (y = 0; y < height; y++)
+ {
+ pSrc8 = &pixels[(width * 4) * (height - 1 - y)];
+ pDst8 = &(pointerColor->xorMaskData[y * xorStep]);
+
+ andBit = 0x80;
+ andBits = &(pointerColor->andMaskData[andStep * y]);
+
+ for (x = 0; x < width; x++)
+ {
+ B = *pSrc8++;
+ G = *pSrc8++;
+ R = *pSrc8++;
+ A = *pSrc8++;
+
+ andPixel = 0;
+
+ if (A < 64)
+ A = 0; /* pixel cannot be partially transparent */
+
+ if (!A)
+ {
+ /* transparent pixel: XOR = black, AND = 1 */
+ andPixel = 1;
+ B = G = R = 0;
+ }
+ else
+ {
+ if (premultiplied)
+ {
+ B = (B * 0xFF ) / A;
+ G = (G * 0xFF ) / A;
+ R = (R * 0xFF ) / A;
+ }
+ }
+
+ *pDst8++ = B;
+ *pDst8++ = G;
+ *pDst8++ = R;
+
+ if (andPixel) *andBits |= andBit;
+ if (!(andBit >>= 1)) { andBits++; andBit = 0x80; }
+ }
+ }
+
+ return 1;
+}
+
+int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
+{
+ rdpContext* context = (rdpContext*) client;
+ rdpUpdate* update = context->update;
+
+ /* FIXME: the pointer updates appear to be broken when used with bulk compression and mstsc */
+
+ switch(message->id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ {
+ POINTER_POSITION_UPDATE pointerPosition;
+ SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_POSITION_UPDATE*) message->wParam;
+
+ pointerPosition.xPos = msg->xPos;
+ pointerPosition.yPos = msg->yPos;
+
+ if (client->activated)
+ {
+ if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY))
+ {
+ IFCALL(update->pointer->PointerPosition, context, &pointerPosition);
+
+ client->pointerX = msg->xPos;
+ client->pointerY = msg->yPos;
+ }
+ }
+ break;
+ }
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ {
+ POINTER_NEW_UPDATE pointerNew;
+ POINTER_COLOR_UPDATE* pointerColor;
+ POINTER_CACHED_UPDATE pointerCached;
+ SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*) message->wParam;
+
+ ZeroMemory(&pointerNew, sizeof(POINTER_NEW_UPDATE));
+
+ pointerNew.xorBpp = 24;
+ pointerColor = &(pointerNew.colorPtrAttr);
+
+ pointerColor->cacheIndex = 0;
+ pointerColor->xPos = msg->xHot;
+ pointerColor->yPos = msg->yHot;
+ pointerColor->width = msg->width;
+ pointerColor->height = msg->height;
+
+ pointerCached.cacheIndex = pointerColor->cacheIndex;
+
+ if (client->activated)
+ {
+ shadow_client_convert_alpha_pointer_data(msg->pixels, msg->premultiplied,
+ msg->width, msg->height, pointerColor);
+
+ IFCALL(update->pointer->PointerNew, context, &pointerNew);
+ IFCALL(update->pointer->PointerCached, context, &pointerCached);
+
+ free(pointerColor->xorMaskData);
+ free(pointerColor->andMaskData);
+ }
+ break;
+ }
+ case SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID:
+ {
+ SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES* msg = (SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES*) message->wParam;
+ if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+ {
+ client->rdpsnd->src_format = msg->audio_format;
+ IFCALL(client->rdpsnd->SendSamples, client->rdpsnd, msg->buf, msg->nFrames, msg->wTimestamp);
+ }
+ break;
+ }
+ case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+ {
+ SHADOW_MSG_OUT_AUDIO_OUT_VOLUME* msg = (SHADOW_MSG_OUT_AUDIO_OUT_VOLUME*) message->wParam;
+ if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+ {
+ IFCALL(client->rdpsnd->SetVolume, client->rdpsnd, msg->left, msg->right);
+ }
+ break;
+ }
+ default:
+ WLog_ERR(TAG, "Unknown message id: %u", message->id);
+ break;
+ }
+
+ shadow_client_free_queued_message(message);
+
+ return 1;
+}
+
void* shadow_client_thread(rdpShadowClient* client)
{
DWORD status;
DWORD nCount;
+ wMessage message;
+ wMessage pointerPositionMsg;
+ wMessage pointerAlphaMsg;
+ wMessage audioVolumeMsg;
HANDLE events[32];
- HANDLE StopEvent;
HANDLE ClientEvent;
HANDLE ChannelEvent;
+ void* UpdateSubscriber;
HANDLE UpdateEvent;
freerdp_peer* peer;
rdpContext* context;
rdpShadowScreen* screen;
rdpShadowEncoder* encoder;
rdpShadowSubsystem* subsystem;
+ wMessageQueue* MsgQueue = client->MsgQueue;
server = client->server;
screen = server->screen;
peer->Initialize(peer);
- peer->update->RefreshRect = (pRefreshRect) shadow_client_refresh_rect;
- peer->update->SuppressOutput = (pSuppressOutput) shadow_client_suppress_output;
- peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge) shadow_client_surface_frame_acknowledge;
+ peer->update->RefreshRect = (pRefreshRect)shadow_client_refresh_rect;
+ peer->update->SuppressOutput = (pSuppressOutput)shadow_client_suppress_output;
+ peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge)shadow_client_surface_frame_acknowledge;
- StopEvent = client->StopEvent;
- UpdateEvent = subsystem->updateEvent;
+ if ((!client->vcm) || (!subsystem->updateEvent))
+ goto out;
+
+ UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent);
+ if (!UpdateSubscriber)
+ goto out;
+
+ UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber);
ClientEvent = peer->GetEventHandle(peer);
ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm);
while (1)
{
nCount = 0;
- events[nCount++] = StopEvent;
events[nCount++] = UpdateEvent;
events[nCount++] = ClientEvent;
events[nCount++] = ChannelEvent;
+ events[nCount++] = MessageQueue_Event(MsgQueue);
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
- if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
- {
- if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
- {
- EnterSynchronizationBarrier(&(subsystem->barrier), 0);
- }
-
- break;
- }
-
if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
{
if (client->activated)
shadow_client_send_surface_update(client);
}
- EnterSynchronizationBarrier(&(subsystem->barrier), 0);
-
- while (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0);
+ /*
+ * The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event.
+ * It's not cared currently.
+ */
+ (void)shadow_multiclient_consume(UpdateSubscriber);
}
if (WaitForSingleObject(ClientEvent, 0) == WAIT_OBJECT_0)
if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
{
- if (WTSVirtualChannelManagerCheckFileDescriptor(client->vcm) != TRUE)
+ if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
{
WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure");
break;
}
}
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0)
+ {
+ /* Drain messages. Pointer update could be accumulated. */
+ pointerPositionMsg.id = 0;
+ pointerPositionMsg.Free= NULL;
+ pointerAlphaMsg.id = 0;
+ pointerAlphaMsg.Free = NULL;
+ audioVolumeMsg.id = 0;
+ audioVolumeMsg.Free = NULL;
+ while (MessageQueue_Peek(MsgQueue, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ {
+ break;
+ }
+
+ switch(message.id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&pointerPositionMsg);
+ CopyMemory(&pointerPositionMsg, &message, sizeof(wMessage));
+ break;
+
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&pointerAlphaMsg);
+ CopyMemory(&pointerAlphaMsg, &message, sizeof(wMessage));
+ break;
+
+ case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&audioVolumeMsg);
+ CopyMemory(&audioVolumeMsg, &message, sizeof(wMessage));
+ break;
+
+ default:
+ shadow_client_subsystem_process_message(client, &message);
+ break;
+ }
+ }
+
+ if (message.id == WMQ_QUIT)
+ {
+ /* Release stored message */
+ shadow_client_free_queued_message(&pointerPositionMsg);
+ shadow_client_free_queued_message(&pointerAlphaMsg);
+ shadow_client_free_queued_message(&audioVolumeMsg);
+ break;
+ }
+ else
+ {
+ /* Process accumulated messages if needed */
+ if (pointerPositionMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &pointerPositionMsg);
+ }
+ if (pointerAlphaMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &pointerAlphaMsg);
+ }
+ if (audioVolumeMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &audioVolumeMsg);
+ }
+ }
+ }
+ }
+
+ /* Free channels early because we establish channels in post connect */
+ shadow_client_channels_free(client);
+
+ if (UpdateSubscriber)
+ {
+ shadow_multiclient_release_subscriber(UpdateSubscriber);
+ UpdateSubscriber = NULL;
}
+out:
peer->Disconnect(peer);
freerdp_peer_context_free(peer);
freerdp_peer_free(peer);
-
ExitThread(0);
-
return NULL;
}
-void shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
+BOOL shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
{
rdpShadowClient* client;
rdpShadowServer* server;
peer->ContextSize = sizeof(rdpShadowClient);
peer->ContextNew = (psPeerContextNew) shadow_client_context_new;
peer->ContextFree = (psPeerContextFree) shadow_client_context_free;
- freerdp_peer_context_new(peer);
+
+ if (!freerdp_peer_context_new(peer))
+ return FALSE;
client = (rdpShadowClient*) peer->context;
- client->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
- shadow_client_thread, client, 0, NULL);
+ if (!(client->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
+ shadow_client_thread, client, 0, NULL)))
+ {
+ freerdp_peer_context_free(peer);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void shadow_msg_out_addref(wMessage* message)
+{
+ SHADOW_MSG_OUT* msg = (SHADOW_MSG_OUT *)message->wParam;
+ InterlockedIncrement(&(msg->refCount));
+}
+
+static void shadow_msg_out_release(wMessage* message)
+{
+ SHADOW_MSG_OUT* msg = (SHADOW_MSG_OUT *)message->wParam;
+ if (InterlockedDecrement(&(msg->refCount)) <= 0)
+ {
+ if (msg->Free)
+ msg->Free(message->id, msg);
+ }
+}
+
+static BOOL shadow_client_dispatch_msg(rdpShadowClient* client, wMessage* message)
+{
+ /* Add reference when it is posted */
+ shadow_msg_out_addref(message);
+ if (MessageQueue_Dispatch(client->MsgQueue, message))
+ {
+ return TRUE;
+ }
+ else
+ {
+ /* Release the reference since post failed */
+ shadow_msg_out_release(message);
+ return FALSE;
+ }
+}
+
+BOOL shadow_client_post_msg(rdpShadowClient* client, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam)
+{
+ wMessage message = {0};
+
+ message.context = context;
+ message.id = type;
+ message.wParam = (void *)msg;
+ message.lParam = lParam;
+ message.Free = shadow_msg_out_release;
+
+ return shadow_client_dispatch_msg(client, &message);
+}
+
+int shadow_client_boardcast_msg(rdpShadowServer* server, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam)
+{
+ wMessage message = {0};
+ rdpShadowClient* client = NULL;
+ int count = 0;
+ int index = 0;
+
+ message.context = context;
+ message.id = type;
+ message.wParam = (void *)msg;
+ message.lParam = lParam;
+ message.Free = shadow_msg_out_release;
+
+ /* First add reference as we reference it in this function.
+ * Therefore it would not be free'ed during post. */
+ shadow_msg_out_addref(&message);
+
+ ArrayList_Lock(server->clients);
+ for (index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
+ if (shadow_client_dispatch_msg(client, &message))
+ {
+ count++;
+ }
+ }
+ ArrayList_Unlock(server->clients);
+
+ /* Release the reference for this function */
+ shadow_msg_out_release(&message);
+
+ return count;
+}
+
+int shadow_client_boardcast_quit(rdpShadowServer* server, int nExitCode)
+{
+ wMessageQueue* queue = NULL;
+ int count = 0;
+ int index = 0;
+
+ ArrayList_Lock(server->clients);
+ for (index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ queue = ((rdpShadowClient*)ArrayList_GetItem(server->clients, index))->MsgQueue;
+ if (MessageQueue_PostQuit(queue, nExitCode))
+ {
+ count++;
+ }
+ }
+ ArrayList_Unlock(server->clients);
+
+ return count;
}