Blame server/Mac/mf_peer.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Client
Packit 1fb8d4
 * FreeRDP Mac OS X Server
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <freerdp/listener.h>
Packit 1fb8d4
#include <freerdp/codec/rfx.h>
Packit 1fb8d4
#include <winpr/stream.h>
Packit 1fb8d4
#include <freerdp/peer.h>
Packit 1fb8d4
#include <freerdp/codec/color.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
Packit 1fb8d4
#include "mf_peer.h"
Packit 1fb8d4
#include "mf_info.h"
Packit 1fb8d4
#include "mf_input.h"
Packit 1fb8d4
#include "mf_event.h"
Packit 1fb8d4
#include "mf_rdpsnd.h"
Packit 1fb8d4
#include "mf_audin.h"
Packit 1fb8d4
Packit 1fb8d4
#include <mach/clock.h>
Packit 1fb8d4
#include <mach/mach.h>
Packit 1fb8d4
#include <dispatch/dispatch.h>
Packit 1fb8d4
Packit 1fb8d4
#include "OpenGL/OpenGL.h"
Packit 1fb8d4
#include "OpenGL/gl.h"
Packit 1fb8d4
Packit 1fb8d4
#include "CoreVideo/CoreVideo.h"
Packit 1fb8d4
Packit 1fb8d4
//refactor these
Packit 1fb8d4
static int info_last_sec = 0;
Packit 1fb8d4
static int info_last_nsec = 0;
Packit 1fb8d4
Packit 1fb8d4
static dispatch_source_t info_timer;
Packit 1fb8d4
static dispatch_queue_t info_queue;
Packit 1fb8d4
Packit 1fb8d4
static mfEventQueue* info_event_queue;
Packit 1fb8d4
Packit 1fb8d4
static CGLContextObj glContext;
Packit 1fb8d4
static CGContextRef bmp;
Packit 1fb8d4
static CGImageRef img;
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount)
Packit 1fb8d4
{
Packit 1fb8d4
	if (info_event_queue->pipe_fd[0] == -1)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	rfds[*rcount] = (void*)(long) info_event_queue->pipe_fd[0];
Packit 1fb8d4
	(*rcount)++;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
static void mf_peer_rfx_update(freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	//check
Packit 1fb8d4
	mfInfo* mfi = mf_info_get_instance();
Packit 1fb8d4
	mf_info_find_invalid_region(mfi);
Packit 1fb8d4
Packit 1fb8d4
	if (mf_info_have_invalid_region(mfi) == false)
Packit 1fb8d4
	{
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	long width;
Packit 1fb8d4
	long height;
Packit 1fb8d4
	int pitch;
Packit 1fb8d4
	BYTE* dataBits = NULL;
Packit 1fb8d4
	mf_info_getScreenData(mfi, &width, &height, &dataBits, &pitch);
Packit 1fb8d4
	mf_info_clear_invalid_region(mfi);
Packit 1fb8d4
	//encode
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	RFX_RECT rect;
Packit 1fb8d4
	rdpUpdate* update;
Packit 1fb8d4
	mfPeerContext* mfp;
Packit 1fb8d4
	SURFACE_BITS_COMMAND cmd = { 0 };
Packit 1fb8d4
	update = client->update;
Packit 1fb8d4
	mfp = (mfPeerContext*) client->context;
Packit 1fb8d4
	s = mfp->s;
Packit 1fb8d4
	Stream_Clear(s);
Packit 1fb8d4
	Stream_SetPosition(s, 0);
Packit 1fb8d4
	UINT32 x = mfi->invalid.x / mfi->scale;
Packit 1fb8d4
	UINT32 y = mfi->invalid.y / mfi->scale;
Packit 1fb8d4
	rect.x = 0;
Packit 1fb8d4
	rect.y = 0;
Packit 1fb8d4
	rect.width = width;
Packit 1fb8d4
	rect.height = height;
Packit 1fb8d4
	mfp->rfx_context->width = mfi->servscreen_width;
Packit 1fb8d4
	mfp->rfx_context->height = mfi->servscreen_height;
Packit 1fb8d4
Packit 1fb8d4
	if (!(rfx_compose_message(mfp->rfx_context, s, &rect, 1,
Packit 1fb8d4
	                          (BYTE*) dataBits, rect.width, rect.height, pitch)))
Packit 1fb8d4
	{
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	cmd.destLeft = x;
Packit 1fb8d4
	cmd.destTop = y;
Packit 1fb8d4
	cmd.destRight = x + rect.width;
Packit 1fb8d4
	cmd.destBottom = y + rect.height;
Packit 1fb8d4
	cmd.bmp.bpp = 32;
Packit 1fb8d4
	cmd.bmp.codecID = 3;
Packit 1fb8d4
	cmd.bmp.width = rect.width;
Packit 1fb8d4
	cmd.bmp.height = rect.height;
Packit 1fb8d4
	cmd.bmp.bitmapDataLength = Stream_GetPosition(s);
Packit 1fb8d4
	cmd.bmp.bitmapData = Stream_Buffer(s);
Packit 1fb8d4
	//send
Packit 1fb8d4
	update->SurfaceBits(update->context, &cmd);
Packit 1fb8d4
	//clean up... maybe?
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_check_fds(freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	mfPeerContext* context = (mfPeerContext*) client->context;
Packit 1fb8d4
	mfEvent* event;
Packit 1fb8d4
Packit 1fb8d4
	if (context->activated == FALSE)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	event = mf_event_peek(info_event_queue);
Packit 1fb8d4
Packit 1fb8d4
	if (event != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_REGION)
Packit 1fb8d4
		{
Packit 1fb8d4
		}
Packit 1fb8d4
		else if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK)
Packit 1fb8d4
		{
Packit 1fb8d4
			event = mf_event_pop(info_event_queue);
Packit 1fb8d4
			mf_peer_rfx_update(client);
Packit 1fb8d4
			mf_event_free(event);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* Called when we have a new peer connecting */
Packit 1fb8d4
static BOOL mf_peer_context_new(freerdp_peer* client, mfPeerContext* context)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!(context->info = mf_info_get_instance()))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!(context->rfx_context = rfx_context_new(TRUE)))
Packit 1fb8d4
		goto fail_rfx_context;
Packit 1fb8d4
Packit 1fb8d4
	context->rfx_context->mode = RLGR3;
Packit 1fb8d4
	context->rfx_context->width = client->settings->DesktopWidth;
Packit 1fb8d4
	context->rfx_context->height = client->settings->DesktopHeight;
Packit 1fb8d4
	rfx_context_set_pixel_format(context->rfx_context, PIXEL_FORMAT_BGRA32);
Packit 1fb8d4
Packit 1fb8d4
	if (!(context->s = Stream_New(NULL, 0xFFFF)))
Packit 1fb8d4
		goto fail_stream_new;
Packit 1fb8d4
Packit 1fb8d4
	context->vcm = WTSOpenServerA((LPSTR) client->context);
Packit 1fb8d4
Packit 1fb8d4
	if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
Packit 1fb8d4
		goto fail_open_server;
Packit 1fb8d4
Packit 1fb8d4
	mf_info_peer_register(context->info, context);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
fail_open_server:
Packit 1fb8d4
	Stream_Free(context->s, TRUE);
Packit 1fb8d4
	context->s = NULL;
Packit 1fb8d4
fail_stream_new:
Packit 1fb8d4
	rfx_context_free(context->rfx_context);
Packit 1fb8d4
	context->rfx_context = NULL;
Packit 1fb8d4
fail_rfx_context:
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* Called after a peer disconnects */
Packit 1fb8d4
static void mf_peer_context_free(freerdp_peer* client, mfPeerContext* context)
Packit 1fb8d4
{
Packit 1fb8d4
	if (context)
Packit 1fb8d4
	{
Packit 1fb8d4
		mf_info_peer_unregister(context->info, context);
Packit 1fb8d4
		dispatch_suspend(info_timer);
Packit 1fb8d4
		Stream_Free(context->s, TRUE);
Packit 1fb8d4
		rfx_context_free(context->rfx_context);
Packit 1fb8d4
		//nsc_context_free(context->nsc_context);
Packit 1fb8d4
#ifdef CHANNEL_AUDIN_SERVER
Packit 1fb8d4
Packit 1fb8d4
		if (context->audin)
Packit 1fb8d4
			audin_server_context_free(context->audin);
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef CHANNEL_RDPSND_SERVER
Packit 1fb8d4
		mf_peer_rdpsnd_stop();
Packit 1fb8d4
Packit 1fb8d4
		if (context->rdpsnd)
Packit 1fb8d4
			rdpsnd_server_context_free(context->rdpsnd);
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
		WTSCloseServer(context->vcm);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* Called when a new client connects */
Packit 1fb8d4
static BOOL mf_peer_init(freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	client->ContextSize = sizeof(mfPeerContext);
Packit 1fb8d4
	client->ContextNew = (psPeerContextNew) mf_peer_context_new;
Packit 1fb8d4
	client->ContextFree = (psPeerContextFree) mf_peer_context_free;
Packit 1fb8d4
Packit 1fb8d4
	if (!freerdp_peer_context_new(client))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	info_event_queue = mf_event_queue_new();
Packit 1fb8d4
	info_queue = dispatch_queue_create("FreeRDP.update.timer",
Packit 1fb8d4
	                                   DISPATCH_QUEUE_SERIAL);
Packit 1fb8d4
	info_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
Packit 1fb8d4
	                                    info_queue);
Packit 1fb8d4
Packit 1fb8d4
	if (info_timer)
Packit 1fb8d4
	{
Packit 1fb8d4
		//DEBUG_WARN( "created timer\n");
Packit 1fb8d4
		dispatch_source_set_timer(info_timer, DISPATCH_TIME_NOW, 42ull * NSEC_PER_MSEC,
Packit 1fb8d4
		                          100ull * NSEC_PER_MSEC);
Packit 1fb8d4
		dispatch_source_set_event_handler(info_timer, ^
Packit 1fb8d4
		{
Packit 1fb8d4
			//DEBUG_WARN( "dispatch\n");
Packit 1fb8d4
			mfEvent* event = mf_event_new(FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK);
Packit 1fb8d4
			mf_event_push(info_event_queue, (mfEvent*) event);
Packit 1fb8d4
		}
Packit 1fb8d4
		                                 );
Packit 1fb8d4
		dispatch_resume(info_timer);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_post_connect(freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	mfPeerContext* context = (mfPeerContext*) client->context;
Packit 1fb8d4
	rdpSettings* settings = client->settings;
Packit 1fb8d4
	mfInfo* mfi = mf_info_get_instance();
Packit 1fb8d4
	mfi->scale = 1;
Packit 1fb8d4
	//mfi->servscreen_width = 2880 / mfi->scale;
Packit 1fb8d4
	//mfi->servscreen_height = 1800 / mfi->scale;
Packit 1fb8d4
	UINT32 bitsPerPixel = 32;
Packit 1fb8d4
Packit 1fb8d4
	if ((settings->DesktopWidth != mfi->servscreen_width)
Packit 1fb8d4
	    || (settings->DesktopHeight != mfi->servscreen_height))
Packit 1fb8d4
	{
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	settings->DesktopWidth = mfi->servscreen_width;
Packit 1fb8d4
	settings->DesktopHeight = mfi->servscreen_height;
Packit 1fb8d4
	settings->ColorDepth = bitsPerPixel;
Packit 1fb8d4
	client->update->DesktopResize(client->update->context);
Packit 1fb8d4
	mfi->mouse_down_left = FALSE;
Packit 1fb8d4
	mfi->mouse_down_right = FALSE;
Packit 1fb8d4
	mfi->mouse_down_other = FALSE;
Packit 1fb8d4
#ifdef CHANNEL_RDPSND_SERVER
Packit 1fb8d4
Packit 1fb8d4
	if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "rdpsnd"))
Packit 1fb8d4
	{
Packit 1fb8d4
		mf_peer_rdpsnd_init(context); /* Audio Output */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	/* Dynamic Virtual Channels */
Packit 1fb8d4
#ifdef CHANNEL_AUDIN_SERVER
Packit 1fb8d4
	mf_peer_audin_init(context); /* Audio Input */
Packit 1fb8d4
#endif
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_activate(freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	mfPeerContext* context = (mfPeerContext*) client->context;
Packit 1fb8d4
	rfx_context_reset(context->rfx_context, client->settings->DesktopWidth,
Packit 1fb8d4
	                  client->settings->DesktopHeight);
Packit 1fb8d4
	context->activated = TRUE;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_synchronize_event(rdpInput* input, UINT32 flags)
Packit 1fb8d4
{
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void mf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 down = 0x4000;
Packit 1fb8d4
	//UINT16 up = 0x8000;
Packit 1fb8d4
	bool state_down = FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (flags == down)
Packit 1fb8d4
	{
Packit 1fb8d4
		state_down = TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL mf_peer_suppress_output(rdpContext* context, BYTE allow,
Packit 1fb8d4
                                    const RECTANGLE_16* area)
Packit 1fb8d4
{
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
static void* mf_peer_main_loop(void* arg)
Packit 1fb8d4
{
Packit 1fb8d4
	int i;
Packit 1fb8d4
	int fds;
Packit 1fb8d4
	int max_fds;
Packit 1fb8d4
	int rcount;
Packit 1fb8d4
	void* rfds[32];
Packit 1fb8d4
	fd_set rfds_set;
Packit 1fb8d4
	mfPeerContext* context;
Packit 1fb8d4
	freerdp_peer* client = (freerdp_peer*) arg;
Packit 1fb8d4
	memset(rfds, 0, sizeof(rfds));
Packit 1fb8d4
Packit 1fb8d4
	if (!mf_peer_init(client))
Packit 1fb8d4
	{
Packit 1fb8d4
		freerdp_peer_free(client);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Initialize the real server settings here */
Packit 1fb8d4
	client->settings->CertificateFile = _strdup("server.crt");
Packit 1fb8d4
	client->settings->PrivateKeyFile = _strdup("server.key");
Packit 1fb8d4
Packit 1fb8d4
	if (!client->settings->CertificateFile || !client->settings->PrivateKeyFile)
Packit 1fb8d4
	{
Packit 1fb8d4
		freerdp_peer_free(client);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	client->settings->NlaSecurity = FALSE;
Packit 1fb8d4
	client->settings->RemoteFxCodec = TRUE;
Packit 1fb8d4
	client->settings->ColorDepth = 32;
Packit 1fb8d4
	client->settings->SuppressOutput = TRUE;
Packit 1fb8d4
	client->settings->RefreshRect = FALSE;
Packit 1fb8d4
	client->PostConnect = mf_peer_post_connect;
Packit 1fb8d4
	client->Activate = mf_peer_activate;
Packit 1fb8d4
	client->input->SynchronizeEvent = mf_peer_synchronize_event;
Packit 1fb8d4
	client->input->KeyboardEvent = mf_input_keyboard_event;//mf_peer_keyboard_event;
Packit 1fb8d4
	client->input->UnicodeKeyboardEvent = mf_peer_unicode_keyboard_event;
Packit 1fb8d4
	client->input->MouseEvent = mf_input_mouse_event;
Packit 1fb8d4
	client->input->ExtendedMouseEvent = mf_input_extended_mouse_event;
Packit 1fb8d4
	//client->update->RefreshRect = mf_peer_refresh_rect;
Packit 1fb8d4
	client->update->SuppressOutput = mf_peer_suppress_output;
Packit 1fb8d4
	client->Initialize(client);
Packit 1fb8d4
	context = (mfPeerContext*) client->context;
Packit 1fb8d4
Packit 1fb8d4
	while (1)
Packit 1fb8d4
	{
Packit 1fb8d4
		rcount = 0;
Packit 1fb8d4
Packit 1fb8d4
		if (client->GetFileDescriptor(client, rfds, &rcount) != TRUE)
Packit 1fb8d4
		{
Packit 1fb8d4
			break;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (mf_peer_get_fds(client, rfds, &rcount) != TRUE)
Packit 1fb8d4
		{
Packit 1fb8d4
			break;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		WTSVirtualChannelManagerGetFileDescriptor(context->vcm, rfds, &rcount);
Packit 1fb8d4
		max_fds = 0;
Packit 1fb8d4
		FD_ZERO(&rfds_set);
Packit 1fb8d4
Packit 1fb8d4
		for (i = 0; i < rcount; i++)
Packit 1fb8d4
		{
Packit 1fb8d4
			fds = (int)(long)(rfds[i]);
Packit 1fb8d4
Packit 1fb8d4
			if (fds > max_fds)
Packit 1fb8d4
				max_fds = fds;
Packit 1fb8d4
Packit 1fb8d4
			FD_SET(fds, &rfds_set);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (max_fds == 0)
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* these are not really errors */
Packit 1fb8d4
			if (!((errno == EAGAIN) ||
Packit 1fb8d4
			      (errno == EWOULDBLOCK) ||
Packit 1fb8d4
			      (errno == EINPROGRESS) ||
Packit 1fb8d4
			      (errno == EINTR))) /* signal occurred */
Packit 1fb8d4
			{
Packit 1fb8d4
				break;
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (client->CheckFileDescriptor(client) != TRUE)
Packit 1fb8d4
		{
Packit 1fb8d4
			break;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if ((mf_peer_check_fds(client)) != TRUE)
Packit 1fb8d4
		{
Packit 1fb8d4
			break;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
Packit 1fb8d4
		{
Packit 1fb8d4
			break;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	client->Disconnect(client);
Packit 1fb8d4
	freerdp_peer_context_free(client);
Packit 1fb8d4
	freerdp_peer_free(client);
Packit 1fb8d4
	return NULL;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL mf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
Packit 1fb8d4
{
Packit 1fb8d4
	pthread_t th;
Packit 1fb8d4
Packit 1fb8d4
	if (pthread_create(&th, 0, mf_peer_main_loop, client) == 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		pthread_detach(th);
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}