Example #1
0
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;
}
Example #2
0
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;
}