Blame libfreerdp/core/input.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 * Input PDUs
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@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 <winpr/crt.h>
Packit 1fb8d4
Packit 1fb8d4
#include <freerdp/input.h>
Packit 1fb8d4
#include <freerdp/log.h>
Packit 1fb8d4
Packit 1fb8d4
#include "message.h"
Packit 1fb8d4
Packit 1fb8d4
#include "input.h"
Packit 1fb8d4
Packit 1fb8d4
#define TAG FREERDP_TAG("core")
Packit 1fb8d4
Packit 1fb8d4
/* Input Events */
Packit Service 5a9772
#define INPUT_EVENT_SYNC 0x0000
Packit Service 5a9772
#define INPUT_EVENT_SCANCODE 0x0004
Packit Service 5a9772
#define INPUT_EVENT_UNICODE 0x0005
Packit Service 5a9772
#define INPUT_EVENT_MOUSE 0x8001
Packit Service 5a9772
#define INPUT_EVENT_MOUSEX 0x8002
Packit 1fb8d4
Packit Service 5a9772
#define RDP_CLIENT_INPUT_PDU_HEADER_LENGTH 4
Packit 1fb8d4
Packit 1fb8d4
static void rdp_write_client_input_pdu_header(wStream* s, UINT16 number)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT16(s, 1); /* numberEvents (2 bytes) */
Packit 1fb8d4
	Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void rdp_write_input_event_header(wStream* s, UINT32 time, UINT16 type)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT32(s, time); /* eventTime (4 bytes) */
Packit 1fb8d4
	Stream_Write_UINT16(s, type); /* messageType (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static wStream* rdp_client_input_pdu_init(rdpRdp* rdp, UINT16 type)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	s = rdp_data_pdu_init(rdp);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	rdp_write_client_input_pdu_header(s, 1);
Packit 1fb8d4
	rdp_write_input_event_header(s, 0, type);
Packit 1fb8d4
	return s;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL rdp_send_client_input_pdu(rdpRdp* rdp, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_INPUT, rdp->mcs->userId);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_write_synchronize_event(wStream* s, UINT32 flags)
Packit 1fb8d4
{
Packit Service 5a9772
	Stream_Write_UINT16(s, 0);     /* pad2Octets (2 bytes) */
Packit 1fb8d4
	Stream_Write_UINT32(s, flags); /* toggleFlags (4 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_synchronize_event(rdpInput* input, UINT32 flags)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SYNC);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_synchronize_event(s, flags);
Packit 1fb8d4
	return rdp_send_client_input_pdu(rdp, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_write_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, code);  /* keyCode (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, 0);     /* pad2Octets (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SCANCODE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_keyboard_event(s, flags, code);
Packit 1fb8d4
	return rdp_send_client_input_pdu(rdp, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_write_unicode_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, code);  /* unicodeCode (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, 0);     /* pad2Octets (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->UnicodeInput)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "Unicode input not supported by server.");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_UNICODE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_unicode_keyboard_event(s, flags, code);
Packit 1fb8d4
	return rdp_send_client_input_pdu(rdp, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_write_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, x);     /* xPos (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, y);     /* yPos (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context || !input->context->settings)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->HasHorizontalWheel)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (flags & PTR_FLAGS_HWHEEL)
Packit 1fb8d4
		{
Packit Service 5a9772
			WLog_WARN(TAG,
Packit Service 5a9772
			          "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
Packit Service 5a9772
			          ", no horizontal mouse wheel supported",
Packit 1fb8d4
			          x, y, flags);
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_mouse_event(s, flags, x, y);
Packit 1fb8d4
	return rdp_send_client_input_pdu(rdp, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_write_extended_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, x);     /* xPos (2 bytes) */
Packit Service 5a9772
	Stream_Write_UINT16(s, y);     /* yPos (2 bytes) */
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->HasExtendedMouseEvent)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_WARN(TAG,
Packit Service 5a9772
		          "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
Packit Service 5a9772
		          ", no extended mouse events supported",
Packit 1fb8d4
		          x, y, flags);
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSEX);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_extended_mouse_event(s, flags, x, y);
Packit 1fb8d4
	return rdp_send_client_input_pdu(rdp, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
Packit 1fb8d4
{
Packit 1fb8d4
	/* send a tab up like mstsc.exe */
Packit 1fb8d4
	if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* send the toggle key states */
Packit 1fb8d4
	if (!input_send_synchronize_event(input, (toggleStates & 0x1F)))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* send another tab up like mstsc.exe */
Packit 1fb8d4
	return input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_keyboard_pause_event(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	/* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
Packit 1fb8d4
	 * and pause-up sent nothing.  However, reverse engineering mstsc shows
Packit 1fb8d4
	 * it sending the following sequence:
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	/* Control down (0x1D) */
Packit Service 5a9772
	if (!input_send_keyboard_event(input, KBD_FLAGS_EXTENDED1,
Packit 1fb8d4
	                               RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* Numlock down (0x45) */
Packit Service 5a9772
	if (!input_send_keyboard_event(input, 0, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK)))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* Control up (0x1D) */
Packit Service 5a9772
	if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE | KBD_FLAGS_EXTENDED1,
Packit 1fb8d4
	                               RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* Numlock up (0x45) */
Packit 1fb8d4
	return input_send_keyboard_event(input, KBD_FLAGS_RELEASE,
Packit 1fb8d4
	                                 RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_fastpath_synchronize_event(rdpInput* input, UINT32 flags)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	/* The FastPath Synchronization eventFlags has identical values as SlowPath */
Packit Service 5a9772
	s = fastpath_input_pdu_init(rdp->fastpath, (BYTE)flags, FASTPATH_INPUT_EVENT_SYNC);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return fastpath_send_input_pdu(rdp->fastpath, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_fastpath_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	BYTE eventFlags = 0;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
Packit 1fb8d4
	eventFlags |= (flags & KBD_FLAGS_EXTENDED) ? FASTPATH_INPUT_KBDFLAGS_EXTENDED : 0;
Packit Service 5a9772
	eventFlags |= (flags & KBD_FLAGS_EXTENDED1) ? FASTPATH_INPUT_KBDFLAGS_PREFIX_E1 : 0;
Packit 1fb8d4
	s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_SCANCODE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Write_UINT8(s, code); /* keyCode (1 byte) */
Packit 1fb8d4
	return fastpath_send_input_pdu(rdp->fastpath, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_fastpath_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	BYTE eventFlags = 0;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->UnicodeInput)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "Unicode input not supported by server.");
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
Packit 1fb8d4
	s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_UNICODE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
Packit 1fb8d4
	return fastpath_send_input_pdu(rdp->fastpath, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL input_send_fastpath_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context || !input->context->settings)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->HasHorizontalWheel)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (flags & PTR_FLAGS_HWHEEL)
Packit 1fb8d4
		{
Packit Service 5a9772
			WLog_WARN(TAG,
Packit Service 5a9772
			          "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
Packit Service 5a9772
			          ", no horizontal mouse wheel supported",
Packit 1fb8d4
			          x, y, flags);
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSE);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_mouse_event(s, flags, x, y);
Packit 1fb8d4
	return fastpath_send_input_pdu(rdp->fastpath, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL input_send_fastpath_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
Packit Service 5a9772
                                                     UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->context->settings->HasExtendedMouseEvent)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_WARN(TAG,
Packit Service 5a9772
		          "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
Packit Service 5a9772
		          ", no extended mouse events supported",
Packit 1fb8d4
		          x, y, flags);
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSEX);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	input_write_extended_mouse_event(s, flags, x, y);
Packit 1fb8d4
	return fastpath_send_input_pdu(rdp->fastpath, s);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_fastpath_focus_in_event(rdpInput* input, UINT16 toggleStates)
Packit 1fb8d4
{
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	BYTE eventFlags = 0;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = fastpath_input_pdu_init_header(rdp->fastpath);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* send a tab up like mstsc.exe */
Packit 1fb8d4
	eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
Packit 1fb8d4
	Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
Packit Service 5a9772
	Stream_Write_UINT8(s, 0x0f);       /* keyCode (1 byte) */
Packit 1fb8d4
	/* send the toggle key states */
Packit 1fb8d4
	eventFlags = (toggleStates & 0x1F) | FASTPATH_INPUT_EVENT_SYNC << 5;
Packit 1fb8d4
	Stream_Write_UINT8(s, eventFlags); /* toggle state (1 byte) */
Packit 1fb8d4
	/* send another tab up like mstsc.exe */
Packit 1fb8d4
	eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
Packit 1fb8d4
	Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
Packit Service 5a9772
	Stream_Write_UINT8(s, 0x0f);       /* keyCode (1 byte) */
Packit 1fb8d4
	return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 3);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_send_fastpath_keyboard_pause_event(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	/* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
Packit 1fb8d4
	 * and pause-up sent nothing.  However, reverse engineering mstsc shows
Packit 1fb8d4
	 * it sending the following sequence:
Packit 1fb8d4
	 */
Packit 1fb8d4
	wStream* s;
Packit 1fb8d4
	const BYTE keyDownEvent = FASTPATH_INPUT_EVENT_SCANCODE << 5;
Packit Service 5a9772
	const BYTE keyUpEvent = (FASTPATH_INPUT_EVENT_SCANCODE << 5) | FASTPATH_INPUT_KBDFLAGS_RELEASE;
Packit 1fb8d4
	rdpRdp* rdp;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	rdp = input->context->rdp;
Packit 1fb8d4
	s = fastpath_input_pdu_init_header(rdp->fastpath);
Packit 1fb8d4
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* Control down (0x1D) */
Packit 1fb8d4
	Stream_Write_UINT8(s, keyDownEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
Packit 1fb8d4
	Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
Packit 1fb8d4
	/* Numlock down (0x45) */
Packit 1fb8d4
	Stream_Write_UINT8(s, keyDownEvent);
Packit 1fb8d4
	Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
Packit 1fb8d4
	/* Control up (0x1D) */
Packit 1fb8d4
	Stream_Write_UINT8(s, keyUpEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
Packit 1fb8d4
	Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
Packit 1fb8d4
	/* Numlock down (0x45) */
Packit 1fb8d4
	Stream_Write_UINT8(s, keyUpEvent);
Packit 1fb8d4
	Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
Packit 1fb8d4
	return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 4);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_sync_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT32 toggleFlags;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	Stream_Seek(s, 2);                  /* pad2Octets (2 bytes) */
Packit 1fb8d4
	Stream_Read_UINT32(s, toggleFlags); /* toggleFlags (4 bytes) */
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, toggleFlags);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_keyboard_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 keyboardFlags, keyCode;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, keyCode);       /* keyCode (2 bytes) */
Packit Service 5a9772
	Stream_Seek(s, 2);                    /* pad2Octets (2 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * Note: A lot of code in FreeRDP and in dependent projects checks the
Packit 1fb8d4
	 * KBDFLAGS_DOWN flag in order to detect a key press.
Packit 1fb8d4
	 * According to the specs only the absence of the slow-path
Packit 1fb8d4
	 * KBDFLAGS_RELEASE flag indicates a key-down event.
Packit 1fb8d4
	 * The slow-path KBDFLAGS_DOWN flag merely indicates that the key was
Packit 1fb8d4
	 * down prior to this event.
Packit 1fb8d4
	 * The checks for KBDFLAGS_DOWN only work successfully because the code
Packit 1fb8d4
	 * handling the fast-path keyboard input sets the KBDFLAGS_DOWN flag if
Packit 1fb8d4
	 * the FASTPATH_INPUT_KBDFLAGS_RELEASE flag is missing.
Packit 1fb8d4
	 * Since the same input callback is used for slow- and fast-path events
Packit 1fb8d4
	 * we have to follow that "convention" here.
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	if (keyboardFlags & KBD_FLAGS_RELEASE)
Packit 1fb8d4
		keyboardFlags &= ~KBD_FLAGS_DOWN;
Packit 1fb8d4
	else
Packit 1fb8d4
		keyboardFlags |= KBD_FLAGS_DOWN;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->KeyboardEvent, input, keyboardFlags, keyCode);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_unicode_keyboard_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 keyboardFlags, unicodeCode;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, unicodeCode);   /* unicodeCode (2 bytes) */
Packit Service 5a9772
	Stream_Seek(s, 2);                    /* pad2Octets (2 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	/* "fix" keyboardFlags - see comment in input_recv_keyboard_event() */
Packit 1fb8d4
Packit 1fb8d4
	if (keyboardFlags & KBD_FLAGS_RELEASE)
Packit 1fb8d4
		keyboardFlags &= ~KBD_FLAGS_DOWN;
Packit 1fb8d4
	else
Packit 1fb8d4
		keyboardFlags |= KBD_FLAGS_DOWN;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, keyboardFlags, unicodeCode);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_mouse_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 pointerFlags, xPos, yPos;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, xPos);         /* xPos (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, yPos);         /* yPos (2 bytes) */
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->MouseEvent, input, pointerFlags, xPos, yPos);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_extended_mouse_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 pointerFlags, xPos, yPos;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, xPos);         /* xPos (2 bytes) */
Packit Service 5a9772
	Stream_Read_UINT16(s, yPos);         /* yPos (2 bytes) */
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, pointerFlags, xPos, yPos);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL input_recv_event(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 messageType;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 6)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	Stream_Seek(s, 4);                  /* eventTime (4 bytes), ignored by the server */
Packit 1fb8d4
	Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	switch (messageType)
Packit 1fb8d4
	{
Packit 1fb8d4
		case INPUT_EVENT_SYNC:
Packit 1fb8d4
			if (!input_recv_sync_event(input, s))
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case INPUT_EVENT_SCANCODE:
Packit 1fb8d4
			if (!input_recv_keyboard_event(input, s))
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case INPUT_EVENT_UNICODE:
Packit 1fb8d4
			if (!input_recv_unicode_keyboard_event(input, s))
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case INPUT_EVENT_MOUSE:
Packit 1fb8d4
			if (!input_recv_mouse_event(input, s))
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case INPUT_EVENT_MOUSEX:
Packit 1fb8d4
			if (!input_recv_extended_mouse_event(input, s))
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit Service 5a9772
			WLog_ERR(TAG, "Unknown messageType %" PRIu16 "", messageType);
Packit 1fb8d4
			/* Each input event uses 6 bytes. */
Packit 1fb8d4
			Stream_Seek(s, 6);
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL input_recv(rdpInput* input, wStream* s)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT16 i, numberEvents;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !s)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 4)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT16(s, numberEvents); /* numberEvents (2 bytes) */
Packit Service 5a9772
	Stream_Seek(s, 2);                   /* pad2Octets (2 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	/* Each input event uses 6 exactly bytes. */
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < (size_t)(6 * numberEvents))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	for (i = 0; i < numberEvents; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!input_recv_event(input, s))
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL input_register_client_callbacks(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	rdpSettings* settings;
Packit 1fb8d4
Packit 1fb8d4
	if (!input || !input->context)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	settings = input->context->settings;
Packit 1fb8d4
Packit 1fb8d4
	if (!settings)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (settings->FastPathInput)
Packit 1fb8d4
	{
Packit 1fb8d4
		input->SynchronizeEvent = input_send_fastpath_synchronize_event;
Packit 1fb8d4
		input->KeyboardEvent = input_send_fastpath_keyboard_event;
Packit 1fb8d4
		input->KeyboardPauseEvent = input_send_fastpath_keyboard_pause_event;
Packit 1fb8d4
		input->UnicodeKeyboardEvent = input_send_fastpath_unicode_keyboard_event;
Packit 1fb8d4
		input->MouseEvent = input_send_fastpath_mouse_event;
Packit 1fb8d4
		input->ExtendedMouseEvent = input_send_fastpath_extended_mouse_event;
Packit 1fb8d4
		input->FocusInEvent = input_send_fastpath_focus_in_event;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		input->SynchronizeEvent = input_send_synchronize_event;
Packit 1fb8d4
		input->KeyboardEvent = input_send_keyboard_event;
Packit 1fb8d4
		input->KeyboardPauseEvent = input_send_keyboard_pause_event;
Packit 1fb8d4
		input->UnicodeKeyboardEvent = input_send_unicode_keyboard_event;
Packit 1fb8d4
		input->MouseEvent = input_send_mouse_event;
Packit 1fb8d4
		input->ExtendedMouseEvent = input_send_extended_mouse_event;
Packit 1fb8d4
		input->FocusInEvent = input_send_focus_in_event;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	input->asynchronous = settings->AsyncInput;
Packit 1fb8d4
Packit 1fb8d4
	if (input->asynchronous)
Packit 1fb8d4
	{
Packit 1fb8d4
		input->proxy = input_message_proxy_new(input);
Packit 1fb8d4
Packit 1fb8d4
		if (!input->proxy)
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, flags);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->KeyboardEvent, input, flags, code);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, UINT32 rdp_scancode)
Packit 1fb8d4
{
Packit Service 5a9772
	return freerdp_input_send_keyboard_event(
Packit Service 5a9772
	    input,
Packit Service 5a9772
	    (RDP_SCANCODE_EXTENDED(rdp_scancode) ? KBD_FLAGS_EXTENDED : 0) |
Packit 1fb8d4
	        ((down) ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE),
Packit Service 5a9772
	    RDP_SCANCODE_CODE(rdp_scancode));
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, flags, code);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->MouseEvent, input, flags, x, y);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, flags, x, y);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->FocusInEvent, input, toggleStates);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL freerdp_input_send_keyboard_pause_event(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return IFCALLRESULT(TRUE, input->KeyboardPauseEvent, input);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
int input_process_events(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return input_message_queue_process_pending_messages(input);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void input_free_queued_message(void* obj)
Packit 1fb8d4
{
Packit 1fb8d4
	wMessage* msg = (wMessage*)obj;
Packit 1fb8d4
	input_message_queue_free_message(msg);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
rdpInput* input_new(rdpRdp* rdp)
Packit 1fb8d4
{
Packit 1fb8d4
	const wObject cb = { NULL, NULL, NULL, input_free_queued_message, NULL };
Packit 1fb8d4
	rdpInput* input;
Packit Service 5a9772
	input = (rdpInput*)calloc(1, sizeof(rdpInput));
Packit 1fb8d4
Packit 1fb8d4
	if (!input)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	input->queue = MessageQueue_New(&cb;;
Packit 1fb8d4
Packit 1fb8d4
	if (!input->queue)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(input);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return input;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void input_free(rdpInput* input)
Packit 1fb8d4
{
Packit 1fb8d4
	if (input != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (input->asynchronous)
Packit 1fb8d4
			input_message_proxy_free(input->proxy);
Packit 1fb8d4
Packit 1fb8d4
		MessageQueue_Free(input->queue);
Packit 1fb8d4
		free(input);
Packit 1fb8d4
	}
Packit 1fb8d4
}