Added Advanced Input Channel

This commit is contained in:
akallabeth 2022-01-19 09:14:29 +01:00 committed by akallabeth
parent b5c9a410d3
commit f45e515fcd
12 changed files with 1063 additions and 0 deletions

View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2022 Thincast Technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("ainput")
if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,13 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT ON)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "ainput" TYPE "dynamic"
DESCRIPTION "Advanced Input Virtual Channel Extension"
SPECIFICATIONS "[XXXXX]"
DEFAULT ${OPTION_DEFAULT})
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,34 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2022 Thincast Technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_client("ainput")
set(${MODULE_PREFIX}_SRCS
ainput_main.c
ainput_main.h)
include_directories(..)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
endif()
target_link_libraries(${MODULE_NAME} winpr)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")

View File

@ -0,0 +1,301 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h>
#include "ainput_main.h"
#include <freerdp/channels/log.h>
#include <freerdp/client/ainput.h>
#include <freerdp/channels/ainput.h>
#define TAG CHANNELS_TAG("ainput.client")
typedef struct AINPUT_CHANNEL_CALLBACK_ AINPUT_CHANNEL_CALLBACK;
struct AINPUT_CHANNEL_CALLBACK_
{
IWTSVirtualChannelCallback iface;
IWTSPlugin* plugin;
IWTSVirtualChannelManager* channel_mgr;
IWTSVirtualChannel* channel;
};
typedef struct AINPUT_LISTENER_CALLBACK_ AINPUT_LISTENER_CALLBACK;
struct AINPUT_LISTENER_CALLBACK_
{
IWTSListenerCallback iface;
IWTSPlugin* plugin;
IWTSVirtualChannelManager* channel_mgr;
AINPUT_CHANNEL_CALLBACK* channel_callback;
};
typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
struct AINPUT_PLUGIN_
{
IWTSPlugin iface;
AINPUT_LISTENER_CALLBACK* listener_callback;
IWTSListener* listener;
UINT32 MajorVersion;
UINT32 MinorVersion;
BOOL initialized;
};
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
{
UINT16 type;
AINPUT_PLUGIN* ainput;
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
WINPR_ASSERT(callback);
WINPR_ASSERT(data);
ainput = (AINPUT_PLUGIN*)callback->plugin;
WINPR_ASSERT(ainput);
if (Stream_GetRemainingLength(data) < 2)
return ERROR_NO_DATA;
Stream_Read_UINT16(data, type);
switch (type)
{
case MSG_AINPUT_VERSION:
if (Stream_GetRemainingLength(data) < 8)
return ERROR_NO_DATA;
Stream_Read_UINT32(data, ainput->MajorVersion);
Stream_Read_UINT32(data, ainput->MinorVersion);
break;
default:
WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
break;
}
return CHANNEL_RC_OK;
}
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
{
AINPUT_PLUGIN* ainput;
AINPUT_CHANNEL_CALLBACK* callback;
BYTE buffer[32] = { 0 };
wStream sbuffer = { 0 };
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
WINPR_ASSERT(context);
ainput = (AINPUT_PLUGIN*)context->handle;
WINPR_ASSERT(ainput);
WINPR_ASSERT(ainput->listener_callback);
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
{
WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
ainput->MajorVersion, ainput->MinorVersion);
return CHANNEL_RC_UNSUPPORTED_VERSION;
}
callback = ainput->listener_callback->channel_callback;
WINPR_ASSERT(callback);
/* Message type */
Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
/* Event data */
Stream_Write_UINT64(s, flags);
Stream_Write_INT32(s, x);
Stream_Write_INT32(s, y);
Stream_SealLength(s);
/* ainput back what we have received. AINPUT does not have any message IDs. */
WINPR_ASSERT(callback->channel);
WINPR_ASSERT(callback->channel->Write);
return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
NULL);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
free(callback);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel, BYTE* Data,
BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback)
{
AINPUT_CHANNEL_CALLBACK* callback;
AINPUT_LISTENER_CALLBACK* listener_callback = (AINPUT_LISTENER_CALLBACK*)pListenerCallback;
WINPR_ASSERT(listener_callback);
WINPR_UNUSED(Data);
WINPR_UNUSED(pbAccept);
callback = (AINPUT_CHANNEL_CALLBACK*)calloc(1, sizeof(AINPUT_CHANNEL_CALLBACK));
if (!callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
callback->iface.OnDataReceived = ainput_on_data_received;
callback->iface.OnClose = ainput_on_close;
callback->plugin = listener_callback->plugin;
callback->channel_mgr = listener_callback->channel_mgr;
callback->channel = pChannel;
listener_callback->channel_callback = callback;
*ppCallback = &callback->iface;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
{
UINT status;
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
WINPR_ASSERT(ainput);
if (ainput->initialized)
{
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AINPUT_DVC_CHANNEL_NAME);
return ERROR_INVALID_DATA;
}
ainput->listener_callback =
(AINPUT_LISTENER_CALLBACK*)calloc(1, sizeof(AINPUT_LISTENER_CALLBACK));
if (!ainput->listener_callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
ainput->listener_callback->iface.OnNewChannelConnection = ainput_on_new_channel_connection;
ainput->listener_callback->plugin = pPlugin;
ainput->listener_callback->channel_mgr = pChannelMgr;
status = pChannelMgr->CreateListener(pChannelMgr, AINPUT_DVC_CHANNEL_NAME, 0,
&ainput->listener_callback->iface, &ainput->listener);
ainput->listener->pInterface = ainput->iface.pInterface;
ainput->initialized = status == CHANNEL_RC_OK;
return status;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
if (ainput && ainput->listener_callback)
{
IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr;
if (mgr)
IFCALL(mgr->DestroyListener, mgr, ainput->listener);
}
if (ainput)
{
free(ainput->listener_callback);
free(ainput->iface.pInterface);
}
free(ainput);
return CHANNEL_RC_OK;
}
#ifdef BUILTIN_CHANNELS
#define DVCPluginEntry ainput_DVCPluginEntry
#else
#define DVCPluginEntry FREERDP_API DVCPluginEntry
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
{
UINT status = CHANNEL_RC_OK;
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "ainput");
if (!ainput)
{
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
ainput = (AINPUT_PLUGIN*)calloc(1, sizeof(AINPUT_PLUGIN));
if (!ainput || !context)
{
free(context);
free(ainput);
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
ainput->iface.Initialize = ainput_plugin_initialize;
ainput->iface.Terminated = ainput_plugin_terminated;
context->handle = (void*)ainput;
context->AInputSendInputEvent = ainput_send_input_event;
ainput->iface.pInterface = (void*)context;
status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface);
}
return status;
}

View File

@ -0,0 +1,43 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
#define FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/channels/log.h>
#define DVC_TAG CHANNELS_TAG("ainput.client")
#ifdef WITH_DEBUG_DVC
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
#else
#define DEBUG_DVC(...) \
do \
{ \
} while (0)
#endif
#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H */

View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Armin Novak <anovak@thincast.com>
# Copyright 2022 Thincast Technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("ainput")
set(${MODULE_PREFIX}_SRCS
ainput_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,418 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Advanced Input Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <freerdp/server/ainput.h>
#include <freerdp/channels/ainput.h>
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("ainput.server")
typedef struct ainput_server_
{
ainput_server_context context;
BOOL opened;
HANDLE stopEvent;
HANDLE thread;
void* ainput_channel;
DWORD SessionId;
} ainput_server;
static BOOL ainput_server_is_open(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
return ainput->thread != NULL;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_open_channel(ainput_server* ainput)
{
DWORD Error;
HANDLE hEvent;
DWORD StartTick;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
WINPR_ASSERT(ainput);
if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
ainput->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
StartTick = GetTickCount();
while (ainput->ainput_channel == NULL)
{
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
WTS_CHANNEL_OPTION_DYNAMIC);
if (ainput->ainput_channel)
break;
Error = GetLastError();
if (Error == ERROR_NOT_FOUND)
break;
if (GetTickCount() - StartTick > 5000)
break;
}
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static UINT ainput_server_send_version(ainput_server* ainput, wStream* s)
{
ULONG written;
WINPR_ASSERT(ainput);
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
if (!Stream_EnsureCapacity(s, 10))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX);
if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
{
UINT error = CHANNEL_RC_OK;
UINT64 flags;
INT32 x, y;
WINPR_ASSERT(ainput);
WINPR_ASSERT(s);
if (Stream_GetRemainingLength(s) < 16)
return ERROR_NO_DATA;
Stream_Read_UINT64(s, flags);
Stream_Read_INT32(s, x);
Stream_Read_INT32(s, y);
IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, flags, x, y);
return error;
}
static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
{
wStream* s;
void* buffer;
DWORD nCount;
HANDLE events[8] = { 0 };
BOOL ready = FALSE;
HANDLE ChannelEvent;
DWORD BytesReturned = 0;
ainput_server* ainput = (ainput_server*)arg;
UINT error;
DWORD status;
WINPR_ASSERT(ainput);
if ((error = ainput_server_open_channel(ainput)))
{
WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
goto out;
}
buffer = NULL;
BytesReturned = 0;
ChannelEvent = NULL;
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
nCount = 0;
events[nCount++] = ainput->stopEvent;
events[nCount++] = ChannelEvent;
while (1)
{
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
break;
}
if (status == WAIT_OBJECT_0)
{
if (error)
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
break;
}
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer,
&BytesReturned) == FALSE)
{
if (error)
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
break;
}
ready = *((BOOL*)buffer);
WTSFreeMemory(buffer);
if (ready)
{
if (error)
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
break;
}
}
s = Stream_New(NULL, 4096);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
WTSVirtualChannelClose(ainput->ainput_channel);
ExitThread(ERROR_NOT_ENOUGH_MEMORY);
return ERROR_NOT_ENOUGH_MEMORY;
}
if (ready)
{
if ((error = ainput_server_send_version(ainput, s)))
{
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!", error);
goto out_capacity;
}
}
while (ready)
{
UINT16 MessageId;
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
break;
}
if (status == WAIT_OBJECT_0)
break;
Stream_SetPosition(s, 0);
WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
if (BytesReturned < 2)
continue;
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
break;
}
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
Stream_SetLength(s, BytesReturned);
Stream_Read_UINT16(s, MessageId);
switch (MessageId)
{
case MSG_AINPUT_MOUSE:
error = ainput_server_recv_mouse_event(ainput, s);
break;
default:
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
break;
}
if (error)
{
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
break;
}
}
out_capacity:
Stream_Free(s, TRUE);
out:
WTSVirtualChannelClose(ainput->ainput_channel);
ainput->ainput_channel = NULL;
if (error && ainput->context.rdpcontext)
setChannelError(ainput->context.rdpcontext, error,
"ainput_server_thread_func reported an error");
ExitThread(error);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_open(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (ainput->thread == NULL)
{
ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ainput->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
if (!ainput->thread)
{
WLog_ERR(TAG, "CreateEvent failed!");
CloseHandle(ainput->stopEvent);
ainput->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT ainput_server_close(ainput_server_context* context)
{
UINT error = CHANNEL_RC_OK;
ainput_server* ainput = (ainput_server*)context;
WINPR_ASSERT(ainput);
if (ainput->thread)
{
SetEvent(ainput->stopEvent);
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(ainput->thread);
CloseHandle(ainput->stopEvent);
ainput->thread = NULL;
ainput->stopEvent = NULL;
}
return error;
}
ainput_server_context* ainput_server_context_new(HANDLE vcm)
{
ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
if (!ainput)
return NULL;
ainput->context.vcm = vcm;
ainput->context.Open = ainput_server_open;
ainput->context.IsOpen = ainput_server_is_open;
ainput->context.Close = ainput_server_close;
return &ainput->context;
}
void ainput_server_context_free(ainput_server_context* context)
{
ainput_server* ainput = (ainput_server*)context;
if (ainput)
ainput_server_close(context);
free(ainput);
}

View File

@ -54,6 +54,10 @@
#include <freerdp/server/disp.h>
#include <freerdp/server/gfxredir.h>
#if defined(CHANNEL_AINPUT_SERVER)
#include <freerdp/server/ainput.h>
#endif
extern void freerdp_channels_dummy(void);
void freerdp_channels_dummy(void)
@ -101,6 +105,12 @@ void freerdp_channels_dummy(void)
gfxredir = gfxredir_server_context_new(NULL);
gfxredir_server_context_free(gfxredir);
#endif // WITH_CHANNEL_GFXREDIR
#if defined(CHANNEL_AINPUT_SERVER)
{
ainput_server_context* ainput = ainput_server_context_new(NULL);
ainput_server_context_free(ainput);
}
#endif
}
/**

View File

@ -67,6 +67,9 @@
#cmakedefine WITH_RDPDR
/* Channels */
#cmakedefine CHANNEL_AINPUT
#cmakedefine CHANNEL_AINPUT_CLIENT
#cmakedefine CHANNEL_AINPUT_SERVER
#cmakedefine CHANNEL_AUDIN
#cmakedefine CHANNEL_AUDIN_CLIENT
#cmakedefine CHANNEL_AUDIN_SERVER

View File

@ -0,0 +1,60 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AINPUT_H
#define FREERDP_CHANNEL_AINPUT_H
#include <freerdp/api.h>
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#define AINPUT_CHANNEL_NAME "ainput"
#define AINPUT_DVC_CHANNEL_NAME "FreeRDP::Advanced::Input"
typedef enum
{
MSG_AINPUT_VERSION = 0x01,
MSG_AINPUT_MOUSE = 0x02
} eAInputMsgType;
typedef enum
{
AINPUT_FLAGS_WHEEL = 0x0001,
AINPUT_FLAGS_MOVE = 0x0004,
AINPUT_FLAGS_DOWN = 0x0008,
AINPUT_FLAGS_REL = 0x0010,
/* Pointer Flags */
AINPUT_FLAGS_BUTTON1 = 0x1000, /* left */
AINPUT_FLAGS_BUTTON2 = 0x2000, /* right */
AINPUT_FLAGS_BUTTON3 = 0x4000, /* middle */
/* Extended Pointer Flags */
AINPUT_XFLAGS_BUTTON1 = 0x0100,
AINPUT_XFLAGS_BUTTON2 = 0x0200
} AInputEventFlags;
typedef struct ainput_client_context AInputClientContext;
#define AINPUT_VERSION_MAJOR 1
#define AINPUT_VERSION_MINOR 0
#endif /* FREERDP_CHANNEL_AINPUT_H */

View File

@ -0,0 +1,39 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Display Update Virtual Channel Extension
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H
#define FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H
#include <winpr/assert.h>
#include <freerdp/channels/ainput.h>
typedef UINT (*pcAInputSendInputEvent)(AInputClientContext* context, UINT64 flags, INT32 x,
INT32 y);
struct ainput_client_context
{
void* handle;
void* custom;
pcAInputSendInputEvent AInputSendInputEvent;
};
#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H */

View File

@ -0,0 +1,88 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* AInput Virtual Channel Extension
*
* Copyright 2022 Armin Novak <anovak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_AINPUT_SERVER_H
#define FREERDP_CHANNEL_AINPUT_SERVER_H
#include <freerdp/channels/wtsvc.h>
typedef enum AINPUT_SERVER_OPEN_RESULT
{
AINPUT_SERVER_OPEN_RESULT_OK = 0,
AINPUT_SERVER_OPEN_RESULT_CLOSED = 1,
AINPUT_SERVER_OPEN_RESULT_NOTSUPPORTED = 2,
AINPUT_SERVER_OPEN_RESULT_ERROR = 3
} AINPUT_SERVER_OPEN_RESULT;
typedef struct _ainput_server_context ainput_server_context;
typedef UINT (*psAInputServerOpen)(ainput_server_context* context);
typedef UINT (*psAInputServerClose)(ainput_server_context* context);
typedef BOOL (*psAInputServerIsOpen)(ainput_server_context* context);
typedef UINT (*psAInputServerOpenResult)(ainput_server_context* context,
AINPUT_SERVER_OPEN_RESULT result);
typedef UINT (*psAInputServerMouseEvent)(ainput_server_context* context, UINT64 flags, INT32 x,
INT32 y);
struct _ainput_server_context
{
HANDLE vcm;
/* Server self-defined pointer. */
void* data;
/*** APIs called by the server. ***/
/**
* Open the ainput channel.
*/
psAInputServerOpen Open;
/**
* Close the ainput channel.
*/
psAInputServerClose Close;
/**
* Status of the ainput channel.
*/
psAInputServerIsOpen IsOpen;
/*** Callbacks registered by the server. ***/
/**
* Receive ainput mouse event PDU.
*/
psAInputServerMouseEvent MouseEvent;
rdpContext* rdpcontext;
};
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API ainput_server_context* ainput_server_context_new(HANDLE vcm);
FREERDP_API void ainput_server_context_free(ainput_server_context* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_AINPUT_SERVER_H */