FreeRDP/server/shadow/shadow_client.c

1621 lines
41 KiB
C
Raw Normal View History

2014-07-11 01:20:41 +04:00
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/path.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
2014-07-14 03:42:57 +04:00
#include <winpr/sysinfo.h>
#include <winpr/interlocked.h>
2014-07-11 01:20:41 +04:00
2014-09-12 19:38:12 +04:00
#include <freerdp/log.h>
#include "libfreerdp/core/server.h"
2014-07-11 01:20:41 +04:00
#include "shadow.h"
2014-09-12 19:38:12 +04:00
#define TAG CLIENT_TAG("shadow")
struct _SHADOW_GFX_STATUS
{
BOOL gfxOpened;
BOOL gfxSurfaceCreated;
};
typedef struct _SHADOW_GFX_STATUS SHADOW_GFX_STATUS;
static void shadow_client_rdpgfx_new_surface(rdpShadowClient *client)
{
RDPGFX_CREATE_SURFACE_PDU createSurface;
RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU surfaceToOutput;
RdpgfxServerContext* context = client->rdpgfx;
rdpSettings* settings = ((rdpContext*) client)->settings;
createSurface.width = settings->DesktopWidth;
createSurface.height = settings->DesktopHeight;
createSurface.pixelFormat = PIXEL_FORMAT_XRGB_8888;
createSurface.surfaceId = 0;
surfaceToOutput.outputOriginX = 0;
surfaceToOutput.outputOriginY = 0;
surfaceToOutput.surfaceId = 0;
surfaceToOutput.reserved = 0;
IFCALL(context->CreateSurface, context, &createSurface);
IFCALL(context->MapSurfaceToOutput, context, &surfaceToOutput);
}
static void shadow_client_rdpgfx_release_surface(rdpShadowClient *client)
{
RDPGFX_DELETE_SURFACE_PDU pdu;
RdpgfxServerContext* context = client->rdpgfx;
pdu.surfaceId = 0;
IFCALL(context->DeleteSurface, context, &pdu);
}
static void shadow_client_rdpgfx_reset_graphic(rdpShadowClient *client)
{
RDPGFX_RESET_GRAPHICS_PDU pdu;
RdpgfxServerContext* context = client->rdpgfx;
rdpSettings* settings = ((rdpContext*) client)->settings;
pdu.width = settings->DesktopWidth;
pdu.height = settings->DesktopHeight;
pdu.monitorCount = client->subsystem->numMonitors;
pdu.monitorDefArray = client->subsystem->monitors;
IFCALL(context->ResetGraphics, context, &pdu);
}
static void shadow_client_free_queued_message(void *obj)
{
wMessage *message = (wMessage*)obj;
if (message->Free)
{
message->Free(message);
message->Free = NULL;
}
}
static BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
2014-07-11 01:20:41 +04:00
{
2014-07-14 03:42:57 +04:00
rdpSettings* settings;
rdpShadowServer* server;
const wObject cb = { NULL, NULL, NULL, shadow_client_free_queued_message, NULL };
2014-07-11 01:20:41 +04:00
server = (rdpShadowServer*) peer->ContextExtra;
client->server = server;
2014-09-19 01:22:44 +04:00
client->subsystem = server->subsystem;
2014-07-12 09:18:08 +04:00
2014-07-14 03:42:57 +04:00
settings = peer->settings;
2014-07-14 03:42:57 +04:00
settings->ColorDepth = 32;
2014-09-24 20:10:02 +04:00
settings->NSCodec = TRUE;
2014-07-14 03:42:57 +04:00
settings->RemoteFxCodec = TRUE;
settings->BitmapCacheV3Enabled = TRUE;
settings->FrameMarkerCommandEnabled = TRUE;
settings->SurfaceFrameMarkerEnabled = TRUE;
settings->SupportGraphicsPipeline = TRUE;
settings->GfxH264 = FALSE;
2014-07-14 03:42:57 +04:00
settings->DrawAllowSkipAlpha = TRUE;
settings->DrawAllowColorSubsampling = TRUE;
settings->DrawAllowDynamicColorFidelity = TRUE;
2014-11-07 21:51:10 +03:00
settings->CompressionLevel = PACKET_COMPR_TYPE_RDP6;
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;
2014-08-15 21:50:22 +04:00
if (server->ipcSocket)
{
settings->LyncRdpMode = TRUE;
settings->CompressionEnabled = FALSE;
}
2014-07-15 02:01:29 +04:00
client->inLobby = TRUE;
client->mayView = server->mayView;
client->mayInteract = server->mayInteract;
if (!InitializeCriticalSectionAndSpinCount(&(client->lock), 4000))
goto fail_client_lock;
region16_init(&(client->invalidRegion));
2014-07-15 02:01:29 +04:00
client->vcm = WTSOpenServerA((LPSTR) peer->context);
if (!client->vcm || client->vcm == INVALID_HANDLE_VALUE)
goto fail_open_server;
2014-07-15 02:01:29 +04:00
if (!(client->MsgQueue = MessageQueue_New(&cb)))
goto fail_message_queue;
if (!(client->encoder = shadow_encoder_new(client)))
goto fail_encoder_new;
2015-05-20 20:19:50 +03:00
if (ArrayList_Add(server->clients, (void*) client) >= 0)
return TRUE;
2015-05-20 20:19:50 +03:00
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;
2014-07-11 01:20:41 +04:00
}
static void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
2014-07-11 01:20:41 +04:00
{
rdpShadowServer* server = client->server;
ArrayList_Remove(server->clients, (void*) client);
if (client->encoder)
{
shadow_encoder_free(client->encoder);
client->encoder = NULL;
}
/* Clear queued messages and free resource */
MessageQueue_Clear(client->MsgQueue);
MessageQueue_Free(client->MsgQueue);
WTSCloseServer((HANDLE) client->vcm);
client->vcm = NULL;
region16_uninit(&(client->invalidRegion));
DeleteCriticalSection(&(client->lock));
2014-07-11 01:20:41 +04:00
}
void shadow_client_message_free(wMessage* message)
{
switch(message->id)
{
case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
/* Refresh request do not have message to free */
break;
default:
WLog_ERR(TAG, "Unknown message id: %u", message->id);
free(message->wParam);
break;
}
}
static void shadow_client_mark_invalid(rdpShadowClient* client, int numRects, const RECTANGLE_16* rects)
2014-07-11 01:20:41 +04:00
{
int index;
RECTANGLE_16 screenRegion;
rdpSettings* settings = ((rdpContext*) client)->settings;
EnterCriticalSection(&(client->lock));
/* Mark client invalid region. No rectangle means full screen */
if (numRects > 0)
{
for (index = 0; index < numRects; index++)
{
region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]);
}
}
else
{
screenRegion.left = 0;
screenRegion.top = 0;
screenRegion.right = settings->DesktopWidth;
screenRegion.bottom = settings->DesktopHeight;
region16_union_rect(&(client->invalidRegion),
&(client->invalidRegion), &screenRegion);
}
LeaveCriticalSection(&(client->lock));
2014-07-11 01:20:41 +04:00
}
/**
* Function description
* Recalculate client desktop size
*
* @return TRUE if width/height changed.
*/
static BOOL shadow_client_recalc_desktop_size(rdpShadowClient* client)
{
int width, height;
rdpShadowServer* server = client->server;
rdpSettings* settings = client->context.settings;
RECTANGLE_16 viewport = {0, 0, server->surface->width, server->surface->height};
if (server->shareSubRect)
{
rectangles_intersection(&viewport, &(server->subRect), &viewport);
}
width = viewport.right - viewport.left;
height = viewport.bottom - viewport.top;
if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height)
{
settings->DesktopWidth = width;
settings->DesktopHeight = height;
return TRUE;
}
return FALSE;
}
static BOOL shadow_client_capabilities(freerdp_peer* peer)
{
rdpShadowSubsystem* subsystem;
rdpShadowClient* client;
client = (rdpShadowClient*) peer->context;
subsystem = client->server->subsystem;
IFCALL(subsystem->ClientCapabilities, subsystem, client);
/* Make sure we send correct width/height to client */
(void)shadow_client_recalc_desktop_size(client);
return TRUE;
}
static BOOL shadow_client_post_connect(freerdp_peer* peer)
2014-07-11 01:20:41 +04:00
{
2014-09-27 03:03:48 +04:00
int authStatus;
rdpSettings* settings;
rdpShadowClient* client;
rdpShadowServer* server;
2014-09-27 03:03:48 +04:00
rdpShadowSubsystem* subsystem;
client = (rdpShadowClient*) peer->context;
settings = peer->settings;
server = client->server;
2014-09-27 03:03:48 +04:00
subsystem = server->subsystem;
2014-07-16 23:12:20 +04:00
if (settings->ColorDepth == 24)
settings->ColorDepth = 16; /* disable 24bpp */
if (settings->MultifragMaxRequestSize < 0x3F0000)
settings->NSCodec = FALSE; /* NSCodec compressor does not support fragmentation yet */
WLog_INFO(TAG, "Client from %s is activated (%dx%d@%d)",
peer->hostname, settings->DesktopWidth,
settings->DesktopHeight, settings->ColorDepth);
2014-07-11 01:20:41 +04:00
/* Resize client if necessary */
if (shadow_client_recalc_desktop_size(client))
{
peer->update->DesktopResize(peer->update->context);
WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)",
peer->hostname, settings->DesktopWidth,
settings->DesktopHeight, settings->ColorDepth);
}
2014-07-11 01:20:41 +04:00
if (shadow_client_channels_post_connect(client) != CHANNEL_RC_OK)
return FALSE;
2014-07-15 02:01:29 +04:00
shadow_client_mark_invalid(client, 0, NULL);
2014-09-27 03:03:48 +04:00
authStatus = -1;
if (settings->Username && settings->Password)
settings->AutoLogonEnabled = TRUE;
if (server->authentication && !settings->NlaSecurity)
2014-09-27 03:03:48 +04:00
{
if (subsystem->Authenticate)
{
authStatus = subsystem->Authenticate(subsystem, client,
2014-09-27 03:03:48 +04:00
settings->Username, settings->Domain, settings->Password);
}
if (authStatus < 0)
{
WLog_ERR(TAG, "client authentication failure: %d", authStatus);
return FALSE;
}
}
if (subsystem->ClientConnect)
{
return subsystem->ClientConnect(subsystem, client);
}
2014-07-11 01:20:41 +04:00
return TRUE;
}
/* Convert rects in sub rect coordinate to client/surface coordinate */
static INLINE void shadow_client_convert_rects(rdpShadowClient* client,
RECTANGLE_16* dst, const RECTANGLE_16* src, UINT32 numRects)
{
if (client->server->shareSubRect)
{
int i = 0;
UINT16 offsetX = client->server->subRect.left;
UINT16 offsetY = client->server->subRect.top;
for (i = 0; i < numRects; i++)
{
dst[i].left = src[i].left + offsetX;
dst[i].right = src[i].right + offsetX;
dst[i].top = src[i].top + offsetY;
dst[i].bottom = src[i].bottom + offsetY;
}
}
else
{
if (src != dst)
{
CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16));
}
}
}
static BOOL shadow_client_refresh_request(rdpShadowClient* client)
2014-09-19 01:22:44 +04:00
{
wMessage message = { 0 };
2014-09-19 01:22:44 +04:00
wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
message.id = SHADOW_MSG_IN_REFRESH_REQUEST_ID;
message.wParam = NULL;
message.lParam = NULL;
message.context = (void*) client;
message.Free = NULL;
return MessageQueue_Dispatch(MsgPipe->In, &message);
}
static BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
{
RECTANGLE_16* rects;
/* It is invalid if we have area count but no actual area */
if (count && !areas)
return FALSE;
2014-09-19 01:22:44 +04:00
if (count)
{
rects = (RECTANGLE_16*) calloc(count, sizeof(RECTANGLE_16));
if (!rects)
2014-11-17 02:21:04 +03:00
{
2015-05-23 23:47:18 +03:00
return FALSE;
2014-11-17 02:21:04 +03:00
}
shadow_client_convert_rects(client, rects, areas, count);
shadow_client_mark_invalid(client, count, rects);
free(rects);
}
else
{
shadow_client_mark_invalid(client, 0, NULL);
}
return shadow_client_refresh_request(client);
2014-09-19 01:22:44 +04:00
}
static BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
2014-09-19 01:22:44 +04:00
{
RECTANGLE_16 region;
client->suppressOutput = allow ? FALSE : TRUE;
if (allow)
{
if (area)
{
shadow_client_convert_rects(client, &region, area, 1);
shadow_client_mark_invalid(client, 1, &region);
}
else
{
shadow_client_mark_invalid(client, 0, NULL);
}
}
return shadow_client_refresh_request(client);
2014-09-19 01:22:44 +04:00
}
static BOOL shadow_client_activate(freerdp_peer* peer)
2014-07-11 01:20:41 +04:00
{
rdpSettings* settings = peer->settings;
rdpShadowClient* client = (rdpShadowClient*) peer->context;
2014-10-30 02:11:22 +03:00
if (settings->ClientDir && (strcmp(settings->ClientDir, "librdp") == 0))
{
/* Hack for Mac/iOS/Android Microsoft RDP clients */
2014-09-24 20:10:02 +04:00
settings->RemoteFxCodec = FALSE;
2014-09-24 20:10:02 +04:00
settings->NSCodec = FALSE;
settings->NSCodecAllowSubsampling = FALSE;
2014-09-24 20:10:02 +04:00
settings->SurfaceFrameMarkerEnabled = FALSE;
}
client->activated = TRUE;
2014-07-15 02:01:29 +04:00
client->inLobby = client->mayView ? FALSE : TRUE;
shadow_encoder_reset(client->encoder);
2014-07-15 20:50:47 +04:00
/* Update full screen in next update */
2015-05-23 23:47:18 +03:00
return shadow_client_refresh_rect(client, 0, NULL);
2014-07-11 01:20:41 +04:00
}
BOOL shadow_client_logon(freerdp_peer* peer, SEC_WINNT_AUTH_IDENTITY* identity, BOOL automatic)
{
char* user = NULL;
char* domain = NULL;
char* password = NULL;
rdpSettings* settings = peer->settings;
if (identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE)
{
if (identity->User)
ConvertFromUnicode(CP_UTF8, 0, identity->User, identity->UserLength, &user, 0, NULL, NULL);
if (identity->Domain)
ConvertFromUnicode(CP_UTF8, 0, identity->Domain, identity->DomainLength, &domain, 0, NULL, NULL);
if (identity->Password)
ConvertFromUnicode(CP_UTF8, 0, identity->Password, identity->PasswordLength, &user, 0, NULL, NULL);
}
else
{
if (identity->User)
user = _strdup((char*) identity->User);
if (identity->Domain)
domain = _strdup((char*) identity->Domain);
if (identity->Password)
password = _strdup((char*) identity->Password);
}
if ((identity->User && !user) || (identity->Domain && !domain) || (identity->Password && !password))
return FALSE;
if (user)
{
free(settings->Username);
settings->Username = user;
user = NULL;
}
if (domain)
{
free(settings->Domain);
settings->Domain = domain;
domain = NULL;
}
if (password)
{
free(settings->Password);
settings->Password = password;
password = NULL;
}
free(user);
free(domain);
free(password);
return TRUE;
}
static BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
2014-07-14 03:42:57 +04:00
{
/*
* Record the last client acknowledged frame id to
* calculate how much frames are in progress.
* Some rdp clients (win7 mstsc) skips frame ACK if it is
* inactive, we should not expect ACK for each frame.
* So it is OK to calculate inflight frame count according to
* a latest acknowledged frame id.
*/
client->encoder->lastAckframeId = frameId;
2014-07-14 03:42:57 +04:00
2015-05-23 23:47:18 +03:00
return TRUE;
2014-07-14 03:42:57 +04:00
}
static UINT shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge)
2014-07-14 03:42:57 +04:00
{
shadow_client_surface_frame_acknowledge((rdpShadowClient *)context->custom,
frameAcknowledge->frameId);
return CHANNEL_RC_OK;
}
2014-07-14 03:42:57 +04:00
static int shadow_client_send_surface_gfx(rdpShadowClient* client,
BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
rdpUpdate* update;
rdpContext* context;
rdpSettings* settings;
rdpShadowServer* server;
rdpShadowEncoder* encoder;
RDPGFX_SURFACE_COMMAND cmd;
RDPGFX_START_FRAME_PDU cmdstart;
RDPGFX_END_FRAME_PDU cmdend;
SYSTEMTIME sTime;
context = (rdpContext*) client;
update = context->update;
settings = context->settings;
server = client->server;
encoder = client->encoder;
2014-07-14 03:42:57 +04:00
cmdstart.frameId = shadow_encoder_create_frame_id(encoder);
GetSystemTime(&sTime);
cmdstart.timestamp = sTime.wHour << 22 | sTime.wMinute << 16 |
sTime.wSecond << 10 | sTime.wMilliseconds;
cmdend.frameId = cmdstart.frameId;
cmd.surfaceId = 0;
cmd.contextId = 0;
cmd.format = PIXEL_FORMAT_XRGB_8888;
cmd.left = nXSrc;
cmd.top = nYSrc;
cmd.right = cmd.left + nWidth;
cmd.bottom = cmd.top + nHeight;
cmd.width = nWidth;
cmd.height = nHeight;
if (settings->GfxH264)
{
RDPGFX_AVC420_BITMAP_STREAM avc420;
RECTANGLE_16 regionRect;
RDPGFX_H264_QUANT_QUALITY quantQualityVal;
shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC420);
avc420_compress(encoder->h264, pSrcData, PIXEL_FORMAT_RGB32, nSrcStep,
nWidth, nHeight, &avc420.data, &avc420.length);
cmd.codecId = RDPGFX_CODECID_AVC420;
cmd.extra = (void *)&avc420;
regionRect.left = cmd.left;
regionRect.top = cmd.top;
regionRect.right = cmd.right;
regionRect.bottom = cmd.bottom;
quantQualityVal.qp = encoder->h264->QP;
quantQualityVal.r = 0;
quantQualityVal.p = 0;
quantQualityVal.qualityVal = 100 - quantQualityVal.qp;
avc420.meta.numRegionRects = 1;
avc420.meta.regionRects = &regionRect;
avc420.meta.quantQualityVals = &quantQualityVal;
IFCALL(client->rdpgfx->StartFrame, client->rdpgfx, &cmdstart);
IFCALL(client->rdpgfx->SurfaceCommand, client->rdpgfx, &cmd);
IFCALL(client->rdpgfx->EndFrame, client->rdpgfx, &cmdend);
}
2014-07-14 03:42:57 +04:00
return 1;
}
static int shadow_client_send_surface_bits(rdpShadowClient* client,
BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
int i;
BOOL first;
BOOL last;
wStream* s;
int numMessages;
2014-07-14 03:42:57 +04:00
UINT32 frameId = 0;
rdpUpdate* update;
rdpContext* context;
rdpSettings* settings;
rdpShadowServer* server;
rdpShadowEncoder* encoder;
SURFACE_BITS_COMMAND cmd;
context = (rdpContext*) client;
update = context->update;
settings = context->settings;
server = client->server;
encoder = client->encoder;
2014-07-15 02:01:29 +04:00
2014-07-14 03:42:57 +04:00
if (encoder->frameAck)
frameId = shadow_encoder_create_frame_id(encoder);
2014-07-14 03:42:57 +04:00
if (settings->RemoteFxCodec)
{
RFX_RECT rect;
RFX_MESSAGE* messages;
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
RFX_RECT *messageRects = NULL;
shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX);
s = encoder->bs;
rect.x = nXSrc;
rect.y = nYSrc;
rect.width = nWidth;
rect.height = nHeight;
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
if (!(messages = rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
settings->DesktopWidth, settings->DesktopHeight, nSrcStep, &numMessages,
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
settings->MultifragMaxRequestSize)))
{
return 0;
}
cmd.codecID = settings->RemoteFxCodecId;
cmd.destLeft = 0;
cmd.destTop = 0;
cmd.destRight = settings->DesktopWidth;
cmd.destBottom = settings->DesktopHeight;
cmd.bpp = 32;
cmd.width = settings->DesktopWidth;
cmd.height = settings->DesktopHeight;
cmd.skipCompression = TRUE;
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
if (numMessages > 0)
messageRects = messages[0].rects;
for (i = 0; i < numMessages; i++)
{
Stream_SetPosition(s, 0);
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
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);
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);
}
codec/rfx: error checking and various fixes - removed some unneeded null checks for free() - fixed a memory leak in shadow_client - removed rfx_compose_message_header from API Changed the following functions to BOOL, check the result where they are called and handle failures: - rfx_compose_message - rfx_compose_message_header - rfx_write_tile - rfx_write_message_tileset - rfx_write_message_frame_begin - rfx_write_message_region - rfx_write_message_frame_end - rfx_write_message rfx_process_message: - check memory allocation failures - verify protocol-conform order of data messages to prevents memory leaks caused by repeated allocations - verify that header messages were parsed/received before the data messages - treat unknown rlgr mode as error - fixed/added error handling - fixed all callers to check/handle result rfx_encode_message: - fixed incorrect usage of realloc - missing malloc check - missing check of CreateThreadpoolWork - correct cleanup on failure (threadpool, memory) - check rfx_encode_message result rfx_encode_messages: - check rfx_split_message result - correct cleanup on failure - prevent memory leak on failure rfx_write_message_context: - fixed invalid channelId value (must be 0xFF for WBT_CONTEXT) rfx_process_message_codec_versions: - fixed invalid read size of codec_version (it is 16bit) rfx_process_message_channels: - verify protocol conform channelId value rfx_process_message_region: - replaced invalid reallocs with malloc - read and verify regionType and numTileSets from stream rfx_process_message_tileset: - check allocation results - fixed incorrect usages of realloc setupWorkers: - fixed incorrect usages of realloc rfx_split_message: - removed dead code - missing malloc check rfx_compose_message: - fixed a memory leak - check/handle rfx_encode_message result
2015-04-23 16:42:21 +03:00
free(messageRects);
free(messages);
}
else if (settings->NSCodec)
{
shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC);
s = encoder->bs;
2014-09-24 20:10:02 +04:00
Stream_SetPosition(s, 0);
pSrcData = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
2014-09-24 20:10:02 +04:00
nsc_compose_message(encoder->nsc, s, pSrcData, nWidth, nHeight, nSrcStep);
cmd.bpp = 32;
cmd.codecID = settings->NSCodecId;
2014-09-24 20:10:02 +04:00
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;
}
static int shadow_client_send_bitmap_update(rdpShadowClient* client,
BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
2014-07-11 01:20:41 +04:00
{
BYTE* data;
BYTE* buffer;
2014-09-24 21:17:52 +04:00
int yIdx, xIdx, k;
int rows, cols;
UINT32 DstSize;
2014-09-24 21:17:52 +04:00
UINT32 SrcFormat;
BITMAP_DATA* bitmap;
rdpUpdate* update;
rdpContext* context;
rdpSettings* settings;
UINT32 maxUpdateSize;
UINT32 totalBitmapSize;
UINT32 updateSizeEstimate;
BITMAP_DATA* bitmapData;
BITMAP_UPDATE bitmapUpdate;
rdpShadowServer* server;
rdpShadowEncoder* encoder;
2014-07-11 01:20:41 +04:00
context = (rdpContext*) client;
update = context->update;
settings = context->settings;
2014-07-11 01:20:41 +04:00
server = client->server;
encoder = client->encoder;
2014-07-11 01:20:41 +04:00
maxUpdateSize = settings->MultifragMaxRequestSize;
if (settings->ColorDepth < 32)
shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED);
else
shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR);
2014-09-24 21:17:52 +04:00
SrcFormat = PIXEL_FORMAT_RGB32;
2014-07-11 01:20:41 +04:00
if ((nXSrc % 4) != 0)
{
nWidth += (nXSrc % 4);
nXSrc -= (nXSrc % 4);
}
2014-07-11 01:20:41 +04:00
if ((nYSrc % 4) != 0)
2014-07-11 01:20:41 +04:00
{
nHeight += (nYSrc % 4);
nYSrc -= (nYSrc % 4);
}
2014-07-11 01:20:41 +04:00
2014-09-24 21:17:52 +04:00
rows = (nHeight / 64) + ((nHeight % 64) ? 1 : 0);
cols = (nWidth / 64) + ((nWidth % 64) ? 1 : 0);
k = 0;
totalBitmapSize = 0;
bitmapUpdate.count = bitmapUpdate.number = rows * cols;
if (!(bitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * bitmapUpdate.number)))
return -1;
bitmapUpdate.rectangles = bitmapData;
if ((nWidth % 4) != 0)
{
nWidth += (4 - (nWidth % 4));
2014-07-11 01:20:41 +04:00
}
if ((nHeight % 4) != 0)
{
nHeight += (4 - (nHeight % 4));
}
2014-09-24 21:17:52 +04:00
for (yIdx = 0; yIdx < rows; yIdx++)
{
2014-09-24 21:17:52 +04:00
for (xIdx = 0; xIdx < cols; xIdx++)
{
2014-09-24 21:17:52 +04:00
bitmap = &bitmapData[k];
bitmap->width = 64;
bitmap->height = 64;
bitmap->destLeft = nXSrc + (xIdx * 64);
bitmap->destTop = nYSrc + (yIdx * 64);
if ((bitmap->destLeft + bitmap->width) > (nXSrc + nWidth))
bitmap->width = (nXSrc + nWidth) - bitmap->destLeft;
if ((bitmap->destTop + bitmap->height) > (nYSrc + nHeight))
bitmap->height = (nYSrc + nHeight) - bitmap->destTop;
bitmap->destRight = bitmap->destLeft + bitmap->width - 1;
bitmap->destBottom = bitmap->destTop + bitmap->height - 1;
bitmap->compressed = TRUE;
if ((bitmap->width < 4) || (bitmap->height < 4))
continue;
if (settings->ColorDepth < 32)
{
2014-09-24 21:17:52 +04:00
int bitsPerPixel = settings->ColorDepth;
int bytesPerPixel = (bitsPerPixel + 7) / 8;
DstSize = 64 * 64 * 4;
buffer = encoder->grid[k];
interleaved_compress(encoder->interleaved, buffer, &DstSize, bitmap->width, bitmap->height,
pSrcData, SrcFormat, nSrcStep, bitmap->destLeft, bitmap->destTop, NULL, bitsPerPixel);
bitmap->bitmapDataStream = buffer;
bitmap->bitmapLength = DstSize;
bitmap->bitsPerPixel = bitsPerPixel;
bitmap->cbScanWidth = bitmap->width * bytesPerPixel;
bitmap->cbUncompressedSize = bitmap->width * bitmap->height * bytesPerPixel;
}
2014-09-24 21:17:52 +04:00
else
{
int dstSize;
buffer = encoder->grid[k];
data = &pSrcData[(bitmap->destTop * nSrcStep) + (bitmap->destLeft * 4)];
buffer = freerdp_bitmap_compress_planar(encoder->planar, data, SrcFormat,
bitmap->width, bitmap->height, nSrcStep, buffer, &dstSize);
bitmap->bitmapDataStream = buffer;
bitmap->bitmapLength = dstSize;
bitmap->bitsPerPixel = 32;
bitmap->cbScanWidth = bitmap->width * 4;
bitmap->cbUncompressedSize = bitmap->width * bitmap->height * 4;
}
bitmap->cbCompFirstRowSize = 0;
bitmap->cbCompMainBodySize = bitmap->bitmapLength;
totalBitmapSize += bitmap->bitmapLength;
k++;
}
}
bitmapUpdate.count = bitmapUpdate.number = k;
updateSizeEstimate = totalBitmapSize + (k * bitmapUpdate.count) + 16;
if (updateSizeEstimate > maxUpdateSize)
{
UINT32 i, j;
UINT32 updateSize;
UINT32 newUpdateSize;
2015-09-01 13:05:22 +03:00
BITMAP_DATA* fragBitmapData = NULL;
if (k > 0)
fragBitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * k);
if (!fragBitmapData)
{
free(bitmapData);
return -1;
}
bitmapUpdate.rectangles = fragBitmapData;
i = j = 0;
updateSize = 1024;
while (i < k)
{
newUpdateSize = updateSize + (bitmapData[i].bitmapLength + 16);
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;
}
}
free(fragBitmapData);
}
else
{
IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
}
2014-07-11 01:20:41 +04:00
free(bitmapData);
return 1;
}
static int shadow_client_send_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
{
int status = -1;
int nXSrc, nYSrc;
int nWidth, nHeight;
rdpContext* context;
rdpSettings* settings;
rdpShadowServer* server;
rdpShadowSurface* surface;
rdpShadowEncoder* encoder;
REGION16 invalidRegion;
RECTANGLE_16 surfaceRect;
const RECTANGLE_16* extents;
BYTE* pSrcData;
int nSrcStep;
int index;
int numRects = 0;
const RECTANGLE_16* rects;
context = (rdpContext*) client;
settings = context->settings;
server = client->server;
encoder = client->encoder;
surface = client->inLobby ? server->lobby : server->surface;
EnterCriticalSection(&(client->lock));
region16_init(&invalidRegion);
region16_copy(&invalidRegion, &(client->invalidRegion));
region16_clear(&(client->invalidRegion));
LeaveCriticalSection(&(client->lock));
rects = region16_rects(&(surface->invalidRegion), &numRects);
for (index = 0; index < numRects; index++)
{
region16_union_rect(&invalidRegion, &invalidRegion, &rects[index]);
}
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = surface->width;
surfaceRect.bottom = surface->height;
region16_intersect_rect(&invalidRegion, &invalidRegion, &surfaceRect);
if (server->shareSubRect)
{
region16_intersect_rect(&invalidRegion, &invalidRegion, &(server->subRect));
}
if (region16_is_empty(&invalidRegion))
{
/* No image region need to be updated */
region16_uninit(&invalidRegion);
return 1;
}
extents = region16_extents(&invalidRegion);
nXSrc = extents->left;
nYSrc = extents->top;
nWidth = extents->right - extents->left;
nHeight = extents->bottom - extents->top;
pSrcData = surface->data;
nSrcStep = surface->scanline;
/* Move to new pSrcData / nXSrc / nYSrc according to sub rect */
if (server->shareSubRect)
{
int subX, subY;
subX = server->subRect.left;
subY = server->subRect.top;
nXSrc -= subX;
nYSrc -= subY;
pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)];
}
2014-09-12 19:38:12 +04:00
//WLog_INFO(TAG, "shadow_client_send_surface_update: x: %d y: %d width: %d height: %d right: %d bottom: %d",
// nXSrc, nYSrc, nWidth, nHeight, nXSrc + nWidth, nYSrc + nHeight);
if (settings->SupportGraphicsPipeline &&
settings->GfxH264 &&
pStatus->gfxOpened)
{
/* GFX/h264 always full screen encoded */
nWidth = settings->DesktopWidth;
nHeight = settings->DesktopHeight;
/* Create primary surface if have not */
if (!pStatus->gfxSurfaceCreated)
{
/* Only init surface when we have h264 supported */
shadow_client_rdpgfx_new_surface(client);
shadow_client_rdpgfx_reset_graphic(client);
pStatus->gfxSurfaceCreated = TRUE;
}
status = shadow_client_send_surface_gfx(client, pSrcData, nSrcStep, 0, 0, nWidth, nHeight);
}
else if (settings->RemoteFxCodec || settings->NSCodec)
{
status = shadow_client_send_surface_bits(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight);
}
else
{
status = shadow_client_send_bitmap_update(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight);
}
region16_uninit(&invalidRegion);
return status;
2014-07-11 01:20:41 +04:00
}
static void shadow_client_send_resize(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
{
rdpContext* context;
rdpSettings* settings;
rdpShadowServer* server;
freerdp_peer* peer;
server = client->server;
context = (rdpContext*) client;
peer = context->peer;
settings = context->settings;
/**
* Unset client activated flag to avoid sending update message during
* resize. DesktopResize will reactive the client and
* shadow_client_activate would be invoked later.
*/
client->activated = FALSE;
/* Close Gfx surfaces */
if (pStatus->gfxSurfaceCreated)
{
shadow_client_rdpgfx_release_surface(client);
pStatus->gfxSurfaceCreated = FALSE;
}
/* Send Resize */
peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize
/* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */
EnterCriticalSection(&(client->lock));
region16_clear(&(client->invalidRegion));
LeaveCriticalSection(&(client->lock));
WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)",
peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
}
static INLINE void shadow_client_no_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
{
rdpShadowServer* server;
rdpShadowSurface* surface;
server = client->server;
surface = client->inLobby ? server->lobby : server->surface;
shadow_client_surface_update(client, &(surface->invalidRegion));
}
int shadow_client_surface_update(rdpShadowClient* client, REGION16* region)
{
int numRects = 0;
const RECTANGLE_16* rects;
rects = region16_rects(region, &numRects);
shadow_client_mark_invalid(client, numRects, rects);
return 1;
}
static int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
2014-11-07 01:25:41 +03:00
{
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)
2014-11-07 01:25:41 +03:00
{
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;
2014-11-07 01:25:41 +03:00
pointerPosition.xPos = msg->xPos;
pointerPosition.yPos = msg->yPos;
2014-11-07 01:25:41 +03:00
if (client->server->shareSubRect)
{
pointerPosition.xPos -= client->server->subRect.left;
pointerPosition.yPos -= client->server->subRect.top;
}
if (client->activated)
{
if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY))
{
IFCALL(update->pointer->PointerPosition, context, &pointerPosition);
2014-11-07 21:51:10 +03:00
client->pointerX = msg->xPos;
client->pointerY = msg->yPos;
}
}
break;
2014-11-07 01:25:41 +03:00
}
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;
2014-11-07 01:25:41 +03:00
ZeroMemory(&pointerNew, sizeof(POINTER_NEW_UPDATE));
2014-11-07 01:25:41 +03:00
pointerNew.xorBpp = 24;
pointerColor = &(pointerNew.colorPtrAttr);
2014-11-07 01:25:41 +03:00
pointerColor->cacheIndex = 0;
pointerColor->xPos = msg->xHot;
pointerColor->yPos = msg->yHot;
pointerColor->width = msg->width;
pointerColor->height = msg->height;
pointerColor->lengthAndMask = msg->lengthAndMask;
pointerColor->lengthXorMask = msg->lengthXorMask;
pointerColor->xorMaskData = msg->xorMaskData;
pointerColor->andMaskData = msg->andMaskData;
2014-11-07 01:25:41 +03:00
pointerCached.cacheIndex = pointerColor->cacheIndex;
2014-11-07 21:51:10 +03:00
if (client->activated)
{
IFCALL(update->pointer->PointerNew, context, &pointerNew);
IFCALL(update->pointer->PointerCached, context, &pointerCached);
}
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;
2014-11-07 01:25:41 +03:00
}
shadow_client_free_queued_message(message);
2014-11-07 01:25:41 +03:00
return 1;
}
static void* shadow_client_thread(rdpShadowClient* client)
2014-07-11 01:20:41 +04:00
{
DWORD status;
DWORD nCount;
2014-11-07 01:25:41 +03:00
wMessage message;
wMessage pointerPositionMsg;
wMessage pointerAlphaMsg;
wMessage audioVolumeMsg;
2014-07-11 01:20:41 +04:00
HANDLE events[32];
HANDLE ClientEvent;
HANDLE ChannelEvent;
void* UpdateSubscriber;
HANDLE UpdateEvent;
freerdp_peer* peer;
2014-09-19 01:22:44 +04:00
rdpContext* context;
2014-07-11 01:20:41 +04:00
rdpSettings* settings;
2014-07-12 09:18:08 +04:00
rdpShadowServer* server;
rdpShadowScreen* screen;
2014-07-14 03:42:57 +04:00
rdpShadowEncoder* encoder;
2014-07-12 09:18:08 +04:00
rdpShadowSubsystem* subsystem;
wMessageQueue* MsgQueue = client->MsgQueue;
/* This should only be visited in client thread */
SHADOW_GFX_STATUS gfxstatus;
gfxstatus.gfxOpened = FALSE;
gfxstatus.gfxSurfaceCreated = FALSE;
2014-07-12 09:18:08 +04:00
server = client->server;
screen = server->screen;
encoder = client->encoder;
2014-07-12 09:18:08 +04:00
subsystem = server->subsystem;
2014-07-11 01:20:41 +04:00
2014-09-19 01:22:44 +04:00
context = (rdpContext*) client;
peer = context->peer;
settings = peer->settings;
2014-07-11 01:20:41 +04:00
peer->Capabilities = shadow_client_capabilities;
peer->PostConnect = shadow_client_post_connect;
peer->Activate = shadow_client_activate;
peer->Logon = shadow_client_logon;
2014-07-11 01:20:41 +04:00
shadow_input_register_callbacks(peer->input);
2014-07-11 01:20:41 +04:00
peer->Initialize(peer);
2014-07-11 01:20:41 +04:00
2015-05-23 23:47:18 +03:00
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;
2014-07-14 03:42:57 +04:00
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);
2014-07-11 01:20:41 +04:00
while (1)
{
nCount = 0;
events[nCount++] = UpdateEvent;
2014-07-11 01:20:41 +04:00
events[nCount++] = ClientEvent;
events[nCount++] = ChannelEvent;
events[nCount++] = MessageQueue_Event(MsgQueue);
2014-07-12 09:18:08 +04:00
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
2014-07-11 01:20:41 +04:00
if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
{
/* The UpdateEvent means to start sending current frame. It is
* triggered from subsystem implementation and it should ensure
* that the screen and primary surface meta data (width, height,
* scanline, invalid region, etc) is not changed until it is reset
* (at shadow_multiclient_consume). As best practice, subsystem
* implementation should invoke shadow_subsystem_frame_update which
* triggers the event and then wait for completion */
if (client->activated && !client->suppressOutput)
{
/* Send screen update or resize to this client */
/* Check resize */
if (shadow_client_recalc_desktop_size(client))
{
/* Screen size changed, do resize */
shadow_client_send_resize(client, &gfxstatus);
}
else
{
/* Send frame */
shadow_client_send_surface_update(client, &gfxstatus);
}
}
else
{
/* Our client don't receive graphic updates. Just save the invalid region */
shadow_client_no_surface_update(client, &gfxstatus);
}
/*
* 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);
}
2014-07-11 01:20:41 +04:00
if (WaitForSingleObject(ClientEvent, 0) == WAIT_OBJECT_0)
{
if (!peer->CheckFileDescriptor(peer))
2014-07-11 01:20:41 +04:00
{
2014-09-12 19:38:12 +04:00
WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
2014-07-11 01:20:41 +04:00
break;
}
if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, "drdynvc"))
{
/* Dynamic channel status may have been changed after processing */
if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_NONE)
{
/* Call this routine to Initialize drdynvc channel */
if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
{
WLog_ERR(TAG, "Failed to initialize drdynvc channel");
break;
}
}
else if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_READY)
{
/* Init RDPGFX dynamic channel */
if (settings->SupportGraphicsPipeline && client->rdpgfx &&
!gfxstatus.gfxOpened)
{
if (!client->rdpgfx->Open(client->rdpgfx))
{
WLog_ERR(TAG, "Failed to open GraphicsPipeline");
settings->SupportGraphicsPipeline = FALSE;
}
client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge;
gfxstatus.gfxOpened = TRUE;
WLog_INFO(TAG, "Gfx Pipeline Opened");
}
}
}
2014-07-11 01:20:41 +04:00
}
if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
{
2014-11-07 01:25:41 +03:00
if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
{
2014-09-12 19:38:12 +04:00
WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure");
break;
}
}
2014-11-07 01:25:41 +03:00
if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0)
2014-11-07 01:25:41 +03:00
{
/* 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))
2014-11-07 01:25:41 +03:00
{
if (message.id == WMQ_QUIT)
{
2014-11-07 01:25:41 +03:00
break;
}
2014-11-07 01:25:41 +03:00
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;
}
}
2014-11-07 01:25:41 +03:00
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);
}
2014-11-07 01:25:41 +03:00
}
}
2014-07-11 01:20:41 +04:00
}
/* Free channels early because we establish channels in post connect */
if (gfxstatus.gfxOpened)
{
if (gfxstatus.gfxSurfaceCreated)
{
shadow_client_rdpgfx_release_surface(client);
}
(void)client->rdpgfx->Close(client->rdpgfx);
}
shadow_client_channels_free(client);
if (UpdateSubscriber)
{
shadow_multiclient_release_subscriber(UpdateSubscriber);
UpdateSubscriber = NULL;
}
if (peer->connected && subsystem->ClientDisconnect)
{
subsystem->ClientDisconnect(subsystem, client);
}
out:
peer->Disconnect(peer);
2014-07-11 01:20:41 +04:00
freerdp_peer_context_free(peer);
freerdp_peer_free(peer);
2014-07-11 01:20:41 +04:00
ExitThread(0);
return NULL;
}
BOOL shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
2014-07-11 01:20:41 +04:00
{
rdpShadowClient* client;
rdpShadowServer* server;
server = (rdpShadowServer*) listener->info;
peer->ContextExtra = (void*) server;
peer->ContextSize = sizeof(rdpShadowClient);
peer->ContextNew = (psPeerContextNew) shadow_client_context_new;
peer->ContextFree = (psPeerContextFree) shadow_client_context_free;
peer->settings = freerdp_settings_clone(server->settings);
if (!freerdp_peer_context_new(peer))
return FALSE;
client = (rdpShadowClient*) peer->context;
2014-07-11 01:20:41 +04:00
if (!(client->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
shadow_client_thread, client, 0, NULL)))
{
freerdp_peer_context_free(peer);
return FALSE;
}
return TRUE;
2014-07-11 01:20:41 +04:00
}
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;
}