예제 #1
0
파일: wf_rdpsnd.c 프로젝트: kpsone/FreeRDP
BOOL wf_peer_rdpsnd_init(wfPeerContext* context)
{
	wfInfo* wfi;
	
	wfi = wf_info_get_instance();

	wfi->snd_mutex = CreateMutex(NULL, FALSE, NULL);
	context->rdpsnd = rdpsnd_server_context_new(context->vcm);
	context->rdpsnd->data = context;

	context->rdpsnd->server_formats = test_audio_formats;
	context->rdpsnd->num_server_formats =
			sizeof(test_audio_formats) / sizeof(test_audio_formats[0]);

	context->rdpsnd->src_format.wFormatTag = 1;
	context->rdpsnd->src_format.nChannels = 2;
	context->rdpsnd->src_format.nSamplesPerSec = 44100;
	context->rdpsnd->src_format.wBitsPerSample = 16;

	context->rdpsnd->Activated = wf_peer_rdpsnd_activated;

	context->rdpsnd->Initialize(context->rdpsnd);

	wf_rdpsnd_set_latest_peer(context);

	wfi->snd_stop = FALSE;
	return TRUE;
}
예제 #2
0
FREERDP_API UINT32 wfreerdp_server_num_peers()
{
	wfInfo* wfi;

	wfi = wf_info_get_instance();
	return wfi->peerCount;
}
예제 #3
0
파일: wf_rdpsnd.c 프로젝트: kpsone/FreeRDP
int wf_rdpsnd_lock()
{
	DWORD dRes;
	wfInfo* wfi;

	wfi = wf_info_get_instance();

	dRes = WaitForSingleObject(wfi->snd_mutex, INFINITE);

	switch (dRes)
	{
	case WAIT_ABANDONED:
	case WAIT_OBJECT_0:
		return TRUE;
		break;

	case WAIT_TIMEOUT:
		return FALSE;
		break;

	case WAIT_FAILED:
		printf("wf_rdpsnd_lock failed with 0x%08X\n", GetLastError());
		return -1;
		break;
	}

	return -1;
}
예제 #4
0
DWORD WINAPI wf_server_main_loop(LPVOID lpParam)
{
	int i, fds;
	int rcount;
	int max_fds;
	void* rfds[32];
	fd_set rfds_set;
	freerdp_listener* instance;
	wfInfo* wfi;

	wfi = wf_info_get_instance();
	wfi->force_all_disconnect = FALSE;

	ZeroMemory(rfds, sizeof(rfds));
	instance = (freerdp_listener*) lpParam;

	while(wfi->force_all_disconnect == FALSE)
	{
		rcount = 0;

		if (instance->GetFileDescriptor(instance, rfds, &rcount) != TRUE)
		{
			printf("Failed to get FreeRDP file descriptor\n");
			break;
		}

		max_fds = 0;
		FD_ZERO(&rfds_set);

		for (i = 0; i < rcount; i++)
		{
			fds = (int)(long)(rfds[i]);

			if (fds > max_fds)
				max_fds = fds;

			FD_SET(fds, &rfds_set);
		}

		if (max_fds == 0)
			break;

		
		select(max_fds + 1, &rfds_set, NULL, NULL, NULL);

		if (instance->CheckFileDescriptor(instance) != TRUE)
		{
			printf("Failed to check FreeRDP file descriptor\n");
			break;
		}
	}

	printf("wf_server_main_loop terminating\n");

	instance->Close(instance);

	return 0;
}
예제 #5
0
void set_screen_id(int id)
{
	wfInfo* wfi;

	wfi = wf_info_get_instance();
	wfi->screenID = id;

	return;
}
예제 #6
0
BOOL wfreerdp_server_stop(wfServer* server)
{
	wfInfo* wfi;

	wfi = wf_info_get_instance();
	WLog_INFO(TAG, "Stopping server");
	wfi->force_all_disconnect = TRUE;
	server->instance->Close(server->instance);
	return TRUE;
}
예제 #7
0
int wf_directsound_activate(RdpsndServerContext* context)
{
	HRESULT hr;
	wfInfo* wfi;
	
	LPDIRECTSOUNDCAPTUREBUFFER  pDSCB;

	wfi = wf_info_get_instance();
	if (!wfi)
	{
		WLog_ERR(TAG, "Failed to wfi instance");
		return 1;
	}
	WLog_DBG(TAG, "RDPSND (direct sound) Activated");
	hr = DirectSoundCaptureCreate8(NULL, &cap, NULL);

	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to create sound capture device");
		return 1;
	}

	WLog_INFO(TAG, "Created sound capture device");
	dscbd.dwSize = sizeof(DSCBUFFERDESC);
	dscbd.dwFlags = 0;
	dscbd.dwBufferBytes = wfi->agreed_format->nAvgBytesPerSec;
	dscbd.dwReserved = 0;
	dscbd.lpwfxFormat = wfi->agreed_format;
	dscbd.dwFXCount = 0;
	dscbd.lpDSCFXDesc = NULL;

	hr = cap->lpVtbl->CreateCaptureBuffer(cap, &dscbd, &pDSCB, NULL);

	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to create capture buffer");
	}

	WLog_INFO(TAG, "Created capture buffer");
	hr = pDSCB->lpVtbl->QueryInterface(pDSCB, &IID_IDirectSoundCaptureBuffer8, (LPVOID*)&capBuf);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to QI capture buffer");
	}
	WLog_INFO(TAG, "Created IDirectSoundCaptureBuffer8");
	pDSCB->lpVtbl->Release(pDSCB);
	lastPos = 0;

	CreateThread(NULL, 0, wf_rdpsnd_directsound_thread, latestPeer, 0, NULL);

	return 0;
}
예제 #8
0
int wf_rdpsnd_unlock()
{
	wfInfo* wfi;
	wfi = wf_info_get_instance();

	if (ReleaseMutex(wfi->snd_mutex) == 0)
	{
		WLog_DBG(TAG, "wf_rdpsnd_unlock failed with 0x%08lX", GetLastError());
		return -1;
	}

	return TRUE;
}
예제 #9
0
static void wf_peer_rdpsnd_activated(RdpsndServerContext* context)
{
	wfInfo* wfi;
	int i, j;

	wfi = wf_info_get_instance();

	wfi->agreed_format = NULL;
	WLog_DBG(TAG, "Client supports the following %d formats:", context->num_client_formats);

	for (i = 0; i < context->num_client_formats; i++)
	{
		//TODO: improve the way we agree on a format
		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))
			{
				WLog_DBG(TAG, "agreed on format!");
				wfi->agreed_format = (AUDIO_FORMAT*) &context->server_formats[j];
				break;
			}
		}
		if (wfi->agreed_format != NULL)
			break;
		
	}
	
	if (wfi->agreed_format == NULL)
	{
		WLog_ERR(TAG, "Could not agree on a audio format with the server");
		return;
	}
	
	context->SelectFormat(context, i);
	context->SetVolume(context, 0x7FFF, 0x7FFF);

#ifdef WITH_RDPSND_DSOUND

	wf_directsound_activate(context);

#else

	wf_wasapi_activate(context);

#endif
	
}
예제 #10
0
파일: wf_input.c 프로젝트: FreeRDP/FreeRDP
BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
	if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
	{
		INPUT mouse_event;
		ZeroMemory(&mouse_event, sizeof(INPUT));
		mouse_event.type = INPUT_MOUSE;

		if (flags & PTR_FLAGS_MOVE)
		{
			float width, height;
			wfInfo* wfi;
			wfi = wf_info_get_instance();

			if (!wfi)
				return FALSE;

			//width and height of primary screen (even in multimon setups
			width = (float) GetSystemMetrics(SM_CXSCREEN);
			height = (float) GetSystemMetrics(SM_CYSCREEN);
			x += wfi->servscreen_xoffset;
			y += wfi->servscreen_yoffset;
			mouse_event.mi.dx = (LONG)((float) x * (65535.0f / width));
			mouse_event.mi.dy = (LONG)((float) y * (65535.0f / height));
			mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
			SendInput(1, &mouse_event, sizeof(INPUT));
		}

		mouse_event.mi.dx = mouse_event.mi.dy = mouse_event.mi.dwFlags = 0;

		if (flags & PTR_XFLAGS_DOWN)
			mouse_event.mi.dwFlags |= MOUSEEVENTF_XDOWN;
		else
			mouse_event.mi.dwFlags |= MOUSEEVENTF_XUP;

		if (flags & PTR_XFLAGS_BUTTON1)
			mouse_event.mi.mouseData = XBUTTON1;
		else if (flags & PTR_XFLAGS_BUTTON2)
			mouse_event.mi.mouseData = XBUTTON2;

		SendInput(1, &mouse_event, sizeof(INPUT));
	}
	else
	{
		wf_peer_mouse_event(input, flags, x, y);
	}

	return TRUE;
}
예제 #11
0
BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    wfInfo * wfi;

    wfi = wf_info_get_instance();

    if(_IDcount == wfi->screenID)
    {
        wfi->servscreen_xoffset = lprcMonitor->left;
        wfi->servscreen_yoffset = lprcMonitor->top;
    }

    _IDcount++;

    return TRUE;
}
예제 #12
0
FREERDP_API BOOL wfreerdp_server_peer_is_authenticated(int pId)
{
	wfInfo* wfi;
	freerdp_peer* peer;

	wfi = wf_info_get_instance();
	peer = wfi->peers[pId];

	if (peer)
	{
		return peer->authenticated;
	}
	else
	{
		return FALSE;
	}
}
예제 #13
0
파일: wf_peer.c 프로젝트: achrafweb/FreeRDP
BOOL wf_peer_context_new(freerdp_peer* client, wfPeerContext* context)
{
	if (!(context->info = wf_info_get_instance()))
		return FALSE;

	context->vcm = WTSOpenServerA((LPSTR) client->context);

	if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
		return FALSE;

	if (!wf_info_peer_register(context->info, context))
	{
		WTSCloseServer(context->vcm);
		context->vcm = NULL;
		return FALSE;
	}

	return TRUE;
}
예제 #14
0
FREERDP_API BOOL wfreerdp_server_peer_is_local(int pId)
{
	wfInfo* wfi;
	freerdp_peer* peer;

	wfi = wf_info_get_instance();
	if (!wfi)
		return FALSE;
	peer = wfi->peers[pId];

	if (peer)
	{
		return peer->local;
	}
	else
	{
		return FALSE;
	}
}
예제 #15
0
FREERDP_API UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t * dstStr)
{
	wfInfo* wfi;
	freerdp_peer* peer;

	wfi = wf_info_get_instance();
	peer = wfi->peers[pId];

	if (peer)
	{
		UINT32 sLen;

		sLen = strnlen_s(peer->hostname, 50);
		swprintf(dstStr, 50, L"%hs", peer->hostname);
		return sLen;
	}
	else
	{
		printf("nonexistent peer id=%d\n", pId);
		return 0;
	}
}
예제 #16
0
BOOL wf_peer_rdpsnd_init(wfPeerContext* context)
{
	wfInfo* wfi = wf_info_get_instance();

	if (!wfi)
		return FALSE;

	if (!(wfi->snd_mutex = CreateMutex(NULL, FALSE, NULL)))
		return FALSE;

	context->rdpsnd = rdpsnd_server_context_new(context->vcm);
	context->rdpsnd->rdpcontext = &context->_p;
	context->rdpsnd->data = context;
	context->rdpsnd->num_server_formats = server_rdpsnd_get_formats(&context->rdpsnd->server_formats);

	if (context->rdpsnd->num_server_formats > 0)
		context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];

	context->rdpsnd->Activated = wf_peer_rdpsnd_activated;
	context->rdpsnd->Initialize(context->rdpsnd, TRUE);
	wf_rdpsnd_set_latest_peer(context);
	wfi->snd_stop = FALSE;
	return TRUE;
}
예제 #17
0
void wf_peer_context_new(freerdp_peer* client, wfPeerContext* context)
{
	context->info = wf_info_get_instance();
	context->vcm = WTSOpenServerA((LPSTR) client->context);
	wf_info_peer_register(context->info, context);
}
예제 #18
0
DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam)
{
	HRESULT hr;
	DWORD beg = 0;
	DWORD end = 0;
	DWORD diff, rate;
	wfPeerContext* context;
	wfInfo* wfi;

	VOID* pbCaptureData  = NULL;
	DWORD dwCaptureLength = 0;
	VOID* pbCaptureData2 = NULL;
	DWORD dwCaptureLength2 = 0;
	VOID* pbPlayData   = NULL;
	DWORD dwReadPos = 0;
	LONG lLockSize = 0;

	wfi = wf_info_get_instance();

	context = (wfPeerContext*)lpParam;
	rate = 1000 / 24;

	_tprintf(_T("Trying to start capture\n"));
	hr = capBuf->lpVtbl->Start(capBuf, DSCBSTART_LOOPING);
	if (FAILED(hr))
	{
		_tprintf(_T("Failed to start capture\n"));
	}
	_tprintf(_T("Capture started\n"));

	while (1)
	{

		end = GetTickCount();
		diff = end - beg;

		if (diff < rate)
		{
			Sleep(rate - diff);
		}

		beg = GetTickCount();

		if (wf_rdpsnd_lock() > 0)
		{
			//check for main exit condition
			if (wfi->snd_stop == TRUE)
			{
				wf_rdpsnd_unlock();
				break;
			}

			hr = capBuf->lpVtbl->GetCurrentPosition(capBuf, NULL, &dwReadPos);
			if (FAILED(hr))
			{
				_tprintf(_T("Failed to get read pos\n"));
				wf_rdpsnd_unlock();
				break;
			}

			lLockSize = dwReadPos - lastPos;//dscbd.dwBufferBytes;
			if (lLockSize < 0) lLockSize += dscbd.dwBufferBytes;

			//printf("Last, read, lock = [%d, %d, %d]\n", lastPos, dwReadPos, lLockSize);

			if (lLockSize == 0) 
			{
				wf_rdpsnd_unlock();
				continue;
			}

			
			hr = capBuf->lpVtbl->Lock(capBuf, lastPos, lLockSize, &pbCaptureData, &dwCaptureLength, &pbCaptureData2, &dwCaptureLength2, 0L);
			if (FAILED(hr))
			{
				_tprintf(_T("Failed to lock sound capture buffer\n"));
				wf_rdpsnd_unlock();
				break;
			}

			//fwrite(pbCaptureData, 1, dwCaptureLength, pFile);
			//fwrite(pbCaptureData2, 1, dwCaptureLength2, pFile);

			//FIXME: frames = bytes/(bytespersample * channels)
		
			context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData, dwCaptureLength/4, (UINT16)(beg & 0xffff));
			context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData2, dwCaptureLength2/4, (UINT16)(beg & 0xffff));


			hr = capBuf->lpVtbl->Unlock(capBuf, pbCaptureData, dwCaptureLength, pbCaptureData2, dwCaptureLength2);
			if (FAILED(hr))
			{
				_tprintf(_T("Failed to unlock sound capture buffer\n"));
				wf_rdpsnd_unlock();
				return 0;
			}

			//TODO keep track of location in buffer
			lastPos += dwCaptureLength;
			lastPos %= dscbd.dwBufferBytes;
			lastPos += dwCaptureLength2;
			lastPos %= dscbd.dwBufferBytes;

			wf_rdpsnd_unlock();
		}

		
	}

	_tprintf(_T("Trying to stop sound capture\n"));
	hr = capBuf->lpVtbl->Stop(capBuf);
	if (FAILED(hr))
	{
		_tprintf(_T("Failed to stop capture\n"));
	}
	_tprintf(_T("Capture stopped\n"));
	capBuf->lpVtbl->Release(capBuf);
	cap->lpVtbl->Release(cap);

	lastPos = 0;

	return 0;
}
예제 #19
0
파일: wf_wasapi.c 프로젝트: BUGgs/FreeRDP
DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
{
	IMMDeviceEnumerator *pEnumerator = NULL;
	IMMDevice *pDevice = NULL;
	IAudioClient *pAudioClient = NULL;
	IAudioCaptureClient *pCaptureClient = NULL;
	WAVEFORMATEX *pwfx = NULL;
	HRESULT hr;
	REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
	REFERENCE_TIME hnsActualDuration;
	UINT32 bufferFrameCount;
	UINT32 numFramesAvailable;
	UINT32 packetLength = 0;
	UINT32 dCount = 0;
	BYTE *pData;

	wfPeerContext* context;
	wfInfo* wfi;

	wfi = wf_info_get_instance();
	context = (wfPeerContext*)lpParam;

	CoInitialize(NULL);
	hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **) &pEnumerator);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to cocreate device enumerator");
		exit(1);
	}

	hr = pEnumerator->lpVtbl->GetDevice(pEnumerator, devStr, &pDevice);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to cocreate get device");
		exit(1);
	}

	hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to activate audio client");
		exit(1);
	}

	hr = pAudioClient->lpVtbl->GetMixFormat(pAudioClient, &pwfx);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to get mix format");
		exit(1);
	}

	pwfx->wFormatTag = wfi->agreed_format->wFormatTag;
	pwfx->nChannels = wfi->agreed_format->nChannels;
	pwfx->nSamplesPerSec = wfi->agreed_format->nSamplesPerSec;
	pwfx->nAvgBytesPerSec = wfi->agreed_format->nAvgBytesPerSec;
	pwfx->nBlockAlign = wfi->agreed_format->nBlockAlign;
	pwfx->wBitsPerSample = wfi->agreed_format->wBitsPerSample;
	pwfx->cbSize = wfi->agreed_format->cbSize;

	hr = pAudioClient->lpVtbl->Initialize(
		pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0,
		hnsRequestedDuration, 0, pwfx, NULL);

	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to initialize the audio client");
		exit(1);
	}

	hr = pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to get buffer size");
		exit(1);
	}

	hr = pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioCaptureClient, (void **) &pCaptureClient);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to get the capture client");
		exit(1);
	}

	hnsActualDuration = (UINT32)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;

	hr = pAudioClient->lpVtbl->Start(pAudioClient);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to start capture");
		exit(1);
	}

	dCount = 0;

	while (wfi->snd_stop == FALSE)
	{
		DWORD flags;

		Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);

		hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
		if (FAILED(hr))
		{
			WLog_ERR(TAG, "Failed to get packet length");
			exit(1);
		}

		while (packetLength != 0)
		{
			hr = pCaptureClient->lpVtbl->GetBuffer(pCaptureClient, &pData, &numFramesAvailable, &flags, NULL, NULL);
			if (FAILED(hr))
			{
				WLog_ERR(TAG, "Failed to get buffer");
				exit(1);
			}

			//Here we are writing the audio data
			//not sure if this flag is ever set by the system; msdn is not clear about it
			if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT))
				context->rdpsnd->SendSamples(context->rdpsnd, pData, packetLength, (UINT16)(GetTickCount() & 0xffff));

			hr = pCaptureClient->lpVtbl->ReleaseBuffer(pCaptureClient, numFramesAvailable);
			if (FAILED(hr))
			{
				WLog_ERR(TAG, "Failed to release buffer");
				exit(1);
			}

			hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
			if (FAILED(hr))
			{
				WLog_ERR(TAG, "Failed to get packet length");
				exit(1);
			}
		}
	}

	pAudioClient->lpVtbl->Stop(pAudioClient);
	if (FAILED(hr))
	{
		WLog_ERR(TAG, "Failed to stop audio client");
		exit(1);
	}

	CoTaskMemFree(pwfx);

	if (pEnumerator != NULL)
		pEnumerator->lpVtbl->Release(pEnumerator);

	if (pDevice != NULL)
		pDevice->lpVtbl->Release(pDevice);

	if (pAudioClient != NULL)
		pAudioClient->lpVtbl->Release(pAudioClient);

	if (pCaptureClient != NULL)
		pCaptureClient->lpVtbl->Release(pCaptureClient);

	CoUninitialize();

	return 0;
}
예제 #20
0
파일: wf_peer.c 프로젝트: woelfl-tw/FreeRDP
void wf_peer_context_new(freerdp_peer* client, wfPeerContext* context)
{
	context->info = wf_info_get_instance();
	wf_info_peer_register(context->info, context);
}
예제 #21
0
파일: wf_peer.c 프로젝트: ago1024/FreeRDP
void wf_peer_context_new(freerdp_peer* client, wfPeerContext* context)
{
	context->info = wf_info_get_instance();
	context->vcm = WTSCreateVirtualChannelManager(client);
	wf_info_peer_register(context->info, context);
}
예제 #22
0
파일: wf_input.c 프로젝트: AMV007/FreeRDP
void wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
	INPUT mouse_event;
	float width, height;

	ZeroMemory(&mouse_event, sizeof(INPUT));
	mouse_event.type = INPUT_MOUSE;

	if (flags & PTR_FLAGS_WHEEL)
	{
		mouse_event.mi.dwFlags = MOUSEEVENTF_WHEEL;
		mouse_event.mi.mouseData = flags & WheelRotationMask;

		if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
			mouse_event.mi.mouseData *= -1;

		SendInput(1, &mouse_event, sizeof(INPUT));
	}
	else
	{
		wfInfo * wfi;
		
		wfi = wf_info_get_instance();

		//width and height of primary screen (even in multimon setups
		width = (float) GetSystemMetrics(SM_CXSCREEN);
		height = (float) GetSystemMetrics(SM_CYSCREEN);

		x += wfi->servscreen_xoffset;
		y += wfi->servscreen_yoffset;

		mouse_event.mi.dx = (LONG) ((float) x * (65535.0f / width));
		mouse_event.mi.dy = (LONG) ((float) y * (65535.0f / height));
		mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;

		if (flags & PTR_FLAGS_MOVE)
		{
			mouse_event.mi.dwFlags |= MOUSEEVENTF_MOVE;
			SendInput(1, &mouse_event, sizeof(INPUT));
		}

		mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;

		if (flags & PTR_FLAGS_BUTTON1)
		{
			if (flags & PTR_FLAGS_DOWN)
				mouse_event.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
			else
				mouse_event.mi.dwFlags |= MOUSEEVENTF_LEFTUP;

			SendInput(1, &mouse_event, sizeof(INPUT));
		}
		else if (flags & PTR_FLAGS_BUTTON2)
		{
			if (flags & PTR_FLAGS_DOWN)
				mouse_event.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
			else
				mouse_event.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;

			SendInput(1, &mouse_event, sizeof(INPUT));
		}
		else if (flags & PTR_FLAGS_BUTTON3)
		{
			if (flags & PTR_FLAGS_DOWN)
				mouse_event.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
			else
				mouse_event.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;

			SendInput(1, &mouse_event, sizeof(INPUT));
		}
	}
}