diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 012d2e68b..c5994b340 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -31,6 +31,8 @@ #include #include +#include +#include #include #include @@ -73,6 +75,8 @@ typedef int (*pfnShadowUnicodeKeyboardEvent)(rdpShadowSubsystem* subsystem, UINT typedef int (*pfnShadowMouseEvent)(rdpShadowSubsystem* subsystem, UINT16 flags, UINT16 x, UINT16 y); typedef int (*pfnShadowExtendedMouseEvent)(rdpShadowSubsystem* subsystem, UINT16 flags, UINT16 x, UINT16 y); +typedef void (*pfnShadowChannelAudinServerReceiveSamples)(rdpShadowSubsystem* subsystem, const void* buf, int nframes); + struct rdp_shadow_client { rdpContext context; @@ -82,7 +86,7 @@ struct rdp_shadow_client BOOL inLobby; BOOL mayView; BOOL mayInteract; - HANDLE StopEvent; + wMessageQueue* MsgQueue; CRITICAL_SECTION lock; REGION16 invalidRegion; rdpShadowServer* server; @@ -96,6 +100,8 @@ struct rdp_shadow_client HANDLE vcm; EncomspServerContext* encomsp; RemdeskServerContext* remdesk; + RdpsndServerContext* rdpsnd; + audin_server_context* audin; }; struct rdp_shadow_server @@ -153,11 +159,17 @@ struct _RDP_SHADOW_ENTRY_POINTS UINT32 pointerX; \ UINT32 pointerY; \ \ + const AUDIO_FORMAT* rdpsndFormats; \ + int nRdpsndFormats; \ + const AUDIO_FORMAT* audinFormats; \ + int nAudinFormats; \ + \ pfnShadowSynchronizeEvent SynchronizeEvent; \ pfnShadowKeyboardEvent KeyboardEvent; \ pfnShadowUnicodeKeyboardEvent UnicodeKeyboardEvent; \ pfnShadowMouseEvent MouseEvent; \ pfnShadowExtendedMouseEvent ExtendedMouseEvent; \ + pfnShadowChannelAudinServerReceiveSamples AudinServerReceiveSamples; \ \ pfnShadowAuthenticate Authenticate; \ pfnShadowClientConnect ClientConnect; \ @@ -170,6 +182,79 @@ struct rdp_shadow_subsystem RDP_SHADOW_SUBSYSTEM_COMMON(); }; +/* Definition of message between subsystem and clients */ +#define SHADOW_MSG_IN_REFRESH_OUTPUT_ID 1001 +#define SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID 1002 + +struct _SHADOW_MSG_IN_REFRESH_OUTPUT +{ + UINT32 numRects; + RECTANGLE_16* rects; +}; +typedef struct _SHADOW_MSG_IN_REFRESH_OUTPUT SHADOW_MSG_IN_REFRESH_OUTPUT; + +struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT +{ + BOOL allow; + RECTANGLE_16 rect; +}; +typedef struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT SHADOW_MSG_IN_SUPPRESS_OUTPUT; + +typedef struct _SHADOW_MSG_OUT SHADOW_MSG_OUT; +typedef void (*MSG_OUT_FREE_FN)(UINT32 id, SHADOW_MSG_OUT* msg); +#define RDP_SHADOW_MSG_OUT_COMMON() \ + int refCount; \ + MSG_OUT_FREE_FN Free; /* function to free SHADOW_MSG_OUT */ + +struct _SHADOW_MSG_OUT +{ + RDP_SHADOW_MSG_OUT_COMMON(); +}; + +#define SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID 2001 +#define SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID 2002 +#define SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID 2003 +#define SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID 2004 + +struct _SHADOW_MSG_OUT_POINTER_POSITION_UPDATE +{ + RDP_SHADOW_MSG_OUT_COMMON(); + UINT32 xPos; + UINT32 yPos; +}; +typedef struct _SHADOW_MSG_OUT_POINTER_POSITION_UPDATE SHADOW_MSG_OUT_POINTER_POSITION_UPDATE; + +struct _SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE +{ + RDP_SHADOW_MSG_OUT_COMMON(); + UINT32 xHot; + UINT32 yHot; + UINT32 width; + UINT32 height; + BYTE* pixels; + int scanline; + BOOL premultiplied; +}; +typedef struct _SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE; + +struct _SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES +{ + RDP_SHADOW_MSG_OUT_COMMON(); + AUDIO_FORMAT audio_format; + void* buf; + int nFrames; + UINT16 wTimestamp; +}; +typedef struct _SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES; + +struct _SHADOW_MSG_OUT_AUDIO_OUT_VOLUME +{ + RDP_SHADOW_MSG_OUT_COMMON(); + int left; + int right; +}; +typedef struct _SHADOW_MSG_OUT_AUDIO_OUT_VOLUME SHADOW_MSG_OUT_AUDIO_OUT_VOLUME; + #ifdef __cplusplus extern "C" { #endif @@ -190,6 +275,10 @@ FREERDP_API int shadow_enum_monitors(MONITOR_DEF* monitors, int maxMonitors); FREERDP_API rdpShadowServer* shadow_server_new(); FREERDP_API void shadow_server_free(rdpShadowServer* server); +FREERDP_API BOOL shadow_client_post_msg(rdpShadowClient* client, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam); +FREERDP_API int shadow_client_boardcast_msg(rdpShadowServer* server, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam); +FREERDP_API int shadow_client_boardcast_quit(rdpShadowServer* server, int nExitCode); + #ifdef __cplusplus } #endif diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt index 06c60f5d1..eda2f617d 100644 --- a/server/shadow/CMakeLists.txt +++ b/server/shadow/CMakeLists.txt @@ -169,6 +169,10 @@ set(${MODULE_PREFIX}_SRCS shadow_encomsp.h shadow_remdesk.c shadow_remdesk.h + shadow_rdpsnd.c + shadow_rdpsnd.h + shadow_audin.c + shadow_audin.h shadow_subsystem.c shadow_subsystem.h shadow_mcevent.c diff --git a/server/shadow/Mac/mac_shadow.c b/server/shadow/Mac/mac_shadow.c index f4e25c984..d3fbb77b0 100644 --- a/server/shadow/Mac/mac_shadow.c +++ b/server/shadow/Mac/mac_shadow.c @@ -452,48 +452,56 @@ int mac_shadow_screen_grab(macShadowSubsystem* subsystem) int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message) { - if (message->id == SHADOW_MSG_IN_REFRESH_OUTPUT_ID) + switch(message->id) { - UINT32 index; - SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; - - if (msg->numRects) + case SHADOW_MSG_IN_REFRESH_OUTPUT_ID: { - for (index = 0; index < msg->numRects; index++) + UINT32 index; + SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; + + if (msg->numRects) + { + for (index = 0; index < msg->numRects; index++) + { + region16_union_rect(&(subsystem->invalidRegion), + &(subsystem->invalidRegion), &msg->rects[index]); + } + } + else + { + RECTANGLE_16 refreshRect; + + refreshRect.left = 0; + refreshRect.top = 0; + refreshRect.right = subsystem->width; + refreshRect.bottom = subsystem->height; + + region16_union_rect(&(subsystem->invalidRegion), + &(subsystem->invalidRegion), &refreshRect); + } + break; + } + case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID: + { + SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; + + subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; + + if (msg->allow) { region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &msg->rects[index]); + &(subsystem->invalidRegion), &(msg->rect)); } + break; } - else - { - RECTANGLE_16 refreshRect; - - refreshRect.left = 0; - refreshRect.top = 0; - refreshRect.right = subsystem->width; - refreshRect.bottom = subsystem->height; - - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &refreshRect); - } + default: + WLog_ERR(TAG, "Unknown message id: %u", message->id); + break; } - else if (message->id == SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID) - { - SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; - - subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; - - if (msg->allow) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &(msg->rect)); - } - } - + if (message->Free) message->Free(message); - + return 1; } diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c index 70fc36ab6..c297683a8 100644 --- a/server/shadow/X11/x11_shadow.c +++ b/server/shadow/X11/x11_shadow.c @@ -348,11 +348,30 @@ void x11_shadow_input_extended_mouse_event(x11ShadowSubsystem* subsystem, UINT16 #endif } +static void x11_shadow_message_free(UINT32 id, SHADOW_MSG_OUT* msg) +{ + switch(id) + { + case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID: + free(msg); + break; + + case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID: + free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->pixels); + free(msg); + break; + + default: + WLog_ERR(TAG, "Unknown message id: %u", id); + free(msg); + break; + } +} + int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem) { SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg; UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID; - wMessagePipe* MsgPipe = subsystem->MsgPipe; msg = (SHADOW_MSG_OUT_POINTER_POSITION_UPDATE*) calloc(1, sizeof(SHADOW_MSG_OUT_POINTER_POSITION_UPDATE)); @@ -361,15 +380,15 @@ int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem) msg->xPos = subsystem->pointerX; msg->yPos = subsystem->pointerY; + msg->Free = x11_shadow_message_free; - return MessageQueue_Post(MsgPipe->Out, NULL, msgId, (void*) msg, NULL) ? 1 : -1; + return shadow_client_boardcast_msg(subsystem->server, NULL, msgId, (SHADOW_MSG_OUT*) msg, NULL) ? 1 : -1; } int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem) { SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg; UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID; - wMessagePipe* MsgPipe = subsystem->MsgPipe; msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*) calloc(1, sizeof(SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE)); @@ -392,8 +411,9 @@ int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem) CopyMemory(msg->pixels, subsystem->cursorPixels, msg->scanline * msg->height); msg->premultiplied = TRUE; + msg->Free = x11_shadow_message_free; - return MessageQueue_Post(MsgPipe->Out, NULL, msgId, (void*) msg, NULL) ? 1 : -1; + return shadow_client_boardcast_msg(subsystem->server, NULL, msgId, (SHADOW_MSG_OUT*) msg, NULL) ? 1 : -1; } int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage) @@ -730,43 +750,51 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message) { - if (message->id == SHADOW_MSG_IN_REFRESH_OUTPUT_ID) + switch(message->id) { - UINT32 index; - SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; - - if (msg->numRects) + case SHADOW_MSG_IN_REFRESH_OUTPUT_ID: { - for (index = 0; index < msg->numRects; index++) + UINT32 index; + SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; + + if (msg->numRects) + { + for (index = 0; index < msg->numRects; index++) + { + region16_union_rect(&(subsystem->invalidRegion), + &(subsystem->invalidRegion), &msg->rects[index]); + } + } + else + { + RECTANGLE_16 refreshRect; + + refreshRect.left = 0; + refreshRect.top = 0; + refreshRect.right = subsystem->width; + refreshRect.bottom = subsystem->height; + + region16_union_rect(&(subsystem->invalidRegion), + &(subsystem->invalidRegion), &refreshRect); + } + break; + } + case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID: + { + SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; + + subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; + + if (msg->allow) { region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &msg->rects[index]); + &(subsystem->invalidRegion), &(msg->rect)); } + break; } - else - { - RECTANGLE_16 refreshRect; - - refreshRect.left = 0; - refreshRect.top = 0; - refreshRect.right = subsystem->width; - refreshRect.bottom = subsystem->height; - - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &refreshRect); - } - } - else if (message->id == SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID) - { - SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; - - subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; - - if (msg->allow) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &(msg->rect)); - } + default: + WLog_ERR(TAG, "Unknown message id: %u", message->id); + break; } if (message->Free) diff --git a/server/shadow/shadow_audin.c b/server/shadow/shadow_audin.c new file mode 100644 index 000000000..0b975b39b --- /dev/null +++ b/server/shadow/shadow_audin.c @@ -0,0 +1,121 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2015 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "shadow.h" + +#include "shadow_audin.h" + +#define TAG SERVER_TAG("shadow") + +/* Default supported audio formats */ +static const AUDIO_FORMAT default_supported_audio_formats[] = +{ + { WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL }, + { WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL } +}; + +static void AudinServerOpening(audin_server_context* context) +{ + AUDIO_FORMAT* agreed_format = NULL; + int i = 0, j = 0; + for (i = 0; i < context->num_client_formats; i++) + { + for (j = 0; j < context->num_server_formats; j++) + { + if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) && + (context->client_formats[i].nChannels == context->server_formats[j].nChannels) && + (context->client_formats[i].nSamplesPerSec == context->server_formats[j].nSamplesPerSec)) + { + agreed_format = (AUDIO_FORMAT*) &context->server_formats[j]; + break; + } + } + if (agreed_format != NULL) + break; + + } + + if (agreed_format == NULL) + { + WLog_ERR(TAG, "Could not agree on a audio format with the server\n"); + return; + } + + context->SelectFormat(context, i); +} +static void AudinServerOpenResult(audin_server_context* context, UINT32 result) +{ + WLog_INFO(TAG, "AUDIN open result %u.\n", result); +} +static void AudinServerReceiveSamples(audin_server_context* context, const void* buf, int nframes) +{ + rdpShadowClient* client = (rdpShadowClient* )context->data; + rdpShadowSubsystem* subsystem = client->server->subsystem; + + if (!client->mayInteract) + return; + + if (subsystem->AudinServerReceiveSamples) + subsystem->AudinServerReceiveSamples(subsystem, buf, nframes); +} + +int shadow_client_audin_init(rdpShadowClient* client) +{ + audin_server_context* audin; + audin = client->audin = audin_server_context_new(client->vcm); + if (!audin) + { + return 0; + } + + audin->data = client; + + if (client->subsystem->audinFormats) + { + audin->server_formats = client->subsystem->audinFormats; + audin->num_server_formats = client->subsystem->nAudinFormats; + } + else + { + /* Set default audio formats. */ + audin->server_formats = default_supported_audio_formats; + audin->num_server_formats = sizeof(default_supported_audio_formats) / sizeof(default_supported_audio_formats[0]); + } + + audin->dst_format = audin->server_formats[0]; + + audin->Opening = AudinServerOpening; + audin->OpenResult = AudinServerOpenResult; + audin->ReceiveSamples = AudinServerReceiveSamples; + + return 1; +} + +void shadow_client_audin_uninit(rdpShadowClient* client) +{ + if (client->audin) + { + audin_server_context_free(client->audin); + client->audin = NULL; + } +} diff --git a/server/shadow/shadow_audin.h b/server/shadow/shadow_audin.h new file mode 100644 index 000000000..a02bd3b4e --- /dev/null +++ b/server/shadow/shadow_audin.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2015 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SHADOW_SERVER_AUDIN_H +#define FREERDP_SHADOW_SERVER_AUDIN_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int shadow_client_audin_init(rdpShadowClient* client); +void shadow_client_audin_uninit(rdpShadowClient* client); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_SHADOW_SERVER_AUDIN_H */ diff --git a/server/shadow/shadow_channels.c b/server/shadow/shadow_channels.c index 5a2e4bb03..eaf787e34 100644 --- a/server/shadow/shadow_channels.c +++ b/server/shadow/shadow_channels.c @@ -36,5 +36,23 @@ int shadow_client_channels_post_connect(rdpShadowClient* client) shadow_client_remdesk_init(client); } + if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, "rdpsnd")) + { + shadow_client_rdpsnd_init(client); + } + + shadow_client_audin_init(client); + return 1; } + +void shadow_client_channels_free(rdpShadowClient* client) +{ + shadow_client_audin_uninit(client); + + shadow_client_rdpsnd_uninit(client); + + shadow_client_remdesk_uninit(client); + + shadow_client_encomsp_uninit(client); +} diff --git a/server/shadow/shadow_channels.h b/server/shadow/shadow_channels.h index 7c11eb84e..d6abef9c7 100644 --- a/server/shadow/shadow_channels.h +++ b/server/shadow/shadow_channels.h @@ -26,12 +26,15 @@ #include "shadow_encomsp.h" #include "shadow_remdesk.h" +#include "shadow_rdpsnd.h" +#include "shadow_audin.h" #ifdef __cplusplus extern "C" { #endif int shadow_client_channels_post_connect(rdpShadowClient* client); +void shadow_client_channels_free(rdpShadowClient* client); #ifdef __cplusplus } diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index ce414ec27..650101400 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -33,10 +34,21 @@ #define TAG CLIENT_TAG("shadow") +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; @@ -89,8 +101,8 @@ BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client) if (!client->vcm || client->vcm == INVALID_HANDLE_VALUE) goto fail_open_server; - if (!(client->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) - goto fail_stop_event; + if (!(client->MsgQueue = MessageQueue_New(&cb))) + goto fail_message_queue; if (!(client->encoder = shadow_encoder_new(client))) goto fail_encoder_new; @@ -101,9 +113,9 @@ BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client) shadow_encoder_free(client->encoder); client->encoder = NULL; fail_encoder_new: - CloseHandle(client->StopEvent); - client->StopEvent = NULL; -fail_stop_event: + MessageQueue_Free(client->MsgQueue); + client->MsgQueue = NULL; +fail_message_queue: WTSCloseServer((HANDLE) client->vcm); client->vcm = NULL; fail_open_server: @@ -134,7 +146,9 @@ 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) { @@ -147,25 +161,25 @@ void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client) shadow_encoder_free(client->encoder); client->encoder = NULL; } - - shadow_client_encomsp_uninit(client); - - shadow_client_remdesk_uninit(client); } 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; } } @@ -889,63 +903,87 @@ int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* m /* FIXME: the pointer updates appear to be broken when used with bulk compression and mstsc */ - if (message->id == SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID) + switch(message->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) + case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID: { - if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY)) + 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) { - IFCALL(update->pointer->PointerPosition, context, &pointerPosition); + if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY)) + { + IFCALL(update->pointer->PointerPosition, context, &pointerPosition); - client->pointerX = msg->xPos; - client->pointerY = msg->yPos; + client->pointerX = msg->xPos; + client->pointerY = msg->yPos; + } } + break; } - - free(msg); - } - else if (message->id == 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) + case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID: { - shadow_client_convert_alpha_pointer_data(msg->pixels, msg->premultiplied, - msg->width, msg->height, pointerColor); + 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; - IFCALL(update->pointer->PointerNew, context, &pointerNew); - IFCALL(update->pointer->PointerCached, context, &pointerCached); + ZeroMemory(&pointerNew, sizeof(POINTER_NEW_UPDATE)); - free(pointerColor->xorMaskData); - free(pointerColor->andMaskData); + 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; } - - free(msg->pixels); - free(msg); + 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; } @@ -954,8 +992,10 @@ 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; @@ -967,7 +1007,7 @@ void* shadow_client_thread(rdpShadowClient* client) rdpShadowScreen* screen; rdpShadowEncoder* encoder; rdpShadowSubsystem* subsystem; - wMessagePipe* MsgPipe = client->subsystem->MsgPipe; + wMessageQueue* MsgQueue = client->MsgQueue; server = client->server; screen = server->screen; @@ -990,14 +1030,13 @@ void* shadow_client_thread(rdpShadowClient* client) peer->update->SuppressOutput = (pSuppressOutput)shadow_client_suppress_output; peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge)shadow_client_surface_frame_acknowledge; - if ((!client->StopEvent) || (!client->vcm) || (!subsystem->updateEvent)) + if ((!client->vcm) || (!subsystem->updateEvent)) goto out; UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent); if (!UpdateSubscriber) goto out; - StopEvent = client->StopEvent; UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber); ClientEvent = peer->GetEventHandle(peer); ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm); @@ -1005,19 +1044,13 @@ void* shadow_client_thread(rdpShadowClient* client) while (1) { nCount = 0; - events[nCount++] = StopEvent; events[nCount++] = UpdateEvent; events[nCount++] = ClientEvent; events[nCount++] = ChannelEvent; - events[nCount++] = MessageQueue_Event(MsgPipe->Out); + events[nCount++] = MessageQueue_Event(MsgQueue); status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); - if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0) - { - break; - } - if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0) { if (client->activated) @@ -1061,18 +1094,78 @@ void* shadow_client_thread(rdpShadowClient* client) } } - if (WaitForSingleObject(MessageQueue_Event(MsgPipe->Out), 0) == WAIT_OBJECT_0) + if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0) { - if (MessageQueue_Peek(MsgPipe->Out, &message, TRUE)) + /* 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; + } - shadow_client_subsystem_process_message(client, &message); + 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); @@ -1119,3 +1212,102 @@ BOOL shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer) 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; +} diff --git a/server/shadow/shadow_encomsp.c b/server/shadow/shadow_encomsp.c index 61bb2e582..2540eaf5d 100644 --- a/server/shadow/shadow_encomsp.c +++ b/server/shadow/shadow_encomsp.c @@ -113,6 +113,7 @@ void shadow_client_encomsp_uninit(rdpShadowClient* client) { if (client->encomsp) { client->encomsp->Stop(client->encomsp); + encomsp_server_context_free(client->encomsp); client->encomsp = NULL; } } diff --git a/server/shadow/shadow_rdpsnd.c b/server/shadow/shadow_rdpsnd.c new file mode 100644 index 000000000..15428c655 --- /dev/null +++ b/server/shadow/shadow_rdpsnd.c @@ -0,0 +1,111 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2015 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "shadow.h" + +#include "shadow_rdpsnd.h" + +#define TAG SERVER_TAG("shadow") + +/* Default supported audio formats */ +static const AUDIO_FORMAT default_supported_audio_formats[] = +{ + { WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL }, + { WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL } +}; + +static void rdpsnd_activated(RdpsndServerContext* context) +{ + AUDIO_FORMAT* agreed_format = NULL; + int i = 0, j = 0; + for (i = 0; i < context->num_client_formats; i++) + { + for (j = 0; j < context->num_server_formats; j++) + { + if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) && + (context->client_formats[i].nChannels == context->server_formats[j].nChannels) && + (context->client_formats[i].nSamplesPerSec == context->server_formats[j].nSamplesPerSec)) + { + agreed_format = (AUDIO_FORMAT*) &context->server_formats[j]; + break; + } + } + if (agreed_format != NULL) + break; + + } + + if (agreed_format == NULL) + { + WLog_ERR(TAG, "Could not agree on a audio format with the server\n"); + return; + } + + context->SelectFormat(context, i); + context->SetVolume(context, 0x7FFF, 0x7FFF); +} + +int shadow_client_rdpsnd_init(rdpShadowClient* client) +{ + RdpsndServerContext* rdpsnd; + + rdpsnd = client->rdpsnd = rdpsnd_server_context_new(client->vcm); + if (!rdpsnd) + { + return 0; + } + + rdpsnd->data = client; + + if (client->subsystem->rdpsndFormats) + { + rdpsnd->server_formats = client->subsystem->rdpsndFormats; + rdpsnd->num_server_formats = client->subsystem->nRdpsndFormats; + } + else + { + /* Set default audio formats. */ + rdpsnd->server_formats = default_supported_audio_formats; + rdpsnd->num_server_formats = + sizeof(default_supported_audio_formats) / sizeof(default_supported_audio_formats[0]); + } + + rdpsnd->src_format = rdpsnd->server_formats[0]; + + rdpsnd->Activated = rdpsnd_activated; + + rdpsnd->Initialize(rdpsnd, TRUE); + + return 1; + +} + +void shadow_client_rdpsnd_uninit(rdpShadowClient* client) +{ + if (client->rdpsnd) + { + client->rdpsnd->Stop(client->rdpsnd); + rdpsnd_server_context_free(client->rdpsnd); + client->rdpsnd = NULL; + } +} diff --git a/server/shadow/shadow_rdpsnd.h b/server/shadow/shadow_rdpsnd.h new file mode 100644 index 000000000..b95e9ce3b --- /dev/null +++ b/server/shadow/shadow_rdpsnd.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2015 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SHADOW_SERVER_RDPSND_H +#define FREERDP_SHADOW_SERVER_RDPSND_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int shadow_client_rdpsnd_init(rdpShadowClient* client); +void shadow_client_rdpsnd_uninit(rdpShadowClient* client); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_SHADOW_SERVER_RDPSND_H */ diff --git a/server/shadow/shadow_remdesk.c b/server/shadow/shadow_remdesk.c index 5e7b27330..0797a25ac 100644 --- a/server/shadow/shadow_remdesk.c +++ b/server/shadow/shadow_remdesk.c @@ -42,6 +42,7 @@ void shadow_client_remdesk_uninit(rdpShadowClient* client) { if (client->remdesk) { client->remdesk->Stop(client->remdesk); + remdesk_server_context_free(client->remdesk); client->remdesk = NULL; } } diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index 2d9f26013..ebcd00a2a 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -346,7 +346,7 @@ void* shadow_server_thread(rdpShadowServer* server) /* Signal to the clients that server is being stopped and wait for them * to disconnect. */ - if (MessageQueue_PostQuit(subsystem->MsgPipe->Out, 0)) + if (shadow_client_boardcast_quit(server, 0)) { while(ArrayList_Count(server->clients) > 0) { diff --git a/server/shadow/shadow_subsystem.c b/server/shadow/shadow_subsystem.c index efb791ecf..04775c936 100644 --- a/server/shadow/shadow_subsystem.c +++ b/server/shadow/shadow_subsystem.c @@ -107,6 +107,16 @@ fail: return status; } +static void shadow_subsystem_free_queued_message(void *obj) +{ + wMessage *message = (wMessage*)obj; + if (message->Free) + { + message->Free(message); + message->Free = NULL; + } +} + void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem) { if (!subsystem) @@ -117,6 +127,11 @@ void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem) if (subsystem->MsgPipe) { + /* Release resource in messages before free */ + subsystem->MsgPipe->In->object.fnObjectFree = shadow_subsystem_free_queued_message; + MessageQueue_Clear(subsystem->MsgPipe->In); + subsystem->MsgPipe->Out->object.fnObjectFree = shadow_subsystem_free_queued_message; + MessageQueue_Clear(subsystem->MsgPipe->Out); MessagePipe_Free(subsystem->MsgPipe); subsystem->MsgPipe = NULL; } diff --git a/server/shadow/shadow_subsystem.h b/server/shadow/shadow_subsystem.h index d32967d7a..7170cf207 100644 --- a/server/shadow/shadow_subsystem.h +++ b/server/shadow/shadow_subsystem.h @@ -24,45 +24,6 @@ #include #include -#define SHADOW_MSG_IN_REFRESH_OUTPUT_ID 1001 -#define SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID 1002 - -struct _SHADOW_MSG_IN_REFRESH_OUTPUT -{ - UINT32 numRects; - RECTANGLE_16* rects; -}; -typedef struct _SHADOW_MSG_IN_REFRESH_OUTPUT SHADOW_MSG_IN_REFRESH_OUTPUT; - -struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT -{ - BOOL allow; - RECTANGLE_16 rect; -}; -typedef struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT SHADOW_MSG_IN_SUPPRESS_OUTPUT; - -#define SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID 2001 -#define SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID 2002 - -struct _SHADOW_MSG_OUT_POINTER_POSITION_UPDATE -{ - UINT32 xPos; - UINT32 yPos; -}; -typedef struct _SHADOW_MSG_OUT_POINTER_POSITION_UPDATE SHADOW_MSG_OUT_POINTER_POSITION_UPDATE; - -struct _SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE -{ - UINT32 xHot; - UINT32 yHot; - UINT32 width; - UINT32 height; - BYTE* pixels; - int scanline; - BOOL premultiplied; -}; -typedef struct _SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE; - #ifdef __cplusplus extern "C" { #endif