Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
292
third_party/FreeRDP/channels/CMakeLists.txt
vendored
Normal file
292
third_party/FreeRDP/channels/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
macro(define_channel_options)
|
||||
set(PREFIX "CHANNEL")
|
||||
|
||||
cmake_parse_arguments(
|
||||
${PREFIX} "" "NAME;TYPE;DESCRIPTION;SPECIFICATIONS;DEFAULT;CLIENT_DEFAULT;SERVER_DEFAULT" "" ${ARGN}
|
||||
)
|
||||
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" CHANNEL_OPTION)
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_CLIENT_OPTION)
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" CHANNEL_SERVER_OPTION)
|
||||
string(TOUPPER "${CHANNEL_TYPE}" CHANNEL_TYPE)
|
||||
|
||||
if(CHANNEL_DEFAULT)
|
||||
set(OPTION_DEFAULT ${CHANNEL_DEFAULT})
|
||||
elseif(CHANNEL_CLIENT_OPTION OR CHANNEL_SERVER_OPTION)
|
||||
set(OPTION_DEFAULT "ON")
|
||||
endif()
|
||||
|
||||
set(CHANNEL_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel")
|
||||
set(CHANNEL_CLIENT_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel client")
|
||||
set(CHANNEL_SERVER_OPTION_DOC "Build ${CHANNEL_NAME} ${CHANNEL_TYPE} channel server")
|
||||
|
||||
if("${CHANNEL_TYPE}" STREQUAL "DYNAMIC")
|
||||
cmake_dependent_option(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT} "CHANNEL_DRDYNVC" OFF)
|
||||
else()
|
||||
option(${CHANNEL_OPTION} "${CHANNEL_OPTION_DOC}" ${CHANNEL_DEFAULT})
|
||||
endif()
|
||||
|
||||
# If the channel was enabled before the client/server options will stay ensure
|
||||
# they are deleted if the channel is gone.
|
||||
if(NOT ${CHANNEL_OPTION})
|
||||
unset(${CHANNEL_CLIENT_OPTION} CACHE)
|
||||
unset(${CHANNEL_SERVER_OPTION} CACHE)
|
||||
endif()
|
||||
|
||||
cmake_dependent_option(
|
||||
${CHANNEL_CLIENT_OPTION} "${CHANNEL_CLIENT_OPTION_DOC}" ${CHANNEL_CLIENT_DEFAULT} "${CHANNEL_OPTION}" OFF
|
||||
)
|
||||
|
||||
cmake_dependent_option(
|
||||
${CHANNEL_SERVER_OPTION} "${CHANNEL_SERVER_OPTION_DOC}" ${CHANNEL_SERVER_DEFAULT} "${CHANNEL_OPTION}" OFF
|
||||
)
|
||||
endmacro(define_channel_options)
|
||||
|
||||
macro(define_channel _channel_name)
|
||||
set(CHANNEL_NAME ${_channel_name})
|
||||
set(MODULE_NAME ${CHANNEL_NAME})
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}" MODULE_PREFIX)
|
||||
endmacro(define_channel)
|
||||
|
||||
macro(define_channel_client _channel_name)
|
||||
set(CHANNEL_NAME ${_channel_name})
|
||||
set(MODULE_NAME "${CHANNEL_NAME}-client")
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" MODULE_PREFIX)
|
||||
endmacro(define_channel_client)
|
||||
|
||||
macro(define_channel_server _channel_name)
|
||||
set(CHANNEL_NAME ${_channel_name})
|
||||
set(MODULE_NAME "${CHANNEL_NAME}-server")
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_SERVER" MODULE_PREFIX)
|
||||
endmacro(define_channel_server)
|
||||
|
||||
macro(define_channel_client_subsystem _channel_name _subsystem _type)
|
||||
set(CHANNEL_NAME ${_channel_name})
|
||||
set(CHANNEL_SUBSYSTEM ${_subsystem})
|
||||
string(LENGTH "${_type}" _type_length)
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT" CHANNEL_PREFIX)
|
||||
if(_type_length GREATER 0)
|
||||
set(SUBSYSTEM_TYPE ${_type})
|
||||
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}-${SUBSYSTEM_TYPE}")
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}_${SUBSYSTEM_TYPE}" MODULE_PREFIX)
|
||||
else()
|
||||
set(MODULE_NAME "${CHANNEL_NAME}-client-${CHANNEL_SUBSYSTEM}")
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_CLIENT_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
|
||||
endif()
|
||||
endmacro(define_channel_client_subsystem)
|
||||
|
||||
macro(define_channel_server_subsystem _channel_name _subsystem _type)
|
||||
set(CHANNEL_NAME ${_channel_name})
|
||||
set(CHANNEL_SUBSYSTEM ${_subsystem})
|
||||
set(MODULE_NAME "${CHANNEL_NAME}-server-${CHANNEL_SUBSYSTEM}")
|
||||
string(TOUPPER "CHANNEL_${CHANNEL_NAME}_server_${CHANNEL_SUBSYSTEM}" MODULE_PREFIX)
|
||||
endmacro(define_channel_server_subsystem)
|
||||
|
||||
macro(add_channel_client _channel_prefix _channel_name)
|
||||
if(${_channel_prefix}_CLIENT)
|
||||
add_subdirectory(client)
|
||||
if(${${_channel_prefix}_CLIENT_STATIC})
|
||||
set(CHANNEL_STATIC_CLIENT_MODULES ${CHANNEL_STATIC_CLIENT_MODULES} ${_channel_prefix} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_CLIENT_NAME ${${_channel_prefix}_CLIENT_NAME} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_CLIENT_CHANNEL ${${_channel_prefix}_CLIENT_CHANNEL} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_CLIENT_ENTRY ${${_channel_prefix}_CLIENT_ENTRY} PARENT_SCOPE)
|
||||
set(CHANNEL_STATIC_CLIENT_ENTRIES ${CHANNEL_STATIC_CLIENT_ENTRIES} ${${_channel_prefix}_CLIENT_ENTRY}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client)
|
||||
|
||||
macro(add_channel_server _channel_prefix _channel_name)
|
||||
if(${_channel_prefix}_SERVER)
|
||||
add_subdirectory(server)
|
||||
if(${${_channel_prefix}_SERVER_STATIC})
|
||||
set(CHANNEL_STATIC_SERVER_MODULES ${CHANNEL_STATIC_SERVER_MODULES} ${_channel_prefix} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_SERVER_NAME ${${_channel_prefix}_SERVER_NAME} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_SERVER_CHANNEL ${${_channel_prefix}_SERVER_CHANNEL} PARENT_SCOPE)
|
||||
set(${_channel_prefix}_SERVER_ENTRY ${${_channel_prefix}_SERVER_ENTRY} PARENT_SCOPE)
|
||||
set(CHANNEL_STATIC_SERVER_ENTRIES ${CHANNEL_STATIC_SERVER_ENTRIES} ${${_channel_prefix}_SERVER_ENTRY}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_server)
|
||||
|
||||
macro(add_channel_client_subsystem _channel_prefix _channel_name _subsystem _type)
|
||||
add_subdirectory(${_subsystem})
|
||||
set(_channel_module_name "${_channel_name}-client")
|
||||
string(LENGTH "${_type}" _type_length)
|
||||
if(_type_length GREATER 0)
|
||||
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}_${_type}" _subsystem_prefix)
|
||||
else()
|
||||
string(TOUPPER "CHANNEL_${_channel_name}_CLIENT_${_subsystem}" _subsystem_prefix)
|
||||
endif()
|
||||
if(${${_subsystem_prefix}_STATIC})
|
||||
get_target_property(CHANNEL_SUBSYSTEMS ${_channel_module_name} SUBSYSTEMS)
|
||||
if(_type_length GREATER 0)
|
||||
set(SUBSYSTEMS ${SUBSYSTEMS} "${_subsystem}-${_type}")
|
||||
else()
|
||||
set(SUBSYSTEMS ${SUBSYSTEMS} ${_subsystem})
|
||||
endif()
|
||||
set_target_properties(${_channel_module_name} PROPERTIES SUBSYSTEMS "${SUBSYSTEMS}")
|
||||
endif()
|
||||
endmacro(add_channel_client_subsystem)
|
||||
|
||||
macro(channel_install _targets _destination _export_target)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
foreach(_target_name IN ITEMS ${_targets})
|
||||
target_include_directories(${_target_name} INTERFACE $<INSTALL_INTERFACE:include>)
|
||||
endforeach()
|
||||
installwithrpath(TARGETS ${_targets} DESTINATION ${_destination} EXPORT ${_export_target})
|
||||
endif()
|
||||
endmacro(channel_install)
|
||||
|
||||
macro(server_channel_install _targets _destination)
|
||||
channel_install(${_targets} ${_destination} "FreeRDP-ServerTargets")
|
||||
endmacro(server_channel_install)
|
||||
|
||||
macro(client_channel_install _targets _destination)
|
||||
channel_install(${_targets} ${_destination} "FreeRDP-ClientTargets")
|
||||
endmacro(client_channel_install)
|
||||
|
||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if(NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
||||
|
||||
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
|
||||
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
if(${_module_prefix}_LIBS)
|
||||
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
|
||||
endif()
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endmacro(add_channel_client_library)
|
||||
|
||||
macro(
|
||||
add_channel_client_subsystem_library
|
||||
_module_prefix
|
||||
_module_name
|
||||
_channel_name
|
||||
_type
|
||||
_dynamic
|
||||
_entry
|
||||
)
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if(NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
|
||||
|
||||
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
|
||||
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${_channel_name}/Client/Subsystem/${CHANNEL_SUBSYSTEM}")
|
||||
|
||||
if(${_module_prefix}_LIBS)
|
||||
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
|
||||
endif()
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endmacro(add_channel_client_subsystem_library)
|
||||
|
||||
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
set(_lnk_dir ${${_module_prefix}_LINK_DIRS})
|
||||
if(NOT "${_lnk_dir}" STREQUAL "")
|
||||
link_directories(${_lnk_dir})
|
||||
endif()
|
||||
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
||||
|
||||
add_library(${_module_name} OBJECT ${${_module_prefix}_SRCS})
|
||||
set_property(TARGET ${_module_name} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
||||
if(${_module_prefix}_LIBS)
|
||||
target_link_libraries(${_module_name} PUBLIC ${${_module_prefix}_LIBS})
|
||||
endif()
|
||||
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endmacro(add_channel_server_library)
|
||||
|
||||
set(FILENAME "ChannelOptions.cmake")
|
||||
file(GLOB FILEPATHS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${FILENAME}")
|
||||
|
||||
# We need special treatment for drdynvc:
|
||||
# It needs to be the first entry so that every
|
||||
# dynamic channel has the dependent options available.
|
||||
set(DRDYNVC_MATCH "")
|
||||
|
||||
foreach(FILEPATH ${FILEPATHS})
|
||||
if(${FILEPATH} MATCHES "^([^/]*)drdynvc/+${FILENAME}")
|
||||
set(DRDYNVC_MATCH ${FILEPATH})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT "${DRDYNVC_MATCH}" STREQUAL "")
|
||||
list(REMOVE_ITEM FILEPATHS ${DRDYNVC_MATCH})
|
||||
list(APPEND FILEPATHS ${DRDYNVC_MATCH})
|
||||
list(REVERSE FILEPATHS) # list PREPEND is not available on old CMake3
|
||||
endif()
|
||||
|
||||
foreach(FILEPATH ${FILEPATHS})
|
||||
if(${FILEPATH} MATCHES "^([^/]*)/+${FILENAME}")
|
||||
string(REGEX REPLACE "^([^/]*)/+${FILENAME}" "\\1" DIR ${FILEPATH})
|
||||
set(CHANNEL_OPTION)
|
||||
include(${FILEPATH})
|
||||
if(${CHANNEL_OPTION})
|
||||
set(CHANNEL_MESSAGE "Adding ${CHANNEL_TYPE} channel")
|
||||
if(${CHANNEL_CLIENT_OPTION})
|
||||
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} client")
|
||||
endif()
|
||||
if(${CHANNEL_SERVER_OPTION})
|
||||
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} server")
|
||||
endif()
|
||||
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE} \"${CHANNEL_NAME}\"")
|
||||
set(CHANNEL_MESSAGE "${CHANNEL_MESSAGE}: ${CHANNEL_DESCRIPTION}")
|
||||
message(STATUS "${CHANNEL_MESSAGE}")
|
||||
add_subdirectory(${DIR})
|
||||
endif()
|
||||
endif()
|
||||
endforeach(FILEPATH)
|
||||
|
||||
if(WITH_CHANNELS)
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_subdirectory(client)
|
||||
set(FREERDP_CHANNELS_CLIENT_SRCS ${FREERDP_CHANNELS_CLIENT_SRCS} PARENT_SCOPE)
|
||||
set(FREERDP_CHANNELS_CLIENT_LIBS ${FREERDP_CHANNELS_CLIENT_LIBS} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_subdirectory(server)
|
||||
set(FREERDP_CHANNELS_SERVER_SRCS ${FREERDP_CHANNELS_SERVER_SRCS} PARENT_SCOPE)
|
||||
set(FREERDP_CHANNELS_SERVER_LIBS ${FREERDP_CHANNELS_SERVER_LIBS} PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
27
third_party/FreeRDP/channels/ainput/CMakeLists.txt
vendored
Normal file
27
third_party/FreeRDP/channels/ainput/CMakeLists.txt
vendored
Normal 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()
|
||||
20
third_party/FreeRDP/channels/ainput/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/ainput/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
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}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
27
third_party/FreeRDP/channels/ainput/client/CMakeLists.txt
vendored
Normal file
27
third_party/FreeRDP/channels/ainput/client/CMakeLists.txt
vendored
Normal 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_client("ainput")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ainput_main.c ainput_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
200
third_party/FreeRDP/channels/ainput/client/ainput_main.c
vendored
Normal file
200
third_party/FreeRDP/channels/ainput/client/ainput_main.c
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "ainput_main.h"
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/client/ainput.h>
|
||||
#include <freerdp/channels/ainput.h>
|
||||
|
||||
#include "../common/ainput_common.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("ainput.client")
|
||||
|
||||
typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
|
||||
struct AINPUT_PLUGIN_
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN base;
|
||||
AInputClientContext* context;
|
||||
UINT32 MajorVersion;
|
||||
UINT32 MinorVersion;
|
||||
CRITICAL_SECTION lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
UINT16 type = 0;
|
||||
AINPUT_PLUGIN* ainput = nullptr;
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(data);
|
||||
|
||||
ainput = (AINPUT_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, data, 2))
|
||||
return ERROR_NO_DATA;
|
||||
Stream_Read_UINT16(data, type);
|
||||
switch (type)
|
||||
{
|
||||
case MSG_AINPUT_VERSION:
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, 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)
|
||||
{
|
||||
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
|
||||
wStream sbuffer = WINPR_C_ARRAY_INIT;
|
||||
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
|
||||
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
const UINT64 time = GetTickCount64();
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)context->handle;
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
|
||||
{
|
||||
WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
|
||||
ainput->MajorVersion, ainput->MinorVersion);
|
||||
return CHANNEL_RC_UNSUPPORTED_VERSION;
|
||||
}
|
||||
|
||||
{
|
||||
char ebuffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_VRB(TAG, "sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
|
||||
ainput_flags_to_string(flags, ebuffer, sizeof(ebuffer)), x, y);
|
||||
}
|
||||
|
||||
/* Message type */
|
||||
Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
|
||||
|
||||
/* Event data */
|
||||
Stream_Write_UINT64(s, time);
|
||||
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. */
|
||||
EnterCriticalSection(&ainput->lock);
|
||||
GENERIC_CHANNEL_CALLBACK* callback = ainput->base.listener_callback->channel_callback;
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(callback->channel);
|
||||
WINPR_ASSERT(callback->channel->Write);
|
||||
const UINT rc = callback->channel->Write(callback->channel, (ULONG)Stream_Length(s),
|
||||
Stream_Buffer(s), nullptr);
|
||||
LeaveCriticalSection(&ainput->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
if (callback)
|
||||
{
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
/* Lock here to ensure that no ainput_send_input_event is in progress. */
|
||||
EnterCriticalSection(&ainput->lock);
|
||||
free(callback);
|
||||
LeaveCriticalSection(&ainput->lock);
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
|
||||
WINPR_ATTR_UNUSED rdpSettings* settings)
|
||||
{
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
|
||||
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
|
||||
if (!context)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
context->handle = (void*)base;
|
||||
context->AInputSendInputEvent = ainput_send_input_event;
|
||||
|
||||
InitializeCriticalSection(&ainput->lock);
|
||||
|
||||
EnterCriticalSection(&ainput->lock);
|
||||
ainput->context = context;
|
||||
ainput->base.iface.pInterface = context;
|
||||
LeaveCriticalSection(&ainput->lock);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
{
|
||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
DeleteCriticalSection(&ainput->lock);
|
||||
free(ainput->context);
|
||||
}
|
||||
|
||||
static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_received,
|
||||
nullptr, /* Open */
|
||||
ainput_on_close, nullptr };
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME,
|
||||
sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&ainput_functions, init_plugin_cb, terminate_plugin_cb);
|
||||
}
|
||||
40
third_party/FreeRDP/channels/ainput/client/ainput_main.h
vendored
Normal file
40
third_party/FreeRDP/channels/ainput/client/ainput_main.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#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 */
|
||||
60
third_party/FreeRDP/channels/ainput/common/ainput_common.h
vendored
Normal file
60
third_party/FreeRDP/channels/ainput/common/ainput_common.h
vendored
Normal 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_INT_AINPUT_COMMON_H
|
||||
#define FREERDP_INT_AINPUT_COMMON_H
|
||||
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <freerdp/channels/ainput.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static inline const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size)
|
||||
{
|
||||
char number[32] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (flags & AINPUT_FLAGS_HAVE_REL)
|
||||
winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_WHEEL)
|
||||
winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_MOVE)
|
||||
winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_DOWN)
|
||||
winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_REL)
|
||||
winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON1)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON2)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|");
|
||||
if (flags & AINPUT_FLAGS_BUTTON3)
|
||||
winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|");
|
||||
if (flags & AINPUT_XFLAGS_BUTTON1)
|
||||
winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|");
|
||||
if (flags & AINPUT_XFLAGS_BUTTON2)
|
||||
winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|");
|
||||
|
||||
_snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags);
|
||||
winpr_str_append(number, buffer, size, " ");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endif /* FREERDP_INT_AINPUT_COMMON_H */
|
||||
24
third_party/FreeRDP/channels/ainput/server/CMakeLists.txt
vendored
Normal file
24
third_party/FreeRDP/channels/ainput/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
596
third_party/FreeRDP/channels/ainput/server/ainput_main.c
vendored
Normal file
596
third_party/FreeRDP/channels/ainput/server/ainput_main.c
vendored
Normal file
@@ -0,0 +1,596 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#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/freerdp.h>
|
||||
#include <freerdp/channels/ainput.h>
|
||||
#include <freerdp/server/ainput.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "../common/ainput_common.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("ainput.server")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AINPUT_INITIAL,
|
||||
AINPUT_OPENED,
|
||||
AINPUT_VERSION_SENT,
|
||||
} eAInputChannelState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ainput_server_context context;
|
||||
|
||||
BOOL opened;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* ainput_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
BOOL isOpened;
|
||||
BOOL externalThread;
|
||||
|
||||
/* Channel state */
|
||||
eAInputChannelState state;
|
||||
|
||||
wStream* buffer;
|
||||
} ainput_server;
|
||||
|
||||
static UINT ainput_server_context_poll(ainput_server_context* context);
|
||||
static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
|
||||
static UINT ainput_server_context_poll_int(ainput_server_context* context);
|
||||
|
||||
static BOOL ainput_server_is_open(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
return ainput->isOpened;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ainput_server_open_channel(ainput_server* ainput)
|
||||
{
|
||||
DWORD Error = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
DWORD StartTick = 0;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = nullptr;
|
||||
|
||||
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 == nullptr)
|
||||
{
|
||||
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);
|
||||
|
||||
Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_NOT_FOUND)
|
||||
{
|
||||
WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ainput->ainput_channel)
|
||||
{
|
||||
UINT32 channelId = 0;
|
||||
BOOL status = TRUE;
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
|
||||
|
||||
IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetTickCount() - StartTick > 5000)
|
||||
{
|
||||
WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static UINT ainput_server_send_version(ainput_server* ainput)
|
||||
{
|
||||
ULONG written = 0;
|
||||
wStream* s = nullptr;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
s = ainput->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_ResetPosition(s);
|
||||
if (!Stream_EnsureCapacity(s, 10))
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
|
||||
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) <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char),
|
||||
(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 = 0;
|
||||
UINT64 time = 0;
|
||||
INT32 x = 0;
|
||||
INT32 y = 0;
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT64(s, time);
|
||||
Stream_Read_UINT64(s, flags);
|
||||
Stream_Read_INT32(s, x);
|
||||
Stream_Read_INT32(s, y);
|
||||
|
||||
WLog_VRB(TAG, "received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
|
||||
ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
|
||||
IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
HANDLE ChannelEvent = nullptr;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
ChannelEvent = *(HANDLE*)buffer;
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
return ChannelEvent;
|
||||
}
|
||||
|
||||
static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
|
||||
{
|
||||
DWORD nCount = 0;
|
||||
HANDLE events[2] = WINPR_C_ARRAY_INIT;
|
||||
ainput_server* ainput = (ainput_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status = 0;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = ainput->stopEvent;
|
||||
|
||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
||||
{
|
||||
switch (ainput->state)
|
||||
{
|
||||
case AINPUT_OPENED:
|
||||
events[1] = ainput_server_get_channel_handle(ainput);
|
||||
nCount = 2;
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_OBJECT_0:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AINPUT_VERSION_SENT:
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_OBJECT_0:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = ainput_server_context_poll_int(&ainput->context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(void)WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = nullptr;
|
||||
|
||||
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->externalThread && (ainput->thread == nullptr))
|
||||
{
|
||||
ainput->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!ainput->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ainput->thread = CreateThread(nullptr, 0, ainput_server_thread_func, ainput, 0, nullptr);
|
||||
if (!ainput->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
(void)CloseHandle(ainput->stopEvent);
|
||||
ainput->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
ainput->isOpened = TRUE;
|
||||
|
||||
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->externalThread && ainput->thread)
|
||||
{
|
||||
(void)SetEvent(ainput->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(ainput->thread);
|
||||
(void)CloseHandle(ainput->stopEvent);
|
||||
ainput->thread = nullptr;
|
||||
ainput->stopEvent = nullptr;
|
||||
}
|
||||
if (ainput->externalThread)
|
||||
{
|
||||
if (ainput->state != AINPUT_INITIAL)
|
||||
{
|
||||
(void)WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = nullptr;
|
||||
ainput->state = AINPUT_INITIAL;
|
||||
}
|
||||
}
|
||||
ainput->isOpened = FALSE;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
if (ainput->isOpened)
|
||||
{
|
||||
WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
|
||||
"state is not possible!");
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
ainput->externalThread = externalThread;
|
||||
return error;
|
||||
}
|
||||
|
||||
ainput_server_context* ainput_server_context_new(HANDLE vcm)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
|
||||
|
||||
if (!ainput)
|
||||
return nullptr;
|
||||
|
||||
ainput->context.vcm = vcm;
|
||||
ainput->context.Open = ainput_server_open;
|
||||
ainput->context.IsOpen = ainput_server_is_open;
|
||||
ainput->context.Close = ainput_server_close;
|
||||
ainput->context.Initialize = ainput_server_initialize;
|
||||
ainput->context.Poll = ainput_server_context_poll;
|
||||
ainput->context.ChannelHandle = ainput_server_context_handle;
|
||||
|
||||
ainput->buffer = Stream_New(nullptr, 4096);
|
||||
if (!ainput->buffer)
|
||||
goto fail;
|
||||
return &ainput->context;
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
ainput_server_context_free(&ainput->context);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ainput_server_context_free(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
if (ainput)
|
||||
{
|
||||
ainput_server_close(context);
|
||||
Stream_Free(ainput->buffer, TRUE);
|
||||
}
|
||||
free(ainput);
|
||||
}
|
||||
|
||||
static UINT ainput_process_message(ainput_server* ainput)
|
||||
{
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
ULONG BytesReturned = 0;
|
||||
ULONG ActualBytesReturned = 0;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(ainput->ainput_channel);
|
||||
|
||||
wStream* s = ainput->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_ResetPosition(s);
|
||||
const BOOL rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, nullptr, 0, &BytesReturned);
|
||||
if (!rc)
|
||||
goto out;
|
||||
|
||||
if (BytesReturned < 2)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (BytesReturned != ActualBytesReturned)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRIu32 ", expected %" PRIu32,
|
||||
ActualBytesReturned, BytesReturned);
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_SetLength(s, ActualBytesReturned);
|
||||
{
|
||||
const UINT16 MessageId = Stream_Get_UINT16(s);
|
||||
|
||||
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 %" PRIu16 "", MessageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (error)
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
WINPR_ASSERT(ainput);
|
||||
WINPR_ASSERT(handle);
|
||||
|
||||
if (!ainput->externalThread)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return FALSE;
|
||||
}
|
||||
if (ainput->state == AINPUT_INITIAL)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return FALSE;
|
||||
}
|
||||
*handle = ainput_server_get_channel_handle(ainput);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT ainput_server_context_poll_int(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
|
||||
switch (ainput->state)
|
||||
{
|
||||
case AINPUT_INITIAL:
|
||||
error = ainput_server_open_channel(ainput);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
|
||||
else
|
||||
ainput->state = AINPUT_OPENED;
|
||||
break;
|
||||
case AINPUT_OPENED:
|
||||
{
|
||||
union
|
||||
{
|
||||
BYTE* pb;
|
||||
void* pv;
|
||||
} buffer;
|
||||
DWORD BytesReturned = 0;
|
||||
|
||||
buffer.pv = nullptr;
|
||||
|
||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer.pv,
|
||||
&BytesReturned) != TRUE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*buffer.pb != 0)
|
||||
{
|
||||
error = ainput_server_send_version(ainput);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
|
||||
error);
|
||||
else
|
||||
ainput->state = AINPUT_VERSION_SENT;
|
||||
}
|
||||
else
|
||||
error = CHANNEL_RC_OK;
|
||||
}
|
||||
WTSFreeMemory(buffer.pv);
|
||||
}
|
||||
break;
|
||||
case AINPUT_VERSION_SENT:
|
||||
error = ainput_process_message(ainput);
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "AINPUT channel is in invalid state %u", ainput->state);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
UINT ainput_server_context_poll(ainput_server_context* context)
|
||||
{
|
||||
ainput_server* ainput = (ainput_server*)context;
|
||||
|
||||
WINPR_ASSERT(ainput);
|
||||
if (!ainput->externalThread)
|
||||
{
|
||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
return ainput_server_context_poll_int(context);
|
||||
}
|
||||
26
third_party/FreeRDP/channels/audin/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/audin/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("audin")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
24
third_party/FreeRDP/channels/audin/ChannelOptions.cmake
vendored
Normal file
24
third_party/FreeRDP/channels/audin/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
if(ANDROID)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"audin"
|
||||
TYPE
|
||||
"dynamic"
|
||||
DESCRIPTION
|
||||
"Audio Input Redirection Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEAI]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
58
third_party/FreeRDP/channels/audin/client/CMakeLists.txt
vendored
Normal file
58
third_party/FreeRDP/channels/audin/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("audin")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_main.c audin_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
if(WITH_OSS)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "oss" "")
|
||||
endif()
|
||||
|
||||
if(WITH_ALSA)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "alsa" "")
|
||||
endif()
|
||||
|
||||
if(WITH_PULSE)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "")
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSLES)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
|
||||
endif()
|
||||
|
||||
if(WITH_WINMM)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "")
|
||||
endif()
|
||||
|
||||
if(WITH_MACAUDIO)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "mac" "")
|
||||
endif()
|
||||
|
||||
if(WITH_SNDIO)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "sndio" "")
|
||||
endif()
|
||||
|
||||
if(WITH_IOSAUDIO)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "ios" "")
|
||||
endif()
|
||||
31
third_party/FreeRDP/channels/audin/client/alsa/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/channels/audin/client/alsa/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client_subsystem("audin" "alsa" "")
|
||||
|
||||
find_package(ALSA REQUIRED)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_alsa.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${ALSA_LIBRARIES})
|
||||
|
||||
freerdp_client_pc_add_requires_private("alsa")
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${ALSA_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
466
third_party/FreeRDP/channels/audin/client/alsa/audin_alsa.c
vendored
Normal file
466
third_party/FreeRDP/channels/audin/client/alsa/audin_alsa.c
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - ALSA implementation
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
UINT32 frames_per_packet;
|
||||
AUDIO_FORMAT aformat;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
size_t bytes_per_frame;
|
||||
} AudinALSADevice;
|
||||
|
||||
static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChannel)
|
||||
{
|
||||
switch (wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (bitPerChannel)
|
||||
{
|
||||
case 16:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
|
||||
case 8:
|
||||
return SND_PCM_FORMAT_S8;
|
||||
|
||||
default:
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
default:
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
|
||||
{
|
||||
int error = 0;
|
||||
SSIZE_T s = 0;
|
||||
UINT32 channels = alsa->aformat.nChannels;
|
||||
snd_pcm_hw_params_t* hw_params = nullptr;
|
||||
snd_pcm_format_t format =
|
||||
audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
|
||||
|
||||
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_hw_params_malloc (%s)", snd_strerror(error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_any(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
|
||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->aformat.nSamplesPerSec,
|
||||
nullptr);
|
||||
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &channels);
|
||||
snd_pcm_hw_params(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_prepare(capture_handle);
|
||||
if (channels > UINT16_MAX)
|
||||
return FALSE;
|
||||
s = snd_pcm_format_size(format, 1);
|
||||
if ((s < 0) || (s > UINT16_MAX))
|
||||
return FALSE;
|
||||
alsa->aformat.nChannels = (UINT16)channels;
|
||||
alsa->bytes_per_frame = (size_t)s * channels;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
{
|
||||
DWORD error = CHANNEL_RC_OK;
|
||||
BYTE* buffer = nullptr;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)arg;
|
||||
|
||||
WINPR_ASSERT(alsa);
|
||||
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
||||
|
||||
snd_pcm_t* capture_handle = nullptr;
|
||||
const int rc = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(rc));
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!audin_alsa_set_params(alsa, capture_handle))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_set_params failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer =
|
||||
(BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t frames = alsa->frames_per_packet;
|
||||
const DWORD status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "alsa->stopEvent requests termination");
|
||||
break;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t framesRead = snd_pcm_readi(capture_handle, buffer, frames);
|
||||
|
||||
if (framesRead == 0)
|
||||
continue;
|
||||
|
||||
if (framesRead == -EPIPE)
|
||||
{
|
||||
const int res = snd_pcm_recover(capture_handle, (int)framesRead, 0);
|
||||
if (res < 0)
|
||||
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_recover (%s)", snd_strerror(res));
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (framesRead < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_readi (%s)", snd_strerror((int)framesRead));
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
error = alsa->receive(&alsa->aformat, buffer,
|
||||
WINPR_ASSERTING_INT_CAST(size_t, framesRead) * alsa->bytes_per_frame,
|
||||
alsa->user_data);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR,
|
||||
"audin_alsa_thread_receive failed with error %" PRIu32, error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
if (capture_handle)
|
||||
{
|
||||
const int res = snd_pcm_close(capture_handle);
|
||||
if (res < 0)
|
||||
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_close (%s)", snd_strerror(res));
|
||||
}
|
||||
|
||||
out:
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
||||
|
||||
if (error && alsa->rdpcontext)
|
||||
setChannelError(alsa->rdpcontext, error, "audin_alsa_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_free(IAudinDevice* device)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (alsa)
|
||||
free(alsa->device_name);
|
||||
|
||||
free(alsa);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (!device || !format)
|
||||
return FALSE;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (!alsa || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
alsa->aformat = *format;
|
||||
alsa->frames_per_packet = FramesPerPacket;
|
||||
|
||||
if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (!device || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
alsa->receive = receive;
|
||||
alsa->user_data = user_data;
|
||||
|
||||
if (!(alsa->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateEvent failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(alsa->thread = CreateThread(nullptr, 0, audin_alsa_thread_func, alsa, 0, nullptr)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
(void)CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
|
||||
if (!alsa)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (alsa->stopEvent)
|
||||
{
|
||||
(void)SetEvent(alsa->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = nullptr;
|
||||
(void)CloseHandle(alsa->thread);
|
||||
alsa->thread = nullptr;
|
||||
}
|
||||
|
||||
alsa->receive = nullptr;
|
||||
alsa->user_data = nullptr;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, const ADDIN_ARGV* args)
|
||||
{
|
||||
int status = 0;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
|
||||
AudinALSADevice* alsa = device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_alsa_args, flags, alsa,
|
||||
nullptr, nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_alsa_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
alsa->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE alsa_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = nullptr;
|
||||
AudinALSADevice* alsa = nullptr;
|
||||
UINT error = 0;
|
||||
alsa = (AudinALSADevice*)calloc(1, sizeof(AudinALSADevice));
|
||||
|
||||
if (!alsa)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
alsa->log = WLog_Get(TAG);
|
||||
alsa->iface.Open = audin_alsa_open;
|
||||
alsa->iface.FormatSupported = audin_alsa_format_supported;
|
||||
alsa->iface.SetFormat = audin_alsa_set_format;
|
||||
alsa->iface.Close = audin_alsa_close;
|
||||
alsa->iface.Free = audin_alsa_free;
|
||||
alsa->rdpcontext = pEntryPoints->rdpcontext;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_alsa_parse_addin_args(alsa, args)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR,
|
||||
"audin_alsa_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
alsa->device_name = _strdup("default");
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->frames_per_packet = 128;
|
||||
alsa->aformat.nChannels = 2;
|
||||
alsa->aformat.wBitsPerSample = 16;
|
||||
alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
alsa->aformat.nSamplesPerSec = 44100;
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)alsa)))
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(alsa->device_name);
|
||||
free(alsa);
|
||||
return error;
|
||||
}
|
||||
1124
third_party/FreeRDP/channels/audin/client/audin_main.c
vendored
Normal file
1124
third_party/FreeRDP/channels/audin/client/audin_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
33
third_party/FreeRDP/channels/audin/client/audin_main.h
vendored
Normal file
33
third_party/FreeRDP/channels/audin/client/audin_main.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
*
|
||||
* 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_AUDIN_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("audin.client")
|
||||
|
||||
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */
|
||||
31
third_party/FreeRDP/channels/audin/client/ios/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/channels/audin/client/ios/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright (c) 2015 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_subsystem("audin" "ios" "")
|
||||
find_library(CORE_AUDIO CoreAudio)
|
||||
find_library(AVFOUNDATION AVFoundation)
|
||||
find_library(AUDIO_TOOL AudioToolbox)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_ios.m)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${AVFOUNDATION} ${CORE_AUDIO} ${AUDIO_TOOL})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
335
third_party/FreeRDP/channels/audin/client/ios/audin_ios.m
vendored
Normal file
335
third_party/FreeRDP/channels/audin/client/ios/audin_ios.m
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - iOS implementation
|
||||
*
|
||||
* Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#define __COREFOUNDATION_CFPLUGINCOM__ 1
|
||||
#define IUNKNOWN_C_GUTS \
|
||||
void *_reserved; \
|
||||
void *QueryInterface; \
|
||||
void *AddRef; \
|
||||
void *Release
|
||||
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define IOS_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void *user_data;
|
||||
|
||||
rdpContext *rdpcontext;
|
||||
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[IOS_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
} AudinIosDevice;
|
||||
|
||||
static AudioFormatID audin_ios_get_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_ios_get_flags_for_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_ios_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format)
|
||||
{
|
||||
AudinIosDevice *ios = (AudinIosDevice *)device;
|
||||
AudioFormatID req_fmt = 0;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return FALSE;
|
||||
|
||||
req_fmt = audin_ios_get_format(format);
|
||||
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_ios_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinIosDevice *ios = (AudinIosDevice *)device;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
ios->FramesPerPacket = FramesPerPacket;
|
||||
ios->format = *format;
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
audio_format_get_tag_string(format->wFormatTag), format->nChannels,
|
||||
format->nSamplesPerSec, format->wBitsPerSample);
|
||||
ios->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
|
||||
if (format->wBitsPerSample == 0)
|
||||
ios->audioFormat.mBitsPerChannel = 16;
|
||||
|
||||
ios->audioFormat.mChannelsPerFrame = ios->format.nChannels;
|
||||
ios->audioFormat.mFramesPerPacket = 1;
|
||||
|
||||
ios->audioFormat.mBytesPerFrame =
|
||||
ios->audioFormat.mChannelsPerFrame * (ios->audioFormat.mBitsPerChannel / 8);
|
||||
ios->audioFormat.mBytesPerPacket =
|
||||
ios->audioFormat.mBytesPerFrame * ios->audioFormat.mFramesPerPacket;
|
||||
|
||||
ios->audioFormat.mFormatFlags = audin_ios_get_flags_for_format(format);
|
||||
ios->audioFormat.mFormatID = audin_ios_get_format(format);
|
||||
ios->audioFormat.mReserved = 0;
|
||||
ios->audioFormat.mSampleRate = ios->format.nSamplesPerSec;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void ios_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
|
||||
const AudioStreamPacketDescription *inPacketDesc)
|
||||
{
|
||||
AudinIosDevice *ios = (AudinIosDevice *)aqData;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
const BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
|
||||
if (buffer_size > 0)
|
||||
error = ios->receive(&ios->format, buffer, buffer_size, ios->user_data);
|
||||
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "ios->receive failed with error %" PRIu32 "", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static UINT audin_ios_close(IAudinDevice *device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinIosDevice *ios = (AudinIosDevice *)device;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (ios->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(ios->audioQueue, true);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
ios->isOpen = false;
|
||||
}
|
||||
|
||||
if (ios->audioQueue)
|
||||
{
|
||||
devStat = AudioQueueDispose(ios->audioQueue, true);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
ios->audioQueue = nullptr;
|
||||
}
|
||||
|
||||
ios->receive = nullptr;
|
||||
ios->user_data = nullptr;
|
||||
return errCode;
|
||||
}
|
||||
|
||||
static UINT audin_ios_open(IAudinDevice *device, AudinReceive receive, void *user_data)
|
||||
{
|
||||
AudinIosDevice *ios = (AudinIosDevice *)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
|
||||
ios->receive = receive;
|
||||
ios->user_data = user_data;
|
||||
devStat = AudioQueueNewInput(&(ios->audioFormat), ios_audio_queue_input_cb, ios, nullptr,
|
||||
kCFRunLoopCommonModes, 0, &(ios->audioQueue));
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < IOS_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(ios->audioQueue,
|
||||
ios->FramesPerPacket * 2 * ios->format.nChannels,
|
||||
&ios->audioBuffers[index]);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, nullptr);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
devStat = AudioQueueStart(ios->audioQueue, nullptr);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ios->isOpen = true;
|
||||
return CHANNEL_RC_OK;
|
||||
err_out:
|
||||
audin_ios_close(device);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
static UINT audin_ios_free(IAudinDevice *device)
|
||||
{
|
||||
AudinIosDevice *ios = (AudinIosDevice *)device;
|
||||
int error;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_ios_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
|
||||
free(ios);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
const ADDIN_ARGV *args;
|
||||
AudinIosDevice *ios;
|
||||
UINT error;
|
||||
ios = (AudinIosDevice *)calloc(1, sizeof(AudinIosDevice));
|
||||
|
||||
if (!ios)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
ios->iface.Open = audin_ios_open;
|
||||
ios->iface.FormatSupported = audin_ios_format_supported;
|
||||
ios->iface.SetFormat = audin_ios_set_format;
|
||||
ios->iface.Close = audin_ios_close;
|
||||
ios->iface.Free = audin_ios_free;
|
||||
ios->rdpcontext = pEntryPoints->rdpcontext;
|
||||
ios->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)ios)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(ios);
|
||||
return error;
|
||||
}
|
||||
32
third_party/FreeRDP/channels/audin/client/mac/CMakeLists.txt
vendored
Normal file
32
third_party/FreeRDP/channels/audin/client/mac/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright (c) 2015 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_subsystem("audin" "mac" "")
|
||||
find_library(CORE_AUDIO CoreAudio)
|
||||
find_library(AVFOUNDATION AVFoundation)
|
||||
find_library(AUDIO_TOOL AudioToolbox)
|
||||
find_library(APP_SERVICES ApplicationServices)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_mac.m)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${AVFOUNDATION} ${CORE_AUDIO} ${AUDIO_TOOL} ${APP_SERVICES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
465
third_party/FreeRDP/channels/audin/client/mac/audin_mac.m
vendored
Normal file
465
third_party/FreeRDP/channels/audin/client/mac/audin_mac.m
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - Mac OS X implementation
|
||||
*
|
||||
* Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#define __COREFOUNDATION_CFPLUGINCOM__ 1
|
||||
#define IUNKNOWN_C_GUTS \
|
||||
void *_reserved; \
|
||||
void *QueryInterface; \
|
||||
void *AddRef; \
|
||||
void *Release
|
||||
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
|
||||
/* Fix for #4462: Provide type alias if not declared (Mac OS < 10.10)
|
||||
* https://developer.apple.com/documentation/coreaudio/audioformatid
|
||||
*/
|
||||
#ifndef AudioFormatID
|
||||
typedef UInt32 AudioFormatID;
|
||||
#endif
|
||||
|
||||
#ifndef AudioFormatFlags
|
||||
typedef UInt32 AudioFormatFlags;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void *user_data;
|
||||
|
||||
rdpContext *rdpcontext;
|
||||
|
||||
bool isAuthorized;
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
} AudinMacDevice;
|
||||
|
||||
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT *format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_mac_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
AudioFormatID req_fmt = 0;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return FALSE;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return FALSE;
|
||||
|
||||
if (format->nChannels != 2)
|
||||
return FALSE;
|
||||
|
||||
req_fmt = audin_mac_get_format(format);
|
||||
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
mac->FramesPerPacket = FramesPerPacket;
|
||||
mac->format = *format;
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
audio_format_get_tag_string(format->wFormatTag), format->nChannels,
|
||||
format->nSamplesPerSec, format->wBitsPerSample);
|
||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
|
||||
if (format->wBitsPerSample == 0)
|
||||
mac->audioFormat.mBitsPerChannel = 16;
|
||||
|
||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
|
||||
mac->audioFormat.mBytesPerFrame =
|
||||
mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8);
|
||||
mac->audioFormat.mBytesPerPacket =
|
||||
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
||||
|
||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||
mac->audioFormat.mReserved = 0;
|
||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void mac_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
|
||||
const AudioStreamPacketDescription *inPacketDesc)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice *)aqData;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
const BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
|
||||
if (buffer_size > 0)
|
||||
error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data);
|
||||
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %" PRIu32 "", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static UINT audin_mac_close(IAudinDevice *device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(mac->audioQueue, true);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->isOpen = false;
|
||||
}
|
||||
|
||||
if (mac->audioQueue)
|
||||
{
|
||||
devStat = AudioQueueDispose(mac->audioQueue, true);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->audioQueue = nullptr;
|
||||
}
|
||||
|
||||
mac->receive = nullptr;
|
||||
mac->user_data = nullptr;
|
||||
return errCode;
|
||||
}
|
||||
|
||||
static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
|
||||
if (!mac->isAuthorized)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
mac->receive = receive;
|
||||
mac->user_data = user_data;
|
||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb, mac, nullptr,
|
||||
kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(mac->audioQueue,
|
||||
mac->FramesPerPacket * 2 * mac->format.nChannels,
|
||||
&mac->audioBuffers[index]);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, nullptr);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
devStat = AudioQueueStart(mac->audioQueue, nullptr);
|
||||
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
mac->isOpen = true;
|
||||
return CHANNEL_RC_OK;
|
||||
err_out:
|
||||
audin_mac_close(device);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
static UINT audin_mac_free(IAudinDevice *device)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
int error;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_mac_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
|
||||
free(mac);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, const ADDIN_ARGV *args)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char *str_num, *eptr;
|
||||
DWORD flags;
|
||||
const COMMAND_LINE_ARGUMENT_A *arg;
|
||||
COMMAND_LINE_ARGUMENT_A audin_mac_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
|
||||
AudinMacDevice *mac = (AudinMacDevice *)device;
|
||||
|
||||
if (args->argc == 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags, mac, nullptr,
|
||||
nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_mac_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
|
||||
if (!str_num)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "_strdup failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
mac->dev_unit = strtol(str_num, &eptr, 10);
|
||||
|
||||
if (mac->dev_unit < 0 || *eptr != '\0')
|
||||
mac->dev_unit = -1;
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
const ADDIN_ARGV *args;
|
||||
AudinMacDevice *mac;
|
||||
UINT error;
|
||||
mac = (AudinMacDevice *)calloc(1, sizeof(AudinMacDevice));
|
||||
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
mac->iface.Open = audin_mac_open;
|
||||
mac->iface.FormatSupported = audin_mac_format_supported;
|
||||
mac->iface.SetFormat = audin_mac_set_format;
|
||||
mac->iface.Close = audin_mac_close;
|
||||
mac->iface.Free = audin_mac_free;
|
||||
mac->rdpcontext = pEntryPoints->rdpcontext;
|
||||
mac->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_mac_parse_addin_args(mac, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_14)
|
||||
if (@available(macOS 10.14, *))
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
AVAuthorizationStatus status =
|
||||
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
|
||||
switch (status)
|
||||
{
|
||||
case AVAuthorizationStatusAuthorized:
|
||||
mac->isAuthorized = TRUE;
|
||||
break;
|
||||
case AVAuthorizationStatusNotDetermined:
|
||||
[AVCaptureDevice
|
||||
requestAccessForMediaType:AVMediaTypeAudio
|
||||
completionHandler:^(BOOL granted) {
|
||||
if (granted == YES)
|
||||
{
|
||||
mac->isAuthorized = TRUE;
|
||||
}
|
||||
else
|
||||
WLog_WARN(TAG, "Microphone access denied by user");
|
||||
}];
|
||||
break;
|
||||
case AVAuthorizationStatusRestricted:
|
||||
WLog_WARN(TAG, "Microphone access restricted by policy");
|
||||
break;
|
||||
case AVAuthorizationStatusDenied:
|
||||
WLog_WARN(TAG, "Microphone access denied by policy");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(mac);
|
||||
return error;
|
||||
}
|
||||
29
third_party/FreeRDP/channels/audin/client/opensles/CMakeLists.txt
vendored
Normal file
29
third_party/FreeRDP/channels/audin/client/opensles/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Armin Novak <armin.novak@gmail.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.
|
||||
|
||||
define_channel_client_subsystem("audin" "opensles" "")
|
||||
|
||||
find_package(OpenSLES REQUIRED)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS opensl_io.c audin_opensl_es.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${OpenSLES_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${OpenSLES_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
334
third_party/FreeRDP/channels/audin/client/opensles/audin_opensl_es.c
vendored
Normal file
334
third_party/FreeRDP/channels/audin/client/opensles/audin_opensl_es.c
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
|
||||
*
|
||||
* Copyright 2013 Armin Novak <armin.novak@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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
#include "opensl_io.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
OPENSL_STREAM* stream;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 frames_per_packet;
|
||||
|
||||
UINT32 bytes_per_channel;
|
||||
|
||||
AudinReceive receive;
|
||||
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinOpenSLESDevice;
|
||||
|
||||
static UINT audin_opensles_close(IAudinDevice* device);
|
||||
|
||||
static void audin_receive(void* context, const void* data, size_t size)
|
||||
{
|
||||
UINT error;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)context;
|
||||
|
||||
if (!opensles || !data)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid arguments context=%p, data=%p", opensles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
error = opensles->receive(&opensles->format, data, size, opensles->user_data);
|
||||
|
||||
if (error && opensles->rdpcontext)
|
||||
setChannelError(opensles->rdpcontext, error, "audin_receive reported an error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
|
||||
|
||||
free(opensles->device_name);
|
||||
free(opensles);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_opensles_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles || !format)
|
||||
return FALSE;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*)opensles, (void*)format);
|
||||
WINPR_ASSERT(format);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04" PRIX16 "] not supported",
|
||||
audio_format_get_tag_string(format->wFormatTag), format->wFormatTag);
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%" PRIu32 "",
|
||||
(void*)device, (void*)format, FramesPerPacket);
|
||||
WINPR_ASSERT(format);
|
||||
|
||||
opensles->format = *format;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
opensles->frames_per_packet = FramesPerPacket;
|
||||
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 4:
|
||||
opensles->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
opensles->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
opensles->bytes_per_channel = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_Print(opensles->log, WLOG_ERROR,
|
||||
"Encoding '%" PRIu16 "' [%04" PRIX16 "] not supported", format->wFormatTag,
|
||||
format->wFormatTag);
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "frames_per_packet=%" PRIu32,
|
||||
opensles->frames_per_packet);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*)device,
|
||||
(void*)receive, (void*)user_data);
|
||||
|
||||
if (opensles->stream)
|
||||
goto error_out;
|
||||
|
||||
if (!(opensles->stream = android_OpenRecDevice(
|
||||
opensles, audin_receive, opensles->format.nSamplesPerSec, opensles->format.nChannels,
|
||||
opensles->frames_per_packet, opensles->format.wBitsPerSample)))
|
||||
{
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
opensles->receive = receive;
|
||||
opensles->user_data = user_data;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
audin_opensles_close(device);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT audin_opensles_close(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*)device);
|
||||
android_CloseRecDevice(opensles->stream);
|
||||
opensles->receive = nullptr;
|
||||
opensles->user_data = nullptr;
|
||||
opensles->stream = nullptr;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, const ADDIN_ARGV* args)
|
||||
{
|
||||
UINT status;
|
||||
DWORD flags;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, args=%p", (void*)device, (void*)args);
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_opensles_args, flags,
|
||||
opensles, nullptr, nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
arg = audin_opensles_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
opensles->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!opensles->device_name)
|
||||
{
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)calloc(1, sizeof(AudinOpenSLESDevice));
|
||||
|
||||
if (!opensles)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
opensles->log = WLog_Get(TAG);
|
||||
opensles->iface.Open = audin_opensles_open;
|
||||
opensles->iface.FormatSupported = audin_opensles_format_supported;
|
||||
opensles->iface.SetFormat = audin_opensles_set_format;
|
||||
opensles->iface.Close = audin_opensles_close;
|
||||
opensles->iface.Free = audin_opensles_free;
|
||||
opensles->rdpcontext = pEntryPoints->rdpcontext;
|
||||
const ADDIN_ARGV* args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_opensles_parse_addin_args(opensles, args)))
|
||||
{
|
||||
WLog_Print(opensles->log, WLOG_ERROR,
|
||||
"audin_opensles_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)opensles)))
|
||||
{
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(opensles);
|
||||
return error;
|
||||
}
|
||||
388
third_party/FreeRDP/channels/audin/client/opensles/opensl_io.c
vendored
Normal file
388
third_party/FreeRDP/channels/audin/client/opensles/opensl_io.c
vendored
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
opensl_io.c:
|
||||
Android OpenSL input/output module
|
||||
Copyright (c) 2012, Victor Lazzarini
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
#include "opensl_io.h"
|
||||
#define CONV16BIT 32768
|
||||
#define CONVMYFLT (1. / 32768.)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t size;
|
||||
void* data;
|
||||
} queue_element;
|
||||
|
||||
struct opensl_stream
|
||||
{
|
||||
// engine interfaces
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engineEngine;
|
||||
|
||||
// device interfaces
|
||||
SLDeviceVolumeItf deviceVolume;
|
||||
|
||||
// recorder interfaces
|
||||
SLObjectItf recorderObject;
|
||||
SLRecordItf recorderRecord;
|
||||
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
|
||||
|
||||
unsigned int inchannels;
|
||||
unsigned int sr;
|
||||
unsigned int buffersize;
|
||||
unsigned int bits_per_sample;
|
||||
|
||||
queue_element* prep;
|
||||
queue_element* next;
|
||||
|
||||
void* context;
|
||||
opensl_receive_t receive;
|
||||
};
|
||||
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
|
||||
// creates the OpenSL ES audio engine
|
||||
static SLresult openSLCreateEngine(OPENSL_STREAM* p)
|
||||
{
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&(p->engineObject), 0, nullptr, 0, nullptr, nullptr);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
goto engine_end;
|
||||
|
||||
// realize the engine
|
||||
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
goto engine_end;
|
||||
|
||||
// get the engine interface, which is needed in order to create other objects
|
||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
goto engine_end;
|
||||
|
||||
// get the volume interface - important, this is optional!
|
||||
result =
|
||||
(*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME, &(p->deviceVolume));
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
p->deviceVolume = nullptr;
|
||||
result = SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
engine_end:
|
||||
WINPR_ASSERT(SL_RESULT_SUCCESS == result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Open the OpenSL ES device for input
|
||||
static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
{
|
||||
SLresult result;
|
||||
SLuint32 sr = p->sr;
|
||||
SLuint32 channels = p->inchannels;
|
||||
WINPR_ASSERT(!p->recorderObject);
|
||||
|
||||
if (channels)
|
||||
{
|
||||
switch (sr)
|
||||
{
|
||||
case 8000:
|
||||
sr = SL_SAMPLINGRATE_8;
|
||||
break;
|
||||
|
||||
case 11025:
|
||||
sr = SL_SAMPLINGRATE_11_025;
|
||||
break;
|
||||
|
||||
case 16000:
|
||||
sr = SL_SAMPLINGRATE_16;
|
||||
break;
|
||||
|
||||
case 22050:
|
||||
sr = SL_SAMPLINGRATE_22_05;
|
||||
break;
|
||||
|
||||
case 24000:
|
||||
sr = SL_SAMPLINGRATE_24;
|
||||
break;
|
||||
|
||||
case 32000:
|
||||
sr = SL_SAMPLINGRATE_32;
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
sr = SL_SAMPLINGRATE_44_1;
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
sr = SL_SAMPLINGRATE_48;
|
||||
break;
|
||||
|
||||
case 64000:
|
||||
sr = SL_SAMPLINGRATE_64;
|
||||
break;
|
||||
|
||||
case 88200:
|
||||
sr = SL_SAMPLINGRATE_88_2;
|
||||
break;
|
||||
|
||||
case 96000:
|
||||
sr = SL_SAMPLINGRATE_96;
|
||||
break;
|
||||
|
||||
case 192000:
|
||||
sr = SL_SAMPLINGRATE_192;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
// configure audio source
|
||||
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr };
|
||||
SLDataSource audioSrc = { &loc_dev, nullptr };
|
||||
// configure audio sink
|
||||
int speakers;
|
||||
|
||||
if (channels > 1)
|
||||
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
else
|
||||
speakers = SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
2 };
|
||||
SLDataFormat_PCM format_pcm;
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = channels;
|
||||
format_pcm.samplesPerSec = sr;
|
||||
format_pcm.channelMask = speakers;
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
|
||||
if (16 == p->bits_per_sample)
|
||||
{
|
||||
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
format_pcm.containerSize = 16;
|
||||
}
|
||||
else if (8 == p->bits_per_sample)
|
||||
{
|
||||
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
|
||||
format_pcm.containerSize = 8;
|
||||
}
|
||||
else
|
||||
WINPR_ASSERT(0);
|
||||
|
||||
SLDataSink audioSnk = { &loc_bq, &format_pcm };
|
||||
// create audio recorder
|
||||
// (requires the RECORD_AUDIO permission)
|
||||
const SLInterfaceID id[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||
const SLboolean req[] = { SL_BOOLEAN_TRUE };
|
||||
result = (*p->engineEngine)
|
||||
->CreateAudioRecorder(p->engineEngine, &(p->recorderObject), &audioSrc,
|
||||
&audioSnk, 1, id, req);
|
||||
WINPR_ASSERT(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// realize the audio recorder
|
||||
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
|
||||
WINPR_ASSERT(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// get the record interface
|
||||
result = (*p->recorderObject)
|
||||
->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord));
|
||||
WINPR_ASSERT(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// get the buffer queue interface
|
||||
result = (*p->recorderObject)
|
||||
->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&(p->recorderBufferQueue));
|
||||
WINPR_ASSERT(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
// register callback on the buffer queue
|
||||
result = (*p->recorderBufferQueue)
|
||||
->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback, p);
|
||||
WINPR_ASSERT(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
goto end_recopen;
|
||||
|
||||
end_recopen:
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// close the OpenSL IO and destroy the audio engine
|
||||
static void openSLDestroyEngine(OPENSL_STREAM* p)
|
||||
{
|
||||
// destroy audio recorder object, and invalidate all associated interfaces
|
||||
if (p->recorderObject != nullptr)
|
||||
{
|
||||
(*p->recorderObject)->Destroy(p->recorderObject);
|
||||
p->recorderObject = nullptr;
|
||||
p->recorderRecord = nullptr;
|
||||
p->recorderBufferQueue = nullptr;
|
||||
}
|
||||
|
||||
// destroy engine object, and invalidate all associated interfaces
|
||||
if (p->engineObject != nullptr)
|
||||
{
|
||||
(*p->engineObject)->Destroy(p->engineObject);
|
||||
p->engineObject = nullptr;
|
||||
p->engineEngine = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static queue_element* opensles_queue_element_new(size_t size)
|
||||
{
|
||||
queue_element* q = calloc(1, sizeof(queue_element));
|
||||
|
||||
if (!q)
|
||||
goto fail;
|
||||
|
||||
q->size = size;
|
||||
q->data = malloc(size);
|
||||
|
||||
if (!q->data)
|
||||
goto fail;
|
||||
|
||||
return q;
|
||||
fail:
|
||||
free(q);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void opensles_queue_element_free(void* obj)
|
||||
{
|
||||
queue_element* e = (queue_element*)obj;
|
||||
|
||||
if (e)
|
||||
free(e->data);
|
||||
|
||||
free(e);
|
||||
}
|
||||
|
||||
// open the android audio device for input
|
||||
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, int sr,
|
||||
int inchannels, int bufferframes, int bits_per_sample)
|
||||
{
|
||||
OPENSL_STREAM* p;
|
||||
|
||||
if (!context || !receive)
|
||||
return nullptr;
|
||||
|
||||
p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
|
||||
|
||||
if (!p)
|
||||
return nullptr;
|
||||
|
||||
p->context = context;
|
||||
p->receive = receive;
|
||||
p->inchannels = inchannels;
|
||||
p->sr = sr;
|
||||
p->buffersize = bufferframes;
|
||||
p->bits_per_sample = bits_per_sample;
|
||||
|
||||
if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16))
|
||||
goto fail;
|
||||
|
||||
if (openSLCreateEngine(p) != SL_RESULT_SUCCESS)
|
||||
goto fail;
|
||||
|
||||
if (openSLRecOpen(p) != SL_RESULT_SUCCESS)
|
||||
goto fail;
|
||||
|
||||
/* Create receive buffers, prepare them and start recording */
|
||||
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
|
||||
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
|
||||
|
||||
if (!p->prep || !p->next)
|
||||
goto fail;
|
||||
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->next->data, p->next->size);
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, p->prep->data, p->prep->size);
|
||||
(*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||
return p;
|
||||
fail:
|
||||
android_CloseRecDevice(p);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// close the android audio device
|
||||
void android_CloseRecDevice(OPENSL_STREAM* p)
|
||||
{
|
||||
if (p == nullptr)
|
||||
return;
|
||||
|
||||
opensles_queue_element_free(p->next);
|
||||
opensles_queue_element_free(p->prep);
|
||||
openSLDestroyEngine(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
// this callback handler is called every time a buffer finishes recording
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||
{
|
||||
OPENSL_STREAM* p = (OPENSL_STREAM*)context;
|
||||
queue_element* e;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
e = p->next;
|
||||
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
if (!p->context || !p->receive)
|
||||
WLog_WARN(TAG, "Missing receive callback=%p, context=%p", p->receive, p->context);
|
||||
else
|
||||
p->receive(p->context, e->data, e->size);
|
||||
|
||||
p->next = p->prep;
|
||||
p->prep = e;
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, e->data, e->size);
|
||||
}
|
||||
67
third_party/FreeRDP/channels/audin/client/opensles/opensl_io.h
vendored
Normal file
67
third_party/FreeRDP/channels/audin/client/opensles/opensl_io.h
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
opensl_io.c:
|
||||
Android OpenSL input/output module header
|
||||
Copyright (c) 2012, Victor Lazzarini
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H
|
||||
#define FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct opensl_stream OPENSL_STREAM;
|
||||
|
||||
typedef void (*opensl_receive_t)(void* context, const void* data, size_t size);
|
||||
|
||||
/*
|
||||
Close the audio device
|
||||
*/
|
||||
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
|
||||
|
||||
/*
|
||||
Open the audio device with a given sampling rate (sr), input and output channels and IO buffer
|
||||
size in frames. Returns a handle to the OpenSL stream
|
||||
*/
|
||||
WINPR_ATTR_MALLOC(android_CloseRecDevice, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
||||
int sr, int inchannels, int bufferframes,
|
||||
int bits_per_sample);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_OPENSL_IO_H */
|
||||
31
third_party/FreeRDP/channels/audin/client/oss/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/channels/audin/client/oss/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.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.
|
||||
|
||||
define_channel_client_subsystem("audin" "oss" "")
|
||||
|
||||
find_package(OSS REQUIRED)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_oss.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${OSS_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(SYSTEM ${OSS_INCLUDE_DIRS})
|
||||
cleaning_configure_file(${CMAKE_SOURCE_DIR}/cmake/oss-includes.h.in ${CMAKE_CURRENT_BINARY_DIR}/oss-includes.h @ONLY)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
449
third_party/FreeRDP/channels/audin/client/oss/audin_oss.c
vendored
Normal file
449
third_party/FreeRDP/channels/audin/client/oss/audin_oss.c
vendored
Normal file
@@ -0,0 +1,449 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - OSS implementation
|
||||
*
|
||||
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <oss-includes.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinOSSDevice;
|
||||
|
||||
static void OSS_LOG_ERR(const char* _text, int _error)
|
||||
{
|
||||
if ((_error) != 0)
|
||||
{
|
||||
char buffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "%s: %i - %s\n", (_text), (_error),
|
||||
winpr_strerror((_error), buffer, sizeof(buffer)));
|
||||
}
|
||||
}
|
||||
|
||||
static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
return AFMT_S8;
|
||||
|
||||
case 16:
|
||||
return AFMT_S16_LE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (device == nullptr || format == nullptr)
|
||||
return FALSE;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
|
||||
(format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
|
||||
(format->nChannels != 1 && format->nChannels != 2))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
oss->FramesPerPacket = FramesPerPacket;
|
||||
oss->format = *format;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
{
|
||||
char dev_name[PATH_MAX] = "/dev/dsp";
|
||||
int pcm_handle = -1;
|
||||
BYTE* buffer = nullptr;
|
||||
unsigned long tmp = 0;
|
||||
size_t buffer_size = 0;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
||||
UINT error = 0;
|
||||
DWORD status = 0;
|
||||
|
||||
if (oss == nullptr)
|
||||
{
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (oss->dev_unit != -1)
|
||||
(void)sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
|
||||
|
||||
WLog_INFO(TAG, "open: %s", dev_name);
|
||||
|
||||
if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
|
||||
{
|
||||
OSS_LOG_ERR("sound dev open failed", errno);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Set format. */
|
||||
tmp = audin_oss_get_format(&oss->format);
|
||||
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
|
||||
|
||||
tmp = oss->format.nChannels;
|
||||
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
|
||||
|
||||
tmp = oss->format.nSamplesPerSec;
|
||||
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
|
||||
|
||||
tmp = oss->format.nBlockAlign;
|
||||
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
||||
|
||||
buffer_size =
|
||||
(1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
|
||||
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
|
||||
|
||||
if (nullptr == buffer)
|
||||
{
|
||||
OSS_LOG_ERR("malloc() fail", errno);
|
||||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
SSIZE_T stmp = -1;
|
||||
status = WaitForSingleObject(oss->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
stmp = read(pcm_handle, buffer, buffer_size);
|
||||
|
||||
/* Error happen. */
|
||||
if (stmp < 0)
|
||||
{
|
||||
OSS_LOG_ERR("read() error", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((size_t)stmp < buffer_size) /* Not enough data. */
|
||||
continue;
|
||||
|
||||
if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "oss->receive failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err_out:
|
||||
|
||||
if (error && oss && oss->rdpcontext)
|
||||
setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
|
||||
|
||||
if (pcm_handle != -1)
|
||||
{
|
||||
WLog_INFO(TAG, "close: %s", dev_name);
|
||||
close(pcm_handle);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
oss->receive = receive;
|
||||
oss->user_data = user_data;
|
||||
|
||||
if (!(oss->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(oss->thread = CreateThread(nullptr, 0, audin_oss_thread_func, oss, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
(void)CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error = 0;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (oss->stopEvent != nullptr)
|
||||
{
|
||||
(void)SetEvent(oss->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = nullptr;
|
||||
(void)CloseHandle(oss->thread);
|
||||
oss->thread = nullptr;
|
||||
}
|
||||
|
||||
oss->receive = nullptr;
|
||||
oss->user_data = nullptr;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
UINT error = 0;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_oss_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %" PRIu32 "!", error);
|
||||
}
|
||||
|
||||
free(oss);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV* args)
|
||||
{
|
||||
int status = 0;
|
||||
char* str_num = nullptr;
|
||||
char* eptr = nullptr;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
|
||||
AudinOSSDevice* oss = device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_oss_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, nullptr,
|
||||
nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_oss_args;
|
||||
errno = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
|
||||
if (!str_num)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
{
|
||||
long val = strtol(str_num, &eptr, 10);
|
||||
|
||||
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
|
||||
{
|
||||
free(str_num);
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
}
|
||||
|
||||
oss->dev_unit = (INT32)val;
|
||||
}
|
||||
|
||||
if (oss->dev_unit < 0 || *eptr != '\0')
|
||||
oss->dev_unit = -1;
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = nullptr;
|
||||
AudinOSSDevice* oss = nullptr;
|
||||
UINT error = 0;
|
||||
oss = (AudinOSSDevice*)calloc(1, sizeof(AudinOSSDevice));
|
||||
|
||||
if (!oss)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
oss->iface.Open = audin_oss_open;
|
||||
oss->iface.FormatSupported = audin_oss_format_supported;
|
||||
oss->iface.SetFormat = audin_oss_set_format;
|
||||
oss->iface.Close = audin_oss_close;
|
||||
oss->iface.Free = audin_oss_free;
|
||||
oss->rdpcontext = pEntryPoints->rdpcontext;
|
||||
oss->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_oss_parse_addin_args(oss, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(oss);
|
||||
return error;
|
||||
}
|
||||
30
third_party/FreeRDP/channels/audin/client/pulse/CMakeLists.txt
vendored
Normal file
30
third_party/FreeRDP/channels/audin/client/pulse/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client_subsystem("audin" "pulse" "")
|
||||
|
||||
find_package(PulseAudio REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("libpulse")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_pulse.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${PULSEAUDIO_INCLUDE_DIR})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
584
third_party/FreeRDP/channels/audin/client/pulse/audin_pulse.c
vendored
Normal file
584
third_party/FreeRDP/channels/audin/client/pulse/audin_pulse.c
vendored
Normal file
@@ -0,0 +1,584 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - PulseAudio implementation
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/audio.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
#include <freerdp/utils/helpers.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
char* client_name;
|
||||
char* stream_name;
|
||||
UINT32 frames_per_packet;
|
||||
pa_threaded_mainloop* mainloop;
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream* stream;
|
||||
AUDIO_FORMAT format;
|
||||
|
||||
size_t bytes_per_frame;
|
||||
size_t buffer_frames;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinPulseDevice;
|
||||
|
||||
static const char* pulse_context_state_string(pa_context_state_t state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
return "PA_CONTEXT_UNCONNECTED";
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
return "PA_CONTEXT_CONNECTING";
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
return "PA_CONTEXT_AUTHORIZING";
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
return "PA_CONTEXT_SETTING_NAME";
|
||||
case PA_CONTEXT_READY:
|
||||
return "PA_CONTEXT_READY";
|
||||
case PA_CONTEXT_FAILED:
|
||||
return "PA_CONTEXT_FAILED";
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
return "PA_CONTEXT_TERMINATED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* pulse_stream_state_string(pa_stream_state_t state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
return "PA_STREAM_UNCONNECTED";
|
||||
case PA_STREAM_CREATING:
|
||||
return "PA_STREAM_CREATING";
|
||||
case PA_STREAM_READY:
|
||||
return "PA_STREAM_READY";
|
||||
case PA_STREAM_FAILED:
|
||||
return "PA_STREAM_FAILED";
|
||||
case PA_STREAM_TERMINATED:
|
||||
return "PA_STREAM_TERMINATED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
pa_context_state_t state = pa_context_get_state(context);
|
||||
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "context state %s", pulse_context_state_string(state));
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
{
|
||||
pa_context_state_t state = PA_CONTEXT_FAILED;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (pa_context_connect(pulse->context, nullptr, PA_CONTEXT_NOFLAGS, nullptr))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_context_get_state(pulse->context);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%s: %d)",
|
||||
pulse_context_state_string(state), pa_context_errno(pulse->context));
|
||||
pa_context_disconnect(pulse->context);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_free(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (pulse->context)
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
pa_context_unref(pulse->context);
|
||||
pulse->context = nullptr;
|
||||
}
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_free(pulse->mainloop);
|
||||
pulse->mainloop = nullptr;
|
||||
}
|
||||
|
||||
free(pulse->device_name);
|
||||
free(pulse->client_name);
|
||||
free(pulse->stream_name);
|
||||
free(pulse);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return FALSE;
|
||||
|
||||
if (!pulse->context)
|
||||
return 0;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (FramesPerPacket > 0)
|
||||
pulse->frames_per_packet = FramesPerPacket;
|
||||
|
||||
pa_sample_format_t sformat = PA_SAMPLE_INVALID;
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
sformat = PA_SAMPLE_U8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
sformat = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const pa_sample_spec sample_spec = {
|
||||
.format = sformat,
|
||||
.rate = format->nSamplesPerSec,
|
||||
.channels = WINPR_ASSERTING_INT_CAST(uint8_t, format->nChannels),
|
||||
};
|
||||
|
||||
pulse->sample_spec = sample_spec;
|
||||
pulse->format = *format;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
pa_stream_state_t state = pa_stream_get_state(stream);
|
||||
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "stream state %s", pulse_stream_state_string(state));
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
const void* data = nullptr;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
pa_stream_peek(stream, &data, &length);
|
||||
error =
|
||||
IFCALLRESULT(CHANNEL_RC_OK, pulse->receive, &pulse->format, data, length, pulse->user_data);
|
||||
pa_stream_drop(stream);
|
||||
|
||||
if (error && pulse->rdpcontext)
|
||||
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_close(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = nullptr;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
pulse->receive = nullptr;
|
||||
pulse->user_data = nullptr;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
pa_stream_state_t state = PA_STREAM_FAILED;
|
||||
pa_buffer_attr buffer_attr = WINPR_C_ARRAY_INIT;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
|
||||
if (!pulse || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->sample_spec.rate || pulse->stream)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
pulse->receive = receive;
|
||||
pulse->user_data = user_data;
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pulse->stream = pa_stream_new(pulse->context, pulse->stream_name, &pulse->sample_spec, nullptr);
|
||||
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
const int rc = pa_context_errno(pulse->context);
|
||||
return (UINT)rc;
|
||||
}
|
||||
|
||||
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
|
||||
pa_stream_set_state_callback(pulse->stream, audin_pulse_stream_state_callback, pulse);
|
||||
pa_stream_set_read_callback(pulse->stream, audin_pulse_stream_request_callback, pulse);
|
||||
buffer_attr.maxlength = (UINT32)-1;
|
||||
buffer_attr.tlength = (UINT32)-1;
|
||||
buffer_attr.prebuf = (UINT32)-1;
|
||||
buffer_attr.minreq = (UINT32)-1;
|
||||
/* 500ms latency */
|
||||
const size_t frag = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||
WINPR_ASSERT(frag <= UINT32_MAX);
|
||||
buffer_attr.fragsize = (uint32_t)frag;
|
||||
|
||||
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
||||
buffer_attr.fragsize +=
|
||||
pulse->format.nBlockAlign - buffer_attr.fragsize % pulse->format.nBlockAlign;
|
||||
|
||||
if (pa_stream_connect_record(pulse->stream, pulse->device_name, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
const int rc = pa_context_errno(pulse->context);
|
||||
return (UINT)rc;
|
||||
}
|
||||
|
||||
while (pulse->stream)
|
||||
{
|
||||
state = pa_stream_get_state(pulse->stream);
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
audin_pulse_close(device);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%s: %d)",
|
||||
pulse_stream_state_string(state), pa_context_errno(pulse->context));
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
const int rc = pa_context_errno(pulse->context);
|
||||
return (UINT)rc;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
pulse->buffer_frames = 0;
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_parse_addin_args(AudinPulseDevice* pulse, const ADDIN_ARGV* args)
|
||||
{
|
||||
COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ "client_name", COMMAND_LINE_VALUE_REQUIRED, "<client_name>", nullptr, nullptr, -1,
|
||||
nullptr, "name of pulse client" },
|
||||
{ "stream_name", COMMAND_LINE_VALUE_REQUIRED, "<stream_name>", nullptr, nullptr, -1,
|
||||
nullptr, "name of pulse stream" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
|
||||
const DWORD flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_pulse_args, flags,
|
||||
pulse, nullptr, nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = audin_pulse_args;
|
||||
|
||||
const char* client_name = nullptr;
|
||||
const char* stream_name = nullptr;
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
pulse->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!pulse->device_name)
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
if (!client_name)
|
||||
client_name = freerdp_getApplicationDetailsString();
|
||||
if (!stream_name)
|
||||
stream_name = freerdp_getApplicationDetailsString();
|
||||
|
||||
pulse->client_name = _strdup(client_name);
|
||||
pulse->stream_name = _strdup(stream_name);
|
||||
if (!pulse->client_name || !pulse->stream_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE pulse_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = nullptr;
|
||||
AudinPulseDevice* pulse = nullptr;
|
||||
UINT error = 0;
|
||||
pulse = (AudinPulseDevice*)calloc(1, sizeof(AudinPulseDevice));
|
||||
|
||||
if (!pulse)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
pulse->log = WLog_Get(TAG);
|
||||
pulse->iface.Open = audin_pulse_open;
|
||||
pulse->iface.FormatSupported = audin_pulse_format_supported;
|
||||
pulse->iface.SetFormat = audin_pulse_set_format;
|
||||
pulse->iface.Close = audin_pulse_close;
|
||||
pulse->iface.Free = audin_pulse_free;
|
||||
pulse->rdpcontext = pEntryPoints->rdpcontext;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_pulse_parse_addin_args(pulse, args)))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR,
|
||||
"audin_pulse_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_new failed");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->context =
|
||||
pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), pulse->client_name);
|
||||
|
||||
if (!pulse->context)
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_new failed");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
|
||||
|
||||
if ((error = audin_pulse_connect(&pulse->iface)))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_connect failed");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &pulse->iface)))
|
||||
{
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
audin_pulse_free(&pulse->iface);
|
||||
return error;
|
||||
}
|
||||
30
third_party/FreeRDP/channels/audin/client/sndio/CMakeLists.txt
vendored
Normal file
30
third_party/FreeRDP/channels/audin/client/sndio/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
|
||||
# Copyright (c) 2020 Ingo Feinerer <feinerer@logic.at>
|
||||
#
|
||||
# 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_subsystem("audin" "sndio" "")
|
||||
|
||||
find_package(SNDIO REQUIRED)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_sndio.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${SNDIO_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${SNDIO_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
353
third_party/FreeRDP/channels/audin/client/sndio/audin_sndio.c
vendored
Normal file
353
third_party/FreeRDP/channels/audin/client/sndio/audin_sndio.c
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - sndio implementation
|
||||
*
|
||||
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2020 Ingo Feinerer <feinerer@logic.at>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice device;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinSndioDevice;
|
||||
|
||||
static BOOL audin_sndio_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (device == nullptr || format == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return (format->wFormatTag == WAVE_FORMAT_PCM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_sndio_set_format(IAudinDevice* device, AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
|
||||
|
||||
if (device == nullptr || format == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
sndio->format = *format;
|
||||
sndio->FramesPerPacket = FramesPerPacket;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void* audin_sndio_thread_func(void* arg)
|
||||
{
|
||||
struct sio_hdl* hdl;
|
||||
struct sio_par par;
|
||||
BYTE* buffer = nullptr;
|
||||
size_t n, nbytes;
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
|
||||
UINT error = 0;
|
||||
DWORD status;
|
||||
|
||||
if (arg == nullptr)
|
||||
{
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
|
||||
if (hdl == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "could not open audio device");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
sio_initpar(&par);
|
||||
par.bits = sndio->format.wBitsPerSample;
|
||||
par.rchan = sndio->format.nChannels;
|
||||
par.rate = sndio->format.nSamplesPerSec;
|
||||
if (!sio_setpar(hdl, &par))
|
||||
{
|
||||
WLog_ERR(TAG, "could not set audio parameters");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
if (!sio_getpar(hdl, &par))
|
||||
{
|
||||
WLog_ERR(TAG, "could not get audio parameters");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!sio_start(hdl))
|
||||
{
|
||||
WLog_ERR(TAG, "could not start audio device");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
nbytes =
|
||||
(sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));
|
||||
buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForSingleObject(sndio->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
n = sio_read(hdl, buffer, nbytes);
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "could not read");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n < nbytes)
|
||||
continue;
|
||||
|
||||
if ((error = sndio->receive(&sndio->format, buffer, nbytes, sndio->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "sndio->receive failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err_out:
|
||||
if (error && sndio && sndio->rdpcontext)
|
||||
setChannelError(sndio->rdpcontext, error, "audin_sndio_thread_func reported an error");
|
||||
|
||||
if (hdl != nullptr)
|
||||
{
|
||||
WLog_INFO(TAG, "sio_close");
|
||||
sio_stop(hdl);
|
||||
sio_close(hdl);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
ExitThread(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_sndio_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
|
||||
sndio->receive = receive;
|
||||
sndio->user_data = user_data;
|
||||
|
||||
if (!(sndio->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(sndio->thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)audin_sndio_thread_func,
|
||||
sndio, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed");
|
||||
(void)CloseHandle(sndio->stopEvent);
|
||||
sndio->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_sndio_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error;
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (sndio->stopEvent != nullptr)
|
||||
{
|
||||
(void)SetEvent(sndio->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(sndio->stopEvent);
|
||||
sndio->stopEvent = nullptr;
|
||||
(void)CloseHandle(sndio->thread);
|
||||
sndio->thread = nullptr;
|
||||
}
|
||||
|
||||
sndio->receive = nullptr;
|
||||
sndio->user_data = nullptr;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_sndio_free(IAudinDevice* device)
|
||||
{
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
|
||||
int error;
|
||||
|
||||
if (device == nullptr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_sndio_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_sndio_close failed with error code %d", error);
|
||||
}
|
||||
|
||||
free(sndio);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_sndio_parse_addin_args(AudinSndioDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinSndioDevice* sndio = (AudinSndioDevice*)device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_sndio_args[] = { { nullptr, 0, nullptr, nullptr, nullptr, -1,
|
||||
nullptr, nullptr } };
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_sndio_args,
|
||||
flags, sndio, nullptr, nullptr);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_sndio_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinSndioDevice* sndio;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
sndio = (AudinSndioDevice*)calloc(1, sizeof(AudinSndioDevice));
|
||||
|
||||
if (sndio == nullptr)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
sndio->device.Open = audin_sndio_open;
|
||||
sndio->device.FormatSupported = audin_sndio_format_supported;
|
||||
sndio->device.SetFormat = audin_sndio_set_format;
|
||||
sndio->device.Close = audin_sndio_close;
|
||||
sndio->device.Free = audin_sndio_free;
|
||||
sndio->rdpcontext = pEntryPoints->rdpcontext;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if (args->argc > 1)
|
||||
{
|
||||
ret = audin_sndio_parse_addin_args(sndio, args);
|
||||
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing arguments");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)sndio)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ret;
|
||||
error:
|
||||
audin_sndio_free(&sndio->device);
|
||||
return ret;
|
||||
}
|
||||
26
third_party/FreeRDP/channels/audin/client/winmm/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/audin/client/winmm/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client_subsystem("audin" "winmm" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin_winmm.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp winmm.lib)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
568
third_party/FreeRDP/channels/audin/client/winmm/audin_winmm.c
vendored
Normal file
568
third_party/FreeRDP/channels/audin/client/winmm/audin_winmm.c
vendored
Normal file
@@ -0,0 +1,568 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - WinMM implementation
|
||||
*
|
||||
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
/* fix missing definitions in mingw */
|
||||
#ifndef WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE
|
||||
#define WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE 0x0010
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
HWAVEIN hWaveIn;
|
||||
PWAVEFORMATEX* ppwfx;
|
||||
PWAVEFORMATEX pwfx_cur;
|
||||
UINT32 ppwfx_size;
|
||||
UINT32 cFormats;
|
||||
UINT32 frames_per_packet;
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinWinmmDevice;
|
||||
|
||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)dwInstance;
|
||||
PWAVEHDR pWaveHdr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
MMRESULT mmResult;
|
||||
|
||||
switch (uMsg)
|
||||
{
|
||||
case WIM_CLOSE:
|
||||
break;
|
||||
|
||||
case WIM_DATA:
|
||||
pWaveHdr = (WAVEHDR*)dwParam1;
|
||||
|
||||
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
|
||||
{
|
||||
if (pWaveHdr->dwBytesRecorded &&
|
||||
!(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
{
|
||||
AUDIO_FORMAT format;
|
||||
format.cbSize = winmm->pwfx_cur->cbSize;
|
||||
format.nBlockAlign = winmm->pwfx_cur->nBlockAlign;
|
||||
format.nAvgBytesPerSec = winmm->pwfx_cur->nAvgBytesPerSec;
|
||||
format.nChannels = winmm->pwfx_cur->nChannels;
|
||||
format.nSamplesPerSec = winmm->pwfx_cur->nSamplesPerSec;
|
||||
format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
|
||||
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
|
||||
|
||||
if ((error = winmm->receive(&format, pWaveHdr->lpData,
|
||||
pWaveHdr->dwBytesRecorded, winmm->user_data)))
|
||||
break;
|
||||
|
||||
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WIM_OPEN:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||
}
|
||||
|
||||
static BOOL log_mmresult(AudinWinmmDevice* winmm, const char* what, MMRESULT result)
|
||||
{
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
CHAR buffer[8192] = WINPR_C_ARRAY_INIT;
|
||||
CHAR msg[8192] = WINPR_C_ARRAY_INIT;
|
||||
CHAR cmsg[8192] = WINPR_C_ARRAY_INIT;
|
||||
waveInGetErrorTextA(result, buffer, sizeof(buffer));
|
||||
|
||||
_snprintf(msg, sizeof(msg) - 1, "%s failed. %" PRIu32 " [%s]", what, result, buffer);
|
||||
_snprintf(cmsg, sizeof(cmsg) - 1, "audin_winmm_thread_func reported an error '%s'", msg);
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "%s", msg);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
|
||||
{
|
||||
MMRESULT rc;
|
||||
WAVEINCAPSA caps = WINPR_C_ARRAY_INIT;
|
||||
|
||||
rc = waveInGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps));
|
||||
if (rc != MMSYSERR_NOERROR)
|
||||
return FALSE;
|
||||
|
||||
switch (pwfx->nChannels)
|
||||
{
|
||||
case 1:
|
||||
if ((caps.dwFormats &
|
||||
(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
|
||||
WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
|
||||
return FALSE;
|
||||
break;
|
||||
case 2:
|
||||
if ((caps.dwFormats &
|
||||
(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
|
||||
WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = waveInOpen(nullptr, WAVE_MAPPER, pwfx, 0, 0,
|
||||
WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||
return (rc == MMSYSERR_NOERROR);
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
||||
char* buffer = nullptr;
|
||||
int size = 0;
|
||||
WAVEHDR waveHdr[4] = WINPR_C_ARRAY_INIT;
|
||||
DWORD status = 0;
|
||||
MMRESULT rc = 0;
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
|
||||
(DWORD_PTR)winmm,
|
||||
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||
if (!log_mmresult(winmm, "waveInOpen", rc))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
size =
|
||||
(winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
|
||||
7) /
|
||||
8;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
buffer = (char*)malloc(size);
|
||||
|
||||
if (!buffer)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
waveHdr[i].dwBufferLength = size;
|
||||
waveHdr[i].dwFlags = 0;
|
||||
waveHdr[i].lpData = buffer;
|
||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
|
||||
{
|
||||
}
|
||||
|
||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
rc = waveInStart(winmm->hWaveIn);
|
||||
|
||||
if (!log_mmresult(winmm, "waveInStart", rc))
|
||||
{
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "WaitForSingleObject failed.");
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInReset(winmm->hWaveIn);
|
||||
|
||||
if (!log_mmresult(winmm, "waveInReset", rc))
|
||||
{
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
|
||||
{
|
||||
}
|
||||
|
||||
free(waveHdr[i].lpData);
|
||||
}
|
||||
|
||||
rc = waveInClose(winmm->hWaveIn);
|
||||
|
||||
if (!log_mmresult(winmm, "waveInClose", rc))
|
||||
{
|
||||
}
|
||||
|
||||
winmm->hWaveIn = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_free(IAudinDevice* device)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (UINT32 i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
free(winmm->ppwfx[i]);
|
||||
}
|
||||
|
||||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_close(IAudinDevice* device)
|
||||
{
|
||||
DWORD status;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
(void)SetEvent(winmm->stopEvent);
|
||||
status = WaitForSingleObject(winmm->thread, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(winmm->thread);
|
||||
(void)CloseHandle(winmm->stopEvent);
|
||||
winmm->thread = nullptr;
|
||||
winmm->stopEvent = nullptr;
|
||||
winmm->receive = nullptr;
|
||||
winmm->user_data = nullptr;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
winmm->frames_per_packet = FramesPerPacket;
|
||||
|
||||
for (UINT32 i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
|
||||
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
|
||||
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
|
||||
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
|
||||
{
|
||||
/* BUG: Many devices report to support stereo recording but fail here.
|
||||
* Ensure we always use mono. */
|
||||
if (ppwfx->nChannels > 1)
|
||||
{
|
||||
ppwfx->nChannels = 1;
|
||||
}
|
||||
|
||||
if (ppwfx->nBlockAlign != 2)
|
||||
{
|
||||
ppwfx->nBlockAlign = 2;
|
||||
ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign;
|
||||
}
|
||||
|
||||
if (!test_format_supported(ppwfx))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
winmm->pwfx_cur = ppwfx;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
PWAVEFORMATEX pwfx;
|
||||
BYTE* data;
|
||||
|
||||
if (!winmm || !format)
|
||||
return FALSE;
|
||||
|
||||
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
||||
return FALSE;
|
||||
|
||||
if (format->nChannels != 1)
|
||||
return FALSE;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
|
||||
if (!pwfx)
|
||||
return FALSE;
|
||||
|
||||
pwfx->cbSize = format->cbSize;
|
||||
pwfx->wFormatTag = format->wFormatTag;
|
||||
pwfx->nChannels = format->nChannels;
|
||||
pwfx->nSamplesPerSec = format->nSamplesPerSec;
|
||||
pwfx->nBlockAlign = format->nBlockAlign;
|
||||
pwfx->wBitsPerSample = format->wBitsPerSample;
|
||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||
memcpy(data, format->data, format->cbSize);
|
||||
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
|
||||
if (!test_format_supported(pwfx))
|
||||
goto fail;
|
||||
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
|
||||
if (!tmp_ppwfx)
|
||||
goto fail;
|
||||
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
free(pwfx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
|
||||
if (!winmm || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
winmm->receive = receive;
|
||||
winmm->user_data = user_data;
|
||||
|
||||
if (!(winmm->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(winmm->thread = CreateThread(nullptr, 0, audin_winmm_thread_func, winmm, 0, nullptr)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
||||
(void)CloseHandle(winmm->stopEvent);
|
||||
winmm->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, const ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_winmm_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr,
|
||||
"audio device name" },
|
||||
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
|
||||
};
|
||||
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags, winmm,
|
||||
nullptr, nullptr);
|
||||
arg = audin_winmm_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
winmm->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args;
|
||||
AudinWinmmDevice* winmm;
|
||||
UINT error;
|
||||
|
||||
if (waveInGetNumDevs() == 0)
|
||||
{
|
||||
WLog_Print(WLog_Get(TAG), WLOG_ERROR, "No microphone available!");
|
||||
return ERROR_DEVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
winmm = (AudinWinmmDevice*)calloc(1, sizeof(AudinWinmmDevice));
|
||||
|
||||
if (!winmm)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
winmm->log = WLog_Get(TAG);
|
||||
winmm->iface.Open = audin_winmm_open;
|
||||
winmm->iface.FormatSupported = audin_winmm_format_supported;
|
||||
winmm->iface.SetFormat = audin_winmm_set_format;
|
||||
winmm->iface.Close = audin_winmm_close;
|
||||
winmm->iface.Free = audin_winmm_free;
|
||||
winmm->rdpcontext = pEntryPoints->rdpcontext;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_winmm_parse_addin_args(winmm, args)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR,
|
||||
"audin_winmm_parse_addin_args failed with error %" PRIu32 "!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
winmm->device_name = _strdup("default");
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
|
||||
|
||||
if (!winmm->ppwfx)
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
return error;
|
||||
}
|
||||
23
third_party/FreeRDP/channels/audin/server/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/audin/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_server("audin")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS audin.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
927
third_party/FreeRDP/channels/audin/server/audin.c
vendored
Normal file
927
third_party/FreeRDP/channels/audin/server/audin.c
vendored
Normal file
@@ -0,0 +1,927 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Server Audio Input Virtual Channel
|
||||
*
|
||||
* Copyright 2012 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/server/server-common.h>
|
||||
#include <freerdp/server/audin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define AUDIN_TAG CHANNELS_TAG("audin.server")
|
||||
|
||||
#define SNDIN_HEADER_SIZE 1
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MSG_SNDIN_VERSION = 0x01,
|
||||
MSG_SNDIN_FORMATS = 0x02,
|
||||
MSG_SNDIN_OPEN = 0x03,
|
||||
MSG_SNDIN_OPEN_REPLY = 0x04,
|
||||
MSG_SNDIN_DATA_INCOMING = 0x05,
|
||||
MSG_SNDIN_DATA = 0x06,
|
||||
MSG_SNDIN_FORMATCHANGE = 0x07,
|
||||
} MSG_SNDIN;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
audin_server_context context;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* audin_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
AUDIO_FORMAT* audin_server_formats;
|
||||
UINT32 audin_n_server_formats;
|
||||
AUDIO_FORMAT* audin_negotiated_format;
|
||||
UINT32 audin_client_format_idx;
|
||||
wLog* log;
|
||||
} audin_server;
|
||||
|
||||
static UINT audin_server_recv_version(audin_server_context* context, wStream* s,
|
||||
const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_VERSION pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
{
|
||||
const UINT32 version = Stream_Get_UINT32(s);
|
||||
switch (version)
|
||||
{
|
||||
case SNDIN_VERSION_Version_1:
|
||||
pdu.Version = SNDIN_VERSION_Version_1;
|
||||
break;
|
||||
case SNDIN_VERSION_Version_2:
|
||||
pdu.Version = SNDIN_VERSION_Version_2;
|
||||
break;
|
||||
default:
|
||||
pdu.Version = SNDIN_VERSION_Version_2;
|
||||
WLog_Print(audin->log, WLOG_WARN,
|
||||
"Received unsupported channel version %" PRIu32
|
||||
", using highest supported version %u",
|
||||
version, pdu.Version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IFCALLRET(context->ReceiveVersion, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->ReceiveVersion failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_recv_formats(audin_server_context* context, wStream* s,
|
||||
const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_FORMATS pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
/* Implementations MUST, at a minimum, support WAVE_FORMAT_PCM (0x0001) */
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4 + 4 + 18))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, pdu.NumFormats);
|
||||
Stream_Read_UINT32(s, pdu.cbSizeFormatsPacket);
|
||||
|
||||
if (pdu.NumFormats == 0)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Sound Formats PDU contains no formats");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
pdu.SoundFormats = audio_formats_new(pdu.NumFormats);
|
||||
if (!pdu.SoundFormats)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Failed to allocate %u SoundFormats", pdu.NumFormats);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < pdu.NumFormats; ++i)
|
||||
{
|
||||
AUDIO_FORMAT* format = &pdu.SoundFormats[i];
|
||||
|
||||
if (!audio_format_read(s, format))
|
||||
goto fail;
|
||||
|
||||
audio_format_print(audin->log, WLOG_DEBUG, format);
|
||||
}
|
||||
|
||||
if (pdu.cbSizeFormatsPacket != Stream_GetPosition(s))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_WARN,
|
||||
"cbSizeFormatsPacket is invalid! Expected: %u Got: %zu. Fixing size",
|
||||
pdu.cbSizeFormatsPacket, Stream_GetPosition(s));
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
if (pos > UINT32_MAX)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Stream too long, %" PRIuz " exceeds UINT32_MAX",
|
||||
pos);
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto fail;
|
||||
}
|
||||
pdu.cbSizeFormatsPacket = (UINT32)pos;
|
||||
}
|
||||
|
||||
pdu.ExtraDataSize = Stream_GetRemainingLength(s);
|
||||
|
||||
IFCALLRET(context->ReceiveFormats, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->ReceiveFormats failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
fail:
|
||||
audio_formats_free(pdu.SoundFormats, pdu.NumFormats);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_recv_open_reply(audin_server_context* context, wStream* s,
|
||||
const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_OPEN_REPLY pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, pdu.Result);
|
||||
|
||||
IFCALLRET(context->OpenReply, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->OpenReply failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_recv_data_incoming(audin_server_context* context,
|
||||
WINPR_ATTR_UNUSED wStream* s, const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_DATA_INCOMING pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
IFCALLRET(context->IncomingData, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->IncomingData failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_recv_data(audin_server_context* context, wStream* s,
|
||||
const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_DATA pdu = WINPR_C_ARRAY_INIT;
|
||||
wStream dataBuffer = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
pdu.Data = Stream_StaticInit(&dataBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s));
|
||||
|
||||
IFCALLRET(context->Data, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->Data failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_recv_format_change(audin_server_context* context, wStream* s,
|
||||
const SNDIN_PDU* header)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
SNDIN_FORMATCHANGE pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, pdu.NewFormat);
|
||||
|
||||
IFCALLRET(context->ReceiveFormatChange, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"context->ReceiveFormatChange failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
void* buffer = nullptr;
|
||||
DWORD nCount = 0;
|
||||
HANDLE events[8] = WINPR_C_ARRAY_INIT;
|
||||
BOOL ready = FALSE;
|
||||
HANDLE ChannelEvent = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
audin_server* audin = (audin_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
ChannelEvent = *(HANDLE*)buffer;
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelQuery failed");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = audin->stopEvent;
|
||||
events[nCount++] = ChannelEvent;
|
||||
|
||||
/* Wait for the client to confirm that the Audio Input dynamic channel is ready */
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
goto out;
|
||||
}
|
||||
if (status == WAIT_OBJECT_0)
|
||||
goto out;
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelQuery failed");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ready = *((BOOL*)buffer);
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
break;
|
||||
}
|
||||
|
||||
s = Stream_New(nullptr, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ready)
|
||||
{
|
||||
SNDIN_VERSION version = WINPR_C_ARRAY_INIT;
|
||||
|
||||
version.Version = audin->context.serverVersion;
|
||||
|
||||
if ((error = audin->context.SendVersion(&audin->context, &version)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "SendVersion failed with error %" PRIu32 "!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
while (ready)
|
||||
{
|
||||
SNDIN_PDU header = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
Stream_ResetPosition(s);
|
||||
|
||||
if (!WTSVirtualChannelRead(audin->audin_channel, 0, nullptr, 0, &BytesReturned))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
break;
|
||||
|
||||
WINPR_ASSERT(Stream_Capacity(s) <= UINT32_MAX);
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
Stream_SetLength(s, BytesReturned);
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, SNDIN_HEADER_SIZE))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
Stream_Read_UINT8(s, header.MessageId);
|
||||
|
||||
switch (header.MessageId)
|
||||
{
|
||||
case MSG_SNDIN_VERSION:
|
||||
error = audin_server_recv_version(&audin->context, s, &header);
|
||||
break;
|
||||
case MSG_SNDIN_FORMATS:
|
||||
error = audin_server_recv_formats(&audin->context, s, &header);
|
||||
break;
|
||||
case MSG_SNDIN_OPEN_REPLY:
|
||||
error = audin_server_recv_open_reply(&audin->context, s, &header);
|
||||
break;
|
||||
case MSG_SNDIN_DATA_INCOMING:
|
||||
error = audin_server_recv_data_incoming(&audin->context, s, &header);
|
||||
break;
|
||||
case MSG_SNDIN_DATA:
|
||||
error = audin_server_recv_data(&audin->context, s, &header);
|
||||
break;
|
||||
case MSG_SNDIN_FORMATCHANGE:
|
||||
error = audin_server_recv_format_change(&audin->context, s, &header);
|
||||
break;
|
||||
default:
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"audin_server_thread_func: unknown or invalid MessageId %" PRIu8 "",
|
||||
header.MessageId);
|
||||
error = ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
out_capacity:
|
||||
Stream_Free(s, TRUE);
|
||||
out:
|
||||
(void)WTSVirtualChannelClose(audin->audin_channel);
|
||||
audin->audin_channel = nullptr;
|
||||
|
||||
if (error && audin->context.rdpcontext)
|
||||
setChannelError(audin->context.rdpcontext, error,
|
||||
"audin_server_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL audin_server_open(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
if (!audin->thread)
|
||||
{
|
||||
PULONG pSessionId = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
audin->SessionId = WTS_CURRENT_SESSION;
|
||||
UINT32 channelId = 0;
|
||||
BOOL status = TRUE;
|
||||
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned))
|
||||
{
|
||||
audin->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
}
|
||||
|
||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!audin->audin_channel)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelOpenEx failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
|
||||
|
||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "context->ChannelIdAssigned failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(audin->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "CreateEvent failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(audin->thread =
|
||||
CreateThread(nullptr, 0, audin_server_thread_func, (void*)audin, 0, nullptr)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "CreateThread failed!");
|
||||
(void)CloseHandle(audin->stopEvent);
|
||||
audin->stopEvent = nullptr;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WLog_Print(audin->log, WLOG_ERROR, "thread already running!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL audin_server_is_open(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
return audin->thread != nullptr;
|
||||
}
|
||||
|
||||
static BOOL audin_server_close(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
|
||||
if (audin->thread)
|
||||
{
|
||||
(void)SetEvent(audin->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
|
||||
GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
(void)CloseHandle(audin->thread);
|
||||
(void)CloseHandle(audin->stopEvent);
|
||||
audin->thread = nullptr;
|
||||
audin->stopEvent = nullptr;
|
||||
}
|
||||
|
||||
if (audin->audin_channel)
|
||||
{
|
||||
(void)WTSVirtualChannelClose(audin->audin_channel);
|
||||
audin->audin_channel = nullptr;
|
||||
}
|
||||
|
||||
audin->audin_negotiated_format = nullptr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static wStream* audin_server_packet_new(wLog* log, size_t size, BYTE MessageId)
|
||||
{
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
/* Allocate what we need plus header bytes */
|
||||
wStream* s = Stream_New(nullptr, size + SNDIN_HEADER_SIZE);
|
||||
if (!s)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(s, MessageId);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static UINT audin_server_packet_send(audin_server_context* context, wStream* s)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ULONG written = 0;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, Stream_BufferAs(s, char), (UINT32)pos,
|
||||
&written))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (written < Stream_GetPosition(s))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_WARN, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "",
|
||||
written, Stream_GetPosition(s));
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT audin_server_send_version(audin_server_context* context, const SNDIN_VERSION* version)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(version);
|
||||
|
||||
wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_VERSION);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, version->Version);
|
||||
|
||||
return audin_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT audin_server_send_formats(audin_server_context* context, const SNDIN_FORMATS* formats)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(formats);
|
||||
|
||||
wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18, MSG_SNDIN_FORMATS);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, formats->NumFormats);
|
||||
Stream_Write_UINT32(s, formats->cbSizeFormatsPacket);
|
||||
|
||||
for (UINT32 i = 0; i < formats->NumFormats; ++i)
|
||||
{
|
||||
AUDIO_FORMAT* format = &formats->SoundFormats[i];
|
||||
|
||||
if (!audio_format_write(s, format))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Failed to write audio format");
|
||||
Stream_Free(s, TRUE);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return audin_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT audin_server_send_open(audin_server_context* context, const SNDIN_OPEN* open)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(open);
|
||||
|
||||
wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18 + 22, MSG_SNDIN_OPEN);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, open->FramesPerPacket);
|
||||
Stream_Write_UINT32(s, open->initialFormat);
|
||||
|
||||
Stream_Write_UINT16(s, open->captureFormat.wFormatTag);
|
||||
Stream_Write_UINT16(s, open->captureFormat.nChannels);
|
||||
Stream_Write_UINT32(s, open->captureFormat.nSamplesPerSec);
|
||||
Stream_Write_UINT32(s, open->captureFormat.nAvgBytesPerSec);
|
||||
Stream_Write_UINT16(s, open->captureFormat.nBlockAlign);
|
||||
Stream_Write_UINT16(s, open->captureFormat.wBitsPerSample);
|
||||
|
||||
if (open->ExtraFormatData)
|
||||
{
|
||||
Stream_Write_UINT16(s, 22); /* cbSize */
|
||||
|
||||
Stream_Write_UINT16(s, open->ExtraFormatData->Samples.wReserved);
|
||||
Stream_Write_UINT32(s, open->ExtraFormatData->dwChannelMask);
|
||||
|
||||
Stream_Write_UINT32(s, open->ExtraFormatData->SubFormat.Data1);
|
||||
Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data2);
|
||||
Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data3);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[0]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[1]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[2]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[3]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[4]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[5]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[6]);
|
||||
Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINPR_ASSERT(open->captureFormat.wFormatTag != WAVE_FORMAT_EXTENSIBLE);
|
||||
|
||||
Stream_Write_UINT16(s, 0); /* cbSize */
|
||||
}
|
||||
|
||||
return audin_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT audin_server_send_format_change(audin_server_context* context,
|
||||
const SNDIN_FORMATCHANGE* format_change)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(format_change);
|
||||
|
||||
wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_FORMATCHANGE);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT32(s, format_change->NewFormat);
|
||||
|
||||
return audin_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT audin_server_receive_version_default(audin_server_context* audin_ctx,
|
||||
const SNDIN_VERSION* version)
|
||||
{
|
||||
audin_server* audin = (audin_server*)audin_ctx;
|
||||
SNDIN_FORMATS formats = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(version);
|
||||
|
||||
if (version->Version == 0)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Received invalid AUDIO_INPUT version from client");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "AUDIO_INPUT version of client: %u", version->Version);
|
||||
|
||||
formats.NumFormats = audin->audin_n_server_formats;
|
||||
formats.SoundFormats = audin->audin_server_formats;
|
||||
|
||||
return audin->context.SendFormats(&audin->context, &formats);
|
||||
}
|
||||
|
||||
static UINT send_open(audin_server* audin)
|
||||
{
|
||||
SNDIN_OPEN open = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
|
||||
open.FramesPerPacket = 441;
|
||||
open.initialFormat = audin->audin_client_format_idx;
|
||||
open.captureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
open.captureFormat.nChannels = 2;
|
||||
open.captureFormat.nSamplesPerSec = 44100;
|
||||
open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2;
|
||||
open.captureFormat.nBlockAlign = 4;
|
||||
open.captureFormat.wBitsPerSample = 16;
|
||||
|
||||
WINPR_ASSERT(audin->context.SendOpen);
|
||||
return audin->context.SendOpen(&audin->context, &open);
|
||||
}
|
||||
|
||||
static UINT audin_server_receive_formats_default(audin_server_context* context,
|
||||
const SNDIN_FORMATS* formats)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(formats);
|
||||
|
||||
if (audin->audin_negotiated_format)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"Received client formats, but negotiation was already done");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < audin->audin_n_server_formats; ++i)
|
||||
{
|
||||
for (UINT32 j = 0; j < formats->NumFormats; ++j)
|
||||
{
|
||||
if (audio_format_compatible(&audin->audin_server_formats[i], &formats->SoundFormats[j]))
|
||||
{
|
||||
audin->audin_negotiated_format = &audin->audin_server_formats[i];
|
||||
audin->audin_client_format_idx = i;
|
||||
return send_open(audin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Could not agree on a audio format with the server");
|
||||
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
static UINT audin_server_receive_format_change_default(audin_server_context* context,
|
||||
const SNDIN_FORMATCHANGE* format_change)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(format_change);
|
||||
|
||||
if (format_change->NewFormat != audin->audin_client_format_idx)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR,
|
||||
"NewFormat in FormatChange differs from requested format");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "Received Format Change PDU: %u", format_change->NewFormat);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT
|
||||
audin_server_incoming_data_default(audin_server_context* context,
|
||||
WINPR_ATTR_UNUSED const SNDIN_DATA_INCOMING* data_incoming)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(data_incoming);
|
||||
|
||||
/* TODO: Implement bandwidth measure of clients uplink */
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "Received Incoming Data PDU");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT audin_server_open_reply_default(audin_server_context* context,
|
||||
const SNDIN_OPEN_REPLY* open_reply)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
WINPR_ASSERT(open_reply);
|
||||
|
||||
/* TODO: Implement failure handling */
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "Open Reply PDU: Result: %" PRIu32, open_reply->Result);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
{
|
||||
audin_server* audin = (audin_server*)calloc(1, sizeof(audin_server));
|
||||
|
||||
if (!audin)
|
||||
{
|
||||
WLog_ERR(AUDIN_TAG, "calloc failed!");
|
||||
return nullptr;
|
||||
}
|
||||
audin->log = WLog_Get(AUDIN_TAG);
|
||||
audin->context.vcm = vcm;
|
||||
audin->context.Open = audin_server_open;
|
||||
audin->context.IsOpen = audin_server_is_open;
|
||||
audin->context.Close = audin_server_close;
|
||||
|
||||
audin->context.SendVersion = audin_server_send_version;
|
||||
audin->context.SendFormats = audin_server_send_formats;
|
||||
audin->context.SendOpen = audin_server_send_open;
|
||||
audin->context.SendFormatChange = audin_server_send_format_change;
|
||||
|
||||
/* Default values */
|
||||
audin->context.serverVersion = SNDIN_VERSION_Version_2;
|
||||
audin->context.ReceiveVersion = audin_server_receive_version_default;
|
||||
audin->context.ReceiveFormats = audin_server_receive_formats_default;
|
||||
audin->context.ReceiveFormatChange = audin_server_receive_format_change_default;
|
||||
audin->context.IncomingData = audin_server_incoming_data_default;
|
||||
audin->context.OpenReply = audin_server_open_reply_default;
|
||||
|
||||
return &audin->context;
|
||||
}
|
||||
|
||||
void audin_server_context_free(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
|
||||
if (!audin)
|
||||
return;
|
||||
|
||||
audin_server_close(context);
|
||||
audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
|
||||
audin->audin_server_formats = nullptr;
|
||||
free(audin);
|
||||
}
|
||||
|
||||
BOOL audin_server_set_formats(audin_server_context* context, SSIZE_T count,
|
||||
const AUDIO_FORMAT* formats)
|
||||
{
|
||||
audin_server* audin = (audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
|
||||
audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
|
||||
audin->audin_n_server_formats = 0;
|
||||
audin->audin_server_formats = nullptr;
|
||||
audin->audin_negotiated_format = nullptr;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
const size_t audin_n_server_formats =
|
||||
server_audin_get_formats(&audin->audin_server_formats);
|
||||
WINPR_ASSERT(audin_n_server_formats <= UINT32_MAX);
|
||||
|
||||
audin->audin_n_server_formats = (UINT32)audin_n_server_formats;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t scount = (size_t)count;
|
||||
AUDIO_FORMAT* audin_server_formats = audio_formats_new(scount);
|
||||
if (!audin_server_formats)
|
||||
return count == 0;
|
||||
|
||||
for (SSIZE_T x = 0; x < count; x++)
|
||||
{
|
||||
if (!audio_format_copy(&formats[x], &audin_server_formats[x]))
|
||||
{
|
||||
audio_formats_free(audin_server_formats, scount);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ASSERT(count <= UINT32_MAX);
|
||||
audin->audin_server_formats = audin_server_formats;
|
||||
audin->audin_n_server_formats = (UINT32)count;
|
||||
}
|
||||
return audin->audin_n_server_formats > 0;
|
||||
}
|
||||
|
||||
const AUDIO_FORMAT* audin_server_get_negotiated_format(const audin_server_context* context)
|
||||
{
|
||||
const audin_server* audin = (const audin_server*)context;
|
||||
WINPR_ASSERT(audin);
|
||||
|
||||
return audin->audin_negotiated_format;
|
||||
}
|
||||
165
third_party/FreeRDP/channels/client/CMakeLists.txt
vendored
Normal file
165
third_party/FreeRDP/channels/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2024 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.
|
||||
|
||||
set(MODULE_NAME "freerdp-channels-client")
|
||||
set(MODULE_PREFIX "FREERDP_CHANNELS_CLIENT")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tables.c ${CMAKE_CURRENT_SOURCE_DIR}/tables.h ${CMAKE_CURRENT_SOURCE_DIR}/addin.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/addin.h ${CMAKE_CURRENT_SOURCE_DIR}/generic_dynvc.c
|
||||
)
|
||||
|
||||
if(CHANNEL_STATIC_CLIENT_ENTRIES)
|
||||
list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
|
||||
endif()
|
||||
|
||||
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||
if(${ENTRY} STREQUAL ${STATIC_ENTRY})
|
||||
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
||||
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${STATIC_MODULE_NAME})
|
||||
|
||||
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${ENTRY}")
|
||||
if(${ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
||||
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS_EX,PVOID);")
|
||||
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
|
||||
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(ENTRY_POINT_IMPORT
|
||||
"extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);"
|
||||
)
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(void);")
|
||||
endif()
|
||||
|
||||
string(APPEND ${STATIC_ENTRY}_IMPORTS "\n${ENTRY_POINT_IMPORT}")
|
||||
string(APPEND ${STATIC_ENTRY}_TABLE "\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\nextern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];\n")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
|
||||
|
||||
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
set(CLIENT_STATIC_ENTRY_IMPORTS "${CLIENT_STATIC_ENTRY_IMPORTS}\n${${STATIC_ENTRY}_IMPORTS}")
|
||||
if(${STATIC_ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VC")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevc")
|
||||
elseif(${STATIC_ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VCEX")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevcex")
|
||||
elseif(${STATIC_ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DVC")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedvc")
|
||||
elseif(${STATIC_ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DSE")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedse")
|
||||
else()
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".cse")
|
||||
endif()
|
||||
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES
|
||||
"\nextern const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[];\n"
|
||||
)
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n${${STATIC_ENTRY}_TABLE}")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n\t{ nullptr, nullptr }\n};")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST
|
||||
"\n\t{ \"${STATIC_ENTRY}\", { ${CLIENT_STATIC_ENTRY_INITIALIZER} = CLIENT_${STATIC_ENTRY}_TABLE } },"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\n\t{ nullptr, { .cse = nullptr } }\n};")
|
||||
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];\n")
|
||||
string(APPEND CLIENT_STATIC_ADDIN_TABLE "const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
|
||||
|
||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
||||
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
|
||||
string(TOUPPER "CLIENT_${STATIC_MODULE_CHANNEL}_SUBSYSTEM_TABLE" SUBSYSTEM_TABLE_NAME)
|
||||
set(SUBSYSTEM_TABLE
|
||||
"extern const STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[];\nconst STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[] =\n{"
|
||||
)
|
||||
get_target_property(CHANNEL_SUBSYSTEMS ${STATIC_MODULE_NAME} SUBSYSTEMS)
|
||||
if(CHANNEL_SUBSYSTEMS MATCHES "NOTFOUND")
|
||||
set(CHANNEL_SUBSYSTEMS "")
|
||||
endif()
|
||||
foreach(STATIC_SUBSYSTEM ${CHANNEL_SUBSYSTEMS})
|
||||
if(${STATIC_SUBSYSTEM} MATCHES "^([^-]*)-(.*)")
|
||||
string(REGEX REPLACE "^([^-]*)-(.*)" "\\1" STATIC_SUBSYSTEM_NAME ${STATIC_SUBSYSTEM})
|
||||
string(REGEX REPLACE "^([^-]*)-(.*)" "\\2" STATIC_SUBSYSTEM_TYPE ${STATIC_SUBSYSTEM})
|
||||
else()
|
||||
set(STATIC_SUBSYSTEM_NAME "${STATIC_SUBSYSTEM}")
|
||||
set(STATIC_SUBSYSTEM_TYPE "")
|
||||
endif()
|
||||
string(LENGTH "${STATIC_SUBSYSTEM_TYPE}" _type_length)
|
||||
set(SUBSYSTEM_MODULE_NAME "${STATIC_MODULE_NAME}-${STATIC_SUBSYSTEM}")
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${SUBSYSTEM_MODULE_NAME})
|
||||
if(_type_length GREATER 0)
|
||||
set(STATIC_SUBSYSTEM_ENTRY
|
||||
"${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_${STATIC_SUBSYSTEM_TYPE}_subsystem_entry"
|
||||
)
|
||||
else()
|
||||
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
|
||||
endif()
|
||||
string(APPEND SUBSYSTEM_TABLE
|
||||
"\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },"
|
||||
)
|
||||
set(SUBSYSTEM_IMPORT "extern UINT VCAPITYPE ${STATIC_SUBSYSTEM_ENTRY}(void*);")
|
||||
string(APPEND CLIENT_STATIC_SUBSYSTEM_IMPORTS "\n${SUBSYSTEM_IMPORT}")
|
||||
endforeach()
|
||||
string(APPEND SUBSYSTEM_TABLE "\n\t{ nullptr, nullptr, nullptr }\n};")
|
||||
string(APPEND CLIENT_STATIC_SUBSYSTEM_TABLES "\n${SUBSYSTEM_TABLE}")
|
||||
|
||||
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||
set(ENTRY_POINT_NAME ${STATIC_MODULE_CHANNEL}_${ENTRY})
|
||||
if(${ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_INITIALIZER ".csevc")
|
||||
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(ENTRY_INITIALIZER ".csevcex")
|
||||
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(ENTRY_INITIALIZER ".csedvc")
|
||||
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(ENTRY_INITIALIZER ".csedse")
|
||||
else()
|
||||
set(ENTRY_INITIALIZER ".cse")
|
||||
endif()
|
||||
string(
|
||||
APPEND
|
||||
CLIENT_STATIC_ADDIN_TABLE
|
||||
"\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", { ${ENTRY_INITIALIZER} = ${ENTRY_POINT_NAME} }, ${SUBSYSTEM_TABLE_NAME} },"
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
string(APPEND CLIENT_STATIC_ADDIN_TABLE "\n\t{ nullptr, nullptr, { .cse = nullptr }, nullptr }\n};")
|
||||
|
||||
cleaning_configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp winpr)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} PARENT_SCOPE)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
|
||||
759
third_party/FreeRDP/channels/client/addin.c
vendored
Normal file
759
third_party/FreeRDP/channels/client/addin.c
vendored
Normal file
@@ -0,0 +1,759 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Channel Addins
|
||||
*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/library.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/build-config.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
|
||||
#include "tables.h"
|
||||
|
||||
#include "addin.h"
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
#define TAG CHANNELS_TAG("addin")
|
||||
|
||||
extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
|
||||
|
||||
static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
|
||||
const char* identifier)
|
||||
{
|
||||
size_t index = 0;
|
||||
const STATIC_ENTRY* pEntry = &table->table.cse[index++];
|
||||
|
||||
while (pEntry->entry != nullptr)
|
||||
{
|
||||
static_entry_fn_t fkt = pEntry->entry;
|
||||
if (strcmp(pEntry->name, identifier) == 0)
|
||||
return WINPR_FUNC_PTR_CAST(fkt, void*);
|
||||
|
||||
pEntry = &table->table.cse[index++];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
|
||||
{
|
||||
size_t index = 0;
|
||||
const STATIC_ENTRY_TABLE* pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
|
||||
while (pEntry->table.cse != nullptr)
|
||||
{
|
||||
if (strcmp(pEntry->name, name) == 0)
|
||||
{
|
||||
return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
|
||||
}
|
||||
|
||||
pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
|
||||
|
||||
static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(
|
||||
WINPR_ATTR_UNUSED LPCSTR pszName, WINPR_ATTR_UNUSED LPCSTR pszSubsystem,
|
||||
WINPR_ATTR_UNUSED LPCSTR pszType, WINPR_ATTR_UNUSED DWORD dwFlags)
|
||||
{
|
||||
DWORD nAddins = 0;
|
||||
FREERDP_ADDIN** ppAddins = nullptr;
|
||||
const STATIC_SUBSYSTEM_ENTRY* subsystems = nullptr;
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
|
||||
if (!ppAddins)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ppAddins[nAddins] = nullptr;
|
||||
|
||||
for (size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != nullptr; i++)
|
||||
{
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
const STATIC_ADDIN_TABLE* table = &CLIENT_STATIC_ADDIN_TABLE[i];
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
subsystems = table->table;
|
||||
|
||||
for (size_t j = 0; subsystems[j].name != nullptr; j++)
|
||||
{
|
||||
pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
(void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s",
|
||||
subsystems[j].name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
}
|
||||
}
|
||||
|
||||
return ppAddins;
|
||||
error_out:
|
||||
freerdp_channels_addin_list_free(ppAddins);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData)
|
||||
{
|
||||
HANDLE hdl = INVALID_HANDLE_VALUE;
|
||||
if (!pszSearchPath)
|
||||
return hdl;
|
||||
WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, nullptr);
|
||||
if (!wpath)
|
||||
return hdl;
|
||||
|
||||
hdl = FindFirstFileW(wpath, FindData);
|
||||
free(wpath);
|
||||
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||
LPCSTR pszType,
|
||||
WINPR_ATTR_UNUSED DWORD dwFlags)
|
||||
{
|
||||
int nDashes = 0;
|
||||
HANDLE hFind = nullptr;
|
||||
DWORD nAddins = 0;
|
||||
LPSTR pszPattern = nullptr;
|
||||
size_t cchPattern = 0;
|
||||
LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
|
||||
LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
|
||||
LPCSTR pszExtension = nullptr;
|
||||
LPSTR pszSearchPath = nullptr;
|
||||
size_t cchSearchPath = 0;
|
||||
size_t cchAddinPath = 0;
|
||||
size_t cchInstallPrefix = 0;
|
||||
FREERDP_ADDIN** ppAddins = nullptr;
|
||||
WIN32_FIND_DATAW FindData = WINPR_C_ARRAY_INIT;
|
||||
cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
|
||||
cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
|
||||
pszExtension = PathGetSharedLibraryExtensionA(0);
|
||||
cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
|
||||
pszPattern = (LPSTR)malloc(cchPattern + 1);
|
||||
|
||||
if (!pszPattern)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pszName && pszSubsystem && pszType)
|
||||
{
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName && pszType)
|
||||
{
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName)
|
||||
{
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s",
|
||||
pszName, pszExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
|
||||
pszExtension);
|
||||
}
|
||||
|
||||
cchPattern = strnlen(pszPattern, cchPattern);
|
||||
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
|
||||
pszSearchPath = (LPSTR)calloc(cchSearchPath + 1, sizeof(char));
|
||||
|
||||
if (!pszSearchPath)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
free(pszPattern);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
|
||||
pszSearchPath[cchInstallPrefix] = '\0';
|
||||
const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
|
||||
const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
|
||||
free(pszPattern);
|
||||
|
||||
if (FAILED(hr1) || FAILED(hr2))
|
||||
{
|
||||
free(pszSearchPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
|
||||
|
||||
free(pszSearchPath);
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
|
||||
|
||||
if (!ppAddins)
|
||||
{
|
||||
FindClose(hFind);
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
return ppAddins;
|
||||
|
||||
do
|
||||
{
|
||||
char* cFileName = nullptr;
|
||||
BOOL used = FALSE;
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
cFileName =
|
||||
ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), nullptr);
|
||||
if (!cFileName)
|
||||
goto skip;
|
||||
|
||||
nDashes = 0;
|
||||
for (size_t index = 0; cFileName[index]; index++)
|
||||
nDashes += (cFileName[index] == '-') ? 1 : 0;
|
||||
|
||||
if (nDashes == 1)
|
||||
{
|
||||
size_t len = 0;
|
||||
char* p[2] = WINPR_C_ARRAY_INIT;
|
||||
/* <name>-client.<extension> */
|
||||
p[0] = cFileName;
|
||||
p[1] = strchr(p[0], '-');
|
||||
if (!p[1])
|
||||
goto skip;
|
||||
p[1] += 1;
|
||||
|
||||
len = (size_t)(p[1] - p[0]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
else if (nDashes == 2)
|
||||
{
|
||||
size_t len = 0;
|
||||
char* p[4] = WINPR_C_ARRAY_INIT;
|
||||
/* <name>-client-<subsystem>.<extension> */
|
||||
p[0] = cFileName;
|
||||
p[1] = strchr(p[0], '-');
|
||||
if (!p[1])
|
||||
goto skip;
|
||||
p[1] += 1;
|
||||
p[2] = strchr(p[1], '-');
|
||||
if (!p[2])
|
||||
goto skip;
|
||||
p[2] += 1;
|
||||
p[3] = strchr(p[2], '.');
|
||||
if (!p[3])
|
||||
goto skip;
|
||||
p[3] += 1;
|
||||
|
||||
len = (size_t)(p[1] - p[0]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
len = (size_t)(p[3] - p[2]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
else if (nDashes == 3)
|
||||
{
|
||||
size_t len = 0;
|
||||
char* p[5] = WINPR_C_ARRAY_INIT;
|
||||
/* <name>-client-<subsystem>-<type>.<extension> */
|
||||
p[0] = cFileName;
|
||||
p[1] = strchr(p[0], '-');
|
||||
if (!p[1])
|
||||
goto skip;
|
||||
p[1] += 1;
|
||||
p[2] = strchr(p[1], '-');
|
||||
if (!p[2])
|
||||
goto skip;
|
||||
p[2] += 1;
|
||||
p[3] = strchr(p[2], '-');
|
||||
if (!p[3])
|
||||
goto skip;
|
||||
p[3] += 1;
|
||||
p[4] = strchr(p[3], '.');
|
||||
if (!p[4])
|
||||
goto skip;
|
||||
p[4] += 1;
|
||||
|
||||
len = (size_t)(p[1] - p[0]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
||||
|
||||
len = (size_t)(p[3] - p[2]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
||||
|
||||
len = (size_t)(p[4] - p[3]);
|
||||
if (len < 1)
|
||||
{
|
||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
|
||||
goto skip;
|
||||
}
|
||||
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
|
||||
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
||||
ppAddins[nAddins++] = pAddin;
|
||||
|
||||
used = TRUE;
|
||||
}
|
||||
|
||||
skip:
|
||||
free(cFileName);
|
||||
if (!used)
|
||||
free(pAddin);
|
||||
|
||||
} while (FindNextFileW(hFind, &FindData));
|
||||
|
||||
FindClose(hFind);
|
||||
ppAddins[nAddins] = nullptr;
|
||||
return ppAddins;
|
||||
error_out:
|
||||
FindClose(hFind);
|
||||
freerdp_channels_addin_list_free(ppAddins);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
if (dwFlags & FREERDP_ADDIN_STATIC)
|
||||
return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
|
||||
else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
|
||||
return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
||||
{
|
||||
if (!ppAddins)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; ppAddins[index] != nullptr; index++)
|
||||
free(ppAddins[index]);
|
||||
|
||||
free((void*)ppAddins);
|
||||
}
|
||||
|
||||
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
|
||||
|
||||
static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
|
||||
{
|
||||
for (size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != nullptr; i++)
|
||||
{
|
||||
const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
|
||||
|
||||
if (!strncmp(entry->name, pszName, MAX_PATH))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
|
||||
LPCSTR pszType, DWORD dwFlags)
|
||||
{
|
||||
const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
|
||||
const char* type = nullptr;
|
||||
|
||||
if (!pszName)
|
||||
return nullptr;
|
||||
|
||||
if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
|
||||
type = "DVCPluginEntry";
|
||||
else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
|
||||
type = "DeviceServiceEntry";
|
||||
else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
|
||||
{
|
||||
if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
|
||||
type = "VirtualChannelEntryEx";
|
||||
else
|
||||
type = "VirtualChannelEntry";
|
||||
}
|
||||
|
||||
for (; table->name != nullptr; table++)
|
||||
{
|
||||
if (strncmp(table->name, pszName, MAX_PATH) == 0)
|
||||
{
|
||||
if (type && (strncmp(table->type, type, MAX_PATH) != 0))
|
||||
continue;
|
||||
|
||||
if (pszSubsystem != nullptr)
|
||||
{
|
||||
const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
|
||||
|
||||
for (; subsystems->name != nullptr; subsystems++)
|
||||
{
|
||||
/* If the pszSubsystem is an empty string use the default backend. */
|
||||
if ((strnlen(pszSubsystem, 1) ==
|
||||
0) || /* we only want to know if strnlen is > 0 */
|
||||
(strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
|
||||
{
|
||||
static_subsystem_entry_fn_t fkt = subsystems->entry;
|
||||
|
||||
if (pszType)
|
||||
{
|
||||
if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
|
||||
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
|
||||
}
|
||||
else
|
||||
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
|
||||
{
|
||||
if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return table->entry.csevc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
wMessageQueue* queue;
|
||||
wStream* data_in;
|
||||
HANDLE thread;
|
||||
char* channel_name;
|
||||
rdpContext* ctx;
|
||||
LPVOID userdata;
|
||||
MsgHandler msg_handler;
|
||||
} msg_proc_internals;
|
||||
|
||||
static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
wStream* data = nullptr;
|
||||
wMessage message = WINPR_C_ARRAY_INIT;
|
||||
msg_proc_internals* internals = userdata;
|
||||
|
||||
WINPR_ASSERT(internals);
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!MessageQueue_Wait(internals->queue))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Wait failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
if (!MessageQueue_Peek(internals->queue, &message, TRUE))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Peek failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
if (message.id == 0)
|
||||
{
|
||||
data = (wStream*)message.wParam;
|
||||
|
||||
if ((error = internals->msg_handler(internals->userdata, data)))
|
||||
{
|
||||
WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error && internals->ctx)
|
||||
{
|
||||
char msg[128];
|
||||
(void)_snprintf(msg, 127,
|
||||
"%s_virtual_channel_client_thread reported an"
|
||||
" error",
|
||||
internals->channel_name);
|
||||
setChannelError(internals->ctx, error, msg);
|
||||
}
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void free_msg(void* obj)
|
||||
{
|
||||
wMessage* msg = (wMessage*)obj;
|
||||
|
||||
if (msg && (msg->id == 0))
|
||||
{
|
||||
wStream* s = (wStream*)msg->wParam;
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void channel_client_handler_free(msg_proc_internals* internals)
|
||||
{
|
||||
if (!internals)
|
||||
return;
|
||||
|
||||
if (internals->thread)
|
||||
(void)CloseHandle(internals->thread);
|
||||
MessageQueue_Free(internals->queue);
|
||||
Stream_Free(internals->data_in, TRUE);
|
||||
free(internals->channel_name);
|
||||
free(internals);
|
||||
}
|
||||
|
||||
/* Create message queue and thread or not, depending on settings */
|
||||
void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
|
||||
const char* channel_name)
|
||||
{
|
||||
msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
|
||||
if (!internals)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return nullptr;
|
||||
}
|
||||
internals->msg_handler = msg_handler;
|
||||
internals->userdata = userdata;
|
||||
if (channel_name)
|
||||
{
|
||||
internals->channel_name = _strdup(channel_name);
|
||||
if (!internals->channel_name)
|
||||
goto fail;
|
||||
}
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(ctx->settings);
|
||||
internals->ctx = ctx;
|
||||
if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
|
||||
THREADING_FLAGS_DISABLE_THREADS) == 0)
|
||||
{
|
||||
wObject obj = WINPR_C_ARRAY_INIT;
|
||||
obj.fnObjectFree = free_msg;
|
||||
internals->queue = MessageQueue_New(&obj);
|
||||
if (!internals->queue)
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(internals->thread = CreateThread(nullptr, 0, channel_client_thread_proc,
|
||||
(void*)internals, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return internals;
|
||||
|
||||
fail:
|
||||
channel_client_handler_free(internals);
|
||||
return nullptr;
|
||||
}
|
||||
/* post a message in the queue or directly call the processing handler */
|
||||
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
|
||||
UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
msg_proc_internals* internals = MsgsHandle;
|
||||
wStream* data_in = nullptr;
|
||||
|
||||
if (!internals)
|
||||
{
|
||||
/* TODO: return some error here */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (internals->data_in)
|
||||
{
|
||||
if (!Stream_EnsureCapacity(internals->data_in, totalLength))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
else
|
||||
internals->data_in = Stream_New(nullptr, totalLength);
|
||||
}
|
||||
|
||||
if (!(data_in = internals->data_in))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
|
||||
{
|
||||
Stream_Free(internals->data_in, TRUE);
|
||||
internals->data_in = nullptr;
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(data_in, pData, dataLength);
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_LAST)
|
||||
{
|
||||
if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
|
||||
{
|
||||
WLog_ERR(TAG, "%s_plugin_process_received: read error", internals->channel_name);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
internals->data_in = nullptr;
|
||||
Stream_SealLength(data_in);
|
||||
Stream_ResetPosition(data_in);
|
||||
|
||||
if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
|
||||
THREADING_FLAGS_DISABLE_THREADS) != 0)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
if ((error = internals->msg_handler(internals->userdata, data_in)))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"msg_handler failed with error"
|
||||
" %" PRIu32 "!",
|
||||
error);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
else if (!MessageQueue_Post(internals->queue, nullptr, 0, (void*)data_in, nullptr))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
/* Tear down queue and thread */
|
||||
UINT channel_client_quit_handler(void* MsgsHandle)
|
||||
{
|
||||
msg_proc_internals* internals = MsgsHandle;
|
||||
UINT rc = 0;
|
||||
if (!internals)
|
||||
{
|
||||
/* TODO: return some error here */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
WINPR_ASSERT(internals->ctx);
|
||||
WINPR_ASSERT(internals->ctx->settings);
|
||||
|
||||
if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
|
||||
THREADING_FLAGS_DISABLE_THREADS) == 0)
|
||||
{
|
||||
if (internals->queue && internals->thread)
|
||||
{
|
||||
if (MessageQueue_PostQuit(internals->queue, 0) &&
|
||||
(WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
rc = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel_client_handler_free(internals);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
36
third_party/FreeRDP/channels/client/addin.h
vendored
Normal file
36
third_party/FreeRDP/channels/client/addin.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Channel Addins
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
|
||||
typedef UINT (*MsgHandler)(LPVOID userdata, wStream* data);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_API void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata,
|
||||
MsgHandler handler, const char* channel_name);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
|
||||
UINT32 totalLength, UINT32 dataFlags);
|
||||
|
||||
FREERDP_LOCAL UINT channel_client_quit_handler(void* MsgsHandle);
|
||||
215
third_party/FreeRDP/channels/client/generic_dynvc.c
vendored
Normal file
215
third_party/FreeRDP/channels/client/generic_dynvc.c
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Dynamic channel
|
||||
*
|
||||
* Copyright 2022 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
|
||||
#define TAG FREERDP_TAG("genericdynvc")
|
||||
|
||||
static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel,
|
||||
WINPR_ATTR_UNUSED BYTE* Data,
|
||||
WINPR_ATTR_UNUSED BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = nullptr;
|
||||
GENERIC_DYNVC_PLUGIN* plugin = nullptr;
|
||||
GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
|
||||
|
||||
if (!listener_callback || !listener_callback->plugin)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
|
||||
WLog_Print(plugin->log, WLOG_TRACE, "...");
|
||||
|
||||
callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
|
||||
if (!callback)
|
||||
{
|
||||
WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* implant configured channel callbacks */
|
||||
callback->iface = *plugin->channel_callbacks;
|
||||
|
||||
callback->plugin = listener_callback->plugin;
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
|
||||
listener_callback->channel_callback = callback;
|
||||
listener_callback->channel = pChannel;
|
||||
|
||||
*ppCallback = &callback->iface;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
|
||||
IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
UINT rc = 0;
|
||||
GENERIC_LISTENER_CALLBACK* listener_callback = nullptr;
|
||||
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
|
||||
|
||||
if (!plugin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
if (!pChannelMgr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (plugin->initialized)
|
||||
{
|
||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
WLog_Print(plugin->log, WLOG_TRACE, "...");
|
||||
listener_callback = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
|
||||
if (!listener_callback)
|
||||
{
|
||||
WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
plugin->listener_callback = listener_callback;
|
||||
listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
|
||||
listener_callback->plugin = pPlugin;
|
||||
listener_callback->channel_mgr = pChannelMgr;
|
||||
rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
|
||||
&plugin->listener);
|
||||
|
||||
plugin->listener->pInterface = plugin->iface.pInterface;
|
||||
plugin->initialized = (rc == CHANNEL_RC_OK);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!plugin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
WLog_Print(plugin->log, WLOG_TRACE, "...");
|
||||
|
||||
/* some channels (namely rdpei), look at initialized to see if they should continue to run */
|
||||
plugin->initialized = FALSE;
|
||||
|
||||
if (plugin->terminatePluginFn)
|
||||
plugin->terminatePluginFn(plugin);
|
||||
|
||||
if (plugin->listener_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
|
||||
if (mgr)
|
||||
IFCALL(mgr->DestroyListener, mgr, plugin->listener);
|
||||
}
|
||||
|
||||
free(plugin->listener_callback);
|
||||
free(plugin->dynvc_name);
|
||||
free(plugin);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!pluginn)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
pluginn->attached = TRUE;
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!plugin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
plugin->attached = FALSE;
|
||||
return error;
|
||||
}
|
||||
|
||||
UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
|
||||
const char* name, size_t pluginSize, size_t channelCallbackSize,
|
||||
const IWTSVirtualChannelCallback* channel_callbacks,
|
||||
DYNVC_PLUGIN_INIT_FN initPluginFn,
|
||||
DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN* plugin = nullptr;
|
||||
UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
WINPR_ASSERT(pEntryPoints->GetPlugin);
|
||||
WINPR_ASSERT(logTag);
|
||||
WINPR_ASSERT(name);
|
||||
WINPR_ASSERT(pluginSize >= sizeof(*plugin));
|
||||
WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
|
||||
|
||||
plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
|
||||
if (plugin != nullptr)
|
||||
return CHANNEL_RC_ALREADY_INITIALIZED;
|
||||
|
||||
plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize);
|
||||
if (!plugin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
plugin->log = WLog_Get(logTag);
|
||||
plugin->attached = TRUE;
|
||||
plugin->channel_callbacks = channel_callbacks;
|
||||
plugin->channelCallbackSize = channelCallbackSize;
|
||||
plugin->iface.Initialize = generic_dynvc_plugin_initialize;
|
||||
plugin->iface.Connected = nullptr;
|
||||
plugin->iface.Disconnected = nullptr;
|
||||
plugin->iface.Terminated = generic_plugin_terminated;
|
||||
plugin->iface.Attached = generic_dynvc_plugin_attached;
|
||||
plugin->iface.Detached = generic_dynvc_plugin_detached;
|
||||
plugin->terminatePluginFn = terminatePluginFn;
|
||||
|
||||
if (initPluginFn)
|
||||
{
|
||||
rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
|
||||
rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
|
||||
|
||||
error = initPluginFn(plugin, context, settings);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
goto error;
|
||||
}
|
||||
|
||||
plugin->dynvc_name = _strdup(name);
|
||||
if (!plugin->dynvc_name)
|
||||
goto error;
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
|
||||
if (error == CHANNEL_RC_OK)
|
||||
return error;
|
||||
|
||||
error:
|
||||
generic_plugin_terminated(&plugin->iface);
|
||||
return error;
|
||||
}
|
||||
33
third_party/FreeRDP/channels/client/tables.c.in
vendored
Normal file
33
third_party/FreeRDP/channels/client/tables.c.in
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Static Entry Point Tables
|
||||
*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include "tables.h"
|
||||
|
||||
${CLIENT_STATIC_TYPEDEFS}
|
||||
${CLIENT_STATIC_ENTRY_IMPORTS}
|
||||
${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
|
||||
${CLIENT_STATIC_ENTRY_TABLES}
|
||||
${CLIENT_STATIC_ENTRY_TABLES_LIST}
|
||||
${CLIENT_STATIC_SUBSYSTEM_TABLES}
|
||||
${CLIENT_STATIC_ADDIN_TABLE}
|
||||
|
||||
107
third_party/FreeRDP/channels/client/tables.h
vendored
Normal file
107
third_party/FreeRDP/channels/client/tables.h
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Static Entry Point Tables
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winpr/platform.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
/* The 'entry' function pointers have variable arguments. */
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_fn_t)();
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
WINPR_ATTR_NODISCARD static_entry_fn_t entry;
|
||||
} STATIC_ENTRY;
|
||||
|
||||
typedef BOOL(VCAPITYPE* static_entry_vc_fn_t)(PCHANNEL_ENTRY_POINTS);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
WINPR_ATTR_NODISCARD static_entry_vc_fn_t entry;
|
||||
} STATIC_ENTRY_VC;
|
||||
|
||||
typedef BOOL(VCAPITYPE* static_entry_vcex_fn_t)(PCHANNEL_ENTRY_POINTS_EX, PVOID);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
WINPR_ATTR_NODISCARD static_entry_vcex_fn_t entry;
|
||||
} STATIC_ENTRY_VCEX;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_dvc_fn_t)(IDRDYNVC_ENTRY_POINTS*);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
WINPR_ATTR_NODISCARD static_entry_dvc_fn_t entry;
|
||||
} STATIC_ENTRY_DVC;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_dse_fn_t)(PDEVICE_SERVICE_ENTRY_POINTS);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
WINPR_ATTR_NODISCARD static_entry_dse_fn_t entry;
|
||||
} STATIC_ENTRY_DSE;
|
||||
|
||||
typedef union
|
||||
{
|
||||
const STATIC_ENTRY* cse;
|
||||
const STATIC_ENTRY_VC* csevc;
|
||||
const STATIC_ENTRY_VCEX* csevcex;
|
||||
const STATIC_ENTRY_DVC* csedvc;
|
||||
const STATIC_ENTRY_DSE* csedse;
|
||||
} static_entry_u;
|
||||
|
||||
typedef union
|
||||
{
|
||||
WINPR_ATTR_NODISCARD static_entry_fn_t cse;
|
||||
WINPR_ATTR_NODISCARD static_entry_vc_fn_t csevc;
|
||||
WINPR_ATTR_NODISCARD static_entry_vcex_fn_t csevcex;
|
||||
WINPR_ATTR_NODISCARD static_entry_dvc_fn_t csedvc;
|
||||
WINPR_ATTR_NODISCARD static_entry_dse_fn_t csedse;
|
||||
} static_entry_fn_u;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
static_entry_u table;
|
||||
} STATIC_ENTRY_TABLE;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_subsystem_entry_fn_t)(void*);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* type;
|
||||
WINPR_ATTR_NODISCARD static_subsystem_entry_fn_t entry;
|
||||
} STATIC_SUBSYSTEM_ENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* type;
|
||||
static_entry_fn_u entry;
|
||||
const STATIC_SUBSYSTEM_ENTRY* table;
|
||||
} STATIC_ADDIN_TABLE;
|
||||
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
26
third_party/FreeRDP/channels/cliprdr/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/cliprdr/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("cliprdr")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/cliprdr/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/cliprdr/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"cliprdr"
|
||||
TYPE
|
||||
"static"
|
||||
DESCRIPTION
|
||||
"Clipboard Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPECLIP]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
25
third_party/FreeRDP/channels/cliprdr/client/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/channels/cliprdr/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("cliprdr")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS cliprdr_format.c cliprdr_format.h cliprdr_main.c cliprdr_main.h ../cliprdr_common.h
|
||||
../cliprdr_common.c
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp)
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
280
third_party/FreeRDP/channels/cliprdr/client/cliprdr_format.c
vendored
Normal file
280
third_party/FreeRDP/channels/cliprdr/client/cliprdr_format.c
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Clipboard Virtual Channel
|
||||
*
|
||||
* Copyright 2009-2011 Jay Sorg
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/clipboard.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
|
||||
#include "cliprdr_main.h"
|
||||
#include "cliprdr_format.h"
|
||||
#include "../cliprdr_common.h"
|
||||
|
||||
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, const UINT32 mask,
|
||||
const UINT32 checkMask)
|
||||
{
|
||||
const UINT32 maskData =
|
||||
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_REMOTE_TO_LOCAL);
|
||||
const UINT32 maskFiles =
|
||||
checkMask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
|
||||
WINPR_ASSERT(list);
|
||||
|
||||
CLIPRDR_FORMAT_LIST filtered = WINPR_C_ARRAY_INIT;
|
||||
filtered.common.msgType = CB_FORMAT_LIST;
|
||||
filtered.numFormats = list->numFormats;
|
||||
filtered.formats = calloc(filtered.numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
size_t wpos = 0;
|
||||
if ((mask & checkMask) == checkMask)
|
||||
{
|
||||
for (size_t x = 0; x < list->numFormats; x++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &list->formats[x];
|
||||
CLIPRDR_FORMAT* cur = &filtered.formats[x];
|
||||
cur->formatId = format->formatId;
|
||||
if (format->formatName)
|
||||
cur->formatName = _strdup(format->formatName);
|
||||
wpos++;
|
||||
}
|
||||
}
|
||||
else if ((mask & maskFiles) != 0)
|
||||
{
|
||||
for (size_t x = 0; x < list->numFormats; x++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &list->formats[x];
|
||||
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
|
||||
|
||||
if (!format->formatName)
|
||||
continue;
|
||||
if (strcmp(format->formatName, type_FileGroupDescriptorW) == 0 ||
|
||||
strcmp(format->formatName, type_FileContents) == 0)
|
||||
{
|
||||
cur->formatId = format->formatId;
|
||||
cur->formatName = _strdup(format->formatName);
|
||||
wpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((mask & maskData) != 0)
|
||||
{
|
||||
for (size_t x = 0; x < list->numFormats; x++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &list->formats[x];
|
||||
CLIPRDR_FORMAT* cur = &filtered.formats[wpos];
|
||||
|
||||
if (!format->formatName ||
|
||||
(strcmp(format->formatName, type_FileGroupDescriptorW) != 0 &&
|
||||
strcmp(format->formatName, type_FileContents) != 0))
|
||||
{
|
||||
cur->formatId = format->formatId;
|
||||
if (format->formatName)
|
||||
cur->formatName = _strdup(format->formatName);
|
||||
wpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
WINPR_ASSERT(wpos <= UINT32_MAX);
|
||||
filtered.numFormats = (UINT32)wpos;
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
|
||||
CLIPRDR_FORMAT_LIST filteredFormatList = WINPR_C_ARRAY_INIT;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
formatList.common.msgType = CB_FORMAT_LIST;
|
||||
formatList.common.msgFlags = msgFlags;
|
||||
formatList.common.dataLen = dataLen;
|
||||
|
||||
if ((error =
|
||||
cliprdr_read_format_list(cliprdr->log, s, &formatList, cliprdr->useLongFormatNames)))
|
||||
goto error_out;
|
||||
|
||||
{
|
||||
const UINT32 mask = freerdp_settings_get_uint32(context->rdpcontext->settings,
|
||||
FreeRDP_ClipboardFeatureMask);
|
||||
filteredFormatList = cliprdr_filter_format_list(
|
||||
&formatList, mask, CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
|
||||
}
|
||||
|
||||
if (filteredFormatList.numFormats == 0)
|
||||
goto error_out;
|
||||
|
||||
{
|
||||
const DWORD level = WLOG_DEBUG;
|
||||
if (WLog_IsLevelActive(cliprdr->log, level))
|
||||
{
|
||||
WLog_Print(cliprdr->log, level, "ServerFormatList: numFormats: %" PRIu32 "",
|
||||
formatList.numFormats);
|
||||
for (size_t x = 0; x < formatList.numFormats; x++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &formatList.formats[x];
|
||||
WLog_Print(cliprdr->log, level, "[%" PRIuz "]: id=0x%08" PRIx32 " [%s|%s]", x,
|
||||
format->formatId, ClipboardGetFormatIdString(format->formatId),
|
||||
format->formatName);
|
||||
}
|
||||
|
||||
WLog_Print(cliprdr->log, level, "ServerFormatList [filtered]: numFormats: %" PRIu32 "",
|
||||
filteredFormatList.numFormats);
|
||||
for (size_t x = 0; x < filteredFormatList.numFormats; x++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &filteredFormatList.formats[x];
|
||||
WLog_Print(cliprdr->log, level, "[%" PRIuz "]: id=0x%08" PRIx32 " [%s|%s]", x,
|
||||
format->formatId, ClipboardGetFormatIdString(format->formatId),
|
||||
format->formatName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context->ServerFormatList)
|
||||
{
|
||||
if ((error = context->ServerFormatList(context, &filteredFormatList)))
|
||||
WLog_Print(cliprdr->log, WLOG_ERROR, "ServerFormatList failed with error %" PRIu32 "",
|
||||
error);
|
||||
}
|
||||
|
||||
error_out:
|
||||
cliprdr_free_format_list(&filteredFormatList);
|
||||
cliprdr_free_format_list(&formatList);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, WINPR_ATTR_UNUSED wStream* s,
|
||||
UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = WINPR_C_ARRAY_INIT;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
|
||||
|
||||
formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE;
|
||||
formatListResponse.common.msgFlags = msgFlags;
|
||||
formatListResponse.common.dataLen = dataLen;
|
||||
|
||||
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
|
||||
if (error)
|
||||
WLog_Print(cliprdr->log, WLOG_ERROR,
|
||||
"ServerFormatListResponse failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = WINPR_C_ARRAY_INIT;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
|
||||
formatDataRequest.common.msgFlags = msgFlags;
|
||||
formatDataRequest.common.dataLen = dataLen;
|
||||
|
||||
if ((error = cliprdr_read_format_data_request(s, &formatDataRequest)))
|
||||
return error;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest (0x%08" PRIx32 " [%s])",
|
||||
formatDataRequest.requestedFormatId,
|
||||
ClipboardGetFormatIdString(formatDataRequest.requestedFormatId));
|
||||
|
||||
const UINT32 mask =
|
||||
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
|
||||
if ((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) == 0)
|
||||
{
|
||||
return cliprdr_send_error_response(cliprdr, CB_FORMAT_DATA_RESPONSE);
|
||||
}
|
||||
|
||||
context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
|
||||
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
|
||||
if (error)
|
||||
WLog_Print(cliprdr->log, WLOG_ERROR,
|
||||
"ServerFormatDataRequest failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse = WINPR_C_ARRAY_INIT;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG,
|
||||
"ServerFormatDataResponse: msgFlags=0x%08" PRIx32 ", dataLen=%" PRIu32, msgFlags,
|
||||
dataLen);
|
||||
|
||||
formatDataResponse.common.msgType = CB_FORMAT_DATA_RESPONSE;
|
||||
formatDataResponse.common.msgFlags = msgFlags;
|
||||
formatDataResponse.common.dataLen = dataLen;
|
||||
|
||||
if ((error = cliprdr_read_format_data_response(s, &formatDataResponse)))
|
||||
return error;
|
||||
|
||||
const UINT32 mask =
|
||||
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
|
||||
if ((mask & (CLIPRDR_FLAG_REMOTE_TO_LOCAL | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES)) == 0)
|
||||
{
|
||||
WLog_Print(cliprdr->log, WLOG_WARN,
|
||||
"Received ServerFormatDataResponse but remote -> local clipboard is disabled");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
||||
if (error)
|
||||
WLog_Print(cliprdr->log, WLOG_ERROR,
|
||||
"ServerFormatDataResponse failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
59
third_party/FreeRDP/channels/cliprdr/client/cliprdr_format.h
vendored
Normal file
59
third_party/FreeRDP/channels/cliprdr/client/cliprdr_format.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Clipboard Virtual Channel
|
||||
*
|
||||
* Copyright 2009-2011 Jay Sorg
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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_CLIPRDR_CLIENT_FORMAT_H
|
||||
#define FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/channels/cliprdr.h>
|
||||
|
||||
#include "cliprdr_main.h"
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL
|
||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL
|
||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
|
||||
UINT16 msgFlags);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL
|
||||
CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list, UINT32 mask,
|
||||
UINT32 checkMask);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_FORMAT_H */
|
||||
1209
third_party/FreeRDP/channels/cliprdr/client/cliprdr_main.c
vendored
Normal file
1209
third_party/FreeRDP/channels/cliprdr/client/cliprdr_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
63
third_party/FreeRDP/channels/cliprdr/client/cliprdr_main.h
vendored
Normal file
63
third_party/FreeRDP/channels/cliprdr/client/cliprdr_main.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Clipboard Virtual Channel
|
||||
*
|
||||
* Copyright 2009-2011 Jay Sorg
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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_CLIPRDR_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
|
||||
CliprdrClientContext* context;
|
||||
|
||||
wLog* log;
|
||||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
void* MsgsHandle;
|
||||
|
||||
BOOL capabilitiesReceived;
|
||||
BOOL useLongFormatNames;
|
||||
BOOL streamFileClipEnabled;
|
||||
BOOL fileClipNoFilePaths;
|
||||
BOOL canLockClipData;
|
||||
BOOL hasHugeFileSupport;
|
||||
BOOL initialFormatListSent;
|
||||
} cliprdrPlugin;
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type);
|
||||
|
||||
FREERDP_LOCAL extern const char type_FileGroupDescriptorW[];
|
||||
|
||||
FREERDP_LOCAL extern const char type_FileContents[];
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */
|
||||
565
third_party/FreeRDP/channels/cliprdr/cliprdr_common.c
vendored
Normal file
565
third_party/FreeRDP/channels/cliprdr/cliprdr_common.c
vendored
Normal file
@@ -0,0 +1,565 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Cliprdr common
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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.
|
||||
*/
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("cliprdr.common")
|
||||
|
||||
#include "cliprdr_common.h"
|
||||
|
||||
static const char* CB_MSG_TYPE_STR(UINT32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CB_TYPE_NONE:
|
||||
return "CB_TYPE_NONE";
|
||||
case CB_MONITOR_READY:
|
||||
return "CB_MONITOR_READY";
|
||||
case CB_FORMAT_LIST:
|
||||
return "CB_FORMAT_LIST";
|
||||
case CB_FORMAT_LIST_RESPONSE:
|
||||
return "CB_FORMAT_LIST_RESPONSE";
|
||||
case CB_FORMAT_DATA_REQUEST:
|
||||
return "CB_FORMAT_DATA_REQUEST";
|
||||
case CB_FORMAT_DATA_RESPONSE:
|
||||
return "CB_FORMAT_DATA_RESPONSE";
|
||||
case CB_TEMP_DIRECTORY:
|
||||
return "CB_TEMP_DIRECTORY";
|
||||
case CB_CLIP_CAPS:
|
||||
return "CB_CLIP_CAPS";
|
||||
case CB_FILECONTENTS_REQUEST:
|
||||
return "CB_FILECONTENTS_REQUEST";
|
||||
case CB_FILECONTENTS_RESPONSE:
|
||||
return "CB_FILECONTENTS_RESPONSE";
|
||||
case CB_LOCK_CLIPDATA:
|
||||
return "CB_LOCK_CLIPDATA";
|
||||
case CB_UNLOCK_CLIPDATA:
|
||||
return "CB_UNLOCK_CLIPDATA";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size)
|
||||
{
|
||||
(void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", CB_MSG_TYPE_STR(type), type);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size)
|
||||
{
|
||||
if ((msgFlags & CB_RESPONSE_OK) != 0)
|
||||
winpr_str_append("CB_RESPONSE_OK", buffer, size, "|");
|
||||
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
|
||||
winpr_str_append("CB_RESPONSE_FAIL", buffer, size, "|");
|
||||
if ((msgFlags & CB_ASCII_NAMES) != 0)
|
||||
winpr_str_append("CB_ASCII_NAMES", buffer, size, "|");
|
||||
|
||||
const size_t len = strnlen(buffer, size);
|
||||
if (!len)
|
||||
winpr_str_append("NONE", buffer, size, "");
|
||||
|
||||
char val[32] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(val, sizeof(val), "[0x%04" PRIx16 "]", msgFlags);
|
||||
winpr_str_append(val, buffer, size, "|");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static BOOL cliprdr_validate_file_contents_request(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
|
||||
{
|
||||
/*
|
||||
* [MS-RDPECLIP] 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST).
|
||||
*
|
||||
* A request for the size of the file identified by the lindex field. The size MUST be
|
||||
* returned as a 64-bit, unsigned integer. The cbRequested field MUST be set to
|
||||
* 0x00000008 and both the nPositionLow and nPositionHigh fields MUST be
|
||||
* set to 0x00000000.
|
||||
*/
|
||||
|
||||
if (request->dwFlags & FILECONTENTS_SIZE)
|
||||
{
|
||||
if (request->cbRequested != sizeof(UINT64))
|
||||
{
|
||||
WLog_ERR(TAG, "cbRequested must be %" PRIuz ", got %" PRIu32 "", sizeof(UINT64),
|
||||
request->cbRequested);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (request->nPositionHigh != 0 || request->nPositionLow != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "nPositionHigh and nPositionLow must be set to 0");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, size_t dataLen)
|
||||
{
|
||||
WINPR_ASSERT(dataLen < UINT32_MAX);
|
||||
wStream* s = Stream_New(nullptr, dataLen + 8ULL);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, msgType);
|
||||
Stream_Write_UINT16(s, msgFlags);
|
||||
/* Write actual length after the entire packet has been constructed. */
|
||||
Stream_Write_UINT32(s, 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void cliprdr_write_file_contents_request(wStream* s,
|
||||
const CLIPRDR_FILE_CONTENTS_REQUEST* request)
|
||||
{
|
||||
Stream_Write_UINT32(s, request->streamId); /* streamId (4 bytes) */
|
||||
Stream_Write_UINT32(s, request->listIndex); /* listIndex (4 bytes) */
|
||||
Stream_Write_UINT32(s, request->dwFlags); /* dwFlags (4 bytes) */
|
||||
Stream_Write_UINT32(s, request->nPositionLow); /* nPositionLow (4 bytes) */
|
||||
Stream_Write_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
|
||||
Stream_Write_UINT32(s, request->cbRequested); /* cbRequested (4 bytes) */
|
||||
|
||||
if (request->haveClipDataId)
|
||||
Stream_Write_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
|
||||
}
|
||||
|
||||
static inline void cliprdr_write_lock_unlock_clipdata(wStream* s, UINT32 clipDataId)
|
||||
{
|
||||
Stream_Write_UINT32(s, clipDataId);
|
||||
}
|
||||
|
||||
static void cliprdr_write_lock_clipdata(wStream* s,
|
||||
const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
|
||||
{
|
||||
cliprdr_write_lock_unlock_clipdata(s, lockClipboardData->clipDataId);
|
||||
}
|
||||
|
||||
static void cliprdr_write_unlock_clipdata(wStream* s,
|
||||
const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
|
||||
{
|
||||
cliprdr_write_lock_unlock_clipdata(s, unlockClipboardData->clipDataId);
|
||||
}
|
||||
|
||||
static void cliprdr_write_file_contents_response(wStream* s,
|
||||
const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
|
||||
{
|
||||
Stream_Write_UINT32(s, response->streamId); /* streamId (4 bytes) */
|
||||
Stream_Write(s, response->requestedData, response->cbRequested);
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_lock_clipdata_new(const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
if (!lockClipboardData)
|
||||
return nullptr;
|
||||
|
||||
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
|
||||
|
||||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
cliprdr_write_lock_clipdata(s, lockClipboardData);
|
||||
return s;
|
||||
}
|
||||
|
||||
wStream*
|
||||
cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
if (!unlockClipboardData)
|
||||
return nullptr;
|
||||
|
||||
s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
|
||||
|
||||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
cliprdr_write_unlock_clipdata(s, unlockClipboardData);
|
||||
return s;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* request)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
if (!request)
|
||||
return nullptr;
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0, 28);
|
||||
|
||||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
cliprdr_write_file_contents_request(s, request);
|
||||
return s;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
if (!response)
|
||||
return nullptr;
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, response->common.msgFlags,
|
||||
4 + response->cbRequested);
|
||||
|
||||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
cliprdr_write_file_contents_response(s, response);
|
||||
return s;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames, BOOL useAsciiNames)
|
||||
{
|
||||
WINPR_ASSERT(formatList);
|
||||
|
||||
if (formatList->common.msgType != CB_FORMAT_LIST)
|
||||
WLog_WARN(TAG, "called with invalid type %08" PRIx32, formatList->common.msgType);
|
||||
|
||||
if (useLongFormatNames && useAsciiNames)
|
||||
WLog_WARN(TAG, "called with invalid arguments useLongFormatNames=true && "
|
||||
"useAsciiNames=true. useAsciiNames requires "
|
||||
"useLongFormatNames=false, ignoring argument.");
|
||||
|
||||
const UINT32 length = formatList->numFormats * 36;
|
||||
const size_t formatNameCharSize =
|
||||
(useLongFormatNames || !useAsciiNames) ? sizeof(WCHAR) : sizeof(CHAR);
|
||||
|
||||
wStream* s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
const CLIPRDR_FORMAT* format = &(formatList->formats[index]);
|
||||
|
||||
const char* szFormatName = format->formatName;
|
||||
size_t formatNameLength = 0;
|
||||
if (szFormatName)
|
||||
formatNameLength = strlen(szFormatName);
|
||||
|
||||
size_t formatNameMaxLength = formatNameLength + 1; /* Ensure '\0' termination in output */
|
||||
if (!Stream_EnsureRemainingCapacity(s,
|
||||
4 + MAX(32, formatNameMaxLength * formatNameCharSize)))
|
||||
goto fail;
|
||||
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
if (!useLongFormatNames)
|
||||
{
|
||||
formatNameMaxLength = useAsciiNames ? 32 : 16;
|
||||
formatNameLength = MIN(formatNameMaxLength - 1, formatNameLength);
|
||||
}
|
||||
|
||||
if (szFormatName && (formatNameLength > 0))
|
||||
{
|
||||
if (useAsciiNames)
|
||||
{
|
||||
Stream_Write(s, szFormatName, formatNameLength);
|
||||
Stream_Zero(s, formatNameMaxLength - formatNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Stream_Write_UTF16_String_From_UTF8(s, formatNameMaxLength, szFormatName,
|
||||
formatNameLength, TRUE) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
Stream_Zero(s, formatNameMaxLength * formatNameCharSize);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_unlock_clipdata(wStream* s, CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, unlockClipboardData->clipDataId); /* clipDataId (4 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_format_data_request(wStream* s, CLIPRDR_FORMAT_DATA_REQUEST* request)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, request->requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_format_data_response(wStream* s, CLIPRDR_FORMAT_DATA_RESPONSE* response)
|
||||
{
|
||||
response->requestedFormatData = nullptr;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, response->common.dataLen))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (response->common.dataLen)
|
||||
response->requestedFormatData = Stream_ConstPointer(s);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* request)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
request->haveClipDataId = FALSE;
|
||||
Stream_Read_UINT32(s, request->streamId); /* streamId (4 bytes) */
|
||||
Stream_Read_UINT32(s, request->listIndex); /* listIndex (4 bytes) */
|
||||
Stream_Read_UINT32(s, request->dwFlags); /* dwFlags (4 bytes) */
|
||||
Stream_Read_UINT32(s, request->nPositionLow); /* nPositionLow (4 bytes) */
|
||||
Stream_Read_UINT32(s, request->nPositionHigh); /* nPositionHigh (4 bytes) */
|
||||
Stream_Read_UINT32(s, request->cbRequested); /* cbRequested (4 bytes) */
|
||||
|
||||
if (Stream_GetRemainingLength(s) >= 4)
|
||||
{
|
||||
Stream_Read_UINT32(s, request->clipDataId); /* clipDataId (4 bytes) */
|
||||
request->haveClipDataId = TRUE;
|
||||
}
|
||||
|
||||
if (!cliprdr_validate_file_contents_request(request))
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_file_contents_response(wStream* s, CLIPRDR_FILE_CONTENTS_RESPONSE* response)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, response->streamId); /* streamId (4 bytes) */
|
||||
response->requestedData = Stream_ConstPointer(s); /* requestedFileContentsData */
|
||||
|
||||
WINPR_ASSERT(response->common.dataLen >= 4);
|
||||
response->cbRequested = response->common.dataLen - 4;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_format_list(wLog* log, wStream* s, CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames)
|
||||
{
|
||||
UINT32 index = 0;
|
||||
size_t formatNameLength = 0;
|
||||
const char* szFormatName = nullptr;
|
||||
const WCHAR* wszFormatName = nullptr;
|
||||
wStream sub1buffer = WINPR_C_ARRAY_INIT;
|
||||
CLIPRDR_FORMAT* formats = nullptr;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
const BOOL asciiNames = (formatList->common.msgFlags & CB_ASCII_NAMES) != 0;
|
||||
|
||||
index = 0;
|
||||
/* empty format list */
|
||||
formatList->formats = nullptr;
|
||||
formatList->numFormats = 0;
|
||||
|
||||
wStream* sub1 =
|
||||
Stream_StaticConstInit(&sub1buffer, Stream_ConstPointer(s), formatList->common.dataLen);
|
||||
if (!Stream_SafeSeek(s, formatList->common.dataLen))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!formatList->common.dataLen)
|
||||
{
|
||||
}
|
||||
else if (!useLongFormatNames)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(sub1) / 36ULL;
|
||||
if (cap > UINT32_MAX)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "Invalid short format list length: %" PRIuz "", cap);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
formatList->numFormats = (UINT32)cap;
|
||||
|
||||
if (formatList->numFormats)
|
||||
formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
if (!formats)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
formatList->formats = formats;
|
||||
|
||||
while (Stream_GetRemainingLength(sub1) >= 4)
|
||||
{
|
||||
if (index >= formatList->numFormats)
|
||||
goto error_out;
|
||||
|
||||
CLIPRDR_FORMAT* format = &formats[index];
|
||||
|
||||
Stream_Read_UINT32(sub1, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
/* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
|
||||
* the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
|
||||
* or 16 Unicode characters)"
|
||||
* However, both Windows RDSH and mstsc violate this specs as seen in the following
|
||||
* example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
|
||||
* These are 16 unicode characters - *without* terminating null !
|
||||
*/
|
||||
|
||||
szFormatName = Stream_ConstPointer(sub1);
|
||||
wszFormatName = Stream_ConstPointer(sub1);
|
||||
if (!Stream_SafeSeek(sub1, 32))
|
||||
goto error_out;
|
||||
|
||||
free(format->formatName);
|
||||
format->formatName = nullptr;
|
||||
|
||||
if (asciiNames)
|
||||
{
|
||||
if (szFormatName[0])
|
||||
{
|
||||
/* ensure null termination */
|
||||
format->formatName = strndup(szFormatName, 31);
|
||||
if (!format->formatName)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wszFormatName[0])
|
||||
{
|
||||
format->formatName = ConvertWCharNToUtf8Alloc(wszFormatName, 16, nullptr);
|
||||
if (!format->formatName)
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wStream sub2buffer = sub1buffer;
|
||||
wStream* sub2 = &sub2buffer;
|
||||
|
||||
while (Stream_GetRemainingLength(sub1) > 0)
|
||||
{
|
||||
size_t rest = 0;
|
||||
if (!Stream_SafeSeek(sub1, 4)) /* formatId (4 bytes) */
|
||||
goto error_out;
|
||||
|
||||
wszFormatName = Stream_ConstPointer(sub1);
|
||||
rest = Stream_GetRemainingLength(sub1);
|
||||
formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
|
||||
|
||||
if (!Stream_SafeSeek(sub1, (formatNameLength + 1) * sizeof(WCHAR)))
|
||||
goto error_out;
|
||||
formatList->numFormats++;
|
||||
}
|
||||
|
||||
if (formatList->numFormats)
|
||||
formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
if (!formats)
|
||||
{
|
||||
WLog_Print(log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
formatList->formats = formats;
|
||||
|
||||
while (Stream_GetRemainingLength(sub2) >= 4)
|
||||
{
|
||||
if (index >= formatList->numFormats)
|
||||
goto error_out;
|
||||
|
||||
size_t rest = 0;
|
||||
CLIPRDR_FORMAT* format = &formats[index];
|
||||
|
||||
Stream_Read_UINT32(sub2, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
free(format->formatName);
|
||||
format->formatName = nullptr;
|
||||
|
||||
wszFormatName = Stream_ConstPointer(sub2);
|
||||
rest = Stream_GetRemainingLength(sub2);
|
||||
formatNameLength = _wcsnlen(wszFormatName, rest / sizeof(WCHAR));
|
||||
if (!Stream_SafeSeek(sub2, (formatNameLength + 1) * sizeof(WCHAR)))
|
||||
goto error_out;
|
||||
|
||||
if (formatNameLength)
|
||||
{
|
||||
format->formatName =
|
||||
ConvertWCharNToUtf8Alloc(wszFormatName, formatNameLength, nullptr);
|
||||
if (!format->formatName)
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
error_out:
|
||||
cliprdr_free_format_list(formatList);
|
||||
return error;
|
||||
}
|
||||
|
||||
void cliprdr_free_format_list(CLIPRDR_FORMAT_LIST* formatList)
|
||||
{
|
||||
if (formatList == nullptr)
|
||||
return;
|
||||
|
||||
if (formatList->formats)
|
||||
{
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
free(formatList->formats[index].formatName);
|
||||
}
|
||||
|
||||
free(formatList->formats);
|
||||
formatList->formats = nullptr;
|
||||
formatList->numFormats = 0;
|
||||
}
|
||||
}
|
||||
97
third_party/FreeRDP/channels/cliprdr/cliprdr_common.h
vendored
Normal file
97
third_party/FreeRDP/channels/cliprdr/cliprdr_common.h
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Cliprdr common
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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_RDPECLIP_COMMON_H
|
||||
#define FREERDP_CHANNEL_RDPECLIP_COMMON_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/channels/cliprdr.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, size_t dataLen);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream*
|
||||
cliprdr_packet_lock_clipdata_new(const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream*
|
||||
cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream*
|
||||
cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* request);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream*
|
||||
cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response);
|
||||
|
||||
WINPR_ATTR_MALLOC(Stream_Free, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames, BOOL useAsciiNames);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_lock_clipdata(wStream* s,
|
||||
CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_unlock_clipdata(wStream* s,
|
||||
CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_format_data_request(wStream* s,
|
||||
CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_format_data_response(wStream* s,
|
||||
CLIPRDR_FORMAT_DATA_RESPONSE* response);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT
|
||||
cliprdr_read_file_contents_request(wStream* s, CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_file_contents_response(wStream* s,
|
||||
CLIPRDR_FILE_CONTENTS_RESPONSE* response);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT cliprdr_read_format_list(wLog* log, wStream* s, CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames);
|
||||
|
||||
FREERDP_LOCAL void cliprdr_free_format_list(CLIPRDR_FORMAT_LIST* formatList);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPECLIP_COMMON_H */
|
||||
23
third_party/FreeRDP/channels/cliprdr/server/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/cliprdr/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_server("cliprdr")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS cliprdr_main.c cliprdr_main.h ../cliprdr_common.h ../cliprdr_common.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
1523
third_party/FreeRDP/channels/cliprdr/server/cliprdr_main.c
vendored
Normal file
1523
third_party/FreeRDP/channels/cliprdr/server/cliprdr_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
47
third_party/FreeRDP/channels/cliprdr/server/cliprdr_main.h
vendored
Normal file
47
third_party/FreeRDP/channels/cliprdr/server/cliprdr_main.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Clipboard Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.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_CLIPRDR_SERVER_MAIN_H
|
||||
#define FREERDP_CHANNEL_CLIPRDR_SERVER_MAIN_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/server/cliprdr.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("cliprdr.server")
|
||||
|
||||
#define CLIPRDR_HEADER_LENGTH 8
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE vcm;
|
||||
HANDLE Thread;
|
||||
HANDLE StopEvent;
|
||||
void* ChannelHandle;
|
||||
HANDLE ChannelEvent;
|
||||
|
||||
wStream* s;
|
||||
char temporaryDirectory[260];
|
||||
} CliprdrServerPrivate;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_SERVER_MAIN_H */
|
||||
26
third_party/FreeRDP/channels/disp/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/disp/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("disp")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/disp/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/disp/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"disp"
|
||||
TYPE
|
||||
"dynamic"
|
||||
DESCRIPTION
|
||||
"Display Update Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEDISP]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
25
third_party/FreeRDP/channels/disp/client/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/channels/disp/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("disp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS disp_main.c disp_main.h ../disp_common.c ../disp_common.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr)
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
328
third_party/FreeRDP/channels/disp/client/disp_main.c
vendored
Normal file
328
third_party/FreeRDP/channels/disp/client/disp_main.c
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* 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>
|
||||
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/utils/string.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
|
||||
#include "disp_main.h"
|
||||
#include "../disp_common.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN base;
|
||||
|
||||
DispClientContext* context;
|
||||
UINT32 MaxNumMonitors;
|
||||
UINT32 MaxMonitorAreaFactorA;
|
||||
UINT32 MaxMonitorAreaFactorB;
|
||||
} DISP_PLUGIN;
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
disp_send_display_control_monitor_layout_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32 NumMonitors,
|
||||
const DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
UINT status = 0;
|
||||
wStream* s = nullptr;
|
||||
DISP_PLUGIN* disp = nullptr;
|
||||
UINT32 MonitorLayoutSize = 0;
|
||||
DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(Monitors || (NumMonitors == 0));
|
||||
|
||||
disp = (DISP_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(disp);
|
||||
|
||||
MonitorLayoutSize = DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE;
|
||||
header.length = 8 + 8 + (NumMonitors * MonitorLayoutSize);
|
||||
header.type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
|
||||
|
||||
s = Stream_New(nullptr, header.length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((status = disp_write_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (NumMonitors > disp->MaxNumMonitors)
|
||||
NumMonitors = disp->MaxNumMonitors;
|
||||
|
||||
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
|
||||
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
|
||||
WLog_DBG(TAG, "NumMonitors=%" PRIu32 "", NumMonitors);
|
||||
|
||||
for (UINT32 index = 0; index < NumMonitors; index++)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT current = Monitors[index];
|
||||
current.Width -= (current.Width % 2);
|
||||
|
||||
if (current.Width < 200)
|
||||
current.Width = 200;
|
||||
|
||||
if (current.Width > 8192)
|
||||
current.Width = 8192;
|
||||
|
||||
if (current.Width % 2)
|
||||
current.Width++;
|
||||
|
||||
if (current.Height < 200)
|
||||
current.Height = 200;
|
||||
|
||||
if (current.Height > 8192)
|
||||
current.Height = 8192;
|
||||
|
||||
Stream_Write_UINT32(s, current.Flags); /* Flags (4 bytes) */
|
||||
Stream_Write_INT32(s, current.Left); /* Left (4 bytes) */
|
||||
Stream_Write_INT32(s, current.Top); /* Top (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Width); /* Width (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Height); /* Height (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.PhysicalHeight); /* PhysicalHeight (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Orientation); /* Orientation (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||
WLog_DBG(TAG,
|
||||
"\t%" PRIu32 " : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32
|
||||
") W/H=%" PRIu32 "x%" PRIu32 ")",
|
||||
index, current.Flags, current.Left, current.Top, current.Width, current.Height);
|
||||
WLog_DBG(TAG,
|
||||
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32
|
||||
" Orientation: %s DesktopScaleFactor=%" PRIu32 " DeviceScaleFactor=%" PRIu32 "",
|
||||
current.PhysicalWidth, current.PhysicalHeight,
|
||||
freerdp_desktop_rotation_flags_to_string(current.Orientation),
|
||||
current.DesktopScaleFactor, current.DeviceScaleFactor);
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_SealLength(s);
|
||||
status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||
nullptr);
|
||||
Stream_Free(s, TRUE);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_recv_display_control_caps_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
DISP_PLUGIN* disp = nullptr;
|
||||
DispClientContext* context = nullptr;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
disp = (DISP_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(disp);
|
||||
|
||||
context = disp->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
|
||||
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
|
||||
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
||||
|
||||
if (context->DisplayControlCaps)
|
||||
ret = context->DisplayControlCaps(context, disp->MaxNumMonitors,
|
||||
disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT32 error = 0;
|
||||
DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if ((error = disp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, header.length))
|
||||
{
|
||||
WLog_ERR(TAG, "not enough remaining data");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case DISPLAY_CONTROL_PDU_TYPE_CAPS:
|
||||
return disp_recv_display_control_caps_pdu(callback, s);
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Type %" PRIu32 " not recognized!", header.type);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
return disp_recv_pdu(callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
free(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel Client Interface
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors,
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
DISP_PLUGIN* disp = nullptr;
|
||||
GENERIC_CHANNEL_CALLBACK* callback = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
disp = (DISP_PLUGIN*)context->handle;
|
||||
WINPR_ASSERT(disp);
|
||||
|
||||
callback = disp->base.listener_callback->channel_callback;
|
||||
|
||||
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_plugin_initialize(GENERIC_DYNVC_PLUGIN* base,
|
||||
WINPR_ATTR_UNUSED rdpContext* rcontext,
|
||||
WINPR_ATTR_UNUSED rdpSettings* settings)
|
||||
{
|
||||
DispClientContext* context = nullptr;
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
|
||||
|
||||
WINPR_ASSERT(disp);
|
||||
disp->MaxNumMonitors = 16;
|
||||
disp->MaxMonitorAreaFactorA = 8192;
|
||||
disp->MaxMonitorAreaFactorB = 8192;
|
||||
|
||||
context = (DispClientContext*)calloc(1, sizeof(DispClientContext));
|
||||
if (!context)
|
||||
{
|
||||
WLog_Print(base->log, WLOG_ERROR, "unable to allocate DispClientContext");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context->handle = (void*)disp;
|
||||
context->SendMonitorLayout = disp_send_monitor_layout;
|
||||
|
||||
disp->base.iface.pInterface = disp->context = context;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void disp_plugin_terminated(GENERIC_DYNVC_PLUGIN* base)
|
||||
{
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
|
||||
|
||||
WINPR_ASSERT(disp);
|
||||
|
||||
free(disp->context);
|
||||
}
|
||||
|
||||
static const IWTSVirtualChannelCallback disp_callbacks = { disp_on_data_received,
|
||||
nullptr, /* Open */
|
||||
disp_on_close, nullptr };
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE disp_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, DISP_DVC_CHANNEL_NAME,
|
||||
sizeof(DISP_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&disp_callbacks, disp_plugin_initialize,
|
||||
disp_plugin_terminated);
|
||||
}
|
||||
36
third_party/FreeRDP/channels/disp/client/disp_main.h
vendored
Normal file
36
third_party/FreeRDP/channels/disp/client/disp_main.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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_DISP_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_DISP_CLIENT_MAIN_H
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include <freerdp/client/disp.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("disp.client")
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DISP_CLIENT_MAIN_H */
|
||||
55
third_party/FreeRDP/channels/disp/disp_common.c
vendored
Normal file
55
third_party/FreeRDP/channels/disp/disp_common.c
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDPEDISP Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("disp.common")
|
||||
|
||||
#include "disp_common.h"
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, header->type);
|
||||
Stream_Read_UINT32(s, header->length);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header)
|
||||
{
|
||||
Stream_Write_UINT32(s, header->type);
|
||||
Stream_Write_UINT32(s, header->length);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
35
third_party/FreeRDP/channels/disp/disp_common.h
vendored
Normal file
35
third_party/FreeRDP/channels/disp/disp_common.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDPEDISP Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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_DISP_COMMON_H
|
||||
#define FREERDP_CHANNEL_DISP_COMMON_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/channels/disp.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header);
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
FREERDP_LOCAL UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DISP_COMMON_H */
|
||||
25
third_party/FreeRDP/channels/disp/server/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/channels/disp/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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.
|
||||
|
||||
define_channel_server("disp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS disp_main.c disp_main.h ../disp_common.c ../disp_common.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
include_directories(..)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
639
third_party/FreeRDP/channels/disp/server/disp_main.c
vendored
Normal file
639
third_party/FreeRDP/channels/disp/server/disp_main.c
vendored
Normal file
@@ -0,0 +1,639 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDPEDISP Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include "disp_main.h"
|
||||
#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/channels/wtsvc.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include <freerdp/server/disp.h>
|
||||
#include "../disp_common.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpedisp.server")
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
|
||||
static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
|
||||
{
|
||||
UINT error = 0;
|
||||
DISPLAY_CONTROL_HEADER header;
|
||||
wStream* s = Stream_New(nullptr, DISPLAY_CONTROL_HEADER_LENGTH + length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto error;
|
||||
}
|
||||
|
||||
header.type = type;
|
||||
header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
|
||||
|
||||
if ((error = disp_write_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return s;
|
||||
error:
|
||||
Stream_Free(s, TRUE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void disp_server_sanitize_monitor_layout(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
|
||||
{
|
||||
if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
|
||||
monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH ||
|
||||
monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
|
||||
monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
|
||||
{
|
||||
if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0)
|
||||
WLog_DBG(
|
||||
TAG,
|
||||
"Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32
|
||||
", %" PRIu32 "]",
|
||||
monitor->PhysicalWidth, monitor->PhysicalHeight);
|
||||
|
||||
monitor->PhysicalWidth = monitor->PhysicalHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL disp_server_is_monitor_layout_valid(const DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
|
||||
{
|
||||
WINPR_ASSERT(monitor);
|
||||
|
||||
if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
|
||||
monitor->Width > DISPLAY_CONTROL_MAX_MONITOR_WIDTH)
|
||||
{
|
||||
WLog_WARN(TAG, "Received invalid value for monitor->Width: %" PRIu32 "", monitor->Width);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (monitor->Height < DISPLAY_CONTROL_MIN_MONITOR_HEIGHT ||
|
||||
monitor->Height > DISPLAY_CONTROL_MAX_MONITOR_HEIGHT)
|
||||
{
|
||||
WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "", monitor->Height);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (monitor->Orientation)
|
||||
{
|
||||
case ORIENTATION_LANDSCAPE:
|
||||
case ORIENTATION_PORTRAIT:
|
||||
case ORIENTATION_LANDSCAPE_FLIPPED:
|
||||
case ORIENTATION_PORTRAIT_FLIPPED:
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_WARN(TAG, "Received incorrect value for monitor->Orientation: %" PRIu32 "",
|
||||
monitor->Orientation);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerContext* context)
|
||||
{
|
||||
UINT32 error = CHANNEL_RC_OK;
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT_PDU pdu = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, pdu.MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
|
||||
|
||||
if (pdu.MonitorLayoutSize != DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE)
|
||||
{
|
||||
WLog_ERR(TAG, "MonitorLayoutSize is set to %" PRIu32 ". expected %d", pdu.MonitorLayoutSize,
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, pdu.NumMonitors); /* NumMonitors (4 bytes) */
|
||||
|
||||
if (pdu.NumMonitors > context->MaxNumMonitors)
|
||||
{
|
||||
WLog_ERR(TAG, "NumMonitors (%" PRIu32 ")> server MaxNumMonitors (%" PRIu32 ")",
|
||||
pdu.NumMonitors, context->MaxNumMonitors);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.NumMonitors,
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
pdu.Monitors = (DISPLAY_CONTROL_MONITOR_LAYOUT*)calloc(pdu.NumMonitors,
|
||||
sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
|
||||
|
||||
if (!pdu.Monitors)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_recv_display_control_monitor_layout_pdu(): calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "disp_recv_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32 "",
|
||||
pdu.NumMonitors);
|
||||
|
||||
for (UINT32 index = 0; index < pdu.NumMonitors; index++)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* monitor = &(pdu.Monitors[index]);
|
||||
|
||||
Stream_Read_UINT32(s, monitor->Flags); /* Flags (4 bytes) */
|
||||
Stream_Read_INT32(s, monitor->Left); /* Left (4 bytes) */
|
||||
Stream_Read_INT32(s, monitor->Top); /* Top (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->Width); /* Width (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->Height); /* Height (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->PhysicalHeight); /* PhysicalHeight (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||
Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||
|
||||
disp_server_sanitize_monitor_layout(monitor);
|
||||
WLog_DBG(TAG,
|
||||
"\t%" PRIu32 " : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32
|
||||
") W/H=%" PRIu32 "x%" PRIu32 ")",
|
||||
index, monitor->Flags, monitor->Left, monitor->Top, monitor->Width,
|
||||
monitor->Height);
|
||||
WLog_DBG(TAG,
|
||||
"\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
|
||||
"",
|
||||
monitor->PhysicalWidth, monitor->PhysicalHeight, monitor->Orientation);
|
||||
|
||||
if (!disp_server_is_monitor_layout_valid(monitor))
|
||||
{
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (context)
|
||||
IFCALLRET(context->DispMonitorLayout, error, context, &pdu);
|
||||
|
||||
out:
|
||||
free(pdu.Monitors);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT disp_server_receive_pdu(DispServerContext* context, wStream* s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
size_t beg = 0;
|
||||
size_t end = 0;
|
||||
DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
beg = Stream_GetPosition(s);
|
||||
|
||||
if ((error = disp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
|
||||
if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
|
||||
WLog_ERR(TAG,
|
||||
"disp_recv_display_control_monitor_layout_pdu "
|
||||
"failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
error = CHANNEL_RC_BAD_PROC;
|
||||
WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.type);
|
||||
break;
|
||||
}
|
||||
|
||||
end = Stream_GetPosition(s);
|
||||
|
||||
if (end != (beg + header.length))
|
||||
{
|
||||
WLog_ERR(TAG, "Unexpected DISP pdu end: Actual: %" PRIuz ", Expected: %" PRIuz "", end,
|
||||
(beg + header.length));
|
||||
Stream_SetPosition(s, (beg + header.length));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT disp_server_handle_messages(DispServerContext* context)
|
||||
{
|
||||
DWORD BytesReturned = 0;
|
||||
void* buffer = nullptr;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
DispServerPrivate* priv = nullptr;
|
||||
wStream* s = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
priv = context->priv;
|
||||
WINPR_ASSERT(priv);
|
||||
|
||||
s = priv->input_stream;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
/* Check whether the dynamic channel is ready */
|
||||
if (!priv->isReady)
|
||||
{
|
||||
if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_DATA)
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
priv->isReady = *((BOOL*)buffer);
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
/* Consume channel event only after the disp dynamic channel is ready */
|
||||
Stream_ResetPosition(s);
|
||||
|
||||
if (!WTSVirtualChannelRead(priv->disp_channel, 0, nullptr, 0, &BytesReturned))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_DATA)
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
if (cap > UINT32_MAX)
|
||||
return CHANNEL_RC_NO_BUFFER;
|
||||
|
||||
if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s, char), (ULONG)cap,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
Stream_SetLength(s, BytesReturned);
|
||||
Stream_ResetPosition(s);
|
||||
|
||||
while (Stream_GetPosition(s) < Stream_Length(s))
|
||||
{
|
||||
if ((ret = disp_server_receive_pdu(context, s)))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"disp_server_receive_pdu "
|
||||
"failed with error %" PRIu32 "!",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD WINAPI disp_server_thread_func(LPVOID arg)
|
||||
{
|
||||
DispServerContext* context = (DispServerContext*)arg;
|
||||
DispServerPrivate* priv = nullptr;
|
||||
DWORD status = 0;
|
||||
DWORD nCount = 0;
|
||||
HANDLE events[8] = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
priv = context->priv;
|
||||
WINPR_ASSERT(priv);
|
||||
|
||||
events[nCount++] = priv->stopEvent;
|
||||
events[nCount++] = priv->channelEvent;
|
||||
|
||||
/* Main virtual channel loop. RDPEDISP do not need version negotiation */
|
||||
while (TRUE)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop Event */
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if ((error = disp_server_handle_messages(context)))
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_handle_messages failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_server_open(DispServerContext* context)
|
||||
{
|
||||
UINT rc = ERROR_INTERNAL_ERROR;
|
||||
DispServerPrivate* priv = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = nullptr;
|
||||
void* buffer = nullptr;
|
||||
UINT32 channelId = 0;
|
||||
BOOL status = TRUE;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
priv = context->priv;
|
||||
WINPR_ASSERT(priv);
|
||||
|
||||
priv->SessionId = WTS_CURRENT_SESSION;
|
||||
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
priv->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
priv->disp_channel =
|
||||
WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!priv->disp_channel)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
|
||||
rc = GetLastError();
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(priv->disp_channel);
|
||||
|
||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Query for channel event handle */
|
||||
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) ||
|
||||
(BytesReturned != sizeof(HANDLE)))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"WTSVirtualChannelQuery failed "
|
||||
"or invalid returned size(%" PRIu32 ")",
|
||||
BytesReturned);
|
||||
|
||||
if (buffer)
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
priv->channelEvent = *(HANDLE*)buffer;
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (priv->thread == nullptr)
|
||||
{
|
||||
if (!(priv->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (!(priv->thread =
|
||||
CreateThread(nullptr, 0, disp_server_thread_func, (void*)context, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->stopEvent = nullptr;
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
out_close:
|
||||
(void)WTSVirtualChannelClose(priv->disp_channel);
|
||||
priv->disp_channel = nullptr;
|
||||
priv->channelEvent = nullptr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
|
||||
{
|
||||
UINT ret = 0;
|
||||
ULONG written = 0;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
|
||||
&written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (written < Stream_GetPosition(s))
|
||||
{
|
||||
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
|
||||
Stream_GetPosition(s));
|
||||
}
|
||||
|
||||
ret = CHANNEL_RC_OK;
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_server_send_caps_pdu(DispServerContext* context)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_single_packet_new failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
|
||||
Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
|
||||
Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
||||
return disp_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_server_close(DispServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DispServerPrivate* priv = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
priv = context->priv;
|
||||
WINPR_ASSERT(priv);
|
||||
|
||||
if (priv->thread)
|
||||
{
|
||||
(void)SetEvent(priv->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(priv->thread);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->thread = nullptr;
|
||||
priv->stopEvent = nullptr;
|
||||
}
|
||||
|
||||
if (priv->disp_channel)
|
||||
{
|
||||
(void)WTSVirtualChannelClose(priv->disp_channel);
|
||||
priv->disp_channel = nullptr;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
DispServerContext* disp_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DispServerContext* context = nullptr;
|
||||
DispServerPrivate* priv = nullptr;
|
||||
context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->input_stream = Stream_New(nullptr, 4);
|
||||
|
||||
if (!priv->input_stream)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
context->vcm = vcm;
|
||||
context->Open = disp_server_open;
|
||||
context->Close = disp_server_close;
|
||||
context->DisplayControlCaps = disp_server_send_caps_pdu;
|
||||
priv->isReady = FALSE;
|
||||
return context;
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
disp_server_context_free(context);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void disp_server_context_free(DispServerContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
if (context->priv)
|
||||
{
|
||||
disp_server_close(context);
|
||||
Stream_Free(context->priv->input_stream, TRUE);
|
||||
free(context->priv);
|
||||
}
|
||||
|
||||
free(context);
|
||||
}
|
||||
37
third_party/FreeRDP/channels/disp/server/disp_main.h
vendored
Normal file
37
third_party/FreeRDP/channels/disp/server/disp_main.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDPEDISP Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.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_DISP_SERVER_MAIN_H
|
||||
#define FREERDP_CHANNEL_DISP_SERVER_MAIN_H
|
||||
|
||||
#include <freerdp/server/disp.h>
|
||||
|
||||
struct s_disp_server_private
|
||||
{
|
||||
BOOL isReady;
|
||||
wStream* input_stream;
|
||||
HANDLE channelEvent;
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
DWORD SessionId;
|
||||
|
||||
void* disp_channel;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DISP_SERVER_MAIN_H */
|
||||
26
third_party/FreeRDP/channels/drdynvc/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/drdynvc/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("drdynvc")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/drdynvc/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/drdynvc/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"drdynvc"
|
||||
TYPE
|
||||
"static"
|
||||
DESCRIPTION
|
||||
"Dynamic Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEDYC]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
22
third_party/FreeRDP/channels/drdynvc/client/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/channels/drdynvc/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("drdynvc")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS drdynvc_main.c drdynvc_main.h)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
2149
third_party/FreeRDP/channels/drdynvc/client/drdynvc_main.c
vendored
Normal file
2149
third_party/FreeRDP/channels/drdynvc/client/drdynvc_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
135
third_party/FreeRDP/channels/drdynvc/client/drdynvc_main.h
vendored
Normal file
135
third_party/FreeRDP/channels/drdynvc/client/drdynvc_main.h
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Dynamic Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* 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_DRDYNVC_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_DRDYNVC_CLIENT_MAIN_H
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/drdynvc.h>
|
||||
#include <freerdp/codec/zgfx.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct drdynvc_plugin drdynvcPlugin;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSVirtualChannelManager iface;
|
||||
|
||||
drdynvcPlugin* drdynvc;
|
||||
|
||||
wArrayList* plugin_names;
|
||||
wArrayList* plugins;
|
||||
|
||||
wHashTable* listeners;
|
||||
wHashTable* channelsById;
|
||||
wStreamPool* pool;
|
||||
} DVCMAN;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSListener iface;
|
||||
|
||||
DVCMAN* dvcman;
|
||||
char* channel_name;
|
||||
UINT32 flags;
|
||||
IWTSListenerCallback* listener_callback;
|
||||
} DVCMAN_LISTENER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IDRDYNVC_ENTRY_POINTS iface;
|
||||
|
||||
DVCMAN* dvcman;
|
||||
const ADDIN_ARGV* args;
|
||||
rdpContext* context;
|
||||
} DVCMAN_ENTRY_POINTS;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DVC_CHANNEL_INIT,
|
||||
DVC_CHANNEL_RUNNING,
|
||||
DVC_CHANNEL_CLOSED
|
||||
} DVC_CHANNEL_STATE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IWTSVirtualChannel iface;
|
||||
|
||||
volatile LONG refCounter;
|
||||
DVC_CHANNEL_STATE state;
|
||||
DVCMAN* dvcman;
|
||||
void* pInterface;
|
||||
UINT32 channel_id;
|
||||
char* channel_name;
|
||||
IWTSVirtualChannelCallback* channel_callback;
|
||||
|
||||
wStream* dvc_data;
|
||||
UINT32 dvc_data_length;
|
||||
ZGFX_CONTEXT* decompressor;
|
||||
CRITICAL_SECTION lock;
|
||||
} DVCMAN_CHANNEL;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DRDYNVC_STATE_INITIAL,
|
||||
DRDYNVC_STATE_CAPABILITIES,
|
||||
DRDYNVC_STATE_READY,
|
||||
DRDYNVC_STATE_OPENING_CHANNEL,
|
||||
DRDYNVC_STATE_SEND_RECEIVE,
|
||||
DRDYNVC_STATE_FINAL
|
||||
} DRDYNVC_STATE;
|
||||
|
||||
struct drdynvc_plugin
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
|
||||
wLog* log;
|
||||
HANDLE thread;
|
||||
BOOL async;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessageQueue* queue;
|
||||
|
||||
DRDYNVC_STATE state;
|
||||
DrdynvcClientContext* context;
|
||||
|
||||
UINT16 version;
|
||||
int PriorityCharge0;
|
||||
int PriorityCharge1;
|
||||
int PriorityCharge2;
|
||||
int PriorityCharge3;
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DRDYNVC_CLIENT_MAIN_H */
|
||||
23
third_party/FreeRDP/channels/drdynvc/server/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/drdynvc/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_server("drdynvc")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS drdynvc_main.c drdynvc_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
204
third_party/FreeRDP/channels/drdynvc/server/drdynvc_main.c
vendored
Normal file
204
third_party/FreeRDP/channels/drdynvc/server/drdynvc_main.c
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Dynamic 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
|
||||
#include "drdynvc_main.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("drdynvc.server")
|
||||
|
||||
static DWORD WINAPI drdynvc_server_thread(WINPR_ATTR_UNUSED LPVOID arg)
|
||||
{
|
||||
#if 0
|
||||
wStream* s;
|
||||
DWORD status;
|
||||
DWORD nCount;
|
||||
void* buffer;
|
||||
HANDLE events[8];
|
||||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned;
|
||||
DrdynvcServerContext* context;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
context = (DrdynvcServerContext*) arg;
|
||||
buffer = nullptr;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = nullptr;
|
||||
|
||||
s = Stream_New(nullptr, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
ExitThread((DWORD) CHANNEL_RC_NO_MEMORY);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle,
|
||||
&buffer, &BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = ChannelEvent;
|
||||
events[nCount++] = context->priv->StopEvent;
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, nullptr, 0,
|
||||
&BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
Stream_BufferAs(s, char), Stream_Capacity(s), &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
ExitThread((DWORD) error);
|
||||
#endif
|
||||
// WTF ... this code only reads data into the stream until there is no more memory
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle =
|
||||
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, DRDYNVC_SVC_CHANNEL_NAME);
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!(context->priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread =
|
||||
CreateThread(nullptr, 0, drdynvc_server_thread, (void*)context, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
{
|
||||
UINT error = 0;
|
||||
(void)SetEvent(context->priv->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(context->priv->Thread);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DrdynvcServerContext* context = nullptr;
|
||||
context = (DrdynvcServerContext*)calloc(1, sizeof(DrdynvcServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
context->vcm = vcm;
|
||||
context->Start = drdynvc_server_start;
|
||||
context->Stop = drdynvc_server_stop;
|
||||
context->priv = (DrdynvcServerPrivate*)calloc(1, sizeof(DrdynvcServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(context);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void drdynvc_server_context_free(DrdynvcServerContext* context)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
37
third_party/FreeRDP/channels/drdynvc/server/drdynvc_main.h
vendored
Normal file
37
third_party/FreeRDP/channels/drdynvc/server/drdynvc_main.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Dynamic Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.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_DRDYNVC_SERVER_MAIN_H
|
||||
#define FREERDP_CHANNEL_DRDYNVC_SERVER_MAIN_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/settings.h>
|
||||
#include <freerdp/server/drdynvc.h>
|
||||
|
||||
struct s_drdynvc_server_private
|
||||
{
|
||||
HANDLE Thread;
|
||||
HANDLE StopEvent;
|
||||
void* ChannelHandle;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DRDYNVC_SERVER_MAIN_H */
|
||||
22
third_party/FreeRDP/channels/drive/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/channels/drive/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("drive")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/drive/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/drive/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"drive"
|
||||
TYPE
|
||||
"device"
|
||||
DESCRIPTION
|
||||
"Drive Redirection Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEFS]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
23
third_party/FreeRDP/channels/drive/client/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/drive/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("drive")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS drive_file.c drive_file.h drive_main.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp)
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
||||
1087
third_party/FreeRDP/channels/drive/client/drive_file.c
vendored
Normal file
1087
third_party/FreeRDP/channels/drive/client/drive_file.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
84
third_party/FreeRDP/channels/drive/client/drive_file.h
vendored
Normal file
84
third_party/FreeRDP/channels/drive/client/drive_file.h
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* File System Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2012 Gerald Richter
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Inuvika Inc.
|
||||
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.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_DRIVE_CLIENT_FILE_H
|
||||
#define FREERDP_CHANNEL_DRIVE_CLIENT_FILE_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/file.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client")
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT32 id;
|
||||
BOOL is_dir;
|
||||
HANDLE file_handle;
|
||||
HANDLE find_handle;
|
||||
WIN32_FIND_DATAW find_data;
|
||||
const WCHAR* basepath;
|
||||
WCHAR* fullpath;
|
||||
BOOL delete_pending;
|
||||
UINT32 FileAttributes;
|
||||
UINT32 SharedAccess;
|
||||
UINT32 DesiredAccess;
|
||||
UINT32 CreateDisposition;
|
||||
UINT32 CreateOptions;
|
||||
} DRIVE_FILE;
|
||||
|
||||
FREERDP_LOCAL BOOL drive_file_free(DRIVE_FILE* file);
|
||||
|
||||
WINPR_ATTR_MALLOC(drive_file_free, 1)
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL DRIVE_FILE*
|
||||
drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength, UINT32 id,
|
||||
UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions,
|
||||
UINT32 FileAttributes, UINT32 SharedAccess);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_open(DRIVE_FILE* file);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer,
|
||||
UINT32* Length);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_write(DRIVE_FILE* file, const BYTE* buffer,
|
||||
UINT32 Length);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_query_information(DRIVE_FILE* file,
|
||||
UINT32 FsInformationClass,
|
||||
wStream* output);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL drive_file_set_information(DRIVE_FILE* file,
|
||||
UINT32 FsInformationClass,
|
||||
UINT32 Length, wStream* input);
|
||||
|
||||
WINPR_ATTR_NODISCARD FREERDP_LOCAL BOOL
|
||||
drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||
const WCHAR* path, UINT32 PathWCharLength, wStream* output);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
||||
1184
third_party/FreeRDP/channels/drive/client/drive_main.c
vendored
Normal file
1184
third_party/FreeRDP/channels/drive/client/drive_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
26
third_party/FreeRDP/channels/echo/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/echo/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("echo")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/echo/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/echo/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"echo"
|
||||
TYPE
|
||||
"dynamic"
|
||||
DESCRIPTION
|
||||
"Echo Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEECO]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
25
third_party/FreeRDP/channels/echo/client/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/channels/echo/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("echo")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS echo_main.c echo_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr)
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
93
third_party/FreeRDP/channels/echo/client/echo_main.c
vendored
Normal file
93
third_party/FreeRDP/channels/echo/client/echo_main.c
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Echo Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2013 Christian Hofstaedtler
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include "echo_main.h"
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/channels/echo.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("echo.client")
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN baseDynPlugin;
|
||||
} ECHO_PLUGIN;
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
const BYTE* pBuffer = Stream_ConstPointer(data);
|
||||
const size_t cbSize = Stream_GetRemainingLength(data);
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
WINPR_ASSERT(callback->channel);
|
||||
WINPR_ASSERT(callback->channel->Write);
|
||||
|
||||
if (cbSize > UINT32_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
/* echo back what we have received. ECHO does not have any message IDs. */
|
||||
return callback->channel->Write(callback->channel, (ULONG)cbSize, pBuffer, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
|
||||
free(callback);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static const IWTSVirtualChannelCallback echo_callbacks = { echo_on_data_received,
|
||||
nullptr, /* Open */
|
||||
echo_on_close, nullptr };
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE echo_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, ECHO_DVC_CHANNEL_NAME,
|
||||
sizeof(ECHO_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&echo_callbacks, nullptr, nullptr);
|
||||
}
|
||||
40
third_party/FreeRDP/channels/echo/client/echo_main.h
vendored
Normal file
40
third_party/FreeRDP/channels/echo/client/echo_main.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Echo Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2013 Christian Hofstaedtler
|
||||
*
|
||||
* 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_ECHO_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_ECHO_CLIENT_MAIN_H
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define DVC_TAG CHANNELS_TAG("echo.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_ECHO_CLIENT_MAIN_H */
|
||||
23
third_party/FreeRDP/channels/echo/server/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/echo/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_server("echo")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS echo_main.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
386
third_party/FreeRDP/channels/echo/server/echo_main.c
vendored
Normal file
386
third_party/FreeRDP/channels/echo/server/echo_main.c
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Echo Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2014 Vic Lee
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/server/echo.h>
|
||||
#include <freerdp/channels/echo.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("echo.server")
|
||||
|
||||
typedef struct
|
||||
{
|
||||
echo_server_context context;
|
||||
|
||||
BOOL opened;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* echo_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
} echo_server;
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_server_open_channel(echo_server* echo)
|
||||
{
|
||||
DWORD Error = 0;
|
||||
HANDLE hEvent = nullptr;
|
||||
DWORD StartTick = 0;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = nullptr;
|
||||
|
||||
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
echo->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
|
||||
StartTick = GetTickCount();
|
||||
|
||||
while (echo->echo_channel == nullptr)
|
||||
{
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId, ECHO_DVC_CHANNEL_NAME,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (echo->echo_channel)
|
||||
{
|
||||
UINT32 channelId = 0;
|
||||
BOOL status = TRUE;
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
|
||||
|
||||
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_NOT_FOUND)
|
||||
break;
|
||||
|
||||
if (GetTickCount() - StartTick > 5000)
|
||||
break;
|
||||
}
|
||||
|
||||
return echo->echo_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
void* buffer = nullptr;
|
||||
DWORD nCount = 0;
|
||||
HANDLE events[8];
|
||||
BOOL ready = FALSE;
|
||||
HANDLE ChannelEvent = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
echo_server* echo = (echo_server*)arg;
|
||||
UINT error = 0;
|
||||
DWORD status = 0;
|
||||
|
||||
if ((error = echo_server_open_channel(echo)))
|
||||
{
|
||||
UINT error2 = 0;
|
||||
WLog_ERR(TAG, "echo_server_open_channel failed with error %" PRIu32 "!", error);
|
||||
IFCALLRET(echo->context.OpenResult, error2, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
|
||||
|
||||
if (error2)
|
||||
WLog_ERR(TAG, "echo server's OpenResult callback failed with error %" PRIu32 "",
|
||||
error2);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = nullptr;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = nullptr;
|
||||
|
||||
if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
ChannelEvent = *(HANDLE*)buffer;
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = echo->stopEvent;
|
||||
events[nCount++] = ChannelEvent;
|
||||
|
||||
/* Wait for the client to confirm that the Graphics Pipeline dynamic channel is ready */
|
||||
|
||||
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)
|
||||
{
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_CLOSED);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualChannelReady, &buffer,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_ERROR);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ready = *((BOOL*)buffer);
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
{
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s = Stream_New(nullptr, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
(void)WTSVirtualChannelClose(echo->echo_channel);
|
||||
ExitThread(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
while (ready)
|
||||
{
|
||||
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_ResetPosition(s);
|
||||
if (!WTSVirtualChannelRead(echo->echo_channel, 0, nullptr, 0, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
IFCALLRET(echo->context.Response, error, &echo->context, Stream_Buffer(s), BytesReturned);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
(void)WTSVirtualChannelClose(echo->echo_channel);
|
||||
echo->echo_channel = nullptr;
|
||||
out:
|
||||
|
||||
if (error && echo->context.rdpcontext)
|
||||
setChannelError(echo->context.rdpcontext, error,
|
||||
"echo_server_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_server_open(echo_server_context* context)
|
||||
{
|
||||
echo_server* echo = (echo_server*)context;
|
||||
|
||||
if (echo->thread == nullptr)
|
||||
{
|
||||
if (!(echo->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(echo->thread =
|
||||
CreateThread(nullptr, 0, echo_server_thread_func, (void*)echo, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
(void)CloseHandle(echo->stopEvent);
|
||||
echo->stopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_server_close(echo_server_context* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
echo_server* echo = (echo_server*)context;
|
||||
|
||||
if (echo->thread)
|
||||
{
|
||||
(void)SetEvent(echo->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(echo->thread);
|
||||
(void)CloseHandle(echo->stopEvent);
|
||||
echo->thread = nullptr;
|
||||
echo->stopEvent = nullptr;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
|
||||
{
|
||||
union
|
||||
{
|
||||
const BYTE* cpv;
|
||||
CHAR* pv;
|
||||
} cnv;
|
||||
cnv.cpv = buffer;
|
||||
echo_server* echo = (echo_server*)context;
|
||||
WINPR_ASSERT(echo);
|
||||
|
||||
return WTSVirtualChannelWrite(echo->echo_channel, cnv.pv, length, nullptr);
|
||||
}
|
||||
|
||||
echo_server_context* echo_server_context_new(HANDLE vcm)
|
||||
{
|
||||
echo_server* echo = nullptr;
|
||||
echo = (echo_server*)calloc(1, sizeof(echo_server));
|
||||
|
||||
if (echo)
|
||||
{
|
||||
echo->context.vcm = vcm;
|
||||
echo->context.Open = echo_server_open;
|
||||
echo->context.Close = echo_server_close;
|
||||
echo->context.Request = echo_server_request;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
|
||||
return (echo_server_context*)echo;
|
||||
}
|
||||
|
||||
void echo_server_context_free(echo_server_context* context)
|
||||
{
|
||||
echo_server* echo = (echo_server*)context;
|
||||
echo_server_close(context);
|
||||
free(echo);
|
||||
}
|
||||
26
third_party/FreeRDP/channels/encomsp/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/encomsp/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel("encomsp")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/encomsp/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/encomsp/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"encomsp"
|
||||
TYPE
|
||||
"static"
|
||||
DESCRIPTION
|
||||
"Multiparty Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEMC]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
24
third_party/FreeRDP/channels/encomsp/client/CMakeLists.txt
vendored
Normal file
24
third_party/FreeRDP/channels/encomsp/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_client("encomsp")
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS encomsp_main.c encomsp_main.h)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
1304
third_party/FreeRDP/channels/encomsp/client/encomsp_main.c
vendored
Normal file
1304
third_party/FreeRDP/channels/encomsp/client/encomsp_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
third_party/FreeRDP/channels/encomsp/client/encomsp_main.h
vendored
Normal file
42
third_party/FreeRDP/channels/encomsp/client/encomsp_main.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Multiparty Virtual Channel
|
||||
*
|
||||
* Copyright 2014 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_ENCOMSP_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_ENCOMSP_CLIENT_MAIN_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/addin.h>
|
||||
|
||||
#include <freerdp/client/encomsp.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("encomsp.client")
|
||||
|
||||
typedef struct encomsp_plugin encomspPlugin;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_ENCOMSP_CLIENT_MAIN_H */
|
||||
25
third_party/FreeRDP/channels/encomsp/server/CMakeLists.txt
vendored
Normal file
25
third_party/FreeRDP/channels/encomsp/server/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
||||
define_channel_server("encomsp")
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS encomsp_main.c encomsp_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr)
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
352
third_party/FreeRDP/channels/encomsp/server/encomsp_main.c
vendored
Normal file
352
third_party/FreeRDP/channels/encomsp/server/encomsp_main.c
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Multiparty Virtual Channel
|
||||
*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "encomsp_main.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("encomsp.server")
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_read_header(wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, ENCOMSP_ORDER_HEADER_SIZE))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, header->Type); /* Type (2 bytes) */
|
||||
Stream_Read_UINT16(s, header->Length); /* Length (2 bytes) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_recv_change_participant_control_level_pdu(EncomspServerContext* context,
|
||||
wStream* s,
|
||||
const ENCOMSP_ORDER_HEADER* header)
|
||||
{
|
||||
ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = WINPR_C_ARRAY_INIT;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
if (pos < ENCOMSP_ORDER_HEADER_SIZE)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
const size_t beg = pos - ENCOMSP_ORDER_HEADER_SIZE;
|
||||
CopyMemory(&pdu, header, sizeof(ENCOMSP_ORDER_HEADER));
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, pdu.Flags); /* Flags (2 bytes) */
|
||||
Stream_Read_UINT32(s, pdu.ParticipantId); /* ParticipantId (4 bytes) */
|
||||
const size_t end = Stream_GetPosition(s);
|
||||
|
||||
if ((beg + header->Length) < end)
|
||||
{
|
||||
WLog_ERR(TAG, "Not enough data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if ((beg + header->Length) > end)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, (size_t)((beg + header->Length) - end)))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_SetPosition(s, (beg + header->Length));
|
||||
}
|
||||
|
||||
IFCALLRET(context->ChangeParticipantControlLevel, error, context, &pdu);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %" PRIu32 "",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_receive_pdu(EncomspServerContext* context, wStream* s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
while (Stream_GetRemainingLength(s) > 0)
|
||||
{
|
||||
ENCOMSP_ORDER_HEADER header = WINPR_C_ARRAY_INIT;
|
||||
if ((error = encomsp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_read_header failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "EncomspReceive: Type: %" PRIu16 " Length: %" PRIu16 "", header.Type,
|
||||
header.Length);
|
||||
|
||||
switch (header.Type)
|
||||
{
|
||||
case ODTYPE_PARTICIPANT_CTRL_CHANGED:
|
||||
if ((error =
|
||||
encomsp_recv_change_participant_control_level_pdu(context, s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"encomsp_recv_change_participant_control_level_pdu failed with error "
|
||||
"%" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "header.Type unknown %" PRIu16 "!", header.Type);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
DWORD nCount = 0;
|
||||
void* buffer = nullptr;
|
||||
HANDLE events[8];
|
||||
HANDLE ChannelEvent = nullptr;
|
||||
DWORD BytesReturned = 0;
|
||||
ENCOMSP_ORDER_HEADER* header = nullptr;
|
||||
EncomspServerContext* context = nullptr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status = 0;
|
||||
context = (EncomspServerContext*)arg;
|
||||
|
||||
buffer = nullptr;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = nullptr;
|
||||
s = Stream_New(nullptr, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
ChannelEvent = *(HANDLE*)buffer;
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = ChannelEvent;
|
||||
events[nCount++] = context->priv->StopEvent;
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(context->priv->StopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, nullptr, 0, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
if ((cap > UINT32_MAX) ||
|
||||
!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)cap, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Stream_GetPosition(s) >= ENCOMSP_ORDER_HEADER_SIZE)
|
||||
{
|
||||
header = Stream_BufferAs(s, ENCOMSP_ORDER_HEADER);
|
||||
|
||||
if (header->Length >= Stream_GetPosition(s))
|
||||
{
|
||||
Stream_SealLength(s);
|
||||
Stream_ResetPosition(s);
|
||||
|
||||
if ((error = encomsp_server_receive_pdu(context, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_server_receive_pdu failed with error %" PRIu32 "!",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
|
||||
Stream_ResetPosition(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
out:
|
||||
|
||||
if (error && context->rdpcontext)
|
||||
setChannelError(context->rdpcontext, error, "encomsp_server_thread reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_start(EncomspServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle =
|
||||
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, ENCOMSP_SVC_CHANNEL_NAME);
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
|
||||
if (!(context->priv->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread =
|
||||
CreateThread(nullptr, 0, encomsp_server_thread, (void*)context, 0, nullptr)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = nullptr;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
(void)SetEvent(context->priv->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
(void)CloseHandle(context->priv->Thread);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
return error;
|
||||
}
|
||||
|
||||
EncomspServerContext* encomsp_server_context_new(HANDLE vcm)
|
||||
{
|
||||
EncomspServerContext* context = nullptr;
|
||||
context = (EncomspServerContext*)calloc(1, sizeof(EncomspServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
context->vcm = vcm;
|
||||
context->Start = encomsp_server_start;
|
||||
context->Stop = encomsp_server_stop;
|
||||
context->priv = (EncomspServerPrivate*)calloc(1, sizeof(EncomspServerPrivate));
|
||||
|
||||
if (!context->priv)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(context);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void encomsp_server_context_free(EncomspServerContext* context)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
||||
(void)WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
36
third_party/FreeRDP/channels/encomsp/server/encomsp_main.h
vendored
Normal file
36
third_party/FreeRDP/channels/encomsp/server/encomsp_main.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Multiparty Virtual Channel
|
||||
*
|
||||
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.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_ENCOMSP_SERVER_MAIN_H
|
||||
#define FREERDP_CHANNEL_ENCOMSP_SERVER_MAIN_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/server/encomsp.h>
|
||||
|
||||
struct s_encomsp_server_private
|
||||
{
|
||||
HANDLE Thread;
|
||||
HANDLE StopEvent;
|
||||
void* ChannelHandle;
|
||||
};
|
||||
|
||||
#endif /* FREERDP_CHANNEL_ENCOMSP_SERVER_MAIN_H */
|
||||
22
third_party/FreeRDP/channels/geometry/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/channels/geometry/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2017 David Fort <contact@hardening-consulting.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.
|
||||
|
||||
define_channel("geometry")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/geometry/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/geometry/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT ON)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"geometry"
|
||||
TYPE
|
||||
"dynamic"
|
||||
DESCRIPTION
|
||||
"Geometry tracking Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPEGT]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
26
third_party/FreeRDP/channels/geometry/client/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/geometry/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2017 David Fort <contact@hardening-consulting.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.
|
||||
|
||||
define_channel_client("geometry")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS geometry_main.c geometry_main.h)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
409
third_party/FreeRDP/channels/geometry/client/geometry_main.c
vendored
Normal file
409
third_party/FreeRDP/channels/geometry/client/geometry_main.c
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Geometry tracking Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2017 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/client/geometry.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("geometry.client")
|
||||
|
||||
#include "geometry_main.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN base;
|
||||
GeometryClientContext* context;
|
||||
} GEOMETRY_PLUGIN;
|
||||
|
||||
static UINT32 mappedGeometryHash(const void* v)
|
||||
{
|
||||
const UINT64* g = (const UINT64*)v;
|
||||
return (UINT32)((*g >> 32) + (*g & 0xffffffff));
|
||||
}
|
||||
|
||||
static BOOL mappedGeometryKeyCompare(const void* v1, const void* v2)
|
||||
{
|
||||
const UINT64* g1 = (const UINT64*)v1;
|
||||
const UINT64* g2 = (const UINT64*)v2;
|
||||
return *g1 == *g2;
|
||||
}
|
||||
|
||||
static void freerdp_rgndata_reset(FREERDP_RGNDATA* data)
|
||||
{
|
||||
data->nRectCount = 0;
|
||||
}
|
||||
|
||||
static UINT32 geometry_read_RGNDATA(wLog* logger, wStream* s, UINT32 len, FREERDP_RGNDATA* rgndata)
|
||||
{
|
||||
WINPR_ASSERT(rgndata);
|
||||
|
||||
if (len < 32)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
const UINT32 dwSize = Stream_Get_UINT32(s);
|
||||
|
||||
if (dwSize != 32)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "invalid RGNDATA dwSize");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
const UINT32 iType = Stream_Get_UINT32(s);
|
||||
|
||||
if (iType != RDH_RECTANGLE)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "iType %" PRIu32 " for RGNDATA is not supported", iType);
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
rgndata->nRectCount = Stream_Get_UINT32(s);
|
||||
Stream_Seek_UINT32(s); /* nRgnSize IGNORED */
|
||||
{
|
||||
const INT32 x = Stream_Get_INT32(s);
|
||||
const INT32 y = Stream_Get_INT32(s);
|
||||
const INT32 right = Stream_Get_INT32(s);
|
||||
const INT32 bottom = Stream_Get_INT32(s);
|
||||
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
const INT32 w = right - x;
|
||||
const INT32 h = bottom - y;
|
||||
if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
rgndata->boundingRect.x = (INT16)x;
|
||||
rgndata->boundingRect.y = (INT16)y;
|
||||
rgndata->boundingRect.width = (INT16)w;
|
||||
rgndata->boundingRect.height = (INT16)h;
|
||||
}
|
||||
len -= 32;
|
||||
|
||||
if (len / (4 * 4) < rgndata->nRectCount)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "not enough data for region rectangles");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (rgndata->nRectCount)
|
||||
{
|
||||
RDP_RECT* tmp = realloc(rgndata->rects, rgndata->nRectCount * sizeof(RDP_RECT));
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "unable to allocate memory for %" PRIu32 " RECTs",
|
||||
rgndata->nRectCount);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
rgndata->rects = tmp;
|
||||
|
||||
for (UINT32 i = 0; i < rgndata->nRectCount; i++)
|
||||
{
|
||||
RDP_RECT* rect = &rgndata->rects[i];
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 16))
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
const INT32 x = Stream_Get_INT32(s);
|
||||
const INT32 y = Stream_Get_INT32(s);
|
||||
const INT32 right = Stream_Get_INT32(s);
|
||||
const INT32 bottom = Stream_Get_INT32(s);
|
||||
if ((abs(x) > INT16_MAX) || (abs(y) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const INT32 w = right - x;
|
||||
const INT32 h = bottom - y;
|
||||
if ((abs(w) > INT16_MAX) || (abs(h) > INT16_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
rect->x = (INT16)x;
|
||||
rect->y = (INT16)y;
|
||||
rect->width = (INT16)w;
|
||||
rect->height = (INT16)h;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT geometry_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(geometry);
|
||||
|
||||
wLog* logger = geometry->base.log;
|
||||
GeometryClientContext* context = (GeometryClientContext*)geometry->base.iface.pInterface;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
const UINT32 length = Stream_Get_UINT32(s); /* Length (4 bytes) */
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, (length - 4)))
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "invalid packet length");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 20))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
context->remoteVersion = Stream_Get_UINT32(s);
|
||||
const UINT64 id = Stream_Get_UINT64(s);
|
||||
const UINT32 updateType = Stream_Get_UINT32(s);
|
||||
Stream_Seek_UINT32(s); /* flags */
|
||||
|
||||
MAPPED_GEOMETRY* mappedGeometry = HashTable_GetItemValue(context->geometries, &id);
|
||||
|
||||
if (updateType == GEOMETRY_CLEAR)
|
||||
{
|
||||
if (!mappedGeometry)
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR,
|
||||
"geometry 0x%" PRIx64 " not found here, ignoring clear command", id);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
WLog_Print(logger, WLOG_DEBUG, "clearing geometry 0x%" PRIx64 "", id);
|
||||
|
||||
if (mappedGeometry->MappedGeometryClear &&
|
||||
!mappedGeometry->MappedGeometryClear(mappedGeometry))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!HashTable_Remove(context->geometries, &id))
|
||||
WLog_Print(logger, WLOG_ERROR, "geometry not removed from geometries");
|
||||
}
|
||||
else if (updateType == GEOMETRY_UPDATE)
|
||||
{
|
||||
BOOL newOne = FALSE;
|
||||
|
||||
if (!mappedGeometry)
|
||||
{
|
||||
newOne = TRUE;
|
||||
WLog_Print(logger, WLOG_DEBUG, "creating geometry 0x%" PRIx64 "", id);
|
||||
mappedGeometry = calloc(1, sizeof(MAPPED_GEOMETRY));
|
||||
if (!mappedGeometry)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
mappedGeometry->refCounter = 1;
|
||||
mappedGeometry->mappingId = id;
|
||||
|
||||
if (!HashTable_Insert(context->geometries, &(mappedGeometry->mappingId),
|
||||
mappedGeometry))
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR,
|
||||
"unable to register geometry 0x%" PRIx64 " in the table", id);
|
||||
free(mappedGeometry);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_Print(logger, WLOG_DEBUG, "updating geometry 0x%" PRIx64 "", id);
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, 48))
|
||||
{
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
mappedGeometry->topLevelId = Stream_Get_UINT64(s);
|
||||
|
||||
mappedGeometry->left = Stream_Get_INT32(s);
|
||||
mappedGeometry->top = Stream_Get_INT32(s);
|
||||
mappedGeometry->right = Stream_Get_INT32(s);
|
||||
mappedGeometry->bottom = Stream_Get_INT32(s);
|
||||
|
||||
mappedGeometry->topLevelLeft = Stream_Get_INT32(s);
|
||||
mappedGeometry->topLevelTop = Stream_Get_INT32(s);
|
||||
mappedGeometry->topLevelRight = Stream_Get_INT32(s);
|
||||
mappedGeometry->topLevelBottom = Stream_Get_INT32(s);
|
||||
|
||||
const UINT32 geometryType = Stream_Get_UINT32(s);
|
||||
if (geometryType != 0x02)
|
||||
WLog_Print(logger, WLOG_DEBUG, "geometryType should be set to 0x02 and is 0x%" PRIx32,
|
||||
geometryType);
|
||||
|
||||
const UINT32 cbGeometryBuffer = Stream_Get_UINT32(s);
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(logger, s, cbGeometryBuffer))
|
||||
{
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert ownership mappedGeometry
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (cbGeometryBuffer > 0)
|
||||
{
|
||||
ret = geometry_read_RGNDATA(logger, s, cbGeometryBuffer, &mappedGeometry->geometry);
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
freerdp_rgndata_reset(&mappedGeometry->geometry);
|
||||
}
|
||||
|
||||
if (newOne)
|
||||
{
|
||||
if (context->MappedGeometryAdded &&
|
||||
!context->MappedGeometryAdded(context, mappedGeometry))
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "geometry added callback failed");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mappedGeometry->MappedGeometryUpdate &&
|
||||
!mappedGeometry->MappedGeometryUpdate(mappedGeometry))
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "geometry update callback failed");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_Print(logger, WLOG_ERROR, "unknown updateType=%" PRIu32 "", updateType);
|
||||
ret = CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT geometry_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
return geometry_recv_pdu(callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT geometry_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
free(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void mappedGeometryUnref_void(void* arg)
|
||||
{
|
||||
MAPPED_GEOMETRY* g = (MAPPED_GEOMETRY*)arg;
|
||||
mappedGeometryUnref(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel Client Interface
|
||||
*/
|
||||
|
||||
static const IWTSVirtualChannelCallback geometry_callbacks = { geometry_on_data_received,
|
||||
nullptr, /* Open */
|
||||
geometry_on_close, nullptr };
|
||||
|
||||
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
|
||||
rdpSettings* settings)
|
||||
{
|
||||
GeometryClientContext* context = nullptr;
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
|
||||
|
||||
WINPR_ASSERT(base);
|
||||
WINPR_UNUSED(settings);
|
||||
|
||||
context = (GeometryClientContext*)calloc(1, sizeof(GeometryClientContext));
|
||||
if (!context)
|
||||
{
|
||||
WLog_Print(base->log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context->geometries = HashTable_New(FALSE);
|
||||
if (!context->geometries)
|
||||
{
|
||||
WLog_Print(base->log, WLOG_ERROR, "unable to allocate geometries");
|
||||
free(context);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
HashTable_SetHashFunction(context->geometries, mappedGeometryHash);
|
||||
{
|
||||
wObject* obj = HashTable_KeyObject(context->geometries);
|
||||
obj->fnObjectEquals = mappedGeometryKeyCompare;
|
||||
}
|
||||
{
|
||||
wObject* obj = HashTable_ValueObject(context->geometries);
|
||||
obj->fnObjectFree = mappedGeometryUnref_void;
|
||||
}
|
||||
context->handle = (void*)geometry;
|
||||
|
||||
geometry->context = context;
|
||||
geometry->base.iface.pInterface = (void*)context;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
{
|
||||
GEOMETRY_PLUGIN* geometry = (GEOMETRY_PLUGIN*)base;
|
||||
|
||||
if (geometry->context)
|
||||
HashTable_Free(geometry->context->geometries);
|
||||
free(geometry->context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME,
|
||||
sizeof(GEOMETRY_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&geometry_callbacks, init_plugin_cb, terminate_plugin_cb);
|
||||
}
|
||||
30
third_party/FreeRDP/channels/geometry/client/geometry_main.h
vendored
Normal file
30
third_party/FreeRDP/channels/geometry/client/geometry_main.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Geometry tracking virtual channel extension
|
||||
*
|
||||
* Copyright 2017 David Fort <contact@hardening-consulting.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_GEOMETRY_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/geometry.h>
|
||||
|
||||
#endif /* FREERDP_CHANNEL_GEOMETRY_CLIENT_MAIN_H */
|
||||
27
third_party/FreeRDP/channels/gfxredir/CMakeLists.txt
vendored
Normal file
27
third_party/FreeRDP/channels/gfxredir/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2020 Microsoft
|
||||
#
|
||||
# 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("gfxredir")
|
||||
|
||||
if(WITH_SERVER_CHANNELS OR WITH_CLIENT_CHANNELS)
|
||||
include_directories(common)
|
||||
add_subdirectory(common)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
20
third_party/FreeRDP/channels/gfxredir/ChannelOptions.cmake
vendored
Normal file
20
third_party/FreeRDP/channels/gfxredir/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"gfxredir"
|
||||
TYPE
|
||||
"dynamic"
|
||||
DESCRIPTION
|
||||
"Graphics Redirection Virtual Channel Extension"
|
||||
SPECIFICATIONS
|
||||
"[MS-RDPXXXX]"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
24
third_party/FreeRDP/channels/gfxredir/common/CMakeLists.txt
vendored
Normal file
24
third_party/FreeRDP/channels/gfxredir/common/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2024 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2024 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.
|
||||
|
||||
set(SRCS gfxredir_common.h gfxredir_common.c)
|
||||
|
||||
# Library currently header only
|
||||
add_library(gfxredir-common STATIC ${SRCS})
|
||||
|
||||
channel_install(gfxredir-common ${FREERDP_ADDIN_PATH} "FreeRDPTargets")
|
||||
58
third_party/FreeRDP/channels/gfxredir/common/gfxredir_common.c
vendored
Normal file
58
third_party/FreeRDP/channels/gfxredir/common/gfxredir_common.c
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2020 Microsoft
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("gfxredir.common")
|
||||
|
||||
#include "gfxredir_common.h"
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header)
|
||||
{
|
||||
WINPR_ASSERT(header);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, header->cmdId);
|
||||
Stream_Read_UINT32(s, header->length);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header)
|
||||
{
|
||||
WINPR_ASSERT(header);
|
||||
Stream_Write_UINT32(s, header->cmdId);
|
||||
Stream_Write_UINT32(s, header->length);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user