mirror of https://github.com/FreeRDP/FreeRDP
Merge branch 'gateway-http-bugfix'
Conflicts: client/common/cmdline.c include/freerdp/settings.h libfreerdp/common/settings.c libfreerdp/core/settings.c libfreerdp/core/tcp.c libfreerdp/core/transport.c
This commit is contained in:
commit
5d10b3bf5b
|
@ -20,6 +20,8 @@ LICENSE.txt
|
|||
*Config.cmake
|
||||
*ConfigVersion.cmake
|
||||
include/freerdp/version.h
|
||||
include/freerdp/build-config.h
|
||||
buildflags.h
|
||||
|
||||
*.a.objlist.cmake
|
||||
*.a.objlist
|
||||
|
@ -27,16 +29,6 @@ include/freerdp/version.h
|
|||
*_dummy.c
|
||||
*_dummy.c.base
|
||||
|
||||
# Packages
|
||||
*.zip
|
||||
*.exe
|
||||
*.sh
|
||||
*.deb
|
||||
*.rpm
|
||||
*.dmg
|
||||
*.tar.Z
|
||||
*.tar.gz
|
||||
|
||||
# Eclipse
|
||||
*.project
|
||||
*.cproject
|
||||
|
@ -53,8 +45,6 @@ compile_commands.json
|
|||
docs/api
|
||||
client/X11/xfreerdp.1
|
||||
client/X11/xfreerdp.1.xml
|
||||
client/X11/xfreerdp-channels.1.xml
|
||||
client/X11/xfreerdp-examples.1.xml
|
||||
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
|
@ -89,6 +79,7 @@ RelWithDebInfo
|
|||
*.resource.txt
|
||||
*.embed.manifest*
|
||||
*.intermediate.manifest*
|
||||
version.rc
|
||||
|
||||
# Binaries
|
||||
*.a
|
||||
|
@ -103,6 +94,7 @@ client/X11/xfreerdp
|
|||
client/Mac/xcode
|
||||
client/Sample/sfreerdp
|
||||
client/DirectFB/dfreerdp
|
||||
client/Wayland/wlfreerdp
|
||||
server/Sample/sfreerdp-server
|
||||
server/X11/xfreerdp-server
|
||||
xcode
|
||||
|
@ -128,3 +120,22 @@ default.log
|
|||
|
||||
# etags
|
||||
TAGS
|
||||
|
||||
# generated packages
|
||||
*.zip
|
||||
*.exe
|
||||
*.sh
|
||||
*.deb
|
||||
*.rpm
|
||||
*.dmg
|
||||
*.tar.Z
|
||||
*.tar.gz
|
||||
|
||||
# packaging related files
|
||||
!packaging/scripts/prepare_deb_freerdp-nightly.sh
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly-dev
|
||||
packaging/deb/freerdp-nightly/freerdp-nightly-dbg
|
||||
|
||||
#
|
||||
.idea
|
||||
|
|
|
@ -60,7 +60,7 @@ set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources\\\\FreeRDP_Install.bmp")
|
|||
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources\\\\FreeRDP_Icon_96px.ico")
|
||||
set(CPACK_NSIS_MUI_UNICON "${CMAKE_SOURCE_DIR}/resource\\\\FreeRDP_Icon_96px.ico")
|
||||
|
||||
set(CPACK_COMPONENTS_ALL client server libraries headers)
|
||||
set(CPACK_COMPONENTS_ALL client server libraries headers symbols tools)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC_RUNTIME STREQUAL "dynamic")
|
||||
|
@ -85,6 +85,12 @@ set(CPACK_COMPONENT_LIBRARIES_GROUP "Runtime")
|
|||
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Headers")
|
||||
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
|
||||
|
||||
set(CPACK_COMPONENT_SYMBOLS_DISPLAY_NAME "Symbols")
|
||||
set(CPACK_COMPONENT_SYMBOLS_GROUP "Development")
|
||||
|
||||
set(CPACK_COMPONENT_TOOLS_DISPLAY_NAME "Tools")
|
||||
set(CPACK_COMPONENT_TOOLS_GROUP "Applications")
|
||||
|
||||
set(CPACK_COMPONENT_GROUP_RUNTIME_DESCRIPTION "Runtime")
|
||||
set(CPACK_COMPONENT_GROUP_APPLICATIONS_DESCRIPTION "Applications")
|
||||
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION "Development")
|
||||
|
|
498
CMakeLists.txt
498
CMakeLists.txt
|
@ -26,6 +26,10 @@ if(NOT DEFINED VENDOR)
|
|||
set(VENDOR "FreeRDP" CACHE STRING "FreeRDP package vendor")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED PRODUCT)
|
||||
set(PRODUCT "FreeRDP" CACHE STRING "FreeRDP package name")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED FREERDP_VENDOR)
|
||||
set(FREERDP_VENDOR 1)
|
||||
endif()
|
||||
|
@ -37,12 +41,19 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|||
# Include our extra modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||
|
||||
if((CMAKE_SYSTEM_NAME MATCHES "WindowsStore") AND (CMAKE_SYSTEM_VERSION MATCHES "10.0"))
|
||||
set(UWP 1)
|
||||
add_definitions("-D_UWP")
|
||||
set(CMAKE_WINDOWS_VERSION "WIN10")
|
||||
endif()
|
||||
|
||||
# Check for cmake compatibility (enable/disable features)
|
||||
include(CheckCmakeCompat)
|
||||
|
||||
# Include cmake modules
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckStructHasMember)
|
||||
include(FindPkgConfig)
|
||||
include(TestBigEndian)
|
||||
|
@ -60,33 +71,50 @@ include(CMakePackageConfigHelpers)
|
|||
# Soname versioning
|
||||
set(BUILD_NUMBER 0)
|
||||
if ($ENV{BUILD_NUMBER})
|
||||
set(BUILD_NUMBER $ENV{BUILD_NUMBER})
|
||||
set(BUILD_NUMBER $ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
set(WITH_LIBRARY_VERSIONING "ON")
|
||||
set(FREERDP_VERSION_MAJOR "1")
|
||||
set(FREERDP_VERSION_MINOR "2")
|
||||
set(FREERDP_VERSION_MAJOR "2")
|
||||
set(FREERDP_VERSION_MINOR "0")
|
||||
set(FREERDP_VERSION_REVISION "0")
|
||||
set(FREERDP_VERSION_SUFFIX "dev")
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}")
|
||||
set(FREERDP_VERSION "${FREERDP_API_VERSION}.${FREERDP_VERSION_REVISION}")
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}.${FREERDP_VERSION_REVISION}")
|
||||
if (FREERDP_VERSION_SUFFIX)
|
||||
set(FREERDP_VERSION_FULL "${FREERDP_VERSION}-${FREERDP_VERSION_SUFFIX}")
|
||||
else()
|
||||
set(FREERDP_VERSION_FULL "${FREERDP_VERSION}")
|
||||
endif()
|
||||
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
||||
|
||||
# Compatibility options
|
||||
if(DEFINED STATIC_CHANNELS)
|
||||
message(WARNING "STATIC_CHANNELS is obsolete, please use BUILTIN_CHANNELS instead")
|
||||
set(BUILTIN_CHANNELS ${STATIC_CHANNELS} CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
# Make paths absolute
|
||||
if (CMAKE_INSTALL_PREFIX)
|
||||
get_filename_component(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" ABSOLUTE)
|
||||
endif()
|
||||
if (FREERDP_EXTERNAL_PATH)
|
||||
get_filename_component (FREERDP_EXTERNAL_PATH "${FREERDP_EXTERNAL_PATH}" ABSOLUTE)
|
||||
endif()
|
||||
|
||||
# Allow to search the host machine for git
|
||||
if(ANDROID OR IOS)
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
endif(ANDROID OR IOS)
|
||||
endif(CMAKE_CROSSCOMPILING)
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||
|
||||
if (${GIT_REVISION} STREQUAL "n/a")
|
||||
git_rev_parse(GIT_REVISION --short)
|
||||
git_rev_parse (GIT_REVISION --short)
|
||||
endif()
|
||||
if(ANDROID OR IOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
endif(ANDROID OR IOS)
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
endif(CMAKE_CROSSCOMPILING)
|
||||
|
||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||
|
||||
|
@ -99,15 +127,33 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||
endif()
|
||||
|
||||
if(NOT DEFINED BUILD_SHARED_LIBS)
|
||||
if(ANDROID OR IOS OR APPLE)
|
||||
if(IOS OR APPLE)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED EXPORT_ALL_SYMBOLS)
|
||||
if(BUILD_TESTING)
|
||||
set(EXPORT_ALL_SYMBOLS TRUE)
|
||||
elseif(NOT DEFINED EXPORT_ALL_SYMBOLS)
|
||||
set(EXPORT_ALL_SYMBOLS FALSE)
|
||||
endif()
|
||||
|
||||
if (EXPORT_ALL_SYMBOLS)
|
||||
# set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
add_definitions(-DFREERDP_TEST_EXPORTS -DBUILD_TESTING)
|
||||
endif(EXPORT_ALL_SYMBOLS)
|
||||
|
||||
# BSD
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
set(BSD TRUE)
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
set(FREEBSD TRUE)
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
set(OPENBSD TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Configure MSVC Runtime
|
||||
|
@ -116,7 +162,10 @@ if(MSVC)
|
|||
if(NOT DEFINED MSVC_RUNTIME)
|
||||
set(MSVC_RUNTIME "dynamic")
|
||||
endif()
|
||||
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||
if(MSVC_RUNTIME STREQUAL "static")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
message(FATAL_ERROR "Static CRT is only supported in a fully static build")
|
||||
endif()
|
||||
message(STATUS "Use the MSVC static runtime option carefully!")
|
||||
message(STATUS "OpenSSL uses /MD by default, and is very picky")
|
||||
message(STATUS "Random freeing errors are a common sign of runtime issues")
|
||||
|
@ -131,7 +180,8 @@ endif()
|
|||
# Compiler-specific flags
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "i686")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
CHECK_SYMBOL_EXISTS(__x86_64__ "" IS_X86_64)
|
||||
if(IS_X86_64)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686")
|
||||
|
@ -173,23 +223,19 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
|||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format")
|
||||
endif()
|
||||
endif()
|
||||
CHECK_C_COMPILER_FLAG (-Wimplicit-function-declaration Wimplicit-function-declaration)
|
||||
if(Wimplicit-function-declaration)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wimplicit-function-declaration")
|
||||
endif()
|
||||
|
||||
if (NOT OPENBSD)
|
||||
CHECK_C_COMPILER_FLAG (-Wredundant-decls Wredundant-decls)
|
||||
if(Wredundant-decls)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls")
|
||||
endif()
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
|
||||
|
||||
CHECK_C_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefined)
|
||||
if(Wno-builtin-macro-redefined)
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-builtin-macro-redefined")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D__FILE__='\"$(subst ${CMAKE_BINARY_DIR}/,,$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<)))\"'")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefinedCXX)
|
||||
if(Wno-builtin-macro-redefinedCXX)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-builtin-macro-redefined")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D__FILE__='\"$(subst ${CMAKE_BINARY_DIR}/,,$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<)))\"'")
|
||||
endif()
|
||||
add_definitions(-DNDEBUG)
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
|
@ -199,7 +245,24 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||
# When building with Unix Makefiles and doing any release builds
|
||||
# try to set __FILE__ to relative paths via a make specific macro
|
||||
if (CMAKE_GENERATOR MATCHES "Unix Makefile*")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
|
||||
CHECK_C_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefined)
|
||||
if(Wno-builtin-macro-redefined)
|
||||
set(CMAKE_C_FLAGS_${UPPER_BUILD_TYPE} "${CMAKE_C_FLAGS_${UPPER_BUILD_TYPE}} -Wno-builtin-macro-redefined -D__FILE__='\"$(subst ${CMAKE_BINARY_DIR}/,,$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<)))\"'")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefinedCXX)
|
||||
if(Wno-builtin-macro-redefinedCXX)
|
||||
set(CMAKE_CXX_FLAGS_${UPPER_BUILD_TYPE} "${CMAKE_CXX_FLAGS_${UPPER_BUILD_TYPE}} -Wno-builtin-macro-redefined -D__FILE__='\"$(subst ${CMAKE_BINARY_DIR}/,,$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<)))\"'")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-macros -Wno-padded")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-c11-extensions -Wno-gnu")
|
||||
|
@ -216,55 +279,139 @@ if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Enable address sanitizer, where supported and when required
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCC)
|
||||
if(WITH_SANITIZE_ADDRESS)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=address" fsanitize-address)
|
||||
if(fsanitize-address)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
elseif(WITH_SANITIZE_LEAK)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=leak")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=leak" fsanitize-leak)
|
||||
if(fsanitize-leak)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=leak")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_NO_UNDEFINED)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-Wl,--no-undefined")
|
||||
CHECK_C_COMPILER_FLAG (-Wl,--no-undefined no-undefined)
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
unset(SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
if(no-undefined)
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Remove previous warning definitions,
|
||||
# NMake is otherwise complaining.
|
||||
foreach (flags_var_to_scrub
|
||||
CMAKE_C_FLAGS
|
||||
CMAKE_CXX_FLAGS
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_C_FLAGS_MINSIZEREL)
|
||||
string (REGEX REPLACE "(^| )[/-]W[ ]*[1-9]" " "
|
||||
"${flags_var_to_scrub}" "${${flags_var_to_scrub}}")
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gd")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_AMD64_")
|
||||
add_definitions(-D_AMD64_)
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_X86_")
|
||||
add_definitions(-D_X86_)
|
||||
endif()
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUNICODE -D_UNICODE")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32_LEAN_AND_MEAN")
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
# Set product and vendor for dll and exe version information.
|
||||
set(RC_VERSION_VENDOR "FreeRDP")
|
||||
set(RC_VERSION_PRODUCT "FreeRDP")
|
||||
set(RC_VERSION_PATCH ${BUILD_NUMBER})
|
||||
set(RC_VERSION_DESCRIPTION ${GIT_REVISION})
|
||||
set(CMAKE_USE_RELATIVE_PATH ON)
|
||||
if (${CMAKE_GENERATOR} MATCHES "NMake Makefile*")
|
||||
set(CMAKE_PDB_BINARY_DIR ${CMAKE_BINARY_DIR})
|
||||
elseif (${CMAKE_GENERATOR} MATCHES "Visual Studio*")
|
||||
set(CMAKE_PDB_BINARY_DIR "${CMAKE_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}")
|
||||
else()
|
||||
message(FATAL "Unknown generator ${CMAKE_GENERATOR}")
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP RC_VERSION_YEAR "%Y")
|
||||
# Set product and vendor for dll and exe version information.
|
||||
set(RC_VERSION_VENDOR ${VENDOR})
|
||||
set(RC_VERSION_PRODUCT ${PRODUCT})
|
||||
set(RC_VERSION_PATCH ${BUILD_NUMBER})
|
||||
set(RC_VERSION_DESCRIPTION ${GIT_REVISION})
|
||||
|
||||
string(TIMESTAMP RC_VERSION_YEAR "%Y")
|
||||
|
||||
if(NOT DEFINED CMAKE_WINDOWS_VERSION)
|
||||
set(CMAKE_WINDOWS_VERSION "WINXP")
|
||||
endif()
|
||||
|
||||
if(CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINVER=0x0501 -DWIN32_WINNT=0x0501")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINVER=0x0501 -DWIN32_WINNT=0x0501")
|
||||
add_definitions(-DWINVER=0x0501 -D_WIN32_WINNT=0x0501)
|
||||
elseif(CMAKE_WINDOWS_VERSION STREQUAL "WIN7")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINVER=0x0601 -DWIN32_WINNT=0x0601")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINVER=0x0601 -DWIN32_WINNT=0x0601")
|
||||
add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601)
|
||||
elseif(CMAKE_WINDOWS_VERSION STREQUAL "WIN8")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINVER=0x0602 -DWIN32_WINNT=0x0602")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINVER=0x0602 -DWIN32_WINNT=0x0602")
|
||||
add_definitions(-DWINVER=0x0602 -D_WIN32_WINNT=0x0602)
|
||||
elseif(CMAKE_WINDOWS_VERSION STREQUAL "WIN10")
|
||||
add_definitions(-DWINVER=0x0A00 -D_WIN32_WINNT=0x0A00)
|
||||
endif()
|
||||
|
||||
|
||||
if (FREERDP_EXTERNAL_SSL_PATH)
|
||||
set(OPENSSL_ROOT_DIR ${FREERDP_EXTERNAL_SSL_PATH})
|
||||
endif()
|
||||
|
@ -274,24 +421,23 @@ if(IOS)
|
|||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot ${CMAKE_IOS_SDK_ROOT} -g")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_EXPORTS -DFREERDP_EXPORTS")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINPR_EXPORTS -DFREERDP_EXPORTS")
|
||||
add_definitions(-DWINPR_EXPORTS -DFREERDP_EXPORTS)
|
||||
|
||||
# Include files
|
||||
if(NOT IOS)
|
||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(execinfo.h HAVE_EXECINFO_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||
check_include_files(sys/sockio.h HAVE_SYS_SOCKIO_H)
|
||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||
else()
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_UNISTD_H 1)
|
||||
set(HAVE_STDINT_H 1)
|
||||
set(HAVE_INTTYPES_H 1)
|
||||
set(HAVE_SYS_FILIO_H 1)
|
||||
endif()
|
||||
|
@ -323,9 +469,9 @@ if(APPLE)
|
|||
|
||||
# Temporarily disabled, causes the cmake script to be reexecuted, causing the compilation to fail.
|
||||
# Workaround: specify the parameter in the command-line
|
||||
# if(WITH_CLANG)
|
||||
# set(CMAKE_C_COMPILER "clang")
|
||||
# endif()
|
||||
# if(WITH_CLANG)
|
||||
# set(CMAKE_C_COMPILER "clang")
|
||||
# endif()
|
||||
|
||||
if (WITH_VERBOSE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -v")
|
||||
|
@ -333,9 +479,24 @@ if(APPLE)
|
|||
endif()
|
||||
endif(APPLE)
|
||||
|
||||
# OpenBSD
|
||||
if(OPENBSD)
|
||||
set(WITH_MANPAGES "ON")
|
||||
set(WITH_ALSA "OFF")
|
||||
set(WITH_PULSE "OFF")
|
||||
set(WITH_OSS "ON")
|
||||
set(WITH_WAYLAND "OFF")
|
||||
endif()
|
||||
|
||||
# Android
|
||||
if(ANDROID)
|
||||
set(WITH_LIBRARY_VERSIONING "OFF")
|
||||
set(WITH_LIBRARY_VERSIONING "OFF")
|
||||
|
||||
set_property( GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ${ANDROID_LIBRARY_USE_LIB64_PATHS} )
|
||||
|
||||
if (${ANDROID_ABI} STREQUAL "armeabi")
|
||||
set (WITH_NEON OFF)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
add_definitions(-DNDK_DEBUG=1)
|
||||
|
@ -347,65 +508,62 @@ if(ANDROID)
|
|||
endif()
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -llog")
|
||||
|
||||
if (NOT FREERDP_EXTERNAL_JPEG_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg8d")
|
||||
set(FREERDP_EXTERNAL_JPEG_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg8d")
|
||||
if (NOT FREERDP_EXTERNAL_PATH)
|
||||
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/")
|
||||
set (FREERDP_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_SSL_PATH not set! - Needs to be set if openssl is not found in the android NDK (which usually isn't)")
|
||||
endif()
|
||||
endif()
|
||||
if (NOT FREERDP_EXTERNAL_SSL_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl")
|
||||
set(FREERDP_EXTERNAL_SSL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_SSL_PATH not set! - Needs to be set if openssl is not found in the android NDK (which usually isn't)")
|
||||
endif()
|
||||
if(WITH_GPROF)
|
||||
if (NOT FREERDP_EXTERNAL_PROFILER_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler")
|
||||
set(FREERDP_EXTERNAL_PROFILER_PATH
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_PROFILER_PATH not set!")
|
||||
endif()
|
||||
message(STATUS "FREERDP_EXTERNAL_PATH not set!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_EXTERNAL_SSL_PATH} ${FREERDP_EXTERNAL_JPEG_PATH})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_EXTERNAL_PROFILER_PATH})
|
||||
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/jni/${ANDROID_ABI})
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake
|
||||
${CMAKE_BINARY_DIR}/scripts/regenerate_jni_headers.sh @ONLY)
|
||||
list (APPEND CMAKE_INCLUDE_PATH ${FREERDP_EXTERNAL_PATH}/include)
|
||||
list (APPEND CMAKE_LIBRARY_PATH ${FREERDP_EXTERNAL_PATH}/${ANDROID_ABI}/ )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
|
||||
|
||||
if (WITH_GPROF)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/gprof_generate.sh.cmake ${CMAKE_BINARY_DIR}/scripts/gprof_generate.sh @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/gprof_generate.sh.cmake
|
||||
${CMAKE_BINARY_DIR}/scripts/gprof_generate.sh @ONLY)
|
||||
endif(WITH_GPROF)
|
||||
|
||||
endif()
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
|
||||
if(NOT IOS AND NOT ANDROID)
|
||||
if(NOT IOS)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
|
||||
check_library_exists(pthread pthread_tryjoin_np "" HAVE_PTHREAD_GNU_EXT)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
|
||||
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
CHECK_LIBRARY_EXISTS(pthreads pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||
|
||||
if (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
set(HAVE_PTHREAD_MUTEX_TIMEDLOCK ON)
|
||||
endif (HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS)
|
||||
endif()
|
||||
|
||||
if(WITH_VALGRIND_MEMCHECK)
|
||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
else()
|
||||
unset(HAVE_VALGRIND_MEMCHECK_H CACHE)
|
||||
unset(HAVE_VALGRIND_MEMCHECK_H CACHE)
|
||||
endif()
|
||||
|
||||
if(UNIX OR CYGWIN)
|
||||
check_include_files(sys/eventfd.h HAVE_AIO_H)
|
||||
check_include_files(sys/eventfd.h HAVE_EVENTFD_H)
|
||||
if (HAVE_EVENTFD_H)
|
||||
check_symbol_exists(eventfd_read sys/eventfd.h WITH_EVENTFD_READ_WRITE)
|
||||
endif()
|
||||
check_include_files(sys/timerfd.h HAVE_TIMERFD_H)
|
||||
check_include_files(poll.h HAVE_POLL_H)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
|
||||
check_symbol_exists(ceill math.h HAVE_MATH_C99_LONG_DOUBLE)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES m)
|
||||
set(X11_FEATURE_TYPE "RECOMMENDED")
|
||||
set(WAYLAND_FEATURE_TYPE "RECOMMENDED")
|
||||
else()
|
||||
|
@ -435,10 +593,18 @@ set(OPENSSL_FEATURE_TYPE "REQUIRED")
|
|||
set(OPENSSL_FEATURE_PURPOSE "cryptography")
|
||||
set(OPENSSL_FEATURE_DESCRIPTION "encryption, certificate validation, hashing functions")
|
||||
|
||||
set(MBEDTLS_FEATURE_TYPE "OPTIONAL")
|
||||
set(MBEDTLS_FEATURE_PURPOSE "cryptography")
|
||||
set(MBEDTLS_FEATURE_DESCRIPTION "encryption, certificate validation, hashing functions")
|
||||
|
||||
set(OPENSLES_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENSLES_FEATURE_PURPOSE "multimedia")
|
||||
set(OPENSLES_FEATURE_DESCRIPTION "OpenSLES audio / video")
|
||||
|
||||
set(OSS_FEATURE_TYPE "RECOMMENDED")
|
||||
set(OSS_FEATURE_PURPOSE "sound")
|
||||
set(OSS_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
||||
|
||||
set(ALSA_FEATURE_TYPE "RECOMMENDED")
|
||||
set(ALSA_FEATURE_PURPOSE "sound")
|
||||
set(ALSA_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection")
|
||||
|
@ -475,6 +641,10 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
|
|||
set(JPEG_FEATURE_PURPOSE "codec")
|
||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
||||
|
||||
set(X264_FEATURE_TYPE "OPTIONAL")
|
||||
set(X264_FEATURE_PURPOSE "codec")
|
||||
set(X264_FEATURE_DESCRIPTION "use x264 library")
|
||||
|
||||
set(OPENH264_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENH264_FEATURE_PURPOSE "codec")
|
||||
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
||||
|
@ -488,6 +658,7 @@ if(WIN32)
|
|||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
set(ZLIB_FEATURE_TYPE "DISABLED")
|
||||
set(DIRECTFB_FEATURE_TYPE "DISABLED")
|
||||
set(OSS_FEATURE_TYPE "DISABLED")
|
||||
set(ALSA_FEATURE_TYPE "DISABLED")
|
||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||
|
@ -504,9 +675,10 @@ if(APPLE)
|
|||
set(GSTREAMER_1_0_FEATURE_TYPE "OPTIONAL")
|
||||
set(X11_FEATURE_TYPE "OPTIONAL")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
set(OSS_FEATURE_TYPE "DISABLED")
|
||||
set(ALSA_FEATURE_TYPE "DISABLED")
|
||||
if(IOS)
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
set(ALSA_FEATURE_TYPE "DISABLED")
|
||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
|
@ -516,10 +688,26 @@ if(APPLE)
|
|||
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT ANDROID)
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_TYPE "RECOMMENDED")
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_PURPOSE "systemd journal appender")
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_DESCRIPTION "allows to export wLog to systemd journal")
|
||||
|
||||
#include(Findlibsystemd)
|
||||
find_feature(libsystemd ${WLOG_SYSTEMD_JOURNAL_FEATURE_TYPE} ${WLOG_SYSTEMD_JOURNAL_FEATURE_PURPOSE} ${WLOG_SYSTEMD_JOURNAL_FEATURE_DESCRIPTION})
|
||||
|
||||
if(LIBSYSTEMD_FOUND)
|
||||
set(HAVE_JOURNALD_H TRUE)
|
||||
else()
|
||||
unset(HAVE_JOURNALD_H)
|
||||
endif()
|
||||
endif(UNIX AND NOT ANDROID)
|
||||
|
||||
if(ANDROID)
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
set(DIRECTFB_FEATURE_TYPE "DISABLED")
|
||||
set(OSS_FEATURE_TYPE "DISABLED")
|
||||
set(ALSA_FEATURE_TYPE "DISABLED")
|
||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||
|
@ -530,7 +718,6 @@ if(ANDROID)
|
|||
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
||||
endif()
|
||||
|
||||
|
||||
find_feature(X11 ${X11_FEATURE_TYPE} ${X11_FEATURE_PURPOSE} ${X11_FEATURE_DESCRIPTION})
|
||||
find_feature(Wayland ${WAYLAND_FEATURE_TYPE} ${WAYLAND_FEATURE_PURPOSE} ${WAYLAND_FEATURE_DESCRIPTION})
|
||||
find_feature(DirectFB ${DIRECTFB_FEATURE_TYPE} ${DIRECTFB_FEATURE_PURPOSE} ${DIRECTFB_FEATURE_DESCRIPTION})
|
||||
|
@ -540,8 +727,10 @@ endif()
|
|||
|
||||
find_feature(ZLIB ${ZLIB_FEATURE_TYPE} ${ZLIB_FEATURE_PURPOSE} ${ZLIB_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenSSL ${OPENSSL_FEATURE_TYPE} ${OPENSSL_FEATURE_PURPOSE} ${OPENSSL_FEATURE_DESCRIPTION})
|
||||
find_feature(MbedTLS ${MBEDTLS_FEATURE_TYPE} ${MBEDTLS_FEATURE_PURPOSE} ${MBEDTLS_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenSLES ${OPENSLES_FEATURE_TYPE} ${OPENSLES_FEATURE_PURPOSE} ${OPENSLES_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(OSS ${OSS_FEATURE_TYPE} ${OSS_FEATURE_PURPOSE} ${OSS_FEATURE_DESCRIPTION})
|
||||
find_feature(ALSA ${ALSA_FEATURE_TYPE} ${ALSA_FEATURE_PURPOSE} ${ALSA_FEATURE_DESCRIPTION})
|
||||
find_feature(Pulse ${PULSE_FEATURE_TYPE} ${PULSE_FEATURE_PURPOSE} ${PULSE_FEATURE_DESCRIPTION})
|
||||
|
||||
|
@ -554,6 +743,7 @@ find_feature(GStreamer_0_10 ${GSTREAMER_0_10_FEATURE_TYPE} ${GSTREAMER_0_10_FEAT
|
|||
find_feature(GStreamer_1_0 ${GSTREAMER_1_0_FEATURE_TYPE} ${GSTREAMER_1_0_FEATURE_PURPOSE} ${GSTREAMER_1_0_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION})
|
||||
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||
|
||||
|
@ -564,18 +754,30 @@ if(TARGET_ARCH MATCHES "x86|x64")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(OPENSSL_FOUND)
|
||||
add_definitions("-DWITH_OPENSSL")
|
||||
endif()
|
||||
|
||||
if(MBEDTLS_FOUND)
|
||||
add_definitions("-DWITH_MBEDTLS")
|
||||
endif()
|
||||
|
||||
if (TARGET_ARCH MATCHES "sparc")
|
||||
set(HAVE_ALIGNED_REQUIRED 1)
|
||||
endif()
|
||||
|
||||
# Path to put FreeRDP data
|
||||
set(FREERDP_DATA_PATH "${CMAKE_INSTALL_PREFIX}/share/freerdp")
|
||||
set(FREERDP_DATA_PATH "${CMAKE_INSTALL_PREFIX}/share/freerdp${FREERDP_VERSION_MAJOR}")
|
||||
|
||||
# Path to put plugins
|
||||
|
||||
set(FREERDP_LIBRARY_PATH "${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/freerdp")
|
||||
set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
|
||||
|
||||
# Path to put extensions
|
||||
set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp/extensions")
|
||||
set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}/extensions")
|
||||
|
||||
# Include directories
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -588,8 +790,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DI
|
|||
|
||||
# RPATH configuration
|
||||
if(CMAKE_SKIP_RPATH)
|
||||
set(CMAKE_SKIP_RPATH FALSE)
|
||||
set(CMAKE_SKIP_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_SKIP_RPATH FALSE)
|
||||
set(CMAKE_SKIP_INSTALL_RPATH TRUE)
|
||||
endif()
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
|
@ -610,11 +812,9 @@ endif()
|
|||
|
||||
include(CTest)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFREERDP_TEST_EXPORTS")
|
||||
|
||||
if(BUILD_TESTING)
|
||||
enable_testing()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
set(TESTING_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
else()
|
||||
|
@ -626,6 +826,14 @@ endif()
|
|||
include_directories("${CMAKE_SOURCE_DIR}/winpr/include")
|
||||
include_directories("${CMAKE_BINARY_DIR}/winpr/include")
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
set(PUBLIC_KEYWORD "")
|
||||
set(PRIVATE_KEYWORD "")
|
||||
else()
|
||||
set(PUBLIC_KEYWORD "PUBLIC")
|
||||
set(PRIVATE_KEYWORD "PRIVATE")
|
||||
endif()
|
||||
|
||||
add_subdirectory(winpr)
|
||||
|
||||
# Sub-directories
|
||||
|
@ -641,10 +849,6 @@ add_subdirectory(include)
|
|||
|
||||
add_subdirectory(libfreerdp)
|
||||
|
||||
if(WITH_CHANNELS)
|
||||
add_subdirectory(channels)
|
||||
endif()
|
||||
|
||||
if (IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
|
||||
if (IOS_PLATFORM MATCHES "SIMULATOR")
|
||||
|
@ -660,41 +864,34 @@ include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
|||
|
||||
add_subdirectory(rdtk)
|
||||
|
||||
if(WITH_CLIENT)
|
||||
add_subdirectory(client)
|
||||
if(WAYLAND_FOUND)
|
||||
add_subdirectory(uwac)
|
||||
endif()
|
||||
|
||||
if(BSD)
|
||||
if(IS_DIRECTORY /usr/local/include)
|
||||
include_directories(/usr/local/include)
|
||||
link_directories(/usr/local/lib)
|
||||
endif()
|
||||
if(OPENBSD)
|
||||
if(IS_DIRECTORY /usr/X11R6/include)
|
||||
include_directories(/usr/X11R6/include)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_CHANNELS)
|
||||
add_subdirectory(channels)
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT_COMMON OR WITH_CLIENT)
|
||||
add_subdirectory(client)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER)
|
||||
add_subdirectory(server)
|
||||
endif()
|
||||
|
||||
# Exporting
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER "2.8.10")
|
||||
|
||||
export(PACKAGE freerdp)
|
||||
|
||||
set(FREERDP_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/FreeRDP")
|
||||
|
||||
set(FREERDP_INCLUDE_DIR "include")
|
||||
|
||||
# keep for legacy builds
|
||||
set(FREERDP_MONOLITHIC_BUILD OFF)
|
||||
|
||||
configure_package_config_file(FreeRDPConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_CMAKE_INSTALL_DIR}
|
||||
PATH_VARS FREERDP_INCLUDE_DIR FREERDP_MONOLITHIC_BUILD)
|
||||
|
||||
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfigVersion.cmake
|
||||
VERSION ${FREERDP_VERSION} COMPATIBILITY SameMajorVersion)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfigVersion.cmake
|
||||
DESTINATION ${FREERDP_CMAKE_INSTALL_DIR})
|
||||
|
||||
install(EXPORT FreeRDPTargets DESTINATION ${FREERDP_CMAKE_INSTALL_DIR})
|
||||
|
||||
endif()
|
||||
|
||||
# Packaging
|
||||
|
||||
set(CMAKE_CPACK_INCLUDE_FILE "CMakeCPack.cmake")
|
||||
|
@ -711,13 +908,12 @@ endif()
|
|||
|
||||
include(${CMAKE_CPACK_INCLUDE_FILE})
|
||||
|
||||
set(FREERDP_PC_LIBS "-lfreerdp -lfreerdp-client")
|
||||
set(WINPR_PC_LIBS "-lwinpr")
|
||||
if (WITH_SERVER)
|
||||
set(FREERDP_PC_LIBS "${FREERDP_PC_LIBS} -lfreerdp-server")
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
set(FREERDP_BUILD_CONFIG_LIST "")
|
||||
GET_CMAKE_PROPERTY(res VARIABLES)
|
||||
FOREACH(var ${res})
|
||||
IF (var MATCHES "^WITH_*|^BUILD_TESTING|^BUILTIN_CHANNELS|^HAVE_*")
|
||||
LIST(APPEND FREERDP_BUILD_CONFIG_LIST "${var}=${${var}}")
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
string(REPLACE ";" " " FREERDP_BUILD_CONFIG "${FREERDP_BUILD_CONFIG_LIST}")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/buildflags.h.in ${CMAKE_CURRENT_BINARY_DIR}/buildflags.h)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef _FREERDP_BUILD_FLAGS_H
|
||||
#define _FREERDP_BUILD_FLAGS_H
|
||||
|
||||
#define CFLAGS "${CMAKE_C_FLAGS}"
|
||||
#define COMPILER_ID "${CMAKE_C_COMPILER_ID}"
|
||||
#define COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}"
|
||||
#define TARGET_ARCH "${TARGET_ARCH}"
|
||||
#define BUILD_CONFIG "${FREERDP_BUILD_CONFIG}"
|
||||
#define BUILD_TYPE "${CMAKE_BUILD_TYPE}"
|
||||
|
||||
#endif /*_FREERDP_BUILD_FLAGS_H */
|
|
@ -154,12 +154,20 @@ macro(add_channel_client_subsystem _channel_prefix _channel_name _subsystem _typ
|
|||
endif()
|
||||
endmacro(add_channel_client_subsystem)
|
||||
|
||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
macro(channel_install _targets _destination _export_target)
|
||||
install(TARGETS ${_targets} DESTINATION ${_destination} EXPORT ${_export_target})
|
||||
endmacro(channel_install)
|
||||
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
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)
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
|
@ -178,20 +186,21 @@ macro(add_channel_client_library _module_prefix _module_name _channel_name _dyna
|
|||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
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} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_library)
|
||||
|
||||
macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_name _type _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
|
@ -210,19 +219,20 @@ macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_
|
|||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
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} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_subsystem_library)
|
||||
|
||||
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
if(${_dynamic} AND (NOT BUILTIN_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
if (WIN32)
|
||||
|
@ -240,12 +250,16 @@ macro(add_channel_server_library _module_prefix _module_name _channel_name _dyna
|
|||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
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} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12 OR NOT BUILD_SHARED_LIBS)
|
||||
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_server_library)
|
||||
|
||||
|
|
|
@ -25,14 +25,18 @@ include_directories(..)
|
|||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp winpr)
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
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()
|
||||
|
@ -48,3 +52,7 @@ 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()
|
||||
|
|
|
@ -27,9 +27,6 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
|||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${ALSA_LIBRARIES})
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr ${ALSA_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -63,9 +65,12 @@ typedef struct _AudinALSADevice
|
|||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinALSADevice;
|
||||
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa,
|
||||
snd_pcm_t* capture_handle)
|
||||
{
|
||||
int error;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
|
@ -73,64 +78,82 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_hand
|
|||
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_hw_params_malloc (%s)",
|
||||
snd_strerror(error));
|
||||
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_access(capture_handle, hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(capture_handle, hw_params, alsa->format);
|
||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate, NULL);
|
||||
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &alsa->actual_channels);
|
||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate,
|
||||
NULL);
|
||||
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
|
||||
&alsa->actual_channels);
|
||||
snd_pcm_hw_params(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_prepare(capture_handle);
|
||||
|
||||
if ((alsa->actual_rate != alsa->target_rate) ||
|
||||
(alsa->actual_channels != alsa->target_channels))
|
||||
(alsa->actual_channels != alsa->target_channels))
|
||||
{
|
||||
DEBUG_DVC("actual rate %d / channel %d is "
|
||||
"different from target rate %d / channel %d, resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->target_rate, alsa->target_channels);
|
||||
"different from target rate %d / channel %d, resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->target_rate, alsa->target_channels);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src,
|
||||
int size)
|
||||
{
|
||||
int frames;
|
||||
int cframes;
|
||||
int ret = 0;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
int encoded_size;
|
||||
BYTE* encoded_data;
|
||||
int rbytes_per_frame;
|
||||
int tbytes_per_frame;
|
||||
|
||||
int status;
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||
|
||||
if ((alsa->target_rate == alsa->actual_rate) &&
|
||||
(alsa->target_channels == alsa->actual_channels))
|
||||
(alsa->target_channels == alsa->actual_channels))
|
||||
{
|
||||
frames = size / rbytes_per_frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
alsa->dsp_context->resample(alsa->dsp_context, src, alsa->bytes_per_channel,
|
||||
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
|
||||
alsa->target_channels, alsa->target_rate);
|
||||
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
|
||||
alsa->target_channels, alsa->target_rate);
|
||||
frames = alsa->dsp_context->resampled_frames;
|
||||
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
|
||||
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
|
||||
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
|
||||
size = frames * tbytes_per_frame;
|
||||
src = alsa->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
while (frames > 0)
|
||||
{
|
||||
if (WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0)
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
ret = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
cframes = alsa->frames_per_packet - alsa->buffer_frames;
|
||||
|
@ -138,23 +161,26 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
|
|||
if (cframes > frames)
|
||||
cframes = frames;
|
||||
|
||||
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src, cframes * tbytes_per_frame);
|
||||
|
||||
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src,
|
||||
cframes * tbytes_per_frame);
|
||||
alsa->buffer_frames += cframes;
|
||||
|
||||
if (alsa->buffer_frames >= alsa->frames_per_packet)
|
||||
{
|
||||
if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
|
||||
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
|
||||
alsa->target_channels, alsa->block_size);
|
||||
if (!alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
|
||||
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
|
||||
alsa->target_channels, alsa->block_size))
|
||||
{
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = alsa->dsp_context->adpcm_buffer;
|
||||
encoded_size = alsa->dsp_context->adpcm_size;
|
||||
|
||||
DEBUG_DVC("encoded %d to %d",
|
||||
alsa->buffer_frames * tbytes_per_frame, encoded_size);
|
||||
alsa->buffer_frames * tbytes_per_frame, encoded_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -162,19 +188,28 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
|
|||
encoded_size = alsa->buffer_frames * tbytes_per_frame;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0)
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
ret = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
DEBUG_DVC("encoded %d [%d] to %d [%X]", alsa->buffer_frames,
|
||||
tbytes_per_frame, encoded_size,
|
||||
alsa->wformat);
|
||||
tbytes_per_frame, encoded_size,
|
||||
alsa->wformat);
|
||||
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
|
||||
}
|
||||
|
||||
alsa->buffer_frames = 0;
|
||||
|
||||
if (!ret)
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -182,114 +217,154 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
|
|||
frames -= cframes;
|
||||
}
|
||||
|
||||
return (ret) ? TRUE : FALSE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* audin_alsa_thread_func(void* arg)
|
||||
{
|
||||
int error;
|
||||
long error;
|
||||
BYTE* buffer;
|
||||
int rbytes_per_frame;
|
||||
int tbytes_per_frame;
|
||||
snd_pcm_t* capture_handle = NULL;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) arg;
|
||||
|
||||
DWORD status;
|
||||
DEBUG_DVC("in");
|
||||
|
||||
freerdp_channel_init_thread_context(alsa->rdpcontext);
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||
buffer = (BYTE*) malloc(rbytes_per_frame * alsa->frames_per_packet);
|
||||
ZeroMemory(buffer, rbytes_per_frame * alsa->frames_per_packet);
|
||||
buffer = (BYTE*) calloc(1, rbytes_per_frame * alsa->frames_per_packet);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
if (alsa->rdpcontext)
|
||||
setChannelError(alsa->rdpcontext, error, "calloc failed!");
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
|
||||
|
||||
do
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name,
|
||||
SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
{
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
WLog_ERR(TAG, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!audin_alsa_set_params(alsa, capture_handle))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_alsa_set_params failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!audin_alsa_set_params(alsa, capture_handle))
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(capture_handle, error, 0);
|
||||
continue;
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_readi (%s)", snd_strerror(error));
|
||||
break;
|
||||
}
|
||||
|
||||
while (!(WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
if ((error = audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame)))
|
||||
{
|
||||
error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(capture_handle, error, 0);
|
||||
continue;
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_readi (%s)", snd_strerror(error));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame))
|
||||
break;
|
||||
WLog_ERR(TAG, "audin_alsa_thread_receive failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (0);
|
||||
|
||||
free(buffer);
|
||||
|
||||
if (capture_handle)
|
||||
snd_pcm_close(capture_handle);
|
||||
|
||||
out:
|
||||
DEBUG_DVC("out");
|
||||
ExitThread(0);
|
||||
|
||||
if (error && alsa->rdpcontext)
|
||||
setChannelError(alsa->rdpcontext, error,
|
||||
"audin_alsa_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void audin_alsa_free(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_free(IAudinDevice* device)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
|
||||
free(alsa->device_name);
|
||||
|
||||
free(alsa);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
audinFormat* format)
|
||||
{
|
||||
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))
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if ((format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_set_format(IAudinDevice* device, audinFormat* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
alsa->target_rate = format->nSamplesPerSec;
|
||||
alsa->actual_rate = format->nSamplesPerSec;
|
||||
alsa->target_channels = format->nChannels;
|
||||
|
@ -304,11 +379,13 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
|
|||
alsa->format = SND_PCM_FORMAT_S8;
|
||||
alsa->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
|
@ -316,81 +393,118 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
|
|||
alsa->bytes_per_channel = 2;
|
||||
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
|
||||
alsa->frames_per_packet = (alsa->frames_per_packet * format->nChannels * 2 /
|
||||
bs + 1) * bs / (format->nChannels * 2);
|
||||
bs + 1) * bs / (format->nChannels * 2);
|
||||
DEBUG_DVC("aligned FramesPerPacket=%d",
|
||||
alsa->frames_per_packet);
|
||||
alsa->frames_per_packet);
|
||||
break;
|
||||
}
|
||||
|
||||
alsa->wformat = format->wFormatTag;
|
||||
alsa->block_size = format->nBlockAlign;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
{
|
||||
int rbytes_per_frame;
|
||||
int tbytes_per_frame;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
alsa->receive = receive;
|
||||
alsa->user_data = user_data;
|
||||
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||
alsa->buffer = (BYTE*) malloc(tbytes_per_frame * alsa->frames_per_packet);
|
||||
ZeroMemory(alsa->buffer, tbytes_per_frame * alsa->frames_per_packet);
|
||||
alsa->buffer = (BYTE*) calloc(1, tbytes_per_frame * alsa->frames_per_packet);
|
||||
|
||||
if (!alsa->buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
alsa->buffer_frames = 0;
|
||||
|
||||
alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
alsa->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_alsa_thread_func, alsa, 0, NULL);
|
||||
|
||||
if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(alsa->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_alsa_thread_func, alsa, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(alsa->buffer);
|
||||
alsa->buffer = NULL;
|
||||
CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static void audin_alsa_close(IAudinDevice* device)
|
||||
/**
|
||||
* 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;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
if (alsa->stopEvent)
|
||||
{
|
||||
SetEvent(alsa->stopEvent);
|
||||
WaitForSingleObject(alsa->thread, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = NULL;
|
||||
|
||||
CloseHandle(alsa->thread);
|
||||
alsa->thread = NULL;
|
||||
}
|
||||
|
||||
if (alsa->buffer)
|
||||
free(alsa->buffer);
|
||||
free(alsa->buffer);
|
||||
alsa->buffer = NULL;
|
||||
|
||||
alsa->receive = NULL;
|
||||
alsa->user_data = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
|
||||
{
|
||||
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static void audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* args)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_alsa_args, flags, alsa, NULL, NULL);
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
audin_alsa_args, flags, alsa, NULL, NULL);
|
||||
arg = audin_alsa_args;
|
||||
|
||||
do
|
||||
|
@ -399,42 +513,74 @@ static void audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* arg
|
|||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "audio-dev")
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
alsa->device_name = _strdup(arg->Value);
|
||||
}
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry alsa_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinALSADevice* alsa;
|
||||
UINT error;
|
||||
alsa = (AudinALSADevice*) calloc(1, sizeof(AudinALSADevice));
|
||||
|
||||
alsa = (AudinALSADevice*) malloc(sizeof(AudinALSADevice));
|
||||
ZeroMemory(alsa, sizeof(AudinALSADevice));
|
||||
if (!alsa)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
audin_alsa_parse_addin_args(alsa, args);
|
||||
if ((error = audin_alsa_parse_addin_args(alsa, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_alsa_parse_addin_args failed with errorcode %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
alsa->device_name = _strdup("default");
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->frames_per_packet = 128;
|
||||
alsa->target_rate = 22050;
|
||||
alsa->actual_rate = 22050;
|
||||
|
@ -442,10 +588,26 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
|
|||
alsa->target_channels = 2;
|
||||
alsa->actual_channels = 2;
|
||||
alsa->bytes_per_channel = 2;
|
||||
|
||||
alsa->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa);
|
||||
if (!alsa->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) alsa)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
free(alsa->device_name);
|
||||
free(alsa);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* Audio Input Redirection Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2015 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -32,7 +35,7 @@
|
|||
#include <freerdp/addin.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MSG_SNDIN_VERSION 0x01
|
||||
|
@ -79,18 +82,27 @@ struct _AUDIN_PLUGIN
|
|||
|
||||
/* Parsed plugin data */
|
||||
UINT16 fixed_format;
|
||||
UINT16 fixed_channel;
|
||||
UINT16 fixed_channel;
|
||||
UINT32 fixed_rate;
|
||||
char* subsystem;
|
||||
char* device_name;
|
||||
|
||||
/* Device interface */
|
||||
IAudinDevice* device;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
int error;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
UINT32 Version;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
@ -100,6 +112,13 @@ static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
DEBUG_DVC("Version=%d", Version);
|
||||
|
||||
out = Stream_New(NULL, 5);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
|
||||
Stream_Write_UINT32(out, Version);
|
||||
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(out), Stream_Buffer(out), NULL);
|
||||
|
@ -108,7 +127,12 @@ static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
return error;
|
||||
}
|
||||
|
||||
static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
BYTE out_data[1];
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
@ -117,13 +141,18 @@ static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCall
|
|||
return callback->channel->Write(callback->channel, 1, out_data, NULL);
|
||||
}
|
||||
|
||||
static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
UINT32 i;
|
||||
BYTE* fm;
|
||||
int error;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
UINT32 NumFormats;
|
||||
audinFormat format;
|
||||
|
@ -134,14 +163,26 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
if ((NumFormats < 1) || (NumFormats > 1000))
|
||||
{
|
||||
WLog_ERR(TAG, "bad NumFormats %d", NumFormats);
|
||||
return 1;
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
|
||||
|
||||
callback->formats = (audinFormat*) malloc(NumFormats * sizeof(audinFormat));
|
||||
ZeroMemory(callback->formats, NumFormats * sizeof(audinFormat));
|
||||
callback->formats = (audinFormat*) calloc(1, NumFormats * sizeof(audinFormat));
|
||||
if (!callback->formats)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
out = Stream_New(NULL, 9);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_Seek(out, 9);
|
||||
|
||||
/* SoundFormats (variable) */
|
||||
|
@ -157,7 +198,7 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
Stream_Read_UINT16(s, format.cbSize);
|
||||
format.data = Stream_Pointer(s);
|
||||
Stream_Seek(s, format.cbSize);
|
||||
|
||||
|
||||
DEBUG_DVC("wFormatTag=%d nChannels=%d nSamplesPerSec=%d "
|
||||
"nBlockAlign=%d wBitsPerSample=%d cbSize=%d",
|
||||
format.wFormatTag, format.nChannels, format.nSamplesPerSec,
|
||||
|
@ -176,12 +217,23 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
/* Store the agreed format in the corresponding index */
|
||||
callback->formats[callback->formats_count++] = format;
|
||||
/* Put the format to output buffer */
|
||||
Stream_EnsureRemainingCapacity(out, 18 + format.cbSize);
|
||||
if (!Stream_EnsureRemainingCapacity(out, 18 + format.cbSize))
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
Stream_Write(out, fm, 18 + format.cbSize);
|
||||
}
|
||||
}
|
||||
|
||||
audin_send_incoming_data_pdu(pChannelCallback);
|
||||
if ((error = audin_send_incoming_data_pdu(pChannelCallback)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
|
||||
Stream_SetPosition(out, 0);
|
||||
|
@ -191,18 +243,36 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
|
|||
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
|
||||
|
||||
error = callback->channel->Write(callback->channel, cbSizeFormatsPacket, Stream_Buffer(out), NULL);
|
||||
out:
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
free(callback->formats);
|
||||
callback->formats = NULL;
|
||||
}
|
||||
Stream_Free(out, TRUE);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 NewFormat)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 NewFormat)
|
||||
{
|
||||
int error;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
out = Stream_New(NULL, 5);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
|
||||
Stream_Write_UINT32(out, NewFormat);
|
||||
error = callback->channel->Write(callback->channel, 5, Stream_Buffer(out), NULL);
|
||||
|
@ -211,13 +281,25 @@ static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCall
|
|||
return error;
|
||||
}
|
||||
|
||||
static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 Result)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 Result)
|
||||
{
|
||||
int error;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
out = Stream_New(NULL, 5);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
|
||||
Stream_Write_UINT32(out, Result);
|
||||
error = callback->channel->Write(callback->channel, 5, Stream_Buffer(out), NULL);
|
||||
|
@ -226,33 +308,52 @@ static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallbac
|
|||
return error;
|
||||
}
|
||||
|
||||
static BOOL audin_receive_wave_data(BYTE* data, int size, void* user_data)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
|
||||
{
|
||||
int error;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
|
||||
|
||||
error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback);
|
||||
|
||||
if (error != 0)
|
||||
return FALSE;
|
||||
if ((error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
|
||||
return error;
|
||||
}
|
||||
|
||||
out = Stream_New(NULL, size + 1);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_DATA);
|
||||
Stream_Write(out, data, size);
|
||||
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(out), Stream_Buffer(out), NULL);
|
||||
Stream_Free(out, TRUE);
|
||||
|
||||
return (error == 0 ? TRUE : FALSE);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
audinFormat* format;
|
||||
UINT32 initialFormat;
|
||||
UINT32 FramesPerPacket;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
Stream_Read_UINT32(s, FramesPerPacket);
|
||||
Stream_Read_UINT32(s, initialFormat);
|
||||
|
@ -264,28 +365,50 @@ static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStr
|
|||
{
|
||||
WLog_ERR(TAG, "invalid format index %d (total %d)",
|
||||
initialFormat, callback->formats_count);
|
||||
return 1;
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
format = &callback->formats[initialFormat];
|
||||
if (audin->device)
|
||||
{
|
||||
IFCALL(audin->device->SetFormat, audin->device, format, FramesPerPacket);
|
||||
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
|
||||
IFCALLRET(audin->device->SetFormat, error, audin->device, format, FramesPerPacket);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "SetFormat failed with errorcode %lu", error);
|
||||
return error;
|
||||
}
|
||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Open failed with errorcode %lu", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
audin_send_format_change_pdu(pChannelCallback, initialFormat);
|
||||
audin_send_open_reply_pdu(pChannelCallback, 0);
|
||||
if ((error = audin_send_format_change_pdu(pChannelCallback, initialFormat)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if ((error = audin_send_open_reply_pdu(pChannelCallback, 0)))
|
||||
WLog_ERR(TAG, "audin_send_open_reply_pdu failed!");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN * audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
UINT32 NewFormat;
|
||||
audinFormat* format;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
Stream_Read_UINT32(s, NewFormat);
|
||||
|
||||
|
@ -295,26 +418,47 @@ static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallb
|
|||
{
|
||||
WLog_ERR(TAG, "invalid format index %d (total %d)",
|
||||
NewFormat, callback->formats_count);
|
||||
return 1;
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
format = &callback->formats[NewFormat];
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
IFCALL(audin->device->Close, audin->device);
|
||||
IFCALL(audin->device->SetFormat, audin->device, format, 0);
|
||||
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
|
||||
IFCALLRET(audin->device->Close, error, audin->device);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Close failed with errorcode %lu", error);
|
||||
return error;
|
||||
}
|
||||
IFCALLRET(audin->device->SetFormat, error, audin->device, format, 0);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "SetFormat failed with errorcode %lu", error);
|
||||
return error;
|
||||
}
|
||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Open failed with errorcode %lu", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
audin_send_format_change_pdu(pChannelCallback, NewFormat);
|
||||
if ((error = audin_send_format_change_pdu(pChannelCallback, NewFormat)))
|
||||
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
{
|
||||
int error;
|
||||
UINT error;
|
||||
BYTE MessageId;
|
||||
|
||||
Stream_Read_UINT8(data, MessageId);
|
||||
|
@ -341,40 +485,59 @@ static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
|||
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown MessageId=0x%x", MessageId);
|
||||
error = 1;
|
||||
error = ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
DEBUG_DVC("");
|
||||
DEBUG_DVC("...");
|
||||
|
||||
if (audin->device)
|
||||
IFCALL(audin->device->Close, audin->device);
|
||||
{
|
||||
IFCALLRET(audin->device->Close, error, audin->device);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
WLog_ERR(TAG, "Close failed with errorcode %lu", error);
|
||||
}
|
||||
|
||||
free(callback->formats);
|
||||
free(callback);
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback;
|
||||
AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*) pListenerCallback;
|
||||
|
||||
DEBUG_DVC("");
|
||||
DEBUG_DVC("...");
|
||||
|
||||
callback = (AUDIN_CHANNEL_CALLBACK*) malloc(sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
ZeroMemory(callback, sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
callback = (AUDIN_CHANNEL_CALLBACK*) calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
if (!callback)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
callback->iface.OnDataReceived = audin_on_data_received;
|
||||
callback->iface.OnClose = audin_on_close;
|
||||
|
@ -384,17 +547,26 @@ static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
|||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
|
||||
DEBUG_DVC("");
|
||||
DEBUG_DVC("...");
|
||||
|
||||
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) malloc(sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
ZeroMemory(audin->listener_callback, sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) calloc(1, sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
if (!audin->listener_callback)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
|
||||
audin->listener_callback->plugin = pPlugin;
|
||||
|
@ -404,16 +576,26 @@ static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
|||
(IWTSListenerCallback*) audin->listener_callback, NULL);
|
||||
}
|
||||
|
||||
static int audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
DEBUG_DVC("");
|
||||
DEBUG_DVC("...");
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
IFCALL(audin->device->Close, audin->device);
|
||||
IFCALL(audin->device->Free, audin->device);
|
||||
IFCALLRET(audin->device->Free, error, audin->device);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Free failed with errorcode %lu", error);
|
||||
// dont stop on error
|
||||
}
|
||||
audin->device = NULL;
|
||||
}
|
||||
|
||||
|
@ -426,64 +608,103 @@ static int audin_plugin_terminated(IWTSPlugin* pPlugin)
|
|||
free(audin->listener_callback);
|
||||
free(audin);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
WLog_ERR(TAG, "existing device, abort.");
|
||||
return;
|
||||
return ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
DEBUG_DVC("device registered.");
|
||||
|
||||
audin->device = device;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
|
||||
{
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY entry;
|
||||
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
|
||||
UINT error;
|
||||
|
||||
if (!audin_process_addin_args(audin, args))
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
|
||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL, 0);
|
||||
|
||||
if (entry == NULL)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_load_channel_addin_entry did not return any function pointers for %s ", name);
|
||||
return ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
|
||||
entryPoints.plugin = pPlugin;
|
||||
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
|
||||
entryPoints.args = args;
|
||||
entryPoints.rdpcontext = ((AUDIN_PLUGIN*)pPlugin)->rdpcontext;
|
||||
|
||||
if (entry(&entryPoints) != 0)
|
||||
if ((error = entry(&entryPoints)))
|
||||
{
|
||||
WLog_ERR(TAG, "%s entry returns error.", name);
|
||||
return FALSE;
|
||||
WLog_ERR(TAG, "%s entry returned error %lu.", name, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
WLog_INFO(TAG, "Loaded %s backend for audin", name);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
|
||||
{
|
||||
if (audin->subsystem)
|
||||
free(audin->subsystem);
|
||||
|
||||
free(audin->subsystem);
|
||||
audin->subsystem = _strdup(subsystem);
|
||||
if (!audin->subsystem)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
{
|
||||
if (audin->device_name)
|
||||
free(audin->device_name);
|
||||
|
||||
free(audin->device_name);
|
||||
audin->device_name = _strdup(device_name);
|
||||
if (!audin->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_args[] =
|
||||
static COMMAND_LINE_ARGUMENT_A audin_args[] =
|
||||
{
|
||||
{ "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
|
@ -493,17 +714,22 @@ COMMAND_LINE_ARGUMENT_A audin_args[] =
|
|||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
|
||||
BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
UINT error;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
if (!args || args->argc == 1)
|
||||
return TRUE;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
audin_args, flags, audin, NULL, NULL);
|
||||
if (status != 0)
|
||||
return FALSE;
|
||||
|
||||
arg = audin_args;
|
||||
|
||||
|
@ -516,11 +742,19 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
|
|||
|
||||
CommandLineSwitchCase(arg, "sys")
|
||||
{
|
||||
audin_set_subsystem(audin, arg->Value);
|
||||
if ((error = audin_set_subsystem(audin, arg->Value)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem failed with error %lu!", error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
audin_set_device_name(audin, arg->Value);
|
||||
if ((error = audin_set_device_name(audin, arg->Value)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name failed with error %lu!", error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchCase(arg, "format")
|
||||
{
|
||||
|
@ -546,81 +780,121 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry audin_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
int error = 0;
|
||||
struct SubsystemEntry
|
||||
{
|
||||
char *subsystem;
|
||||
char *device;
|
||||
};
|
||||
|
||||
UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
ADDIN_ARGV* args;
|
||||
AUDIN_PLUGIN* audin;
|
||||
struct SubsystemEntry entries[] =
|
||||
{
|
||||
#if defined(WITH_PULSE)
|
||||
{"pulse", ""},
|
||||
#endif
|
||||
#if defined(WITH_OSS)
|
||||
{"oss", "default"},
|
||||
#endif
|
||||
#if defined(WITH_ALSA)
|
||||
{"alsa", "default"},
|
||||
#endif
|
||||
#if defined(WITH_OPENSLES)
|
||||
{"opensles", "default"},
|
||||
#endif
|
||||
#if defined(WITH_WINMM)
|
||||
{"winmm", "default"},
|
||||
#endif
|
||||
#if defined(WITH_MACAUDIO)
|
||||
{"mac", "default"},
|
||||
#endif
|
||||
{NULL,NULL}
|
||||
};
|
||||
struct SubsystemEntry *entry = &entries[0];
|
||||
|
||||
assert(pEntryPoints);
|
||||
assert(pEntryPoints->GetPlugin);
|
||||
|
||||
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
||||
if (audin == NULL)
|
||||
if (audin != NULL)
|
||||
return CHANNEL_RC_ALREADY_INITIALIZED;
|
||||
|
||||
audin = (AUDIN_PLUGIN*) calloc(1, sizeof(AUDIN_PLUGIN));
|
||||
if (!audin)
|
||||
{
|
||||
audin = (AUDIN_PLUGIN*) malloc(sizeof(AUDIN_PLUGIN));
|
||||
ZeroMemory(audin, sizeof(AUDIN_PLUGIN));
|
||||
|
||||
audin->iface.Initialize = audin_plugin_initialize;
|
||||
audin->iface.Connected = NULL;
|
||||
audin->iface.Disconnected = NULL;
|
||||
audin->iface.Terminated = audin_plugin_terminated;
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
audin->iface.Initialize = audin_plugin_initialize;
|
||||
audin->iface.Connected = NULL;
|
||||
audin->iface.Disconnected = NULL;
|
||||
audin->iface.Terminated = audin_plugin_terminated;
|
||||
|
||||
args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||
audin->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(pEntryPoints))->instance)->context;
|
||||
|
||||
if (error == 0)
|
||||
audin_process_addin_args((IWTSPlugin*) audin, args);
|
||||
if (args)
|
||||
{
|
||||
if (!audin_process_addin_args(audin, args))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (audin->subsystem)
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
|
||||
#if defined(WITH_PULSE)
|
||||
if (!audin->device)
|
||||
{
|
||||
audin_set_subsystem(audin, "pulse");
|
||||
audin_set_device_name(audin, "");
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin %s failed with error %lu!",
|
||||
audin->subsystem, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_ALSA)
|
||||
if (!audin->device)
|
||||
else
|
||||
{
|
||||
audin_set_subsystem(audin, "alsa");
|
||||
audin_set_device_name(audin, "default");
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
}
|
||||
#endif
|
||||
while (entry && entry->subsystem && !audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, entry->subsystem)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_set_device_name(audin, entry->device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
|
||||
#if defined(WITH_OPENSLES)
|
||||
if (!audin->device)
|
||||
{
|
||||
audin_set_subsystem(audin, "opensles");
|
||||
audin_set_device_name(audin, "default");
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_WINMM)
|
||||
if (!audin->device)
|
||||
{
|
||||
audin_set_subsystem(audin, "winmm");
|
||||
audin_set_device_name(audin, "default");
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (audin->device == NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "no sound device.");
|
||||
}
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
|
||||
|
||||
out:
|
||||
if (error != CHANNEL_RC_OK)
|
||||
audin_plugin_terminated((IWTSPlugin*)audin);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@
|
|||
#define TAG CHANNELS_TAG("audin.client")
|
||||
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_DVC(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(fmt, ...) do { } while (0)
|
||||
#define DEBUG_DVC(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_AUDIN_CLIENT_MAIN_H */
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# 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(AUDIO_TOOL AudioToolbox)
|
||||
FIND_LIBRARY(APP_SERVICES ApplicationServices)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
audin_mac.c)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${CORE_AUDIO} ${AUDIO_TOOL} ${APP_SERVICES} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
|
@ -0,0 +1,466 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#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>
|
||||
|
||||
#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/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
#define MAC_AUDIO_QUEUE_BUFFER_SIZE 32768
|
||||
|
||||
typedef struct _AudinMacDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
audinFormat format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
} AudinMacDevice;
|
||||
|
||||
static AudioFormatID audin_mac_get_format(const audinFormat* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
/*
|
||||
case WAVE_FORMAT_GSM610:
|
||||
return kAudioFormatMicrosoftGSM;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return kAudioFormatALaw;
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return kAudioFormatULaw;
|
||||
case WAVE_FORMAT_AAC_MS:
|
||||
return kAudioFormatMPEG4AAC;
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const audinFormat* format)
|
||||
{
|
||||
switch(format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
{
|
||||
AudioFormatID req_fmt = 0;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
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, audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
mac->FramesPerPacket = FramesPerPacket;
|
||||
CopyMemory(&(mac->format), format, sizeof(audinFormat));
|
||||
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
rdpsnd_get_audio_tag_string(format->wFormatTag),
|
||||
format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
mac->FramesPerPacket *= 4; /* Compression ratio. */
|
||||
mac->format.wBitsPerSample *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
mac->audioFormat.mBitsPerChannel = mac->format.wBitsPerSample;
|
||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||
mac->audioFormat.mBytesPerFrame =
|
||||
mac->audioFormat.mChannelsPerFrame * mac->audioFormat.mBitsPerChannel / 8;
|
||||
mac->audioFormat.mBytesPerPacket =
|
||||
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
||||
|
||||
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;
|
||||
int encoded_size = 0;
|
||||
const BYTE *encoded_data;
|
||||
BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
|
||||
|
||||
/* Process. */
|
||||
switch (mac->format.wFormatTag) {
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
if (!mac->dsp_context->encode_ms_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (!mac->dsp_context->encode_ima_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
default:
|
||||
encoded_data = buffer;
|
||||
encoded_size = buffer_size;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = mac->receive(encoded_data, encoded_size, mac->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %lu", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static UINT audin_mac_close(IAudinDevice *device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(mac->audioQueue, true);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%d]",
|
||||
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 [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->audioQueue = NULL;
|
||||
}
|
||||
|
||||
mac->receive = NULL;
|
||||
mac->user_data = NULL;
|
||||
|
||||
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;
|
||||
size_t index;
|
||||
|
||||
mac->receive = receive;
|
||||
mac->user_data = user_data;
|
||||
|
||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
|
||||
mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
|
||||
&mac->audioBuffers[index]);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
|
||||
mac->audioBuffers[index],
|
||||
0,
|
||||
NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
|
||||
|
||||
devStat = AudioQueueStart(mac->audioQueue, NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%d]",
|
||||
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 == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_mac_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_mac_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
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, (const char**)args->argv, audin_mac_args, flags, mac, NULL, NULL);
|
||||
|
||||
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)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry mac_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
ADDIN_ARGV *args;
|
||||
AudinMacDevice *mac;
|
||||
UINT error;
|
||||
|
||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%d]",
|
||||
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 %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
mac->dsp_context = freerdp_dsp_context_new();
|
||||
if (!mac->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
error_out:
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
return error;
|
||||
|
||||
}
|
|
@ -31,5 +31,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
|||
set(${MODULE_PREFIX}_LIBS freerdp ${OPENSLES_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -36,6 +38,7 @@
|
|||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
#include "opensl_io.h"
|
||||
|
@ -63,6 +66,8 @@ typedef struct _AudinOpenSLESDevice
|
|||
HANDLE stopEvent;
|
||||
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinOpenSLESDevice;
|
||||
|
||||
static void* audin_opensles_thread_func(void* arg)
|
||||
|
@ -74,7 +79,10 @@ static void* audin_opensles_thread_func(void* arg)
|
|||
BYTE *b;
|
||||
} buffer;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
|
||||
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
|
||||
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
|
||||
int rc = CHANNEL_RC_OK;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
|
||||
DEBUG_DVC("opensles=%p", opensles);
|
||||
|
||||
|
@ -84,36 +92,65 @@ static void* audin_opensles_thread_func(void* arg)
|
|||
assert(opensles->stopEvent);
|
||||
assert(opensles->stream);
|
||||
|
||||
buffer.v = malloc(raw_size);
|
||||
ZeroMemory(buffer.v, raw_size);
|
||||
buffer.v = calloc(1, raw_size);
|
||||
if (!buffer.v)
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
if (opensles->rdpcontext)
|
||||
setChannelError(opensles->rdpcontext, CHANNEL_RC_NO_MEMORY, "audin_opensles_thread_func reported an error");
|
||||
goto out;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(opensles->dsp_context);
|
||||
|
||||
while (!(WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
while (1)
|
||||
{
|
||||
size_t encoded_size;
|
||||
|
||||
status = WaitForSingleObject(opensles->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
size_t encoded_size;
|
||||
void *encoded_data;
|
||||
|
||||
int rc = android_RecIn(opensles->stream, buffer.s, raw_size);
|
||||
rc = android_RecIn(opensles->stream, buffer.s, raw_size);
|
||||
if (rc < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "android_RecIn %d", rc);
|
||||
WLog_ERR(TAG, "android_RecIn %lu", rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(rc == raw_size);
|
||||
if (opensles->format == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
opensles->dsp_context->encode_ms_adpcm(opensles->dsp_context,
|
||||
buffer.b, rc, opensles->channels, opensles->block_size);
|
||||
if (!opensles->dsp_context->encode_ms_adpcm(opensles->dsp_context,
|
||||
buffer.b, rc, opensles->channels, opensles->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = opensles->dsp_context->adpcm_buffer;
|
||||
encoded_size = opensles->dsp_context->adpcm_size;
|
||||
}
|
||||
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context,
|
||||
if (!opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context,
|
||||
buffer.b, rc,
|
||||
opensles->channels, opensles->block_size);
|
||||
opensles->channels, opensles->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = opensles->dsp_context->adpcm_buffer;
|
||||
encoded_size = opensles->dsp_context->adpcm_size;
|
||||
|
@ -124,20 +161,28 @@ static void* audin_opensles_thread_func(void* arg)
|
|||
encoded_size = rc;
|
||||
}
|
||||
|
||||
rc = opensles->receive(encoded_data, encoded_size, opensles->user_data);
|
||||
if (!rc)
|
||||
error = opensles->receive(encoded_data, encoded_size, opensles->user_data);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
free(buffer.v);
|
||||
|
||||
out:
|
||||
DEBUG_DVC("thread shutdown.");
|
||||
|
||||
ExitThread(0);
|
||||
if (error && opensles->rdpcontext)
|
||||
setChannelError(opensles->rdpcontext, error, "audin_opensles_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void audin_opensles_free(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
|
@ -146,7 +191,7 @@ static void audin_opensles_free(IAudinDevice* device)
|
|||
/* The function may have been called out of order,
|
||||
* ignore duplicate requests. */
|
||||
if (!opensles)
|
||||
return;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
assert(opensles);
|
||||
assert(opensles->dsp_context);
|
||||
|
@ -154,15 +199,18 @@ static void audin_opensles_free(IAudinDevice* device)
|
|||
|
||||
freerdp_dsp_context_free(opensles->dsp_context);
|
||||
|
||||
if (opensles->device_name)
|
||||
free(opensles->device_name);
|
||||
free(opensles->device_name);
|
||||
|
||||
free(opensles);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
{
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
#endif
|
||||
|
||||
DEBUG_DVC("device=%p, format=%p", opensles, format);
|
||||
|
||||
|
@ -170,8 +218,6 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
|
|||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
/* TODO: Deactivated, untested */
|
||||
#if 0
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
|
@ -181,7 +227,6 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
|
|||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
/* TODO: Deactivated format, does not work, find out why */
|
||||
// case WAVE_FORMAT_ADPCM: /* IMA ADPCM */
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
|
@ -202,7 +247,12 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void audin_opensles_set_format(IAudinDevice* device,
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
|
@ -216,7 +266,7 @@ static void audin_opensles_set_format(IAudinDevice* device,
|
|||
/* The function may have been called out of order, ignore
|
||||
* requests before the device is available. */
|
||||
if (!opensles)
|
||||
return;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
|
@ -253,7 +303,7 @@ static void audin_opensles_set_format(IAudinDevice* device,
|
|||
WLog_ERR(TAG, "Encoding '%d' [%08X] not supported",
|
||||
(format->wFormatTag),
|
||||
format->wFormatTag);
|
||||
return;
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
opensles->rate = format->nSamplesPerSec;
|
||||
|
@ -264,9 +314,15 @@ static void audin_opensles_set_format(IAudinDevice* device,
|
|||
|
||||
DEBUG_DVC("aligned frames_per_packet=%d, block_size=%d",
|
||||
opensles->frames_per_packet, opensles->block_size);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
||||
/**
|
||||
* 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;
|
||||
|
@ -278,27 +334,51 @@ static void audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
|||
/* The function may have been called out of order,
|
||||
* ignore duplicate open requests. */
|
||||
if(opensles->stream)
|
||||
return;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
opensles->stream = android_OpenRecDevice(
|
||||
if(!(opensles->stream = android_OpenRecDevice(
|
||||
opensles->device_name,
|
||||
opensles->rate,
|
||||
opensles->channels,
|
||||
opensles->frames_per_packet,
|
||||
opensles->bytes_per_channel * 8);
|
||||
assert(opensles->stream);
|
||||
opensles->bytes_per_channel * 8)))
|
||||
{
|
||||
WLog_ERR(TAG, "android_OpenRecDevice failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
opensles->receive = receive;
|
||||
opensles->user_data = user_data;
|
||||
|
||||
opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
opensles->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_opensles_thread_func,
|
||||
opensles, 0, NULL);
|
||||
if (!(opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
goto error_out;
|
||||
}
|
||||
if (!(opensles->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_opensles_thread_func,
|
||||
opensles, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
goto error_out;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
android_CloseRecDevice(opensles->stream);
|
||||
opensles->stream = NULL;
|
||||
CloseHandle(opensles->stopEvent);
|
||||
opensles->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static void audin_opensles_close(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p", device);
|
||||
|
@ -310,7 +390,7 @@ static void audin_opensles_close(IAudinDevice* device)
|
|||
if (!opensles->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "[ERROR] function called without matching open.");
|
||||
return;
|
||||
return ERROR_REQUEST_OUT_OF_SEQUENCE;
|
||||
}
|
||||
|
||||
assert(opensles->stopEvent);
|
||||
|
@ -318,7 +398,12 @@ static void audin_opensles_close(IAudinDevice* device)
|
|||
assert(opensles->stream);
|
||||
|
||||
SetEvent(opensles->stopEvent);
|
||||
WaitForSingleObject(opensles->thread, INFINITE);
|
||||
if (WaitForSingleObject(opensles->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
CloseHandle(opensles->stopEvent);
|
||||
CloseHandle(opensles->thread);
|
||||
|
||||
|
@ -329,26 +414,33 @@ static void audin_opensles_close(IAudinDevice* device)
|
|||
opensles->receive = NULL;
|
||||
opensles->user_data = NULL;
|
||||
opensles->stream = NULL;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
|
||||
{
|
||||
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
UINT status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p, args=%p", device, args);
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
audin_opensles_args, flags, opensles, NULL, NULL);
|
||||
|
@ -364,48 +456,84 @@ static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
|||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "audio-dev")
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
opensles->device_name = _strdup(arg->Value);
|
||||
if (!opensles->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return status;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry \
|
||||
opensles_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry \
|
||||
FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_audin_client_subsystem_entry(
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinOpenSLESDevice* opensles;
|
||||
UINT error;
|
||||
|
||||
DEBUG_DVC("pEntryPoints=%p", pEntryPoints);
|
||||
|
||||
opensles = (AudinOpenSLESDevice*) malloc(sizeof(AudinOpenSLESDevice));
|
||||
ZeroMemory(opensles, sizeof(AudinOpenSLESDevice));
|
||||
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
|
||||
if (!opensles)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
audin_opensles_parse_addin_args(opensles, args);
|
||||
if ((error = audin_opensles_parse_addin_args(opensles, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_opensles_parse_addin_args failed with errorcode %d!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
opensles->dsp_context = freerdp_dsp_context_new();
|
||||
if (!opensles->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) opensles);
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) opensles)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %d!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(opensles->dsp_context);
|
||||
free(opensles);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -234,7 +234,8 @@ OPENSL_STREAM *android_OpenRecDevice(char *name, int sr, int inchannels,
|
|||
|
||||
OPENSL_STREAM *p;
|
||||
p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1);
|
||||
memset(p, 0, sizeof(OPENSL_STREAM));
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
p->inchannels = inchannels;
|
||||
p->sr = sr;
|
||||
|
@ -314,7 +315,14 @@ void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
|||
assert(p->queue);
|
||||
|
||||
e = calloc(1, sizeof(queue_element));
|
||||
if (!e)
|
||||
return;
|
||||
e->data = calloc(p->buffersize, p->bits_per_sample / 8);
|
||||
if (!e->data)
|
||||
{
|
||||
free(e);
|
||||
return;
|
||||
}
|
||||
e->size = p->buffersize * p->bits_per_sample / 8;
|
||||
|
||||
Queue_Enqueue(p->queue, p->next);
|
||||
|
@ -331,6 +339,7 @@ int android_RecIn(OPENSL_STREAM *p,short *buffer,int size)
|
|||
{
|
||||
queue_element *e;
|
||||
int rc;
|
||||
DWORD status;
|
||||
|
||||
assert(p);
|
||||
assert(buffer);
|
||||
|
@ -340,6 +349,7 @@ int android_RecIn(OPENSL_STREAM *p,short *buffer,int size)
|
|||
if (!p->prep)
|
||||
{
|
||||
p->prep = calloc(1, sizeof(queue_element));
|
||||
|
||||
p->prep->data = calloc(p->buffersize, p->bits_per_sample / 8);
|
||||
p->prep->size = p->buffersize * p->bits_per_sample / 8;
|
||||
|
||||
|
@ -357,7 +367,15 @@ int android_RecIn(OPENSL_STREAM *p,short *buffer,int size)
|
|||
|
||||
/* Wait for queue to be filled... */
|
||||
if (!Queue_Count(p->queue))
|
||||
WaitForSingleObject(p->queue->event, INFINITE);
|
||||
{
|
||||
status = WaitForSingleObject(p->queue->event, INFINITE);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
e = Queue_Dequeue(p->queue);
|
||||
if (!e)
|
||||
|
@ -374,6 +392,6 @@ int android_RecIn(OPENSL_STREAM *p,short *buffer,int size)
|
|||
free(e->data);
|
||||
free(e);
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# 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" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
audin_oss.c)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${OSS_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr ${OSS_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
|
@ -0,0 +1,568 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#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>
|
||||
#if defined(__OpenBSD__)
|
||||
#include <soundcard.h>
|
||||
#else
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct _AudinOSSDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
audinFormat format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinOSSDevice;
|
||||
|
||||
#define OSS_LOG_ERR(_text, _error) \
|
||||
if (_error != 0) \
|
||||
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
|
||||
|
||||
|
||||
static int audin_oss_get_format(audinFormat* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
return AFMT_S8;
|
||||
|
||||
case 16:
|
||||
return AFMT_S16_LE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return AFMT_A_LAW;
|
||||
#if 0 /* This does not work on my desktop. */
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return AFMT_MU_LAW;
|
||||
#endif
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return AFMT_S16_LE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
audinFormat* format)
|
||||
{
|
||||
int req_fmt = 0;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
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;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (format->nSamplesPerSec > 48000 ||
|
||||
format->wBitsPerSample != 4 ||
|
||||
(format->nChannels != 1 && format->nChannels != 2))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
req_fmt = audin_oss_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_oss_set_format(IAudinDevice* device, audinFormat* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
oss->FramesPerPacket = FramesPerPacket;
|
||||
CopyMemory(&(oss->format), format, sizeof(audinFormat));
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
oss->FramesPerPacket *= 4; /* Compression ratio. */
|
||||
oss->format.wBitsPerSample *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void* audin_oss_thread_func(void* arg)
|
||||
{
|
||||
char dev_name[PATH_MAX] = "/dev/dsp";
|
||||
char mixer_name[PATH_MAX] = "/dev/mixer";
|
||||
int pcm_handle = -1, mixer_handle;
|
||||
BYTE* buffer = NULL, *encoded_data = NULL;
|
||||
int tmp, buffer_size, encoded_size;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
||||
UINT error = 0;
|
||||
DWORD status;
|
||||
|
||||
if (arg == NULL)
|
||||
{
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
freerdp_channel_init_thread_context(oss->rdpcontext);
|
||||
|
||||
if (oss->dev_unit != -1)
|
||||
{
|
||||
sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
|
||||
sprintf_s(mixer_name, PATH_MAX - 1, "/dev/mixer%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 rec volume to 100%. */
|
||||
if ((mixer_handle = open(mixer_name, O_RDWR)) < 0)
|
||||
{
|
||||
OSS_LOG_ERR("mixer open failed, not critical", errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = (100 | (100 << 8));
|
||||
|
||||
if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_MIC), &tmp) == -1)
|
||||
OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_MIC, not critical", errno);
|
||||
|
||||
tmp = (100 | (100 << 8));
|
||||
|
||||
if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_RECLEV), &tmp) == -1)
|
||||
OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_RECLEV, not critical", errno);
|
||||
|
||||
close(mixer_handle);
|
||||
}
|
||||
|
||||
#if 0 /* FreeBSD OSS implementation at this moment (2015.03) does not set PCM_CAP_INPUT flag. */
|
||||
tmp = 0;
|
||||
|
||||
if (ioctl(pcm_handle, SNDCTL_DSP_GETCAPS, &tmp) == -1)
|
||||
{
|
||||
OSS_LOG_ERR("SNDCTL_DSP_GETCAPS failed, try ignory", errno);
|
||||
}
|
||||
else if ((tmp & PCM_CAP_INPUT) == 0)
|
||||
{
|
||||
OSS_LOG_ERR("Device does not supports playback", EOPNOTSUPP);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* 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 = (oss->FramesPerPacket * oss->format.nChannels *
|
||||
(oss->format.wBitsPerSample / 8));
|
||||
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
|
||||
|
||||
if (NULL == buffer)
|
||||
{
|
||||
OSS_LOG_ERR("malloc() fail", errno);
|
||||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForSingleObject(oss->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
tmp = read(pcm_handle, buffer, buffer_size);
|
||||
|
||||
/* Error happen. */
|
||||
if (tmp < 0)
|
||||
{
|
||||
OSS_LOG_ERR("read() error", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmp < buffer_size) /* Not enouth data. */
|
||||
continue;
|
||||
|
||||
/* Process. */
|
||||
switch (oss->format.wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
if (!oss->dsp_context->encode_ms_adpcm(oss->dsp_context,
|
||||
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
encoded_data = oss->dsp_context->adpcm_buffer;
|
||||
encoded_size = oss->dsp_context->adpcm_size;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (!oss->dsp_context->encode_ima_adpcm(oss->dsp_context,
|
||||
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
encoded_data = oss->dsp_context->adpcm_buffer;
|
||||
encoded_size = oss->dsp_context->adpcm_size;
|
||||
break;
|
||||
|
||||
default:
|
||||
encoded_data = buffer;
|
||||
encoded_size = buffer_size;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = oss->receive(encoded_data, encoded_size, oss->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "oss->receive failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err_out:
|
||||
|
||||
if (error && 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(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(oss->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)audin_oss_thread_func, oss, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = NULL;
|
||||
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;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (oss->stopEvent != NULL)
|
||||
{
|
||||
SetEvent(oss->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = NULL;
|
||||
CloseHandle(oss->thread);
|
||||
oss->thread = NULL;
|
||||
}
|
||||
|
||||
oss->receive = NULL;
|
||||
oss->user_data = NULL;
|
||||
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;
|
||||
int error;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_oss_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
|
||||
freerdp_dsp_context_free(oss->dsp_context);
|
||||
free(oss);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv,
|
||||
audin_oss_args, flags, oss, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_oss_args;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
oss->dev_unit = strtol(str_num, &eptr, 10);
|
||||
|
||||
if (oss->dev_unit < 0 || *eptr != '\0')
|
||||
oss->dev_unit = -1;
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry oss_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinOSSDevice* oss;
|
||||
UINT error;
|
||||
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 %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
oss->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!oss->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) oss)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(oss->dsp_context);
|
||||
free(oss);
|
||||
return error;
|
||||
}
|
|
@ -26,9 +26,6 @@ include_directories(${PULSE_INCLUDE_DIR})
|
|||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${PULSE_LIBRARY})
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${PULSE_LIBRARY} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -33,6 +35,7 @@
|
|||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
|
@ -57,6 +60,8 @@ typedef struct _AudinPulseDevice
|
|||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
} AudinPulseDevice;
|
||||
|
||||
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
|
@ -84,19 +89,24 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
|
|||
}
|
||||
}
|
||||
|
||||
static BOOL audin_pulse_connect(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
|
@ -104,7 +114,7 @@ static BOOL audin_pulse_connect(IAudinDevice* device)
|
|||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
|
@ -115,31 +125,27 @@ static BOOL audin_pulse_connect(IAudinDevice* device)
|
|||
{
|
||||
WLog_ERR(TAG, "bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
pa_context_disconnect(pulse->context);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if (state == PA_CONTEXT_READY)
|
||||
{
|
||||
DEBUG_DVC("connected");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
return FALSE;
|
||||
}
|
||||
DEBUG_DVC("connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_pulse_free(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_free(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
if (!pulse)
|
||||
return;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
|
@ -157,6 +163,8 @@ static void audin_pulse_free(IAudinDevice* device)
|
|||
}
|
||||
freerdp_dsp_context_free(pulse->dsp_context);
|
||||
free(pulse);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
|
@ -201,14 +209,19 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
pa_sample_spec sample_spec = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse->context)
|
||||
return;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (FramesPerPacket > 0)
|
||||
{
|
||||
|
@ -252,6 +265,7 @@ static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UI
|
|||
pulse->sample_spec = sample_spec;
|
||||
pulse->format = format->wFormatTag;
|
||||
pulse->block_size = format->nBlockAlign;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
|
@ -283,12 +297,12 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
|
|||
{
|
||||
int frames;
|
||||
int cframes;
|
||||
BOOL ret;
|
||||
const void* data;
|
||||
const BYTE* src;
|
||||
int encoded_size;
|
||||
BYTE* encoded_data;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
/* There is a race condition here where we may receive this callback
|
||||
* before the buffer has been set up in the main code. It's probably
|
||||
|
@ -319,9 +333,13 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
|
|||
{
|
||||
if (pulse->format == 0x11)
|
||||
{
|
||||
pulse->dsp_context->encode_ima_adpcm(pulse->dsp_context,
|
||||
if (!pulse->dsp_context->encode_ima_adpcm(pulse->dsp_context,
|
||||
pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
|
||||
pulse->sample_spec.channels, pulse->block_size);
|
||||
pulse->sample_spec.channels, pulse->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
encoded_data = pulse->dsp_context->adpcm_buffer;
|
||||
encoded_size = pulse->dsp_context->adpcm_size;
|
||||
}
|
||||
|
@ -334,9 +352,9 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
|
|||
DEBUG_DVC("encoded %d [%d] to %d [%X]",
|
||||
pulse->buffer_frames, pulse->bytes_per_frame, encoded_size,
|
||||
pulse->format);
|
||||
ret = pulse->receive(encoded_data, encoded_size, pulse->user_data);
|
||||
error = pulse->receive(encoded_data, encoded_size, pulse->user_data);
|
||||
pulse->buffer_frames = 0;
|
||||
if (!ret)
|
||||
if (!error)
|
||||
break;
|
||||
}
|
||||
src += cframes * pulse->bytes_per_frame;
|
||||
|
@ -344,17 +362,23 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
|
|||
}
|
||||
|
||||
pa_stream_drop(stream);
|
||||
|
||||
if (error && pulse->rdpcontext)
|
||||
setChannelError(pulse->rdpcontext, error, "audin_oss_thread_func reported an error");
|
||||
}
|
||||
|
||||
|
||||
static void audin_pulse_close(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_close(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return;
|
||||
|
||||
DEBUG_DVC("");
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
|
@ -370,20 +394,24 @@ static void audin_pulse_close(IAudinDevice* device)
|
|||
pulse->buffer = NULL;
|
||||
pulse->buffer_frames = 0;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
/**
|
||||
* 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_buffer_attr buffer_attr = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse->context)
|
||||
return;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (!pulse->sample_spec.rate || pulse->stream)
|
||||
return;
|
||||
|
||||
DEBUG_DVC("");
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
pulse->buffer = NULL;
|
||||
pulse->receive = receive;
|
||||
|
@ -397,7 +425,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_DVC("pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return;
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
|
||||
pa_stream_set_state_callback(pulse->stream,
|
||||
|
@ -417,7 +445,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return;
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
|
@ -427,41 +455,45 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
|||
break;
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
audin_pulse_close(device);
|
||||
WLog_ERR(TAG, "bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if (state == PA_STREAM_READY)
|
||||
{
|
||||
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
|
||||
pulse->buffer = malloc(pulse->bytes_per_frame * pulse->frames_per_packet);
|
||||
ZeroMemory(pulse->buffer, pulse->bytes_per_frame * pulse->frames_per_packet);
|
||||
pulse->buffer_frames = 0;
|
||||
DEBUG_DVC("connected");
|
||||
}
|
||||
else
|
||||
{
|
||||
audin_pulse_close(device);
|
||||
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
|
||||
pulse->buffer = calloc(1, pulse->bytes_per_frame * pulse->frames_per_packet);
|
||||
if (!pulse->buffer) {
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
pulse->buffer_frames = 0;
|
||||
DEBUG_DVC("connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_pulse_args[] =
|
||||
{
|
||||
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static void audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* args)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_pulse_args, flags, pulse, NULL, NULL);
|
||||
|
||||
|
@ -474,47 +506,77 @@ static void audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
|||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "audio-dev")
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
pulse->device_name = _strdup(arg->Value);
|
||||
if (!pulse->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry pulse_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinPulseDevice* pulse;
|
||||
UINT error;
|
||||
|
||||
pulse = (AudinPulseDevice*) malloc(sizeof(AudinPulseDevice));
|
||||
ZeroMemory(pulse, sizeof(AudinPulseDevice));
|
||||
pulse = (AudinPulseDevice*) calloc(1, sizeof(AudinPulseDevice));
|
||||
if (!pulse)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
audin_pulse_parse_addin_args(pulse, args);
|
||||
if ((error = audin_pulse_parse_addin_args(pulse, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_pulse_parse_addin_args failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->dsp_context = freerdp_dsp_context_new();
|
||||
if (!pulse->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
|
||||
audin_pulse_free((IAudinDevice*) pulse);
|
||||
return 1;
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||
|
@ -522,20 +584,27 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
|
|||
if (!pulse->context)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_new failed");
|
||||
audin_pulse_free((IAudinDevice*) pulse);
|
||||
return 1;
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
|
||||
|
||||
if (!audin_pulse_connect((IAudinDevice*) pulse))
|
||||
if ((error = audin_pulse_connect((IAudinDevice*) pulse)))
|
||||
{
|
||||
audin_pulse_free((IAudinDevice*) pulse);
|
||||
return 1;
|
||||
WLog_ERR(TAG, "audin_pulse_connect failed");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
audin_pulse_free((IAudinDevice*)pulse);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,13 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
|||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winmm.lib)
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr winmm.lib)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/winmm")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -31,6 +33,7 @@
|
|||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
|
@ -49,6 +52,7 @@ typedef struct _AudinWinmmDevice
|
|||
UINT32 ppwfx_size;
|
||||
UINT32 cFormats;
|
||||
UINT32 frames_per_packet;
|
||||
rdpContext* rdpcontext;
|
||||
} AudinWinmmDevice;
|
||||
|
||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||
|
@ -56,6 +60,8 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
|||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
|
||||
PWAVEHDR pWaveHdr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
MMRESULT mmResult;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
|
@ -69,8 +75,11 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
|||
if (pWaveHdr->dwBytesRecorded
|
||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
{
|
||||
winmm->receive(pWaveHdr->lpData, pWaveHdr->dwBytesRecorded, winmm->user_data);
|
||||
waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
if ((error = winmm->receive(pWaveHdr->lpData, pWaveHdr->dwBytesRecorded, winmm->user_data)))
|
||||
break;
|
||||
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -81,6 +90,8 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (error && winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||
}
|
||||
|
||||
static DWORD audin_winmm_thread_func(void* arg)
|
||||
|
@ -89,12 +100,16 @@ static DWORD audin_winmm_thread_func(void* arg)
|
|||
char *buffer;
|
||||
int size, i;
|
||||
WAVEHDR waveHdr[4];
|
||||
DWORD status;
|
||||
MMRESULT rc;
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
||||
{
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -103,40 +118,84 @@ static DWORD audin_winmm_thread_func(void* arg)
|
|||
for (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;
|
||||
if (MMSYSERR_NOERROR != waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
|
||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInPrepareHeader failed.");
|
||||
DEBUG_DVC("waveInPrepareHeader failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
if (MMSYSERR_NOERROR != waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
|
||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInAddBuffer failed.");
|
||||
DEBUG_DVC("waveInAddBuffer failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
}
|
||||
waveInStart(winmm->hWaveIn);
|
||||
|
||||
WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
rc = waveInStart(winmm->hWaveIn);
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInStart failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
waveInStop(winmm->hWaveIn);
|
||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
DEBUG_DVC("WaitForSingleObject failed.");
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInReset(winmm->hWaveIn);
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInReset failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInUnprepareHeader failed.");
|
||||
DEBUG_DVC("waveInUnprepareHeader failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
free(waveHdr[i].lpData);
|
||||
}
|
||||
|
||||
waveInClose(winmm->hWaveIn);
|
||||
rc = waveInClose(winmm->hWaveIn);
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInClose failed. %d", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
winmm->hWaveIn = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audin_winmm_free(IAudinDevice* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_free(IAudinDevice* device)
|
||||
{
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
@ -149,17 +208,31 @@ static void audin_winmm_free(IAudinDevice* device)
|
|||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_winmm_close(IAudinDevice* device)
|
||||
/**
|
||||
* 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;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
SetEvent(winmm->stopEvent);
|
||||
|
||||
WaitForSingleObject(winmm->thread, INFINITE);
|
||||
status = WaitForSingleObject(winmm->thread, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(winmm->thread);
|
||||
CloseHandle(winmm->stopEvent);
|
||||
|
@ -168,9 +241,16 @@ static void audin_winmm_close(IAudinDevice* device)
|
|||
winmm->stopEvent = NULL;
|
||||
winmm->receive = NULL;
|
||||
winmm->user_data = NULL;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
@ -187,6 +267,7 @@ static void audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UI
|
|||
break;
|
||||
}
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
|
@ -196,6 +277,8 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* form
|
|||
BYTE *data;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
if (!pwfx)
|
||||
return FALSE;
|
||||
pwfx->cbSize = format->cbSize;
|
||||
pwfx->wFormatTag = format->wFormatTag;
|
||||
pwfx->nChannels = format->nChannels;
|
||||
|
@ -213,46 +296,72 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* form
|
|||
{
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
PWAVEFORMATEX *tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
if (!tmp_ppwfx)
|
||||
return FALSE;
|
||||
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
|
||||
return 1;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
free(pwfx);
|
||||
return 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
/**
|
||||
* 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;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
winmm->receive = receive;
|
||||
winmm->user_data = user_data;
|
||||
|
||||
winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
winmm->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) audin_winmm_thread_func, winmm, 0, NULL);
|
||||
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(winmm->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(winmm->stopEvent);
|
||||
winmm->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
|
||||
{
|
||||
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static void audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_winmm_args, flags, winmm, NULL, NULL);
|
||||
|
||||
|
@ -265,45 +374,92 @@ static void audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
|||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "audio-dev")
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
winmm->device_name = _strdup(arg->Value);
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinWinmmDevice* winmm;
|
||||
UINT error;
|
||||
|
||||
winmm = (AudinWinmmDevice*) malloc(sizeof(AudinWinmmDevice));
|
||||
ZeroMemory(winmm, sizeof(AudinWinmmDevice));
|
||||
winmm = (AudinWinmmDevice*) calloc(1, sizeof(AudinWinmmDevice));
|
||||
if (!winmm)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
audin_winmm_parse_addin_args(winmm, args);
|
||||
if ((error = audin_winmm_parse_addin_args(winmm, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_winmm_parse_addin_args failed with error %d!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
winmm->device_name = _strdup("default");
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
if (!winmm->ppwfx)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm);
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %d!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,4 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Server Audio Input Virtual Channel
|
||||
*
|
||||
* Copyright 2012 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.
|
||||
|
@ -62,12 +64,22 @@ typedef struct _audin_server
|
|||
|
||||
} audin_server;
|
||||
|
||||
static void audin_server_select_format(audin_server_context* context, int client_format_index)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_select_format(audin_server_context* context,
|
||||
int client_format_index)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
|
||||
if (client_format_index >= context->num_client_formats)
|
||||
return;
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"error in protocol: client_format_index >= context->num_client_formats!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
context->selected_client_format = client_format_index;
|
||||
|
||||
|
@ -75,50 +87,87 @@ static void audin_server_select_format(audin_server_context* context, int client
|
|||
{
|
||||
/* TODO: send MSG_SNDIN_FORMATCHANGE */
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_server_send_version(audin_server* audin, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_send_version(audin_server* audin, wStream* s)
|
||||
{
|
||||
ULONG written;
|
||||
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_VERSION);
|
||||
Stream_Write_UINT32(s, 1); /* Version (4 bytes) */
|
||||
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
{
|
||||
UINT32 Version;
|
||||
|
||||
if (length < 4)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %d",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, Version);
|
||||
|
||||
if (Version < 1)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "expected Version > 0 but got %d", Version);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
{
|
||||
int i;
|
||||
UINT32 nAvgBytesPerSec;
|
||||
ULONG written;
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_FORMATS);
|
||||
Stream_Write_UINT32(s, audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(s, 0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.num_server_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */
|
||||
|
||||
for (i = 0; i < audin->context.num_server_formats; i++)
|
||||
{
|
||||
nAvgBytesPerSec = audin->context.server_formats[i].nSamplesPerSec *
|
||||
audin->context.server_formats[i].nChannels *
|
||||
audin->context.server_formats[i].wBitsPerSample / 8;
|
||||
audin->context.server_formats[i].nChannels *
|
||||
audin->context.server_formats[i].wBitsPerSample / 8;
|
||||
|
||||
Stream_EnsureRemainingCapacity(s, 18);
|
||||
if (!Stream_EnsureRemainingCapacity(s, 18))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].wFormatTag);
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].nChannels);
|
||||
|
@ -130,31 +179,56 @@ static void audin_server_send_formats(audin_server* audin, wStream* s)
|
|||
|
||||
if (audin->context.server_formats[i].cbSize)
|
||||
{
|
||||
Stream_EnsureRemainingCapacity(s, audin->context.server_formats[i].cbSize);
|
||||
if (!Stream_EnsureRemainingCapacity(s, audin->context.server_formats[i].cbSize))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(s, audin->context.server_formats[i].data,
|
||||
audin->context.server_formats[i].cbSize);
|
||||
audin->context.server_formats[i].cbSize);
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
{
|
||||
int i;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (length < 8)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing rec formats: expected at least 8 bytes, got %d",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Read_UINT32(s,
|
||||
audin->context.num_client_formats); /* NumFormats (4 bytes) */
|
||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
|
||||
length -= 8;
|
||||
|
||||
if (audin->context.num_client_formats <= 0)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "num_client_formats expected > 0 but got %d",
|
||||
audin->context.num_client_formats);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
audin->context.client_formats = malloc(audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
|
||||
ZeroMemory(audin->context.client_formats, audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
|
||||
audin->context.client_formats = calloc(audin->context.num_client_formats,
|
||||
sizeof(AUDIO_FORMAT));
|
||||
|
||||
if (!audin->context.client_formats)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
for (i = 0; i < audin->context.num_client_formats; i++)
|
||||
{
|
||||
|
@ -162,7 +236,8 @@ static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 le
|
|||
{
|
||||
free(audin->context.client_formats);
|
||||
audin->context.client_formats = NULL;
|
||||
return FALSE;
|
||||
WLog_ERR(TAG, "expected length at least 18, but got %d", length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].wFormatTag);
|
||||
|
@ -179,24 +254,37 @@ static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 le
|
|||
}
|
||||
}
|
||||
|
||||
IFCALL(audin->context.Opening, &audin->context);
|
||||
IFCALLRET(audin->context.Opening, success, &audin->context);
|
||||
|
||||
return TRUE;
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.Opening failed with error %lu", success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void audin_server_send_open(audin_server* audin, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_send_open(audin_server* audin, wStream* s)
|
||||
{
|
||||
ULONG written;
|
||||
|
||||
if (audin->context.selected_client_format < 0)
|
||||
return;
|
||||
{
|
||||
WLog_ERR(TAG, "audin->context.selected_client_format = %d",
|
||||
audin->context.selected_client_format);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
audin->opened = TRUE;
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_OPEN);
|
||||
Stream_Write_UINT32(s, audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */
|
||||
Stream_Write_UINT32(s, audin->context.selected_client_format); /* initialFormat (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */
|
||||
Stream_Write_UINT32(s,
|
||||
audin->context.selected_client_format); /* initialFormat (4 bytes) */
|
||||
/*
|
||||
* [MS-RDPEAI] 3.2.5.1.6
|
||||
* The second format specify the format that SHOULD be used to capture data from
|
||||
|
@ -209,25 +297,44 @@ static void audin_server_send_open(audin_server* audin, wStream* s)
|
|||
Stream_Write_UINT16(s, 4); /* nBlockAlign */
|
||||
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
|
||||
Stream_Write_UINT16(s, 0); /* cbSize */
|
||||
|
||||
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static BOOL audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
{
|
||||
UINT32 Result;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (length < 4)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %d",
|
||||
length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, Result);
|
||||
IFCALLRET(audin->context.OpenResult, success, &audin->context, Result);
|
||||
|
||||
IFCALL(audin->context.OpenResult, &audin->context, Result);
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.OpenResult failed with error %lu", success);
|
||||
|
||||
return TRUE;
|
||||
return success;
|
||||
}
|
||||
|
||||
static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
{
|
||||
AUDIO_FORMAT* format;
|
||||
int sbytes_per_sample;
|
||||
|
@ -235,16 +342,21 @@ static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 lengt
|
|||
BYTE* src;
|
||||
int size;
|
||||
int frames;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (audin->context.selected_client_format < 0)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "audin->context.selected_client_format = %d",
|
||||
audin->context.selected_client_format);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
format = &audin->context.client_formats[audin->context.selected_client_format];
|
||||
|
||||
if (format->wFormatTag == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
audin->dsp_context->decode_ms_adpcm(audin->dsp_context,
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
size = audin->dsp_context->adpcm_size;
|
||||
src = audin->dsp_context->adpcm_buffer;
|
||||
sbytes_per_sample = 2;
|
||||
|
@ -253,7 +365,7 @@ static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 lengt
|
|||
else if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
audin->dsp_context->decode_ima_adpcm(audin->dsp_context,
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
size = audin->dsp_context->adpcm_size;
|
||||
src = audin->dsp_context->adpcm_buffer;
|
||||
sbytes_per_sample = 2;
|
||||
|
@ -267,22 +379,26 @@ static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 lengt
|
|||
sbytes_per_frame = format->nChannels * sbytes_per_sample;
|
||||
}
|
||||
|
||||
if (format->nSamplesPerSec == audin->context.dst_format.nSamplesPerSec && format->nChannels == audin->context.dst_format.nChannels)
|
||||
if (format->nSamplesPerSec == audin->context.dst_format.nSamplesPerSec
|
||||
&& format->nChannels == audin->context.dst_format.nChannels)
|
||||
{
|
||||
frames = size / sbytes_per_frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
audin->dsp_context->resample(audin->dsp_context, src, sbytes_per_sample,
|
||||
format->nChannels, format->nSamplesPerSec, size / sbytes_per_frame,
|
||||
audin->context.dst_format.nChannels, audin->context.dst_format.nSamplesPerSec);
|
||||
format->nChannels, format->nSamplesPerSec, size / sbytes_per_frame,
|
||||
audin->context.dst_format.nChannels, audin->context.dst_format.nSamplesPerSec);
|
||||
frames = audin->dsp_context->resampled_frames;
|
||||
src = audin->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
IFCALL(audin->context.ReceiveSamples, &audin->context, src, frames);
|
||||
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, src, frames);
|
||||
|
||||
return TRUE;
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %lu", success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void* audin_server_thread_func(void* arg)
|
||||
|
@ -296,18 +412,27 @@ static void* audin_server_thread_func(void* arg)
|
|||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
audin_server* audin = (audin_server*) arg;
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = NULL;
|
||||
freerdp_channel_init_thread_context(audin->context.rdpcontext);
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = audin->stopEvent;
|
||||
|
@ -317,14 +442,26 @@ static void* audin_server_thread_func(void* arg)
|
|||
|
||||
while (1)
|
||||
{
|
||||
if (WaitForMultipleObjects(nCount, events, FALSE, 100) == WAIT_OBJECT_0)
|
||||
break;
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
||||
100)) == WAIT_OBJECT_0)
|
||||
goto out;
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE)
|
||||
break;
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady,
|
||||
&buffer, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ready = *((BOOL*) buffer);
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
|
@ -332,28 +469,56 @@ static void* audin_server_thread_func(void* arg)
|
|||
}
|
||||
|
||||
s = Stream_New(NULL, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ready)
|
||||
{
|
||||
audin_server_send_version(audin, s);
|
||||
if ((error = audin_server_send_version(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_version failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
while (ready)
|
||||
{
|
||||
if (WaitForMultipleObjects(nCount, events, FALSE, INFINITE) == WAIT_OBJECT_0)
|
||||
if ((status = WaitForMultipleObjects(nCount, events, FALSE,
|
||||
INFINITE)) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (!WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
Stream_EnsureRemainingCapacity(s, BytesReturned);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
break;
|
||||
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, (PCHAR) Stream_Buffer(s),
|
||||
Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -363,41 +528,76 @@ static void* audin_server_thread_func(void* arg)
|
|||
switch (MessageId)
|
||||
{
|
||||
case MSG_SNDIN_VERSION:
|
||||
if (audin_server_recv_version(audin, s, BytesReturned))
|
||||
audin_server_send_formats(audin, s);
|
||||
if ((error = audin_server_recv_version(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_version failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
if ((error = audin_server_send_formats(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_formats failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MSG_SNDIN_FORMATS:
|
||||
if (audin_server_recv_formats(audin, s, BytesReturned))
|
||||
audin_server_send_open(audin, s);
|
||||
if ((error = audin_server_recv_formats(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_formats failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
if ((error = audin_server_send_open(audin, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_send_open failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MSG_SNDIN_OPEN_REPLY:
|
||||
audin_server_recv_open_reply(audin, s, BytesReturned);
|
||||
if ((error = audin_server_recv_open_reply(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_open_reply failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MSG_SNDIN_DATA_INCOMING:
|
||||
break;
|
||||
|
||||
case MSG_SNDIN_DATA:
|
||||
audin_server_recv_data(audin, s, BytesReturned);
|
||||
if ((error = audin_server_recv_data(audin, s, BytesReturned)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_server_recv_data failed with error %lu!", error);
|
||||
goto out_capacity;
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case MSG_SNDIN_FORMATCHANGE:
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %d\n", MessageId);
|
||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %d", MessageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out_capacity:
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
out:
|
||||
WTSVirtualChannelClose(audin->audin_channel);
|
||||
audin->audin_channel = NULL;
|
||||
|
||||
if (error && audin->context.rdpcontext)
|
||||
setChannelError(audin->context.rdpcontext, error,
|
||||
"audin_server_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -409,30 +609,43 @@ static BOOL audin_server_open(audin_server_context* context)
|
|||
{
|
||||
PULONG pSessionId = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
|
||||
audin->SessionId = WTS_CURRENT_SESSION;
|
||||
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION,
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned))
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned))
|
||||
{
|
||||
audin->SessionId = (DWORD) *pSessionId;
|
||||
audin->SessionId = (DWORD) * pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
}
|
||||
|
||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId,
|
||||
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!audin->audin_channel)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
audin->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_server_thread_func, (void*) audin, 0, NULL);
|
||||
if (!(audin->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) audin_server_thread_func, (void*) audin, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(audin->stopEvent);
|
||||
audin->stopEvent = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "thread already running!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -443,7 +656,13 @@ static BOOL audin_server_close(audin_server_context* context)
|
|||
if (audin->thread)
|
||||
{
|
||||
SetEvent(audin->stopEvent);
|
||||
WaitForSingleObject(audin->thread, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CloseHandle(audin->thread);
|
||||
CloseHandle(audin->stopEvent);
|
||||
audin->thread = NULL;
|
||||
|
@ -457,15 +676,19 @@ static BOOL audin_server_close(audin_server_context* context)
|
|||
}
|
||||
|
||||
audin->context.selected_client_format = -1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
{
|
||||
audin_server* audin;
|
||||
audin = (audin_server*)calloc(1, sizeof(audin_server));
|
||||
|
||||
audin = (audin_server *)calloc(1, sizeof(audin_server));
|
||||
if (!audin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audin->context.vcm = vcm;
|
||||
audin->context.selected_client_format = -1;
|
||||
|
@ -473,23 +696,26 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
|
|||
audin->context.SelectFormat = audin_server_select_format;
|
||||
audin->context.Open = audin_server_open;
|
||||
audin->context.Close = audin_server_close;
|
||||
|
||||
audin->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!audin->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
free(audin);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (audin_server_context*) audin;
|
||||
}
|
||||
|
||||
void audin_server_context_free(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
|
||||
audin_server_close(context);
|
||||
|
||||
if (audin->dsp_context)
|
||||
freerdp_dsp_context_free(audin->dsp_context);
|
||||
|
||||
if (audin->context.client_formats)
|
||||
free(audin->context.client_formats);
|
||||
|
||||
free(audin->context.client_formats);
|
||||
free(audin);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
|||
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern void ${ENTRY_POINT_NAME}();")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}();")
|
||||
endif()
|
||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -29,12 +31,16 @@
|
|||
#include <winpr/collections.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[];
|
||||
|
||||
void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table, const char* identifier)
|
||||
|
@ -83,18 +89,28 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
|||
{
|
||||
int i, j;
|
||||
DWORD nAddins;
|
||||
FREERDP_ADDIN* pAddin;
|
||||
FREERDP_ADDIN* pAddin = NULL;
|
||||
FREERDP_ADDIN** ppAddins = NULL;
|
||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
||||
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**) malloc(sizeof(FREERDP_ADDIN*) * 128);
|
||||
ppAddins = (FREERDP_ADDIN**) calloc(1, sizeof(FREERDP_ADDIN*) * 128);
|
||||
if (!ppAddins)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ppAddins[nAddins] = NULL;
|
||||
|
||||
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
|
||||
{
|
||||
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
|
||||
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
|
||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
|
||||
|
@ -108,8 +124,12 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
|||
|
||||
for (j = 0; subsystems[j].name != NULL; j++)
|
||||
{
|
||||
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
|
||||
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
|
||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
strcpy(pAddin->cSubsystem, subsystems[j].name);
|
||||
|
@ -123,9 +143,10 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
|||
}
|
||||
}
|
||||
|
||||
ppAddins[nAddins] = NULL;
|
||||
|
||||
return ppAddins;
|
||||
error_out:
|
||||
freerdp_channels_addin_list_free(ppAddins);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
|
||||
|
@ -153,25 +174,30 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||
|
||||
cchPattern = 128 + strlen(pszExtension) + 2;
|
||||
pszPattern = (LPSTR) malloc(cchPattern + 1);
|
||||
if (!pszPattern)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pszName && pszSubsystem && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client-%s-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client-?-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
||||
pszName, pszExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
||||
pszExtension);
|
||||
}
|
||||
|
||||
|
@ -179,6 +205,12 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||
|
||||
cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
|
||||
pszSearchPath = (LPSTR) malloc(cchSearchPath + 1);
|
||||
if (!pszSearchPath)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
free(pszPattern);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
|
||||
pszSearchPath[cchInstallPrefix] = '\0';
|
||||
|
@ -195,8 +227,12 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||
free(pszSearchPath);
|
||||
|
||||
nAddins = 0;
|
||||
ppAddins = (FREERDP_ADDIN**) malloc(sizeof(FREERDP_ADDIN*) * 128);
|
||||
ppAddins[nAddins] = NULL;
|
||||
ppAddins = (FREERDP_ADDIN**) calloc(1, sizeof(FREERDP_ADDIN*) * 128);
|
||||
if (!ppAddins)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
return ppAddins;
|
||||
|
@ -207,8 +243,12 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||
FREERDP_ADDIN* pAddin;
|
||||
|
||||
nDashes = 0;
|
||||
pAddin = (FREERDP_ADDIN*) malloc(sizeof(FREERDP_ADDIN));
|
||||
ZeroMemory(pAddin, sizeof(FREERDP_ADDIN));
|
||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
if (!pAddin)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
for (index = 0; FindData.cFileName[index]; index++)
|
||||
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
||||
|
@ -281,6 +321,9 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
|||
ppAddins[nAddins] = NULL;
|
||||
|
||||
return ppAddins;
|
||||
error_out:
|
||||
freerdp_channels_addin_list_free(ppAddins);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FREERDP_ADDIN** freerdp_channels_list_addins(LPSTR pszName, LPSTR pszSubsystem, LPSTR pszType, DWORD dwFlags)
|
||||
|
@ -297,6 +340,9 @@ void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
|
|||
{
|
||||
int index;
|
||||
|
||||
if (!ppAddins)
|
||||
return;
|
||||
|
||||
for (index = 0; ppAddins[index] != NULL; index++)
|
||||
free(ppAddins[index]);
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
|
|
@ -25,10 +25,6 @@ set(${MODULE_PREFIX}_SRCS
|
|||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* 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.
|
||||
|
@ -32,7 +34,12 @@
|
|||
#include "cliprdr_main.h"
|
||||
#include "cliprdr_format.h"
|
||||
|
||||
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
UINT32 index;
|
||||
UINT32 position;
|
||||
|
@ -43,12 +50,16 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
CLIPRDR_FORMAT* formats = NULL;
|
||||
CLIPRDR_FORMAT_LIST formatList;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!context->custom)
|
||||
return;
|
||||
|
||||
{
|
||||
WLog_ERR(TAG, "context->custom not set!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
|
||||
|
||||
|
||||
formatList.msgType = CB_FORMAT_LIST;
|
||||
formatList.msgFlags = msgFlags;
|
||||
formatList.dataLen = dataLen;
|
||||
|
@ -56,7 +67,7 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
index = 0;
|
||||
formatList.numFormats = 0;
|
||||
position = Stream_GetPosition(s);
|
||||
|
||||
|
||||
if (!formatList.dataLen)
|
||||
{
|
||||
/* empty format list */
|
||||
|
@ -66,35 +77,53 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
else if (!cliprdr->useLongFormatNames)
|
||||
{
|
||||
formatList.numFormats = (dataLen / 36);
|
||||
|
||||
|
||||
if ((formatList.numFormats * 36) != dataLen)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid short format list length: %d", dataLen);
|
||||
return;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
if (formatList.numFormats)
|
||||
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
|
||||
if (!formats)
|
||||
return;
|
||||
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
formatList.formats = formats;
|
||||
|
||||
|
||||
while (dataLen)
|
||||
{
|
||||
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
|
||||
dataLen -= 4;
|
||||
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
|
||||
/* 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 charaters - *without* terminating null !
|
||||
*/
|
||||
|
||||
if (asciiNames)
|
||||
{
|
||||
szFormatName = (char*) Stream_Pointer(s);
|
||||
|
||||
|
||||
if (szFormatName[0])
|
||||
{
|
||||
/* ensure null termination */
|
||||
formats[index].formatName = (char*) malloc(32 + 1);
|
||||
if (!formats[index].formatName)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
CopyMemory(formats[index].formatName, szFormatName, 32);
|
||||
formats[index].formatName[32] = '\0';
|
||||
}
|
||||
|
@ -102,14 +131,22 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
else
|
||||
{
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
|
||||
if (wszFormatName[0])
|
||||
{
|
||||
ConvertFromUnicode(CP_UTF8, 0, wszFormatName,
|
||||
16, &(formats[index].formatName), 0, NULL, NULL);
|
||||
/* ConvertFromUnicode always returns a null-terminated
|
||||
* string on success, even if the source string isn't.
|
||||
*/
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert short clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Stream_Seek(s, 32);
|
||||
dataLen -= 32;
|
||||
index++;
|
||||
|
@ -121,17 +158,17 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
{
|
||||
Stream_Seek(s, 4); /* formatId (4 bytes) */
|
||||
dataLen -= 4;
|
||||
|
||||
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
|
||||
if (!wszFormatName[0])
|
||||
formatNameLength = 0;
|
||||
else
|
||||
formatNameLength = _wcslen(wszFormatName);
|
||||
|
||||
|
||||
Stream_Seek(s, (formatNameLength + 1) * 2);
|
||||
dataLen -= ((formatNameLength + 1) * 2);
|
||||
|
||||
|
||||
formatList.numFormats++;
|
||||
}
|
||||
|
||||
|
@ -140,9 +177,12 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
|
||||
if (formatList.numFormats)
|
||||
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
|
||||
if (!formats)
|
||||
return;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
formatList.formats = formats;
|
||||
|
||||
|
@ -152,9 +192,9 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
dataLen -= 4;
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
|
||||
|
||||
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
||||
|
||||
|
||||
if (!wszFormatName[0])
|
||||
formatNameLength = 0;
|
||||
else
|
||||
|
@ -162,10 +202,15 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
|
||||
if (formatNameLength)
|
||||
{
|
||||
ConvertFromUnicode(CP_UTF8, 0, wszFormatName,
|
||||
-1, &(formats[index].formatName), 0, NULL, NULL);
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, -1,
|
||||
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to convert long clipboard format name");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Stream_Seek(s, (formatNameLength + 1) * 2);
|
||||
dataLen -= ((formatNameLength + 1) * 2);
|
||||
|
||||
|
@ -177,44 +222,72 @@ void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 data
|
|||
formatList.numFormats);
|
||||
|
||||
if (context->ServerFormatList)
|
||||
context->ServerFormatList(context, &formatList);
|
||||
|
||||
for (index = 0; index < formatList.numFormats; index++)
|
||||
{
|
||||
if (formats[index].formatName)
|
||||
free(formats[index].formatName);
|
||||
if ((error = context->ServerFormatList(context, &formatList)))
|
||||
WLog_ERR(TAG, "ServerFormatList failed with error %d", error);
|
||||
}
|
||||
|
||||
free(formats);
|
||||
error_out:
|
||||
if (formats)
|
||||
{
|
||||
for (index = 0; index < formatList.numFormats; index++)
|
||||
{
|
||||
free(formats[index].formatName);
|
||||
}
|
||||
|
||||
free(formats);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
|
||||
|
||||
if (!context->custom)
|
||||
return;
|
||||
{
|
||||
WLog_ERR(TAG, "context->custom not set!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
|
||||
formatListResponse.msgFlags = msgFlags;
|
||||
formatListResponse.dataLen = dataLen;
|
||||
|
||||
if (context->ServerFormatListResponse)
|
||||
context->ServerFormatListResponse(context, &formatListResponse);
|
||||
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatListResponse failed with error %lu!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
/**
|
||||
* 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;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
|
||||
|
||||
if (!context->custom)
|
||||
return;
|
||||
{
|
||||
WLog_ERR(TAG, "context->custom not set!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
|
||||
formatDataRequest.msgFlags = msgFlags;
|
||||
|
@ -222,33 +295,44 @@ void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UIN
|
|||
|
||||
Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
|
||||
if (context->ServerFormatDataRequest)
|
||||
context->ServerFormatDataRequest(context, &formatDataRequest);
|
||||
|
||||
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %lu!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
/**
|
||||
* 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;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
|
||||
|
||||
if (!context->custom)
|
||||
return;
|
||||
|
||||
{
|
||||
WLog_ERR(TAG, "context->custom not set!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
|
||||
formatDataResponse.msgFlags = msgFlags;
|
||||
formatDataResponse.dataLen = dataLen;
|
||||
formatDataResponse.requestedFormatData = NULL;
|
||||
|
||||
|
||||
if (dataLen)
|
||||
{
|
||||
formatDataResponse.requestedFormatData = (BYTE*) malloc(dataLen);
|
||||
Stream_Read(s, formatDataResponse.requestedFormatData, dataLen);
|
||||
}
|
||||
|
||||
if (context->ServerFormatDataResponse)
|
||||
context->ServerFormatDataResponse(context, &formatDataResponse);
|
||||
|
||||
free(formatDataResponse.requestedFormatData);
|
||||
formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
|
||||
|
||||
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %lu!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* 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.
|
||||
|
@ -21,9 +23,9 @@
|
|||
#ifndef __CLIPRDR_FORMAT_H
|
||||
#define __CLIPRDR_FORMAT_H
|
||||
|
||||
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags);
|
||||
|
||||
#endif /* __CLIPRDR_FORMAT_H */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* 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.
|
||||
|
@ -51,15 +52,12 @@ struct cliprdr_plugin
|
|||
};
|
||||
typedef struct cliprdr_plugin cliprdrPlugin;
|
||||
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen);
|
||||
void cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* data_out);
|
||||
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
||||
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
#define DEBUG_CLIPRDR(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_CLIPRDR(fmt, ...) do { } while (0)
|
||||
#define DEBUG_CLIPRDR(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* __CLIPRDR_MAIN_H */
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
LIBRARY "cliprdr"
|
||||
EXPORTS
|
||||
VirtualChannelEntry @1
|
|
@ -23,10 +23,6 @@ set(${MODULE_PREFIX}_SRCS
|
|||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,11 +40,6 @@ struct _cliprdr_server_private
|
|||
void* ChannelHandle;
|
||||
HANDLE ChannelEvent;
|
||||
|
||||
BOOL useLongFormatNames;
|
||||
BOOL streamFileClipEnabled;
|
||||
BOOL fileClipNoFilePaths;
|
||||
BOOL canLockClipData;
|
||||
|
||||
wStream* s;
|
||||
char* temporaryDirectory;
|
||||
};
|
||||
|
|
|
@ -27,10 +27,13 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
|||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* 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.
|
||||
|
@ -71,9 +74,14 @@ struct _DISP_PLUGIN
|
|||
};
|
||||
typedef struct _DISP_PLUGIN DISP_PLUGIN;
|
||||
|
||||
int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
int status;
|
||||
UINT status;
|
||||
wStream* s;
|
||||
UINT32 type;
|
||||
UINT32 index;
|
||||
|
@ -91,6 +99,12 @@ int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback
|
|||
|
||||
s = Stream_New(NULL, length);
|
||||
|
||||
if(!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, type); /* Type (4 bytes) */
|
||||
Stream_Write_UINT32(s, length); /* Length (4 bytes) */
|
||||
|
||||
|
@ -101,7 +115,7 @@ int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback
|
|||
|
||||
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
|
||||
|
||||
//WLog_ERR(TAG, "NumMonitors: %d\n", NumMonitors);
|
||||
//WLog_ERR(TAG, "NumMonitors: %d", NumMonitors);
|
||||
|
||||
for (index = 0; index < NumMonitors; index++)
|
||||
{
|
||||
|
@ -134,14 +148,14 @@ int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback
|
|||
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||
|
||||
#if 0
|
||||
WLog_DBG(TAG, "\t: Flags: 0x%04X\n", Monitors[index].Flags);
|
||||
WLog_DBG(TAG, "\t: Left: %d\n", Monitors[index].Left);
|
||||
WLog_DBG(TAG, "\t: Top: %d\n", Monitors[index].Top);
|
||||
WLog_DBG(TAG, "\t: Width: %d\n", Monitors[index].Width);
|
||||
WLog_DBG(TAG, "\t: Height: %d\n", Monitors[index].Height);
|
||||
WLog_DBG(TAG, "\t: PhysicalWidth: %d\n", Monitors[index].PhysicalWidth);
|
||||
WLog_DBG(TAG, "\t: PhysicalHeight: %d\n", Monitors[index].PhysicalHeight);
|
||||
WLog_DBG(TAG, "\t: Orientation: %d\n", Monitors[index].Orientation);
|
||||
WLog_DBG(TAG, "\t: Flags: 0x%04X", Monitors[index].Flags);
|
||||
WLog_DBG(TAG, "\t: Left: %d", Monitors[index].Left);
|
||||
WLog_DBG(TAG, "\t: Top: %d", Monitors[index].Top);
|
||||
WLog_DBG(TAG, "\t: Width: %d", Monitors[index].Width);
|
||||
WLog_DBG(TAG, "\t: Height: %d", Monitors[index].Height);
|
||||
WLog_DBG(TAG, "\t: PhysicalWidth: %d", Monitors[index].PhysicalWidth);
|
||||
WLog_DBG(TAG, "\t: PhysicalHeight: %d", Monitors[index].PhysicalHeight);
|
||||
WLog_DBG(TAG, "\t: Orientation: %d", Monitors[index].Orientation);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -154,74 +168,94 @@ int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback
|
|||
return status;
|
||||
}
|
||||
|
||||
int disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
DISP_PLUGIN* disp;
|
||||
|
||||
disp = (DISP_PLUGIN*) callback->plugin;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 12)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "not enought remaining data");
|
||||
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) */
|
||||
//WLog_ERR(TAG, "DisplayControlCapsPdu: MaxNumMonitors: %d MaxMonitorWidth: %d MaxMonitorHeight: %d\n",
|
||||
//WLog_ERR(TAG, "DisplayControlCapsPdu: MaxNumMonitors: %d MaxMonitorWidth: %d MaxMonitorHeight: %d",
|
||||
// disp->MaxNumMonitors, disp->MaxMonitorWidth, disp->MaxMonitorHeight);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
int disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT32 type;
|
||||
UINT32 length;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "not enought remaining data");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, type); /* Type (4 bytes) */
|
||||
Stream_Read_UINT32(s, length); /* Length (4 bytes) */
|
||||
|
||||
//WLog_ERR(TAG, "Type: %d Length: %d\n", type, length);
|
||||
//WLog_ERR(TAG, "Type: %d Length: %d", type, length);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DISPLAY_CONTROL_PDU_TYPE_CAPS:
|
||||
disp_recv_display_control_caps_pdu(callback, s);
|
||||
break;
|
||||
return disp_recv_display_control_caps_pdu(callback, s);
|
||||
|
||||
default:
|
||||
break;
|
||||
WLog_ERR(TAG, "Type %d not recognized!", type);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
{
|
||||
int status = 0;
|
||||
DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
status = disp_recv_pdu(callback, data);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
{
|
||||
DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
if (callback)
|
||||
{
|
||||
free(callback);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return disp_recv_pdu(callback, data);
|
||||
}
|
||||
|
||||
static int disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
free(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
DISP_CHANNEL_CALLBACK* callback;
|
||||
|
@ -230,7 +264,10 @@ static int disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallbac
|
|||
callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
callback->iface.OnDataReceived = disp_on_data_received;
|
||||
callback->iface.OnClose = disp_on_close;
|
||||
|
@ -241,18 +278,26 @@ static int disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallbac
|
|||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
int status;
|
||||
UINT status;
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
|
||||
|
||||
disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK));
|
||||
|
||||
if (!disp->listener_callback)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
disp->listener_callback->iface.OnNewChannelConnection = disp_on_new_channel_connection;
|
||||
disp->listener_callback->plugin = pPlugin;
|
||||
|
@ -266,39 +311,51 @@ static int disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager
|
|||
return status;
|
||||
}
|
||||
|
||||
static int disp_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
|
||||
|
||||
if (disp)
|
||||
{
|
||||
free(disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
free(disp->listener_callback);
|
||||
free(disp->iface.pInterface);
|
||||
free(pPlugin);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel Client Interface
|
||||
*/
|
||||
|
||||
int disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
DISP_PLUGIN* disp = (DISP_PLUGIN*) context->handle;
|
||||
DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback;
|
||||
|
||||
disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
||||
|
||||
return 1;
|
||||
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry disp_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
int error = 0;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DISP_PLUGIN* disp;
|
||||
DispClientContext* context;
|
||||
|
||||
|
@ -309,7 +366,10 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||
disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN));
|
||||
|
||||
if (!disp)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
disp->iface.Initialize = disp_plugin_initialize;
|
||||
disp->iface.Connected = NULL;
|
||||
|
@ -320,8 +380,9 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(disp);
|
||||
return -1;
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context->handle = (void*) disp;
|
||||
|
@ -336,6 +397,11 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "could not get disp Plugin.");
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -34,5 +36,7 @@
|
|||
#define DISPLAY_CONTROL_PDU_TYPE_CAPS 0x00000005
|
||||
#define DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002
|
||||
|
||||
#define TAG CHANNELS_TAG("disp.client")
|
||||
|
||||
#endif /* FREERDP_CHANNEL_DISP_CLIENT_MAIN_H */
|
||||
|
||||
|
|
|
@ -23,6 +23,5 @@ set(${MODULE_PREFIX}_SRCS
|
|||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -31,6 +33,7 @@
|
|||
#include <freerdp/addin.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/drdynvc.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct drdynvc_plugin drdynvcPlugin;
|
||||
|
||||
|
@ -129,11 +132,10 @@ struct drdynvc_plugin
|
|||
int PriorityCharge1;
|
||||
int PriorityCharge2;
|
||||
int PriorityCharge3;
|
||||
int channel_error;
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
|
||||
IWTSVirtualChannelManager* channel_mgr;
|
||||
};
|
||||
|
||||
int drdynvc_write_data(drdynvcPlugin* plugin, UINT32 ChannelId, BYTE* data, UINT32 data_size);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -24,11 +26,16 @@
|
|||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "drdynvc_main.h"
|
||||
|
||||
#define TAG CHANNELS_TAG("drdynvc.server")
|
||||
|
||||
|
||||
static void* drdynvc_server_thread(void* arg)
|
||||
{
|
||||
#if 0
|
||||
wStream* s;
|
||||
DWORD status;
|
||||
DWORD nCount;
|
||||
|
@ -37,16 +44,23 @@ static void* drdynvc_server_thread(void* arg)
|
|||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned;
|
||||
DrdynvcServerContext* context;
|
||||
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
context = (DrdynvcServerContext*) arg;
|
||||
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = NULL;
|
||||
|
||||
freerdp_channel_init_thread_context(context->rdpcontext);
|
||||
s = Stream_New(NULL, 4096);
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
ExitThread((DWORD) CHANNEL_RC_NO_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle,
|
||||
&buffer, &BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
@ -64,64 +78,119 @@ static void* drdynvc_server_thread(void* arg)
|
|||
|
||||
if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, NULL, 0,
|
||||
&BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
break;
|
||||
}
|
||||
|
||||
WTSVirtualChannelRead(context->priv->ChannelHandle, 0, NULL, 0, &BytesReturned);
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
Stream_EnsureRemainingCapacity(s, BytesReturned);
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), 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 NULL;
|
||||
}
|
||||
|
||||
static int drdynvc_server_start(DrdynvcServerContext* context)
|
||||
/**
|
||||
* 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");
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
||||
WTS_CURRENT_SESSION, "drdynvc");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!(context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
context->priv->Thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) drdynvc_server_thread, (void*) context, 0, NULL);
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) drdynvc_server_thread, (void*) context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
{
|
||||
UINT error;
|
||||
SetEvent(context->priv->StopEvent);
|
||||
|
||||
WaitForSingleObject(context->priv->Thread, INFINITE);
|
||||
CloseHandle(context->priv->Thread);
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
CloseHandle(context->priv->Thread);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DrdynvcServerContext* context;
|
||||
|
||||
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 NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -131,11 +200,7 @@ void drdynvc_server_context_free(DrdynvcServerContext* context)
|
|||
{
|
||||
if (context)
|
||||
{
|
||||
if (context->priv)
|
||||
{
|
||||
free(context->priv);
|
||||
}
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -222,7 +222,7 @@ static DIR *opendir(const char *dirname)
|
|||
* allows rewinddir() to function correctly when the current working
|
||||
* directory is changed between opendir() and rewinddir().
|
||||
*/
|
||||
if (GetFullPathNameA (dirname, MAX_PATH, dirp->patt, NULL)) {
|
||||
if (GetFullPathNameA(dirname, MAX_PATH, dirp->patt, NULL)) {
|
||||
char *p;
|
||||
|
||||
/* append the search pattern "\\*\0" to the directory name */
|
||||
|
@ -234,7 +234,7 @@ static DIR *opendir(const char *dirname)
|
|||
*p = '\0';
|
||||
|
||||
/* open directory stream and retrieve the first entry */
|
||||
dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->find_data);
|
||||
dirp->search_handle = FindFirstFileA(dirp->patt, &dirp->find_data);
|
||||
if (dirp->search_handle != INVALID_HANDLE_VALUE) {
|
||||
/* a directory entry is now waiting in memory */
|
||||
dirp->cached = 1;
|
||||
|
@ -286,7 +286,7 @@ static struct dirent *readdir(DIR *dirp)
|
|||
return NULL;
|
||||
}
|
||||
if (FindNextFileA (dirp->search_handle, &dirp->find_data) == FALSE) {
|
||||
/* the very last entry has been processed or an error occured */
|
||||
/* the very last entry has been processed or an error occurred */
|
||||
FindClose (dirp->search_handle);
|
||||
dirp->search_handle = INVALID_HANDLE_VALUE;
|
||||
return NULL;
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
* 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.
|
||||
|
@ -39,6 +43,7 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
|
@ -57,6 +62,8 @@
|
|||
#ifdef _WIN32
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
#include <Shlwapi.h>
|
||||
#else
|
||||
#include <winpr/path.h>
|
||||
#endif
|
||||
|
||||
#include "drive_file.h"
|
||||
|
@ -96,6 +103,11 @@ static char* drive_file_combine_fullpath(const char* base_path, const char* path
|
|||
char* fullpath;
|
||||
|
||||
fullpath = (char*) malloc(strlen(base_path) + strlen(path) + 1);
|
||||
if (!fullpath)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
strcpy(fullpath, base_path);
|
||||
strcat(fullpath, path);
|
||||
drive_file_fix_path(fullpath);
|
||||
|
@ -127,6 +139,11 @@ static BOOL drive_file_remove_dir(const char* path)
|
|||
}
|
||||
|
||||
p = (char*) malloc(strlen(path) + strlen(pdirent->d_name) + 2);
|
||||
if (!p)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return FALSE;
|
||||
}
|
||||
sprintf(p, "%s/%s", path, pdirent->d_name);
|
||||
|
||||
if (STAT(p, &st) != 0)
|
||||
|
@ -299,8 +316,12 @@ DRIVE_FILE* drive_file_new(const char* base_path, const char* path, UINT32 id,
|
|||
{
|
||||
DRIVE_FILE* file;
|
||||
|
||||
file = (DRIVE_FILE*) malloc(sizeof(DRIVE_FILE));
|
||||
ZeroMemory(file, sizeof(DRIVE_FILE));
|
||||
file = (DRIVE_FILE*) calloc(1, sizeof(DRIVE_FILE));
|
||||
if (!file)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file->id = id;
|
||||
file->basepath = (char*) base_path;
|
||||
|
@ -419,8 +440,9 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||
{
|
||||
case FileBasicInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 36))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 36); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 36);
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */
|
||||
|
@ -431,8 +453,9 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||
|
||||
case FileStandardInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232088.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 22))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 22); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 22);
|
||||
Stream_Write_UINT64(output, st.st_size); /* AllocationSize */
|
||||
Stream_Write_UINT64(output, st.st_size); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, st.st_nlink); /* NumberOfLinks */
|
||||
|
@ -443,17 +466,23 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||
|
||||
case FileAttributeTagInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232093.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 8))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 8); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 8);
|
||||
Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unhandled FsInformationClass */
|
||||
Stream_Write_UINT32(output, 0); /* Length */
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
out_fail:
|
||||
Stream_Write_UINT32(output, 0); /* Length */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int dir_empty(const char *path)
|
||||
|
@ -483,18 +512,23 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||
{
|
||||
char* s = NULL;
|
||||
mode_t m;
|
||||
UINT64 size;
|
||||
INT64 size;
|
||||
int status;
|
||||
char* fullpath;
|
||||
struct STAT st;
|
||||
#if defined(__linux__) && !defined(ANDROID)
|
||||
struct timespec tv[2];
|
||||
#else
|
||||
struct timeval tv[2];
|
||||
#endif
|
||||
UINT64 LastWriteTime;
|
||||
ULARGE_INTEGER liCreationTime;
|
||||
ULARGE_INTEGER liLastAccessTime;
|
||||
ULARGE_INTEGER liLastWriteTime;
|
||||
ULARGE_INTEGER liChangeTime;
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
FILETIME* pftCreationTime = NULL;
|
||||
FILETIME* pftLastAccessTime = NULL;
|
||||
FILETIME* pftLastWriteTime = NULL;
|
||||
UINT32 FileAttributes;
|
||||
UINT32 FileNameLength;
|
||||
HANDLE hFd;
|
||||
LARGE_INTEGER liSize;
|
||||
|
||||
m = 0;
|
||||
|
||||
|
@ -502,55 +536,73 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||
{
|
||||
case FileBasicInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232094.aspx */
|
||||
Stream_Seek_UINT64(input); /* CreationTime */
|
||||
Stream_Seek_UINT64(input); /* LastAccessTime */
|
||||
Stream_Read_UINT64(input, LastWriteTime);
|
||||
Stream_Seek_UINT64(input); /* ChangeTime */
|
||||
Stream_Read_UINT64(input, liCreationTime.QuadPart);
|
||||
Stream_Read_UINT64(input, liLastAccessTime.QuadPart);
|
||||
Stream_Read_UINT64(input, liLastWriteTime.QuadPart);
|
||||
Stream_Read_UINT64(input, liChangeTime.QuadPart);
|
||||
Stream_Read_UINT32(input, FileAttributes);
|
||||
|
||||
if (FSTAT(file->fd, &st) != 0)
|
||||
if (!PathFileExistsA(file->fullpath))
|
||||
return FALSE;
|
||||
|
||||
tv[0].tv_sec = st.st_atime;
|
||||
tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime);
|
||||
#ifndef WIN32
|
||||
/* TODO on win32 */
|
||||
#ifdef ANDROID
|
||||
tv[0].tv_usec = 0;
|
||||
tv[1].tv_usec = 0;
|
||||
utimes(file->fullpath, tv);
|
||||
#elif defined (__linux__)
|
||||
tv[0].tv_nsec = 0;
|
||||
tv[1].tv_nsec = 0;
|
||||
futimens(file->fd, tv);
|
||||
#else
|
||||
tv[0].tv_usec = 0;
|
||||
tv[1].tv_usec = 0;
|
||||
futimes(file->fd, tv);
|
||||
#endif
|
||||
|
||||
if (FileAttributes > 0)
|
||||
hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m = st.st_mode;
|
||||
if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
|
||||
m |= S_IWUSR;
|
||||
else
|
||||
m &= ~S_IWUSR;
|
||||
if (m != st.st_mode)
|
||||
fchmod(file->fd, st.st_mode);
|
||||
WLog_ERR(TAG, "Unable to set file time %s to %d", file->fullpath);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
if (liCreationTime.QuadPart != 0)
|
||||
{
|
||||
ftCreationTime.dwHighDateTime = liCreationTime.HighPart;
|
||||
ftCreationTime.dwLowDateTime = liCreationTime.LowPart;
|
||||
pftCreationTime = &ftCreationTime;
|
||||
}
|
||||
if (liLastAccessTime.QuadPart != 0)
|
||||
{
|
||||
ftLastAccessTime.dwHighDateTime = liLastAccessTime.HighPart;
|
||||
ftLastAccessTime.dwLowDateTime = liLastAccessTime.LowPart;
|
||||
pftLastAccessTime = &ftLastAccessTime;
|
||||
}
|
||||
if (liLastWriteTime.QuadPart != 0)
|
||||
{
|
||||
ftLastWriteTime.dwHighDateTime = liLastWriteTime.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liLastWriteTime.LowPart;
|
||||
pftLastWriteTime = &ftLastWriteTime;
|
||||
}
|
||||
if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart)
|
||||
{
|
||||
ftLastWriteTime.dwHighDateTime = liChangeTime.HighPart;
|
||||
ftLastWriteTime.dwLowDateTime = liChangeTime.LowPart;
|
||||
pftLastWriteTime = &ftLastWriteTime;
|
||||
}
|
||||
if (!SetFileTime(hFd, pftCreationTime, pftLastAccessTime, pftLastWriteTime))
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to set file time %s to %d", file->fullpath);
|
||||
CloseHandle(hFd);
|
||||
return FALSE;
|
||||
}
|
||||
CloseHandle(hFd);
|
||||
break;
|
||||
|
||||
case FileEndOfFileInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232067.aspx */
|
||||
case FileAllocationInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232076.aspx */
|
||||
Stream_Read_UINT64(input, size);
|
||||
#ifndef _WIN32
|
||||
if (ftruncate(file->fd, size) != 0)
|
||||
Stream_Read_INT64(input, size);
|
||||
|
||||
hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d", file->fullpath, size);
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
liSize.QuadPart = size;
|
||||
if (SetFilePointer(hFd, liSize.LowPart, &liSize.HighPart, FILE_BEGIN) == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d", file->fullpath, size);
|
||||
CloseHandle(hFd);
|
||||
return FALSE;
|
||||
}
|
||||
CloseHandle(hFd);
|
||||
break;
|
||||
|
||||
case FileDispositionInformation:
|
||||
|
@ -575,9 +627,19 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||
FileNameLength / 2, &s, 0, NULL, NULL);
|
||||
|
||||
if (status < 1)
|
||||
s = (char*) calloc(1, 1);
|
||||
if (!(s = (char*) calloc(1, 1)))
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fullpath = drive_file_combine_fullpath(file->basepath, s);
|
||||
if (!fullpath)
|
||||
{
|
||||
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
|
||||
free (s);
|
||||
return FALSE;
|
||||
}
|
||||
free(s);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -628,7 +690,13 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
free(file->pattern);
|
||||
|
||||
if (path[0])
|
||||
file->pattern = _strdup(strrchr(path, '\\') + 1);
|
||||
{
|
||||
if (!(file->pattern = _strdup(strrchr(path, '\\') + 1)))
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
file->pattern = NULL;
|
||||
}
|
||||
|
@ -662,6 +730,11 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
|
||||
memset(&st, 0, sizeof(struct STAT));
|
||||
ent_path = (WCHAR*) malloc(strlen(file->fullpath) + strlen(ent->d_name) + 2);
|
||||
if (!ent_path)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return FALSE;
|
||||
}
|
||||
sprintf((char*) ent_path, "%s/%s", file->fullpath, ent->d_name);
|
||||
|
||||
if (STAT((char*) ent_path, &st) != 0)
|
||||
|
@ -680,8 +753,9 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
{
|
||||
case FileDirectoryInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232097.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 64 + length))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 64 + length); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 64 + length);
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
|
||||
|
@ -697,8 +771,9 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
|
||||
case FileFullDirectoryInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232068.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 68 + length))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 68 + length); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 68 + length);
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
|
||||
|
@ -715,8 +790,9 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
|
||||
case FileBothDirectoryInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232095.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 93 + length))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 93 + length); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 93 + length);
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */
|
||||
|
@ -736,8 +812,9 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
|
||||
case FileNamesInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232077.aspx */
|
||||
if (!Stream_EnsureRemainingCapacity(output, 4 + 12 + length))
|
||||
goto out_fail;
|
||||
Stream_Write_UINT32(output, 12 + length); /* Length */
|
||||
Stream_EnsureRemainingCapacity(output, 12 + length);
|
||||
Stream_Write_UINT32(output, 0); /* NextEntryOffset */
|
||||
Stream_Write_UINT32(output, 0); /* FileIndex */
|
||||
Stream_Write_UINT32(output, length); /* FileNameLength */
|
||||
|
@ -745,6 +822,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
break;
|
||||
|
||||
default:
|
||||
/* Unhandled FsInformationClass */
|
||||
Stream_Write_UINT32(output, 0); /* Length */
|
||||
Stream_Write_UINT8(output, 0); /* Padding */
|
||||
ret = FALSE;
|
||||
|
@ -752,8 +830,13 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||
}
|
||||
|
||||
free(ent_path);
|
||||
|
||||
return ret;
|
||||
|
||||
out_fail:
|
||||
free(ent_path);
|
||||
Stream_Write_UINT32(output, 0); /* Length */
|
||||
Stream_Write_UINT8(output, 0); /* Padding */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
* 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.
|
||||
|
@ -24,6 +28,7 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
|
@ -56,7 +61,7 @@
|
|||
typedef UINT32 ssize_t;
|
||||
typedef UINT32 mode_t;
|
||||
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#define STAT stat
|
||||
#define OPEN open
|
||||
#define LSEEK lseek
|
||||
|
@ -81,8 +86,6 @@ typedef UINT32 mode_t;
|
|||
|
||||
#define FILE_TIME_SYSTEM_TO_RDP(_t) \
|
||||
(((UINT64)(_t) + EPOCH_DIFF) * 10000000LL)
|
||||
#define FILE_TIME_RDP_TO_SYSTEM(_t) \
|
||||
(((_t) == 0LL || (_t) == (UINT64)(-1LL)) ? 0 : (time_t)((_t) / 10000000LL - EPOCH_DIFF))
|
||||
|
||||
#define FILE_ATTR_SYSTEM_TO_RDP(_f, _st) ( \
|
||||
(S_ISDIR(_st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) | \
|
||||
|
@ -90,6 +93,8 @@ typedef UINT32 mode_t;
|
|||
(_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \
|
||||
(st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY))
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client")
|
||||
|
||||
typedef struct _DRIVE_FILE DRIVE_FILE;
|
||||
|
||||
struct _DRIVE_FILE
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +0,0 @@
|
|||
LIBRARY "drive"
|
||||
EXPORTS
|
||||
DeviceServiceEntry @1
|
|
@ -3,6 +3,8 @@
|
|||
* statvfs emulation for Windows
|
||||
*
|
||||
* Copyright 2012 Gerald Richter
|
||||
* 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.
|
||||
|
@ -29,17 +31,18 @@ int statvfs(const char *path, struct statvfs *buf)
|
|||
{
|
||||
BOOL res;
|
||||
int len;
|
||||
LPWSTR unicodestr;
|
||||
LPWSTR unicodestr = NULL;
|
||||
DWORD lpSectorsPerCluster;
|
||||
DWORD lpBytesPerSector;
|
||||
DWORD lpNumberOfFreeClusters;
|
||||
DWORD lpTotalNumberOfClusters;
|
||||
|
||||
len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
|
||||
unicodestr = (LPWSTR) malloc(len);
|
||||
MultiByteToWideChar(CP_ACP, 0, path, -1, unicodestr, len);
|
||||
len = ConvertToUnicode(CP_ACP, 0, path, -1, &unicodestr, 0);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
res = GetDiskFreeSpace(unicodestr, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters, &lpTotalNumberOfClusters);
|
||||
res = GetDiskFreeSpaceW(unicodestr, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters, &lpTotalNumberOfClusters);
|
||||
free(unicodestr);
|
||||
|
||||
buf->f_bsize = lpBytesPerSector; /* file system block size */
|
||||
buf->f_frsize = 0; /* fragment size */
|
||||
|
|
|
@ -25,10 +25,9 @@ include_directories(..)
|
|||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -23,15 +25,14 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
|
||||
#include "echo_main.h"
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("echo.client")
|
||||
|
||||
typedef struct _ECHO_LISTENER_CALLBACK ECHO_LISTENER_CALLBACK;
|
||||
struct _ECHO_LISTENER_CALLBACK
|
||||
|
@ -60,30 +61,42 @@ struct _ECHO_PLUGIN
|
|||
ECHO_LISTENER_CALLBACK* listener_callback;
|
||||
};
|
||||
|
||||
static int echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
{
|
||||
int status;
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
BYTE* pBuffer = Stream_Pointer(data);
|
||||
UINT32 cbSize = Stream_GetRemainingLength(data);
|
||||
|
||||
/* echo back what we have received. ECHO does not have any message IDs. */
|
||||
status = callback->channel->Write(callback->channel, cbSize, pBuffer, NULL);
|
||||
|
||||
return status;
|
||||
return callback->channel->Write(callback->channel, cbSize, pBuffer, NULL);
|
||||
}
|
||||
|
||||
static int echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback = (ECHO_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
free(callback);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback;
|
||||
|
@ -92,7 +105,10 @@ static int echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallbac
|
|||
callback = (ECHO_CHANNEL_CALLBACK*) calloc(1, sizeof(ECHO_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
callback->iface.OnDataReceived = echo_on_data_received;
|
||||
callback->iface.OnClose = echo_on_close;
|
||||
|
@ -102,17 +118,25 @@ static int echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallbac
|
|||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*) pPlugin;
|
||||
|
||||
echo->listener_callback = (ECHO_LISTENER_CALLBACK*) calloc(1, sizeof(ECHO_LISTENER_CALLBACK));
|
||||
|
||||
if (!echo->listener_callback)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
echo->listener_callback->iface.OnNewChannelConnection = echo_on_new_channel_connection;
|
||||
echo->listener_callback->plugin = pPlugin;
|
||||
|
@ -122,25 +146,34 @@ static int echo_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager
|
|||
(IWTSListenerCallback*) echo->listener_callback, NULL);
|
||||
}
|
||||
|
||||
static int echo_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
ECHO_PLUGIN* echo = (ECHO_PLUGIN*) pPlugin;
|
||||
|
||||
if (echo)
|
||||
{
|
||||
free(echo);
|
||||
}
|
||||
free(echo);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DVCPluginEntry echo_DVCPluginEntry
|
||||
#else
|
||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
||||
#endif
|
||||
|
||||
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
int status = 0;
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
ECHO_PLUGIN* echo;
|
||||
|
||||
echo = (ECHO_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "echo");
|
||||
|
@ -150,7 +183,10 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||
echo = (ECHO_PLUGIN*) calloc(1, sizeof(ECHO_PLUGIN));
|
||||
|
||||
if (!echo)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
echo->iface.Initialize = echo_plugin_initialize;
|
||||
echo->iface.Connected = NULL;
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
|
||||
#define DVC_TAG CHANNELS_TAG("echo.client")
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(fmt, ...) WLog_DBG(DVC_TAG, fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(fmt, ...) do { } while (0)
|
||||
#define DEBUG_DVC(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* __ECHO_MAIN_H */
|
||||
|
|
|
@ -26,6 +26,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -32,6 +34,9 @@
|
|||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include <freerdp/server/echo.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("echo.server")
|
||||
|
||||
typedef struct _echo_server
|
||||
{
|
||||
|
@ -48,7 +53,12 @@ typedef struct _echo_server
|
|||
|
||||
} echo_server;
|
||||
|
||||
static BOOL echo_server_open_channel(echo_server* echo)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_server_open_channel(echo_server* echo)
|
||||
{
|
||||
DWORD Error;
|
||||
HANDLE hEvent;
|
||||
|
@ -57,27 +67,34 @@ static BOOL echo_server_open_channel(echo_server* echo)
|
|||
PULONG pSessionId = NULL;
|
||||
|
||||
if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION,
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned) == FALSE)
|
||||
WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
echo->SessionId = (DWORD) *pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
|
||||
echo->SessionId = (DWORD) * pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
|
||||
StartTick = GetTickCount();
|
||||
|
||||
while (echo->echo_channel == NULL)
|
||||
{
|
||||
WaitForSingleObject(hEvent, 1000);
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId,
|
||||
"ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
"ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (echo->echo_channel)
|
||||
break;
|
||||
|
||||
Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_NOT_FOUND)
|
||||
break;
|
||||
|
||||
|
@ -85,7 +102,7 @@ static BOOL echo_server_open_channel(echo_server* echo)
|
|||
break;
|
||||
}
|
||||
|
||||
return echo->echo_channel ? TRUE : FALSE;
|
||||
return echo->echo_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static void* echo_server_thread_func(void* arg)
|
||||
|
@ -98,18 +115,30 @@ static void* echo_server_thread_func(void* arg)
|
|||
HANDLE ChannelEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
echo_server* echo = (echo_server*) arg;
|
||||
UINT error;
|
||||
DWORD status;
|
||||
freerdp_channel_init_thread_context(echo->context.rdpcontext);
|
||||
|
||||
if (echo_server_open_channel(echo) == FALSE)
|
||||
if ((error = echo_server_open_channel(echo)))
|
||||
{
|
||||
IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
|
||||
return NULL;
|
||||
UINT error2 = 0;
|
||||
WLog_ERR(TAG, "echo_server_open_channel failed with error %lu!", 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 %lu",
|
||||
error2);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = NULL;
|
||||
|
||||
if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
|
||||
if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
@ -125,101 +154,202 @@ static void* echo_server_thread_func(void* arg)
|
|||
|
||||
while (1)
|
||||
{
|
||||
if (WaitForMultipleObjects(nCount, events, FALSE, 100) == WAIT_OBJECT_0)
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_CLOSED);
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE)
|
||||
if (status == WAIT_OBJECT_0)
|
||||
{
|
||||
IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_ERROR);
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_CLOSED);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %lu!", 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 %lu!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ready = *((BOOL*) buffer);
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
if (ready)
|
||||
{
|
||||
IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
|
||||
IFCALLRET(echo->context.OpenResult, error, &echo->context,
|
||||
ECHO_SERVER_OPEN_RESULT_OK);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "OpenResult failed with error %lu!", error);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s = Stream_New(NULL, 4096);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WTSVirtualChannelClose(echo->echo_channel);
|
||||
ExitThread((DWORD)ERROR_NOT_ENOUGH_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (ready)
|
||||
{
|
||||
if (WaitForMultipleObjects(nCount, events, FALSE, INFINITE) == WAIT_OBJECT_0)
|
||||
break;
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
Stream_EnsureRemainingCapacity(s, BytesReturned);
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR) Stream_Buffer(s),
|
||||
(ULONG) Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
|
||||
IFCALL(echo->context.Response, &echo->context, (BYTE *) Stream_Buffer(s), BytesReturned);
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned);
|
||||
|
||||
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, (PCHAR) Stream_Buffer(s),
|
||||
(ULONG) Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
IFCALLRET(echo->context.Response, error, &echo->context,
|
||||
(BYTE*) Stream_Buffer(s), BytesReturned);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "Response failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
WTSVirtualChannelClose(echo->echo_channel);
|
||||
echo->echo_channel = NULL;
|
||||
out:
|
||||
|
||||
if (error && echo->context.rdpcontext)
|
||||
setChannelError(echo->context.rdpcontext, error,
|
||||
"echo_server_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void echo_server_open(echo_server_context* context)
|
||||
/**
|
||||
* 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 == NULL)
|
||||
{
|
||||
echo->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
echo->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) echo_server_thread_func, (void*) echo, 0, NULL);
|
||||
if (!(echo->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(echo->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) echo_server_thread_func, (void*) echo, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(echo->stopEvent);
|
||||
echo->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void echo_server_close(echo_server_context* context)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
SetEvent(echo->stopEvent);
|
||||
WaitForSingleObject(echo->thread, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(echo->thread);
|
||||
CloseHandle(echo->stopEvent);
|
||||
echo->thread = NULL;
|
||||
echo->stopEvent = NULL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
|
||||
static BOOL echo_server_request(echo_server_context* context,
|
||||
const BYTE* buffer, UINT32 length)
|
||||
{
|
||||
echo_server* echo = (echo_server*) context;
|
||||
|
||||
return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR) buffer, length, NULL);
|
||||
}
|
||||
|
||||
echo_server_context* echo_server_context_new(HANDLE vcm)
|
||||
{
|
||||
echo_server* echo;
|
||||
|
||||
echo = (echo_server*) calloc(1, sizeof(echo_server));
|
||||
|
||||
echo->context.vcm = vcm;
|
||||
echo->context.Open = echo_server_open;
|
||||
echo->context.Close = echo_server_close;
|
||||
echo->context.Request = echo_server_request;
|
||||
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;
|
||||
}
|
||||
|
@ -227,8 +357,6 @@ echo_server_context* echo_server_context_new(HANDLE vcm)
|
|||
void echo_server_context_free(echo_server_context* context)
|
||||
{
|
||||
echo_server* echo = (echo_server*) context;
|
||||
|
||||
echo_server_close(context);
|
||||
|
||||
free(echo);
|
||||
}
|
||||
|
|
|
@ -26,9 +26,4 @@ set(${MODULE_PREFIX}_SRCS
|
|||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -47,6 +49,7 @@ struct encomsp_plugin
|
|||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
typedef struct encomsp_plugin encomspPlugin;
|
||||
|
||||
|
|
|
@ -31,6 +31,5 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
|||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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.
|
||||
|
@ -31,15 +33,19 @@
|
|||
|
||||
#define TAG CHANNELS_TAG("encomsp.server")
|
||||
|
||||
static int encomsp_read_header(wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_read_header(wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < ENCOMSP_ORDER_HEADER_SIZE)
|
||||
return -1;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, header->Type); /* Type (2 bytes) */
|
||||
Stream_Read_UINT16(s, header->Length); /* Length (2 bytes) */
|
||||
|
||||
return 1;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -48,7 +54,6 @@ static int encomsp_write_header(wStream* s, ENCOMSP_ORDER_HEADER* header)
|
|||
{
|
||||
Stream_Write_UINT16(s, header->Type); /* Type (2 bytes) */
|
||||
Stream_Write_UINT16(s, header->Length); /* Length (2 bytes) */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -68,82 +73,110 @@ static int encomsp_read_unicode_string(wStream* s, ENCOMSP_UNICODE_STRING* str)
|
|||
return -1;
|
||||
|
||||
Stream_Read(s, &(str->wString), (str->cchString * 2)); /* String (variable) */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int encomsp_recv_change_participant_control_level_pdu(EncomspServerContext* context, wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_recv_change_participant_control_level_pdu(
|
||||
EncomspServerContext* context, wStream* s, ENCOMSP_ORDER_HEADER* header)
|
||||
{
|
||||
int beg, end;
|
||||
ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu;
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
beg = ((int) Stream_GetPosition(s)) - ENCOMSP_ORDER_HEADER_SIZE;
|
||||
|
||||
CopyMemory(&pdu, header, sizeof(ENCOMSP_ORDER_HEADER));
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 6)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, pdu.Flags); /* Flags (2 bytes) */
|
||||
Stream_Read_UINT32(s, pdu.ParticipantId); /* ParticipantId (4 bytes) */
|
||||
|
||||
end = (int) Stream_GetPosition(s);
|
||||
|
||||
if ((beg + header->Length) < end)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if ((beg + header->Length) > end)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < ((beg + header->Length) - end))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, (beg + header->Length));
|
||||
}
|
||||
|
||||
if (context->ChangeParticipantControlLevel)
|
||||
{
|
||||
return context->ChangeParticipantControlLevel(context, &pdu);
|
||||
}
|
||||
IFCALLRET(context->ChangeParticipantControlLevel, error, context, &pdu);
|
||||
|
||||
return 1;
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->ChangeParticipantControlLevel failed with error %lu",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int encomsp_server_receive_pdu(EncomspServerContext* context, wStream* s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_receive_pdu(EncomspServerContext* context,
|
||||
wStream* s)
|
||||
{
|
||||
int status = 1;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ENCOMSP_ORDER_HEADER header;
|
||||
|
||||
while (Stream_GetRemainingLength(s) > 0)
|
||||
{
|
||||
if (encomsp_read_header(s, &header) < 0)
|
||||
return -1;
|
||||
if ((error = encomsp_read_header(s, &header)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_read_header failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "EncomspReceive: Type: %d Length: %d", header.Type, header.Length);
|
||||
WLog_INFO(TAG, "EncomspReceive: Type: %d Length: %d", header.Type,
|
||||
header.Length);
|
||||
|
||||
switch (header.Type)
|
||||
{
|
||||
case ODTYPE_PARTICIPANT_CTRL_CHANGED:
|
||||
status = encomsp_recv_change_participant_control_level_pdu(context, s, &header);
|
||||
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 %lu!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
status = -1;
|
||||
WLog_ERR(TAG, "header.Type unknown %d!", header.Type);
|
||||
return ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void* encomsp_server_thread(void* arg)
|
||||
{
|
||||
wStream* s;
|
||||
DWORD status;
|
||||
DWORD nCount;
|
||||
void* buffer;
|
||||
HANDLE events[8];
|
||||
|
@ -151,16 +184,24 @@ static void* encomsp_server_thread(void* arg)
|
|||
DWORD BytesReturned;
|
||||
ENCOMSP_ORDER_HEADER* header;
|
||||
EncomspServerContext* context;
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
context = (EncomspServerContext*) arg;
|
||||
|
||||
freerdp_channel_init_thread_context(context->rdpcontext);
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = NULL;
|
||||
|
||||
s = Stream_New(NULL, 4096);
|
||||
|
||||
if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
|
||||
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))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
@ -176,18 +217,44 @@ static void* encomsp_server_thread(void* arg)
|
|||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(context->priv->StopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
WTSVirtualChannelRead(context->priv->ChannelHandle, 0, NULL, 0, &BytesReturned);
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
Stream_EnsureRemainingCapacity(s, BytesReturned);
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -199,60 +266,98 @@ static void* encomsp_server_thread(void* arg)
|
|||
{
|
||||
Stream_SealLength(s);
|
||||
Stream_SetPosition(s, 0);
|
||||
encomsp_server_receive_pdu(context, s);
|
||||
|
||||
if ((error = encomsp_server_receive_pdu(context, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "encomsp_server_receive_pdu failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
out:
|
||||
|
||||
if (error && context->rdpcontext)
|
||||
setChannelError(context->rdpcontext, error,
|
||||
"encomsp_server_thread reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int encomsp_server_start(EncomspServerContext* context)
|
||||
/**
|
||||
* 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");
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm,
|
||||
WTS_CURRENT_SESSION, "encomsp");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
|
||||
context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!(context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
context->priv->Thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) encomsp_server_thread, (void*) context, 0, NULL);
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) encomsp_server_thread, (void*) context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int encomsp_server_stop(EncomspServerContext* context)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
SetEvent(context->priv->StopEvent);
|
||||
|
||||
WaitForSingleObject(context->priv->Thread, INFINITE);
|
||||
CloseHandle(context->priv->Thread);
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
CloseHandle(context->priv->Thread);
|
||||
return error;
|
||||
}
|
||||
|
||||
EncomspServerContext* encomsp_server_context_new(HANDLE vcm)
|
||||
{
|
||||
EncomspServerContext* context;
|
||||
|
||||
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)
|
||||
if (!context->priv)
|
||||
{
|
||||
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,11 +368,7 @@ void encomsp_server_context_free(EncomspServerContext* context)
|
|||
{
|
||||
if (context)
|
||||
{
|
||||
if (context->priv)
|
||||
{
|
||||
free(context->priv);
|
||||
}
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2010 O.S. Systems Software Ltda.
|
||||
* Copyright 2010 Eduardo Fiss Beloni <beloni@ossystems.com.br>
|
||||
* 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.
|
||||
|
@ -54,6 +56,9 @@
|
|||
#include <freerdp/types.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client")
|
||||
|
||||
struct _PARALLEL_DEVICE
|
||||
{
|
||||
|
@ -65,25 +70,33 @@ struct _PARALLEL_DEVICE
|
|||
|
||||
HANDLE thread;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
typedef struct _PARALLEL_DEVICE PARALLEL_DEVICE;
|
||||
|
||||
static void parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
char* path = NULL;
|
||||
int status;
|
||||
UINT32 PathLength;
|
||||
|
||||
Stream_Seek(irp->input, 28);
|
||||
/* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
|
||||
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input),
|
||||
PathLength / 2, &path, 0, NULL, NULL);
|
||||
PathLength / 2, &path, 0, NULL, NULL);
|
||||
|
||||
if (status < 1)
|
||||
path = (char*) calloc(1, 1);
|
||||
if (!(path = (char*) calloc(1, 1)))
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
parallel->id = irp->devman->id_sequence++;
|
||||
parallel->file = open(parallel->path, O_RDWR);
|
||||
|
@ -98,46 +111,54 @@ static void parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||
/* all read and write operations should be non-blocking */
|
||||
if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, parallel->id);
|
||||
Stream_Write_UINT8(irp->output, 0);
|
||||
|
||||
free(path);
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
if (close(parallel->file) < 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Stream_Zero(irp->output, 5); /* Padding(5) */
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
ssize_t status;
|
||||
BYTE* buffer = NULL;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
|
||||
buffer = (BYTE*) malloc(Length);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = read(parallel->file, buffer, Length);
|
||||
|
||||
if (status < 0)
|
||||
|
@ -149,33 +170,40 @@ static void parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
|
||||
if (Length > 0)
|
||||
{
|
||||
Stream_EnsureRemainingCapacity(irp->output, Length);
|
||||
if (!Stream_EnsureRemainingCapacity(irp->output, Length))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
free(buffer);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(irp->output, buffer, Length);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
UINT32 len;
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
ssize_t status;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
|
||||
len = Length;
|
||||
|
||||
while (len > 0)
|
||||
|
@ -195,45 +223,85 @@ static void parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding */
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel,
|
||||
IRP* irp)
|
||||
{
|
||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CREATE:
|
||||
parallel_process_irp_create(parallel, irp);
|
||||
if ((error = parallel_process_irp_create(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_create failed with error %d!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_CLOSE:
|
||||
parallel_process_irp_close(parallel, irp);
|
||||
if ((error = parallel_process_irp_close(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_close failed with error %d!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_READ:
|
||||
parallel_process_irp_read(parallel, irp);
|
||||
if ((error = parallel_process_irp_read(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_read failed with error %d!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_WRITE:
|
||||
parallel_process_irp_write(parallel, irp);
|
||||
if ((error = parallel_process_irp_write(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_write failed with error %d!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
parallel_process_irp_device_control(parallel, irp);
|
||||
if ((error = parallel_process_irp_device_control(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %d!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
irp->IoStatus = STATUS_NOT_SUPPORTED;
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void* parallel_thread_func(void* arg)
|
||||
|
@ -241,59 +309,108 @@ static void* parallel_thread_func(void* arg)
|
|||
IRP* irp;
|
||||
wMessage message;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
freerdp_channel_init_thread_context(parallel->rdpcontext);
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!MessageQueue_Wait(parallel->queue))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Wait failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Peek failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
irp = (IRP*) message.wParam;
|
||||
|
||||
parallel_process_irp(parallel, irp);
|
||||
if ((error = parallel_process_irp(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp failed with error %d!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error && parallel->rdpcontext)
|
||||
setChannelError(parallel->rdpcontext, error,
|
||||
"parallel_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) device;
|
||||
|
||||
MessageQueue_Post(parallel->queue, NULL, 0, (void*) irp, NULL);
|
||||
if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*) irp, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void parallel_free(DEVICE* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_free(DEVICE* device)
|
||||
{
|
||||
UINT error;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*) device;
|
||||
|
||||
MessageQueue_PostQuit(parallel->queue, 0);
|
||||
WaitForSingleObject(parallel->thread, INFINITE);
|
||||
CloseHandle(parallel->thread);
|
||||
if (MessageQueue_PostQuit(parallel->queue, 0)
|
||||
&& (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(parallel->thread);
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
MessageQueue_Free(parallel->queue);
|
||||
|
||||
free(parallel);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DeviceServiceEntry parallel_DeviceServiceEntry
|
||||
#else
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#endif
|
||||
|
||||
int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
char* name;
|
||||
char* path;
|
||||
int i, length;
|
||||
int i;
|
||||
size_t length;
|
||||
RDPDR_PARALLEL* device;
|
||||
PARALLEL_DEVICE* parallel;
|
||||
|
||||
UINT error;
|
||||
device = (RDPDR_PARALLEL*) pEntryPoints->device;
|
||||
name = device->Name;
|
||||
path = device->Path;
|
||||
|
@ -301,7 +418,7 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||
if (!name || (name[0] == '*'))
|
||||
{
|
||||
/* TODO: implement auto detection of parallel ports */
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
if (name[0] && path[0])
|
||||
|
@ -309,27 +426,59 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||
parallel = (PARALLEL_DEVICE*) calloc(1, sizeof(PARALLEL_DEVICE));
|
||||
|
||||
if (!parallel)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
parallel->device.type = RDPDR_DTYP_PARALLEL;
|
||||
parallel->device.name = name;
|
||||
parallel->device.IRPRequest = parallel_irp_request;
|
||||
parallel->device.Free = parallel_free;
|
||||
|
||||
parallel->rdpcontext = pEntryPoints->rdpcontext;
|
||||
length = strlen(name);
|
||||
parallel->device.data = Stream_New(NULL, length + 1);
|
||||
|
||||
if (!parallel->device.data)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
for (i = 0; i <= length; i++)
|
||||
Stream_Write_UINT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
|
||||
|
||||
parallel->path = path;
|
||||
|
||||
parallel->queue = MessageQueue_New(NULL);
|
||||
|
||||
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) parallel);
|
||||
if (!parallel->queue)
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
parallel->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) parallel_thread_func, (void*) parallel, 0, NULL);
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) parallel)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(parallel->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) parallel_thread_func, (void*) parallel, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
MessageQueue_Free(parallel->queue);
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
free(parallel);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ if(WITH_CUPS)
|
|||
add_definitions(-DWITH_CUPS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WIN32 AND NOT UWP)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
printer_win.c
|
||||
printer_win.h)
|
||||
|
@ -48,6 +48,9 @@ endif()
|
|||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* Print Virtual Channel - CUPS driver
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
|
@ -30,6 +33,7 @@
|
|||
#include <cups/cups.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
|
@ -70,12 +74,17 @@ static void printer_cups_get_printjob_name(char* buf, int size)
|
|||
|
||||
tt = time(NULL);
|
||||
t = localtime(&tt);
|
||||
snprintf(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
}
|
||||
|
||||
static void printer_cups_write_printjob(rdpPrintJob* printjob, BYTE* data, int size)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_cups_write_printjob(rdpPrintJob* printjob, BYTE* data, int size)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*) printjob;
|
||||
|
||||
|
@ -87,11 +96,12 @@ static void printer_cups_write_printjob(rdpPrintJob* printjob, BYTE* data, int s
|
|||
fp = fopen((const char*) cups_printjob->printjob_object, "a+b");
|
||||
|
||||
if (!fp)
|
||||
return;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (fwrite(data, 1, size, fp) < size)
|
||||
{
|
||||
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
// FIXME once this function doesn't return void anymore!
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
@ -102,6 +112,8 @@ static void printer_cups_write_printjob(rdpPrintJob* printjob, BYTE* data, int s
|
|||
cupsWriteRequestData((http_t*) cups_printjob->printjob_object, (const char*) data, size);
|
||||
|
||||
#endif
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_cups_close_printjob(rdpPrintJob* printjob)
|
||||
|
@ -144,8 +156,9 @@ static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id)
|
|||
if (cups_printer->printjob != NULL)
|
||||
return NULL;
|
||||
|
||||
cups_printjob = (rdpCupsPrintJob*) malloc(sizeof(rdpCupsPrintJob));
|
||||
ZeroMemory(cups_printjob, sizeof(rdpCupsPrintJob));
|
||||
cups_printjob = (rdpCupsPrintJob*) calloc(1, sizeof(rdpCupsPrintJob));
|
||||
if (!cups_printjob)
|
||||
return NULL;
|
||||
|
||||
cups_printjob->printjob.id = id;
|
||||
cups_printjob->printjob.printer = printer;
|
||||
|
@ -156,6 +169,11 @@ static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id)
|
|||
#ifndef _CUPS_API_1_4
|
||||
|
||||
cups_printjob->printjob_object = _strdup(tmpnam(NULL));
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
free(cups_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
|
@ -212,20 +230,37 @@ static void printer_cups_free_printer(rdpPrinter* printer)
|
|||
cups_printer->printjob->printjob.Close((rdpPrintJob*) cups_printer->printjob);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver, const char* name, BOOL is_default)
|
||||
static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver,
|
||||
const char* name, const char* driverName, BOOL is_default)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer;
|
||||
|
||||
cups_printer = (rdpCupsPrinter*) malloc(sizeof(rdpCupsPrinter));
|
||||
ZeroMemory(cups_printer, sizeof(rdpCupsPrinter));
|
||||
cups_printer = (rdpCupsPrinter*) calloc(1, sizeof(rdpCupsPrinter));
|
||||
if (!cups_printer)
|
||||
return NULL;
|
||||
|
||||
cups_printer->printer.id = cups_driver->id_sequence++;
|
||||
cups_printer->printer.name = _strdup(name);
|
||||
/* This is a generic PostScript printer driver developed by MS, so it should be good in most cases */
|
||||
cups_printer->printer.driver = "MS Publisher Imagesetter";
|
||||
if (!cups_printer->printer.name)
|
||||
{
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (driverName)
|
||||
cups_printer->printer.driver = _strdup(driverName);
|
||||
else
|
||||
cups_printer->printer.driver = _strdup("MS Publisher Imagesetter");
|
||||
if (!cups_printer->printer.driver)
|
||||
{
|
||||
free(cups_printer->printer.name);
|
||||
free(cups_printer);
|
||||
return NULL;
|
||||
}
|
||||
cups_printer->printer.is_default = is_default;
|
||||
|
||||
cups_printer->printer.CreatePrintJob = printer_cups_create_printjob;
|
||||
|
@ -245,8 +280,9 @@ static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
|||
int i;
|
||||
|
||||
num_dests = cupsGetDests(&dests);
|
||||
printers = (rdpPrinter**) malloc(sizeof(rdpPrinter*) * (num_dests + 1));
|
||||
ZeroMemory(printers, sizeof(rdpPrinter*) * (num_dests + 1));
|
||||
printers = (rdpPrinter**) calloc(1, sizeof(rdpPrinter*) * (num_dests + 1));
|
||||
if (!printers)
|
||||
return NULL;
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
|
@ -255,7 +291,7 @@ static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
|||
if (dest->instance == NULL)
|
||||
{
|
||||
printers[num_printers++] = printer_cups_new_printer((rdpCupsPrinterDriver*) driver,
|
||||
dest->name, dest->is_default);
|
||||
dest->name, NULL, dest->is_default);
|
||||
}
|
||||
}
|
||||
cupsFreeDests(num_dests, dests);
|
||||
|
@ -263,11 +299,13 @@ static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
|||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver, const char* name)
|
||||
static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver,
|
||||
const char* name, const char* driverName)
|
||||
{
|
||||
rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*) driver;
|
||||
|
||||
return printer_cups_new_printer(cups_driver, name, cups_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
return printer_cups_new_printer(cups_driver, name, driverName,
|
||||
cups_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static rdpCupsPrinterDriver* cups_driver = NULL;
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
* Print Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Armin Novak <armin.novak@gmail.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.
|
||||
|
@ -26,6 +30,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
|
@ -33,16 +38,22 @@
|
|||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
#include "../printer.h"
|
||||
|
||||
#ifdef WITH_CUPS
|
||||
#include "printer_cups.h"
|
||||
#endif
|
||||
|
||||
#include "printer_main.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#if defined(_WIN32) && !defined(_UWP)
|
||||
#include "printer_win.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("printer.client")
|
||||
|
||||
typedef struct _PRINTER_DEVICE PRINTER_DEVICE;
|
||||
struct _PRINTER_DEVICE
|
||||
{
|
||||
|
@ -50,20 +61,27 @@ struct _PRINTER_DEVICE
|
|||
|
||||
rdpPrinter* printer;
|
||||
|
||||
PSLIST_HEADER pIrpList;
|
||||
WINPR_PSLIST_HEADER pIrpList;
|
||||
|
||||
HANDLE event;
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
static void printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
{
|
||||
rdpPrintJob* printjob = NULL;
|
||||
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->CreatePrintJob(printer_dev->printer, irp->devman->id_sequence++);
|
||||
printjob = printer_dev->printer->CreatePrintJob(printer_dev->printer,
|
||||
irp->devman->id_sequence++);
|
||||
|
||||
if (printjob)
|
||||
{
|
||||
|
@ -75,15 +93,21 @@ static void printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||
irp->IoStatus = STATUS_PRINT_QUEUE_FULL;
|
||||
}
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
{
|
||||
rdpPrintJob* printjob = NULL;
|
||||
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
||||
irp->FileId);
|
||||
|
||||
if (!printjob)
|
||||
{
|
||||
|
@ -95,22 +119,27 @@ static void printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||
}
|
||||
|
||||
Stream_Zero(irp->output, 4); /* Padding(4) */
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
{
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
rdpPrintJob* printjob = NULL;
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
|
||||
if (printer_dev->printer)
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
|
||||
printjob = printer_dev->printer->FindPrintJob(printer_dev->printer,
|
||||
irp->FileId);
|
||||
|
||||
if (!printjob)
|
||||
{
|
||||
|
@ -119,46 +148,87 @@ static void printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
|
|||
}
|
||||
else
|
||||
{
|
||||
printjob->Write(printjob, Stream_Pointer(irp->input), Length);
|
||||
error = printjob->Write(printjob, Stream_Pointer(irp->input), Length);
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "printjob->Write failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding */
|
||||
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void printer_process_irp_device_control(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp_device_control(PRINTER_DEVICE* printer_dev,
|
||||
IRP* irp)
|
||||
{
|
||||
Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CREATE:
|
||||
printer_process_irp_create(printer_dev, irp);
|
||||
if ((error = printer_process_irp_create(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_create failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_CLOSE:
|
||||
printer_process_irp_close(printer_dev, irp);
|
||||
if ((error = printer_process_irp_close(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_close failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_WRITE:
|
||||
printer_process_irp_write(printer_dev, irp);
|
||||
if ((error = printer_process_irp_write(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_write failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
printer_process_irp_device_control(printer_dev, irp);
|
||||
if ((error = printer_process_irp_device_control(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp_device_control failed with error %lu!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
irp->IoStatus = STATUS_NOT_SUPPORTED;
|
||||
irp->Complete(irp);
|
||||
return irp->Complete(irp);
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void* printer_thread_func(void* arg)
|
||||
|
@ -166,47 +236,81 @@ static void* printer_thread_func(void* arg)
|
|||
IRP* irp;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) arg;
|
||||
HANDLE obj[] = {printer_dev->event, printer_dev->stopEvent};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
freerdp_channel_init_thread_context(printer_dev->rdpcontext);
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD rc = WaitForMultipleObjects(2, obj, FALSE, INFINITE);
|
||||
|
||||
if (rc == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == WAIT_OBJECT_0 + 1)
|
||||
break;
|
||||
else if( rc != WAIT_OBJECT_0 )
|
||||
else if (rc != WAIT_OBJECT_0)
|
||||
continue;
|
||||
|
||||
ResetEvent(printer_dev->event);
|
||||
|
||||
irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList);
|
||||
|
||||
if (irp == NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "InterlockedPopEntrySList failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
printer_process_irp(printer_dev, irp);
|
||||
if ((error = printer_process_irp(printer_dev, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_process_irp failed with error %d!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
if (error && printer_dev->rdpcontext)
|
||||
setChannelError(printer_dev->rdpcontext, error,
|
||||
"printer_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD) error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void printer_irp_request(DEVICE* device, IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
||||
|
||||
InterlockedPushEntrySList(printer_dev->pIrpList, &(irp->ItemEntry));
|
||||
|
||||
SetEvent(printer_dev->event);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_free(DEVICE* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_free(DEVICE* device)
|
||||
{
|
||||
IRP* irp;
|
||||
PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*) device;
|
||||
|
||||
UINT error;
|
||||
SetEvent(printer_dev->stopEvent);
|
||||
WaitForSingleObject(printer_dev->thread, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
while ((irp = (IRP*) InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
|
||||
irp->Discard(irp);
|
||||
|
@ -214,18 +318,24 @@ static void printer_free(DEVICE* device)
|
|||
CloseHandle(printer_dev->thread);
|
||||
CloseHandle(printer_dev->stopEvent);
|
||||
CloseHandle(printer_dev->event);
|
||||
|
||||
_aligned_free(printer_dev->pIrpList);
|
||||
|
||||
if (printer_dev->printer)
|
||||
printer_dev->printer->Free(printer_dev->printer);
|
||||
|
||||
free(printer_dev->device.name);
|
||||
|
||||
Stream_Free(printer_dev->device.data, TRUE);
|
||||
free(printer_dev);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* printer)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
rdpPrinter* printer)
|
||||
{
|
||||
char* port;
|
||||
UINT32 Flags;
|
||||
|
@ -236,32 +346,53 @@ void printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* pri
|
|||
UINT32 CachedFieldsLen;
|
||||
BYTE* CachedPrinterConfigData;
|
||||
PRINTER_DEVICE* printer_dev;
|
||||
|
||||
UINT error;
|
||||
port = malloc(10);
|
||||
snprintf(port, 10, "PRN%d", printer->id);
|
||||
|
||||
printer_dev = (PRINTER_DEVICE*) malloc(sizeof(PRINTER_DEVICE));
|
||||
ZeroMemory(printer_dev, sizeof(PRINTER_DEVICE));
|
||||
if (!port)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
sprintf_s(port, 10, "PRN%d", printer->id);
|
||||
printer_dev = (PRINTER_DEVICE*) calloc(1, sizeof(PRINTER_DEVICE));
|
||||
|
||||
if (!printer_dev)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(port);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
printer_dev->device.type = RDPDR_DTYP_PRINT;
|
||||
printer_dev->device.name = port;
|
||||
printer_dev->device.IRPRequest = printer_irp_request;
|
||||
printer_dev->device.Free = printer_free;
|
||||
|
||||
printer_dev->rdpcontext = pEntryPoints->rdpcontext;
|
||||
printer_dev->printer = printer;
|
||||
|
||||
CachedFieldsLen = 0;
|
||||
CachedPrinterConfigData = NULL;
|
||||
|
||||
Flags = 0;
|
||||
|
||||
if (printer->is_default)
|
||||
Flags |= RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER;
|
||||
|
||||
DriverNameLen = ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, &DriverName, 0) * 2;
|
||||
PrintNameLen = ConvertToUnicode(CP_UTF8, 0, printer->name, -1, &PrintName, 0) * 2;
|
||||
DriverNameLen = ConvertToUnicode(CP_UTF8, 0, printer->driver, -1, &DriverName,
|
||||
0) * 2;
|
||||
PrintNameLen = ConvertToUnicode(CP_UTF8, 0, printer->name, -1, &PrintName,
|
||||
0) * 2;
|
||||
printer_dev->device.data = Stream_New(NULL,
|
||||
28 + DriverNameLen + PrintNameLen + CachedFieldsLen);
|
||||
|
||||
printer_dev->device.data = Stream_New(NULL, 28 + DriverNameLen + PrintNameLen + CachedFieldsLen);
|
||||
if (!printer_dev->device.data)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
free(DriverName);
|
||||
free(PrintName);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(printer_dev->device.data, Flags);
|
||||
Stream_Write_UINT32(printer_dev->device.data, 0); /* CodePage, reserved */
|
||||
|
@ -276,29 +407,76 @@ void printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* pri
|
|||
|
||||
if (CachedFieldsLen > 0)
|
||||
{
|
||||
Stream_Write(printer_dev->device.data, CachedPrinterConfigData, CachedFieldsLen);
|
||||
Stream_Write(printer_dev->device.data, CachedPrinterConfigData,
|
||||
CachedFieldsLen);
|
||||
}
|
||||
|
||||
free(DriverName);
|
||||
free(PrintName);
|
||||
printer_dev->pIrpList = (WINPR_PSLIST_HEADER) _aligned_malloc(sizeof(
|
||||
WINPR_SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
|
||||
if (!printer_dev->pIrpList)
|
||||
{
|
||||
WLog_ERR(TAG, "_aligned_malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
printer_dev->pIrpList = (PSLIST_HEADER) _aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
InitializeSListHead(printer_dev->pIrpList);
|
||||
|
||||
printer_dev->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
printer_dev->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!(printer_dev->event = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) printer_dev);
|
||||
if (!(printer_dev->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
printer_dev->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) printer_thread_func, (void*) printer_dev, 0, NULL);
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) printer_dev)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %d!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(printer_dev->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) printer_thread_func, (void*) printer_dev, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
CloseHandle(printer_dev->stopEvent);
|
||||
CloseHandle(printer_dev->event);
|
||||
_aligned_free(printer_dev->pIrpList);
|
||||
Stream_Free(printer_dev->device.data, TRUE);
|
||||
free(printer_dev);
|
||||
free(port);
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define DeviceServiceEntry printer_DeviceServiceEntry
|
||||
#else
|
||||
#define DeviceServiceEntry FREERDP_API DeviceServiceEntry
|
||||
#endif
|
||||
|
||||
int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
int i;
|
||||
char* name;
|
||||
|
@ -307,18 +485,18 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||
rdpPrinter** printers;
|
||||
RDPDR_PRINTER* device;
|
||||
rdpPrinterDriver* driver = NULL;
|
||||
|
||||
UINT error;
|
||||
#ifdef WITH_CUPS
|
||||
driver = printer_cups_get_driver();
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#if defined(_WIN32) && !defined(_UWP)
|
||||
driver = printer_win_get_driver();
|
||||
#endif
|
||||
|
||||
if (!driver)
|
||||
{
|
||||
return 1;
|
||||
WLog_ERR(TAG, "Could not get a printer driver!");
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
device = (RDPDR_PRINTER*) pEntryPoints->device;
|
||||
|
@ -327,15 +505,19 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||
|
||||
if (name && name[0])
|
||||
{
|
||||
printer = driver->GetPrinter(driver, name);
|
||||
printer = driver->GetPrinter(driver, name, driver_name);
|
||||
|
||||
if (!printer)
|
||||
return 1;
|
||||
{
|
||||
WLog_ERR(TAG, "Could not get printer %s!", name);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
if (driver_name && driver_name[0])
|
||||
printer->driver = driver_name;
|
||||
|
||||
printer_register(pEntryPoints, printer);
|
||||
if ((error = printer_register(pEntryPoints, printer)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_register failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -344,11 +526,17 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
|||
for (i = 0; printers[i]; i++)
|
||||
{
|
||||
printer = printers[i];
|
||||
printer_register(pEntryPoints, printer);
|
||||
|
||||
if ((error = printer_register(pEntryPoints, printer)))
|
||||
{
|
||||
WLog_ERR(TAG, "printer_register failed with error %lu!", error);
|
||||
free(printers);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
free(printers);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* Print Virtual Channel
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
|
@ -22,25 +25,12 @@
|
|||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
/* SERVER_PRINTER_CACHE_EVENT.cachedata */
|
||||
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
||||
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
||||
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
||||
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
||||
|
||||
/* DR_PRN_DEVICE_ANNOUNCE.Flags */
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
||||
|
||||
typedef struct rdp_printer_driver rdpPrinterDriver;
|
||||
typedef struct rdp_printer rdpPrinter;
|
||||
typedef struct rdp_print_job rdpPrintJob;
|
||||
|
||||
typedef rdpPrinter** (*pcEnumPrinters) (rdpPrinterDriver* driver);
|
||||
typedef rdpPrinter* (*pcGetPrinter) (rdpPrinterDriver* driver, const char* name);
|
||||
typedef rdpPrinter* (*pcGetPrinter) (rdpPrinterDriver* driver, const char* name, const char* driverName);
|
||||
|
||||
struct rdp_printer_driver
|
||||
{
|
||||
|
@ -64,7 +54,7 @@ struct rdp_printer
|
|||
pcFreePrinter Free;
|
||||
};
|
||||
|
||||
typedef void (*pcWritePrintJob) (rdpPrintJob* printjob, BYTE* data, int size);
|
||||
typedef UINT (*pcWritePrintJob) (rdpPrintJob* printjob, BYTE* data, int size);
|
||||
typedef void (*pcClosePrintJob) (rdpPrintJob* printjob);
|
||||
|
||||
struct rdp_print_job
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* Print Virtual Channel - WIN driver
|
||||
*
|
||||
* Copyright 2012 Gerald Richter
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
|
@ -22,6 +25,7 @@
|
|||
#endif
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <time.h>
|
||||
|
@ -69,12 +73,17 @@ static void printer_win_get_printjob_name(char* buf, int size)
|
|||
|
||||
tt = time(NULL);
|
||||
t = localtime(&tt);
|
||||
snprintf(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print Job %d%02d%02d%02d%02d%02d",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
}
|
||||
|
||||
static void printer_win_write_printjob(rdpPrintJob* printjob, BYTE* data, int size)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_win_write_printjob(rdpPrintJob* printjob, BYTE* data, int size)
|
||||
{
|
||||
rdpWinPrintJob* win_printjob = (rdpWinPrintJob*) printjob;
|
||||
|
||||
|
@ -83,9 +92,8 @@ static void printer_win_write_printjob(rdpPrintJob* printjob, BYTE* data, int si
|
|||
DWORD pcWritten;
|
||||
|
||||
if(!WritePrinter(((rdpWinPrinter*)printjob->printer)->hPrinter, pBuf, cbBuf, &pcWritten))
|
||||
{
|
||||
|
||||
}
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_win_close_printjob(rdpPrintJob* printjob)
|
||||
|
@ -116,6 +124,8 @@ static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
|
|||
return NULL;
|
||||
|
||||
win_printjob = (rdpWinPrintJob*) calloc(1, sizeof(rdpWinPrintJob));
|
||||
if (!win_printjob)
|
||||
return NULL;
|
||||
|
||||
win_printjob->printjob.id = id;
|
||||
win_printjob->printjob.printer = printer;
|
||||
|
@ -127,12 +137,14 @@ static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
|
|||
|
||||
if (!win_printjob->handle)
|
||||
{
|
||||
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StartPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
|
||||
free(win_printjob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win_printjob->printjob.Write = printer_win_write_printjob;
|
||||
|
@ -164,10 +176,12 @@ static void printer_win_free_printer(rdpPrinter* printer)
|
|||
win_printer->printjob->printjob.Close((rdpPrintJob*) win_printer->printjob);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, const char* name, const wchar_t* drivername, BOOL is_default)
|
||||
static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver,
|
||||
const char* name, const WCHAR* drivername, BOOL is_default)
|
||||
{
|
||||
rdpWinPrinter* win_printer;
|
||||
wchar_t wname[256];
|
||||
|
@ -176,9 +190,16 @@ static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, cons
|
|||
size_t charsConverted;
|
||||
|
||||
win_printer = (rdpWinPrinter*) calloc(1, sizeof(rdpWinPrinter));
|
||||
if (!win_printer)
|
||||
return NULL;
|
||||
|
||||
win_printer->printer.id = win_driver->id_sequence++;
|
||||
win_printer->printer.name = _strdup(name);
|
||||
if (!win_printer->printer.name)
|
||||
{
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
win_printer->printer.is_default = is_default;
|
||||
|
||||
win_printer->printer.CreatePrintJob = printer_win_create_printjob;
|
||||
|
@ -190,10 +211,25 @@ static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, cons
|
|||
|
||||
GetPrinter(win_printer->hPrinter, 2, (LPBYTE) prninfo, 0, &needed);
|
||||
prninfo = (PRINTER_INFO_2*) GlobalAlloc(GPTR,needed);
|
||||
if (!prninfo)
|
||||
{
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
GetPrinter(win_printer->hPrinter, 2, (LPBYTE) prninfo, needed, &needed);
|
||||
|
||||
win_printer->printer.driver = malloc(1000);
|
||||
wcstombs_s(&charsConverted, win_printer->printer.driver, 1000, prninfo->pDriverName, _TRUNCATE);
|
||||
if (drivername)
|
||||
win_printer->printer.driver = _wcsdup(drivername);
|
||||
else
|
||||
win_printer->printer.driver = _wcsdup(prninfo->pDriverName);
|
||||
if (!win_printer->printer.driver)
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(win_printer->printer.name);
|
||||
free(win_printer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (rdpPrinter*)win_printer;
|
||||
}
|
||||
|
@ -214,6 +250,8 @@ static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
|||
|
||||
/* allocate array of PRINTER_INFO structures */
|
||||
prninfo = (PRINTER_INFO_2*) GlobalAlloc(GPTR,needed);
|
||||
if (!prninfo)
|
||||
return NULL;
|
||||
|
||||
/* call again */
|
||||
if (!EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE) prninfo, needed, &needed, &returned))
|
||||
|
@ -222,6 +260,11 @@ static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
|||
}
|
||||
|
||||
printers = (rdpPrinter**) calloc((returned + 1), sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
|
@ -236,13 +279,24 @@ static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
|||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name)
|
||||
static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver,
|
||||
const char* name, const char* driverName)
|
||||
{
|
||||
WCHAR* driverNameW = NULL;
|
||||
rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
|
||||
rdpPrinter *myPrinter = NULL;
|
||||
|
||||
myPrinter = printer_win_new_printer(win_driver, name, L"", win_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
|
||||
if (driverName)
|
||||
{
|
||||
ConvertToUnicode(CP_UTF8, 0, driverName, -1, &driverNameW, 0);
|
||||
if (!driverNameW)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
myPrinter = printer_win_new_printer(win_driver, name, driverNameW,
|
||||
win_driver->id_sequence == 1 ? TRUE : FALSE);
|
||||
free(driverNameW);
|
||||
|
||||
return myPrinter;
|
||||
}
|
||||
|
||||
|
@ -253,6 +307,8 @@ rdpPrinterDriver* printer_win_get_driver(void)
|
|||
if (!win_driver)
|
||||
{
|
||||
win_driver = (rdpWinPrinterDriver*) calloc(1, sizeof(rdpWinPrinterDriver));
|
||||
if (!win_driver)
|
||||
return NULL;
|
||||
|
||||
win_driver->driver.EnumPrinters = printer_win_enum_printers;
|
||||
win_driver->driver.GetPrinter = printer_win_get_printer;
|
||||
|
|
|
@ -26,14 +26,10 @@ rdpPrinterDriver* printer_win_get_driver(void);
|
|||
|
||||
#define PRINTER_TAG CHANNELS_TAG("printer.client")
|
||||
#ifdef WITH_DEBUG_WINPR
|
||||
#define DEBUG_WINPR(fmt, ...) WLog_DBG(PRINTER_TAG, fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_WINPR(...) WLog_DBG(PRINTER_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_WINPR(fmt, ...) do { } while (0)
|
||||
#define DEBUG_WINPR(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Definition for the printer channel
|
||||
*
|
||||
* Copyright 2016 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 __CHANNELS_PRINTER_PRINTER_H_
|
||||
#define __CHANNELS_PRINTER_PRINTER_H_
|
||||
|
||||
/* SERVER_PRINTER_CACHE_EVENT.cachedata */
|
||||
#define RDPDR_ADD_PRINTER_EVENT 0x00000001
|
||||
#define RDPDR_UPDATE_PRINTER_EVENT 0x00000002
|
||||
#define RDPDR_DELETE_PRINTER_EVENT 0x00000003
|
||||
#define RDPDR_RENAME_PRINTER_EVENT 0x00000004
|
||||
|
||||
/* DR_PRN_DEVICE_ANNOUNCE.Flags */
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_ASCII 0x00000001
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER 0x00000002
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER 0x00000004
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_TSPRINTER 0x00000008
|
||||
#define RDPDR_PRINTER_ANNOUNCE_FLAG_XPSFORMAT 0x00000010
|
||||
|
||||
|
||||
#endif /* __CHANNELS_PRINTER_PRINTER_H_ */
|
|
@ -31,6 +31,5 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,8 @@
|
|||
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
|
||||
* Copyright 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.
|
||||
|
@ -47,10 +49,11 @@ struct rail_plugin
|
|||
void* InitHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
typedef struct rail_plugin railPlugin;
|
||||
|
||||
RailClientContext* rail_get_client_interface(railPlugin* rail);
|
||||
void rail_send_channel_data(railPlugin* rail, void* data, size_t length);
|
||||
UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIENT_RAIL_MAIN_H */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2009 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2011 Roman Barabanov <romanbarabanov@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.
|
||||
|
@ -27,16 +29,16 @@
|
|||
|
||||
#define TAG CHANNELS_TAG("rail.client")
|
||||
|
||||
BOOL rail_read_server_exec_result_order(wStream* s, RAIL_EXEC_RESULT_ORDER* exec_result);
|
||||
BOOL rail_read_server_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
BOOL rail_read_server_minmaxinfo_order(wStream* s, RAIL_MINMAXINFO_ORDER* minmaxinfo);
|
||||
BOOL rail_read_server_localmovesize_order(wStream* s, RAIL_LOCALMOVESIZE_ORDER* localmovesize);
|
||||
BOOL rail_read_server_get_appid_resp_order(wStream* s, RAIL_GET_APPID_RESP_ORDER* get_appid_resp);
|
||||
BOOL rail_read_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbar_info);
|
||||
UINT rail_read_server_exec_result_order(wStream* s, RAIL_EXEC_RESULT_ORDER* exec_result);
|
||||
UINT rail_read_server_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
UINT rail_read_server_minmaxinfo_order(wStream* s, RAIL_MINMAXINFO_ORDER* minmaxinfo);
|
||||
UINT rail_read_server_localmovesize_order(wStream* s, RAIL_LOCALMOVESIZE_ORDER* localmovesize);
|
||||
UINT rail_read_server_get_appid_resp_order(wStream* s, RAIL_GET_APPID_RESP_ORDER* get_appid_resp);
|
||||
UINT rail_read_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbar_info);
|
||||
|
||||
void rail_write_client_status_order(wStream* s, RAIL_CLIENT_STATUS_ORDER* client_status);
|
||||
void rail_write_client_exec_order(wStream* s, RAIL_EXEC_ORDER* exec);
|
||||
void rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
UINT rail_write_client_exec_order(wStream* s, RAIL_EXEC_ORDER* exec);
|
||||
UINT rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
void rail_write_client_activate_order(wStream* s, RAIL_ACTIVATE_ORDER* activate);
|
||||
void rail_write_client_sysmenu_order(wStream* s, RAIL_SYSMENU_ORDER* sysmenu);
|
||||
void rail_write_client_syscommand_order(wStream* s, RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
|
@ -45,21 +47,21 @@ void rail_write_client_window_move_order(wStream* s, RAIL_WINDOW_MOVE_ORDER* win
|
|||
void rail_write_client_get_appid_req_order(wStream* s, RAIL_GET_APPID_REQ_ORDER* get_appid_req);
|
||||
void rail_write_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbar_info);
|
||||
|
||||
BOOL rail_order_recv(railPlugin* rail, wStream* s);
|
||||
void rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
|
||||
UINT rail_order_recv(railPlugin* rail, wStream* s);
|
||||
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
|
||||
|
||||
void rail_send_handshake_order(railPlugin* rail, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
void rail_send_handshake_ex_order(railPlugin* rail, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
void rail_send_client_status_order(railPlugin* rail, RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||
void rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec);
|
||||
void rail_send_client_sysparam_order(railPlugin* rail, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
void rail_send_client_sysparams_order(railPlugin* rail, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
void rail_send_client_activate_order(railPlugin* rail, RAIL_ACTIVATE_ORDER* activate);
|
||||
void rail_send_client_sysmenu_order(railPlugin* rail, RAIL_SYSMENU_ORDER* sysmenu);
|
||||
void rail_send_client_syscommand_order(railPlugin* rail, RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
void rail_send_client_notify_event_order(railPlugin* rail, RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
void rail_send_client_window_move_order(railPlugin* rail, RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||
void rail_send_client_get_appid_req_order(railPlugin* rail, RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
void rail_send_client_langbar_info_order(railPlugin* rail, RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
UINT rail_send_handshake_order(railPlugin* rail, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
UINT rail_send_client_status_order(railPlugin* rail, RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec);
|
||||
UINT rail_send_client_sysparam_order(railPlugin* rail, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
UINT rail_send_client_sysparams_order(railPlugin* rail, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, RAIL_ACTIVATE_ORDER* activate);
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, RAIL_SYSMENU_ORDER* sysmenu);
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail, RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail, RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail, RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
|
||||
#endif /* __RAIL_ORDERS_H */
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
|
||||
* Copyright 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.
|
||||
|
@ -54,8 +56,7 @@ void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_st
|
|||
WCHAR* buffer = NULL;
|
||||
int length = 0;
|
||||
|
||||
if (unicode_string->string != NULL)
|
||||
free(unicode_string->string);
|
||||
free(unicode_string->string);
|
||||
|
||||
unicode_string->string = NULL;
|
||||
unicode_string->length = 0;
|
||||
|
@ -69,15 +70,20 @@ void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_st
|
|||
unicode_string->length = (UINT16) length;
|
||||
}
|
||||
|
||||
BOOL rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return FALSE;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, *orderType); /* orderType (2 bytes) */
|
||||
Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
|
||||
|
||||
return TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
|
||||
|
@ -86,22 +92,29 @@ void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
|
|||
Stream_Write_UINT16(s, orderLength); /* orderLength (2 bytes) */
|
||||
}
|
||||
|
||||
wStream* rail_pdu_init(int length)
|
||||
wStream* rail_pdu_init(size_t length)
|
||||
{
|
||||
wStream* s;
|
||||
s = Stream_New(NULL, length + RAIL_PDU_HEADER_LENGTH);
|
||||
if (!s)
|
||||
return NULL;
|
||||
Stream_Seek(s, RAIL_PDU_HEADER_LENGTH);
|
||||
return s;
|
||||
}
|
||||
|
||||
BOOL rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return FALSE;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
|
||||
|
||||
return TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
|
@ -109,15 +122,20 @@ void rail_write_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
|||
Stream_Write_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
|
||||
}
|
||||
|
||||
BOOL rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return FALSE;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Read_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
|
||||
|
||||
return TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
|
||||
* Copyright 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.
|
||||
|
@ -43,13 +45,13 @@ extern const char* const RAIL_ORDER_TYPE_STRINGS[];
|
|||
#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* fixed */
|
||||
|
||||
void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_string);
|
||||
BOOL rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
void rail_write_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
BOOL rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
void rail_write_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
|
||||
wStream* rail_pdu_init(int length);
|
||||
BOOL rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
|
||||
wStream* rail_pdu_init(size_t length);
|
||||
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
|
||||
void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||
|
@ -32,7 +34,10 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
if(APPLE AND (NOT IOS))
|
||||
find_library(CORESERVICES_LIBRARY CoreServices)
|
||||
target_link_libraries(${MODULE_NAME} ${CORESERVICES_LIBRARY})
|
||||
endif()
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 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.
|
||||
|
@ -40,6 +43,9 @@
|
|||
|
||||
void devman_device_free(DEVICE* device)
|
||||
{
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
IFCALL(device->Free, device);
|
||||
}
|
||||
|
||||
|
@ -47,13 +53,27 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
|||
{
|
||||
DEVMAN* devman;
|
||||
|
||||
devman = (DEVMAN*) malloc(sizeof(DEVMAN));
|
||||
ZeroMemory(devman, sizeof(DEVMAN));
|
||||
if (!rdpdr)
|
||||
return NULL;
|
||||
|
||||
devman = (DEVMAN*) calloc(1, sizeof(DEVMAN));
|
||||
|
||||
if (!devman)
|
||||
{
|
||||
WLog_INFO(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devman->plugin = (void*) rdpdr;
|
||||
devman->id_sequence = 1;
|
||||
|
||||
devman->devices = ListDictionary_New(TRUE);
|
||||
if (!devman->devices)
|
||||
{
|
||||
WLog_INFO(TAG, "ListDictionary_New failed!");
|
||||
free(devman);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListDictionary_ValueObject(devman->devices)->fnObjectFree =
|
||||
(OBJECT_FREE_FN) devman_device_free;
|
||||
|
@ -71,20 +91,36 @@ void devman_unregister_device(DEVMAN* devman, void* key)
|
|||
{
|
||||
DEVICE* device;
|
||||
|
||||
if (!devman || !key)
|
||||
return;
|
||||
|
||||
device = (DEVICE*) ListDictionary_Remove(devman->devices, key);
|
||||
|
||||
if (device)
|
||||
devman_device_free(device);
|
||||
}
|
||||
|
||||
static void devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
{
|
||||
void* key = NULL;
|
||||
|
||||
if (!devman || !device)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
device->id = devman->id_sequence++;
|
||||
key = (void*) (size_t) device->id;
|
||||
|
||||
ListDictionary_Add(devman->devices, key, device);
|
||||
if (!ListDictionary_Add(devman->devices, key, device))
|
||||
{
|
||||
WLog_INFO(TAG, "ListDictionary_Add failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id)
|
||||
|
@ -92,6 +128,9 @@ DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id)
|
|||
DEVICE* device = NULL;
|
||||
void* key = (void*) (size_t) id;
|
||||
|
||||
if (!devman)
|
||||
return NULL;
|
||||
|
||||
device = (DEVICE*) ListDictionary_GetItemValue(devman->devices, key);
|
||||
|
||||
return device;
|
||||
|
@ -103,12 +142,20 @@ static char SMARTCARD_SERVICE_NAME[] = "smartcard";
|
|||
static char SERIAL_SERVICE_NAME[] = "serial";
|
||||
static char PARALLEL_SERVICE_NAME[] = "parallel";
|
||||
|
||||
BOOL devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext* rdpcontext)
|
||||
{
|
||||
char* ServiceName = NULL;
|
||||
DEVICE_SERVICE_ENTRY_POINTS ep;
|
||||
PDEVICE_SERVICE_ENTRY entry = NULL;
|
||||
|
||||
if (!devman || !device || !rdpcontext)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (device->Type == RDPDR_DTYP_FILESYSTEM)
|
||||
ServiceName = DRIVE_SERVICE_NAME;
|
||||
else if (device->Type == RDPDR_DTYP_PRINT)
|
||||
|
@ -121,19 +168,27 @@ BOOL devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device)
|
|||
ServiceName = PARALLEL_SERVICE_NAME;
|
||||
|
||||
if (!ServiceName)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_INFO(TAG, "ServiceName %s did not match!", ServiceName);
|
||||
return ERROR_INVALID_NAME;
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "Loading device service %s (static)", ServiceName);
|
||||
if (device->Name)
|
||||
WLog_INFO(TAG, "Loading device service %s [%s] (static)", ServiceName, device->Name);
|
||||
else
|
||||
WLog_INFO(TAG, "Loading device service %s (static)", ServiceName);
|
||||
entry = (PDEVICE_SERVICE_ENTRY) freerdp_load_channel_addin_entry(ServiceName, NULL, "DeviceServiceEntry", 0);
|
||||
|
||||
if (!entry)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_INFO(TAG, "freerdp_load_channel_addin_entry failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ep.devman = devman;
|
||||
ep.RegisterDevice = devman_register_device;
|
||||
ep.device = device;
|
||||
ep.rdpcontext = rdpcontext;
|
||||
|
||||
entry(&ep);
|
||||
|
||||
return TRUE;
|
||||
return entry(&ep);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-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.
|
||||
|
@ -24,7 +26,7 @@
|
|||
#include "rdpdr_main.h"
|
||||
|
||||
void devman_unregister_device(DEVMAN* devman, void* key);
|
||||
BOOL devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device);
|
||||
UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext* rdpcontext);
|
||||
DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id);
|
||||
|
||||
DEVMAN* devman_new(rdpdrPlugin* rdpdr);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-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.
|
||||
|
@ -33,21 +35,33 @@
|
|||
#include "devman.h"
|
||||
#include "irp.h"
|
||||
|
||||
static void irp_free(IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT irp_free(IRP* irp)
|
||||
{
|
||||
if (!irp)
|
||||
return;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
Stream_Free(irp->input, TRUE);
|
||||
Stream_Free(irp->output, TRUE);
|
||||
|
||||
_aligned_free(irp);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void irp_complete(IRP* irp)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT irp_complete(IRP* irp)
|
||||
{
|
||||
int pos;
|
||||
rdpdrPlugin* rdpdr;
|
||||
UINT error;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) irp->devman->plugin;
|
||||
|
||||
|
@ -56,28 +70,48 @@ static void irp_complete(IRP* irp)
|
|||
Stream_Write_UINT32(irp->output, irp->IoStatus); /* IoStatus (4 bytes) */
|
||||
Stream_SetPosition(irp->output, pos);
|
||||
|
||||
rdpdr_send(rdpdr, irp->output);
|
||||
error = rdpdr_send(rdpdr, irp->output);
|
||||
irp->output = NULL;
|
||||
|
||||
irp_free(irp);
|
||||
return error;
|
||||
}
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s)
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
||||
{
|
||||
IRP* irp;
|
||||
DEVICE* device;
|
||||
UINT32 DeviceId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 20)
|
||||
{
|
||||
if (error)
|
||||
*error = ERROR_INVALID_DATA;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */
|
||||
device = devman_get_device_by_id(devman, DeviceId);
|
||||
|
||||
if (!device)
|
||||
{
|
||||
WLog_WARN(TAG, "devman_get_device_by_id failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_OK;
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
||||
irp = (IRP*) _aligned_malloc(sizeof(IRP), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
|
||||
if (!irp)
|
||||
{
|
||||
WLog_ERR(TAG, "_aligned_malloc failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ZeroMemory(irp, sizeof(IRP));
|
||||
|
||||
|
@ -91,6 +125,14 @@ IRP* irp_new(DEVMAN* devman, wStream* s)
|
|||
Stream_Read_UINT32(s, irp->MinorFunction); /* MinorFunction (4 bytes) */
|
||||
|
||||
irp->output = Stream_New(NULL, 256);
|
||||
if (!irp->output)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
_aligned_free(irp);
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
Stream_Write_UINT16(irp->output, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
Stream_Write_UINT16(irp->output, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */
|
||||
Stream_Write_UINT32(irp->output, DeviceId); /* DeviceId (4 bytes) */
|
||||
|
@ -103,5 +145,8 @@ IRP* irp_new(DEVMAN* devman, wStream* s)
|
|||
irp->thread = NULL;
|
||||
irp->cancelled = FALSE;
|
||||
|
||||
if (error)
|
||||
*error = CHANNEL_RC_OK;
|
||||
|
||||
return irp;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s);
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015-2016 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -58,12 +61,21 @@ static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||
}
|
||||
|
||||
/* Process device direction general capability set */
|
||||
static void rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output printer direction capability set */
|
||||
|
@ -73,12 +85,21 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||
}
|
||||
|
||||
/* Process printer direction capability set */
|
||||
static void rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output port redirection capability set */
|
||||
|
@ -88,12 +109,21 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||
}
|
||||
|
||||
/* Process port redirection capability set */
|
||||
static void rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output drive redirection capability set */
|
||||
|
@ -103,12 +133,21 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||
}
|
||||
|
||||
/* Process drive redirection capability set */
|
||||
static void rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output smart card redirection capability set */
|
||||
|
@ -118,60 +157,94 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||
}
|
||||
|
||||
/* Process smartcard redirection capability set */
|
||||
static void rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
UINT16 i;
|
||||
UINT16 numCapabilities;
|
||||
UINT16 capabilityType;
|
||||
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, numCapabilities);
|
||||
Stream_Seek(s, 2); /* pad (2 bytes) */
|
||||
|
||||
for (i = 0; i < numCapabilities; i++)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < sizeof(UINT16))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityType);
|
||||
|
||||
switch (capabilityType)
|
||||
{
|
||||
case CAP_GENERAL_TYPE:
|
||||
rdpdr_process_general_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_GENERAL_TYPE:
|
||||
status = rdpdr_process_general_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_PRINTER_TYPE:
|
||||
rdpdr_process_printer_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_PRINTER_TYPE:
|
||||
status = rdpdr_process_printer_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_PORT_TYPE:
|
||||
rdpdr_process_port_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_PORT_TYPE:
|
||||
status = rdpdr_process_port_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_DRIVE_TYPE:
|
||||
rdpdr_process_drive_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_DRIVE_TYPE:
|
||||
status = rdpdr_process_drive_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
rdpdr_process_smartcard_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
status = rdpdr_process_smartcard_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
s = Stream_New(NULL, 256);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE);
|
||||
Stream_Write_UINT16(s, PAKID_CORE_CLIENT_CAPABILITY);
|
||||
|
@ -185,5 +258,5 @@ void rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
|||
rdpdr_write_drive_capset(rdpdr, s);
|
||||
rdpdr_write_smartcard_capset(rdpdr, s);
|
||||
|
||||
rdpdr_send(rdpdr, s);
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-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.
|
||||
|
@ -23,7 +25,7 @@
|
|||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
void rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s);
|
||||
void rdpdr_send_capability_response(rdpdrPlugin* rdpdr);
|
||||
UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s);
|
||||
UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_CAPABILITIES_H */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,10 @@
|
|||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* 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.
|
||||
|
@ -34,6 +38,10 @@
|
|||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#ifdef __MACOSX__
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||
|
||||
typedef struct rdpdr_plugin rdpdrPlugin;
|
||||
|
@ -62,11 +70,14 @@ struct rdpdr_plugin
|
|||
HANDLE hotplugThread;
|
||||
#ifdef _WIN32
|
||||
HWND hotplug_wnd;
|
||||
#elif __MACOSX__
|
||||
CFRunLoopRef runLoop;
|
||||
#else
|
||||
HANDLE stopEvent;
|
||||
#endif
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
int rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
||||
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */
|
||||
|
|
|
@ -27,6 +27,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,10 @@
|
|||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Device Redirection Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2014 Dell Software <Mike.McDonald@software.dell.com>
|
||||
* 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.
|
||||
|
@ -20,6 +23,7 @@
|
|||
#ifndef FREERDP_CHANNEL_SERVER_RDPDR_MAIN_H
|
||||
#define FREERDP_CHANNEL_SERVER_RDPDR_MAIN_H
|
||||
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
@ -39,6 +43,9 @@ struct _rdpdr_server_private
|
|||
char* ClientComputerName;
|
||||
|
||||
BOOL UserLoggedOnPdu;
|
||||
|
||||
wListDictionary* IrpList;
|
||||
UINT32 NextCompletionId;
|
||||
};
|
||||
|
||||
#define RDPDR_HEADER_LENGTH 4
|
||||
|
@ -67,4 +74,16 @@ struct _RDPDR_CAPABILITY_HEADER
|
|||
};
|
||||
typedef struct _RDPDR_CAPABILITY_HEADER RDPDR_CAPABILITY_HEADER;
|
||||
|
||||
struct _RDPDR_IRP
|
||||
{
|
||||
UINT32 CompletionId;
|
||||
UINT32 DeviceId;
|
||||
UINT32 FileId;
|
||||
char PathName[256];
|
||||
char ExtraBuffer[256];
|
||||
void *CallbackData;
|
||||
UINT (*Callback)(RdpdrServerContext* context, wStream* s, struct _RDPDR_IRP* irp, UINT32 deviceId, UINT32 completionId, UINT32 ioStatus);
|
||||
};
|
||||
typedef struct _RDPDR_IRP RDPDR_IRP;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_SERVER_RDPDR_MAIN_H */
|
||||
|
|
|
@ -30,6 +30,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -82,9 +82,9 @@ struct _RDPINPUT_CONTACT_POINT
|
|||
typedef struct _RDPINPUT_CONTACT_POINT RDPINPUT_CONTACT_POINT;
|
||||
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_DVC(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(fmt, ...) do { } while (0)
|
||||
#define DEBUG_DVC(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPEI_CLIENT_MAIN_H */
|
||||
|
|
|
@ -33,6 +33,5 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
|||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* Copyright 2014 Thincast Technologies Gmbh.
|
||||
* Copyright 2014 David FORT <contact@hardening-consulting.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.
|
||||
|
@ -90,7 +92,12 @@ out_free:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int rdpei_server_init(RdpeiServerContext *context)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpei_server_init(RdpeiServerContext *context)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
DWORD bytesReturned;
|
||||
|
@ -99,14 +106,13 @@ int rdpei_server_init(RdpeiServerContext *context)
|
|||
priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
if (!priv->channelHandle)
|
||||
{
|
||||
fprintf(stderr, "%s: unable to open channel\n", __FUNCTION__);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer, &bytesReturned) || (bytesReturned != sizeof(HANDLE)))
|
||||
{
|
||||
fprintf(stderr, "%s: error during WTSVirtualChannelQuery(WTSVirtualEventHandle) or invalid returned size(%d)\n",
|
||||
__FUNCTION__, bytesReturned);
|
||||
WLog_ERR(TAG, "WTSVirtualChannelQuery failed or invalid invalid returned size(%d)!", bytesReturned);
|
||||
if (buffer)
|
||||
WTSFreeMemory(buffer);
|
||||
goto out_close;
|
||||
|
@ -114,11 +120,11 @@ int rdpei_server_init(RdpeiServerContext *context)
|
|||
CopyMemory(&priv->eventHandle, buffer, sizeof(HANDLE));
|
||||
WTSFreeMemory(buffer);
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
out_close:
|
||||
WTSVirtualChannelClose(priv->channelHandle);
|
||||
return -1;
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,10 +155,19 @@ HANDLE rdpei_server_get_event_handle(RdpeiServerContext *context)
|
|||
}
|
||||
|
||||
|
||||
static int read_cs_ready_message(RdpeiServerContext *context, wStream *s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT read_cs_ready_message(RdpeiServerContext *context, wStream *s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
if (Stream_GetRemainingLength(s) < 10)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, context->protocolFlags);
|
||||
Stream_Read_UINT32(s, context->clientVersion);
|
||||
|
@ -164,26 +179,39 @@ static int read_cs_ready_message(RdpeiServerContext *context, wStream *s)
|
|||
case RDPINPUT_PROTOCOL_V101:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unhandled RPDEI protocol version 0x%x\n", __FUNCTION__, context->clientVersion);
|
||||
WLog_ERR(TAG, "unhandled RPDEI protocol version 0x%x", context->clientVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
if (context->onClientReady)
|
||||
context->onClientReady(context);
|
||||
return 0;
|
||||
IFCALLRET(context->onClientReady, error, context);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->onClientReady failed with error %lu", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int read_touch_contact_data(RdpeiServerContext *context, wStream *s, RDPINPUT_CONTACT_DATA *contactData)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT read_touch_contact_data(RdpeiServerContext *context, wStream *s, RDPINPUT_CONTACT_DATA *contactData)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT8(s, contactData->contactId);
|
||||
if (!rdpei_read_2byte_unsigned(s, &contactData->fieldsPresent) ||
|
||||
!rdpei_read_4byte_signed(s, &contactData->x) ||
|
||||
!rdpei_read_4byte_signed(s, &contactData->y) ||
|
||||
!rdpei_read_4byte_unsigned(s, &contactData->contactFlags))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (contactData->fieldsPresent & CONTACT_DATA_CONTACTRECT_PRESENT)
|
||||
{
|
||||
|
@ -191,113 +219,164 @@ static int read_touch_contact_data(RdpeiServerContext *context, wStream *s, RDPI
|
|||
!rdpei_read_2byte_signed(s, &contactData->contactRectTop) ||
|
||||
!rdpei_read_2byte_signed(s, &contactData->contactRectRight) ||
|
||||
!rdpei_read_2byte_signed(s, &contactData->contactRectBottom))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if ((contactData->fieldsPresent & CONTACT_DATA_ORIENTATION_PRESENT) &&
|
||||
!rdpei_read_4byte_unsigned(s, &contactData->orientation))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
if ((contactData->fieldsPresent & CONTACT_DATA_PRESSURE_PRESENT) &&
|
||||
!rdpei_read_4byte_unsigned(s, &contactData->pressure))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int read_touch_frame(RdpeiServerContext *context, wStream *s, RDPINPUT_TOUCH_FRAME *frame)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT read_touch_frame(RdpeiServerContext *context, wStream *s, RDPINPUT_TOUCH_FRAME *frame)
|
||||
{
|
||||
int i;
|
||||
RDPINPUT_CONTACT_DATA *contact;
|
||||
UINT error;
|
||||
|
||||
if (!rdpei_read_2byte_unsigned(s, &frame->contactCount) || !rdpei_read_8byte_unsigned(s, &frame->frameOffset))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
frame->contacts = contact = calloc(frame->contactCount, sizeof(RDPINPUT_CONTACT_DATA));
|
||||
if (!frame->contacts)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < frame->contactCount; i++, contact++)
|
||||
{
|
||||
if (read_touch_contact_data(context, s, contact) < 0)
|
||||
if ((error = read_touch_contact_data(context, s, contact)))
|
||||
{
|
||||
WLog_ERR(TAG, "read_touch_contact_data failed with error %lu!", error);
|
||||
frame->contactCount = i;
|
||||
goto out_cleanup;
|
||||
touch_frame_reset(frame);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_cleanup:
|
||||
touch_frame_reset(frame);
|
||||
return -1;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static int read_touch_event(RdpeiServerContext *context, wStream *s)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT read_touch_event(RdpeiServerContext *context, wStream *s)
|
||||
{
|
||||
UINT32 frameCount;
|
||||
int i, ret;
|
||||
int i;
|
||||
RDPINPUT_TOUCH_EVENT *event = &context->priv->touchEvent;
|
||||
RDPINPUT_TOUCH_FRAME *frame;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
ret = -1;
|
||||
if (!rdpei_read_4byte_unsigned(s, &event->encodeTime) || !rdpei_read_2byte_unsigned(s, &frameCount))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_read_ failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
event->frameCount = frameCount;
|
||||
event->frames = frame = calloc(event->frameCount, sizeof(RDPINPUT_TOUCH_FRAME));
|
||||
if (!event->frames)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < frameCount; i++, frame++)
|
||||
{
|
||||
if (read_touch_frame(context, s, frame) < 0)
|
||||
if ((error = read_touch_frame(context, s, frame)))
|
||||
{
|
||||
WLog_ERR(TAG, "read_touch_contact_data failed with error %lu!", error);
|
||||
event->frameCount = i;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->onTouchEvent)
|
||||
context->onTouchEvent(context, event);
|
||||
|
||||
ret = 0;
|
||||
IFCALLRET(context->onTouchEvent, error, context, event);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->onTouchEvent failed with error %lu", error);
|
||||
|
||||
out_cleanup:
|
||||
touch_event_reset(event);
|
||||
return ret;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int read_dismiss_hovering_contact(RdpeiServerContext *context, wStream *s) {
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT read_dismiss_hovering_contact(RdpeiServerContext *context, wStream *s) {
|
||||
BYTE contactId;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "Not enought data!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT8(s, contactId);
|
||||
if (context->onTouchReleased)
|
||||
context->onTouchReleased(context, contactId);
|
||||
return 0;
|
||||
|
||||
IFCALLRET(context->onTouchReleased, error, context, contactId);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->onTouchReleased failed with error %lu", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int rdpei_server_handle_messages(RdpeiServerContext *context) {
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpei_server_handle_messages(RdpeiServerContext *context) {
|
||||
DWORD bytesReturned;
|
||||
RdpeiServerPrivate *priv = context->priv;
|
||||
wStream *s = priv->inputStream;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!WTSVirtualChannelRead(priv->channelHandle, 0, (PCHAR)Stream_Pointer(s), priv->expectedBytes, &bytesReturned))
|
||||
{
|
||||
if (GetLastError() == ERROR_NO_DATA)
|
||||
return -1;
|
||||
return ERROR_READ_FAULT;
|
||||
|
||||
fprintf(stderr, "%s: channel connection closed\n", __FUNCTION__);
|
||||
return 0;
|
||||
WLog_DBG(TAG, "channel connection closed");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
priv->expectedBytes -= bytesReturned;
|
||||
Stream_Seek(s, bytesReturned);
|
||||
|
||||
if (priv->expectedBytes)
|
||||
return 1;
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
Stream_SealLength(s);
|
||||
Stream_SetPosition(s, 0);
|
||||
|
@ -312,16 +391,20 @@ int rdpei_server_handle_messages(RdpeiServerContext *context) {
|
|||
|
||||
if (pduLen < RDPINPUT_HEADER_LENGTH)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid pduLength %d\n", __FUNCTION__, pduLen);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "invalid pduLength %d", pduLen);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
priv->expectedBytes = pduLen - RDPINPUT_HEADER_LENGTH;
|
||||
priv->waitingHeaders = FALSE;
|
||||
Stream_SetPosition(s, 0);
|
||||
if (priv->expectedBytes)
|
||||
{
|
||||
Stream_EnsureCapacity(s, priv->expectedBytes);
|
||||
return 1;
|
||||
if (!Stream_EnsureCapacity(s, priv->expectedBytes))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,52 +414,65 @@ int rdpei_server_handle_messages(RdpeiServerContext *context) {
|
|||
case EVENTID_CS_READY:
|
||||
if (priv->automataState != STATE_WAITING_CLIENT_READY)
|
||||
{
|
||||
fprintf(stderr, "%s: not expecting a CS_READY packet in this state(%d)\n", __FUNCTION__, (int)priv->automataState);
|
||||
return 0;
|
||||
WLog_ERR(TAG, "not expecting a CS_READY packet in this state(%d)", (int)priv->automataState);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (read_cs_ready_message(context, s) < 0)
|
||||
return 0;
|
||||
if ((error = read_cs_ready_message(context, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "read_cs_ready_message failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENTID_TOUCH:
|
||||
if (read_touch_event(context, s) < 0)
|
||||
if ((error = read_touch_event(context, s)))
|
||||
{
|
||||
fprintf(stderr, "%s: error in touch event packet\n", __FUNCTION__);
|
||||
return 0;
|
||||
WLog_ERR(TAG, "read_touch_event failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
case EVENTID_DISMISS_HOVERING_CONTACT:
|
||||
if (read_dismiss_hovering_contact(context, s) < 0)
|
||||
if ((error = read_dismiss_hovering_contact(context, s)))
|
||||
{
|
||||
fprintf(stderr, "%s: error reading read_dismiss_hovering_contact\n", __FUNCTION__);
|
||||
return 0;
|
||||
WLog_ERR(TAG, "read_dismiss_hovering_contact failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unexpected message type 0x%x\n", __FUNCTION__, priv->currentMsgType);
|
||||
WLog_ERR(TAG, "unexpected message type 0x%x", priv->currentMsgType);
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
priv->waitingHeaders = TRUE;
|
||||
priv->expectedBytes = RDPINPUT_HEADER_LENGTH;
|
||||
return 1;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int rdpei_server_send_sc_ready(RdpeiServerContext *context, UINT32 version)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpei_server_send_sc_ready(RdpeiServerContext *context, UINT32 version)
|
||||
{
|
||||
ULONG written;
|
||||
RdpeiServerPrivate *priv = context->priv;
|
||||
|
||||
if (priv->automataState != STATE_INITIAL)
|
||||
{
|
||||
fprintf(stderr, "%s: called from unexpected state %d\n", __FUNCTION__, priv->automataState);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "called from unexpected state %d", (int)priv->automataState);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Stream_SetPosition(priv->outputStream, 0);
|
||||
Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4);
|
||||
|
||||
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
|
||||
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4);
|
||||
|
@ -385,15 +481,20 @@ int rdpei_server_send_sc_ready(RdpeiServerContext *context, UINT32 version)
|
|||
if (!WTSVirtualChannelWrite(priv->channelHandle, (PCHAR)Stream_Buffer(priv->outputStream),
|
||||
Stream_GetPosition(priv->outputStream), &written))
|
||||
{
|
||||
fprintf(stderr, "%s: error writing ready message\n", __FUNCTION__);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
priv->automataState = STATE_WAITING_CLIENT_READY;
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
int rdpei_server_suspend(RdpeiServerContext *context)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpei_server_suspend(RdpeiServerContext *context)
|
||||
{
|
||||
ULONG written;
|
||||
RdpeiServerPrivate *priv = context->priv;
|
||||
|
@ -401,17 +502,21 @@ int rdpei_server_suspend(RdpeiServerContext *context)
|
|||
switch (priv->automataState)
|
||||
{
|
||||
case STATE_SUSPENDED:
|
||||
fprintf(stderr, "%s: already suspended\n", __FUNCTION__);
|
||||
return 0;
|
||||
WLog_ERR(TAG, "already suspended");
|
||||
return CHANNEL_RC_OK;
|
||||
case STATE_WAITING_FRAME:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: called from unexpected state %d\n", __FUNCTION__, priv->automataState);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "called from unexpected state %d", (int)priv->automataState);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Stream_SetPosition(priv->outputStream, 0);
|
||||
Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH);
|
||||
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(priv->outputStream, EVENTID_SUSPEND_TOUCH);
|
||||
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH);
|
||||
|
@ -419,16 +524,21 @@ int rdpei_server_suspend(RdpeiServerContext *context)
|
|||
if (!WTSVirtualChannelWrite(priv->channelHandle, (PCHAR)Stream_Buffer(priv->outputStream),
|
||||
Stream_GetPosition(priv->outputStream), &written))
|
||||
{
|
||||
fprintf(stderr, "%s: error writing suspendTouch message\n", __FUNCTION__);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
priv->automataState = STATE_SUSPENDED;
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
|
||||
int rdpei_server_resume(RdpeiServerContext *context)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpei_server_resume(RdpeiServerContext *context)
|
||||
{
|
||||
ULONG written;
|
||||
RdpeiServerPrivate *priv = context->priv;
|
||||
|
@ -436,17 +546,21 @@ int rdpei_server_resume(RdpeiServerContext *context)
|
|||
switch (priv->automataState)
|
||||
{
|
||||
case STATE_WAITING_FRAME:
|
||||
fprintf(stderr, "%s: not suspended\n", __FUNCTION__);
|
||||
return 0;
|
||||
WLog_ERR(TAG, "not suspended");
|
||||
return CHANNEL_RC_OK;
|
||||
case STATE_SUSPENDED:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: called from unexpected state %d\n", __FUNCTION__, priv->automataState);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "called from unexpected state %d", (int)priv->automataState);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Stream_SetPosition(priv->outputStream, 0);
|
||||
Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH);
|
||||
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(priv->outputStream, EVENTID_RESUME_TOUCH);
|
||||
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH);
|
||||
|
@ -454,11 +568,11 @@ int rdpei_server_resume(RdpeiServerContext *context)
|
|||
if (!WTSVirtualChannelWrite(priv->channelHandle, (PCHAR)Stream_Buffer(priv->outputStream),
|
||||
Stream_GetPosition(priv->outputStream), &written))
|
||||
{
|
||||
fprintf(stderr, "%s: error writing resumeTouch message\n", __FUNCTION__);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
priv->automataState = STATE_WAITING_FRAME;
|
||||
return 0;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Extended Input channel server-side implementation
|
||||
*
|
||||
* Copyright 2014 David Fort <contact@hardening-consulting.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.
|
||||
|
@ -23,6 +25,10 @@
|
|||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpei.server")
|
||||
|
||||
|
||||
|
||||
#endif /* FREERDP_CHANNEL_SERVER_RDPEI_MAIN_H_ */
|
||||
|
|
|
@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS)
|
|||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
|
|
@ -22,8 +22,8 @@ set(${MODULE_PREFIX}_SRCS
|
|||
rdpgfx_main.h
|
||||
rdpgfx_codec.c
|
||||
rdpgfx_codec.h
|
||||
rdpgfx_common.c
|
||||
rdpgfx_common.h)
|
||||
../rdpgfx_common.c
|
||||
../rdpgfx_common.h)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
|
@ -33,7 +33,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
|||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Graphics Pipeline Extension
|
||||
*
|
||||
* 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.
|
||||
|
@ -31,65 +33,74 @@
|
|||
|
||||
#define TAG CHANNELS_TAG("rdpgfx.client")
|
||||
|
||||
int rdpgfx_decode_uncompressed(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_remotefx(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_clear(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_planar(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdpgfx_read_h264_metablock(RDPGFX_PLUGIN* gfx, wStream* s, RDPGFX_H264_METABLOCK* meta)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_read_h264_metablock(RDPGFX_PLUGIN* gfx, wStream* s,
|
||||
RDPGFX_H264_METABLOCK* meta)
|
||||
{
|
||||
UINT32 index;
|
||||
RDPGFX_RECT16* regionRect;
|
||||
RECTANGLE_16* regionRect;
|
||||
RDPGFX_H264_QUANT_QUALITY* quantQualityVal;
|
||||
UINT error = ERROR_INVALID_DATA;
|
||||
|
||||
meta->regionRects = NULL;
|
||||
meta->quantQualityVals = NULL;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */
|
||||
|
||||
if (Stream_GetRemainingLength(s) < (meta->numRegionRects * 8))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
meta->regionRects = (RDPGFX_RECT16*) malloc(meta->numRegionRects * sizeof(RDPGFX_RECT16));
|
||||
meta->regionRects = (RECTANGLE_16*) malloc(meta->numRegionRects * sizeof(RECTANGLE_16));
|
||||
|
||||
if (!meta->regionRects)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
meta->quantQualityVals = (RDPGFX_H264_QUANT_QUALITY*) malloc(meta->numRegionRects * sizeof(RDPGFX_H264_QUANT_QUALITY));
|
||||
|
||||
if (!meta->quantQualityVals)
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "H264_METABLOCK: numRegionRects: %d", (int) meta->numRegionRects);
|
||||
|
||||
for (index = 0; index < meta->numRegionRects; index++)
|
||||
{
|
||||
regionRect = &(meta->regionRects[index]);
|
||||
rdpgfx_read_rect16(s, regionRect);
|
||||
if ((error = rdpgfx_read_rect16(s, regionRect)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_read_rect16 failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
WLog_DBG(TAG, "regionRects[%d]: left: %d top: %d right: %d bottom: %d",
|
||||
index, regionRect->left, regionRect->top, regionRect->right, regionRect->bottom);
|
||||
}
|
||||
|
||||
if (Stream_GetRemainingLength(s) < (meta->numRegionRects * 2))
|
||||
return -1;
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data!");
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
for (index = 0; index < meta->numRegionRects; index++)
|
||||
{
|
||||
|
@ -104,30 +115,39 @@ int rdpgfx_read_h264_metablock(RDPGFX_PLUGIN* gfx, wStream* s, RDPGFX_H264_METAB
|
|||
index, quantQualityVal->qp, quantQualityVal->r, quantQualityVal->p, quantQualityVal->qualityVal);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(meta->regionRects);
|
||||
meta->regionRects = NULL;
|
||||
free(meta->quantQualityVals);
|
||||
meta->quantQualityVals = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_h264(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
int status;
|
||||
UINT error;
|
||||
wStream* s;
|
||||
RDPGFX_H264_BITMAP_STREAM h264;
|
||||
RDPGFX_AVC420_BITMAP_STREAM h264;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface;
|
||||
|
||||
s = Stream_New(cmd->data, cmd->length);
|
||||
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
status = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta));
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
if (h264.meta.regionRects)
|
||||
free(h264.meta.regionRects);
|
||||
if (h264.meta.quantQualityVals)
|
||||
free(h264.meta.quantQualityVals);
|
||||
return -1;
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta))))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
h264.data = Stream_Pointer(s);
|
||||
|
@ -137,64 +157,141 @@ int rdpgfx_decode_h264(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
|||
|
||||
cmd->extra = (void*) &h264;
|
||||
|
||||
if (context && context->SurfaceCommand)
|
||||
if (context)
|
||||
{
|
||||
context->SurfaceCommand(context, cmd);
|
||||
IFCALLRET(context->SurfaceCommand, error, context, cmd);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SurfaceCommand failed with error %lu", error);
|
||||
}
|
||||
|
||||
free(h264.meta.regionRects);
|
||||
free(h264.meta.quantQualityVals);
|
||||
|
||||
return 1;
|
||||
return error;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_alpha(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
UINT error;
|
||||
UINT32 tmp;
|
||||
size_t pos1, pos2;
|
||||
wStream* s;
|
||||
RDPGFX_AVC444_BITMAP_STREAM h264;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface;
|
||||
|
||||
s = Stream_New(cmd->data, cmd->length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, tmp);
|
||||
h264.cbAvc420EncodedBitstream1 = tmp & 0x3FFFFFFFUL;
|
||||
h264.LC = (tmp >> 30UL) & 0x03UL;
|
||||
|
||||
if (h264.LC == 0x03)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
pos1 = Stream_GetPosition(s);
|
||||
if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[0].meta))))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
pos2 = Stream_GetPosition(s);
|
||||
|
||||
h264.bitstream[0].data = Stream_Pointer(s);
|
||||
|
||||
if (h264.LC == 0)
|
||||
{
|
||||
tmp = h264.cbAvc420EncodedBitstream1 - pos2 + pos1;
|
||||
if (Stream_GetRemainingLength(s) < tmp)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
h264.bitstream[0].length = tmp;
|
||||
Stream_Seek(s, tmp);
|
||||
|
||||
if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[1].meta))))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
h264.bitstream[1].data = Stream_Pointer(s);
|
||||
h264.bitstream[1].length = Stream_GetRemainingLength(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
h264.bitstream[0].length = Stream_GetRemainingLength(s);
|
||||
memset(&h264.bitstream[1], 0, sizeof(h264.bitstream[1]));
|
||||
}
|
||||
|
||||
Stream_Free(s, FALSE);
|
||||
|
||||
cmd->extra = (void*) &h264;
|
||||
|
||||
if (context)
|
||||
{
|
||||
IFCALLRET(context->SurfaceCommand, error, context, cmd);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SurfaceCommand failed with error %lu", error);
|
||||
}
|
||||
|
||||
free(h264.bitstream[0].meta.regionRects);
|
||||
free(h264.bitstream[0].meta.quantQualityVals);
|
||||
free(h264.bitstream[1].meta.regionRects);
|
||||
free(h264.bitstream[1].meta.quantQualityVals);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int rdpgfx_decode_progressive(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
|
||||
{
|
||||
int status;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface;
|
||||
|
||||
switch (cmd->codecId)
|
||||
{
|
||||
case RDPGFX_CODECID_UNCOMPRESSED:
|
||||
status = rdpgfx_decode_uncompressed(gfx, cmd);
|
||||
case RDPGFX_CODECID_AVC420:
|
||||
if ((error = rdpgfx_decode_AVC420(gfx, cmd)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_decode_AVC420 failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_CAVIDEO:
|
||||
status = rdpgfx_decode_remotefx(gfx, cmd);
|
||||
case RDPGFX_CODECID_AVC444:
|
||||
if ((error = rdpgfx_decode_AVC444(gfx, cmd)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpgfx_decode_AVC444 failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_CLEARCODEC:
|
||||
status = rdpgfx_decode_clear(gfx, cmd);
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_PLANAR:
|
||||
status = rdpgfx_decode_planar(gfx, cmd);
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_H264:
|
||||
status = rdpgfx_decode_h264(gfx, cmd);
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_ALPHA:
|
||||
status = rdpgfx_decode_alpha(gfx, cmd);
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_CAPROGRESSIVE:
|
||||
status = rdpgfx_decode_progressive(gfx, cmd);
|
||||
break;
|
||||
|
||||
case RDPGFX_CODECID_CAPROGRESSIVE_V2:
|
||||
default:
|
||||
if (context)
|
||||
{
|
||||
IFCALLRET(context->SurfaceCommand, error, context, cmd);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SurfaceCommand failed with error %lu", error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Graphics Pipeline Extension
|
||||
*
|
||||
* 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.
|
||||
|
@ -24,9 +26,10 @@
|
|||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include "rdpgfx_main.h"
|
||||
|
||||
int rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd);
|
||||
FREERDP_LOCAL UINT rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPGFX_CLIENT_CODEC_H */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,8 +30,7 @@
|
|||
#include <freerdp/client/rdpgfx.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/codec/zgfx.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpgfx.client")
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
struct _RDPGFX_CHANNEL_CALLBACK
|
||||
{
|
||||
|
@ -60,7 +59,6 @@ struct _RDPGFX_PLUGIN
|
|||
IWTSListener* listener;
|
||||
RDPGFX_LISTENER_CALLBACK* listener_callback;
|
||||
|
||||
wLog* log;
|
||||
rdpSettings* settings;
|
||||
|
||||
BOOL ThinClient;
|
||||
|
@ -68,15 +66,18 @@ struct _RDPGFX_PLUGIN
|
|||
BOOL Progressive;
|
||||
BOOL ProgressiveV2;
|
||||
BOOL H264;
|
||||
BOOL AVC444;
|
||||
|
||||
ZGFX_CONTEXT* zgfx;
|
||||
UINT32 UnacknowledgedFrames;
|
||||
UINT32 TotalDecodedFrames;
|
||||
BOOL suspendFrameAcks;
|
||||
|
||||
wHashTable* SurfaceTable;
|
||||
|
||||
UINT16 MaxCacheSlot;
|
||||
void* CacheSlots[25600];
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
typedef struct _RDPGFX_PLUGIN RDPGFX_PLUGIN;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue