int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message) { rdpContext* context = (rdpContext*) client; rdpUpdate* update = context->update; /* FIXME: the pointer updates appear to be broken when used with bulk compression and mstsc */ switch(message->id) { case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID: { POINTER_POSITION_UPDATE pointerPosition; SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_POSITION_UPDATE*) message->wParam; pointerPosition.xPos = msg->xPos; pointerPosition.yPos = msg->yPos; if (client->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); client->pointerX = msg->xPos; client->pointerY = msg->yPos; } } break; } case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID: { POINTER_NEW_UPDATE pointerNew; POINTER_COLOR_UPDATE* pointerColor; POINTER_CACHED_UPDATE pointerCached; SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*) message->wParam; ZeroMemory(&pointerNew, sizeof(POINTER_NEW_UPDATE)); pointerNew.xorBpp = 24; pointerColor = &(pointerNew.colorPtrAttr); pointerColor->cacheIndex = 0; pointerColor->xPos = msg->xHot; pointerColor->yPos = msg->yHot; pointerColor->width = msg->width; pointerColor->height = msg->height; pointerColor->lengthAndMask = msg->lengthAndMask; pointerColor->lengthXorMask = msg->lengthXorMask; pointerColor->xorMaskData = msg->xorMaskData; pointerColor->andMaskData = msg->andMaskData; pointerCached.cacheIndex = pointerColor->cacheIndex; 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; } shadow_client_free_queued_message(message); return 1; }
void* shadow_client_thread(rdpShadowClient* client) { DWORD status; DWORD nCount; wMessage message; wMessage pointerPositionMsg; wMessage pointerAlphaMsg; wMessage audioVolumeMsg; HANDLE events[32]; HANDLE ClientEvent; HANDLE ChannelEvent; void* UpdateSubscriber; HANDLE UpdateEvent; freerdp_peer* peer; rdpContext* context; rdpSettings* settings; rdpShadowServer* server; rdpShadowScreen* screen; rdpShadowEncoder* encoder; rdpShadowSubsystem* subsystem; wMessageQueue* MsgQueue = client->MsgQueue; server = client->server; screen = server->screen; encoder = client->encoder; subsystem = server->subsystem; context = (rdpContext*) client; peer = context->peer; settings = peer->settings; peer->Capabilities = shadow_client_capabilities; peer->PostConnect = shadow_client_post_connect; peer->Activate = shadow_client_activate; shadow_input_register_callbacks(peer->input); peer->Initialize(peer); peer->update->RefreshRect = (pRefreshRect)shadow_client_refresh_rect; peer->update->SuppressOutput = (pSuppressOutput)shadow_client_suppress_output; peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge)shadow_client_surface_frame_acknowledge; if ((!client->vcm) || (!subsystem->updateEvent)) goto out; UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent); if (!UpdateSubscriber) goto out; UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber); ClientEvent = peer->GetEventHandle(peer); ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm); while (1) { nCount = 0; events[nCount++] = UpdateEvent; events[nCount++] = ClientEvent; events[nCount++] = ChannelEvent; events[nCount++] = MessageQueue_Event(MsgQueue); status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0) { if (client->activated) { int index; int numRects = 0; const RECTANGLE_16* rects; int width, height; /* Check resize */ shadow_client_calc_desktop_size(server, &width, &height); if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height) { /* Screen size changed, do resize */ settings->DesktopWidth = width; settings->DesktopHeight = height; /** * 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; /* Send Resize */ peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */ region16_clear(&(client->invalidRegion)); WLog_ERR(TAG, "Client from %s is resized (%dx%d@%d)", peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth); } else { /* Send frame */ rects = region16_rects(&(subsystem->invalidRegion), &numRects); for (index = 0; index < numRects; index++) { region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); } shadow_client_send_surface_update(client); } } /* * The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event. * It's not cared currently. */ (void)shadow_multiclient_consume(UpdateSubscriber); } if (WaitForSingleObject(ClientEvent, 0) == WAIT_OBJECT_0) { if (!peer->CheckFileDescriptor(peer)) { WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); break; } } if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0) { if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm)) { WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure"); break; } } if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0) { /* Drain messages. Pointer update could be accumulated. */ pointerPositionMsg.id = 0; pointerPositionMsg.Free= NULL; pointerAlphaMsg.id = 0; pointerAlphaMsg.Free = NULL; audioVolumeMsg.id = 0; audioVolumeMsg.Free = NULL; while (MessageQueue_Peek(MsgQueue, &message, TRUE)) { if (message.id == WMQ_QUIT) { break; } switch(message.id) { case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID: /* Abandon previous message */ shadow_client_free_queued_message(&pointerPositionMsg); CopyMemory(&pointerPositionMsg, &message, sizeof(wMessage)); break; case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID: /* Abandon previous message */ shadow_client_free_queued_message(&pointerAlphaMsg); CopyMemory(&pointerAlphaMsg, &message, sizeof(wMessage)); break; case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID: /* Abandon previous message */ shadow_client_free_queued_message(&audioVolumeMsg); CopyMemory(&audioVolumeMsg, &message, sizeof(wMessage)); break; default: shadow_client_subsystem_process_message(client, &message); break; } } if (message.id == WMQ_QUIT) { /* Release stored message */ shadow_client_free_queued_message(&pointerPositionMsg); shadow_client_free_queued_message(&pointerAlphaMsg); shadow_client_free_queued_message(&audioVolumeMsg); break; } else { /* Process accumulated messages if needed */ if (pointerPositionMsg.id) { shadow_client_subsystem_process_message(client, &pointerPositionMsg); } if (pointerAlphaMsg.id) { shadow_client_subsystem_process_message(client, &pointerAlphaMsg); } if (audioVolumeMsg.id) { shadow_client_subsystem_process_message(client, &audioVolumeMsg); } } } } /* Free channels early because we establish channels in post connect */ shadow_client_channels_free(client); if (UpdateSubscriber) { shadow_multiclient_release_subscriber(UpdateSubscriber); UpdateSubscriber = NULL; } if (peer->connected && subsystem->ClientDisconnect) { subsystem->ClientDisconnect(subsystem, client); } out: peer->Disconnect(peer); freerdp_peer_context_free(peer); freerdp_peer_free(peer); ExitThread(0); return NULL; }
static void* shadow_client_thread(rdpShadowClient* client) { DWORD status; DWORD nCount; wMessage message; wMessage pointerPositionMsg; wMessage pointerAlphaMsg; wMessage audioVolumeMsg; HANDLE events[32]; HANDLE ClientEvent; HANDLE ChannelEvent; void* UpdateSubscriber; HANDLE UpdateEvent; freerdp_peer* peer; rdpContext* context; rdpSettings* settings; rdpShadowServer* server; rdpShadowScreen* screen; rdpShadowEncoder* encoder; rdpShadowSubsystem* subsystem; wMessageQueue* MsgQueue = client->MsgQueue; /* This should only be visited in client thread */ SHADOW_GFX_STATUS gfxstatus; gfxstatus.gfxOpened = FALSE; gfxstatus.gfxSurfaceCreated = FALSE; server = client->server; screen = server->screen; encoder = client->encoder; subsystem = server->subsystem; context = (rdpContext*) client; peer = context->peer; settings = peer->settings; peer->Capabilities = shadow_client_capabilities; peer->PostConnect = shadow_client_post_connect; peer->Activate = shadow_client_activate; peer->Logon = shadow_client_logon; shadow_input_register_callbacks(peer->input); peer->Initialize(peer); peer->update->RefreshRect = (pRefreshRect)shadow_client_refresh_rect; peer->update->SuppressOutput = (pSuppressOutput)shadow_client_suppress_output; peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge)shadow_client_surface_frame_acknowledge; if ((!client->vcm) || (!subsystem->updateEvent)) goto out; UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent); if (!UpdateSubscriber) goto out; UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber); ClientEvent = peer->GetEventHandle(peer); ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm); while (1) { nCount = 0; events[nCount++] = UpdateEvent; events[nCount++] = ClientEvent; events[nCount++] = ChannelEvent; events[nCount++] = MessageQueue_Event(MsgQueue); status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); 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 */ if (!shadow_client_send_resize(client, &gfxstatus)) { WLog_ERR(TAG, "Failed to send resize message"); break; } } else { /* Send frame */ if (!shadow_client_send_surface_update(client, &gfxstatus)) { WLog_ERR(TAG, "Failed to send surface update"); break; } } } else { /* Our client don't receive graphic updates. Just save the invalid region */ if (!shadow_client_no_surface_update(client, &gfxstatus)) { WLog_ERR(TAG, "Failed to handle surface update"); break; } } /* * The return value of shadow_multiclient_consume is whether or not * the subscriber really consumes the event. It's not cared currently. */ (void)shadow_multiclient_consume(UpdateSubscriber); } if (WaitForSingleObject(ClientEvent, 0) == WAIT_OBJECT_0) { if (!peer->CheckFileDescriptor(peer)) { WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); break; } if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, "drdynvc")) { /* Dynamic channel status may have been changed after processing */ if (WTSVirtualChannelManagerGetDrdynvcState(client->vcm) == 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 (WTSVirtualChannelManagerGetDrdynvcState(client->vcm) == DRDYNVC_STATE_READY) { /* Init RDPGFX dynamic channel */ if (settings->SupportGraphicsPipeline && client->rdpgfx && !gfxstatus.gfxOpened) { if (!client->rdpgfx->Open(client->rdpgfx)) { WLog_WARN(TAG, "Failed to open GraphicsPipeline"); settings->SupportGraphicsPipeline = FALSE; } client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge; client->rdpgfx->QoeFrameAcknowledge = shadow_client_rdpgfx_qoe_frame_acknowledge; gfxstatus.gfxOpened = TRUE; WLog_INFO(TAG, "Gfx Pipeline Opened"); } } } } if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0) { if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm)) { WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure"); break; } } if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0) { /* Drain messages. Pointer update could be accumulated. */ pointerPositionMsg.id = 0; pointerPositionMsg.Free= NULL; pointerAlphaMsg.id = 0; pointerAlphaMsg.Free = NULL; audioVolumeMsg.id = 0; audioVolumeMsg.Free = NULL; while (MessageQueue_Peek(MsgQueue, &message, TRUE)) { if (message.id == WMQ_QUIT) { break; } switch(message.id) { case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID: /* Abandon previous message */ shadow_client_free_queued_message(&pointerPositionMsg); CopyMemory(&pointerPositionMsg, &message, sizeof(wMessage)); break; case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID: /* Abandon previous message */ shadow_client_free_queued_message(&pointerAlphaMsg); CopyMemory(&pointerAlphaMsg, &message, sizeof(wMessage)); break; case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID: /* Abandon previous message */ shadow_client_free_queued_message(&audioVolumeMsg); CopyMemory(&audioVolumeMsg, &message, sizeof(wMessage)); break; default: shadow_client_subsystem_process_message(client, &message); break; } } if (message.id == WMQ_QUIT) { /* Release stored message */ shadow_client_free_queued_message(&pointerPositionMsg); shadow_client_free_queued_message(&pointerAlphaMsg); shadow_client_free_queued_message(&audioVolumeMsg); break; } else { /* Process accumulated messages if needed */ if (pointerPositionMsg.id) { shadow_client_subsystem_process_message(client, &pointerPositionMsg); } if (pointerAlphaMsg.id) { shadow_client_subsystem_process_message(client, &pointerAlphaMsg); } if (audioVolumeMsg.id) { shadow_client_subsystem_process_message(client, &audioVolumeMsg); } } } } /* Free channels early because we establish channels in post connect */ if (gfxstatus.gfxOpened) { if (gfxstatus.gfxSurfaceCreated) { if (!shadow_client_rdpgfx_release_surface(client)) WLog_WARN(TAG, "GFX release surface failure!"); } (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); freerdp_peer_context_free(peer); freerdp_peer_free(peer); ExitThread(0); return NULL; }