Compare commits
49 Commits
master
...
debian/bus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a62ef46b9b | ||
|
|
528ad1ee7f | ||
|
|
9c2cb890eb | ||
|
|
1cddd4643a | ||
|
|
ad925990b3 | ||
|
|
cb0fee7d81 | ||
|
|
343a6e9965 | ||
|
|
180fc8f332 | ||
|
|
326341043d | ||
|
|
b7fad42bde | ||
|
|
eb2c3e598e | ||
|
|
ae332a9e65 | ||
|
|
9abf5f033b | ||
|
|
56d1291988 | ||
|
|
3f665697f0 | ||
|
|
a8d9578fb9 | ||
|
|
14442af9ac | ||
|
|
8f234906e9 | ||
|
|
6ebfab5ec1 | ||
|
|
bb305bcdf7 | ||
|
|
f1641af0fd | ||
|
|
b743b819c0 | ||
|
|
d9179e0766 | ||
|
|
24c51f4ed8 | ||
|
|
f6f2bf7896 | ||
|
|
0f3fd7d339 | ||
|
|
d1217c6dad | ||
|
|
09055cabae | ||
|
|
ab18013d96 | ||
|
|
2dacc519e5 | ||
|
|
792f6a14d1 | ||
|
|
a4c483bc30 | ||
|
|
43cbb16760 | ||
|
|
500b4499a7 | ||
|
|
6ae95183f4 | ||
|
|
21305b53c4 | ||
|
|
efc916e1a4 | ||
|
|
b6e609f697 | ||
|
|
0333c99067 | ||
|
|
6e4db706a5 | ||
|
|
0359d79a4b | ||
|
|
6dd3e7854d | ||
|
|
03201de47f | ||
|
|
4db4aa6d33 | ||
|
|
0553695929 | ||
|
|
bdab8340a1 | ||
|
|
163efd4c7d | ||
|
|
85341458bc | ||
|
|
c6fe442da0 |
@ -104,6 +104,7 @@ ForEachMacros:
|
|||||||
...
|
...
|
||||||
Language: ObjC
|
Language: ObjC
|
||||||
PointerBindsToType: false
|
PointerBindsToType: false
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
SortIncludes: false
|
SortIncludes: false
|
||||||
ObjCBlockIndentWidth: 4
|
ObjCBlockIndentWidth: 4
|
||||||
ObjCSpaceAfterProperty: false
|
ObjCSpaceAfterProperty: false
|
||||||
|
|||||||
@ -41,6 +41,7 @@ addons:
|
|||||||
- libgsm1-dev
|
- libgsm1-dev
|
||||||
- libavcodec-dev
|
- libavcodec-dev
|
||||||
- libavutil-dev
|
- libavutil-dev
|
||||||
|
- libx264-dev
|
||||||
- libxext-dev
|
- libxext-dev
|
||||||
- ninja-build
|
- ninja-build
|
||||||
- libsystemd-dev
|
- libsystemd-dev
|
||||||
|
|||||||
128
CMakeLists.txt
128
CMakeLists.txt
@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR)
|
|||||||
set(FREERDP_VENDOR 1)
|
set(FREERDP_VENDOR 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON)
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON)
|
|
||||||
option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
# Include our extra modules
|
# Include our extra modules
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||||
@ -74,10 +74,6 @@ include(InstallFreeRDPMan)
|
|||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
include(SetFreeRDPCMakeInstallDir)
|
include(SetFreeRDPCMakeInstallDir)
|
||||||
|
|
||||||
if (DEFINE_NO_DEPRECATED)
|
|
||||||
add_definitions(-DDEFINE_NO_DEPRECATED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Soname versioning
|
# Soname versioning
|
||||||
set(BUILD_NUMBER 0)
|
set(BUILD_NUMBER 0)
|
||||||
if ($ENV{BUILD_NUMBER})
|
if ($ENV{BUILD_NUMBER})
|
||||||
@ -85,7 +81,7 @@ if ($ENV{BUILD_NUMBER})
|
|||||||
endif()
|
endif()
|
||||||
set(WITH_LIBRARY_VERSIONING "ON")
|
set(WITH_LIBRARY_VERSIONING "ON")
|
||||||
|
|
||||||
set(RAW_VERSION_STRING "2.11.7")
|
set(RAW_VERSION_STRING "2.3.0")
|
||||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
||||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
||||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||||
@ -111,24 +107,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
|
message("FREERDP_VERSION=${FREERDP_VERSION_FULL}")
|
||||||
|
|
||||||
if(EXISTS "${PROJECT_SOURCE_DIR}/.source_version" )
|
|
||||||
file(READ ${PROJECT_SOURCE_DIR}/.source_version GIT_REVISION)
|
|
||||||
|
|
||||||
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
|
||||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
|
||||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
|
||||||
|
|
||||||
if (${GIT_REVISION} STREQUAL "n/a")
|
|
||||||
git_rev_parse (GIT_REVISION --short)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT GIT_REVISION)
|
|
||||||
set(GIT_REVISION ${FREERDP_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
|
||||||
|
|
||||||
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
||||||
|
|
||||||
# Compatibility options
|
# Compatibility options
|
||||||
@ -165,11 +143,25 @@ if(CCACHE AND WITH_CCACHE)
|
|||||||
endif()
|
endif()
|
||||||
endif(CCACHE AND WITH_CCACHE)
|
endif(CCACHE AND WITH_CCACHE)
|
||||||
|
|
||||||
|
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_version" )
|
||||||
|
file(READ ${CMAKE_SOURCE_DIR}/.source_version GIT_REVISION)
|
||||||
|
|
||||||
|
string(STRIP ${GIT_REVISION} GIT_REVISION)
|
||||||
|
else()
|
||||||
|
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||||
|
|
||||||
|
if (${GIT_REVISION} STREQUAL "n/a")
|
||||||
|
git_rev_parse (GIT_REVISION --short)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CMAKE_CROSSCOMPILING)
|
if(CMAKE_CROSSCOMPILING)
|
||||||
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||||
endif(CMAKE_CROSSCOMPILING)
|
endif(CMAKE_CROSSCOMPILING)
|
||||||
# /Allow to search the host machine for git/ccache
|
# /Allow to search the host machine for git/ccache
|
||||||
|
|
||||||
|
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||||
|
|
||||||
# Turn on solution folders (2.8.4+)
|
# Turn on solution folders (2.8.4+)
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
@ -225,7 +217,7 @@ endif()
|
|||||||
if(MSVC)
|
if(MSVC)
|
||||||
include(MSVCRuntime)
|
include(MSVCRuntime)
|
||||||
if(NOT DEFINED MSVC_RUNTIME)
|
if(NOT DEFINED MSVC_RUNTIME)
|
||||||
set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]")
|
set(MSVC_RUNTIME "dynamic")
|
||||||
endif()
|
endif()
|
||||||
if(MSVC_RUNTIME STREQUAL "static")
|
if(MSVC_RUNTIME STREQUAL "static")
|
||||||
if(BUILD_SHARED_LIBS)
|
if(BUILD_SHARED_LIBS)
|
||||||
@ -355,7 +347,7 @@ if(NOT IOS)
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT IOS)
|
if(NOT WIN32)
|
||||||
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||||
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
|
||||||
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
|
||||||
@ -532,6 +524,7 @@ add_definitions(-DWINPR_EXPORTS -DFREERDP_EXPORTS)
|
|||||||
if(NOT IOS)
|
if(NOT IOS)
|
||||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||||
|
check_include_files(execinfo.h HAVE_EXECINFO_H)
|
||||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_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/filio.h HAVE_SYS_FILIO_H)
|
||||||
@ -539,18 +532,6 @@ if(NOT IOS)
|
|||||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||||
check_include_files(execinfo.h HAVE_EXECINFO_HEADER)
|
|
||||||
if (HAVE_EXECINFO_HEADER)
|
|
||||||
check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
|
|
||||||
check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS)
|
|
||||||
check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
|
||||||
|
|
||||||
# Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define
|
|
||||||
# the backtrace functions. Disable detection for these cases
|
|
||||||
if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD)
|
|
||||||
set(HAVE_EXECINFO_H ON)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
set(HAVE_FCNTL_H 1)
|
set(HAVE_FCNTL_H 1)
|
||||||
set(HAVE_UNISTD_H 1)
|
set(HAVE_UNISTD_H 1)
|
||||||
@ -609,11 +590,6 @@ if(ANDROID)
|
|||||||
set (WITH_NEON OFF)
|
set (WITH_NEON OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ANDROID_ABI STREQUAL arm64-v8a)
|
|
||||||
# https://github.com/android/ndk/issues/910
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||||
add_definitions(-DNDK_DEBUG=1)
|
add_definitions(-DNDK_DEBUG=1)
|
||||||
|
|
||||||
@ -672,7 +648,6 @@ if(UNIX OR CYGWIN)
|
|||||||
include(CheckFunctionExists)
|
include(CheckFunctionExists)
|
||||||
|
|
||||||
check_function_exists(getlogin_r HAVE_GETLOGIN_R)
|
check_function_exists(getlogin_r HAVE_GETLOGIN_R)
|
||||||
check_function_exists(getpwuid_r HAVE_GETPWUID_R)
|
|
||||||
else()
|
else()
|
||||||
set(X11_FEATURE_TYPE "DISABLED")
|
set(X11_FEATURE_TYPE "DISABLED")
|
||||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||||
@ -730,7 +705,7 @@ set(FFMPEG_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback
|
|||||||
|
|
||||||
set(VAAPI_FEATURE_TYPE "OPTIONAL")
|
set(VAAPI_FEATURE_TYPE "OPTIONAL")
|
||||||
set(VAAPI_FEATURE_PURPOSE "multimedia")
|
set(VAAPI_FEATURE_PURPOSE "multimedia")
|
||||||
set(VAAPI_FEATURE_DESCRIPTION "[experimental] VA-API hardware acceleration for video playback")
|
set(VAAPI_FEATURE_DESCRIPTION "VA-API hardware acceleration for video playback")
|
||||||
|
|
||||||
set(IPP_FEATURE_TYPE "OPTIONAL")
|
set(IPP_FEATURE_TYPE "OPTIONAL")
|
||||||
set(IPP_FEATURE_PURPOSE "performance")
|
set(IPP_FEATURE_PURPOSE "performance")
|
||||||
@ -740,13 +715,17 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
|
|||||||
set(JPEG_FEATURE_PURPOSE "codec")
|
set(JPEG_FEATURE_PURPOSE "codec")
|
||||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
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_TYPE "OPTIONAL")
|
||||||
set(OPENH264_FEATURE_PURPOSE "codec")
|
set(OPENH264_FEATURE_PURPOSE "codec")
|
||||||
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
||||||
|
|
||||||
set(OPENCL_FEATURE_TYPE "OPTIONAL")
|
set(OPENCL_FEATURE_TYPE "OPTIONAL")
|
||||||
set(OPENCL_FEATURE_PURPOSE "codec")
|
set(OPENCL_FEATURE_PURPOSE "codec")
|
||||||
set(OPENCL_FEATURE_DESCRIPTION "[experimental] use OpenCL library")
|
set(OPENCL_FEATURE_DESCRIPTION "use OpenCL library")
|
||||||
|
|
||||||
set(GSM_FEATURE_TYPE "OPTIONAL")
|
set(GSM_FEATURE_TYPE "OPTIONAL")
|
||||||
set(GSM_FEATURE_PURPOSE "codec")
|
set(GSM_FEATURE_PURPOSE "codec")
|
||||||
@ -762,7 +741,7 @@ set(FAAD2_FEATURE_DESCRIPTION "FAAD2 AAC audio codec library")
|
|||||||
|
|
||||||
set(FAAC_FEATURE_TYPE "OPTIONAL")
|
set(FAAC_FEATURE_TYPE "OPTIONAL")
|
||||||
set(FAAC_FEATURE_PURPOSE "codec")
|
set(FAAC_FEATURE_PURPOSE "codec")
|
||||||
set(FAAC_FEATURE_DESCRIPTION "[experimental] FAAC AAC audio codec library")
|
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library")
|
||||||
|
|
||||||
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
||||||
set(SOXR_FEATURE_PURPOSE "codec")
|
set(SOXR_FEATURE_PURPOSE "codec")
|
||||||
@ -770,7 +749,7 @@ set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
|
|||||||
|
|
||||||
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
||||||
set(GSSAPI_FEATURE_PURPOSE "auth")
|
set(GSSAPI_FEATURE_PURPOSE "auth")
|
||||||
set(GSSAPI_FEATURE_DESCRIPTION "[experimental] add kerberos support")
|
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(X11_FEATURE_TYPE "DISABLED")
|
set(X11_FEATURE_TYPE "DISABLED")
|
||||||
@ -825,6 +804,7 @@ if(ANDROID)
|
|||||||
set(PULSE_FEATURE_TYPE "DISABLED")
|
set(PULSE_FEATURE_TYPE "DISABLED")
|
||||||
set(CUPS_FEATURE_TYPE "DISABLED")
|
set(CUPS_FEATURE_TYPE "DISABLED")
|
||||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||||
|
set(FFMPEG_FEATURE_TYPE "DISABLED")
|
||||||
set(VAAPI_FEATURE_TYPE "DISABLED")
|
set(VAAPI_FEATURE_TYPE "DISABLED")
|
||||||
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
||||||
endif()
|
endif()
|
||||||
@ -847,6 +827,7 @@ find_feature(PCSC ${PCSC_FEATURE_TYPE} ${PCSC_FEATURE_PURPOSE} ${PCSC_FEATURE_DE
|
|||||||
find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION})
|
find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION})
|
||||||
|
|
||||||
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_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(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||||
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
|
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
|
||||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||||
@ -872,23 +853,18 @@ if (WITH_DSP_FFMPEG)
|
|||||||
# Deactivate FFmpeg backend for sound, if the version is too old.
|
# Deactivate FFmpeg backend for sound, if the version is too old.
|
||||||
# See libfreerdp/codec/dsp_ffmpeg.h
|
# See libfreerdp/codec/dsp_ffmpeg.h
|
||||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||||
if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h")
|
|
||||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
|
||||||
list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
FOREACH(item ${AV_VERSION_FILE})
|
FOREACH(item ${AV_VERSION_FILE})
|
||||||
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
||||||
IF(litem)
|
IF(litem)
|
||||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||||
list(GET VSPLIT_LINE 0 VNAME)
|
list(GET VSPLIT_LINE 0 VNAME)
|
||||||
list(GET VSPLIT_LINE 1 VVALUE)
|
list(GET VSPLIT_LINE 1 VVALUE)
|
||||||
set(${VNAME} ${VVALUE})
|
set(${VNAME} ${VVALUE})
|
||||||
ENDIF(litem)
|
ENDIF(litem)
|
||||||
ENDFOREACH(item ${AV_VERSION_FILE})
|
ENDFOREACH(item ${AV_VERSION_FILE})
|
||||||
|
|
||||||
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
||||||
@ -937,7 +913,7 @@ if(MBEDTLS_FOUND)
|
|||||||
add_definitions("-DWITH_MBEDTLS")
|
add_definitions("-DWITH_MBEDTLS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG OR WITH_MEDIACODEC)
|
if (WITH_X264 OR WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG)
|
||||||
set(WITH_GFX_H264 ON)
|
set(WITH_GFX_H264 ON)
|
||||||
else()
|
else()
|
||||||
set(WITH_GFX_H264 OFF)
|
set(WITH_GFX_H264 OFF)
|
||||||
@ -990,16 +966,7 @@ if (APPLE)
|
|||||||
else (APPLE)
|
else (APPLE)
|
||||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
if (NOT FREEBSD)
|
if (NOT FREEBSD)
|
||||||
if (NOT BUILTIN_CHANNELS)
|
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||||
if (NOT DEFINED WITH_PLUGIN_RPATH_ONLY)
|
|
||||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..:\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
|
||||||
else()
|
|
||||||
# we need to supply this run path, even if not using RPATH in general
|
|
||||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${FREERDP_PLUGIN_PATH}")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
@ -1062,6 +1029,15 @@ add_subdirectory(include)
|
|||||||
|
|
||||||
add_subdirectory(libfreerdp)
|
add_subdirectory(libfreerdp)
|
||||||
|
|
||||||
|
if (IOS)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0")
|
||||||
|
if (IOS_PLATFORM MATCHES "SIMULATOR")
|
||||||
|
set(CMAKE_OSX_SYSROOT "iphonesimulator")
|
||||||
|
else()
|
||||||
|
set(CMAKE_OSX_SYSROOT "iphoneos")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# RdTk
|
# RdTk
|
||||||
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
|
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
|
||||||
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
|
||||||
|
|||||||
344
ChangeLog
344
ChangeLog
@ -1,346 +1,3 @@
|
|||||||
# 2024-04-22 Version 2.11.7
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported oss-fuzz fixes
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.7...2.11.6
|
|
||||||
|
|
||||||
# 2024-04-17 Version 2.11.6
|
|
||||||
|
|
||||||
CVE:
|
|
||||||
CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment
|
|
||||||
CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in clear_decompress_residual_data
|
|
||||||
CVE-2024-32040 [Low] integer underflow in nsc_rle_decode
|
|
||||||
CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle
|
|
||||||
CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress
|
|
||||||
CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported #10077
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.6...2.11.5
|
|
||||||
|
|
||||||
# 2024-01-19 Version 2.11.5
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Fix integer overflow in progressive decoder
|
|
||||||
* Update OpenSSL API usage for compatiblility with newer versions (#9747)
|
|
||||||
* Prevent NULL dereference for single thread decoder (#9712)
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.5...2.11.4
|
|
||||||
|
|
||||||
# 2023-12-14 Version 2.11.4
|
|
||||||
|
|
||||||
Notworthy changes:
|
|
||||||
* fix a typo in unicode commit (#9652)
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.4...2.11.3
|
|
||||||
|
|
||||||
# 2023-12-14 Version 2.11.3
|
|
||||||
|
|
||||||
Notworthy changes:
|
|
||||||
* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469)
|
|
||||||
* Fix issues with drive redirection (#9530,9554, #9586, #9617)
|
|
||||||
* Use endian safe ICU string converter (#9631)
|
|
||||||
* Improve AAC support (#9577)
|
|
||||||
* Fix swiss german keyboard layout (#9560)
|
|
||||||
* Enable rfx-mode:image (#9428)
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.3...2.11.2
|
|
||||||
|
|
||||||
# 2023-09-20 Version 2.11.2
|
|
||||||
|
|
||||||
Notworthy changes:
|
|
||||||
* Backported #9378: backported wArrayList (optional) copy on insert
|
|
||||||
* Backported #9360: backported certificate algorithm detection
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.2...2.11.1
|
|
||||||
|
|
||||||
# 2023-09-04 Version 2.11.1
|
|
||||||
|
|
||||||
Notworthy changes:
|
|
||||||
* Backported #9356: Fix issues with order updates
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.11.1..2.11.0
|
|
||||||
|
|
||||||
# 2023-08-28 Version 2.11.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Various input validation fixes
|
|
||||||
* Added various CMake options #9317
|
|
||||||
* LibreSSL build fixes #8709
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #9233: Big endian support
|
|
||||||
* Backported #9099: Mouse grabbing support
|
|
||||||
* Backported #6851: wayland scrolling fix
|
|
||||||
* Backported #8690: Update h264 to use new FFMPEG API
|
|
||||||
* Backported #7306: early bail from update_read_window_state_order breaks protocol
|
|
||||||
* Backported #8903: rdpecam/server: Remove wrong assertion
|
|
||||||
* Backported #8994: bounds checks for gdi/gfx rectangles
|
|
||||||
* Backported #9023: enforce rdpdr client side state checks
|
|
||||||
* Backported #6331: deactivate mouse grabbing by default
|
|
||||||
* Cherry-pick out of #9172: channels/cliprdr: Fix writing incorrect PDU type for unlock PDUs
|
|
||||||
|
|
||||||
# 2023-02-16 Version 2.10.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Fix android build scripts, use CMake from SDK
|
|
||||||
* Fix connection negotiation with mstsc/msrdc #8426
|
|
||||||
* [ntlm]: use rfc5929 binding hash algorithm #8430
|
|
||||||
* [channels,printer] Fixed reference counting #8433
|
|
||||||
* Fix uwac pixman #8439
|
|
||||||
* Fix Rdp security #8457
|
|
||||||
* [client,x11] Detect key autorepeat #8522
|
|
||||||
* [build] add channel path to RPATH #8551
|
|
||||||
* Fix build with BUILTIN_CHANNELS=OFF #8560
|
|
||||||
* revert changes so that the osmajortype/osminortype is not overwritten #8571
|
|
||||||
* [uwac] do not use iso C functions #8604
|
|
||||||
* [winpr,sam] fix inalid NULL arguments #8605
|
|
||||||
* Fix incompatible function pointer types #8625
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #8581: Ignore data PDUs for DVCs that were not opened successfully
|
|
||||||
* Backported #8498: [channel,urbdrc] fix type of usb hotplug callback
|
|
||||||
* Backported #8537: Extended info enforce limits
|
|
||||||
* Backported #8611: [core] add missing redirection fields
|
|
||||||
|
|
||||||
# 2022-11-16 Version 2.9.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported #8252: Support sending server redirection PDU
|
|
||||||
* Backported #8406: Ensure X11 client cursor is never smaller 1x1
|
|
||||||
* Backported #8403: Fixed multiple client side input validation issues
|
|
||||||
(CVE-2022-39316, CVE-2022-39317, CVE-2022-39318, CVE-2022-39319,
|
|
||||||
CVE-2022-39320, CVE-2022-41877, CVE-2022-39347)
|
|
||||||
* Backported #7282: Proxy server now discards input events sent before
|
|
||||||
activation was received
|
|
||||||
* Backported #8324: Internal replacements for md4, md5 and hmac-md5
|
|
||||||
For the time being the RDP protocol requires these outdated hash
|
|
||||||
algorithms. So any distribution that wants to ship a working
|
|
||||||
FreeRDP should check the options WITH_INTERNAL_MD4 (and depending
|
|
||||||
on OpenSSL deprecation status WITH_INTERNAL_MD5)
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #8341: Null checks in winpr_Digest_Free
|
|
||||||
* Backported #8335: Missing NULL return in winpr_Digest_New
|
|
||||||
* Backported #8192: Support for audin version 2 microphone channel
|
|
||||||
* Backported #7282: Discard input events before activation (Fixes #8374)
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.8.1..2.9.0
|
|
||||||
|
|
||||||
|
|
||||||
# 2022-10-12 Version 2.8.1
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Fixed CVE-2022-39282
|
|
||||||
* Fixed CVE-2022-39283
|
|
||||||
* Added missing commit for backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
|
|
||||||
* Added hash checks for android build script dependencies
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #8190: Fix build break with newer FFMPEG versions
|
|
||||||
* Backported #8234: Updated flatpak with build script
|
|
||||||
* Backported #8210: Better execinfo support check for android
|
|
||||||
* Backported #7708: Header now defines DumpThreadHandles
|
|
||||||
* Backported #8176: Check fullscreen state and not setting
|
|
||||||
* Backported #8236: Send resize on window state change
|
|
||||||
* Backported #7611: Audin macOS monterey fix
|
|
||||||
* Backported #8291: Android build script update
|
|
||||||
|
|
||||||
# 2022-07-28 Version 2.8.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
|
|
||||||
* Backported API to get peer accepted channel option flags
|
|
||||||
* Backported API to get peer accepted channel names
|
|
||||||
* Backported Stream_CheckAndLogRequiredLength
|
|
||||||
* Backported #7954: Add server side handling for [MS-RDPET]
|
|
||||||
* Backported #8010: Add server side handling for [MS-RDPECAM]
|
|
||||||
* Backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
|
|
||||||
* Backported #8051: Relieve CLIPRDR filename restriction when connecting to non-MS Windows servers
|
|
||||||
* Backported #8048: TLS version control
|
|
||||||
* Backported #7987: Add a new command line arg to enforce tls1.2
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
|
|
||||||
* Fixed #7837: Prevent out of bound reads for FFMPEG
|
|
||||||
* Backported #7859 and #7861: Unwind support for backtrace generation
|
|
||||||
* Backported #7440: wlfreerdp appid
|
|
||||||
* Backported #7832: RAIL window restore
|
|
||||||
* Backported #7833: Refactored WinPR thread locking
|
|
||||||
* Backported #7893: Mac rdpsnd memory leak fixes
|
|
||||||
* Backported #7895: Mac audin memory leak fixes
|
|
||||||
* Backported #7898: Automatic android versioning
|
|
||||||
* Backported #7916: GFX 10.7 capability support
|
|
||||||
* Backported #7949: Server RDPSND API improvements
|
|
||||||
* Backported #7957: Server DVC API improvements
|
|
||||||
* Backported #7760: Fixed osMinorType values
|
|
||||||
* Backported #8013: Add missing osMajorType values
|
|
||||||
* Backported #8076: Fix wrong usage of subband diffing flag (tile artifact fix)
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.7.0..2.8.0
|
|
||||||
|
|
||||||
|
|
||||||
# 2022-04-25 Version 2.7.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported OpenSSL3 gateway support (#7822)
|
|
||||||
* Backported various NTLM fixes
|
|
||||||
* Backported WINPR_ASSERT to ease future backports
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #6786: Use /network:auto by default
|
|
||||||
* Backported #7714: Workaround for broken surface frame marker
|
|
||||||
* Backported #7733: Support 10bit X11 color (BGRX32 only)
|
|
||||||
* Backported #7745: GFX progressive double free
|
|
||||||
* Backported #7808: Disable websockets with /gt:rpc
|
|
||||||
* Backported #7815: RAIL expect LOGON_MSG_SESSION_CONTINUE
|
|
||||||
|
|
||||||
Important notes:
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.6.1..2.7.0
|
|
||||||
|
|
||||||
# 2022-03-07 Version 2.6.1
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported freerdp_abort_connect during freerdp_connect fix (#7700)
|
|
||||||
* Backported improved version dection see docs/version_detection.md for details
|
|
||||||
* Backported various rdpsnd fixes (#7695)
|
|
||||||
|
|
||||||
Important notes:
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.0.0..2.6.1
|
|
||||||
|
|
||||||
# 2022-02-22 Version 2.6.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported android FFMPEG build scripts
|
|
||||||
* Updated android build dependencies
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backported #7303: Fix PDU length for RDPINPUT_PROTOCOL_V300
|
|
||||||
* Backported #7658: Sanitize optional physical monitor size values
|
|
||||||
* Backported #7426: Wayland memory corruption
|
|
||||||
* Backported #7293: Remove unused codec x264
|
|
||||||
* Backported #7541: Allow resolutions larger 2048x2048
|
|
||||||
* Backported #7574: FFMPEG 5.0 support
|
|
||||||
* Backported #7578: FFMPEG 5.0 support
|
|
||||||
* Backported #7580: Fixed device hotplugging
|
|
||||||
* Backported #7583: GetUserNameExA: Prefer getpwuid_r over getlogin_r over getlogin
|
|
||||||
* Backported #7585: Android Mediacodec support
|
|
||||||
|
|
||||||
Important notes:
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.5.0..2.6.0
|
|
||||||
|
|
||||||
# 2022-01-12 Version 2.5.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Fixed smartcard login in case a redirection occurs the pin was lost
|
|
||||||
* Backported windows client drawing fixes
|
|
||||||
* Backported improved macOS keyboard layout detection
|
|
||||||
* Backported TcpConnectTimeout
|
|
||||||
* Backported LibreSSL compatibility patches
|
|
||||||
* Backported signal handler backtrace
|
|
||||||
* Backported OpenSSL 3.0 support
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* Backport #7539: Wayland client clipboard issues
|
|
||||||
* Backport #7509: Various fixes regarding registry emulation, addin loader
|
|
||||||
and updated locale detection
|
|
||||||
* Backport #7466: Android android_register_pointer missing initialization
|
|
||||||
|
|
||||||
Important notes:
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.4.1..2.5.0
|
|
||||||
|
|
||||||
# 2021-10-20 Version 2.4.1
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Refactored RPC gateway parsing code
|
|
||||||
* OpenSSL 3.0 compatibility fixes
|
|
||||||
* USB redirection: fixed transfer lengths
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* #7363: Length checks in ConvertUTF8toUTF16
|
|
||||||
* #7349: Added checks for bitmap width and heigth values
|
|
||||||
|
|
||||||
Important notes:
|
|
||||||
* CVE-2021-41159: Improper client input validation for gateway connections allows to overwrite memory
|
|
||||||
* CVE-2021-41160: Improper region checks in all clients allow out of bound write to memory
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.4.0..2.4.1
|
|
||||||
|
|
||||||
# 2021-07-27 Version 2.4.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Backported multithreadded progressive decoder (#7036)
|
|
||||||
* Backported clipboard fixes (#6924)
|
|
||||||
* Fixed remote file read (#7185)
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* #6938: RAILS clipboard remote -> local
|
|
||||||
* #6985: Support newer FFMPEG builds
|
|
||||||
* #6989: Use OpenSSL default certificate store settings
|
|
||||||
* #7073: Planar alignment fixes
|
|
||||||
|
|
||||||
# 2021-03-15 Version 2.3.2
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.3.2..2.4.0
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* Fixed autoreconnect printer backend loading
|
|
||||||
* Fixed compilation on older mac os versions < 10.14
|
|
||||||
* Fixed mouse pointer move with smart-sizing
|
|
||||||
* Added command line option to disable websocket gateway support
|
|
||||||
* Fixed drive hotplugging issues with windows
|
|
||||||
* Fixed smartcard issues on mac
|
|
||||||
|
|
||||||
Fixed issues:
|
|
||||||
* #6900: Transparency issues with aFreeRDP
|
|
||||||
* #6848: Invalid format string in smartcard trace
|
|
||||||
* #6846: Fixed static builds
|
|
||||||
* #6888: Crash due to missing bounds checks
|
|
||||||
* #6882: Use default sound devoce on mac
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.3.1..2.3.2
|
|
||||||
|
|
||||||
# 2021-03-01 Version 2.3.1
|
|
||||||
|
|
||||||
Noteworthy changes:
|
|
||||||
* This is a compatibility bugfix release readding some (deprecated)
|
|
||||||
symbols/defines
|
|
||||||
* Also add some more EXPERIMENTAL warnings to CMake flags as some were not
|
|
||||||
clear enough.
|
|
||||||
* Fixed a memory leak in xfreerdp (mouse pointer updates)
|
|
||||||
* No longer activating some compile time debug options with -DWITH_DEBUG_ALL=ON
|
|
||||||
which might leak sensitive information.
|
|
||||||
* Added -DDEFINE_NO_DEPRECATED for developers to detect use of deprecated
|
|
||||||
symbols
|
|
||||||
|
|
||||||
For a complete and detailed change log since the last release run:
|
|
||||||
git log 2.3.0..2.3.1
|
|
||||||
|
|
||||||
|
|
||||||
# 2021-02-24 Version 2.3.0
|
# 2021-02-24 Version 2.3.0
|
||||||
|
|
||||||
Important notes:
|
Important notes:
|
||||||
@ -776,3 +433,4 @@ Virtual Channels:
|
|||||||
* rdpsnd (Sound Redirection)
|
* rdpsnd (Sound Redirection)
|
||||||
* alsa support
|
* alsa support
|
||||||
* pulse support
|
* pulse support
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
set(OPTION_DEFAULT OFF)
|
|
||||||
set(OPTION_CLIENT_DEFAULT ON)
|
|
||||||
set(OPTION_SERVER_DEFAULT ON)
|
|
||||||
|
|
||||||
define_channel_options(NAME "ainput" TYPE "dynamic"
|
|
||||||
DESCRIPTION "Advanced Input Virtual Channel Extension"
|
|
||||||
SPECIFICATIONS "[XXXXX]"
|
|
||||||
DEFAULT ${OPTION_DEFAULT})
|
|
||||||
|
|
||||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
|
||||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
|
||||||
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
# Copyright 2022 Thincast Technologies GmbH
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel_client("ainput")
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
|
||||||
ainput_main.c
|
|
||||||
ainput_main.h)
|
|
||||||
|
|
||||||
include_directories(..)
|
|
||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
|
||||||
|
|
||||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
|
|
||||||
install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} winpr)
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
|
||||||
@ -1,315 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Advanced Input Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
* Copyright 2022 Thincast Technologies GmbH
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
|
||||||
#include <winpr/assert.h>
|
|
||||||
#include <winpr/stream.h>
|
|
||||||
#include <winpr/sysinfo.h>
|
|
||||||
|
|
||||||
#include "ainput_main.h"
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
#include <freerdp/client/ainput.h>
|
|
||||||
#include <freerdp/channels/ainput.h>
|
|
||||||
|
|
||||||
#include "../common/ainput_common.h"
|
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("ainput.client")
|
|
||||||
|
|
||||||
typedef struct AINPUT_CHANNEL_CALLBACK_ AINPUT_CHANNEL_CALLBACK;
|
|
||||||
struct AINPUT_CHANNEL_CALLBACK_
|
|
||||||
{
|
|
||||||
IWTSVirtualChannelCallback iface;
|
|
||||||
|
|
||||||
IWTSPlugin* plugin;
|
|
||||||
IWTSVirtualChannelManager* channel_mgr;
|
|
||||||
IWTSVirtualChannel* channel;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct AINPUT_LISTENER_CALLBACK_ AINPUT_LISTENER_CALLBACK;
|
|
||||||
struct AINPUT_LISTENER_CALLBACK_
|
|
||||||
{
|
|
||||||
IWTSListenerCallback iface;
|
|
||||||
|
|
||||||
IWTSPlugin* plugin;
|
|
||||||
IWTSVirtualChannelManager* channel_mgr;
|
|
||||||
AINPUT_CHANNEL_CALLBACK* channel_callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
|
|
||||||
struct AINPUT_PLUGIN_
|
|
||||||
{
|
|
||||||
IWTSPlugin iface;
|
|
||||||
|
|
||||||
AINPUT_LISTENER_CALLBACK* listener_callback;
|
|
||||||
IWTSListener* listener;
|
|
||||||
UINT32 MajorVersion;
|
|
||||||
UINT32 MinorVersion;
|
|
||||||
BOOL initialized;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
|
||||||
{
|
|
||||||
UINT16 type;
|
|
||||||
AINPUT_PLUGIN* ainput;
|
|
||||||
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
|
|
||||||
|
|
||||||
WINPR_ASSERT(callback);
|
|
||||||
WINPR_ASSERT(data);
|
|
||||||
|
|
||||||
ainput = (AINPUT_PLUGIN*)callback->plugin;
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(data) < 2)
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
Stream_Read_UINT16(data, type);
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case MSG_AINPUT_VERSION:
|
|
||||||
if (Stream_GetRemainingLength(data) < 8)
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
Stream_Read_UINT32(data, ainput->MajorVersion);
|
|
||||||
Stream_Read_UINT32(data, ainput->MinorVersion);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
|
|
||||||
{
|
|
||||||
AINPUT_PLUGIN* ainput;
|
|
||||||
AINPUT_CHANNEL_CALLBACK* callback;
|
|
||||||
BYTE buffer[32] = { 0 };
|
|
||||||
UINT64 time;
|
|
||||||
wStream sbuffer = { 0 };
|
|
||||||
wStream* s = &sbuffer;
|
|
||||||
|
|
||||||
Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
time = GetTickCount64();
|
|
||||||
ainput = (AINPUT_PLUGIN*)context->handle;
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
WINPR_ASSERT(ainput->listener_callback);
|
|
||||||
|
|
||||||
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
|
|
||||||
ainput->MajorVersion, ainput->MinorVersion);
|
|
||||||
return CHANNEL_RC_UNSUPPORTED_VERSION;
|
|
||||||
}
|
|
||||||
callback = ainput->listener_callback->channel_callback;
|
|
||||||
WINPR_ASSERT(callback);
|
|
||||||
|
|
||||||
{
|
|
||||||
char buffer[128] = { 0 };
|
|
||||||
WLog_VRB(TAG, "[%s] sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
|
|
||||||
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Message type */
|
|
||||||
Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
|
|
||||||
|
|
||||||
/* Event data */
|
|
||||||
Stream_Write_UINT64(s, time);
|
|
||||||
Stream_Write_UINT64(s, flags);
|
|
||||||
Stream_Write_INT32(s, x);
|
|
||||||
Stream_Write_INT32(s, y);
|
|
||||||
Stream_SealLength(s);
|
|
||||||
|
|
||||||
/* ainput back what we have received. AINPUT does not have any message IDs. */
|
|
||||||
WINPR_ASSERT(callback->channel);
|
|
||||||
WINPR_ASSERT(callback->channel->Write);
|
|
||||||
return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|
||||||
{
|
|
||||||
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback;
|
|
||||||
|
|
||||||
free(callback);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
|
||||||
IWTSVirtualChannel* pChannel, BYTE* Data,
|
|
||||||
BOOL* pbAccept,
|
|
||||||
IWTSVirtualChannelCallback** ppCallback)
|
|
||||||
{
|
|
||||||
AINPUT_CHANNEL_CALLBACK* callback;
|
|
||||||
AINPUT_LISTENER_CALLBACK* listener_callback = (AINPUT_LISTENER_CALLBACK*)pListenerCallback;
|
|
||||||
|
|
||||||
WINPR_ASSERT(listener_callback);
|
|
||||||
WINPR_UNUSED(Data);
|
|
||||||
WINPR_UNUSED(pbAccept);
|
|
||||||
|
|
||||||
callback = (AINPUT_CHANNEL_CALLBACK*)calloc(1, sizeof(AINPUT_CHANNEL_CALLBACK));
|
|
||||||
|
|
||||||
if (!callback)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback->iface.OnDataReceived = ainput_on_data_received;
|
|
||||||
callback->iface.OnClose = ainput_on_close;
|
|
||||||
callback->plugin = listener_callback->plugin;
|
|
||||||
callback->channel_mgr = listener_callback->channel_mgr;
|
|
||||||
callback->channel = pChannel;
|
|
||||||
listener_callback->channel_callback = callback;
|
|
||||||
|
|
||||||
*ppCallback = &callback->iface;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
|
||||||
{
|
|
||||||
UINT status;
|
|
||||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (ainput->initialized)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
ainput->listener_callback =
|
|
||||||
(AINPUT_LISTENER_CALLBACK*)calloc(1, sizeof(AINPUT_LISTENER_CALLBACK));
|
|
||||||
|
|
||||||
if (!ainput->listener_callback)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput->listener_callback->iface.OnNewChannelConnection = ainput_on_new_channel_connection;
|
|
||||||
ainput->listener_callback->plugin = pPlugin;
|
|
||||||
ainput->listener_callback->channel_mgr = pChannelMgr;
|
|
||||||
|
|
||||||
status = pChannelMgr->CreateListener(pChannelMgr, AINPUT_DVC_CHANNEL_NAME, 0,
|
|
||||||
&ainput->listener_callback->iface, &ainput->listener);
|
|
||||||
|
|
||||||
ainput->listener->pInterface = ainput->iface.pInterface;
|
|
||||||
ainput->initialized = status == CHANNEL_RC_OK;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
|
|
||||||
{
|
|
||||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
|
|
||||||
if (ainput && ainput->listener_callback)
|
|
||||||
{
|
|
||||||
IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr;
|
|
||||||
if (mgr)
|
|
||||||
IFCALL(mgr->DestroyListener, mgr, ainput->listener);
|
|
||||||
}
|
|
||||||
if (ainput)
|
|
||||||
{
|
|
||||||
free(ainput->listener_callback);
|
|
||||||
free(ainput->iface.pInterface);
|
|
||||||
}
|
|
||||||
free(ainput);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
|
||||||
#define DVCPluginEntry ainput_DVCPluginEntry
|
|
||||||
#else
|
|
||||||
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|
||||||
{
|
|
||||||
UINT status = CHANNEL_RC_OK;
|
|
||||||
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "ainput");
|
|
||||||
|
|
||||||
if (!ainput)
|
|
||||||
{
|
|
||||||
AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
|
|
||||||
ainput = (AINPUT_PLUGIN*)calloc(1, sizeof(AINPUT_PLUGIN));
|
|
||||||
|
|
||||||
if (!ainput || !context)
|
|
||||||
{
|
|
||||||
free(context);
|
|
||||||
free(ainput);
|
|
||||||
|
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput->iface.Initialize = ainput_plugin_initialize;
|
|
||||||
ainput->iface.Terminated = ainput_plugin_terminated;
|
|
||||||
|
|
||||||
context->handle = (void*)ainput;
|
|
||||||
context->AInputSendInputEvent = ainput_send_input_event;
|
|
||||||
ainput->iface.pInterface = (void*)context;
|
|
||||||
|
|
||||||
status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Advanced Input Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
* Copyright 2022 Thincast Technologies GmbH
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
|
|
||||||
#define FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <freerdp/dvc.h>
|
|
||||||
#include <freerdp/types.h>
|
|
||||||
#include <freerdp/addin.h>
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
|
|
||||||
#define DVC_TAG CHANNELS_TAG("ainput.client")
|
|
||||||
#ifdef WITH_DEBUG_DVC
|
|
||||||
#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define DEBUG_DVC(...) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H */
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Audio Input Redirection Virtual Channel
|
|
||||||
*
|
|
||||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
* Copyright 2022 Thincast Technologies GmbH
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FREERDP_INT_AINPUT_COMMON_H
|
|
||||||
#define FREERDP_INT_AINPUT_COMMON_H
|
|
||||||
|
|
||||||
#include <winpr/string.h>
|
|
||||||
|
|
||||||
#include <freerdp/channels/ainput.h>
|
|
||||||
|
|
||||||
static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size)
|
|
||||||
{
|
|
||||||
char number[32] = { 0 };
|
|
||||||
|
|
||||||
if (flags & AINPUT_FLAGS_HAVE_REL)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_WHEEL)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_MOVE)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_DOWN)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_REL)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_BUTTON1)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_BUTTON2)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_FLAGS_BUTTON3)
|
|
||||||
winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_XFLAGS_BUTTON1)
|
|
||||||
winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|");
|
|
||||||
if (flags & AINPUT_XFLAGS_BUTTON2)
|
|
||||||
winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|");
|
|
||||||
|
|
||||||
_snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags);
|
|
||||||
winpr_str_append(number, buffer, size, " ");
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* FREERDP_INT_AINPUT_COMMON_H */
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
# Copyright 2022 Thincast Technologies GmbH
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel_server("ainput")
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
|
||||||
ainput_main.c)
|
|
||||||
|
|
||||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} freerdp)
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
|
||||||
@ -1,587 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Advanced Input Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Armin Novak <anovak@thincast.com>
|
|
||||||
* Copyright 2022 Thincast Technologies GmbH
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
|
||||||
#include <winpr/assert.h>
|
|
||||||
#include <winpr/synch.h>
|
|
||||||
#include <winpr/thread.h>
|
|
||||||
#include <winpr/stream.h>
|
|
||||||
#include <winpr/sysinfo.h>
|
|
||||||
#include <winpr/stream.h>
|
|
||||||
|
|
||||||
#include <freerdp/server/ainput.h>
|
|
||||||
#include <freerdp/channels/ainput.h>
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
|
|
||||||
#include "../common/ainput_common.h"
|
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("ainput.server")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
AINPUT_INITIAL,
|
|
||||||
AINPUT_OPENED,
|
|
||||||
AINPUT_VERSION_SENT,
|
|
||||||
} eAInputChannelState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
ainput_server_context context;
|
|
||||||
|
|
||||||
BOOL opened;
|
|
||||||
|
|
||||||
HANDLE stopEvent;
|
|
||||||
|
|
||||||
HANDLE thread;
|
|
||||||
void* ainput_channel;
|
|
||||||
|
|
||||||
DWORD SessionId;
|
|
||||||
|
|
||||||
BOOL isOpened;
|
|
||||||
BOOL externalThread;
|
|
||||||
|
|
||||||
/* Channel state */
|
|
||||||
eAInputChannelState state;
|
|
||||||
|
|
||||||
wStream* buffer;
|
|
||||||
} ainput_server;
|
|
||||||
|
|
||||||
static UINT ainput_server_context_poll(ainput_server_context* context);
|
|
||||||
static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
|
|
||||||
static UINT ainput_server_context_poll_int(ainput_server_context* context);
|
|
||||||
|
|
||||||
static BOOL ainput_server_is_open(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
return ainput->isOpened;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_server_open_channel(ainput_server* ainput)
|
|
||||||
{
|
|
||||||
DWORD Error;
|
|
||||||
HANDLE hEvent;
|
|
||||||
DWORD StartTick;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
PULONG pSessionId = NULL;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput->SessionId = (DWORD)*pSessionId;
|
|
||||||
WTSFreeMemory(pSessionId);
|
|
||||||
hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
|
|
||||||
StartTick = GetTickCount();
|
|
||||||
|
|
||||||
while (ainput->ainput_channel == NULL)
|
|
||||||
{
|
|
||||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
|
|
||||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
|
||||||
|
|
||||||
Error = GetLastError();
|
|
||||||
|
|
||||||
if (Error == ERROR_NOT_FOUND)
|
|
||||||
{
|
|
||||||
WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ainput->ainput_channel)
|
|
||||||
{
|
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
|
|
||||||
|
|
||||||
IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetTickCount() - StartTick > 5000)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ainput_server_send_version(ainput_server* ainput)
|
|
||||||
{
|
|
||||||
ULONG written;
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
s = ainput->buffer;
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
if (!Stream_EnsureCapacity(s, 10))
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
return ERROR_OUTOFMEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
|
|
||||||
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
|
|
||||||
Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
|
|
||||||
|
|
||||||
WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX);
|
|
||||||
if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s),
|
|
||||||
(ULONG)Stream_GetPosition(s), &written))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
UINT64 flags, time;
|
|
||||||
INT32 x, y;
|
|
||||||
char buffer[128] = { 0 };
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT64(s, time);
|
|
||||||
Stream_Read_UINT64(s, flags);
|
|
||||||
Stream_Read_INT32(s, x);
|
|
||||||
Stream_Read_INT32(s, y);
|
|
||||||
|
|
||||||
WLog_VRB(TAG, "[%s] received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32,
|
|
||||||
__FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
|
|
||||||
IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
|
|
||||||
{
|
|
||||||
BYTE* buffer = NULL;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
HANDLE ChannelEvent = NULL;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
|
|
||||||
&BytesReturned) == TRUE)
|
|
||||||
{
|
|
||||||
if (BytesReturned == sizeof(HANDLE))
|
|
||||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
|
||||||
|
|
||||||
WTSFreeMemory(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChannelEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
|
|
||||||
{
|
|
||||||
DWORD nCount;
|
|
||||||
HANDLE events[2] = { 0 };
|
|
||||||
ainput_server* ainput = (ainput_server*)arg;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
DWORD status;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
nCount = 0;
|
|
||||||
events[nCount++] = ainput->stopEvent;
|
|
||||||
|
|
||||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
|
||||||
{
|
|
||||||
switch (ainput->state)
|
|
||||||
{
|
|
||||||
case AINPUT_OPENED:
|
|
||||||
events[1] = ainput_server_get_channel_handle(ainput);
|
|
||||||
nCount = 2;
|
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, 100);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
error = ainput_server_context_poll_int(&ainput->context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAIT_FAILED:
|
|
||||||
default:
|
|
||||||
WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AINPUT_VERSION_SENT:
|
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
error = ainput_server_context_poll_int(&ainput->context);
|
|
||||||
break;
|
|
||||||
case WAIT_FAILED:
|
|
||||||
default:
|
|
||||||
WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = ainput_server_context_poll_int(&ainput->context);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
|
||||||
ainput->ainput_channel = NULL;
|
|
||||||
|
|
||||||
if (error && ainput->context.rdpcontext)
|
|
||||||
setChannelError(ainput->context.rdpcontext, error,
|
|
||||||
"ainput_server_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_server_open(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (!ainput->externalThread && (ainput->thread == NULL))
|
|
||||||
{
|
|
||||||
ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!ainput->stopEvent)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
|
|
||||||
if (!ainput->thread)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
|
||||||
CloseHandle(ainput->stopEvent);
|
|
||||||
ainput->stopEvent = NULL;
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ainput->isOpened = TRUE;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function description
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT ainput_server_close(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (!ainput->externalThread && ainput->thread)
|
|
||||||
{
|
|
||||||
SetEvent(ainput->stopEvent);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(ainput->thread);
|
|
||||||
CloseHandle(ainput->stopEvent);
|
|
||||||
ainput->thread = NULL;
|
|
||||||
ainput->stopEvent = NULL;
|
|
||||||
}
|
|
||||||
if (ainput->externalThread)
|
|
||||||
{
|
|
||||||
if (ainput->state != AINPUT_INITIAL)
|
|
||||||
{
|
|
||||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
|
||||||
ainput->ainput_channel = NULL;
|
|
||||||
ainput->state = AINPUT_INITIAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ainput->isOpened = FALSE;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
if (ainput->isOpened)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
|
|
||||||
"state is not possible!");
|
|
||||||
return ERROR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
ainput->externalThread = externalThread;
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
ainput_server_context* ainput_server_context_new(HANDLE vcm)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
|
|
||||||
|
|
||||||
if (!ainput)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ainput->context.vcm = vcm;
|
|
||||||
ainput->context.Open = ainput_server_open;
|
|
||||||
ainput->context.IsOpen = ainput_server_is_open;
|
|
||||||
ainput->context.Close = ainput_server_close;
|
|
||||||
ainput->context.Initialize = ainput_server_initialize;
|
|
||||||
ainput->context.Poll = ainput_server_context_poll;
|
|
||||||
ainput->context.ChannelHandle = ainput_server_context_handle;
|
|
||||||
|
|
||||||
ainput->buffer = Stream_New(NULL, 4096);
|
|
||||||
if (!ainput->buffer)
|
|
||||||
goto fail;
|
|
||||||
return &ainput->context;
|
|
||||||
fail:
|
|
||||||
ainput_server_context_free(ainput);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ainput_server_context_free(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
if (ainput)
|
|
||||||
{
|
|
||||||
ainput_server_close(context);
|
|
||||||
Stream_Free(ainput->buffer, TRUE);
|
|
||||||
}
|
|
||||||
free(ainput);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT ainput_process_message(ainput_server* ainput)
|
|
||||||
{
|
|
||||||
BOOL rc;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
ULONG BytesReturned, ActualBytesReturned;
|
|
||||||
UINT16 MessageId;
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
WINPR_ASSERT(ainput->ainput_channel);
|
|
||||||
|
|
||||||
s = ainput->buffer;
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
|
|
||||||
if (!rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (BytesReturned < 2)
|
|
||||||
{
|
|
||||||
error = CHANNEL_RC_OK;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
|
||||||
error = CHANNEL_RC_NO_MEMORY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
|
|
||||||
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BytesReturned != ActualBytesReturned)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
|
|
||||||
ActualBytesReturned, BytesReturned);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_SetLength(s, ActualBytesReturned);
|
|
||||||
Stream_Read_UINT16(s, MessageId);
|
|
||||||
|
|
||||||
switch (MessageId)
|
|
||||||
{
|
|
||||||
case MSG_AINPUT_MOUSE:
|
|
||||||
error = ainput_server_recv_mouse_event(ainput, s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
WINPR_ASSERT(handle);
|
|
||||||
|
|
||||||
if (!ainput->externalThread)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (ainput->state == AINPUT_INITIAL)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
*handle = ainput_server_get_channel_handle(ainput);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT ainput_server_context_poll_int(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
|
|
||||||
switch (ainput->state)
|
|
||||||
{
|
|
||||||
case AINPUT_INITIAL:
|
|
||||||
error = ainput_server_open_channel(ainput);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
|
|
||||||
else
|
|
||||||
ainput->state = AINPUT_OPENED;
|
|
||||||
break;
|
|
||||||
case AINPUT_OPENED:
|
|
||||||
{
|
|
||||||
BYTE* buffer = NULL;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer,
|
|
||||||
&BytesReturned) != TRUE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*buffer != 0)
|
|
||||||
{
|
|
||||||
error = ainput_server_send_version(ainput);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
|
|
||||||
error);
|
|
||||||
else
|
|
||||||
ainput->state = AINPUT_VERSION_SENT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error = CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
WTSFreeMemory(buffer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AINPUT_VERSION_SENT:
|
|
||||||
error = ainput_process_message(ainput);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "AINPUT chanel is in invalid state %d", ainput->state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT ainput_server_context_poll(ainput_server_context* context)
|
|
||||||
{
|
|
||||||
ainput_server* ainput = (ainput_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(ainput);
|
|
||||||
if (!ainput->externalThread)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
return ainput_server_context_poll_int(context);
|
|
||||||
}
|
|
||||||
@ -242,6 +242,10 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WAVE_FORMAT_ALAW:
|
||||||
|
case WAVE_FORMAT_MULAW:
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,18 +43,13 @@
|
|||||||
|
|
||||||
#include "audin_main.h"
|
#include "audin_main.h"
|
||||||
|
|
||||||
#define SNDIN_VERSION 0x02
|
#define MSG_SNDIN_VERSION 0x01
|
||||||
|
#define MSG_SNDIN_FORMATS 0x02
|
||||||
enum
|
#define MSG_SNDIN_OPEN 0x03
|
||||||
{
|
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||||
MSG_SNDIN_VERSION = 0x01,
|
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||||
MSG_SNDIN_FORMATS = 0x02,
|
#define MSG_SNDIN_DATA 0x06
|
||||||
MSG_SNDIN_OPEN = 0x03,
|
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||||
MSG_SNDIN_OPEN_REPLY = 0x04,
|
|
||||||
MSG_SNDIN_DATA_INCOMING = 0x05,
|
|
||||||
MSG_SNDIN_DATA = 0x06,
|
|
||||||
MSG_SNDIN_FORMATCHANGE = 0x07
|
|
||||||
} MSG_SNDIN_CMD;
|
|
||||||
|
|
||||||
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
||||||
struct _AUDIN_LISTENER_CALLBACK
|
struct _AUDIN_LISTENER_CALLBACK
|
||||||
@ -110,7 +105,6 @@ struct _AUDIN_PLUGIN
|
|||||||
IWTSListener* listener;
|
IWTSListener* listener;
|
||||||
|
|
||||||
BOOL initialized;
|
BOOL initialized;
|
||||||
UINT32 version;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
||||||
@ -144,7 +138,7 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
|||||||
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||||
{
|
{
|
||||||
wStream* out;
|
wStream* out;
|
||||||
const UINT32 ClientVersion = SNDIN_VERSION;
|
const UINT32 ClientVersion = 0x01;
|
||||||
UINT32 ServerVersion;
|
UINT32 ServerVersion;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
@ -155,7 +149,7 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
|||||||
ServerVersion, ClientVersion);
|
ServerVersion, ClientVersion);
|
||||||
|
|
||||||
/* Do not answer server packet, we do not support the channel version. */
|
/* Do not answer server packet, we do not support the channel version. */
|
||||||
if (ServerVersion > ClientVersion)
|
if (ServerVersion != ClientVersion)
|
||||||
{
|
{
|
||||||
WLog_Print(audin->log, WLOG_WARN,
|
WLog_Print(audin->log, WLOG_WARN,
|
||||||
"Incompatible channel version server=%" PRIu32
|
"Incompatible channel version server=%" PRIu32
|
||||||
@ -163,7 +157,6 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
|||||||
ServerVersion, ClientVersion);
|
ServerVersion, ClientVersion);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
audin->version = ServerVersion;
|
|
||||||
|
|
||||||
out = Stream_New(NULL, 5);
|
out = Stream_New(NULL, 5);
|
||||||
|
|
||||||
@ -451,8 +444,11 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
|
if (!supported)
|
||||||
return FALSE;
|
{
|
||||||
|
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||||
|
|
||||||
|
|||||||
@ -154,16 +154,12 @@ static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *forma
|
|||||||
if (format->wBitsPerSample == 0)
|
if (format->wBitsPerSample == 0)
|
||||||
mac->audioFormat.mBitsPerChannel = 16;
|
mac->audioFormat.mBitsPerChannel = 16;
|
||||||
|
|
||||||
|
mac->audioFormat.mBytesPerFrame = 0;
|
||||||
|
mac->audioFormat.mBytesPerPacket = 0;
|
||||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||||
mac->audioFormat.mFramesPerPacket = 1;
|
|
||||||
|
|
||||||
mac->audioFormat.mBytesPerFrame =
|
|
||||||
mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8);
|
|
||||||
mac->audioFormat.mBytesPerPacket =
|
|
||||||
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
|
||||||
|
|
||||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||||
|
mac->audioFormat.mFramesPerPacket = 1;
|
||||||
mac->audioFormat.mReserved = 0;
|
mac->audioFormat.mReserved = 0;
|
||||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
@ -424,40 +420,33 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MAC_OS_X_VERSION_10_14)
|
AVAuthorizationStatus status =
|
||||||
if (@available(macOS 10.14, *))
|
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
|
||||||
|
switch (status)
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
case AVAuthorizationStatusAuthorized:
|
||||||
AVAuthorizationStatus status =
|
mac->isAuthorized = TRUE;
|
||||||
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
|
break;
|
||||||
switch (status)
|
case AVAuthorizationStatusNotDetermined:
|
||||||
{
|
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
|
||||||
case AVAuthorizationStatusAuthorized:
|
completionHandler:^(BOOL granted) {
|
||||||
mac->isAuthorized = TRUE;
|
if (granted == YES)
|
||||||
break;
|
{
|
||||||
case AVAuthorizationStatusNotDetermined:
|
mac->isAuthorized = TRUE;
|
||||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
|
}
|
||||||
completionHandler:^(BOOL granted) {
|
else
|
||||||
if (granted == YES)
|
WLog_WARN(TAG, "Microphone access denied by user");
|
||||||
{
|
}];
|
||||||
mac->isAuthorized = TRUE;
|
break;
|
||||||
}
|
case AVAuthorizationStatusRestricted:
|
||||||
else
|
WLog_WARN(TAG, "Microphone access restricted by policy");
|
||||||
WLog_WARN(TAG, "Microphone access denied by user");
|
break;
|
||||||
}];
|
case AVAuthorizationStatusDenied:
|
||||||
break;
|
WLog_WARN(TAG, "Microphone access denied by policy");
|
||||||
case AVAuthorizationStatusRestricted:
|
break;
|
||||||
WLog_WARN(TAG, "Microphone access restricted by policy");
|
default:
|
||||||
break;
|
break;
|
||||||
case AVAuthorizationStatusDenied:
|
|
||||||
WLog_WARN(TAG, "Microphone access denied by policy");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
|
|||||||
@ -215,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void
|
|||||||
opensles->user_data = user_data;
|
opensles->user_data = user_data;
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
audin_opensles_close(device);
|
audin_opensles_close(opensles);
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_A
|
|||||||
{
|
{
|
||||||
UINT status;
|
UINT status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
const COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device;
|
||||||
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
|
COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = {
|
||||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,
|
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL,
|
||||||
|
|||||||
@ -113,6 +113,10 @@ static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT*
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WAVE_FORMAT_ALAW:
|
||||||
|
case WAVE_FORMAT_MULAW:
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -236,6 +236,17 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WAVE_FORMAT_ALAW: /* A-LAW */
|
||||||
|
case WAVE_FORMAT_MULAW: /* U-LAW */
|
||||||
|
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
|
(format->wBitsPerSample == 8) &&
|
||||||
|
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,74 +110,27 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
|||||||
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL log_mmresult(AudinWinmmDevice* winmm, const char* what, MMRESULT result)
|
|
||||||
{
|
|
||||||
if (result != MMSYSERR_NOERROR)
|
|
||||||
{
|
|
||||||
CHAR buffer[8192] = { 0 };
|
|
||||||
CHAR msg[8192] = { 0 };
|
|
||||||
CHAR cmsg[8192] = { 0 };
|
|
||||||
waveInGetErrorTextA(result, buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
_snprintf(msg, sizeof(msg) - 1, "%s failed. %" PRIu32 " [%s]", what, result, buffer);
|
|
||||||
_snprintf(cmsg, sizeof(cmsg) - 1, "audin_winmm_thread_func reported an error '%s'", msg);
|
|
||||||
WLog_Print(winmm->log, WLOG_DEBUG, "%s", msg);
|
|
||||||
if (winmm->rdpcontext)
|
|
||||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
|
|
||||||
{
|
|
||||||
MMRESULT rc;
|
|
||||||
WAVEINCAPSA caps = { 0 };
|
|
||||||
|
|
||||||
rc = waveInGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps));
|
|
||||||
if (rc != MMSYSERR_NOERROR)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
switch (pwfx->nChannels)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
if ((caps.dwFormats &
|
|
||||||
(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
|
|
||||||
WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if ((caps.dwFormats &
|
|
||||||
(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
|
|
||||||
WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0,
|
|
||||||
WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
|
||||||
return (rc == MMSYSERR_NOERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||||
{
|
{
|
||||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
||||||
char* buffer;
|
char* buffer;
|
||||||
int size, i;
|
int size, i;
|
||||||
WAVEHDR waveHdr[4] = { 0 };
|
WAVEHDR waveHdr[4];
|
||||||
DWORD status;
|
DWORD status;
|
||||||
MMRESULT rc;
|
MMRESULT rc;
|
||||||
|
|
||||||
if (!winmm->hWaveIn)
|
if (!winmm->hWaveIn)
|
||||||
{
|
{
|
||||||
MMRESULT rc;
|
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||||
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
|
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm,
|
||||||
(DWORD_PTR)winmm,
|
CALLBACK_FUNCTION))
|
||||||
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
{
|
||||||
if (!log_mmresult(winmm, "waveInOpen", rc))
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size =
|
size =
|
||||||
@ -197,22 +150,36 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
waveHdr[i].lpData = buffer;
|
waveHdr[i].lpData = buffer;
|
||||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInPrepareHeader failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInAddBuffer failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = waveInStart(winmm->hWaveIn);
|
rc = waveInStart(winmm->hWaveIn);
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInStart", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInStart failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||||
@ -228,17 +195,26 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
rc = waveInReset(winmm->hWaveIn);
|
rc = waveInReset(winmm->hWaveIn);
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInReset", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInReset failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInUnprepareHeader failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
free(waveHdr[i].lpData);
|
free(waveHdr[i].lpData);
|
||||||
@ -246,8 +222,13 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
|||||||
|
|
||||||
rc = waveInClose(winmm->hWaveIn);
|
rc = waveInClose(winmm->hWaveIn);
|
||||||
|
|
||||||
if (!log_mmresult(winmm, "waveInClose", rc))
|
if (MMSYSERR_NOERROR != rc)
|
||||||
{
|
{
|
||||||
|
WLog_Print(winmm->log, WLOG_DEBUG, "waveInClose failed. %" PRIu32 "", rc);
|
||||||
|
|
||||||
|
if (winmm->rdpcontext)
|
||||||
|
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||||
|
"audin_winmm_thread_func reported an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
winmm->hWaveIn = NULL;
|
winmm->hWaveIn = NULL;
|
||||||
@ -330,32 +311,16 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
|||||||
|
|
||||||
for (i = 0; i < winmm->cFormats; i++)
|
for (i = 0; i < winmm->cFormats; i++)
|
||||||
{
|
{
|
||||||
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
|
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag &&
|
||||||
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
|
winmm->ppwfx[i]->nChannels == format->nChannels &&
|
||||||
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
|
winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
||||||
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
|
|
||||||
{
|
{
|
||||||
/* BUG: Many devices report to support stereo recording but fail here.
|
winmm->pwfx_cur = winmm->ppwfx[i];
|
||||||
* Ensure we always use mono. */
|
break;
|
||||||
if (ppwfx->nChannels > 1)
|
|
||||||
{
|
|
||||||
ppwfx->nChannels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ppwfx->nBlockAlign != 2)
|
|
||||||
{
|
|
||||||
ppwfx->nBlockAlign = 2;
|
|
||||||
ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!test_format_supported(ppwfx))
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
|
||||||
winmm->pwfx_cur = ppwfx;
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||||
@ -367,9 +332,6 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
if (!winmm || !format)
|
if (!winmm || !format)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||||
|
|
||||||
if (!pwfx)
|
if (!pwfx)
|
||||||
@ -384,27 +346,29 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
|||||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||||
memcpy(data, format->data, format->cbSize);
|
memcpy(data, format->data, format->cbSize);
|
||||||
|
|
||||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
||||||
|
|
||||||
if (!test_format_supported(pwfx))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
|
||||||
{
|
{
|
||||||
PWAVEFORMATEX* tmp_ppwfx;
|
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
|
||||||
|
|
||||||
if (!tmp_ppwfx)
|
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
|
||||||
goto fail;
|
{
|
||||||
|
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||||
|
{
|
||||||
|
PWAVEFORMATEX* tmp_ppwfx;
|
||||||
|
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||||
|
|
||||||
winmm->ppwfx_size *= 2;
|
if (!tmp_ppwfx)
|
||||||
winmm->ppwfx = tmp_ppwfx;
|
return FALSE;
|
||||||
|
|
||||||
|
winmm->ppwfx_size *= 2;
|
||||||
|
winmm->ppwfx = tmp_ppwfx;
|
||||||
|
}
|
||||||
|
|
||||||
|
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
free(pwfx);
|
free(pwfx);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -543,7 +507,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
winmm->ppwfx_size = 10;
|
winmm->ppwfx_size = 10;
|
||||||
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
|
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||||
|
|
||||||
if (!winmm->ppwfx)
|
if (!winmm->ppwfx)
|
||||||
{
|
{
|
||||||
@ -552,7 +516,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
|
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)winmm)))
|
||||||
{
|
{
|
||||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||||
error);
|
error);
|
||||||
|
|||||||
@ -162,6 +162,9 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
|||||||
for (i = 0; i < audin->context.num_server_formats; i++)
|
for (i = 0; i < audin->context.num_server_formats; i++)
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT format = audin->context.server_formats[i];
|
AUDIO_FORMAT format = audin->context.server_formats[i];
|
||||||
|
// TODO: Eliminate this
|
||||||
|
format.nAvgBytesPerSec =
|
||||||
|
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||||
|
|
||||||
if (!audio_format_write(s, &format))
|
if (!audio_format_write(s, &format))
|
||||||
{
|
{
|
||||||
@ -557,8 +560,6 @@ static BOOL audin_server_open(audin_server_context* context)
|
|||||||
PULONG pSessionId = NULL;
|
PULONG pSessionId = NULL;
|
||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
audin->SessionId = WTS_CURRENT_SESSION;
|
audin->SessionId = WTS_CURRENT_SESSION;
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||||
(LPSTR*)&pSessionId, &BytesReturned))
|
(LPSTR*)&pSessionId, &BytesReturned))
|
||||||
@ -576,15 +577,6 @@ static BOOL audin_server_open(audin_server_context* context)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
WLog_ERR(TAG, "CreateEvent failed!");
|
||||||
|
|||||||
@ -231,8 +231,10 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
BOOL used = FALSE;
|
char* p[5];
|
||||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
FREERDP_ADDIN* pAddin;
|
||||||
|
nDashes = 0;
|
||||||
|
pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
|
||||||
|
|
||||||
if (!pAddin)
|
if (!pAddin)
|
||||||
{
|
{
|
||||||
@ -240,116 +242,57 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS
|
|||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
nDashes = 0;
|
|
||||||
for (index = 0; FindData.cFileName[index]; index++)
|
for (index = 0; FindData.cFileName[index]; index++)
|
||||||
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
nDashes += (FindData.cFileName[index] == '-') ? 1 : 0;
|
||||||
|
|
||||||
if (nDashes == 1)
|
if (nDashes == 1)
|
||||||
{
|
{
|
||||||
size_t len;
|
|
||||||
char* p[2] = { 0 };
|
|
||||||
/* <name>-client.<extension> */
|
/* <name>-client.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
|
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
||||||
len = p[1] - p[0];
|
|
||||||
if (len < 1)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
used = TRUE;
|
|
||||||
}
|
}
|
||||||
else if (nDashes == 2)
|
else if (nDashes == 2)
|
||||||
{
|
{
|
||||||
size_t len;
|
|
||||||
char* p[4] = { 0 };
|
|
||||||
/* <name>-client-<subsystem>.<extension> */
|
/* <name>-client-<subsystem>.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
p[2] = strchr(p[1], '-') + 1;
|
p[2] = strchr(p[1], '-') + 1;
|
||||||
p[3] = strchr(p[2], '.') + 1;
|
p[3] = strchr(p[2], '.') + 1;
|
||||||
|
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
||||||
len = p[1] - p[0];
|
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
|
||||||
if (len < 1)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
|
||||||
|
|
||||||
len = p[3] - p[2];
|
|
||||||
if (len < 1)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
used = TRUE;
|
|
||||||
}
|
}
|
||||||
else if (nDashes == 3)
|
else if (nDashes == 3)
|
||||||
{
|
{
|
||||||
size_t len;
|
|
||||||
char* p[5] = { 0 };
|
|
||||||
/* <name>-client-<subsystem>-<type>.<extension> */
|
/* <name>-client-<subsystem>-<type>.<extension> */
|
||||||
p[0] = FindData.cFileName;
|
p[0] = FindData.cFileName;
|
||||||
p[1] = strchr(p[0], '-') + 1;
|
p[1] = strchr(p[0], '-') + 1;
|
||||||
p[2] = strchr(p[1], '-') + 1;
|
p[2] = strchr(p[1], '-') + 1;
|
||||||
p[3] = strchr(p[2], '-') + 1;
|
p[3] = strchr(p[2], '-') + 1;
|
||||||
p[4] = strchr(p[3], '.') + 1;
|
p[4] = strchr(p[3], '.') + 1;
|
||||||
|
strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1);
|
||||||
len = p[1] - p[0];
|
strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1);
|
||||||
if (len < 1)
|
strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1);
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
|
|
||||||
|
|
||||||
len = p[3] - p[2];
|
|
||||||
if (len < 1)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
|
|
||||||
|
|
||||||
len = p[4] - p[3];
|
|
||||||
if (len < 1)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName);
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
|
|
||||||
|
|
||||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
|
||||||
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
|
||||||
ppAddins[nAddins++] = pAddin;
|
ppAddins[nAddins++] = pAddin;
|
||||||
|
|
||||||
used = TRUE;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
skip:
|
{
|
||||||
if (!used)
|
|
||||||
free(pAddin);
|
free(pAddin);
|
||||||
|
}
|
||||||
} while (FindNextFileA(hFind, &FindData));
|
} while (FindNextFileA(hFind, &FindData));
|
||||||
|
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
|
|||||||
@ -173,3 +173,198 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UINT64 filetime_to_uint64(FILETIME value)
|
||||||
|
{
|
||||||
|
UINT64 converted = 0;
|
||||||
|
converted |= (UINT32)value.dwHighDateTime;
|
||||||
|
converted <<= 32;
|
||||||
|
converted |= (UINT32)value.dwLowDateTime;
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILETIME uint64_to_filetime(UINT64 value)
|
||||||
|
{
|
||||||
|
FILETIME converted;
|
||||||
|
converted.dwLowDateTime = (UINT32)(value >> 0);
|
||||||
|
converted.dwHighDateTime = (UINT32)(value >> 32);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a packed file list.
|
||||||
|
*
|
||||||
|
* The resulting array must be freed with the `free()` function.
|
||||||
|
*
|
||||||
|
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
|
||||||
|
* @param [in] format_data_length length of `format_data` in bytes.
|
||||||
|
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
|
||||||
|
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
|
||||||
|
*
|
||||||
|
* @returns 0 on success, otherwise a Win32 error code.
|
||||||
|
*/
|
||||||
|
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
|
||||||
|
FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
|
||||||
|
{
|
||||||
|
UINT result = NO_ERROR;
|
||||||
|
UINT32 i;
|
||||||
|
UINT32 count = 0;
|
||||||
|
wStream* s = NULL;
|
||||||
|
|
||||||
|
if (!format_data || !file_descriptor_array || !file_descriptor_count)
|
||||||
|
return ERROR_BAD_ARGUMENTS;
|
||||||
|
|
||||||
|
s = Stream_New((BYTE*)format_data, format_data_length);
|
||||||
|
if (!s)
|
||||||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "invalid packed file list");
|
||||||
|
|
||||||
|
result = ERROR_INCORRECT_SIZE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
|
||||||
|
((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
|
||||||
|
|
||||||
|
result = ERROR_INCORRECT_SIZE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*file_descriptor_count = count;
|
||||||
|
*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
|
||||||
|
if (!*file_descriptor_array)
|
||||||
|
{
|
||||||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
UINT64 lastWriteTime;
|
||||||
|
FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
|
||||||
|
|
||||||
|
Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
|
||||||
|
Stream_Seek(s, 32); /* reserved1 (32 bytes) */
|
||||||
|
Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
|
||||||
|
Stream_Seek(s, 16); /* reserved2 (16 bytes) */
|
||||||
|
Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
|
||||||
|
file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
|
||||||
|
Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
|
||||||
|
Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
|
||||||
|
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
|
||||||
|
Stream_Read_UINT16(s, file->cFileName[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) > 0)
|
||||||
|
WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
|
||||||
|
Stream_GetRemainingLength(s));
|
||||||
|
out:
|
||||||
|
Stream_Free(s, FALSE);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a packed file list.
|
||||||
|
*
|
||||||
|
* The resulting format data must be freed with the `free()` function.
|
||||||
|
*
|
||||||
|
* @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
|
||||||
|
* @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
|
||||||
|
* @param [out] format_data serialized CLIPRDR_FILELIST.
|
||||||
|
* @param [out] format_data_length length of `format_data` in bytes.
|
||||||
|
*
|
||||||
|
* @returns 0 on success, otherwise a Win32 error code.
|
||||||
|
*/
|
||||||
|
UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
|
||||||
|
UINT32 file_descriptor_count, BYTE** format_data,
|
||||||
|
UINT32* format_data_length)
|
||||||
|
{
|
||||||
|
return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
|
||||||
|
file_descriptor_count, format_data, format_data_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
|
||||||
|
UINT32 file_descriptor_count, BYTE** format_data,
|
||||||
|
UINT32* format_data_length)
|
||||||
|
{
|
||||||
|
UINT result = NO_ERROR;
|
||||||
|
UINT32 i;
|
||||||
|
wStream* s = NULL;
|
||||||
|
|
||||||
|
if (!file_descriptor_array || !format_data || !format_data_length)
|
||||||
|
return ERROR_BAD_ARGUMENTS;
|
||||||
|
|
||||||
|
if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "No file clipboard support annouonced!");
|
||||||
|
return ERROR_BAD_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
|
||||||
|
if (!s)
|
||||||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
|
Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
|
||||||
|
|
||||||
|
for (i = 0; i < file_descriptor_count; i++)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
UINT64 lastWriteTime;
|
||||||
|
const FILEDESCRIPTORW* file = &file_descriptor_array[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is a known issue with Windows server getting stuck in
|
||||||
|
* an infinite loop when downloading files that are larger than
|
||||||
|
* 2 gigabytes. Do not allow clients to send such file lists.
|
||||||
|
*
|
||||||
|
* https://support.microsoft.com/en-us/help/2258090
|
||||||
|
*/
|
||||||
|
if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
|
||||||
|
{
|
||||||
|
if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
|
||||||
|
result = ERROR_FILE_TOO_LARGE;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
|
||||||
|
Stream_Zero(s, 32); /* reserved1 (32 bytes) */
|
||||||
|
Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
|
||||||
|
Stream_Zero(s, 16); /* reserved2 (16 bytes) */
|
||||||
|
lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
|
||||||
|
Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
|
||||||
|
Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
|
||||||
|
Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
|
||||||
|
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
|
||||||
|
Stream_Write_UINT16(s, file->cFileName[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream_SealLength(s);
|
||||||
|
|
||||||
|
Stream_GetBuffer(s, *format_data);
|
||||||
|
Stream_GetLength(s, *format_data_length);
|
||||||
|
|
||||||
|
Stream_Free(s, FALSE);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Stream_Free(s, TRUE);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -138,7 +138,7 @@ cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockCl
|
|||||||
if (!unlockClipboardData)
|
if (!unlockClipboardData)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
|
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@ -58,7 +58,7 @@ static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
header.type = type;
|
header.type = type;
|
||||||
header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
|
header.length = length;
|
||||||
|
|
||||||
if ((error = disp_write_header(s, &header)))
|
if ((error = disp_write_header(s, &header)))
|
||||||
{
|
{
|
||||||
@ -72,24 +72,6 @@ error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disp_server_sanitize_monitor_layout(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
|
|
||||||
{
|
|
||||||
if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
|
|
||||||
monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH ||
|
|
||||||
monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
|
|
||||||
monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
|
|
||||||
{
|
|
||||||
if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0)
|
|
||||||
WLog_DBG(
|
|
||||||
TAG,
|
|
||||||
"Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32
|
|
||||||
", %" PRIu32 "]",
|
|
||||||
monitor->PhysicalWidth, monitor->PhysicalHeight);
|
|
||||||
|
|
||||||
monitor->PhysicalWidth = monitor->PhysicalHeight = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
|
static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
|
||||||
{
|
{
|
||||||
if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
|
if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
|
||||||
@ -106,6 +88,22 @@ static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT*
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
|
||||||
|
monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Received invalid value for monitor->PhysicalWidth: %" PRIu32 "",
|
||||||
|
monitor->PhysicalWidth);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
|
||||||
|
monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
|
||||||
|
{
|
||||||
|
WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "",
|
||||||
|
monitor->PhysicalHeight);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
switch (monitor->Orientation)
|
switch (monitor->Orientation)
|
||||||
{
|
{
|
||||||
case ORIENTATION_LANDSCAPE:
|
case ORIENTATION_LANDSCAPE:
|
||||||
@ -185,8 +183,6 @@ static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerC
|
|||||||
Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */
|
Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */
|
||||||
Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
|
||||||
Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
|
||||||
|
|
||||||
disp_server_sanitize_monitor_layout(monitor);
|
|
||||||
WLog_DBG(TAG,
|
WLog_DBG(TAG,
|
||||||
"\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32
|
"\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32
|
||||||
"x%" PRIu32 ")",
|
"x%" PRIu32 ")",
|
||||||
@ -378,8 +374,6 @@ static UINT disp_server_open(DispServerContext* context)
|
|||||||
void* buffer;
|
void* buffer;
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
priv->SessionId = WTS_CURRENT_SESSION;
|
priv->SessionId = WTS_CURRENT_SESSION;
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||||
@ -401,16 +395,6 @@ static UINT disp_server_open(DispServerContext* context)
|
|||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(priv->disp_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
rc = ERROR_INTERNAL_ERROR;
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Query for channel event handle */
|
/* Query for channel event handle */
|
||||||
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
|
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
|
||||||
&BytesReturned) ||
|
&BytesReturned) ||
|
||||||
|
|||||||
@ -61,14 +61,10 @@
|
|||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static BOOL drive_file_fix_path(WCHAR* path, size_t length)
|
static void drive_file_fix_path(WCHAR* path)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
size_t length = _wcslen(path);
|
||||||
if ((length == 0) || (length > UINT32_MAX))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
WINPR_ASSERT(path);
|
|
||||||
|
|
||||||
for (i = 0; i < length; i++)
|
for (i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@ -79,82 +75,58 @@ static BOOL drive_file_fix_path(WCHAR* path, size_t length)
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
||||||
return FALSE;
|
return;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
if ((length == 1) && (path[0] == L'/'))
|
if ((length == 1) && (path[0] == L'/'))
|
||||||
return FALSE;
|
return;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((length > 0) && (path[length - 1] == L'/'))
|
if ((length > 0) && (path[length - 1] == L'/'))
|
||||||
path[length - 1] = L'\0';
|
path[length - 1] = L'\0';
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
||||||
size_t PathWCharLength)
|
size_t PathLength)
|
||||||
{
|
{
|
||||||
BOOL ok = FALSE;
|
WCHAR* fullpath;
|
||||||
WCHAR* fullpath = NULL;
|
size_t base_path_length;
|
||||||
size_t length;
|
|
||||||
|
|
||||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
if (!base_path || (!path && (PathLength > 0)))
|
||||||
goto fail;
|
return NULL;
|
||||||
|
|
||||||
const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
|
base_path_length = _wcslen(base_path) * 2;
|
||||||
length = base_path_length + PathWCharLength + 1;
|
fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR));
|
||||||
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
|
|
||||||
|
|
||||||
if (!fullpath)
|
if (!fullpath)
|
||||||
goto fail;
|
{
|
||||||
|
WLog_ERR(TAG, "malloc failed!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
|
CopyMemory(fullpath, base_path, base_path_length);
|
||||||
if (path)
|
if (path)
|
||||||
CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
|
CopyMemory((char*)fullpath + base_path_length, path, PathLength);
|
||||||
|
drive_file_fix_path(fullpath);
|
||||||
if (!drive_file_fix_path(fullpath, length))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* Ensure the path does not contain sequences like '..' */
|
|
||||||
const WCHAR dotdot[] = { '.', '.', '\0' };
|
|
||||||
if (_wcsstr(&fullpath[base_path_length], dotdot))
|
|
||||||
{
|
|
||||||
char abuffer[MAX_PATH] = { 0 };
|
|
||||||
ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer,
|
|
||||||
ARRAYSIZE(abuffer) - 1, NULL, NULL);
|
|
||||||
|
|
||||||
WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
|
|
||||||
&abuffer[base_path_length]);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = TRUE;
|
|
||||||
fail:
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
free(fullpath);
|
|
||||||
fullpath = NULL;
|
|
||||||
}
|
|
||||||
return fullpath;
|
return fullpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL drive_file_remove_dir(const WCHAR* path)
|
static BOOL drive_file_remove_dir(const WCHAR* path)
|
||||||
{
|
{
|
||||||
WIN32_FIND_DATAW findFileData = { 0 };
|
WIN32_FIND_DATAW findFileData;
|
||||||
BOOL ret = TRUE;
|
BOOL ret = TRUE;
|
||||||
HANDLE dir = INVALID_HANDLE_VALUE;
|
HANDLE dir;
|
||||||
WCHAR* fullpath = NULL;
|
WCHAR* fullpath;
|
||||||
WCHAR* path_slash = NULL;
|
WCHAR* path_slash;
|
||||||
size_t base_path_length = 0;
|
size_t base_path_length;
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
base_path_length = _wcslen(path);
|
base_path_length = _wcslen(path) * 2;
|
||||||
path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR));
|
path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3);
|
||||||
|
|
||||||
if (!path_slash)
|
if (!path_slash)
|
||||||
{
|
{
|
||||||
@ -162,11 +134,12 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR));
|
CopyMemory(path_slash, path, base_path_length);
|
||||||
path_slash[base_path_length] = L'/';
|
path_slash[base_path_length / 2] = L'/';
|
||||||
path_slash[base_path_length + 1] = L'*';
|
path_slash[base_path_length / 2 + 1] = L'*';
|
||||||
DEBUG_WSTR("Search in %s", path_slash);
|
DEBUG_WSTR("Search in %s", path_slash);
|
||||||
dir = FindFirstFileW(path_slash, &findFileData);
|
dir = FindFirstFileW(path_slash, &findFileData);
|
||||||
|
path_slash[base_path_length / 2 + 1] = 0;
|
||||||
|
|
||||||
if (dir == INVALID_HANDLE_VALUE)
|
if (dir == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
@ -176,7 +149,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
|
size_t len = _wcslen(findFileData.cFileName);
|
||||||
|
|
||||||
if ((len == 1 && findFileData.cFileName[0] == L'.') ||
|
if ((len == 1 && findFileData.cFileName[0] == L'.') ||
|
||||||
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
|
||||||
@ -184,7 +157,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len);
|
fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2);
|
||||||
DEBUG_WSTR("Delete %s", fullpath);
|
DEBUG_WSTR("Delete %s", fullpath);
|
||||||
|
|
||||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
@ -360,13 +333,13 @@ static BOOL drive_file_init(DRIVE_FILE* file)
|
|||||||
return file->file_handle != INVALID_HANDLE_VALUE;
|
return file->file_handle != INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
||||||
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions,
|
||||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
|
UINT32 FileAttributes, UINT32 SharedAccess)
|
||||||
{
|
{
|
||||||
DRIVE_FILE* file;
|
DRIVE_FILE* file;
|
||||||
|
|
||||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
if (!base_path || (!path && (PathLength > 0)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
|
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
|
||||||
@ -386,7 +359,7 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
|
|||||||
file->CreateDisposition = CreateDisposition;
|
file->CreateDisposition = CreateDisposition;
|
||||||
file->CreateOptions = CreateOptions;
|
file->CreateOptions = CreateOptions;
|
||||||
file->SharedAccess = SharedAccess;
|
file->SharedAccess = SharedAccess;
|
||||||
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength));
|
drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength));
|
||||||
|
|
||||||
if (!drive_file_init(file))
|
if (!drive_file_init(file))
|
||||||
{
|
{
|
||||||
@ -741,10 +714,13 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
||||||
FileNameLength / sizeof(WCHAR));
|
FileNameLength);
|
||||||
|
|
||||||
if (!fullpath)
|
if (!fullpath)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
@ -783,7 +759,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||||
const WCHAR* path, UINT32 PathWCharLength, wStream* output)
|
const WCHAR* path, UINT32 PathLength, wStream* output)
|
||||||
{
|
{
|
||||||
size_t length;
|
size_t length;
|
||||||
WCHAR* ent_path;
|
WCHAR* ent_path;
|
||||||
@ -797,7 +773,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
|
|||||||
if (file->find_handle != INVALID_HANDLE_VALUE)
|
if (file->find_handle != INVALID_HANDLE_VALUE)
|
||||||
FindClose(file->find_handle);
|
FindClose(file->find_handle);
|
||||||
|
|
||||||
ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
|
ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength);
|
||||||
/* open new search handle and retrieve the first entry */
|
/* open new search handle and retrieve the first entry */
|
||||||
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
|
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
|
||||||
free(ent_path);
|
free(ent_path);
|
||||||
|
|||||||
@ -51,9 +51,9 @@ struct _DRIVE_FILE
|
|||||||
UINT32 CreateOptions;
|
UINT32 CreateOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
|
||||||
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions,
|
||||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
|
UINT32 FileAttributes, UINT32 SharedAccess);
|
||||||
BOOL drive_file_free(DRIVE_FILE* file);
|
BOOL drive_file_free(DRIVE_FILE* file);
|
||||||
|
|
||||||
BOOL drive_file_open(DRIVE_FILE* file);
|
BOOL drive_file_open(DRIVE_FILE* file);
|
||||||
@ -64,6 +64,6 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
|||||||
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
|
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
|
||||||
wStream* input);
|
wStream* input);
|
||||||
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
|
||||||
const WCHAR* path, UINT32 PathWCharLength, wStream* output);
|
const WCHAR* path, UINT32 PathLength, wStream* output);
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
|
||||||
|
|||||||
@ -184,8 +184,8 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
|
|
||||||
path = (const WCHAR*)Stream_Pointer(irp->input);
|
path = (const WCHAR*)Stream_Pointer(irp->input);
|
||||||
FileId = irp->devman->id_sequence++;
|
FileId = irp->devman->id_sequence++;
|
||||||
file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
|
file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition,
|
||||||
CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
|
CreateOptions, FileAttributes, SharedAccess);
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
@ -629,9 +629,6 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
Stream_Read_UINT32(irp->input, PathLength);
|
Stream_Read_UINT32(irp->input, PathLength);
|
||||||
Stream_Seek(irp->input, 23); /* Padding */
|
Stream_Seek(irp->input, 23); /* Padding */
|
||||||
path = (WCHAR*)Stream_Pointer(irp->input);
|
path = (WCHAR*)Stream_Pointer(irp->input);
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
file = drive_get_file_by_id(drive, irp->FileId);
|
file = drive_get_file_by_id(drive, irp->FileId);
|
||||||
|
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
@ -639,8 +636,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
|||||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||||
Stream_Write_UINT32(irp->output, 0); /* Length */
|
Stream_Write_UINT32(irp->output, 0); /* Length */
|
||||||
}
|
}
|
||||||
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
|
else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength,
|
||||||
PathLength / sizeof(WCHAR), irp->output))
|
irp->output))
|
||||||
{
|
{
|
||||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,21 +91,7 @@ static UINT echo_server_open_channel(echo_server* echo)
|
|||||||
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||||
|
|
||||||
if (echo->echo_channel)
|
if (echo->echo_channel)
|
||||||
{
|
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
|
|
||||||
|
|
||||||
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
Error = GetLastError();
|
Error = GetLastError();
|
||||||
|
|
||||||
|
|||||||
@ -25,10 +25,9 @@ include_directories(..)
|
|||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||||
if (NOT BUILTIN_CHANNELS OR NOT BUILD_SHARED_LIBS)
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|
||||||
|
|||||||
@ -159,7 +159,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
Stream_Read_UINT32(irp->input, Length);
|
Stream_Read_UINT32(irp->input, Length);
|
||||||
Stream_Read_UINT64(irp->input, Offset);
|
Stream_Read_UINT64(irp->input, Offset);
|
||||||
buffer = (BYTE*)calloc(Length, sizeof(BYTE));
|
buffer = (BYTE*)malloc(Length);
|
||||||
|
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
{
|
{
|
||||||
@ -178,7 +178,6 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Length = status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT32(irp->output, Length);
|
Stream_Write_UINT32(irp->output, Length);
|
||||||
|
|||||||
@ -33,7 +33,6 @@
|
|||||||
#include <cups/cups.h>
|
#include <cups/cups.h>
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/file.h>
|
|
||||||
#include <winpr/string.h>
|
#include <winpr/string.h>
|
||||||
|
|
||||||
#include <freerdp/channels/rdpdr.h>
|
#include <freerdp/channels/rdpdr.h>
|
||||||
@ -93,7 +92,7 @@ static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data,
|
|||||||
{
|
{
|
||||||
FILE* fp;
|
FILE* fp;
|
||||||
|
|
||||||
fp = winpr_fopen((const char*)cups_printjob->printjob_object, "a+b");
|
fp = fopen((const char*)cups_printjob->printjob_object, "a+b");
|
||||||
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
@ -403,8 +402,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
|
|||||||
uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver;
|
uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver;
|
||||||
|
|
||||||
uniq_cups_driver->id_sequence = 1;
|
uniq_cups_driver->id_sequence = 1;
|
||||||
|
uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver);
|
||||||
}
|
}
|
||||||
uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver);
|
|
||||||
|
|
||||||
return &uniq_cups_driver->driver;
|
return &uniq_cups_driver->driver;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,9 +82,9 @@ static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* n
|
|||||||
char* bname = crypto_base64_encode((const BYTE*)name, (int)length);
|
char* bname = crypto_base64_encode((const BYTE*)name, (int)length);
|
||||||
char* config = GetCombinedPath(dir, bname);
|
char* config = GetCombinedPath(dir, bname);
|
||||||
|
|
||||||
if (config && !winpr_PathFileExists(config))
|
if (config && !PathFileExistsA(config))
|
||||||
{
|
{
|
||||||
if (!winpr_PathMakePath(config, NULL))
|
if (!PathMakePathA(config, NULL))
|
||||||
{
|
{
|
||||||
free(config);
|
free(config);
|
||||||
config = NULL;
|
config = NULL;
|
||||||
@ -145,7 +145,7 @@ static BOOL printer_config_valid(const char* path)
|
|||||||
if (!path)
|
if (!path)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!winpr_PathFileExists(path))
|
if (!PathFileExistsA(path))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -261,7 +261,7 @@ static BOOL printer_remove_config(const rdpSettings* settings, const WCHAR* name
|
|||||||
if (!printer_config_valid(path))
|
if (!printer_config_valid(path))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
rc = winpr_RemoveDirectory(path);
|
rc = RemoveDirectoryA(path);
|
||||||
fail:
|
fail:
|
||||||
free(path);
|
free(path);
|
||||||
return rc;
|
return rc;
|
||||||
@ -275,7 +275,7 @@ static BOOL printer_move_config(const rdpSettings* settings, const WCHAR* oldNam
|
|||||||
char* newPath = get_printer_config_path(settings, newName, newLength);
|
char* newPath = get_printer_config_path(settings, newName, newLength);
|
||||||
|
|
||||||
if (printer_config_valid(oldPath))
|
if (printer_config_valid(oldPath))
|
||||||
rc = winpr_MoveFile(oldPath, newPath);
|
rc = MoveFileA(oldPath, newPath);
|
||||||
|
|
||||||
free(oldPath);
|
free(oldPath);
|
||||||
free(newPath);
|
free(newPath);
|
||||||
@ -979,7 +979,7 @@ printer_DeviceServiceEntry
|
|||||||
|
|
||||||
device = (RDPDR_PRINTER*)pEntryPoints->device;
|
device = (RDPDR_PRINTER*)pEntryPoints->device;
|
||||||
name = device->Name;
|
name = device->Name;
|
||||||
driver_name = _strdup(device->DriverName);
|
driver_name = device->DriverName;
|
||||||
|
|
||||||
/* Secondary argument is one of the following:
|
/* Secondary argument is one of the following:
|
||||||
*
|
*
|
||||||
@ -1016,8 +1016,7 @@ printer_DeviceServiceEntry
|
|||||||
if (!driver)
|
if (!driver)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Could not get a printer driver!");
|
WLog_ERR(TAG, "Could not get a printer driver!");
|
||||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name && name[0])
|
if (name && name[0])
|
||||||
@ -1065,9 +1064,7 @@ printer_DeviceServiceEntry
|
|||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
free(driver_name);
|
driver->ReleaseRef(driver);
|
||||||
if (driver)
|
|
||||||
driver->ReleaseRef(driver);
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -452,8 +452,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
|
|||||||
win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
|
win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
|
||||||
|
|
||||||
win_driver->id_sequence = 1;
|
win_driver->id_sequence = 1;
|
||||||
|
win_driver->driver.AddRef(&win_driver->driver);
|
||||||
}
|
}
|
||||||
win_driver->driver.AddRef(&win_driver->driver);
|
|
||||||
|
|
||||||
return &win_driver->driver;
|
return &win_driver->driver;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,6 @@
|
|||||||
|
|
||||||
#include "rail_orders.h"
|
#include "rail_orders.h"
|
||||||
|
|
||||||
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -41,7 +39,6 @@ static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureM
|
|||||||
*/
|
*/
|
||||||
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
||||||
{
|
{
|
||||||
char buffer[128] = { 0 };
|
|
||||||
UINT16 orderLength;
|
UINT16 orderLength;
|
||||||
|
|
||||||
if (!rail || !s)
|
if (!rail || !s)
|
||||||
@ -52,7 +49,7 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
|||||||
rail_write_pdu_header(s, orderType, orderLength);
|
rail_write_pdu_header(s, orderType, orderLength);
|
||||||
Stream_SetPosition(s, orderLength);
|
Stream_SetPosition(s, orderLength);
|
||||||
WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "",
|
WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "",
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
|
rail_get_order_type_string(orderType), orderLength);
|
||||||
return rail_send_channel_data(rail, s);
|
return rail_send_channel_data(rail, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,46 +388,7 @@ static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT rail_read_compartment_info_order(wStream* s,
|
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
|
||||||
RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
|
|
||||||
{
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */
|
|
||||||
Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */
|
|
||||||
Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */
|
|
||||||
Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s)
|
|
||||||
{
|
|
||||||
RailClientContext* context = rail_get_client_interface(rail);
|
|
||||||
RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 };
|
|
||||||
UINT error;
|
|
||||||
|
|
||||||
if (!context || !s)
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
|
||||||
|
|
||||||
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
|
|
||||||
return ERROR_BAD_CONFIGURATION;
|
|
||||||
|
|
||||||
if ((error = rail_read_compartment_info_order(s, &pdu)))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (context->custom)
|
|
||||||
{
|
|
||||||
IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
|
|
||||||
{
|
{
|
||||||
UINT32 supported, masked;
|
UINT32 supported, masked;
|
||||||
|
|
||||||
@ -442,15 +400,7 @@ BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
|
|||||||
masked = (supported & featureMask);
|
masked = (supported & featureMask);
|
||||||
|
|
||||||
if (masked != featureMask)
|
if (masked != featureMask)
|
||||||
{
|
|
||||||
char mask[256] = { 0 };
|
|
||||||
char actual[256] = { 0 };
|
|
||||||
|
|
||||||
WLog_WARN(TAG, "[%s] have %s, require %s", __func__,
|
|
||||||
freerdp_rail_support_flags_to_string(supported, actual, sizeof(actual)),
|
|
||||||
freerdp_rail_support_flags_to_string(featureMask, mask, sizeof(mask)));
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -954,73 +904,6 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(pTextScaleFactor);
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT32(s, *pTextScaleFactor);
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s)
|
|
||||||
{
|
|
||||||
RailClientContext* context = rail_get_client_interface(rail);
|
|
||||||
UINT32 TextScaleFactor = 0;
|
|
||||||
UINT error;
|
|
||||||
|
|
||||||
if (!context)
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
|
||||||
|
|
||||||
if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor)))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (context->custom)
|
|
||||||
{
|
|
||||||
IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(pCaretBlinkRate);
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT32(s, *pCaretBlinkRate);
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s)
|
|
||||||
{
|
|
||||||
RailClientContext* context = rail_get_client_interface(rail);
|
|
||||||
UINT32 CaretBlinkRate = 0;
|
|
||||||
UINT error;
|
|
||||||
|
|
||||||
if (!context)
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
|
||||||
if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate)))
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (context->custom)
|
|
||||||
{
|
|
||||||
IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -1028,10 +911,9 @@ static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s)
|
|||||||
*/
|
*/
|
||||||
UINT rail_order_recv(railPlugin* rail, wStream* s)
|
UINT rail_order_recv(railPlugin* rail, wStream* s)
|
||||||
{
|
{
|
||||||
char buffer[128] = { 0 };
|
|
||||||
UINT16 orderType;
|
UINT16 orderType;
|
||||||
UINT16 orderLength;
|
UINT16 orderLength;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error;
|
||||||
|
|
||||||
if (!rail || !s)
|
if (!rail || !s)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -1043,88 +925,55 @@ UINT rail_order_recv(railPlugin* rail, wStream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "",
|
WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "",
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
|
rail_get_order_type_string(orderType), orderLength);
|
||||||
|
|
||||||
switch (orderType)
|
switch (orderType)
|
||||||
{
|
{
|
||||||
case TS_RAIL_ORDER_HANDSHAKE:
|
case TS_RAIL_ORDER_HANDSHAKE:
|
||||||
error = rail_recv_handshake_order(rail, s);
|
return rail_recv_handshake_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_COMPARTMENTINFO:
|
|
||||||
error = rail_recv_compartmentinfo_order(rail, s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_HANDSHAKE_EX:
|
case TS_RAIL_ORDER_HANDSHAKE_EX:
|
||||||
error = rail_recv_handshake_ex_order(rail, s);
|
return rail_recv_handshake_ex_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_EXEC_RESULT:
|
case TS_RAIL_ORDER_EXEC_RESULT:
|
||||||
error = rail_recv_exec_result_order(rail, s);
|
return rail_recv_exec_result_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_SYSPARAM:
|
case TS_RAIL_ORDER_SYSPARAM:
|
||||||
error = rail_recv_server_sysparam_order(rail, s);
|
return rail_recv_server_sysparam_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_MINMAXINFO:
|
case TS_RAIL_ORDER_MINMAXINFO:
|
||||||
error = rail_recv_server_minmaxinfo_order(rail, s);
|
return rail_recv_server_minmaxinfo_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_LOCALMOVESIZE:
|
case TS_RAIL_ORDER_LOCALMOVESIZE:
|
||||||
error = rail_recv_server_localmovesize_order(rail, s);
|
return rail_recv_server_localmovesize_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_GET_APPID_RESP:
|
case TS_RAIL_ORDER_GET_APPID_RESP:
|
||||||
error = rail_recv_server_get_appid_resp_order(rail, s);
|
return rail_recv_server_get_appid_resp_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_LANGBARINFO:
|
case TS_RAIL_ORDER_LANGBARINFO:
|
||||||
error = rail_recv_langbar_info_order(rail, s);
|
return rail_recv_langbar_info_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_TASKBARINFO:
|
case TS_RAIL_ORDER_TASKBARINFO:
|
||||||
error = rail_recv_taskbar_info_order(rail, s);
|
return rail_recv_taskbar_info_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_ZORDER_SYNC:
|
case TS_RAIL_ORDER_ZORDER_SYNC:
|
||||||
error = rail_recv_zorder_sync_order(rail, s);
|
return rail_recv_zorder_sync_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_CLOAK:
|
case TS_RAIL_ORDER_CLOAK:
|
||||||
error = rail_recv_cloak_order(rail, s);
|
return rail_recv_cloak_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
|
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
|
||||||
error = rail_recv_power_display_request_order(rail, s);
|
return rail_recv_power_display_request_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
|
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
|
||||||
error = rail_recv_get_application_id_extended_response_order(rail, s);
|
return rail_recv_get_application_id_extended_response_order(rail, s);
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_TEXTSCALEINFO:
|
|
||||||
error = rail_recv_textscaleinfo_order(rail, s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TS_RAIL_ORDER_CARETBLINKINFO:
|
|
||||||
error = rail_recv_caretblinkinfo_order(rail, s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WLog_ERR(TAG, "Unknown RAIL PDU %s received.",
|
WLog_ERR(TAG, "Unknown RAIL PDU order reveived.");
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)));
|
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != CHANNEL_RC_OK)
|
return CHANNEL_RC_OK;
|
||||||
{
|
|
||||||
char buffer[128] = { 0 };
|
|
||||||
WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "",
|
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1509,9 +1358,6 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail,
|
|||||||
if (!rail || !compartmentInfo)
|
if (!rail || !compartmentInfo)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
|
|
||||||
return ERROR_BAD_CONFIGURATION;
|
|
||||||
|
|
||||||
s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH);
|
s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH);
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
|
|||||||
@ -27,72 +27,37 @@
|
|||||||
|
|
||||||
#define TAG CHANNELS_TAG("rail.common")
|
#define TAG CHANNELS_TAG("rail.common")
|
||||||
|
|
||||||
|
static const char* const RAIL_ORDER_TYPE_STRINGS[] = { "",
|
||||||
|
"Execute",
|
||||||
|
"Activate",
|
||||||
|
"System Parameters Update",
|
||||||
|
"System Command",
|
||||||
|
"Handshake",
|
||||||
|
"Notify Event",
|
||||||
|
"",
|
||||||
|
"Window Move",
|
||||||
|
"Local Move/Size",
|
||||||
|
"Min Max Info",
|
||||||
|
"Client Status",
|
||||||
|
"System Menu",
|
||||||
|
"Language Bar Info",
|
||||||
|
"Get Application ID Request",
|
||||||
|
"Get Application ID Response",
|
||||||
|
"Execute Result",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"" };
|
||||||
|
|
||||||
const char* rail_get_order_type_string(UINT16 orderType)
|
const char* rail_get_order_type_string(UINT16 orderType)
|
||||||
{
|
{
|
||||||
switch (orderType)
|
UINT32 index = ((orderType & 0xF0) >> 3) + (orderType & 0x0F);
|
||||||
{
|
if (index >= ARRAYSIZE(RAIL_ORDER_TYPE_STRINGS))
|
||||||
case TS_RAIL_ORDER_EXEC:
|
return "UNKNOWN";
|
||||||
return "TS_RAIL_ORDER_EXEC";
|
|
||||||
case TS_RAIL_ORDER_ACTIVATE:
|
|
||||||
return "TS_RAIL_ORDER_ACTIVATE";
|
|
||||||
case TS_RAIL_ORDER_SYSPARAM:
|
|
||||||
return "TS_RAIL_ORDER_SYSPARAM";
|
|
||||||
case TS_RAIL_ORDER_SYSCOMMAND:
|
|
||||||
return "TS_RAIL_ORDER_SYSCOMMAND";
|
|
||||||
case TS_RAIL_ORDER_HANDSHAKE:
|
|
||||||
return "TS_RAIL_ORDER_HANDSHAKE";
|
|
||||||
case TS_RAIL_ORDER_NOTIFY_EVENT:
|
|
||||||
return "TS_RAIL_ORDER_NOTIFY_EVENT";
|
|
||||||
case TS_RAIL_ORDER_WINDOWMOVE:
|
|
||||||
return "TS_RAIL_ORDER_WINDOWMOVE";
|
|
||||||
case TS_RAIL_ORDER_LOCALMOVESIZE:
|
|
||||||
return "TS_RAIL_ORDER_LOCALMOVESIZE";
|
|
||||||
case TS_RAIL_ORDER_MINMAXINFO:
|
|
||||||
return "TS_RAIL_ORDER_MINMAXINFO";
|
|
||||||
case TS_RAIL_ORDER_CLIENTSTATUS:
|
|
||||||
return "TS_RAIL_ORDER_CLIENTSTATUS";
|
|
||||||
case TS_RAIL_ORDER_SYSMENU:
|
|
||||||
return "TS_RAIL_ORDER_SYSMENU";
|
|
||||||
case TS_RAIL_ORDER_LANGBARINFO:
|
|
||||||
return "TS_RAIL_ORDER_LANGBARINFO";
|
|
||||||
case TS_RAIL_ORDER_GET_APPID_REQ:
|
|
||||||
return "TS_RAIL_ORDER_GET_APPID_REQ";
|
|
||||||
case TS_RAIL_ORDER_GET_APPID_RESP:
|
|
||||||
return "TS_RAIL_ORDER_GET_APPID_RESP";
|
|
||||||
case TS_RAIL_ORDER_TASKBARINFO:
|
|
||||||
return "TS_RAIL_ORDER_TASKBARINFO";
|
|
||||||
case TS_RAIL_ORDER_LANGUAGEIMEINFO:
|
|
||||||
return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
|
|
||||||
case TS_RAIL_ORDER_COMPARTMENTINFO:
|
|
||||||
return "TS_RAIL_ORDER_COMPARTMENTINFO";
|
|
||||||
case TS_RAIL_ORDER_HANDSHAKE_EX:
|
|
||||||
return "TS_RAIL_ORDER_HANDSHAKE_EX";
|
|
||||||
case TS_RAIL_ORDER_ZORDER_SYNC:
|
|
||||||
return "TS_RAIL_ORDER_ZORDER_SYNC";
|
|
||||||
case TS_RAIL_ORDER_CLOAK:
|
|
||||||
return "TS_RAIL_ORDER_CLOAK";
|
|
||||||
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
|
|
||||||
return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
|
|
||||||
case TS_RAIL_ORDER_SNAP_ARRANGE:
|
|
||||||
return "TS_RAIL_ORDER_SNAP_ARRANGE";
|
|
||||||
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
|
|
||||||
return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
|
|
||||||
case TS_RAIL_ORDER_EXEC_RESULT:
|
|
||||||
return "TS_RAIL_ORDER_EXEC_RESULT";
|
|
||||||
case TS_RAIL_ORDER_TEXTSCALEINFO:
|
|
||||||
return "TS_RAIL_ORDER_TEXTSCALEINFO";
|
|
||||||
case TS_RAIL_ORDER_CARETBLINKINFO:
|
|
||||||
return "TS_RAIL_ORDER_CARETBLINKINFO";
|
|
||||||
default:
|
|
||||||
return "TS_RAIL_ORDER_UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
|
return RAIL_ORDER_TYPE_STRINGS[index];
|
||||||
{
|
|
||||||
_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
|
|
||||||
orderType);
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -71,6 +71,5 @@ UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
|
|||||||
BOOL extendedSpiSupported);
|
BOOL extendedSpiSupported);
|
||||||
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
|
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
|
||||||
const char* rail_get_order_type_string(UINT16 orderType);
|
const char* rail_get_order_type_string(UINT16 orderType);
|
||||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
|
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */
|
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */
|
||||||
|
|||||||
@ -62,7 +62,6 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length)
|
|||||||
*/
|
*/
|
||||||
static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType)
|
static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType)
|
||||||
{
|
{
|
||||||
char buffer[128] = { 0 };
|
|
||||||
UINT16 orderLength;
|
UINT16 orderLength;
|
||||||
|
|
||||||
if (!context || !s)
|
if (!context || !s)
|
||||||
@ -72,8 +71,8 @@ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16
|
|||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
rail_write_pdu_header(s, orderType, orderLength);
|
rail_write_pdu_header(s, orderType, orderLength);
|
||||||
Stream_SetPosition(s, orderLength);
|
Stream_SetPosition(s, orderLength);
|
||||||
WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "",
|
WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", rail_get_order_type_string(orderType),
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
|
orderLength);
|
||||||
return rail_send(context, s, orderLength);
|
return rail_send(context, s, orderLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1536,7 +1535,6 @@ void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags)
|
|||||||
|
|
||||||
UINT rail_server_handle_messages(RailServerContext* context)
|
UINT rail_server_handle_messages(RailServerContext* context)
|
||||||
{
|
{
|
||||||
char buffer[128] = { 0 };
|
|
||||||
UINT status = CHANNEL_RC_OK;
|
UINT status = CHANNEL_RC_OK;
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
UINT16 orderType;
|
UINT16 orderType;
|
||||||
@ -1586,8 +1584,8 @@ UINT rail_server_handle_messages(RailServerContext* context)
|
|||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "",
|
WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", rail_get_order_type_string(orderType),
|
||||||
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
|
orderLength);
|
||||||
|
|
||||||
switch (orderType)
|
switch (orderType)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -22,6 +22,6 @@ set(${MODULE_PREFIX}_SRCS
|
|||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
target_link_libraries(${MODULE_NAME} freerdp)
|
||||||
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||||
|
|||||||
@ -41,8 +41,6 @@
|
|||||||
|
|
||||||
#include "devman.h"
|
#include "devman.h"
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("rdpdr.client")
|
|
||||||
|
|
||||||
static void devman_device_free(void* obj)
|
static void devman_device_free(void* obj)
|
||||||
{
|
{
|
||||||
DEVICE* device = (DEVICE*)obj;
|
DEVICE* device = (DEVICE*)obj;
|
||||||
@ -64,7 +62,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
|||||||
|
|
||||||
if (!devman)
|
if (!devman)
|
||||||
{
|
{
|
||||||
WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!");
|
WLog_INFO(TAG, "calloc failed!");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +72,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
|||||||
|
|
||||||
if (!devman->devices)
|
if (!devman->devices)
|
||||||
{
|
{
|
||||||
WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!");
|
WLog_INFO(TAG, "ListDictionary_New failed!");
|
||||||
free(devman);
|
free(devman);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,13 +76,13 @@ static UINT irp_complete(IRP* irp)
|
|||||||
return irp_free(irp);
|
return irp_free(irp);
|
||||||
}
|
}
|
||||||
|
|
||||||
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
||||||
{
|
{
|
||||||
IRP* irp;
|
IRP* irp;
|
||||||
DEVICE* device;
|
DEVICE* device;
|
||||||
UINT32 DeviceId;
|
UINT32 DeviceId;
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20))
|
if (Stream_GetRemainingLength(s) < 20)
|
||||||
{
|
{
|
||||||
if (error)
|
if (error)
|
||||||
*error = ERROR_INVALID_DATA;
|
*error = ERROR_INVALID_DATA;
|
||||||
@ -94,7 +94,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
|||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!");
|
WLog_WARN(TAG, "devman_get_device_by_id failed!");
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_OK;
|
*error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
|||||||
|
|
||||||
if (!irp)
|
if (!irp)
|
||||||
{
|
{
|
||||||
WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!");
|
WLog_ERR(TAG, "_aligned_malloc failed!");
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_NO_MEMORY;
|
*error = CHANNEL_RC_NO_MEMORY;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -125,7 +125,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
|
|||||||
irp->output = Stream_New(NULL, 256);
|
irp->output = Stream_New(NULL, 256);
|
||||||
if (!irp->output)
|
if (!irp->output)
|
||||||
{
|
{
|
||||||
WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
|
WLog_ERR(TAG, "Stream_New failed!");
|
||||||
_aligned_free(irp);
|
_aligned_free(irp);
|
||||||
if (error)
|
if (error)
|
||||||
*error = CHANNEL_RC_NO_MEMORY;
|
*error = CHANNEL_RC_NO_MEMORY;
|
||||||
|
|||||||
@ -21,9 +21,8 @@
|
|||||||
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||||
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
#define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
|
||||||
|
|
||||||
#include <winpr/wlog.h>
|
|
||||||
#include "rdpdr_main.h"
|
#include "rdpdr_main.h"
|
||||||
|
|
||||||
IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error);
|
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
||||||
|
|||||||
@ -35,8 +35,6 @@
|
|||||||
#include "rdpdr_main.h"
|
#include "rdpdr_main.h"
|
||||||
#include "rdpdr_capabilities.h"
|
#include "rdpdr_capabilities.h"
|
||||||
|
|
||||||
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
|
||||||
|
|
||||||
/* Output device redirection capability set header */
|
/* Output device redirection capability set header */
|
||||||
static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength,
|
static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength,
|
||||||
UINT32 version)
|
UINT32 version)
|
||||||
@ -50,59 +48,39 @@ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16
|
|||||||
static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, 44, GENERAL_CAPABILITY_VERSION_02);
|
||||||
const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1;
|
Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */
|
||||||
const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2;
|
Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */
|
||||||
|
Stream_Write_UINT16(s, 1); /* protocolMajorVersion, must be set to 1 */
|
||||||
rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
|
Stream_Write_UINT16(s, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinorVersion */
|
||||||
GENERAL_CAPABILITY_VERSION_02);
|
Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */
|
||||||
Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */
|
Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */
|
||||||
Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */
|
Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU |
|
||||||
Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */
|
RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
|
||||||
Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */
|
Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */
|
||||||
Stream_Write_UINT32(s, ioCode1); /* ioCode1 */
|
Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */
|
||||||
Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */
|
|
||||||
Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */
|
|
||||||
Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */
|
|
||||||
Stream_Write_UINT32(
|
Stream_Write_UINT32(
|
||||||
s,
|
s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */
|
||||||
rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
|
|
||||||
Stream_Write_UINT32(
|
|
||||||
s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
|
|
||||||
be redirected before logon */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process device direction general capability set */
|
/* Process device direction general capability set */
|
||||||
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
|
UINT16 capabilityLength;
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
|
||||||
if (capabilityLength != 36)
|
if (Stream_GetRemainingLength(s) < 2)
|
||||||
{
|
|
||||||
WLog_Print(rdpdr->log, WLOG_ERROR,
|
|
||||||
"CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32,
|
|
||||||
capabilityLength);
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT32(s, rdpdr->serverOsType); /* osType, ignored on receipt */
|
Stream_Read_UINT16(s, capabilityLength);
|
||||||
Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */
|
|
||||||
Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */
|
if (capabilityLength < 4)
|
||||||
Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */
|
return ERROR_INVALID_DATA;
|
||||||
Stream_Read_UINT32(s, rdpdr->serverIOCode1); /* ioCode1 */
|
|
||||||
Stream_Read_UINT32(
|
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
|
||||||
s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */
|
return ERROR_INVALID_DATA;
|
||||||
Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */
|
|
||||||
Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */
|
Stream_Seek(s, capabilityLength - 4U);
|
||||||
Stream_Read_UINT32(
|
|
||||||
s,
|
|
||||||
rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
|
|
||||||
Stream_Read_UINT32(
|
|
||||||
s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
|
|
||||||
be redirected before logon */
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +92,23 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process printer direction capability set */
|
/* Process printer direction capability set */
|
||||||
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
|
UINT16 capabilityLength;
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
if (Stream_GetRemainingLength(s) < 2)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Seek(s, capabilityLength);
|
Stream_Read_UINT16(s, capabilityLength);
|
||||||
|
|
||||||
|
if (capabilityLength < 4)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
Stream_Seek(s, capabilityLength - 4U);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,14 +120,23 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process port redirection capability set */
|
/* Process port redirection capability set */
|
||||||
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
|
UINT16 capabilityLength;
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
if (Stream_GetRemainingLength(s) < 2)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Seek(s, capabilityLength);
|
Stream_Read_UINT16(s, capabilityLength);
|
||||||
|
|
||||||
|
if (capabilityLength < 4U)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
Stream_Seek(s, capabilityLength - 4U);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,14 +148,23 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process drive redirection capability set */
|
/* Process drive redirection capability set */
|
||||||
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
|
UINT16 capabilityLength;
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
if (Stream_GetRemainingLength(s) < 2)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Seek(s, capabilityLength);
|
Stream_Read_UINT16(s, capabilityLength);
|
||||||
|
|
||||||
|
if (capabilityLength < 4)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
Stream_Seek(s, capabilityLength - 4U);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,14 +176,23 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process smartcard redirection capability set */
|
/* Process smartcard redirection capability set */
|
||||||
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||||
{
|
{
|
||||||
|
UINT16 capabilityLength;
|
||||||
WINPR_UNUSED(rdpdr);
|
WINPR_UNUSED(rdpdr);
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
if (Stream_GetRemainingLength(s) < 2)
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Seek(s, capabilityLength);
|
Stream_Read_UINT16(s, capabilityLength);
|
||||||
|
|
||||||
|
if (capabilityLength < 4)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
|
||||||
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
|
Stream_Seek(s, capabilityLength - 4U);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,14 +201,12 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
UINT status = CHANNEL_RC_OK;
|
UINT status = CHANNEL_RC_OK;
|
||||||
UINT16 i;
|
UINT16 i;
|
||||||
UINT16 numCapabilities;
|
UINT16 numCapabilities;
|
||||||
|
UINT16 capabilityType;
|
||||||
|
|
||||||
if (!rdpdr || !s)
|
if (!rdpdr || !s)
|
||||||
return CHANNEL_RC_NULL_DATA;
|
return CHANNEL_RC_NULL_DATA;
|
||||||
|
|
||||||
WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_SERVER_CAPS);
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_CLIENT_CAPS);
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
|
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT16(s, numCapabilities);
|
Stream_Read_UINT16(s, numCapabilities);
|
||||||
@ -202,44 +214,31 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
|||||||
|
|
||||||
for (i = 0; i < numCapabilities; i++)
|
for (i = 0; i < numCapabilities; i++)
|
||||||
{
|
{
|
||||||
UINT16 capabilityType;
|
if (Stream_GetRemainingLength(s) < sizeof(UINT16))
|
||||||
UINT16 capabilityLength;
|
|
||||||
UINT32 version = 0;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s,
|
|
||||||
2 * sizeof(UINT16) + sizeof(UINT32)))
|
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
|
||||||
Stream_Read_UINT16(s, capabilityType);
|
Stream_Read_UINT16(s, capabilityType);
|
||||||
Stream_Read_UINT16(s, capabilityLength);
|
|
||||||
Stream_Read_UINT32(s, version);
|
|
||||||
|
|
||||||
if (capabilityLength < 8)
|
|
||||||
{
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
capabilityLength -= 8;
|
|
||||||
|
|
||||||
switch (capabilityType)
|
switch (capabilityType)
|
||||||
{
|
{
|
||||||
case CAP_GENERAL_TYPE:
|
case CAP_GENERAL_TYPE:
|
||||||
status = rdpdr_process_general_capset(rdpdr, s, capabilityLength);
|
status = rdpdr_process_general_capset(rdpdr, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAP_PRINTER_TYPE:
|
case CAP_PRINTER_TYPE:
|
||||||
status = rdpdr_process_printer_capset(rdpdr, s, capabilityLength);
|
status = rdpdr_process_printer_capset(rdpdr, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAP_PORT_TYPE:
|
case CAP_PORT_TYPE:
|
||||||
status = rdpdr_process_port_capset(rdpdr, s, capabilityLength);
|
status = rdpdr_process_port_capset(rdpdr, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAP_DRIVE_TYPE:
|
case CAP_DRIVE_TYPE:
|
||||||
status = rdpdr_process_drive_capset(rdpdr, s, capabilityLength);
|
status = rdpdr_process_drive_capset(rdpdr, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAP_SMARTCARD_TYPE:
|
case CAP_SMARTCARD_TYPE:
|
||||||
status = rdpdr_process_smartcard_capset(rdpdr, s, capabilityLength);
|
status = rdpdr_process_smartcard_capset(rdpdr, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -265,7 +264,7 @@ UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
|||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
|
WLog_ERR(TAG, "Stream_New failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -42,33 +42,15 @@
|
|||||||
#include <CoreServices/CoreServices.h>
|
#include <CoreServices/CoreServices.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(Stream_CheckAndLogRequiredLengthWLog)
|
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||||
#define Stream_CheckAndLogRequiredLengthWLog(log, s, len) \
|
|
||||||
Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \
|
|
||||||
__FILE__, __LINE__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct rdpdr_plugin rdpdrPlugin;
|
typedef struct rdpdr_plugin rdpdrPlugin;
|
||||||
|
|
||||||
enum RDPDR_CHANNEL_STATE
|
|
||||||
{
|
|
||||||
RDPDR_CHANNEL_STATE_INITIAL = 0,
|
|
||||||
RDPDR_CHANNEL_STATE_ANNOUNCE,
|
|
||||||
RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY,
|
|
||||||
RDPDR_CHANNEL_STATE_NAME_REQUEST,
|
|
||||||
RDPDR_CHANNEL_STATE_SERVER_CAPS,
|
|
||||||
RDPDR_CHANNEL_STATE_CLIENT_CAPS,
|
|
||||||
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM,
|
|
||||||
RDPDR_CHANNEL_STATE_READY,
|
|
||||||
RDPDR_CHANNEL_STATE_USER_LOGGEDON
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rdpdr_plugin
|
struct rdpdr_plugin
|
||||||
{
|
{
|
||||||
CHANNEL_DEF channelDef;
|
CHANNEL_DEF channelDef;
|
||||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||||
|
|
||||||
enum RDPDR_CHANNEL_STATE state;
|
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
wStream* data_in;
|
wStream* data_in;
|
||||||
void* InitHandle;
|
void* InitHandle;
|
||||||
@ -77,33 +59,12 @@ struct rdpdr_plugin
|
|||||||
|
|
||||||
DEVMAN* devman;
|
DEVMAN* devman;
|
||||||
|
|
||||||
UINT32 serverOsType;
|
UINT16 versionMajor;
|
||||||
UINT32 serverOsVersion;
|
UINT16 versionMinor;
|
||||||
UINT16 serverVersionMajor;
|
UINT16 clientID;
|
||||||
UINT16 serverVersionMinor;
|
|
||||||
UINT32 serverExtendedPDU;
|
|
||||||
UINT32 serverIOCode1;
|
|
||||||
UINT32 serverIOCode2;
|
|
||||||
UINT32 serverExtraFlags1;
|
|
||||||
UINT32 serverExtraFlags2;
|
|
||||||
UINT32 serverSpecialTypeDeviceCap;
|
|
||||||
|
|
||||||
UINT32 clientOsType;
|
|
||||||
UINT32 clientOsVersion;
|
|
||||||
UINT16 clientVersionMajor;
|
|
||||||
UINT16 clientVersionMinor;
|
|
||||||
UINT32 clientExtendedPDU;
|
|
||||||
UINT32 clientIOCode1;
|
|
||||||
UINT32 clientIOCode2;
|
|
||||||
UINT32 clientExtraFlags1;
|
|
||||||
UINT32 clientExtraFlags2;
|
|
||||||
UINT32 clientSpecialTypeDeviceCap;
|
|
||||||
|
|
||||||
UINT32 clientID;
|
|
||||||
char computerName[256];
|
char computerName[256];
|
||||||
|
|
||||||
UINT32 sequenceId;
|
UINT32 sequenceId;
|
||||||
BOOL userLoggedOn;
|
|
||||||
|
|
||||||
/* hotplug support */
|
/* hotplug support */
|
||||||
HANDLE hotplugThread;
|
HANDLE hotplugThread;
|
||||||
@ -117,10 +78,8 @@ struct rdpdr_plugin
|
|||||||
HANDLE stopEvent;
|
HANDLE stopEvent;
|
||||||
#endif
|
#endif
|
||||||
rdpContext* rdpcontext;
|
rdpContext* rdpcontext;
|
||||||
wLog* log;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);
|
|
||||||
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
||||||
|
|
||||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */
|
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */
|
||||||
|
|||||||
@ -1952,7 +1952,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con
|
|||||||
irp->Callback = rdpdr_server_drive_query_directory_callback2;
|
irp->Callback = rdpdr_server_drive_query_directory_callback2;
|
||||||
irp->DeviceId = deviceId;
|
irp->DeviceId = deviceId;
|
||||||
irp->FileId = fileId;
|
irp->FileId = fileId;
|
||||||
winpr_str_append("\\*.*", irp->PathName, ARRAYSIZE(irp->PathName), NULL);
|
strcat(irp->PathName, "\\*.*");
|
||||||
|
|
||||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -57,6 +57,13 @@ struct _RDPDR_HEADER
|
|||||||
};
|
};
|
||||||
typedef struct _RDPDR_HEADER RDPDR_HEADER;
|
typedef struct _RDPDR_HEADER RDPDR_HEADER;
|
||||||
|
|
||||||
|
#define RDPDR_VERSION_MAJOR 0x0001
|
||||||
|
|
||||||
|
#define RDPDR_VERSION_MINOR_RDP50 0x0002
|
||||||
|
#define RDPDR_VERSION_MINOR_RDP51 0x0005
|
||||||
|
#define RDPDR_VERSION_MINOR_RDP52 0x000A
|
||||||
|
#define RDPDR_VERSION_MINOR_RDP6X 0x000C
|
||||||
|
|
||||||
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
||||||
|
|
||||||
struct _RDPDR_CAPABILITY_HEADER
|
struct _RDPDR_CAPABILITY_HEADER
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel("rdpecam")
|
|
||||||
|
|
||||||
if(WITH_SERVER_CHANNELS)
|
|
||||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
|
||||||
endif()
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
set(OPTION_DEFAULT ON)
|
|
||||||
set(OPTION_CLIENT_DEFAULT OFF)
|
|
||||||
set(OPTION_SERVER_DEFAULT ON)
|
|
||||||
|
|
||||||
define_channel_options(NAME "rdpecam" TYPE "dynamic"
|
|
||||||
DESCRIPTION "Video Capture Virtual Channel Extension"
|
|
||||||
SPECIFICATIONS "[MS-RDPECAM]"
|
|
||||||
DEFAULT ${OPTION_DEFAULT})
|
|
||||||
|
|
||||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
|
||||||
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel_server("rdpecam")
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
|
||||||
camera_device_enumerator_main.c
|
|
||||||
camera_device_main.c)
|
|
||||||
|
|
||||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} freerdp)
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
|
||||||
@ -1,612 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Video Capture Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
#include <freerdp/server/rdpecam-enumerator.h>
|
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("rdpecam-enumerator.server")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
ENUMERATOR_INITIAL,
|
|
||||||
ENUMERATOR_OPENED,
|
|
||||||
} eEnumeratorChannelState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
CamDevEnumServerContext context;
|
|
||||||
|
|
||||||
HANDLE stopEvent;
|
|
||||||
|
|
||||||
HANDLE thread;
|
|
||||||
void* enumerator_channel;
|
|
||||||
|
|
||||||
DWORD SessionId;
|
|
||||||
|
|
||||||
BOOL isOpened;
|
|
||||||
BOOL externalThread;
|
|
||||||
|
|
||||||
/* Channel state */
|
|
||||||
eEnumeratorChannelState state;
|
|
||||||
|
|
||||||
wStream* buffer;
|
|
||||||
} enumerator_server;
|
|
||||||
|
|
||||||
static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (enumerator->isOpened)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
|
|
||||||
"calling in this state is not possible!");
|
|
||||||
return ERROR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerator->externalThread = externalThread;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_open_channel(enumerator_server* enumerator)
|
|
||||||
{
|
|
||||||
CamDevEnumServerContext* context = &enumerator->context;
|
|
||||||
DWORD Error = ERROR_SUCCESS;
|
|
||||||
HANDLE hEvent;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
PULONG pSessionId = NULL;
|
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerator->SessionId = (DWORD)*pSessionId;
|
|
||||||
WTSFreeMemory(pSessionId);
|
|
||||||
hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
|
|
||||||
enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
|
||||||
if (!enumerator->enumerator_channel)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_SELECT_VERSION_REQUEST pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_DEVICE_ADDED_NOTIFICATION pdu;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
size_t remaining_length;
|
|
||||||
WCHAR* channel_name_start;
|
|
||||||
char* tmp;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RequiredLength 4:
|
|
||||||
*
|
|
||||||
* Nullterminator DeviceName (2),
|
|
||||||
* VirtualChannelName (>= 1),
|
|
||||||
* Nullterminator VirtualChannelName (1)
|
|
||||||
*/
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
pdu.DeviceName = (WCHAR*)Stream_Pointer(s);
|
|
||||||
|
|
||||||
remaining_length = Stream_GetRemainingLength(s);
|
|
||||||
channel_name_start = (WCHAR*)Stream_Pointer(s);
|
|
||||||
|
|
||||||
/* Search for null terminator of DeviceName */
|
|
||||||
for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
|
|
||||||
{
|
|
||||||
if (*channel_name_start == L'\0')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*channel_name_start != L'\0')
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
pdu.VirtualChannelName = (char*)++channel_name_start;
|
|
||||||
++i;
|
|
||||||
|
|
||||||
if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG,
|
|
||||||
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = pdu.VirtualChannelName;
|
|
||||||
for (; i < remaining_length; ++i, ++tmp)
|
|
||||||
{
|
|
||||||
if (*tmp == '\0')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*tmp != '\0')
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG,
|
|
||||||
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_DEVICE_REMOVED_NOTIFICATION pdu;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
size_t remaining_length;
|
|
||||||
char* tmp;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
pdu.VirtualChannelName = (char*)Stream_Pointer(s);
|
|
||||||
|
|
||||||
remaining_length = Stream_GetRemainingLength(s);
|
|
||||||
tmp = (char*)(Stream_Pointer(s) + 1);
|
|
||||||
|
|
||||||
for (i = 1; i < remaining_length; ++i, ++tmp)
|
|
||||||
{
|
|
||||||
if (*tmp == '\0')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*tmp != '\0')
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG,
|
|
||||||
"enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_process_message(enumerator_server* enumerator)
|
|
||||||
{
|
|
||||||
BOOL rc;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
ULONG BytesReturned;
|
|
||||||
CAM_SHARED_MSG_HEADER header = { 0 };
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
WINPR_ASSERT(enumerator->enumerator_channel);
|
|
||||||
|
|
||||||
s = enumerator->buffer;
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
|
|
||||||
if (!rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (BytesReturned < 1)
|
|
||||||
{
|
|
||||||
error = CHANNEL_RC_OK;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
|
||||||
error = CHANNEL_RC_NO_MEMORY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s),
|
|
||||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_SetLength(s, BytesReturned);
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, header.Version);
|
|
||||||
Stream_Read_UINT8(s, header.MessageId);
|
|
||||||
|
|
||||||
switch (header.MessageId)
|
|
||||||
{
|
|
||||||
case CAM_MSG_ID_SelectVersionRequest:
|
|
||||||
error =
|
|
||||||
enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_DeviceAddedNotification:
|
|
||||||
error =
|
|
||||||
enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_DeviceRemovedNotification:
|
|
||||||
error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
|
|
||||||
&header);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
|
|
||||||
header.MessageId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
switch (enumerator->state)
|
|
||||||
{
|
|
||||||
case ENUMERATOR_INITIAL:
|
|
||||||
error = enumerator_server_open_channel(enumerator);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
|
|
||||||
error);
|
|
||||||
else
|
|
||||||
enumerator->state = ENUMERATOR_OPENED;
|
|
||||||
break;
|
|
||||||
case ENUMERATOR_OPENED:
|
|
||||||
error = enumerator_process_message(enumerator);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
|
|
||||||
{
|
|
||||||
void* buffer = NULL;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
HANDLE ChannelEvent = NULL;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
|
|
||||||
&BytesReturned) == TRUE)
|
|
||||||
{
|
|
||||||
if (BytesReturned == sizeof(HANDLE))
|
|
||||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
|
||||||
|
|
||||||
WTSFreeMemory(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChannelEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
|
|
||||||
{
|
|
||||||
DWORD nCount;
|
|
||||||
HANDLE events[2] = { 0 };
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)arg;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
DWORD status;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
nCount = 0;
|
|
||||||
events[nCount++] = enumerator->stopEvent;
|
|
||||||
|
|
||||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
|
||||||
{
|
|
||||||
switch (enumerator->state)
|
|
||||||
{
|
|
||||||
case ENUMERATOR_INITIAL:
|
|
||||||
error = enumerator_server_context_poll_int(&enumerator->context);
|
|
||||||
if (error == CHANNEL_RC_OK)
|
|
||||||
{
|
|
||||||
events[1] = enumerator_server_get_channel_handle(enumerator);
|
|
||||||
nCount = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ENUMERATOR_OPENED:
|
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
break;
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
error = enumerator_server_context_poll_int(&enumerator->context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAIT_FAILED:
|
|
||||||
default:
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
|
||||||
enumerator->enumerator_channel = NULL;
|
|
||||||
|
|
||||||
if (error && enumerator->context.rdpcontext)
|
|
||||||
setChannelError(enumerator->context.rdpcontext, error,
|
|
||||||
"enumerator_server_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_open(CamDevEnumServerContext* context)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (!enumerator->externalThread && (enumerator->thread == NULL))
|
|
||||||
{
|
|
||||||
enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!enumerator->stopEvent)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerator->thread =
|
|
||||||
CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
|
|
||||||
if (!enumerator->thread)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
|
||||||
CloseHandle(enumerator->stopEvent);
|
|
||||||
enumerator->stopEvent = NULL;
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator->isOpened = TRUE;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_close(CamDevEnumServerContext* context)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (!enumerator->externalThread && enumerator->thread)
|
|
||||||
{
|
|
||||||
SetEvent(enumerator->stopEvent);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(enumerator->thread);
|
|
||||||
CloseHandle(enumerator->stopEvent);
|
|
||||||
enumerator->thread = NULL;
|
|
||||||
enumerator->stopEvent = NULL;
|
|
||||||
}
|
|
||||||
if (enumerator->externalThread)
|
|
||||||
{
|
|
||||||
if (enumerator->state != ENUMERATOR_INITIAL)
|
|
||||||
{
|
|
||||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
|
||||||
enumerator->enumerator_channel = NULL;
|
|
||||||
enumerator->state = ENUMERATOR_INITIAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator->isOpened = FALSE;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
|
|
||||||
if (!enumerator->externalThread)
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
return enumerator_server_context_poll_int(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(enumerator);
|
|
||||||
WINPR_ASSERT(handle);
|
|
||||||
|
|
||||||
if (!enumerator->externalThread)
|
|
||||||
return FALSE;
|
|
||||||
if (enumerator->state == ENUMERATOR_INITIAL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
*handle = enumerator_server_get_channel_handle(enumerator);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
ULONG written;
|
|
||||||
|
|
||||||
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s),
|
|
||||||
Stream_GetPosition(s), &written))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (written < Stream_GetPosition(s))
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
|
|
||||||
Stream_GetPosition(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
Stream_Free(s, TRUE);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT enumerator_send_select_version_response_pdu(
|
|
||||||
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
s = Stream_New(NULL, CAM_HEADER_SIZE);
|
|
||||||
if (!s)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
|
|
||||||
Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId);
|
|
||||||
|
|
||||||
return enumerator_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
|
|
||||||
|
|
||||||
if (!enumerator)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
enumerator->context.vcm = vcm;
|
|
||||||
enumerator->context.Initialize = enumerator_server_initialize;
|
|
||||||
enumerator->context.Open = enumerator_server_open;
|
|
||||||
enumerator->context.Close = enumerator_server_close;
|
|
||||||
enumerator->context.Poll = enumerator_server_context_poll;
|
|
||||||
enumerator->context.ChannelHandle = enumerator_server_context_handle;
|
|
||||||
|
|
||||||
enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
|
|
||||||
|
|
||||||
enumerator->buffer = Stream_New(NULL, 4096);
|
|
||||||
if (!enumerator->buffer)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
return &enumerator->context;
|
|
||||||
fail:
|
|
||||||
cam_dev_enum_server_context_free(&enumerator->context);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
|
|
||||||
{
|
|
||||||
enumerator_server* enumerator = (enumerator_server*)context;
|
|
||||||
|
|
||||||
if (enumerator)
|
|
||||||
{
|
|
||||||
enumerator_server_close(context);
|
|
||||||
Stream_Free(enumerator->buffer, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(enumerator);
|
|
||||||
}
|
|
||||||
@ -1,969 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Video Capture Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
#include <freerdp/server/rdpecam.h>
|
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("rdpecam.server")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
CAMERA_DEVICE_INITIAL,
|
|
||||||
CAMERA_DEVICE_OPENED,
|
|
||||||
} eCameraDeviceChannelState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
CameraDeviceServerContext context;
|
|
||||||
|
|
||||||
HANDLE stopEvent;
|
|
||||||
|
|
||||||
HANDLE thread;
|
|
||||||
void* device_channel;
|
|
||||||
|
|
||||||
DWORD SessionId;
|
|
||||||
|
|
||||||
BOOL isOpened;
|
|
||||||
BOOL externalThread;
|
|
||||||
|
|
||||||
/* Channel state */
|
|
||||||
eCameraDeviceChannelState state;
|
|
||||||
|
|
||||||
wStream* buffer;
|
|
||||||
} device_server;
|
|
||||||
|
|
||||||
static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (device->isOpened)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Application error: Camera channel already initialized, "
|
|
||||||
"calling in this state is not possible!");
|
|
||||||
return ERROR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->externalThread = externalThread;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_open_channel(device_server* device)
|
|
||||||
{
|
|
||||||
CameraDeviceServerContext* context = &device->context;
|
|
||||||
DWORD Error = ERROR_SUCCESS;
|
|
||||||
HANDLE hEvent;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
PULONG pSessionId = NULL;
|
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->SessionId = (DWORD)*pSessionId;
|
|
||||||
WTSFreeMemory(pSessionId);
|
|
||||||
hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName,
|
|
||||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
|
||||||
if (!device->device_channel)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(device->device_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_SUCCESS_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
IFCALLRET(context->SuccessResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_ERROR_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT32(s, pdu.ErrorCode);
|
|
||||||
|
|
||||||
IFCALLRET(context->ErrorResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_STREAM_LIST_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
BYTE i;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255);
|
|
||||||
|
|
||||||
for (i = 0; i < pdu.N_Descriptions; ++i)
|
|
||||||
{
|
|
||||||
CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i];
|
|
||||||
|
|
||||||
Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes);
|
|
||||||
Stream_Read_UINT8(s, StreamDescription->StreamCategory);
|
|
||||||
Stream_Read_UINT8(s, StreamDescription->Selected);
|
|
||||||
Stream_Read_UINT8(s, StreamDescription->CanBeShared);
|
|
||||||
}
|
|
||||||
|
|
||||||
IFCALLRET(context->StreamListResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_MEDIA_TYPE_LIST_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
BYTE i;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26;
|
|
||||||
|
|
||||||
pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
|
|
||||||
if (!pdu.MediaTypeDescriptions)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs",
|
|
||||||
pdu.N_Descriptions);
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < pdu.N_Descriptions; ++i)
|
|
||||||
{
|
|
||||||
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i];
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, MediaTypeDescriptions->Format);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->Width);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->Height);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator);
|
|
||||||
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator);
|
|
||||||
Stream_Read_UINT8(s, MediaTypeDescriptions->Flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
IFCALLRET(context->MediaTypeListResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
free(pdu.MediaTypeDescriptions);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator);
|
|
||||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator);
|
|
||||||
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags);
|
|
||||||
|
|
||||||
IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_SAMPLE_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, pdu.StreamIndex);
|
|
||||||
|
|
||||||
pdu.SampleSize = Stream_GetRemainingLength(s);
|
|
||||||
pdu.Sample = Stream_Pointer(s);
|
|
||||||
|
|
||||||
IFCALLRET(context->SampleResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_SAMPLE_ERROR_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, pdu.StreamIndex);
|
|
||||||
Stream_Read_UINT32(s, pdu.ErrorCode);
|
|
||||||
|
|
||||||
IFCALLRET(context->SampleErrorResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_PROPERTY_LIST_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
pdu.N_Properties = Stream_GetRemainingLength(s) / 19;
|
|
||||||
|
|
||||||
if (pdu.N_Properties > 0)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION));
|
|
||||||
if (!pdu.Properties)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs",
|
|
||||||
pdu.N_Properties);
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < pdu.N_Properties; ++i)
|
|
||||||
{
|
|
||||||
Stream_Read_UINT8(s, pdu.Properties[i].PropertySet);
|
|
||||||
Stream_Read_UINT8(s, pdu.Properties[i].PropertyId);
|
|
||||||
Stream_Read_UINT8(s, pdu.Properties[i].Capabilities);
|
|
||||||
Stream_Read_INT32(s, pdu.Properties[i].MinValue);
|
|
||||||
Stream_Read_INT32(s, pdu.Properties[i].MaxValue);
|
|
||||||
Stream_Read_INT32(s, pdu.Properties[i].Step);
|
|
||||||
Stream_Read_INT32(s, pdu.Properties[i].DefaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IFCALLRET(context->PropertyListResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
free(pdu.Properties);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context,
|
|
||||||
wStream* s,
|
|
||||||
const CAM_SHARED_MSG_HEADER* header)
|
|
||||||
{
|
|
||||||
CAM_PROPERTY_VALUE_RESPONSE pdu = { 0 };
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
pdu.Header = *header;
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, pdu.PropertyValue.Mode);
|
|
||||||
Stream_Read_INT32(s, pdu.PropertyValue.Value);
|
|
||||||
|
|
||||||
IFCALLRET(context->PropertyValueResponse, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_process_message(device_server* device)
|
|
||||||
{
|
|
||||||
BOOL rc;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
ULONG BytesReturned;
|
|
||||||
CAM_SHARED_MSG_HEADER header = { 0 };
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
WINPR_ASSERT(device->device_channel);
|
|
||||||
|
|
||||||
s = device->buffer;
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned);
|
|
||||||
if (!rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (BytesReturned < 1)
|
|
||||||
{
|
|
||||||
error = CHANNEL_RC_OK;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
|
||||||
error = CHANNEL_RC_NO_MEMORY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s),
|
|
||||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_SetLength(s, BytesReturned);
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, header.Version);
|
|
||||||
Stream_Read_UINT8(s, header.MessageId);
|
|
||||||
|
|
||||||
switch (header.MessageId)
|
|
||||||
{
|
|
||||||
case CAM_MSG_ID_SuccessResponse:
|
|
||||||
error = device_server_handle_success_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_ErrorResponse:
|
|
||||||
error = device_server_recv_error_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_StreamListResponse:
|
|
||||||
error = device_server_recv_stream_list_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_MediaTypeListResponse:
|
|
||||||
error = device_server_recv_media_type_list_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_CurrentMediaTypeResponse:
|
|
||||||
error = device_server_recv_current_media_type_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_SampleResponse:
|
|
||||||
error = device_server_recv_sample_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_SampleErrorResponse:
|
|
||||||
error = device_server_recv_sample_error_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_PropertyListResponse:
|
|
||||||
error = device_server_recv_property_list_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
case CAM_MSG_ID_PropertyValueResponse:
|
|
||||||
error = device_server_recv_property_value_response(&device->context, s, &header);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "",
|
|
||||||
header.MessageId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_context_poll_int(CameraDeviceServerContext* context)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
switch (device->state)
|
|
||||||
{
|
|
||||||
case CAMERA_DEVICE_INITIAL:
|
|
||||||
error = device_server_open_channel(device);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error);
|
|
||||||
else
|
|
||||||
device->state = CAMERA_DEVICE_OPENED;
|
|
||||||
break;
|
|
||||||
case CAMERA_DEVICE_OPENED:
|
|
||||||
error = device_process_message(device);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HANDLE device_server_get_channel_handle(device_server* device)
|
|
||||||
{
|
|
||||||
void* buffer = NULL;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
HANDLE ChannelEvent = NULL;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer,
|
|
||||||
&BytesReturned) == TRUE)
|
|
||||||
{
|
|
||||||
if (BytesReturned == sizeof(HANDLE))
|
|
||||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
|
||||||
|
|
||||||
WTSFreeMemory(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChannelEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI device_server_thread_func(LPVOID arg)
|
|
||||||
{
|
|
||||||
DWORD nCount;
|
|
||||||
HANDLE events[2] = { 0 };
|
|
||||||
device_server* device = (device_server*)arg;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
DWORD status;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
nCount = 0;
|
|
||||||
events[nCount++] = device->stopEvent;
|
|
||||||
|
|
||||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
|
||||||
{
|
|
||||||
switch (device->state)
|
|
||||||
{
|
|
||||||
case CAMERA_DEVICE_INITIAL:
|
|
||||||
error = device_server_context_poll_int(&device->context);
|
|
||||||
if (error == CHANNEL_RC_OK)
|
|
||||||
{
|
|
||||||
events[1] = device_server_get_channel_handle(device);
|
|
||||||
nCount = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CAMERA_DEVICE_OPENED:
|
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
break;
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
error = device_server_context_poll_int(&device->context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAIT_FAILED:
|
|
||||||
default:
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WTSVirtualChannelClose(device->device_channel);
|
|
||||||
device->device_channel = NULL;
|
|
||||||
|
|
||||||
if (error && device->context.rdpcontext)
|
|
||||||
setChannelError(device->context.rdpcontext, error,
|
|
||||||
"device_server_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_open(CameraDeviceServerContext* context)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (!device->externalThread && (device->thread == NULL))
|
|
||||||
{
|
|
||||||
device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!device->stopEvent)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL);
|
|
||||||
if (!device->thread)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
|
||||||
CloseHandle(device->stopEvent);
|
|
||||||
device->stopEvent = NULL;
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
device->isOpened = TRUE;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_close(CameraDeviceServerContext* context)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (!device->externalThread && device->thread)
|
|
||||||
{
|
|
||||||
SetEvent(device->stopEvent);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(device->thread);
|
|
||||||
CloseHandle(device->stopEvent);
|
|
||||||
device->thread = NULL;
|
|
||||||
device->stopEvent = NULL;
|
|
||||||
}
|
|
||||||
if (device->externalThread)
|
|
||||||
{
|
|
||||||
if (device->state != CAMERA_DEVICE_INITIAL)
|
|
||||||
{
|
|
||||||
WTSVirtualChannelClose(device->device_channel);
|
|
||||||
device->device_channel = NULL;
|
|
||||||
device->state = CAMERA_DEVICE_INITIAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
device->isOpened = FALSE;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_context_poll(CameraDeviceServerContext* context)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
|
|
||||||
if (!device->externalThread)
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
return device_server_context_poll_int(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(device);
|
|
||||||
WINPR_ASSERT(handle);
|
|
||||||
|
|
||||||
if (!device->externalThread)
|
|
||||||
return FALSE;
|
|
||||||
if (device->state == CAMERA_DEVICE_INITIAL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
*handle = device_server_get_channel_handle(device);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
/* Allocate what we need plus header bytes */
|
|
||||||
s = Stream_New(NULL, size + CAM_HEADER_SIZE);
|
|
||||||
if (!s)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, version);
|
|
||||||
Stream_Write_UINT8(s, messageId);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
ULONG written;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s),
|
|
||||||
Stream_GetPosition(s), &written))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (written < Stream_GetPosition(s))
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
|
|
||||||
Stream_GetPosition(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
Stream_Free(s, TRUE);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
s = device_server_packet_new(0, context->protocolVersion, messageId);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT
|
|
||||||
device_send_activate_device_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_deactivate_device_request_pdu(
|
|
||||||
CameraDeviceServerContext* context,
|
|
||||||
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_STREAM_LIST_REQUEST* streamListRequest)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT
|
|
||||||
device_send_media_type_list_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(mediaTypeListRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_MediaTypeListRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex);
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_current_media_type_request_pdu(
|
|
||||||
CameraDeviceServerContext* context,
|
|
||||||
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(currentMediaTypeRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_CurrentMediaTypeRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex);
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT
|
|
||||||
device_send_start_streams_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_START_STREAMS_REQUEST* startStreamsRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(startStreamsRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(startStreamsRequest->N_Infos * 27ul, context->protocolVersion,
|
|
||||||
CAM_MSG_ID_StartStreamsRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
for (i = 0; i < startStreamsRequest->N_Infos; ++i)
|
|
||||||
{
|
|
||||||
const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i];
|
|
||||||
const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, info->StreamIndex);
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, description->Format);
|
|
||||||
Stream_Write_UINT32(s, description->Width);
|
|
||||||
Stream_Write_UINT32(s, description->Height);
|
|
||||||
Stream_Write_UINT32(s, description->FrameRateNumerator);
|
|
||||||
Stream_Write_UINT32(s, description->FrameRateDenominator);
|
|
||||||
Stream_Write_UINT32(s, description->PixelAspectRatioNumerator);
|
|
||||||
Stream_Write_UINT32(s, description->PixelAspectRatioDenominator);
|
|
||||||
Stream_Write_UINT8(s, description->Flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_SAMPLE_REQUEST* sampleRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(sampleRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_SampleRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, sampleRequest->StreamIndex);
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT
|
|
||||||
device_send_property_list_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_PROPERTY_LIST_REQUEST* propertyListRequest)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT
|
|
||||||
device_send_property_value_request_pdu(CameraDeviceServerContext* context,
|
|
||||||
const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(propertyValueRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(2, context->protocolVersion, CAM_MSG_ID_PropertyValueRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, propertyValueRequest->PropertySet);
|
|
||||||
Stream_Write_UINT8(s, propertyValueRequest->PropertyId);
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT device_send_set_property_value_request_pdu(
|
|
||||||
CameraDeviceServerContext* context,
|
|
||||||
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(setPropertyValueRequest);
|
|
||||||
|
|
||||||
s = device_server_packet_new(2 + 5, context->protocolVersion,
|
|
||||||
CAM_MSG_ID_SetPropertyValueRequest);
|
|
||||||
if (!s)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet);
|
|
||||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId);
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode);
|
|
||||||
Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value);
|
|
||||||
|
|
||||||
return device_server_packet_send(context, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)calloc(1, sizeof(device_server));
|
|
||||||
|
|
||||||
if (!device)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
device->context.vcm = vcm;
|
|
||||||
device->context.Initialize = device_server_initialize;
|
|
||||||
device->context.Open = device_server_open;
|
|
||||||
device->context.Close = device_server_close;
|
|
||||||
device->context.Poll = device_server_context_poll;
|
|
||||||
device->context.ChannelHandle = device_server_context_handle;
|
|
||||||
|
|
||||||
device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu;
|
|
||||||
device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu;
|
|
||||||
|
|
||||||
device->context.StreamListRequest = device_send_stream_list_request_pdu;
|
|
||||||
device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu;
|
|
||||||
device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu;
|
|
||||||
|
|
||||||
device->context.StartStreamsRequest = device_send_start_streams_request_pdu;
|
|
||||||
device->context.StopStreamsRequest = device_send_stop_streams_request_pdu;
|
|
||||||
device->context.SampleRequest = device_send_sample_request_pdu;
|
|
||||||
|
|
||||||
device->context.PropertyListRequest = device_send_property_list_request_pdu;
|
|
||||||
device->context.PropertyValueRequest = device_send_property_value_request_pdu;
|
|
||||||
device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu;
|
|
||||||
|
|
||||||
device->buffer = Stream_New(NULL, 4096);
|
|
||||||
if (!device->buffer)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
return &device->context;
|
|
||||||
fail:
|
|
||||||
camera_device_server_context_free(&device->context);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void camera_device_server_context_free(CameraDeviceServerContext* context)
|
|
||||||
{
|
|
||||||
device_server* device = (device_server*)context;
|
|
||||||
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
device_server_close(context);
|
|
||||||
Stream_Free(device->buffer, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(context->virtualChannelName);
|
|
||||||
|
|
||||||
free(device);
|
|
||||||
}
|
|
||||||
@ -1386,6 +1386,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
error_out:
|
error_out:
|
||||||
free(context);
|
free(context);
|
||||||
|
free(rdpei->contactPoints);
|
||||||
free(rdpei);
|
free(rdpei);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,8 +99,6 @@ UINT rdpei_server_init(RdpeiServerContext* context)
|
|||||||
void* buffer = NULL;
|
void* buffer = NULL;
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
RdpeiServerPrivate* priv = context->priv;
|
RdpeiServerPrivate* priv = context->priv;
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME,
|
priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME,
|
||||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||||
@ -110,15 +108,6 @@ UINT rdpei_server_init(RdpeiServerContext* context)
|
|||||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(priv->channelHandle);
|
|
||||||
|
|
||||||
IFCALLRET(context->onChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->onChannelIdAssigned failed!");
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer,
|
if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer,
|
||||||
&bytesReturned) ||
|
&bytesReturned) ||
|
||||||
(bytesReturned != sizeof(HANDLE)))
|
(bytesReturned != sizeof(HANDLE)))
|
||||||
@ -623,7 +612,6 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version,
|
|||||||
{
|
{
|
||||||
ULONG written;
|
ULONG written;
|
||||||
RdpeiServerPrivate* priv = context->priv;
|
RdpeiServerPrivate* priv = context->priv;
|
||||||
UINT32 pduLen = 4;
|
|
||||||
|
|
||||||
if (priv->automataState != STATE_INITIAL)
|
if (priv->automataState != STATE_INITIAL)
|
||||||
{
|
{
|
||||||
@ -633,17 +621,14 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version,
|
|||||||
|
|
||||||
Stream_SetPosition(priv->outputStream, 0);
|
Stream_SetPosition(priv->outputStream, 0);
|
||||||
|
|
||||||
if (version >= RDPINPUT_PROTOCOL_V300)
|
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4))
|
||||||
pduLen += 4;
|
|
||||||
|
|
||||||
if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
WLog_ERR(TAG, "Stream_EnsureCapacity failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
|
Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY);
|
||||||
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen);
|
Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4);
|
||||||
Stream_Write_UINT32(priv->outputStream, version);
|
Stream_Write_UINT32(priv->outputStream, version);
|
||||||
if (version >= RDPINPUT_PROTOCOL_V300)
|
if (version >= RDPINPUT_PROTOCOL_V300)
|
||||||
Stream_Write_UINT32(priv->outputStream, features);
|
Stream_Write_UINT32(priv->outputStream, features);
|
||||||
|
|||||||
@ -163,12 +163,11 @@ fail:
|
|||||||
static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps)
|
static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps)
|
||||||
{
|
{
|
||||||
const UINT32 filter = gfx->capsFilter;
|
const UINT32 filter = gfx->capsFilter;
|
||||||
const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81,
|
const UINT32 capList[] = {
|
||||||
RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101,
|
RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10,
|
||||||
RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
|
RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
|
||||||
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
|
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106
|
||||||
RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
|
};
|
||||||
RDPGFX_CAPVERSION_107 };
|
|
||||||
UINT32 x;
|
UINT32 x;
|
||||||
|
|
||||||
for (x = 0; x < ARRAYSIZE(capList); x++)
|
for (x = 0; x < ARRAYSIZE(capList); x++)
|
||||||
@ -191,7 +190,7 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
|
|||||||
RdpgfxClientContext* context;
|
RdpgfxClientContext* context;
|
||||||
RDPGFX_CAPSET* capsSet;
|
RDPGFX_CAPSET* capsSet;
|
||||||
RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 };
|
RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 };
|
||||||
RDPGFX_CAPS_ADVERTISE_PDU pdu = { 0 };
|
RDPGFX_CAPS_ADVERTISE_PDU pdu;
|
||||||
|
|
||||||
if (!callback)
|
if (!callback)
|
||||||
return ERROR_BAD_ARGUMENTS;
|
return ERROR_BAD_ARGUMENTS;
|
||||||
@ -324,25 +323,6 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
|
|||||||
capsSet->length = 0x4;
|
capsSet->length = 0x4;
|
||||||
capsSet->flags = caps10Flags;
|
capsSet->flags = caps10Flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR))
|
|
||||||
{
|
|
||||||
capsSet = &capsSets[pdu.capsSetCount++];
|
|
||||||
capsSet->version = RDPGFX_CAPVERSION_106_ERR;
|
|
||||||
capsSet->length = 0x4;
|
|
||||||
capsSet->flags = caps10Flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107))
|
|
||||||
{
|
|
||||||
capsSet = &capsSets[pdu.capsSetCount++];
|
|
||||||
capsSet->version = RDPGFX_CAPVERSION_107;
|
|
||||||
capsSet->length = 0x4;
|
|
||||||
capsSet->flags = caps10Flags;
|
|
||||||
#if !defined(CAIRO_FOUND) && !defined(SWSCALE_FOUND)
|
|
||||||
capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
|
return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
|
||||||
@ -915,8 +895,6 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream
|
|||||||
case RDPGFX_CAPVERSION_104:
|
case RDPGFX_CAPVERSION_104:
|
||||||
case RDPGFX_CAPVERSION_105:
|
case RDPGFX_CAPVERSION_105:
|
||||||
case RDPGFX_CAPVERSION_106:
|
case RDPGFX_CAPVERSION_106:
|
||||||
case RDPGFX_CAPVERSION_106_ERR:
|
|
||||||
case RDPGFX_CAPVERSION_107:
|
|
||||||
if (gfx->SendQoeAck)
|
if (gfx->SendQoeAck)
|
||||||
{
|
{
|
||||||
RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe;
|
RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe;
|
||||||
@ -1021,19 +999,6 @@ static UINT rdpgfx_recv_wire_to_surface_1_pdu(RDPGFX_CHANNEL_CALLBACK* callback,
|
|||||||
cmd.data = pdu.bitmapData;
|
cmd.data = pdu.bitmapData;
|
||||||
cmd.extra = NULL;
|
cmd.extra = NULL;
|
||||||
|
|
||||||
if (cmd.right < cmd.left)
|
|
||||||
{
|
|
||||||
WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu right=%" PRIu32 " < left=%" PRIu32,
|
|
||||||
cmd.right, cmd.left);
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
if (cmd.bottom < cmd.top)
|
|
||||||
{
|
|
||||||
WLog_Print(gfx->log, WLOG_ERROR, "RecvWireToSurface1Pdu bottom=%" PRIu32 " < top=%" PRIu32,
|
|
||||||
cmd.bottom, cmd.top);
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((error = rdpgfx_decode(gfx, &cmd)))
|
if ((error = rdpgfx_decode(gfx, &cmd)))
|
||||||
WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!", error);
|
WLog_Print(gfx->log, WLOG_ERROR, "rdpgfx_decode failed with error %" PRIu32 "!", error);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
* Graphics Pipeline Extension
|
* Graphics Pipeline Extension
|
||||||
*
|
*
|
||||||
@ -24,7 +24,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/assert.h>
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <freerdp/channels/log.h>
|
#include <freerdp/channels/log.h>
|
||||||
|
|
||||||
@ -111,9 +110,6 @@ const char* rdpgfx_get_codec_id_string(UINT16 codecId)
|
|||||||
*/
|
*/
|
||||||
UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
|
UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(s);
|
|
||||||
WINPR_ASSERT(header);
|
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 8)
|
if (Stream_GetRemainingLength(s) < 8)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
@ -123,13 +119,6 @@ UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header)
|
|||||||
Stream_Read_UINT16(s, header->cmdId); /* cmdId (2 bytes) */
|
Stream_Read_UINT16(s, header->cmdId); /* cmdId (2 bytes) */
|
||||||
Stream_Read_UINT16(s, header->flags); /* flags (2 bytes) */
|
Stream_Read_UINT16(s, header->flags); /* flags (2 bytes) */
|
||||||
Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */
|
Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */
|
||||||
|
|
||||||
if ((header->pduLength < 8) || (Stream_GetRemainingLength(s) < (header->pduLength - 8)))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "header->pduLength %u less than 8!", header->pduLength);
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1428,8 +1428,6 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
|
|||||||
PULONG pSessionId = NULL;
|
PULONG pSessionId = NULL;
|
||||||
DWORD BytesReturned = 0;
|
DWORD BytesReturned = 0;
|
||||||
priv->SessionId = WTS_CURRENT_SESSION;
|
priv->SessionId = WTS_CURRENT_SESSION;
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||||
@ -1449,15 +1447,6 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(priv->rdpgfx_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Query for channel event handle */
|
/* Query for channel event handle */
|
||||||
if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer,
|
if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer,
|
||||||
&BytesReturned) ||
|
&BytesReturned) ||
|
||||||
|
|||||||
@ -213,6 +213,10 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMA
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WAVE_FORMAT_ALAW:
|
||||||
|
case WAVE_FORMAT_MULAW:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,99 +139,86 @@ static void rdpsnd_mac_release(rdpsndMacPlugin *mac)
|
|||||||
|
|
||||||
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency)
|
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
AudioDeviceID outputDeviceID;
|
||||||
{
|
UInt32 propertySize;
|
||||||
AudioDeviceID outputDeviceID;
|
OSStatus err;
|
||||||
UInt32 propertySize;
|
NSError *error;
|
||||||
OSStatus err;
|
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
||||||
NSError *error;
|
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultSystemOutputDevice,
|
||||||
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
kAudioObjectPropertyScopeGlobal,
|
||||||
AudioObjectPropertyAddress propertyAddress = {
|
kAudioObjectPropertyElementMaster };
|
||||||
kAudioHardwarePropertyDefaultOutputDevice,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
#if defined(MAC_OS_VERSION_12_0)
|
|
||||||
kAudioObjectPropertyElementMain
|
|
||||||
#else
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mac->isOpen)
|
if (mac->isOpen)
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (!rdpsnd_mac_set_format(device, format, latency))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
propertySize = sizeof(outputDeviceID);
|
|
||||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
|
|
||||||
&propertySize, &outputDeviceID);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mac->engine = [[AVAudioEngine alloc] init];
|
|
||||||
if (!mac->engine)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
|
|
||||||
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
|
|
||||||
0, &outputDeviceID, sizeof(outputDeviceID));
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
rdpsnd_mac_release(mac);
|
|
||||||
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mac->player = [[AVAudioPlayerNode alloc] init];
|
|
||||||
if (!mac->player)
|
|
||||||
{
|
|
||||||
rdpsnd_mac_release(mac);
|
|
||||||
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
[mac->engine attachNode:mac->player];
|
|
||||||
|
|
||||||
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
|
|
||||||
|
|
||||||
[mac->engine prepare];
|
|
||||||
|
|
||||||
if (![mac->engine startAndReturnError:&error])
|
|
||||||
{
|
|
||||||
device->Close(device);
|
|
||||||
WLog_ERR(TAG, "Failed to start audio player %s",
|
|
||||||
[error.localizedDescription UTF8String]);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mac->isOpen = TRUE;
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
if (!rdpsnd_mac_set_format(device, format, latency))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
propertySize = sizeof(outputDeviceID);
|
||||||
|
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
|
||||||
|
&propertySize, &outputDeviceID);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mac->engine = [[AVAudioEngine alloc] init];
|
||||||
|
if (!mac->engine)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
|
||||||
|
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
|
||||||
|
&outputDeviceID, sizeof(outputDeviceID));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
rdpsnd_mac_release(mac);
|
||||||
|
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mac->player = [[AVAudioPlayerNode alloc] init];
|
||||||
|
if (!mac->player)
|
||||||
|
{
|
||||||
|
rdpsnd_mac_release(mac);
|
||||||
|
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
[mac->engine attachNode:mac->player];
|
||||||
|
|
||||||
|
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
|
||||||
|
|
||||||
|
[mac->engine prepare];
|
||||||
|
|
||||||
|
if (![mac->engine startAndReturnError:&error])
|
||||||
|
{
|
||||||
|
device->Close(device);
|
||||||
|
WLog_ERR(TAG, "Failed to start audio player %s", [error.localizedDescription UTF8String]);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mac->isOpen = TRUE;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
|
static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
||||||
|
|
||||||
|
if (mac->isPlaying)
|
||||||
{
|
{
|
||||||
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
[mac->player stop];
|
||||||
|
mac->isPlaying = FALSE;
|
||||||
if (mac->isPlaying)
|
|
||||||
{
|
|
||||||
[mac->player stop];
|
|
||||||
mac->isPlaying = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mac->isOpen)
|
|
||||||
{
|
|
||||||
[mac->engine stop];
|
|
||||||
mac->isOpen = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
rdpsnd_mac_release(mac);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mac->isOpen)
|
||||||
|
{
|
||||||
|
[mac->engine stop];
|
||||||
|
mac->isOpen = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdpsnd_mac_release(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
|
static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
|
||||||
@ -259,118 +246,108 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device, const AUDIO_
|
|||||||
|
|
||||||
static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
|
static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
Float32 fVolume;
|
||||||
{
|
UINT16 volumeLeft;
|
||||||
Float32 fVolume;
|
UINT16 volumeRight;
|
||||||
UINT16 volumeLeft;
|
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
||||||
UINT16 volumeRight;
|
|
||||||
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
|
||||||
|
|
||||||
if (!mac->player)
|
if (!mac->player)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
volumeLeft = (value & 0xFFFF);
|
volumeLeft = (value & 0xFFFF);
|
||||||
volumeRight = ((value >> 16) & 0xFFFF);
|
volumeRight = ((value >> 16) & 0xFFFF);
|
||||||
fVolume = ((float)volumeLeft) / 65535.0f;
|
fVolume = ((float)volumeLeft) / 65535.0f;
|
||||||
|
|
||||||
mac->player.volume = fVolume;
|
mac->player.volume = fVolume;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
|
static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
||||||
|
|
||||||
|
if (!mac->isPlaying)
|
||||||
{
|
{
|
||||||
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
if (!mac->engine.isRunning)
|
||||||
|
|
||||||
if (!mac->isPlaying)
|
|
||||||
{
|
{
|
||||||
if (!mac->engine.isRunning)
|
NSError *error;
|
||||||
|
|
||||||
|
if (![mac->engine startAndReturnError:&error])
|
||||||
{
|
{
|
||||||
NSError *error;
|
device->Close(device);
|
||||||
|
WLog_ERR(TAG, "Failed to start audio player %s",
|
||||||
if (![mac->engine startAndReturnError:&error])
|
[error.localizedDescription UTF8String]);
|
||||||
{
|
return;
|
||||||
device->Close(device);
|
|
||||||
WLog_ERR(TAG, "Failed to start audio player %s",
|
|
||||||
[error.localizedDescription UTF8String]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[mac->player play];
|
|
||||||
|
|
||||||
mac->isPlaying = TRUE;
|
|
||||||
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[mac->player play];
|
||||||
|
|
||||||
|
mac->isPlaying = TRUE;
|
||||||
|
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size)
|
static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size)
|
||||||
{
|
{
|
||||||
@autoreleasepool
|
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
||||||
|
AVAudioPCMBuffer *buffer;
|
||||||
|
AVAudioFormat *format;
|
||||||
|
float *const *db;
|
||||||
|
size_t pos, step, x;
|
||||||
|
AVAudioFrameCount count;
|
||||||
|
UINT64 start = GetTickCount64();
|
||||||
|
|
||||||
|
if (!mac->isOpen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
step = 2 * mac->format.nChannels;
|
||||||
|
|
||||||
|
count = size / step;
|
||||||
|
format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
|
||||||
|
sampleRate:mac->format.nSamplesPerSec
|
||||||
|
channels:mac->format.nChannels
|
||||||
|
interleaved:NO];
|
||||||
|
if (!format)
|
||||||
{
|
{
|
||||||
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
|
WLog_WARN(TAG, "AVAudioFormat::init() failed");
|
||||||
AVAudioPCMBuffer *buffer;
|
return 0;
|
||||||
AVAudioFormat *format;
|
|
||||||
float *const *db;
|
|
||||||
size_t pos, step, x;
|
|
||||||
AVAudioFrameCount count;
|
|
||||||
UINT64 start = GetTickCount64();
|
|
||||||
|
|
||||||
if (!mac->isOpen)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
step = 2 * mac->format.nChannels;
|
|
||||||
|
|
||||||
count = size / step;
|
|
||||||
format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
|
|
||||||
sampleRate:mac->format.nSamplesPerSec
|
|
||||||
channels:mac->format.nChannels
|
|
||||||
interleaved:NO];
|
|
||||||
if (!format)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "AVAudioFormat::init() failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
|
|
||||||
[format release];
|
|
||||||
if (!buffer)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.frameLength = buffer.frameCapacity;
|
|
||||||
db = buffer.floatChannelData;
|
|
||||||
|
|
||||||
for (pos = 0; pos < count; pos++)
|
|
||||||
{
|
|
||||||
const BYTE *d = &data[pos * step];
|
|
||||||
for (x = 0; x < mac->format.nChannels; x++)
|
|
||||||
{
|
|
||||||
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
|
|
||||||
db[x][pos] = val;
|
|
||||||
d += sizeof(int16_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdpsnd_mac_start(device);
|
|
||||||
|
|
||||||
[mac->player scheduleBuffer:buffer
|
|
||||||
completionHandler:^{
|
|
||||||
UINT64 stop = GetTickCount64();
|
|
||||||
if (start > stop)
|
|
||||||
mac->diff = 0;
|
|
||||||
else
|
|
||||||
mac->diff = stop - start;
|
|
||||||
}];
|
|
||||||
[buffer release];
|
|
||||||
|
|
||||||
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
|
|
||||||
}
|
}
|
||||||
|
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
|
||||||
|
if (!buffer)
|
||||||
|
{
|
||||||
|
[format release];
|
||||||
|
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.frameLength = buffer.frameCapacity;
|
||||||
|
db = buffer.floatChannelData;
|
||||||
|
|
||||||
|
for (pos = 0; pos < count; pos++)
|
||||||
|
{
|
||||||
|
const BYTE *d = &data[pos * step];
|
||||||
|
for (x = 0; x < mac->format.nChannels; x++)
|
||||||
|
{
|
||||||
|
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
|
||||||
|
db[x][pos] = val;
|
||||||
|
d += sizeof(int16_t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rdpsnd_mac_start(device);
|
||||||
|
|
||||||
|
[mac->player scheduleBuffer:buffer
|
||||||
|
completionHandler:^{
|
||||||
|
UINT64 stop = GetTickCount64();
|
||||||
|
if (start > stop)
|
||||||
|
mac->diff = 0;
|
||||||
|
else
|
||||||
|
mac->diff = stop - start;
|
||||||
|
}];
|
||||||
|
|
||||||
|
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_CHANNELS
|
#ifdef BUILTIN_CHANNELS
|
||||||
|
|||||||
@ -117,6 +117,10 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WAVE_FORMAT_MULAW:
|
||||||
|
case WAVE_FORMAT_ALAW:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,9 +25,6 @@ include_directories(..)
|
|||||||
|
|
||||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||||
|
|
||||||
if (NOT BUILTIN_CHANNELS)
|
|
||||||
list(APPEND ${MODULE_PREFIX}_LIBS freerdp-client)
|
|
||||||
endif()
|
|
||||||
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
|
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
|
||||||
list(APPEND ${MODULE_PREFIX}_LIBS winpr)
|
list(APPEND ${MODULE_PREFIX}_LIBS winpr)
|
||||||
|
|
||||||
|
|||||||
@ -23,15 +23,11 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/assert.h>
|
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
#include <winpr/cmdline.h>
|
#include <winpr/cmdline.h>
|
||||||
|
|
||||||
@ -55,39 +51,8 @@ struct rdpsnd_pulse_plugin
|
|||||||
pa_stream* stream;
|
pa_stream* stream;
|
||||||
UINT32 latency;
|
UINT32 latency;
|
||||||
UINT32 volume;
|
UINT32 volume;
|
||||||
time_t reconnect_delay_seconds;
|
|
||||||
time_t reconnect_time;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static BOOL rdpsnd_check_pulse(rdpsndPulsePlugin* pulse, BOOL haveStream)
|
|
||||||
{
|
|
||||||
BOOL rc = TRUE;
|
|
||||||
WINPR_ASSERT(pulse);
|
|
||||||
|
|
||||||
if (!pulse->context)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "pulse->context=%p", pulse->context);
|
|
||||||
rc = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveStream)
|
|
||||||
{
|
|
||||||
if (!pulse->stream)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "pulse->stream=%p", pulse->stream);
|
|
||||||
rc = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pulse->mainloop)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "pulse->mainloop=%p", pulse->mainloop);
|
|
||||||
rc = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
|
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
|
||||||
|
|
||||||
static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol,
|
static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol,
|
||||||
@ -100,8 +65,7 @@ static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int
|
|||||||
;
|
;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||||
|
|
||||||
WINPR_ASSERT(c);
|
if (!pulse || !c || !i)
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE) || !i)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (x = 0; x < i->volume.channels; x++)
|
for (x = 0; x < i->volume.channels; x++)
|
||||||
@ -133,10 +97,6 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
|||||||
{
|
{
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(pulse);
|
|
||||||
|
|
||||||
state = pa_context_get_state(context);
|
state = pa_context_get_state(context);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
@ -146,14 +106,6 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PA_CONTEXT_FAILED:
|
case PA_CONTEXT_FAILED:
|
||||||
// Destroy context now, create new one for next connection attempt
|
|
||||||
pa_context_unref(pulse->context);
|
|
||||||
pulse->context = NULL;
|
|
||||||
if (pulse->reconnect_delay_seconds >= 0)
|
|
||||||
pulse->reconnect_time = time(NULL) + pulse->reconnect_delay_seconds;
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PA_CONTEXT_TERMINATED:
|
case PA_CONTEXT_TERMINATED:
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
@ -165,17 +117,21 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
|||||||
|
|
||||||
static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||||
{
|
{
|
||||||
BOOL rc;
|
|
||||||
pa_operation* o;
|
pa_operation* o;
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
if (!pulse->context)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
|
|
||||||
if (pa_context_connect(pulse->context, NULL, 0, NULL) < 0)
|
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||||
{
|
{
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -201,35 +157,27 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
|||||||
if (o)
|
if (o)
|
||||||
pa_operation_unref(o);
|
pa_operation_unref(o);
|
||||||
|
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
|
||||||
if (state == PA_CONTEXT_READY)
|
if (state == PA_CONTEXT_READY)
|
||||||
{
|
{
|
||||||
rc = TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pa_context_disconnect(pulse->context);
|
pa_context_disconnect(pulse->context);
|
||||||
rc = FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
||||||
{
|
{
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
|
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
|
||||||
{
|
{
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!operation)
|
if (!operation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -245,11 +193,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
|||||||
{
|
{
|
||||||
pa_stream_state_t state;
|
pa_stream_state_t state;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||||
|
|
||||||
WINPR_ASSERT(stream);
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
state = pa_stream_get_state(stream);
|
state = pa_stream_get_state(stream);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
@ -260,8 +203,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
|||||||
|
|
||||||
case PA_STREAM_FAILED:
|
case PA_STREAM_FAILED:
|
||||||
case PA_STREAM_TERMINATED:
|
case PA_STREAM_TERMINATED:
|
||||||
// Stream object is about to be destroyed, clean up our pointer
|
|
||||||
pulse->stream = NULL;
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -273,11 +214,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
|||||||
static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||||
{
|
{
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||||
|
|
||||||
WINPR_ASSERT(stream);
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,20 +221,15 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
|
|||||||
{
|
{
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
WINPR_ASSERT(pulse);
|
if (!pulse->context || !pulse->stream)
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
if (pulse->stream)
|
rdpsnd_pulse_wait_for_operation(
|
||||||
{
|
pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
|
||||||
rdpsnd_pulse_wait_for_operation(
|
pa_stream_disconnect(pulse->stream);
|
||||||
pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
|
pa_stream_unref(pulse->stream);
|
||||||
pa_stream_disconnect(pulse->stream);
|
pulse->stream = NULL;
|
||||||
pa_stream_unref(pulse->stream);
|
|
||||||
pulse->stream = NULL;
|
|
||||||
}
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,9 +237,7 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
|
|||||||
{
|
{
|
||||||
pa_sample_spec sample_spec = { 0 };
|
pa_sample_spec sample_spec = { 0 };
|
||||||
|
|
||||||
WINPR_ASSERT(format);
|
if (!pulse->context)
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!rdpsnd_pulse_format_supported(&pulse->device, format))
|
if (!rdpsnd_pulse_format_supported(&pulse->device, format))
|
||||||
@ -352,52 +281,30 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL rdpsnd_pulse_context_connect(rdpsndDevicePlugin* device)
|
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||||
{
|
UINT32 latency)
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
|
||||||
|
|
||||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
|
||||||
|
|
||||||
if (!pulse->context)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
|
|
||||||
|
|
||||||
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
|
|
||||||
{
|
{
|
||||||
pa_stream_state_t state;
|
pa_stream_state_t state;
|
||||||
pa_stream_flags_t flags;
|
pa_stream_flags_t flags;
|
||||||
pa_buffer_attr buffer_attr = { 0 };
|
pa_buffer_attr buffer_attr = { 0 };
|
||||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = { 0 };
|
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
|
if (!pulse->context || pulse->stream)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!rdpsnd_pulse_set_format_spec(pulse, format))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
pulse->latency = latency;
|
||||||
|
|
||||||
if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
|
if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
|
||||||
{
|
{
|
||||||
pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec);
|
pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec);
|
||||||
return FALSE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
if (!pulse->context)
|
|
||||||
{
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
|
||||||
if (pulse->reconnect_delay_seconds >= 0 && time(NULL) - pulse->reconnect_time >= 0)
|
|
||||||
rdpsnd_pulse_context_connect(device);
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
|
||||||
{
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
|
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
|
||||||
|
|
||||||
if (!pulse->stream)
|
if (!pulse->stream)
|
||||||
@ -413,22 +320,19 @@ static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
|
|||||||
|
|
||||||
if (pulse->latency > 0)
|
if (pulse->latency > 0)
|
||||||
{
|
{
|
||||||
buffer_attr.maxlength = UINT32_MAX;
|
buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec);
|
||||||
buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
|
buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
|
||||||
buffer_attr.prebuf = UINT32_MAX;
|
buffer_attr.prebuf = (UINT32)-1;
|
||||||
buffer_attr.minreq = UINT32_MAX;
|
buffer_attr.minreq = (UINT32)-1;
|
||||||
buffer_attr.fragsize = UINT32_MAX;
|
buffer_attr.fragsize = (UINT32)-1;
|
||||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_stream_connect_playback(pulse->stream, pulse->device_name,
|
if (pa_stream_connect_playback(pulse->stream, pulse->device_name,
|
||||||
pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
|
pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "error connecting playback stream");
|
|
||||||
pa_stream_unref(pulse->stream);
|
|
||||||
pulse->stream = NULL;
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
return FALSE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -455,24 +359,6 @@ static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
|
||||||
UINT32 latency)
|
|
||||||
{
|
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
|
||||||
|
|
||||||
WINPR_ASSERT(format);
|
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (!rdpsnd_pulse_set_format_spec(pulse, format))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
pulse->latency = latency;
|
|
||||||
|
|
||||||
return rdpsnd_pulse_open_stream(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
||||||
{
|
{
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
@ -483,7 +369,9 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
|||||||
rdpsnd_pulse_close(device);
|
rdpsnd_pulse_close(device);
|
||||||
|
|
||||||
if (pulse->mainloop)
|
if (pulse->mainloop)
|
||||||
|
{
|
||||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
if (pulse->context)
|
if (pulse->context)
|
||||||
{
|
{
|
||||||
@ -506,7 +394,6 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
|
|||||||
AUDIO_FORMAT* defaultFormat)
|
AUDIO_FORMAT* defaultFormat)
|
||||||
{
|
{
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
if (!pulse || !defaultFormat)
|
if (!pulse || !defaultFormat)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -528,9 +415,6 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
|
|||||||
|
|
||||||
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(device);
|
|
||||||
WINPR_ASSERT(format);
|
|
||||||
|
|
||||||
switch (format->wFormatTag)
|
switch (format->wFormatTag)
|
||||||
{
|
{
|
||||||
case WAVE_FORMAT_PCM:
|
case WAVE_FORMAT_PCM:
|
||||||
@ -543,7 +427,15 @@ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMA
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case WAVE_FORMAT_ALAW:
|
||||||
|
case WAVE_FORMAT_MULAW:
|
||||||
|
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
|
(format->wBitsPerSample == 8) &&
|
||||||
|
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,51 +447,39 @@ static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device)
|
|||||||
pa_operation* o;
|
pa_operation* o;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
if (!pulse)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!pulse->context || !pulse->mainloop)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
|
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
|
||||||
if (o)
|
pa_operation_unref(o);
|
||||||
pa_operation_unref(o);
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
return pulse->volume;
|
return pulse->volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rdpsnd_set_volume_success_cb(pa_context* c, int success, void* userdata)
|
|
||||||
{
|
|
||||||
rdpsndPulsePlugin* pulse = userdata;
|
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
return;
|
|
||||||
WINPR_ASSERT(c);
|
|
||||||
|
|
||||||
WLog_INFO(TAG, "%s: %d", __FUNCTION__, success);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||||
{
|
{
|
||||||
pa_cvolume cv = { 0 };
|
pa_cvolume cv;
|
||||||
pa_volume_t left;
|
pa_volume_t left;
|
||||||
pa_volume_t right;
|
pa_volume_t right;
|
||||||
pa_operation* operation;
|
pa_operation* operation;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
if (!pulse->context || !pulse->stream)
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "%s called before pulse backend was initialized");
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
left = (pa_volume_t)(value & 0xFFFF);
|
left = (pa_volume_t)(value & 0xFFFF);
|
||||||
right = (pa_volume_t)((value >> 16) & 0xFFFF);
|
right = (pa_volume_t)((value >> 16) & 0xFFFF);
|
||||||
pa_cvolume_init(&cv);
|
pa_cvolume_init(&cv);
|
||||||
cv.channels = 2;
|
cv.channels = 2;
|
||||||
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
|
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
|
||||||
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
|
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
|
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
|
||||||
&cv, rdpsnd_set_volume_success_cb, pulse);
|
&cv, NULL, NULL);
|
||||||
|
|
||||||
if (operation)
|
if (operation)
|
||||||
pa_operation_unref(operation);
|
pa_operation_unref(operation);
|
||||||
@ -611,38 +491,28 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
|||||||
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||||
{
|
{
|
||||||
size_t length;
|
size_t length;
|
||||||
void* pa_data;
|
|
||||||
int status;
|
int status;
|
||||||
pa_usec_t latency;
|
pa_usec_t latency;
|
||||||
int negative;
|
int negative;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
|
|
||||||
if (!data)
|
if (!pulse->stream || !data)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
|
|
||||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
|
||||||
{
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
|
||||||
// Discard this playback request and just attempt to reconnect the stream
|
|
||||||
WLog_DBG(TAG, "reconnecting playback stream");
|
|
||||||
rdpsnd_pulse_open_stream(device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (size > 0)
|
while (size > 0)
|
||||||
{
|
{
|
||||||
length = size;
|
while ((length = pa_stream_writable_size(pulse->stream)) == 0)
|
||||||
|
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||||
|
|
||||||
status = pa_stream_begin_write(pulse->stream, &pa_data, &length);
|
if (length == (size_t)-1)
|
||||||
|
|
||||||
if (status < 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
memcpy(pa_data, data, length);
|
if (length > size)
|
||||||
|
length = size;
|
||||||
|
|
||||||
status = pa_stream_write(pulse->stream, pa_data, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||||
|
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
{
|
{
|
||||||
@ -660,24 +530,22 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size
|
|||||||
return latency / 1000;
|
return latency / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function description
|
||||||
|
*
|
||||||
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
|
*/
|
||||||
static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
|
static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
COMMAND_LINE_ARGUMENT_A* arg;
|
COMMAND_LINE_ARGUMENT_A* arg;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||||
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = {
|
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED,
|
||||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
"<device>", NULL, NULL, -1, NULL, "device" },
|
||||||
{ "reconnect_delay_seconds", COMMAND_LINE_VALUE_REQUIRED, "<reconnect_delay_seconds>", NULL,
|
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||||
NULL, -1, NULL, "reconnect_delay_seconds" },
|
|
||||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
|
||||||
};
|
|
||||||
flags =
|
flags =
|
||||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||||
|
|
||||||
WINPR_ASSERT(pulse);
|
|
||||||
WINPR_ASSERT(args);
|
|
||||||
|
|
||||||
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse,
|
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
@ -698,15 +566,6 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
|
|||||||
if (!pulse->device_name)
|
if (!pulse->device_name)
|
||||||
return ERROR_OUTOFMEMORY;
|
return ERROR_OUTOFMEMORY;
|
||||||
}
|
}
|
||||||
CommandLineSwitchCase(arg, "reconnect_delay_seconds")
|
|
||||||
{
|
|
||||||
unsigned long val = strtoul(arg->Value, NULL, 0);
|
|
||||||
|
|
||||||
if ((errno != 0) || (val > INT32_MAX))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
pulse->reconnect_delay_seconds = val;
|
|
||||||
}
|
|
||||||
CommandLineSwitchEnd(arg)
|
CommandLineSwitchEnd(arg)
|
||||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||||
|
|
||||||
@ -719,14 +578,16 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
|
|||||||
#define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
|
#define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function description
|
||||||
|
*
|
||||||
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
|
*/
|
||||||
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
{
|
{
|
||||||
ADDIN_ARGV* args;
|
ADDIN_ARGV* args;
|
||||||
rdpsndPulsePlugin* pulse;
|
rdpsndPulsePlugin* pulse;
|
||||||
UINT ret;
|
UINT ret;
|
||||||
|
|
||||||
WINPR_ASSERT(pEntryPoints);
|
|
||||||
|
|
||||||
pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin));
|
pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin));
|
||||||
|
|
||||||
if (!pulse)
|
if (!pulse)
|
||||||
@ -752,8 +613,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pulse->reconnect_delay_seconds = 5;
|
|
||||||
pulse->reconnect_time = time(NULL);
|
|
||||||
|
|
||||||
ret = CHANNEL_RC_NO_MEMORY;
|
ret = CHANNEL_RC_NO_MEMORY;
|
||||||
pulse->mainloop = pa_threaded_mainloop_new();
|
pulse->mainloop = pa_threaded_mainloop_new();
|
||||||
@ -761,17 +620,15 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
|||||||
if (!pulse->mainloop)
|
if (!pulse->mainloop)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||||
|
|
||||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
if (!pulse->context)
|
||||||
{
|
goto error;
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
|
||||||
|
ret = ERROR_INVALID_OPERATION;
|
||||||
|
|
||||||
if (!rdpsnd_pulse_context_connect((rdpsndDevicePlugin*)pulse))
|
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse);
|
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse);
|
||||||
|
|||||||
@ -126,10 +126,6 @@ struct rdpsnd_plugin
|
|||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
wMessageQueue* queue;
|
wMessageQueue* queue;
|
||||||
BOOL initialized;
|
BOOL initialized;
|
||||||
|
|
||||||
UINT16 wVersion;
|
|
||||||
UINT32 volume;
|
|
||||||
BOOL applyVolume;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* rdpsnd_is_dyn_str(BOOL dynamic)
|
static const char* rdpsnd_is_dyn_str(BOOL dynamic)
|
||||||
@ -186,7 +182,7 @@ static void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd)
|
|||||||
|
|
||||||
rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats);
|
rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats);
|
||||||
|
|
||||||
if (!rdpsnd->ClientFormats || !rdpsnd->device)
|
if (!rdpsnd->ClientFormats)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (index = 0; index < rdpsnd->NumberOfServerFormats; index++)
|
for (index = 0; index < rdpsnd->NumberOfServerFormats; index++)
|
||||||
@ -217,10 +213,6 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
|
|||||||
UINT16 length;
|
UINT16 length;
|
||||||
UINT32 dwVolume;
|
UINT32 dwVolume;
|
||||||
UINT16 wNumberOfFormats;
|
UINT16 wNumberOfFormats;
|
||||||
|
|
||||||
if (!rdpsnd->device || (!rdpsnd->dynamic && (rdpsnd->OpenHandle == 0)))
|
|
||||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
|
||||||
|
|
||||||
dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device);
|
dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device);
|
||||||
wNumberOfFormats = rdpsnd->NumberOfClientFormats;
|
wNumberOfFormats = rdpsnd->NumberOfClientFormats;
|
||||||
length = 4 + 20;
|
length = 4 + 20;
|
||||||
@ -272,9 +264,9 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
|
|||||||
static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||||
{
|
{
|
||||||
UINT16 index;
|
UINT16 index;
|
||||||
|
UINT16 wVersion;
|
||||||
UINT16 wNumberOfFormats;
|
UINT16 wNumberOfFormats;
|
||||||
UINT ret = ERROR_BAD_LENGTH;
|
UINT ret = ERROR_BAD_LENGTH;
|
||||||
|
|
||||||
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||||
rdpsnd->NumberOfServerFormats = 0;
|
rdpsnd->NumberOfServerFormats = 0;
|
||||||
rdpsnd->ServerFormats = NULL;
|
rdpsnd->ServerFormats = NULL;
|
||||||
@ -289,7 +281,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
|
|||||||
Stream_Seek_UINT16(s); /* wDGramPort */
|
Stream_Seek_UINT16(s); /* wDGramPort */
|
||||||
Stream_Read_UINT16(s, wNumberOfFormats);
|
Stream_Read_UINT16(s, wNumberOfFormats);
|
||||||
Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
|
Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
|
||||||
Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */
|
Stream_Read_UINT16(s, wVersion); /* wVersion */
|
||||||
Stream_Seek_UINT8(s); /* bPad */
|
Stream_Seek_UINT8(s); /* bPad */
|
||||||
rdpsnd->NumberOfServerFormats = wNumberOfFormats;
|
rdpsnd->NumberOfServerFormats = wNumberOfFormats;
|
||||||
|
|
||||||
@ -316,7 +308,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
|
|||||||
|
|
||||||
if (ret == CHANNEL_RC_OK)
|
if (ret == CHANNEL_RC_OK)
|
||||||
{
|
{
|
||||||
if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7)
|
if (wVersion >= CHANNEL_VERSION_WIN_7)
|
||||||
ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
|
ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,20 +369,6 @@ static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
|||||||
return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
|
return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(rdpsnd);
|
|
||||||
|
|
||||||
if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device)
|
|
||||||
{
|
|
||||||
BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume);
|
|
||||||
if (!rc)
|
|
||||||
return FALSE;
|
|
||||||
rdpsnd->applyVolume = FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
|
static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
|
||||||
const AUDIO_FORMAT* format)
|
const AUDIO_FORMAT* format)
|
||||||
{
|
{
|
||||||
@ -439,7 +417,7 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
|
|||||||
rdpsnd->totalPlaySize = 0;
|
rdpsnd->totalPlaySize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rdpsnd_apply_volume(rdpsnd);
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -600,7 +578,6 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
|
|||||||
UINT64 end;
|
UINT64 end;
|
||||||
UINT64 diffMS, ts;
|
UINT64 diffMS, ts;
|
||||||
UINT latency = 0;
|
UINT latency = 0;
|
||||||
UINT error;
|
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < size)
|
if (Stream_GetRemainingLength(s) < size)
|
||||||
return ERROR_BAD_LENGTH;
|
return ERROR_BAD_LENGTH;
|
||||||
@ -608,15 +585,6 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
|
|||||||
if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats)
|
if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats)
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
/*
|
|
||||||
* Send the first WaveConfirm PDU. The server side uses this to determine the
|
|
||||||
* network latency.
|
|
||||||
* See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU
|
|
||||||
*/
|
|
||||||
error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
data = Stream_Pointer(s);
|
data = Stream_Pointer(s);
|
||||||
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
|
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
|
||||||
WLog_Print(rdpsnd->log, WLOG_DEBUG,
|
WLog_Print(rdpsnd->log, WLOG_DEBUG,
|
||||||
@ -649,11 +617,10 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
|
|||||||
diffMS = end - rdpsnd->wArrivalTime + latency;
|
diffMS = end - rdpsnd->wArrivalTime + latency;
|
||||||
ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX;
|
ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX;
|
||||||
|
|
||||||
/*
|
/* Don't send wave confirm PDU if on dynamic channel */
|
||||||
* Send the second WaveConfirm PDU. With the first WaveConfirm PDU,
|
if (rdpsnd->dynamic)
|
||||||
* the server side uses this second WaveConfirm PDU to determine the actual
|
return CHANNEL_RC_OK;
|
||||||
* render latency.
|
|
||||||
*/
|
|
||||||
return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo);
|
return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +695,7 @@ static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
|
|||||||
*/
|
*/
|
||||||
static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||||
{
|
{
|
||||||
BOOL rc = TRUE;
|
BOOL rc;
|
||||||
UINT32 dwVolume;
|
UINT32 dwVolume;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
@ -737,10 +704,7 @@ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
|||||||
Stream_Read_UINT32(s, dwVolume);
|
Stream_Read_UINT32(s, dwVolume);
|
||||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
|
WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
|
||||||
rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
|
rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
|
||||||
|
rc = IFCALLRESULT(FALSE, rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
|
||||||
rdpsnd->volume = dwVolume;
|
|
||||||
rdpsnd->applyVolume = TRUE;
|
|
||||||
rc = rdpsnd_apply_volume(rdpsnd);
|
|
||||||
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
@ -1218,51 +1182,38 @@ static UINT rdpsnd_virtual_channel_event_connected(rdpsndPlugin* rdpsnd, LPVOID
|
|||||||
UINT32 dataLength)
|
UINT32 dataLength)
|
||||||
{
|
{
|
||||||
UINT32 status;
|
UINT32 status;
|
||||||
DWORD opened = 0;
|
|
||||||
WINPR_UNUSED(pData);
|
WINPR_UNUSED(pData);
|
||||||
WINPR_UNUSED(dataLength);
|
WINPR_UNUSED(dataLength);
|
||||||
|
|
||||||
status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx(
|
status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx(
|
||||||
rdpsnd->InitHandle, &opened, rdpsnd->channelDef.name, rdpsnd_virtual_channel_open_event_ex);
|
rdpsnd->InitHandle, &rdpsnd->OpenHandle, rdpsnd->channelDef.name,
|
||||||
|
rdpsnd_virtual_channel_open_event_ex);
|
||||||
|
|
||||||
if (status != CHANNEL_RC_OK)
|
if (status != CHANNEL_RC_OK)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
|
WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
|
||||||
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status);
|
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status);
|
||||||
goto fail;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rdpsnd_process_connect(rdpsnd) != CHANNEL_RC_OK)
|
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE);
|
||||||
|
|
||||||
|
if (!rdpsnd->dsp_context)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
rdpsnd->OpenHandle = opened;
|
rdpsnd->pool = StreamPool_New(TRUE, 4096);
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
|
if (!rdpsnd->pool)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return rdpsnd_process_connect(rdpsnd);
|
||||||
fail:
|
fail:
|
||||||
if (opened != 0)
|
freerdp_dsp_context_free(rdpsnd->dsp_context);
|
||||||
rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
|
StreamPool_Free(rdpsnd->pool);
|
||||||
|
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_internals(rdpsndPlugin* rdpsnd)
|
|
||||||
{
|
|
||||||
if (!rdpsnd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rdpsnd->pool)
|
|
||||||
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
|
|
||||||
|
|
||||||
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
|
|
||||||
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
|
||||||
|
|
||||||
rdpsnd->NumberOfClientFormats = 0;
|
|
||||||
rdpsnd->ClientFormats = NULL;
|
|
||||||
rdpsnd->NumberOfServerFormats = 0;
|
|
||||||
rdpsnd->ServerFormats = NULL;
|
|
||||||
|
|
||||||
rdpsnd->data_in = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -1272,25 +1223,33 @@ static UINT rdpsnd_virtual_channel_event_disconnected(rdpsndPlugin* rdpsnd)
|
|||||||
{
|
{
|
||||||
UINT error;
|
UINT error;
|
||||||
|
|
||||||
if (rdpsnd->OpenHandle != 0)
|
if (rdpsnd->OpenHandle == 0)
|
||||||
|
return CHANNEL_RC_OK;
|
||||||
|
|
||||||
|
if (rdpsnd->device)
|
||||||
|
IFCALL(rdpsnd->device->Close, rdpsnd->device);
|
||||||
|
|
||||||
|
error =
|
||||||
|
rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, rdpsnd->OpenHandle);
|
||||||
|
|
||||||
|
if (CHANNEL_RC_OK != error)
|
||||||
{
|
{
|
||||||
DWORD opened = rdpsnd->OpenHandle;
|
WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
|
||||||
rdpsnd->OpenHandle = 0;
|
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error);
|
||||||
|
return error;
|
||||||
if (rdpsnd->device)
|
|
||||||
IFCALL(rdpsnd->device->Close, rdpsnd->device);
|
|
||||||
|
|
||||||
error = rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
|
|
||||||
|
|
||||||
if (CHANNEL_RC_OK != error)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
|
|
||||||
rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup_internals(rdpsnd);
|
rdpsnd->OpenHandle = 0;
|
||||||
|
freerdp_dsp_context_free(rdpsnd->dsp_context);
|
||||||
|
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
|
||||||
|
StreamPool_Free(rdpsnd->pool);
|
||||||
|
|
||||||
|
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
|
||||||
|
rdpsnd->NumberOfClientFormats = 0;
|
||||||
|
rdpsnd->ClientFormats = NULL;
|
||||||
|
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||||
|
rdpsnd->NumberOfServerFormats = 0;
|
||||||
|
rdpsnd->ServerFormats = NULL;
|
||||||
|
|
||||||
if (rdpsnd->device)
|
if (rdpsnd->device)
|
||||||
{
|
{
|
||||||
@ -1307,36 +1266,6 @@ static void _queue_free(void* obj)
|
|||||||
Stream_Release(s);
|
Stream_Release(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_internals(rdpsndPlugin* rdpsnd)
|
|
||||||
{
|
|
||||||
if (!rdpsnd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
freerdp_dsp_context_free(rdpsnd->dsp_context);
|
|
||||||
StreamPool_Free(rdpsnd->pool);
|
|
||||||
rdpsnd->pool = NULL;
|
|
||||||
rdpsnd->dsp_context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL allocate_internals(rdpsndPlugin* rdpsnd)
|
|
||||||
{
|
|
||||||
if (!rdpsnd->pool)
|
|
||||||
{
|
|
||||||
rdpsnd->pool = StreamPool_New(TRUE, 4096);
|
|
||||||
if (!rdpsnd->pool)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rdpsnd->dsp_context)
|
|
||||||
{
|
|
||||||
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE);
|
|
||||||
if (!rdpsnd->dsp_context)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI play_thread(LPVOID arg)
|
static DWORD WINAPI play_thread(LPVOID arg)
|
||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
@ -1382,22 +1311,17 @@ static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd)
|
|||||||
if (!rdpsnd->queue)
|
if (!rdpsnd->queue)
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
|
|
||||||
if (!allocate_internals(rdpsnd))
|
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
|
||||||
|
|
||||||
rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
|
rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
|
||||||
if (!rdpsnd->thread)
|
if (!rdpsnd->thread)
|
||||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
||||||
{
|
{
|
||||||
if (rdpsnd)
|
if (rdpsnd)
|
||||||
{
|
{
|
||||||
if (rdpsnd->queue)
|
MessageQueue_PostQuit(rdpsnd->queue, 0);
|
||||||
MessageQueue_PostQuit(rdpsnd->queue, 0);
|
|
||||||
if (rdpsnd->thread)
|
if (rdpsnd->thread)
|
||||||
{
|
{
|
||||||
WaitForSingleObject(rdpsnd->thread, INFINITE);
|
WaitForSingleObject(rdpsnd->thread, INFINITE);
|
||||||
@ -1405,7 +1329,6 @@ void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
|||||||
}
|
}
|
||||||
MessageQueue_Free(rdpsnd->queue);
|
MessageQueue_Free(rdpsnd->queue);
|
||||||
|
|
||||||
free_internals(rdpsnd);
|
|
||||||
audio_formats_free(rdpsnd->fixed_format, 1);
|
audio_formats_free(rdpsnd->fixed_format, 1);
|
||||||
free(rdpsnd->subsystem);
|
free(rdpsnd->subsystem);
|
||||||
free(rdpsnd->device_name);
|
free(rdpsnd->device_name);
|
||||||
@ -1447,7 +1370,6 @@ static VOID VCAPITYPE rdpsnd_virtual_channel_init_event_ex(LPVOID lpUserParam, L
|
|||||||
|
|
||||||
case CHANNEL_EVENT_TERMINATED:
|
case CHANNEL_EVENT_TERMINATED:
|
||||||
rdpsnd_virtual_channel_event_terminated(plugin);
|
rdpsnd_virtual_channel_event_terminated(plugin);
|
||||||
plugin = NULL;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CHANNEL_EVENT_ATTACHED:
|
case CHANNEL_EVENT_ATTACHED:
|
||||||
@ -1479,27 +1401,6 @@ rdpContext* freerdp_rdpsnd_get_context(rdpsndPlugin* plugin)
|
|||||||
return plugin->rdpcontext;
|
return plugin->rdpcontext;
|
||||||
}
|
}
|
||||||
|
|
||||||
static rdpsndPlugin* allocatePlugin(void)
|
|
||||||
{
|
|
||||||
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin));
|
|
||||||
if (!rdpsnd)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
rdpsnd->fixed_format = audio_format_new();
|
|
||||||
if (!rdpsnd->fixed_format)
|
|
||||||
goto fail;
|
|
||||||
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
|
|
||||||
if (!rdpsnd->log)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
rdpsnd->attached = TRUE;
|
|
||||||
return rdpsnd;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
if (rdpsnd)
|
|
||||||
audio_format_free(rdpsnd->fixed_format);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* rdpsnd is always built-in */
|
/* rdpsnd is always built-in */
|
||||||
BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
|
BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
|
||||||
{
|
{
|
||||||
@ -1510,11 +1411,12 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
|
|||||||
if (!pEntryPoints)
|
if (!pEntryPoints)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
rdpsnd = allocatePlugin();
|
rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin));
|
||||||
|
|
||||||
if (!rdpsnd)
|
if (!rdpsnd)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
rdpsnd->attached = TRUE;
|
||||||
rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP;
|
rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP;
|
||||||
sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name), "rdpsnd");
|
sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name), "rdpsnd");
|
||||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||||
@ -1525,6 +1427,15 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
|
|||||||
rdpsnd->rdpcontext = pEntryPointsEx->context;
|
rdpsnd->rdpcontext = pEntryPointsEx->context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdpsnd->fixed_format = audio_format_new();
|
||||||
|
|
||||||
|
if (!rdpsnd->fixed_format)
|
||||||
|
{
|
||||||
|
free(rdpsnd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
|
||||||
CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints,
|
CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints,
|
||||||
sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
|
||||||
rdpsnd->InitHandle = pInitHandle;
|
rdpsnd->InitHandle = pInitHandle;
|
||||||
@ -1536,7 +1447,7 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
|
|||||||
{
|
{
|
||||||
WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
|
WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
|
||||||
rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc);
|
rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc);
|
||||||
rdpsnd_virtual_channel_event_terminated(rdpsnd);
|
free(rdpsnd);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1548,10 +1459,19 @@ static UINT rdpsnd_on_open(IWTSVirtualChannelCallback* pChannelCallback)
|
|||||||
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
|
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
|
||||||
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin;
|
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin;
|
||||||
|
|
||||||
if (!allocate_internals(rdpsnd))
|
rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE);
|
||||||
return ERROR_OUTOFMEMORY;
|
if (!rdpsnd->dsp_context)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
rdpsnd->pool = StreamPool_New(TRUE, 4096);
|
||||||
|
if (!rdpsnd->pool)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
return rdpsnd_process_connect(rdpsnd);
|
return rdpsnd_process_connect(rdpsnd);
|
||||||
|
fail:
|
||||||
|
freerdp_dsp_context_free(rdpsnd->dsp_context);
|
||||||
|
StreamPool_Free(rdpsnd->pool);
|
||||||
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||||
@ -1559,9 +1479,7 @@ static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback
|
|||||||
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
|
RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback;
|
||||||
rdpsndPlugin* plugin;
|
rdpsndPlugin* plugin;
|
||||||
wStream* copy;
|
wStream* copy;
|
||||||
size_t len;
|
size_t len = Stream_GetRemainingLength(data);
|
||||||
|
|
||||||
len = Stream_GetRemainingLength(data);
|
|
||||||
|
|
||||||
if (!callback || !callback->plugin)
|
if (!callback || !callback->plugin)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -1590,16 +1508,22 @@ static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|||||||
|
|
||||||
if (rdpsnd->device)
|
if (rdpsnd->device)
|
||||||
IFCALL(rdpsnd->device->Close, rdpsnd->device);
|
IFCALL(rdpsnd->device->Close, rdpsnd->device);
|
||||||
|
freerdp_dsp_context_free(rdpsnd->dsp_context);
|
||||||
|
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
|
||||||
|
StreamPool_Free(rdpsnd->pool);
|
||||||
|
|
||||||
cleanup_internals(rdpsnd);
|
audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
|
||||||
|
rdpsnd->NumberOfClientFormats = 0;
|
||||||
|
rdpsnd->ClientFormats = NULL;
|
||||||
|
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||||
|
rdpsnd->NumberOfServerFormats = 0;
|
||||||
|
rdpsnd->ServerFormats = NULL;
|
||||||
if (rdpsnd->device)
|
if (rdpsnd->device)
|
||||||
{
|
{
|
||||||
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
||||||
rdpsnd->device = NULL;
|
rdpsnd->device = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_internals(rdpsnd);
|
|
||||||
free(pChannelCallback);
|
free(pChannelCallback);
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
@ -1698,7 +1622,7 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
|
|
||||||
if (!rdpsnd)
|
if (!rdpsnd)
|
||||||
{
|
{
|
||||||
rdpsnd = allocatePlugin();
|
rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin));
|
||||||
if (!rdpsnd)
|
if (!rdpsnd)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
|
WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
|
||||||
@ -1709,10 +1633,14 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
rdpsnd->iface.Connected = NULL;
|
rdpsnd->iface.Connected = NULL;
|
||||||
rdpsnd->iface.Disconnected = NULL;
|
rdpsnd->iface.Disconnected = NULL;
|
||||||
rdpsnd->iface.Terminated = rdpsnd_plugin_terminated;
|
rdpsnd->iface.Terminated = rdpsnd_plugin_terminated;
|
||||||
|
rdpsnd->attached = TRUE;
|
||||||
rdpsnd->dynamic = TRUE;
|
rdpsnd->dynamic = TRUE;
|
||||||
|
rdpsnd->fixed_format = audio_format_new();
|
||||||
|
if (!rdpsnd->fixed_format)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
/* user data pointer is not const, cast to avoid warning. */
|
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
|
||||||
rdpsnd->channelEntryPoints.pExtendedData = (void*)pEntryPoints->GetPluginData(pEntryPoints);
|
rdpsnd->channelEntryPoints.pExtendedData = pEntryPoints->GetPluginData(pEntryPoints);
|
||||||
|
|
||||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpsnd", &rdpsnd->iface);
|
error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpsnd", &rdpsnd->iface);
|
||||||
}
|
}
|
||||||
@ -1722,5 +1650,8 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|||||||
return CHANNEL_RC_BAD_CHANNEL;
|
return CHANNEL_RC_BAD_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
fail:
|
||||||
|
rdpsnd_plugin_terminated(&rdpsnd->iface);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
#include <winpr/assert.h>
|
|
||||||
#include <winpr/print.h>
|
#include <winpr/print.h>
|
||||||
#include <winpr/stream.h>
|
#include <winpr/stream.h>
|
||||||
|
|
||||||
@ -37,33 +36,17 @@
|
|||||||
#include "rdpsnd_common.h"
|
#include "rdpsnd_common.h"
|
||||||
#include "rdpsnd_main.h"
|
#include "rdpsnd_main.h"
|
||||||
|
|
||||||
static wStream* rdpsnd_server_get_buffer(RdpsndServerContext* context)
|
|
||||||
{
|
|
||||||
wStream* s;
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
s = context->priv->rdpsnd_pdu;
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Server Audio Formats and Version PDU (2.2.2.1)
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
|
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||||
{
|
{
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
|
||||||
size_t pos;
|
size_t pos;
|
||||||
UINT16 i;
|
UINT16 i;
|
||||||
BOOL status = FALSE;
|
BOOL status = FALSE;
|
||||||
ULONG written;
|
ULONG written;
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 24))
|
|
||||||
return ERROR_OUTOFMEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, SNDC_FORMATS);
|
Stream_Write_UINT8(s, SNDC_FORMATS);
|
||||||
Stream_Write_UINT8(s, 0);
|
Stream_Write_UINT8(s, 0);
|
||||||
Stream_Seek_UINT16(s);
|
Stream_Seek_UINT16(s);
|
||||||
@ -78,9 +61,12 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
|
|||||||
|
|
||||||
for (i = 0; i < context->num_server_formats; i++)
|
for (i = 0; i < context->num_server_formats; i++)
|
||||||
{
|
{
|
||||||
const AUDIO_FORMAT* format = &context->server_formats[i];
|
AUDIO_FORMAT format = context->server_formats[i];
|
||||||
|
// TODO: Eliminate this!!!
|
||||||
|
format.nAvgBytesPerSec =
|
||||||
|
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||||
|
|
||||||
if (!audio_format_write(s, format))
|
if (!audio_format_write(s, &format))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +74,6 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
|
|||||||
Stream_SetPosition(s, 2);
|
Stream_SetPosition(s, 2);
|
||||||
Stream_Write_UINT16(s, pos - 4);
|
Stream_Write_UINT16(s, pos - 4);
|
||||||
Stream_SetPosition(s, pos);
|
Stream_SetPosition(s, pos);
|
||||||
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
||||||
Stream_GetPosition(s), &written);
|
Stream_GetPosition(s), &written);
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
@ -98,7 +82,7 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read Wave Confirm PDU (2.2.3.8) and handle callback
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
@ -108,10 +92,11 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
|
|||||||
BYTE confirmBlockNum;
|
BYTE confirmBlockNum;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
|
{
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
WLog_ERR(TAG, "not enough data in stream!");
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
Stream_Read_UINT16(s, timestamp);
|
Stream_Read_UINT16(s, timestamp);
|
||||||
Stream_Read_UINT8(s, confirmBlockNum);
|
Stream_Read_UINT8(s, confirmBlockNum);
|
||||||
@ -125,39 +110,13 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read Training Confirm PDU (2.2.3.2) and handle callback
|
* Function description
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s)
|
|
||||||
{
|
|
||||||
UINT16 timestamp;
|
|
||||||
UINT16 packsize;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT16(s, timestamp);
|
|
||||||
Stream_Read_UINT16(s, packsize);
|
|
||||||
|
|
||||||
IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read Quality Mode PDU (2.2.2.3)
|
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s)
|
static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(context);
|
UINT16 quality;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 4)
|
if (Stream_GetRemainingLength(s) < 4)
|
||||||
{
|
{
|
||||||
@ -165,34 +124,34 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStrea
|
|||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */
|
Stream_Read_UINT16(s, quality);
|
||||||
Stream_Seek_UINT16(s); /* Reserved */
|
Stream_Seek_UINT16(s); // reserved
|
||||||
|
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality);
|
||||||
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read Client Audio Formats and Version PDU (2.2.2.2)
|
* Function description
|
||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
||||||
{
|
{
|
||||||
UINT16 i, num_known_format = 0;
|
UINT16 i, num_known_format = 0;
|
||||||
|
UINT32 flags, vol, pitch;
|
||||||
UINT16 udpPort;
|
UINT16 udpPort;
|
||||||
BYTE lastblock;
|
BYTE lastblock;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
if (Stream_GetRemainingLength(s) < 20)
|
||||||
|
{
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
|
WLog_ERR(TAG, "not enough data in stream!");
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */
|
Stream_Read_UINT32(s, flags); /* dwFlags */
|
||||||
Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */
|
Stream_Read_UINT32(s, vol); /* dwVolume */
|
||||||
Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */
|
Stream_Read_UINT32(s, pitch); /* dwPitch */
|
||||||
Stream_Read_UINT16(s, udpPort); /* wDGramPort */
|
Stream_Read_UINT16(s, udpPort); /* wDGramPort */
|
||||||
Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */
|
Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */
|
||||||
Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */
|
Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */
|
||||||
@ -200,8 +159,11 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
|||||||
Stream_Seek_UINT8(s); /* bPad */
|
Stream_Seek_UINT8(s); /* bPad */
|
||||||
|
|
||||||
/* this check is only a guess as cbSize can influence the size of a format record */
|
/* this check is only a guess as cbSize can influence the size of a format record */
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18ull * context->num_client_formats))
|
if (Stream_GetRemainingLength(s) < context->num_client_formats * 18)
|
||||||
|
{
|
||||||
|
WLog_ERR(TAG, "not enough data in stream!");
|
||||||
return ERROR_INVALID_DATA;
|
return ERROR_INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
if (!context->num_client_formats)
|
if (!context->num_client_formats)
|
||||||
{
|
{
|
||||||
@ -219,26 +181,24 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
|||||||
|
|
||||||
for (i = 0; i < context->num_client_formats; i++)
|
for (i = 0; i < context->num_client_formats; i++)
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT* format = &context->client_formats[i];
|
if (Stream_GetRemainingLength(s) < 18)
|
||||||
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "not enough data in stream!");
|
WLog_ERR(TAG, "not enough data in stream!");
|
||||||
error = ERROR_INVALID_DATA;
|
error = ERROR_INVALID_DATA;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Read_UINT16(s, format->wFormatTag);
|
Stream_Read_UINT16(s, context->client_formats[i].wFormatTag);
|
||||||
Stream_Read_UINT16(s, format->nChannels);
|
Stream_Read_UINT16(s, context->client_formats[i].nChannels);
|
||||||
Stream_Read_UINT32(s, format->nSamplesPerSec);
|
Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec);
|
||||||
Stream_Read_UINT32(s, format->nAvgBytesPerSec);
|
Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec);
|
||||||
Stream_Read_UINT16(s, format->nBlockAlign);
|
Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign);
|
||||||
Stream_Read_UINT16(s, format->wBitsPerSample);
|
Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample);
|
||||||
Stream_Read_UINT16(s, format->cbSize);
|
Stream_Read_UINT16(s, context->client_formats[i].cbSize);
|
||||||
|
|
||||||
if (format->cbSize > 0)
|
if (context->client_formats[i].cbSize > 0)
|
||||||
{
|
{
|
||||||
if (!Stream_SafeSeek(s, format->cbSize))
|
if (!Stream_SafeSeek(s, context->client_formats[i].cbSize))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_SafeSeek failed!");
|
WLog_ERR(TAG, "Stream_SafeSeek failed!");
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
@ -246,7 +206,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format->wFormatTag != 0)
|
if (context->client_formats[i].wFormatTag != 0)
|
||||||
{
|
{
|
||||||
// lets call this a known format
|
// lets call this a known format
|
||||||
// TODO: actually look through our own list of known formats
|
// TODO: actually look through our own list of known formats
|
||||||
@ -268,19 +228,15 @@ out_free:
|
|||||||
|
|
||||||
static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
|
static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
|
||||||
{
|
{
|
||||||
DWORD nCount = 0, status;
|
DWORD nCount, status;
|
||||||
HANDLE events[2] = { 0 };
|
HANDLE events[8];
|
||||||
RdpsndServerContext* context = (RdpsndServerContext*)arg;
|
RdpsndServerContext* context;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
context = (RdpsndServerContext*)arg;
|
||||||
WINPR_ASSERT(context);
|
nCount = 0;
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
events[nCount++] = context->priv->channelEvent;
|
events[nCount++] = context->priv->channelEvent;
|
||||||
events[nCount++] = context->priv->StopEvent;
|
events[nCount++] = context->priv->StopEvent;
|
||||||
|
|
||||||
WINPR_ASSERT(nCount <= ARRAYSIZE(events));
|
|
||||||
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||||
@ -325,9 +281,6 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
|
|||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread)
|
static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
context->priv->ownThread = ownThread;
|
context->priv->ownThread = ownThread;
|
||||||
return context->Start(context);
|
return context->Start(context);
|
||||||
}
|
}
|
||||||
@ -344,9 +297,6 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli
|
|||||||
AUDIO_FORMAT* format;
|
AUDIO_FORMAT* format;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
if ((client_format_index >= context->num_client_formats) || (!context->src_format))
|
if ((client_format_index >= context->num_client_formats) || (!context->src_format))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "index %d is not correct.", client_format_index);
|
WLog_ERR(TAG, "index %d is not correct.", client_format_index);
|
||||||
@ -421,51 +371,6 @@ out:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send Training PDU (2.2.3.1)
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize,
|
|
||||||
BYTE* data)
|
|
||||||
{
|
|
||||||
size_t end = 0;
|
|
||||||
ULONG written;
|
|
||||||
BOOL status;
|
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, SNDC_TRAINING);
|
|
||||||
Stream_Write_UINT8(s, 0);
|
|
||||||
Stream_Seek_UINT16(s);
|
|
||||||
Stream_Write_UINT16(s, timestamp);
|
|
||||||
Stream_Write_UINT16(s, packsize);
|
|
||||||
|
|
||||||
if (packsize > 0)
|
|
||||||
{
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, packsize))
|
|
||||||
{
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Write(s, data, packsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
end = Stream_GetPosition(s);
|
|
||||||
Stream_SetPosition(s, 2);
|
|
||||||
Stream_Write_UINT16(s, end - 4);
|
|
||||||
|
|
||||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
|
||||||
&written);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
|
|
||||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
|
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -499,21 +404,15 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
|||||||
const BYTE* src;
|
const BYTE* src;
|
||||||
AUDIO_FORMAT* format;
|
AUDIO_FORMAT* format;
|
||||||
ULONG written;
|
ULONG written;
|
||||||
|
wStream* s = context->priv->rdpsnd_pdu;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
|
||||||
|
|
||||||
if (context->selected_client_format > context->num_client_formats)
|
if (context->selected_client_format >= context->num_client_formats)
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
WINPR_ASSERT(context->client_formats);
|
|
||||||
|
|
||||||
format = &context->client_formats[context->selected_client_format];
|
format = &context->client_formats[context->selected_client_format];
|
||||||
/* WaveInfo PDU */
|
/* WaveInfo PDU */
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 16))
|
|
||||||
return ERROR_OUTOFMEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
|
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
|
||||||
Stream_Write_UINT8(s, 0); /* bPad */
|
Stream_Write_UINT8(s, 0); /* bPad */
|
||||||
Stream_Write_UINT16(s, 0); /* BodySize */
|
Stream_Write_UINT16(s, 0); /* BodySize */
|
||||||
@ -523,7 +422,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
|||||||
Stream_Seek(s, 3); /* bPad */
|
Stream_Seek(s, 3); /* bPad */
|
||||||
start = Stream_GetPosition(s);
|
start = Stream_GetPosition(s);
|
||||||
src = context->priv->out_buffer;
|
src = context->priv->out_buffer;
|
||||||
length = 1ull * context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
||||||
|
|
||||||
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
|
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
@ -537,6 +436,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
|||||||
Stream_SetPosition(s, 2);
|
Stream_SetPosition(s, 2);
|
||||||
Stream_Write_UINT16(s, end - start + 8);
|
Stream_Write_UINT16(s, end - start + 8);
|
||||||
Stream_SetPosition(s, end);
|
Stream_SetPosition(s, end);
|
||||||
|
context->block_no = (context->block_no + 1) % 256;
|
||||||
|
|
||||||
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
||||||
start + 4, &written))
|
start + 4, &written))
|
||||||
@ -564,8 +464,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
|||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->block_no = (context->block_no + 1) % 256;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
context->priv->out_pending_frames = 0;
|
context->priv->out_pending_frames = 0;
|
||||||
@ -578,79 +476,59 @@ out:
|
|||||||
*
|
*
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
* @return 0 on success, otherwise a Win32 error code
|
||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo,
|
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
|
||||||
const BYTE* data, size_t size, BOOL encoded,
|
|
||||||
UINT16 timestamp, UINT32 audioTimeStamp)
|
|
||||||
{
|
{
|
||||||
|
size_t length;
|
||||||
size_t end = 0;
|
size_t end = 0;
|
||||||
|
const BYTE* src;
|
||||||
|
AUDIO_FORMAT* format;
|
||||||
ULONG written;
|
ULONG written;
|
||||||
|
wStream* s = context->priv->rdpsnd_pdu;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
BOOL status;
|
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 16))
|
if (context->selected_client_format >= context->num_client_formats)
|
||||||
{
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
|
format = &context->client_formats[context->selected_client_format];
|
||||||
|
/* WaveInfo PDU */
|
||||||
|
Stream_SetPosition(s, 0);
|
||||||
|
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
|
||||||
|
Stream_Write_UINT8(s, 0); /* bPad */
|
||||||
|
Stream_Write_UINT16(s, 0); /* BodySize */
|
||||||
|
Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */
|
||||||
|
Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */
|
||||||
|
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
|
||||||
|
Stream_Seek(s, 3); /* bPad */
|
||||||
|
Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */
|
||||||
|
src = context->priv->out_buffer;
|
||||||
|
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
||||||
|
|
||||||
|
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wave2 PDU */
|
|
||||||
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
|
|
||||||
Stream_Write_UINT8(s, 0); /* bPad */
|
|
||||||
Stream_Write_UINT16(s, 0); /* BodySize */
|
|
||||||
Stream_Write_UINT16(s, timestamp); /* wTimeStamp */
|
|
||||||
Stream_Write_UINT16(s, formatNo); /* wFormatNo */
|
|
||||||
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
|
|
||||||
Stream_Write_UINT8(s, 0); /* bPad */
|
|
||||||
Stream_Write_UINT8(s, 0); /* bPad */
|
|
||||||
Stream_Write_UINT8(s, 0); /* bPad */
|
|
||||||
Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */
|
|
||||||
|
|
||||||
if (encoded)
|
|
||||||
{
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, size))
|
|
||||||
{
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Write(s, data, size);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AUDIO_FORMAT* format;
|
BOOL rc;
|
||||||
|
|
||||||
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s))
|
/* Set stream size */
|
||||||
{
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
format = &context->client_formats[formatNo];
|
|
||||||
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
|
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
|
||||||
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
|
end = Stream_GetPosition(s);
|
||||||
|
Stream_SetPosition(s, 2);
|
||||||
|
Stream_Write_UINT16(s, end - 4);
|
||||||
|
context->block_no = (context->block_no + 1) % 256;
|
||||||
|
rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
||||||
|
&written);
|
||||||
|
|
||||||
|
if (!rc || (end != written))
|
||||||
{
|
{
|
||||||
|
WLog_ERR(TAG,
|
||||||
|
"WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
|
||||||
|
end, written);
|
||||||
error = ERROR_INTERNAL_ERROR;
|
error = ERROR_INTERNAL_ERROR;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end = Stream_GetPosition(s);
|
|
||||||
Stream_SetPosition(s, 2);
|
|
||||||
Stream_Write_UINT16(s, end - 4);
|
|
||||||
|
|
||||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
|
||||||
&written);
|
|
||||||
|
|
||||||
if (!status || (end != written))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
|
|
||||||
end, written);
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->block_no = (context->block_no + 1) % 256;
|
|
||||||
|
|
||||||
out:
|
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
context->priv->out_pending_frames = 0;
|
context->priv->out_pending_frames = 0;
|
||||||
return error;
|
return error;
|
||||||
@ -659,21 +537,8 @@ out:
|
|||||||
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
|
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
|
||||||
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
|
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
|
||||||
{
|
{
|
||||||
const BYTE* src;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
if (context->selected_client_format >= context->num_client_formats)
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
src = context->priv->out_buffer;
|
|
||||||
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
|
||||||
|
|
||||||
if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
|
if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
|
||||||
return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length,
|
return rdpsnd_server_send_wave2_pdu(context, wTimestamp);
|
||||||
FALSE, wTimestamp, wTimestamp);
|
|
||||||
else
|
else
|
||||||
return rdpsnd_server_send_wave_pdu(context, wTimestamp);
|
return rdpsnd_server_send_wave_pdu(context, wTimestamp);
|
||||||
}
|
}
|
||||||
@ -686,11 +551,9 @@ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wT
|
|||||||
static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes,
|
static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes,
|
||||||
UINT16 wTimestamp)
|
UINT16 wTimestamp)
|
||||||
{
|
{
|
||||||
|
int cframes;
|
||||||
|
int cframesize;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
EnterCriticalSection(&context->priv->lock);
|
EnterCriticalSection(&context->priv->lock);
|
||||||
|
|
||||||
if (context->selected_client_format >= context->num_client_formats)
|
if (context->selected_client_format >= context->num_client_formats)
|
||||||
@ -703,13 +566,12 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void*
|
|||||||
|
|
||||||
while (nframes > 0)
|
while (nframes > 0)
|
||||||
{
|
{
|
||||||
const size_t cframes =
|
cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames);
|
||||||
MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames);
|
cframesize = cframes * context->priv->src_bytes_per_frame;
|
||||||
size_t cframesize = cframes * context->priv->src_bytes_per_frame;
|
|
||||||
CopyMemory(context->priv->out_buffer +
|
CopyMemory(context->priv->out_buffer +
|
||||||
(context->priv->out_pending_frames * context->priv->src_bytes_per_frame),
|
(context->priv->out_pending_frames * context->priv->src_bytes_per_frame),
|
||||||
buf, cframesize);
|
buf, cframesize);
|
||||||
buf = (const BYTE*)buf + cframesize;
|
buf = (BYTE*)buf + cframesize;
|
||||||
nframes -= cframes;
|
nframes -= cframes;
|
||||||
context->priv->out_pending_frames += cframes;
|
context->priv->out_pending_frames += cframes;
|
||||||
|
|
||||||
@ -728,33 +590,6 @@ out:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send encoded audio samples using a Wave2 PDU.
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a Win32 error code
|
|
||||||
*/
|
|
||||||
static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo,
|
|
||||||
const void* buf, size_t size, UINT16 timestamp,
|
|
||||||
UINT32 audioTimeStamp)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
if (context->clientVersion < CHANNEL_VERSION_WIN_8)
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
EnterCriticalSection(&context->priv->lock);
|
|
||||||
|
|
||||||
error =
|
|
||||||
rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp);
|
|
||||||
|
|
||||||
LeaveCriticalSection(&context->priv->lock);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function description
|
* Function description
|
||||||
*
|
*
|
||||||
@ -762,23 +597,21 @@ static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 for
|
|||||||
*/
|
*/
|
||||||
static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right)
|
static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t pos;
|
||||||
BOOL status;
|
BOOL status;
|
||||||
ULONG written;
|
ULONG written;
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
wStream* s = context->priv->rdpsnd_pdu;
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, SNDC_SETVOLUME);
|
Stream_Write_UINT8(s, SNDC_SETVOLUME);
|
||||||
Stream_Write_UINT8(s, 0);
|
Stream_Write_UINT8(s, 0);
|
||||||
Stream_Write_UINT16(s, 4); /* Payload length */
|
Stream_Seek_UINT16(s);
|
||||||
Stream_Write_UINT16(s, left);
|
Stream_Write_UINT16(s, left);
|
||||||
Stream_Write_UINT16(s, right);
|
Stream_Write_UINT16(s, right);
|
||||||
len = Stream_GetPosition(s);
|
pos = Stream_GetPosition(s);
|
||||||
|
Stream_SetPosition(s, 2);
|
||||||
|
Stream_Write_UINT16(s, pos - 4);
|
||||||
|
Stream_SetPosition(s, pos);
|
||||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
||||||
(ULONG)len, &written);
|
Stream_GetPosition(s), &written);
|
||||||
Stream_SetPosition(s, 0);
|
Stream_SetPosition(s, 0);
|
||||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
@ -793,9 +626,8 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
|
|||||||
size_t pos;
|
size_t pos;
|
||||||
BOOL status;
|
BOOL status;
|
||||||
ULONG written;
|
ULONG written;
|
||||||
|
wStream* s = context->priv->rdpsnd_pdu;
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
wStream* s = rdpsnd_server_get_buffer(context);
|
|
||||||
|
|
||||||
EnterCriticalSection(&context->priv->lock);
|
EnterCriticalSection(&context->priv->lock);
|
||||||
|
|
||||||
if (context->priv->out_pending_frames > 0)
|
if (context->priv->out_pending_frames > 0)
|
||||||
@ -817,10 +649,6 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
|
|||||||
return error;
|
return error;
|
||||||
|
|
||||||
context->selected_client_format = 0xFFFF;
|
context->selected_client_format = 0xFFFF;
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, 4))
|
|
||||||
return ERROR_OUTOFMEMORY;
|
|
||||||
|
|
||||||
Stream_Write_UINT8(s, SNDC_CLOSE);
|
Stream_Write_UINT8(s, SNDC_CLOSE);
|
||||||
Stream_Write_UINT8(s, 0);
|
Stream_Write_UINT8(s, 0);
|
||||||
Stream_Seek_UINT16(s);
|
Stream_Seek_UINT16(s);
|
||||||
@ -843,58 +671,14 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
|
|||||||
{
|
{
|
||||||
void* buffer = NULL;
|
void* buffer = NULL;
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
RdpsndServerPrivate* priv;
|
RdpsndServerPrivate* priv = context->priv;
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
UINT error = ERROR_INTERNAL_ERROR;
|
||||||
PULONG pSessionId = NULL;
|
priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd");
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
if (!priv->ChannelHandle)
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
priv = context->priv;
|
|
||||||
priv->SessionId = WTS_CURRENT_SESSION;
|
|
||||||
|
|
||||||
if (context->use_dynamic_virtual_channel)
|
|
||||||
{
|
{
|
||||||
UINT32 channelId;
|
WLog_ERR(TAG, "WTSVirtualChannelOpen failed!");
|
||||||
BOOL status = TRUE;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
|
||||||
(LPSTR*)&pSessionId, &bytesReturned))
|
|
||||||
{
|
|
||||||
priv->SessionId = (DWORD)*pSessionId;
|
|
||||||
WTSFreeMemory(pSessionId);
|
|
||||||
priv->ChannelHandle = (HANDLE)WTSVirtualChannelOpenEx(
|
|
||||||
priv->SessionId, "AUDIO_PLAYBACK_DVC", WTS_CHANNEL_OPTION_DYNAMIC);
|
|
||||||
if (!priv->ChannelHandle)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Open audio dynamic virtual channel (AUDIO_PLAYBACK_DVC) failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(priv->ChannelHandle);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
priv->ChannelHandle =
|
|
||||||
WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd");
|
|
||||||
if (!priv->ChannelHandle)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Open audio static virtual channel (rdpsnd) failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
|
if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer,
|
||||||
@ -929,7 +713,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
|
|||||||
goto out_pdu;
|
goto out_pdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = rdpsnd_server_send_formats(context)))
|
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu)))
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error);
|
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error);
|
||||||
goto out_lock;
|
goto out_lock;
|
||||||
@ -979,12 +763,6 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
|
|||||||
{
|
{
|
||||||
UINT error = CHANNEL_RC_OK;
|
UINT error = CHANNEL_RC_OK;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
if (!context->priv->StopEvent)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (context->priv->ownThread)
|
if (context->priv->ownThread)
|
||||||
{
|
{
|
||||||
if (context->priv->StopEvent)
|
if (context->priv->StopEvent)
|
||||||
@ -1000,46 +778,36 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
|
|||||||
|
|
||||||
CloseHandle(context->priv->Thread);
|
CloseHandle(context->priv->Thread);
|
||||||
CloseHandle(context->priv->StopEvent);
|
CloseHandle(context->priv->StopEvent);
|
||||||
context->priv->Thread = NULL;
|
|
||||||
context->priv->StopEvent = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteCriticalSection(&context->priv->lock);
|
DeleteCriticalSection(&context->priv->lock);
|
||||||
|
|
||||||
if (context->priv->rdpsnd_pdu)
|
if (context->priv->rdpsnd_pdu)
|
||||||
{
|
|
||||||
Stream_Free(context->priv->rdpsnd_pdu, TRUE);
|
Stream_Free(context->priv->rdpsnd_pdu, TRUE);
|
||||||
context->priv->rdpsnd_pdu = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context->priv->ChannelHandle)
|
|
||||||
{
|
|
||||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
|
||||||
context->priv->ChannelHandle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
||||||
{
|
{
|
||||||
|
RdpsndServerContext* context;
|
||||||
RdpsndServerPrivate* priv;
|
RdpsndServerPrivate* priv;
|
||||||
RdpsndServerContext* context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext));
|
context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
goto fail;
|
{
|
||||||
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
context->vcm = vcm;
|
context->vcm = vcm;
|
||||||
context->Start = rdpsnd_server_start;
|
context->Start = rdpsnd_server_start;
|
||||||
context->Stop = rdpsnd_server_stop;
|
context->Stop = rdpsnd_server_stop;
|
||||||
context->selected_client_format = 0xFFFF;
|
context->selected_client_format = 0xFFFF;
|
||||||
context->Initialize = rdpsnd_server_initialize;
|
context->Initialize = rdpsnd_server_initialize;
|
||||||
context->SendFormats = rdpsnd_server_send_formats;
|
|
||||||
context->SelectFormat = rdpsnd_server_select_format;
|
context->SelectFormat = rdpsnd_server_select_format;
|
||||||
context->Training = rdpsnd_server_training;
|
|
||||||
context->SendSamples = rdpsnd_server_send_samples;
|
context->SendSamples = rdpsnd_server_send_samples;
|
||||||
context->SendSamples2 = rdpsnd_server_send_samples2;
|
|
||||||
context->SetVolume = rdpsnd_server_set_volume;
|
context->SetVolume = rdpsnd_server_set_volume;
|
||||||
context->Close = rdpsnd_server_close;
|
context->Close = rdpsnd_server_close;
|
||||||
context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate));
|
context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate));
|
||||||
@ -1047,7 +815,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "calloc failed!");
|
WLog_ERR(TAG, "calloc failed!");
|
||||||
goto fail;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->dsp_context = freerdp_dsp_context_new(TRUE);
|
priv->dsp_context = freerdp_dsp_context_new(TRUE);
|
||||||
@ -1055,7 +823,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
|||||||
if (!priv->dsp_context)
|
if (!priv->dsp_context)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||||
goto fail;
|
goto out_free_priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->input_stream = Stream_New(NULL, 4);
|
priv->input_stream = Stream_New(NULL, 4);
|
||||||
@ -1063,23 +831,24 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
|||||||
if (!priv->input_stream)
|
if (!priv->input_stream)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
WLog_ERR(TAG, "Stream_New failed!");
|
||||||
goto fail;
|
goto out_free_dsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->expectedBytes = 4;
|
priv->expectedBytes = 4;
|
||||||
priv->waitingHeader = TRUE;
|
priv->waitingHeader = TRUE;
|
||||||
priv->ownThread = TRUE;
|
priv->ownThread = TRUE;
|
||||||
return context;
|
return context;
|
||||||
fail:
|
out_free_dsp:
|
||||||
rdpsnd_server_context_free(context);
|
freerdp_dsp_context_free(priv->dsp_context);
|
||||||
|
out_free_priv:
|
||||||
|
free(context->priv);
|
||||||
|
out_free:
|
||||||
|
free(context);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdpsnd_server_context_reset(RdpsndServerContext* context)
|
void rdpsnd_server_context_reset(RdpsndServerContext* context)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
context->priv->expectedBytes = 4;
|
context->priv->expectedBytes = 4;
|
||||||
context->priv->waitingHeader = TRUE;
|
context->priv->waitingHeader = TRUE;
|
||||||
Stream_SetPosition(context->priv->input_stream, 0);
|
Stream_SetPosition(context->priv->input_stream, 0);
|
||||||
@ -1087,21 +856,16 @@ void rdpsnd_server_context_reset(RdpsndServerContext* context)
|
|||||||
|
|
||||||
void rdpsnd_server_context_free(RdpsndServerContext* context)
|
void rdpsnd_server_context_free(RdpsndServerContext* context)
|
||||||
{
|
{
|
||||||
if (!context)
|
if (context->priv->ChannelHandle)
|
||||||
return;
|
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||||
|
|
||||||
if (context->priv)
|
free(context->priv->out_buffer);
|
||||||
{
|
|
||||||
rdpsnd_server_stop(context);
|
|
||||||
|
|
||||||
free(context->priv->out_buffer);
|
if (context->priv->dsp_context)
|
||||||
|
freerdp_dsp_context_free(context->priv->dsp_context);
|
||||||
|
|
||||||
if (context->priv->dsp_context)
|
if (context->priv->input_stream)
|
||||||
freerdp_dsp_context_free(context->priv->dsp_context);
|
Stream_Free(context->priv->input_stream, TRUE);
|
||||||
|
|
||||||
if (context->priv->input_stream)
|
|
||||||
Stream_Free(context->priv->input_stream, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(context->server_formats);
|
free(context->server_formats);
|
||||||
free(context->client_formats);
|
free(context->client_formats);
|
||||||
@ -1111,9 +875,6 @@ void rdpsnd_server_context_free(RdpsndServerContext* context)
|
|||||||
|
|
||||||
HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context)
|
HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
return context->priv->channelEvent;
|
return context->priv->channelEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1135,14 +896,8 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
|||||||
{
|
{
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
UINT ret = CHANNEL_RC_OK;
|
UINT ret = CHANNEL_RC_OK;
|
||||||
RdpsndServerPrivate* priv;
|
RdpsndServerPrivate* priv = context->priv;
|
||||||
wStream* s;
|
wStream* s = priv->input_stream;
|
||||||
|
|
||||||
WINPR_ASSERT(context);
|
|
||||||
WINPR_ASSERT(context->priv);
|
|
||||||
|
|
||||||
priv = context->priv;
|
|
||||||
s = priv->input_stream;
|
|
||||||
|
|
||||||
if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s),
|
if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s),
|
||||||
priv->expectedBytes, &bytesReturned))
|
priv->expectedBytes, &bytesReturned))
|
||||||
@ -1197,10 +952,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
|||||||
ret = rdpsnd_server_recv_waveconfirm(context, s);
|
ret = rdpsnd_server_recv_waveconfirm(context, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SNDC_TRAINING:
|
|
||||||
ret = rdpsnd_server_recv_trainingconfirm(context, s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SNDC_FORMATS:
|
case SNDC_FORMATS:
|
||||||
ret = rdpsnd_server_recv_formats(context, s);
|
ret = rdpsnd_server_recv_formats(context, s);
|
||||||
|
|
||||||
@ -1211,6 +962,8 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
|||||||
|
|
||||||
case SNDC_QUALITYMODE:
|
case SNDC_QUALITYMODE:
|
||||||
ret = rdpsnd_server_recv_quality_mode(context, s);
|
ret = rdpsnd_server_recv_quality_mode(context, s);
|
||||||
|
Stream_SetPosition(s,
|
||||||
|
0); /* in case the Activated callback tries to treat some messages */
|
||||||
|
|
||||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
|
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
|
||||||
IFCALL(context->Activated, context);
|
IFCALL(context->Activated, context);
|
||||||
|
|||||||
@ -39,7 +39,6 @@ struct _rdpsnd_server_private
|
|||||||
HANDLE StopEvent;
|
HANDLE StopEvent;
|
||||||
HANDLE channelEvent;
|
HANDLE channelEvent;
|
||||||
void* ChannelHandle;
|
void* ChannelHandle;
|
||||||
DWORD SessionId;
|
|
||||||
|
|
||||||
BOOL waitingHeader;
|
BOOL waitingHeader;
|
||||||
DWORD expectedBytes;
|
DWORD expectedBytes;
|
||||||
|
|||||||
@ -50,21 +50,9 @@
|
|||||||
#include <freerdp/server/remdesk.h>
|
#include <freerdp/server/remdesk.h>
|
||||||
#include <freerdp/server/encomsp.h>
|
#include <freerdp/server/encomsp.h>
|
||||||
#include <freerdp/server/rail.h>
|
#include <freerdp/server/rail.h>
|
||||||
#include <freerdp/server/telemetry.h>
|
|
||||||
#include <freerdp/server/rdpgfx.h>
|
#include <freerdp/server/rdpgfx.h>
|
||||||
#include <freerdp/server/disp.h>
|
#include <freerdp/server/disp.h>
|
||||||
|
|
||||||
#if defined(CHANNEL_RDPECAM_SERVER)
|
|
||||||
#include <freerdp/server/rdpecam-enumerator.h>
|
|
||||||
#include <freerdp/server/rdpecam.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CHANNEL_AINPUT_SERVER)
|
|
||||||
#include <freerdp/server/ainput.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void freerdp_channels_dummy(void);
|
|
||||||
|
|
||||||
void freerdp_channels_dummy(void)
|
void freerdp_channels_dummy(void)
|
||||||
{
|
{
|
||||||
audin_server_context* audin;
|
audin_server_context* audin;
|
||||||
@ -77,13 +65,8 @@ void freerdp_channels_dummy(void)
|
|||||||
RemdeskServerContext* remdesk;
|
RemdeskServerContext* remdesk;
|
||||||
EncomspServerContext* encomsp;
|
EncomspServerContext* encomsp;
|
||||||
RailServerContext* rail;
|
RailServerContext* rail;
|
||||||
TelemetryServerContext* telemetry;
|
|
||||||
RdpgfxServerContext* rdpgfx;
|
RdpgfxServerContext* rdpgfx;
|
||||||
DispServerContext* disp;
|
DispServerContext* disp;
|
||||||
#if defined (CHANNEL_RDPECAM_SERVER)
|
|
||||||
CamDevEnumServerContext* camera_enumerator;
|
|
||||||
CameraDeviceServerContext* camera_device;
|
|
||||||
#endif
|
|
||||||
audin = audin_server_context_new(NULL);
|
audin = audin_server_context_new(NULL);
|
||||||
audin_server_context_free(audin);
|
audin_server_context_free(audin);
|
||||||
rdpsnd = rdpsnd_server_context_new(NULL);
|
rdpsnd = rdpsnd_server_context_new(NULL);
|
||||||
@ -104,26 +87,10 @@ void freerdp_channels_dummy(void)
|
|||||||
encomsp_server_context_free(encomsp);
|
encomsp_server_context_free(encomsp);
|
||||||
rail = rail_server_context_new(NULL);
|
rail = rail_server_context_new(NULL);
|
||||||
rail_server_context_free(rail);
|
rail_server_context_free(rail);
|
||||||
telemetry = telemetry_server_context_new(NULL);
|
|
||||||
telemetry_server_context_free(telemetry);
|
|
||||||
rdpgfx = rdpgfx_server_context_new(NULL);
|
rdpgfx = rdpgfx_server_context_new(NULL);
|
||||||
rdpgfx_server_context_free(rdpgfx);
|
rdpgfx_server_context_free(rdpgfx);
|
||||||
disp = disp_server_context_new(NULL);
|
disp = disp_server_context_new(NULL);
|
||||||
disp_server_context_free(disp);
|
disp_server_context_free(disp);
|
||||||
|
|
||||||
#if defined (CHANNEL_RDPECAM_SERVER)
|
|
||||||
camera_enumerator = cam_dev_enum_server_context_new(NULL);
|
|
||||||
cam_dev_enum_server_context_free(camera_enumerator);
|
|
||||||
camera_device = camera_device_server_context_new(NULL);
|
|
||||||
camera_device_server_context_free(camera_device);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CHANNEL_AINPUT_SERVER)
|
|
||||||
{
|
|
||||||
ainput_server_context* ainput = ainput_server_context_new(NULL);
|
|
||||||
ainput_server_context_free(ainput);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -180,7 +180,6 @@ void smartcard_context_free(void* pCtx)
|
|||||||
|
|
||||||
/* cancel blocking calls like SCardGetStatusChange */
|
/* cancel blocking calls like SCardGetStatusChange */
|
||||||
SCardCancel(pContext->hContext);
|
SCardCancel(pContext->hContext);
|
||||||
SCardReleaseContext(pContext->hContext);
|
|
||||||
|
|
||||||
if (MessageQueue_PostQuit(pContext->IrpQueue, 0) &&
|
if (MessageQueue_PostQuit(pContext->IrpQueue, 0) &&
|
||||||
(WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED))
|
(WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED))
|
||||||
@ -238,7 +237,7 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
|
|||||||
|
|
||||||
/* Put thread to sleep so that PC/SC can process the cancel requests. This fixes a race
|
/* Put thread to sleep so that PC/SC can process the cancel requests. This fixes a race
|
||||||
* condition that sometimes caused the pc/sc daemon to crash on MacOS (_xpc_api_misuse) */
|
* condition that sometimes caused the pc/sc daemon to crash on MacOS (_xpc_api_misuse) */
|
||||||
SleepEx(100, FALSE);
|
Sleep(100);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList.
|
* Call SCardReleaseContext on remaining contexts and remove them from rgSCardContextList.
|
||||||
@ -252,7 +251,27 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
|
|||||||
|
|
||||||
for (index = 0; index < keyCount; index++)
|
for (index = 0; index < keyCount; index++)
|
||||||
{
|
{
|
||||||
ListDictionary_SetItemValue(smartcard->rgSCardContextList, (void*)pKeys[index], NULL);
|
pContext = (SMARTCARD_CONTEXT*)ListDictionary_Remove(smartcard->rgSCardContextList,
|
||||||
|
(void*)pKeys[index]);
|
||||||
|
|
||||||
|
if (!pContext)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hContext = pContext->hContext;
|
||||||
|
|
||||||
|
if (SCardIsValidContext(hContext) == SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
SCardReleaseContext(hContext);
|
||||||
|
|
||||||
|
if (MessageQueue_PostQuit(pContext->IrpQueue, 0) &&
|
||||||
|
(WaitForSingleObject(pContext->thread, INFINITE) == WAIT_FAILED))
|
||||||
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!",
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
CloseHandle(pContext->thread);
|
||||||
|
MessageQueue_Free(pContext->IrpQueue);
|
||||||
|
free(pContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(pKeys);
|
free(pKeys);
|
||||||
|
|||||||
@ -349,16 +349,10 @@ static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL un
|
|||||||
if (bytes < 1)
|
if (bytes < 1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (in == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (unicode)
|
if (unicode)
|
||||||
{
|
{
|
||||||
length = (bytes / sizeof(WCHAR)) - 1;
|
length = (bytes / 2);
|
||||||
mszA = (char*)calloc(length + 1, sizeof(WCHAR));
|
if (ConvertFromUnicode(CP_UTF8, 0, string.wz, (int)length, &mszA, 0, NULL, NULL) !=
|
||||||
if (!mszA)
|
|
||||||
return NULL;
|
|
||||||
if (ConvertFromUnicode(CP_UTF8, 0, string.wz, (int)length, &mszA, length + 1, NULL, NULL) !=
|
|
||||||
(int)length)
|
(int)length)
|
||||||
{
|
{
|
||||||
free(mszA);
|
free(mszA);
|
||||||
@ -368,11 +362,10 @@ static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL un
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
length = bytes;
|
length = bytes;
|
||||||
mszA = (char*)calloc(length, sizeof(char));
|
mszA = (char*)malloc(length);
|
||||||
if (!mszA)
|
if (!mszA)
|
||||||
return NULL;
|
return NULL;
|
||||||
CopyMemory(mszA, string.sz, length - 1);
|
CopyMemory(mszA, string.sz, length);
|
||||||
mszA[length - 1] = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index = 0; index < length - 1; index++)
|
for (index = 0; index < length - 1; index++)
|
||||||
@ -1314,24 +1307,21 @@ static void smartcard_trace_status_return(SMARTCARD_DEVICE* smartcard, const Sta
|
|||||||
static void smartcard_trace_state_return(SMARTCARD_DEVICE* smartcard, const State_Return* ret)
|
static void smartcard_trace_state_return(SMARTCARD_DEVICE* smartcard, const State_Return* ret)
|
||||||
{
|
{
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
char* state;
|
|
||||||
WINPR_UNUSED(smartcard);
|
WINPR_UNUSED(smartcard);
|
||||||
|
|
||||||
if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
|
if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state = SCardGetReaderStateString(ret->dwState);
|
|
||||||
WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {");
|
WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {");
|
||||||
WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
|
WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")",
|
||||||
SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
|
SCardGetErrorString(ret->ReturnCode), ret->ReturnCode);
|
||||||
WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", state, ret->dwState);
|
WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", ret->dwState);
|
||||||
WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")",
|
WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")", ret->dwProtocol);
|
||||||
SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol);
|
WLog_LVL(TAG, g_LogLevel, " cbAtrLen: %s (0x%08" PRIX32 ")", ret->cbAtrLen);
|
||||||
WLog_LVL(TAG, g_LogLevel, " cbAtrLen: (0x%08" PRIX32 ")", ret->cbAtrLen);
|
|
||||||
WLog_LVL(TAG, g_LogLevel, " rgAtr: %s",
|
WLog_LVL(TAG, g_LogLevel, " rgAtr: %s",
|
||||||
smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer)));
|
smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer)));
|
||||||
WLog_LVL(TAG, g_LogLevel, "}");
|
WLog_LVL(TAG, g_LogLevel, "}");
|
||||||
free(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smartcard_trace_reconnect_return(SMARTCARD_DEVICE* smartcard,
|
static void smartcard_trace_reconnect_return(SMARTCARD_DEVICE* smartcard,
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel("telemetry")
|
|
||||||
|
|
||||||
if(WITH_SERVER_CHANNELS)
|
|
||||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
|
||||||
endif()
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
set(OPTION_DEFAULT OFF)
|
|
||||||
set(OPTION_CLIENT_DEFAULT OFF)
|
|
||||||
set(OPTION_SERVER_DEFAULT ON)
|
|
||||||
|
|
||||||
define_channel_options(NAME "telemetry" TYPE "dynamic"
|
|
||||||
DESCRIPTION "Telemetry Virtual Channel Extension"
|
|
||||||
SPECIFICATIONS "[MS-RDPET]"
|
|
||||||
DEFAULT ${OPTION_DEFAULT})
|
|
||||||
|
|
||||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
# FreeRDP cmake build script
|
|
||||||
#
|
|
||||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
define_channel_server("telemetry")
|
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
|
||||||
telemetry_main.c)
|
|
||||||
|
|
||||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} freerdp)
|
|
||||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
|
||||||
@ -1,443 +0,0 @@
|
|||||||
/**
|
|
||||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
||||||
* Telemetry Virtual Channel Extension
|
|
||||||
*
|
|
||||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <freerdp/channels/log.h>
|
|
||||||
#include <freerdp/server/telemetry.h>
|
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("telemetry.server")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
TELEMETRY_INITIAL,
|
|
||||||
TELEMETRY_OPENED,
|
|
||||||
} eTelemetryChannelState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
TelemetryServerContext context;
|
|
||||||
|
|
||||||
HANDLE stopEvent;
|
|
||||||
|
|
||||||
HANDLE thread;
|
|
||||||
void* telemetry_channel;
|
|
||||||
|
|
||||||
DWORD SessionId;
|
|
||||||
|
|
||||||
BOOL isOpened;
|
|
||||||
BOOL externalThread;
|
|
||||||
|
|
||||||
/* Channel state */
|
|
||||||
eTelemetryChannelState state;
|
|
||||||
|
|
||||||
wStream* buffer;
|
|
||||||
} telemetry_server;
|
|
||||||
|
|
||||||
static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (telemetry->isOpened)
|
|
||||||
{
|
|
||||||
WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, "
|
|
||||||
"calling in this state is not possible!");
|
|
||||||
return ERROR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
telemetry->externalThread = externalThread;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_open_channel(telemetry_server* telemetry)
|
|
||||||
{
|
|
||||||
TelemetryServerContext* context = &telemetry->context;
|
|
||||||
DWORD Error = ERROR_SUCCESS;
|
|
||||||
HANDLE hEvent;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
PULONG pSessionId = NULL;
|
|
||||||
UINT32 channelId;
|
|
||||||
BOOL status = TRUE;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
|
||||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
telemetry->SessionId = (DWORD)*pSessionId;
|
|
||||||
WTSFreeMemory(pSessionId);
|
|
||||||
hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
telemetry->telemetry_channel = WTSVirtualChannelOpenEx(
|
|
||||||
telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
|
||||||
if (!telemetry->telemetry_channel)
|
|
||||||
{
|
|
||||||
Error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel);
|
|
||||||
|
|
||||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s)
|
|
||||||
{
|
|
||||||
TELEMETRY_RDP_TELEMETRY_PDU pdu;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 16)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "telemetry_server_recv_rdp_telemetry_pdu: Not enough data!");
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis);
|
|
||||||
Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis);
|
|
||||||
Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis);
|
|
||||||
Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis);
|
|
||||||
|
|
||||||
IFCALLRET(context->RdpTelemetry, error, context, &pdu);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_process_message(telemetry_server* telemetry)
|
|
||||||
{
|
|
||||||
BOOL rc;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
ULONG BytesReturned;
|
|
||||||
BYTE MessageId;
|
|
||||||
BYTE Length;
|
|
||||||
wStream* s;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
WINPR_ASSERT(telemetry->telemetry_channel);
|
|
||||||
|
|
||||||
s = telemetry->buffer;
|
|
||||||
WINPR_ASSERT(s);
|
|
||||||
|
|
||||||
Stream_SetPosition(s, 0);
|
|
||||||
rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned);
|
|
||||||
if (!rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (BytesReturned < 1)
|
|
||||||
{
|
|
||||||
error = CHANNEL_RC_OK;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
|
||||||
error = CHANNEL_RC_NO_MEMORY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, (PCHAR)Stream_Buffer(s),
|
|
||||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream_SetLength(s, BytesReturned);
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
|
||||||
return ERROR_NO_DATA;
|
|
||||||
|
|
||||||
Stream_Read_UINT8(s, MessageId);
|
|
||||||
Stream_Read_UINT8(s, Length);
|
|
||||||
|
|
||||||
switch (MessageId)
|
|
||||||
{
|
|
||||||
case 0x01:
|
|
||||||
error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_context_poll_int(TelemetryServerContext* context)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
UINT error = ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
switch (telemetry->state)
|
|
||||||
{
|
|
||||||
case TELEMETRY_INITIAL:
|
|
||||||
error = telemetry_server_open_channel(telemetry);
|
|
||||||
if (error)
|
|
||||||
WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!",
|
|
||||||
error);
|
|
||||||
else
|
|
||||||
telemetry->state = TELEMETRY_OPENED;
|
|
||||||
break;
|
|
||||||
case TELEMETRY_OPENED:
|
|
||||||
error = telemetry_process_message(telemetry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry)
|
|
||||||
{
|
|
||||||
void* buffer = NULL;
|
|
||||||
DWORD BytesReturned = 0;
|
|
||||||
HANDLE ChannelEvent = NULL;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer,
|
|
||||||
&BytesReturned) == TRUE)
|
|
||||||
{
|
|
||||||
if (BytesReturned == sizeof(HANDLE))
|
|
||||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
|
||||||
|
|
||||||
WTSFreeMemory(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChannelEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI telemetry_server_thread_func(LPVOID arg)
|
|
||||||
{
|
|
||||||
DWORD nCount;
|
|
||||||
HANDLE events[2] = { 0 };
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)arg;
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
DWORD status;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
nCount = 0;
|
|
||||||
events[nCount++] = telemetry->stopEvent;
|
|
||||||
|
|
||||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
|
||||||
{
|
|
||||||
switch (telemetry->state)
|
|
||||||
{
|
|
||||||
case TELEMETRY_INITIAL:
|
|
||||||
error = telemetry_server_context_poll_int(&telemetry->context);
|
|
||||||
if (error == CHANNEL_RC_OK)
|
|
||||||
{
|
|
||||||
events[1] = telemetry_server_get_channel_handle(telemetry);
|
|
||||||
nCount = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TELEMETRY_OPENED:
|
|
||||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
break;
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
error = telemetry_server_context_poll_int(&telemetry->context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAIT_FAILED:
|
|
||||||
default:
|
|
||||||
error = ERROR_INTERNAL_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WTSVirtualChannelClose(telemetry->telemetry_channel);
|
|
||||||
telemetry->telemetry_channel = NULL;
|
|
||||||
|
|
||||||
if (error && telemetry->context.rdpcontext)
|
|
||||||
setChannelError(telemetry->context.rdpcontext, error,
|
|
||||||
"telemetry_server_thread_func reported an error");
|
|
||||||
|
|
||||||
ExitThread(error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_open(TelemetryServerContext* context)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (!telemetry->externalThread && (telemetry->thread == NULL))
|
|
||||||
{
|
|
||||||
telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!telemetry->stopEvent)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateEvent failed!");
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL);
|
|
||||||
if (!telemetry->thread)
|
|
||||||
{
|
|
||||||
WLog_ERR(TAG, "CreateThread failed!");
|
|
||||||
CloseHandle(telemetry->stopEvent);
|
|
||||||
telemetry->stopEvent = NULL;
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
telemetry->isOpened = TRUE;
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_close(TelemetryServerContext* context)
|
|
||||||
{
|
|
||||||
UINT error = CHANNEL_RC_OK;
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (!telemetry->externalThread && telemetry->thread)
|
|
||||||
{
|
|
||||||
SetEvent(telemetry->stopEvent);
|
|
||||||
|
|
||||||
if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
error = GetLastError();
|
|
||||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(telemetry->thread);
|
|
||||||
CloseHandle(telemetry->stopEvent);
|
|
||||||
telemetry->thread = NULL;
|
|
||||||
telemetry->stopEvent = NULL;
|
|
||||||
}
|
|
||||||
if (telemetry->externalThread)
|
|
||||||
{
|
|
||||||
if (telemetry->state != TELEMETRY_INITIAL)
|
|
||||||
{
|
|
||||||
WTSVirtualChannelClose(telemetry->telemetry_channel);
|
|
||||||
telemetry->telemetry_channel = NULL;
|
|
||||||
telemetry->state = TELEMETRY_INITIAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
telemetry->isOpened = FALSE;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UINT telemetry_server_context_poll(TelemetryServerContext* context)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
|
|
||||||
if (!telemetry->externalThread)
|
|
||||||
return ERROR_INTERNAL_ERROR;
|
|
||||||
|
|
||||||
return telemetry_server_context_poll_int(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
WINPR_ASSERT(telemetry);
|
|
||||||
WINPR_ASSERT(handle);
|
|
||||||
|
|
||||||
if (!telemetry->externalThread)
|
|
||||||
return FALSE;
|
|
||||||
if (telemetry->state == TELEMETRY_INITIAL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
*handle = telemetry_server_get_channel_handle(telemetry);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryServerContext* telemetry_server_context_new(HANDLE vcm)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server));
|
|
||||||
|
|
||||||
if (!telemetry)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
telemetry->context.vcm = vcm;
|
|
||||||
telemetry->context.Initialize = telemetry_server_initialize;
|
|
||||||
telemetry->context.Open = telemetry_server_open;
|
|
||||||
telemetry->context.Close = telemetry_server_close;
|
|
||||||
telemetry->context.Poll = telemetry_server_context_poll;
|
|
||||||
telemetry->context.ChannelHandle = telemetry_server_context_handle;
|
|
||||||
|
|
||||||
telemetry->buffer = Stream_New(NULL, 4096);
|
|
||||||
if (!telemetry->buffer)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
return &telemetry->context;
|
|
||||||
fail:
|
|
||||||
telemetry_server_context_free(&telemetry->context);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void telemetry_server_context_free(TelemetryServerContext* context)
|
|
||||||
{
|
|
||||||
telemetry_server* telemetry = (telemetry_server*)context;
|
|
||||||
|
|
||||||
if (telemetry)
|
|
||||||
{
|
|
||||||
telemetry_server_close(context);
|
|
||||||
Stream_Free(telemetry->buffer, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(telemetry);
|
|
||||||
}
|
|
||||||
@ -195,12 +195,9 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
|
|||||||
if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
|
if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
|
||||||
media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
|
media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
|
||||||
{
|
{
|
||||||
size_t required = 6;
|
|
||||||
/* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
|
/* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
|
||||||
See http://haali.su/mkv/codecs.pdf */
|
See http://haali.su/mkv/codecs.pdf */
|
||||||
p = mdecoder->codec_context->extradata;
|
p = mdecoder->codec_context->extradata;
|
||||||
if (mdecoder->codec_context->extradata_size < required)
|
|
||||||
return FALSE;
|
|
||||||
*p++ = 1; /* Reserved? */
|
*p++ = 1; /* Reserved? */
|
||||||
*p++ = media_type->ExtraData[8]; /* Profile */
|
*p++ = media_type->ExtraData[8]; /* Profile */
|
||||||
*p++ = 0; /* Profile */
|
*p++ = 0; /* Profile */
|
||||||
@ -209,36 +206,23 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
|
|||||||
*p++ = 0xe0 | 0x01; /* Reserved | #sps */
|
*p++ = 0xe0 | 0x01; /* Reserved | #sps */
|
||||||
s = media_type->ExtraData + 20;
|
s = media_type->ExtraData + 20;
|
||||||
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
||||||
required += size + 2;
|
|
||||||
if (mdecoder->codec_context->extradata_size < required)
|
|
||||||
return FALSE;
|
|
||||||
memcpy(p, s, size + 2);
|
memcpy(p, s, size + 2);
|
||||||
s += size + 2;
|
s += size + 2;
|
||||||
p += size + 2;
|
p += size + 2;
|
||||||
required++;
|
|
||||||
if (mdecoder->codec_context->extradata_size < required)
|
|
||||||
return FALSE;
|
|
||||||
*p++ = 1; /* #pps */
|
*p++ = 1; /* #pps */
|
||||||
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1)));
|
||||||
required += size + 2;
|
|
||||||
if (mdecoder->codec_context->extradata_size < required)
|
|
||||||
return FALSE;
|
|
||||||
memcpy(p, s, size + 2);
|
memcpy(p, s, size + 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(mdecoder->codec_context->extradata, media_type->ExtraData,
|
memcpy(mdecoder->codec_context->extradata, media_type->ExtraData,
|
||||||
media_type->ExtraDataSize);
|
media_type->ExtraDataSize);
|
||||||
if (mdecoder->codec_context->extradata_size < media_type->ExtraDataSize + 8)
|
|
||||||
return FALSE;
|
|
||||||
memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
|
memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
|
|
||||||
if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED)
|
if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED)
|
||||||
mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED;
|
mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||||
#endif
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -261,9 +245,6 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi
|
|||||||
{
|
{
|
||||||
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
|
||||||
|
|
||||||
WINPR_ASSERT(mdecoder);
|
|
||||||
WINPR_ASSERT(media_type);
|
|
||||||
|
|
||||||
switch (media_type->MajorType)
|
switch (media_type->MajorType)
|
||||||
{
|
{
|
||||||
case TSMF_MAJOR_TYPE_VIDEO:
|
case TSMF_MAJOR_TYPE_VIDEO:
|
||||||
@ -316,9 +297,6 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi
|
|||||||
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
||||||
if (media_type->ExtraData)
|
if (media_type->ExtraData)
|
||||||
{
|
{
|
||||||
if (media_type->ExtraDataSize < 12)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
media_type->ExtraData += 12;
|
media_type->ExtraData += 12;
|
||||||
media_type->ExtraDataSize -= 12;
|
media_type->ExtraDataSize -= 12;
|
||||||
}
|
}
|
||||||
@ -637,9 +615,7 @@ static void tsmf_ffmpeg_free(ITSMFDecoder* decoder)
|
|||||||
static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT;
|
static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT;
|
||||||
static BOOL CALLBACK InitializeAvCodecs(PINIT_ONCE once, PVOID param, PVOID* context)
|
static BOOL CALLBACK InitializeAvCodecs(PINIT_ONCE once, PVOID param, PVOID* context)
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
|
|
||||||
avcodec_register_all();
|
avcodec_register_all();
|
||||||
#endif
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -420,8 +420,6 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
|||||||
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
http://msdn.microsoft.com/en-us/library/dd757806.aspx */
|
||||||
if (media_type->ExtraData)
|
if (media_type->ExtraData)
|
||||||
{
|
{
|
||||||
if (media_type->ExtraDataSize < 12)
|
|
||||||
return FALSE;
|
|
||||||
media_type->ExtraData += 12;
|
media_type->ExtraData += 12;
|
||||||
media_type->ExtraDataSize -= 12;
|
media_type->ExtraDataSize -= 12;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -386,12 +386,7 @@ static BOOL tsmf_read_format_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s, UINT3
|
|||||||
|
|
||||||
if (cbFormat > 176)
|
if (cbFormat > 176)
|
||||||
{
|
{
|
||||||
const size_t nsize = cbFormat - 176;
|
mediatype->ExtraDataSize = cbFormat - 176;
|
||||||
if (mediatype->ExtraDataSize < nsize)
|
|
||||||
return FALSE;
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, nsize))
|
|
||||||
return FALSE;
|
|
||||||
mediatype->ExtraDataSize = nsize;
|
|
||||||
mediatype->ExtraData = Stream_Pointer(s);
|
mediatype->ExtraData = Stream_Pointer(s);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -69,20 +69,16 @@ UINT tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman)
|
|||||||
*/
|
*/
|
||||||
UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman)
|
UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman)
|
||||||
{
|
{
|
||||||
UINT32 i = 0;
|
UINT32 i;
|
||||||
UINT32 v = 0;
|
UINT32 v;
|
||||||
UINT32 pos = 0;
|
UINT32 pos;
|
||||||
UINT32 CapabilityType = 0;
|
UINT32 CapabilityType;
|
||||||
UINT32 cbCapabilityLength = 0;
|
UINT32 cbCapabilityLength;
|
||||||
UINT32 numHostCapabilities = 0;
|
UINT32 numHostCapabilities;
|
||||||
|
|
||||||
WINPR_ASSERT(ifman);
|
|
||||||
if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4))
|
if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4))
|
||||||
return ERROR_OUTOFMEMORY;
|
return ERROR_OUTOFMEMORY;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(ifman->input) < ifman->input_size)
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
pos = Stream_GetPosition(ifman->output);
|
pos = Stream_GetPosition(ifman->output);
|
||||||
Stream_Copy(ifman->input, ifman->output, ifman->input_size);
|
Stream_Copy(ifman->input, ifman->output, ifman->input_size);
|
||||||
Stream_SetPosition(ifman->output, pos);
|
Stream_SetPosition(ifman->output, pos);
|
||||||
|
|||||||
@ -97,13 +97,7 @@ static wStream* urb_create_iocompletion(UINT32 InterfaceField, UINT32 MessageId,
|
|||||||
UINT32 OutputBufferSize)
|
UINT32 OutputBufferSize)
|
||||||
{
|
{
|
||||||
const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF);
|
const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF);
|
||||||
|
wStream* out = Stream_New(NULL, OutputBufferSize + 28);
|
||||||
#if UINT32_MAX >= SIZE_MAX
|
|
||||||
if (OutputBufferSize > UINT32_MAX - 28ull)
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
wStream* out = Stream_New(NULL, OutputBufferSize + 28ull);
|
|
||||||
|
|
||||||
if (!out)
|
if (!out)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -247,10 +241,6 @@ static UINT urbdrc_process_io_control(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* c
|
|||||||
|
|
||||||
Stream_Read_UINT32(s, OutputBufferSize);
|
Stream_Read_UINT32(s, OutputBufferSize);
|
||||||
Stream_Read_UINT32(s, RequestId);
|
Stream_Read_UINT32(s, RequestId);
|
||||||
|
|
||||||
if (OutputBufferSize > UINT32_MAX - 4)
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
|
|
||||||
InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
|
InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
|
||||||
out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4);
|
out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4);
|
||||||
|
|
||||||
@ -683,11 +673,7 @@ static UINT urb_control_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba
|
|||||||
buffer = Stream_Pointer(out);
|
buffer = Stream_Pointer(out);
|
||||||
|
|
||||||
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
|
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
|
||||||
{
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
Stream_Copy(s, out, OutputBufferSize);
|
Stream_Copy(s, out, OutputBufferSize);
|
||||||
}
|
|
||||||
|
|
||||||
/** process TS_URB_CONTROL_TRANSFER */
|
/** process TS_URB_CONTROL_TRANSFER */
|
||||||
if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType,
|
if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType,
|
||||||
@ -734,20 +720,10 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA
|
|||||||
Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */
|
Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */
|
||||||
Stream_Read_UINT32(s, OutputBufferSize);
|
Stream_Read_UINT32(s, OutputBufferSize);
|
||||||
EndpointAddress = (PipeHandle & 0x000000ff);
|
EndpointAddress = (PipeHandle & 0x000000ff);
|
||||||
|
|
||||||
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
|
|
||||||
{
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
|
|
||||||
{
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */
|
/** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */
|
||||||
return pdev->bulk_or_interrupt_transfer(
|
return pdev->bulk_or_interrupt_transfer(pdev, callback, MessageId, RequestId, EndpointAddress,
|
||||||
pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck,
|
TransferFlags, noAck, OutputBufferSize,
|
||||||
OutputBufferSize, (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL,
|
urb_bulk_transfer_cb, 10000);
|
||||||
urb_bulk_transfer_cb, 10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out,
|
static void urb_isoch_transfer_cb(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback, wStream* out,
|
||||||
@ -827,13 +803,6 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback
|
|||||||
packetDescriptorData = Stream_Pointer(s);
|
packetDescriptorData = Stream_Pointer(s);
|
||||||
Stream_Seek(s, NumberOfPackets * 12);
|
Stream_Seek(s, NumberOfPackets * 12);
|
||||||
Stream_Read_UINT32(s, OutputBufferSize);
|
Stream_Read_UINT32(s, OutputBufferSize);
|
||||||
|
|
||||||
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
|
|
||||||
{
|
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pdev->isoch_transfer(
|
return pdev->isoch_transfer(
|
||||||
pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame,
|
pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame,
|
||||||
ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize,
|
ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize,
|
||||||
@ -1779,13 +1748,6 @@ static UINT urbdrc_process_transfer_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLB
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
WLog_Print(urbdrc->log, WLOG_WARN,
|
|
||||||
"USB transfer request URB Function %08" PRIx32 " failed with %08" PRIx32,
|
|
||||||
URB_Function, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -193,8 +193,8 @@ const char* usb_interface_class_to_string(uint8_t class)
|
|||||||
|
|
||||||
static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId,
|
static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId,
|
||||||
size_t offset, size_t BufferSize,
|
size_t offset, size_t BufferSize,
|
||||||
const BYTE* data, size_t packetSize,
|
size_t packetSize, BOOL NoAck,
|
||||||
BOOL NoAck, t_isoch_transfer_cb cb,
|
t_isoch_transfer_cb cb,
|
||||||
URBDRC_CHANNEL_CALLBACK* callback)
|
URBDRC_CHANNEL_CALLBACK* callback)
|
||||||
{
|
{
|
||||||
ASYNC_TRANSFER_USER_DATA* user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA));
|
ASYNC_TRANSFER_USER_DATA* user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA));
|
||||||
@ -210,19 +210,14 @@ static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UI
|
|||||||
free(user_data);
|
free(user_data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream_Seek(user_data->data, offset); /* Skip header offset */
|
Stream_Seek(user_data->data, offset); /* Skip header offset */
|
||||||
if (data)
|
|
||||||
memcpy(Stream_Pointer(user_data->data), data, BufferSize);
|
|
||||||
else
|
|
||||||
user_data->OutputBufferSize = BufferSize;
|
|
||||||
|
|
||||||
user_data->noack = NoAck;
|
user_data->noack = NoAck;
|
||||||
user_data->cb = cb;
|
user_data->cb = cb;
|
||||||
user_data->callback = callback;
|
user_data->callback = callback;
|
||||||
user_data->idev = idev;
|
user_data->idev = idev;
|
||||||
user_data->MessageId = MessageId;
|
user_data->MessageId = MessageId;
|
||||||
|
user_data->OutputBufferSize = BufferSize;
|
||||||
user_data->queue = pdev->request_queue;
|
user_data->queue = pdev->request_queue;
|
||||||
|
|
||||||
return user_data;
|
return user_data;
|
||||||
@ -237,7 +232,7 @@ static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer)
|
static void func_iso_callback(struct libusb_transfer* transfer)
|
||||||
{
|
{
|
||||||
ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data;
|
ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data;
|
||||||
const UINT32 streamID = stream_id_from_buffer(transfer);
|
const UINT32 streamID = stream_id_from_buffer(transfer);
|
||||||
@ -336,7 +331,7 @@ static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPT
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer)
|
static void func_bulk_transfer_cb(struct libusb_transfer* transfer)
|
||||||
{
|
{
|
||||||
ASYNC_TRANSFER_USER_DATA* user_data;
|
ASYNC_TRANSFER_USER_DATA* user_data;
|
||||||
uint32_t streamID;
|
uint32_t streamID;
|
||||||
@ -490,19 +485,17 @@ static LIBUSB_DEVICE* udev_get_libusb_dev(libusb_context* context, uint8_t bus_n
|
|||||||
|
|
||||||
for (i = 0; i < total_device; i++)
|
for (i = 0; i < total_device; i++)
|
||||||
{
|
{
|
||||||
LIBUSB_DEVICE* dev = libusb_list[i];
|
uint8_t cbus = libusb_get_bus_number(libusb_list[i]);
|
||||||
if ((bus_number == libusb_get_bus_number(dev)) &&
|
uint8_t caddr = libusb_get_device_address(libusb_list[i]);
|
||||||
(dev_number == libusb_get_device_address(dev)))
|
|
||||||
|
if ((bus_number == cbus) && (dev_number == caddr))
|
||||||
{
|
{
|
||||||
device = dev;
|
device = libusb_list[i];
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
libusb_unref_device(dev);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
libusb_free_device_list(libusb_list, 0);
|
libusb_free_device_list(libusb_list, 1);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,6 +517,7 @@ static LIBUSB_DEVICE_DESCRIPTOR* udev_new_descript(URBDRC_PLUGIN* urbdrc, LIBUSB
|
|||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting)
|
static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting)
|
||||||
{
|
{
|
||||||
int error = 0, diff = 0;
|
int error = 0, diff = 0;
|
||||||
@ -1013,9 +1007,6 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev)
|
|||||||
if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc)
|
if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return TRUE;
|
|
||||||
#else
|
|
||||||
urbdrc = pdev->urbdrc;
|
urbdrc = pdev->urbdrc;
|
||||||
|
|
||||||
if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0)
|
if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0)
|
||||||
@ -1036,7 +1027,6 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev)
|
static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev)
|
||||||
@ -1053,14 +1043,12 @@ static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev)
|
|||||||
|
|
||||||
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err);
|
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err);
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
if (err != LIBUSB_ERROR_NO_DEVICE)
|
if (err != LIBUSB_ERROR_NO_DEVICE)
|
||||||
{
|
{
|
||||||
err = libusb_attach_kernel_driver(pdev->libusb_handle, i);
|
err = libusb_attach_kernel_driver(pdev->libusb_handle, i);
|
||||||
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d",
|
log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d",
|
||||||
err, i);
|
err, i);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -1209,8 +1197,8 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
urbdrc = pdev->urbdrc;
|
urbdrc = pdev->urbdrc;
|
||||||
user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, Buffer,
|
user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, outSize + 1024, NoAck,
|
||||||
outSize + 1024, NoAck, cb, callback);
|
cb, callback);
|
||||||
|
|
||||||
if (!user_data)
|
if (!user_data)
|
||||||
return -1;
|
return -1;
|
||||||
@ -1218,21 +1206,20 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c
|
|||||||
user_data->ErrorCount = ErrorCount;
|
user_data->ErrorCount = ErrorCount;
|
||||||
user_data->StartFrame = StartFrame;
|
user_data->StartFrame = StartFrame;
|
||||||
|
|
||||||
if (!Buffer)
|
if (Buffer) /* We read data, prepare a bufffer */
|
||||||
|
{
|
||||||
|
user_data->OutputBufferSize = 0;
|
||||||
|
memmove(Stream_Pointer(user_data->data), Buffer, BufferSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
Stream_Seek(user_data->data, (NumberOfPackets * 12));
|
Stream_Seek(user_data->data, (NumberOfPackets * 12));
|
||||||
|
|
||||||
if (NumberOfPackets > 0)
|
iso_packet_size = BufferSize / NumberOfPackets;
|
||||||
{
|
iso_transfer = libusb_alloc_transfer(NumberOfPackets);
|
||||||
iso_packet_size = BufferSize / NumberOfPackets;
|
|
||||||
iso_transfer = libusb_alloc_transfer((int)NumberOfPackets);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iso_transfer == NULL)
|
if (iso_transfer == NULL)
|
||||||
{
|
{
|
||||||
WLog_Print(urbdrc->log, WLOG_ERROR,
|
WLog_Print(urbdrc->log, WLOG_ERROR, "Error: libusb_alloc_transfer.");
|
||||||
"Error: libusb_alloc_transfer [NumberOfPackets=%" PRIu32 ", BufferSize=%" PRIu32
|
|
||||||
" ]",
|
|
||||||
NumberOfPackets, BufferSize);
|
|
||||||
async_transfer_user_data_free(user_data);
|
async_transfer_user_data_free(user_data);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1283,7 +1270,7 @@ static BOOL libusb_udev_control_transfer(IUDEVICE* idev, UINT32 RequestId, UINT3
|
|||||||
static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
|
static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
|
||||||
UINT32 MessageId, UINT32 RequestId,
|
UINT32 MessageId, UINT32 RequestId,
|
||||||
UINT32 EndpointAddress, UINT32 TransferFlags,
|
UINT32 EndpointAddress, UINT32 TransferFlags,
|
||||||
BOOL NoAck, UINT32 BufferSize, const BYTE* data,
|
BOOL NoAck, UINT32 BufferSize,
|
||||||
t_isoch_transfer_cb cb, UINT32 Timeout)
|
t_isoch_transfer_cb cb, UINT32 Timeout)
|
||||||
{
|
{
|
||||||
UINT32 transfer_type;
|
UINT32 transfer_type;
|
||||||
@ -1299,7 +1286,7 @@ static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, URBDRC_CHANNEL
|
|||||||
|
|
||||||
urbdrc = pdev->urbdrc;
|
urbdrc = pdev->urbdrc;
|
||||||
user_data =
|
user_data =
|
||||||
async_transfer_user_data_new(idev, MessageId, 36, BufferSize, data, 0, NoAck, cb, callback);
|
async_transfer_user_data_new(idev, MessageId, 36, BufferSize, 0, NoAck, cb, callback);
|
||||||
|
|
||||||
if (!user_data)
|
if (!user_data)
|
||||||
return -1;
|
return -1;
|
||||||
@ -1487,7 +1474,6 @@ static void udev_free(IUDEVICE* idev)
|
|||||||
ArrayList_Free(udev->request_queue);
|
ArrayList_Free(udev->request_queue);
|
||||||
/* free the config descriptor that send from windows */
|
/* free the config descriptor that send from windows */
|
||||||
msusb_msconfig_free(udev->MsConfig);
|
msusb_msconfig_free(udev->MsConfig);
|
||||||
libusb_unref_device(udev->libusb_dev);
|
|
||||||
libusb_close(udev->libusb_handle);
|
libusb_close(udev->libusb_handle);
|
||||||
libusb_close(udev->hub_handle);
|
libusb_close(udev->hub_handle);
|
||||||
free(udev->devDescriptor);
|
free(udev->devDescriptor);
|
||||||
@ -1536,8 +1522,8 @@ static void udev_load_interface(UDEVICE* pdev)
|
|||||||
pdev->iface.free = udev_free;
|
pdev->iface.free = udev_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev,
|
static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev,
|
||||||
UINT16 bus_number, UINT16 dev_number)
|
UINT16 bus_number, UINT16 dev_number)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
ssize_t i, total_device;
|
ssize_t i, total_device;
|
||||||
@ -1549,19 +1535,21 @@ static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UD
|
|||||||
|
|
||||||
for (i = 0; i < total_device; i++)
|
for (i = 0; i < total_device; i++)
|
||||||
{
|
{
|
||||||
LIBUSB_DEVICE* dev = libusb_list[i];
|
LIBUSB_DEVICE_HANDLE* handle;
|
||||||
|
uint8_t cbus = libusb_get_bus_number(libusb_list[i]);
|
||||||
|
uint8_t caddr = libusb_get_device_address(libusb_list[i]);
|
||||||
|
|
||||||
if ((bus_number != libusb_get_bus_number(dev)) ||
|
if ((bus_number != cbus) || (dev_number != caddr))
|
||||||
(dev_number != libusb_get_device_address(dev)))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
error = libusb_open(dev, &pdev->libusb_handle);
|
error = libusb_open(libusb_list[i], &handle);
|
||||||
|
|
||||||
if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
|
if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* get port number */
|
/* get port number */
|
||||||
error = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers));
|
error = libusb_get_port_numbers(libusb_list[i], port_numbers, sizeof(port_numbers));
|
||||||
|
libusb_close(handle);
|
||||||
|
|
||||||
if (error < 1)
|
if (error < 1)
|
||||||
{
|
{
|
||||||
@ -1579,40 +1567,29 @@ static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UD
|
|||||||
WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path);
|
WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
libusb_free_device_list(libusb_list, 1);
|
|
||||||
|
|
||||||
if (error < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev,
|
|
||||||
UINT16 bus_number, UINT16 dev_number)
|
|
||||||
{
|
|
||||||
int error;
|
|
||||||
ssize_t i, total_device;
|
|
||||||
LIBUSB_DEVICE** libusb_list;
|
|
||||||
LIBUSB_DEVICE_HANDLE* handle;
|
|
||||||
total_device = libusb_get_device_list(ctx, &libusb_list);
|
|
||||||
|
|
||||||
/* Look for device hub. */
|
/* Look for device hub. */
|
||||||
error = -1;
|
if (error == 0)
|
||||||
|
|
||||||
for (i = 0; i < total_device; i++)
|
|
||||||
{
|
{
|
||||||
LIBUSB_DEVICE* dev = libusb_list[i];
|
error = -1;
|
||||||
|
|
||||||
if ((bus_number != libusb_get_bus_number(dev)) ||
|
for (i = 0; i < total_device; i++)
|
||||||
(1 != libusb_get_device_address(dev))) /* Root hub allways first on bus. */
|
{
|
||||||
continue;
|
LIBUSB_DEVICE_HANDLE* handle;
|
||||||
|
uint8_t cbus = libusb_get_bus_number(libusb_list[i]);
|
||||||
|
uint8_t caddr = libusb_get_device_address(libusb_list[i]);
|
||||||
|
|
||||||
WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number);
|
if ((bus_number != cbus) || (1 != caddr)) /* Root hub allways first on bus. */
|
||||||
error = libusb_open(dev, &handle);
|
continue;
|
||||||
|
|
||||||
if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
|
WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number);
|
||||||
pdev->hub_handle = handle;
|
error = libusb_open(libusb_list[i], &handle);
|
||||||
|
|
||||||
break;
|
if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error))
|
||||||
|
pdev->hub_handle = handle;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
libusb_free_device_list(libusb_list, 1);
|
libusb_free_device_list(libusb_list, 1);
|
||||||
@ -1663,26 +1640,30 @@ static IUDEVICE* udev_init(URBDRC_PLUGIN* urbdrc, libusb_context* context, LIBUS
|
|||||||
if (urbdrc->listener_callback)
|
if (urbdrc->listener_callback)
|
||||||
udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr);
|
udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr);
|
||||||
|
|
||||||
/* Get DEVICE handle */
|
|
||||||
status = udev_get_device_handle(urbdrc, context, pdev, bus_number, dev_number);
|
|
||||||
if (status != LIBUSB_SUCCESS)
|
|
||||||
{
|
|
||||||
struct libusb_device_descriptor desc;
|
|
||||||
const uint8_t port = libusb_get_port_number(pdev->libusb_dev);
|
|
||||||
libusb_get_device_descriptor(pdev->libusb_dev, &desc);
|
|
||||||
|
|
||||||
log_libusb_result(urbdrc->log, WLOG_ERROR,
|
|
||||||
"libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]", status,
|
|
||||||
bus_number, port, dev_number, desc.idVendor, desc.idProduct);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get HUB handle */
|
/* Get HUB handle */
|
||||||
status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number);
|
status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number);
|
||||||
|
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
pdev->hub_handle = NULL;
|
pdev->hub_handle = NULL;
|
||||||
|
|
||||||
|
{
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
const uint8_t bus = libusb_get_bus_number(pdev->libusb_dev);
|
||||||
|
const uint8_t port = libusb_get_port_number(pdev->libusb_dev);
|
||||||
|
const uint8_t addr = libusb_get_device_address(pdev->libusb_dev);
|
||||||
|
libusb_get_device_descriptor(pdev->libusb_dev, &desc);
|
||||||
|
|
||||||
|
status = libusb_open(pdev->libusb_dev, &pdev->libusb_handle);
|
||||||
|
|
||||||
|
if (status != LIBUSB_SUCCESS)
|
||||||
|
{
|
||||||
|
log_libusb_result(urbdrc->log, WLOG_ERROR,
|
||||||
|
"libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]",
|
||||||
|
status, bus, port, addr, desc.idVendor, desc.idProduct);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev);
|
pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev);
|
||||||
|
|
||||||
if (!pdev->devDescriptor)
|
if (!pdev->devDescriptor)
|
||||||
@ -1753,6 +1734,8 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo
|
|||||||
{
|
{
|
||||||
LIBUSB_DEVICE** libusb_list;
|
LIBUSB_DEVICE** libusb_list;
|
||||||
UDEVICE** array;
|
UDEVICE** array;
|
||||||
|
UINT16 bus_number;
|
||||||
|
UINT16 dev_number;
|
||||||
ssize_t i, total_device;
|
ssize_t i, total_device;
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
|
|
||||||
@ -1769,27 +1752,23 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo
|
|||||||
|
|
||||||
for (i = 0; i < total_device; i++)
|
for (i = 0; i < total_device; i++)
|
||||||
{
|
{
|
||||||
LIBUSB_DEVICE* dev = libusb_list[i];
|
LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, libusb_list[i]);
|
||||||
LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, dev);
|
|
||||||
|
|
||||||
if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct))
|
if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct))
|
||||||
{
|
{
|
||||||
array[num] = (PUDEVICE)udev_init(urbdrc, ctx, dev, libusb_get_bus_number(dev),
|
bus_number = libusb_get_bus_number(libusb_list[i]);
|
||||||
libusb_get_device_address(dev));
|
dev_number = libusb_get_device_address(libusb_list[i]);
|
||||||
|
array[num] = (PUDEVICE)udev_init(urbdrc, ctx, libusb_list[i], bus_number, dev_number);
|
||||||
|
|
||||||
if (array[num] != NULL)
|
if (array[num] != NULL)
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
libusb_unref_device(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(descriptor);
|
free(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
libusb_free_device_list(libusb_list, 0);
|
libusb_free_device_list(libusb_list, 1);
|
||||||
*devArray = (IUDEVICE**)array;
|
*devArray = (IUDEVICE**)array;
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,12 +195,6 @@ static size_t udevman_register_udevice(IUDEVMAN* idevman, BYTE bus_number, BYTE
|
|||||||
/* register all device that match pid vid */
|
/* register all device that match pid vid */
|
||||||
num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray);
|
num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray);
|
||||||
|
|
||||||
if (num == 0)
|
|
||||||
{
|
|
||||||
WLog_Print(urbdrc->log, WLOG_WARN,
|
|
||||||
"Could not find or redirect any usb devices by id %04x:%04x", idVendor, idProduct);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < num; i++)
|
for (i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
UINT32 id;
|
UINT32 id;
|
||||||
@ -511,7 +505,12 @@ static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass)
|
|||||||
|
|
||||||
static BOOL append(char* dst, size_t length, const char* src)
|
static BOOL append(char* dst, size_t length, const char* src)
|
||||||
{
|
{
|
||||||
return winpr_str_append(src, dst, length, NULL);
|
size_t slen = strlen(src);
|
||||||
|
size_t dlen = strnlen(dst, length);
|
||||||
|
if (dlen + slen >= length)
|
||||||
|
return FALSE;
|
||||||
|
strcat(dst, src);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL device_is_filtered(struct libusb_device* dev,
|
static BOOL device_is_filtered(struct libusb_device* dev,
|
||||||
@ -537,7 +536,7 @@ static BOOL device_is_filtered(struct libusb_device* dev,
|
|||||||
|
|
||||||
for (x = 0; x < config->bNumInterfaces; x++)
|
for (x = 0; x < config->bNumInterfaces; x++)
|
||||||
{
|
{
|
||||||
int y;
|
uint8_t y;
|
||||||
const struct libusb_interface* ifc = &config->interface[x];
|
const struct libusb_interface* ifc = &config->interface[x];
|
||||||
for (y = 0; y < ifc->num_altsetting; y++)
|
for (y = 0; y < ifc->num_altsetting; y++)
|
||||||
{
|
{
|
||||||
@ -581,8 +580,8 @@ static BOOL device_is_filtered(struct libusb_device* dev,
|
|||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int LIBUSB_CALL hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev,
|
static int hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev,
|
||||||
libusb_hotplug_event event, void* user_data)
|
libusb_hotplug_event event, void* user_data)
|
||||||
{
|
{
|
||||||
VID_PID_PAIR pair;
|
VID_PID_PAIR pair;
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
@ -835,7 +834,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman)
|
|||||||
{
|
{
|
||||||
int rc = LIBUSB_SUCCESS;
|
int rc = LIBUSB_SUCCESS;
|
||||||
struct timeval tv = { 0, 500 };
|
struct timeval tv = { 0, 500 };
|
||||||
if (libusb_try_lock_events(udevman->context) == 0)
|
if (libusb_try_lock_events(udevman->context))
|
||||||
{
|
{
|
||||||
if (libusb_event_handling_ok(udevman->context))
|
if (libusb_event_handling_ok(udevman->context))
|
||||||
{
|
{
|
||||||
@ -860,7 +859,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman)
|
|||||||
return rc > 0;
|
return rc > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD WINAPI poll_thread(LPVOID lpThreadParameter)
|
static DWORD poll_thread(LPVOID lpThreadParameter)
|
||||||
{
|
{
|
||||||
libusb_hotplug_callback_handle handle;
|
libusb_hotplug_callback_handle handle;
|
||||||
UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter;
|
UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter;
|
||||||
|
|||||||
@ -134,7 +134,7 @@ struct _IUDEVICE
|
|||||||
int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
|
int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* callback,
|
||||||
UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress,
|
UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress,
|
||||||
UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize,
|
UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize,
|
||||||
const BYTE* data, t_isoch_transfer_cb cb, UINT32 Timeout);
|
t_isoch_transfer_cb cb, UINT32 Timeout);
|
||||||
|
|
||||||
int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue);
|
int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue);
|
||||||
|
|
||||||
|
|||||||
@ -25,10 +25,8 @@ include_directories(..)
|
|||||||
|
|
||||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||||
|
|
||||||
if (NOT BUILTIN_CHANNELS)
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} rdpgfx-client)
|
|
||||||
endif()
|
|
||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||||
|
|
||||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|||||||
@ -666,7 +666,7 @@ static void video_timer(VideoClientContext* video, UINT64 now)
|
|||||||
presentation = frame->presentation;
|
presentation = frame->presentation;
|
||||||
|
|
||||||
priv->publishedFrames++;
|
priv->publishedFrames++;
|
||||||
memcpy(presentation->surfaceData, frame->surfaceData, frame->w * frame->h * 4ULL);
|
memcpy(presentation->surfaceData, frame->surfaceData, frame->w * frame->h * 4);
|
||||||
|
|
||||||
video->showSurface(video, presentation->surface);
|
video->showSurface(video, presentation->surface);
|
||||||
|
|
||||||
@ -848,7 +848,7 @@ static UINT video_VideoData(VideoClientContext* context, TSMM_VIDEO_DATA* data)
|
|||||||
frame->w = presentation->SourceWidth;
|
frame->w = presentation->SourceWidth;
|
||||||
frame->h = presentation->SourceHeight;
|
frame->h = presentation->SourceHeight;
|
||||||
|
|
||||||
frame->surfaceData = BufferPool_Take(priv->surfacePool, frame->w * frame->h * 4ULL);
|
frame->surfaceData = BufferPool_Take(priv->surfacePool, frame->w * frame->h * 4);
|
||||||
if (!frame->surfaceData)
|
if (!frame->surfaceData)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "unable to allocate frame data");
|
WLog_ERR(TAG, "unable to allocate frame data");
|
||||||
@ -930,8 +930,6 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall
|
|||||||
Stream_Read_UINT16(s, data.PacketsInSample);
|
Stream_Read_UINT16(s, data.PacketsInSample);
|
||||||
Stream_Read_UINT32(s, data.SampleNumber);
|
Stream_Read_UINT32(s, data.SampleNumber);
|
||||||
Stream_Read_UINT32(s, data.cbSample);
|
Stream_Read_UINT32(s, data.cbSample);
|
||||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, data.cbSample))
|
|
||||||
return ERROR_INVALID_DATA;
|
|
||||||
data.pSample = Stream_Pointer(s);
|
data.pSample = Stream_Pointer(s);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
7
ci/cmake-preloads/config-android.txt
Normal file
7
ci/cmake-preloads/config-android.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
message("PRELOADING android cache")
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ANDROID_NDK/build/cmake/android.toolchain.cmake" CACHE PATH "ToolChain file")
|
||||||
|
set(WITH_SANITIZE_ADDRESS ON)
|
||||||
|
set(FREERDP_EXTERNAL_SSL_PATH $ENV{ANDROID_SSL_PATH} CACHE PATH "android ssl")
|
||||||
|
# ANDROID_NDK and ANDROID_SDK must be set as environment variable
|
||||||
|
#set(ANDROID_NDK $ENV{ANDROID_SDK} CACHE PATH "Android NDK")
|
||||||
|
#set(ANDROID_SDK "${ANDROID_NDK}" CACHE PATH "android SDK")
|
||||||
11
ci/cmake-preloads/config-debian-squeeze.txt
Normal file
11
ci/cmake-preloads/config-debian-squeeze.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
message("PRELOADING cache")
|
||||||
|
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
|
||||||
|
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
|
||||||
|
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
|
||||||
|
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
|
||||||
|
set (WITH_XV OFF CACHE BOOL "xvideo support")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||||
|
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON)
|
||||||
7
ci/cmake-preloads/config-ios.txt
Normal file
7
ci/cmake-preloads/config-ios.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
message("PRELOADING iOS cache")
|
||||||
|
set (CMAKE_TOOLCHAIN_FILE "cmake/iOSToolchain.cmake" CACHE PATH "ToolChain file")
|
||||||
|
set (FREERDP_IOS_EXTERNAL_SSL_PATH $ENV{FREERDP_IOS_EXTERNAL_SSL_PATH} CACHE PATH "android ssl")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (IOS_PLATFORM "SIMULATOR" CACHE STRING "iso platfrorm to build")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON CACHE BOOL "build with address sanitizer")
|
||||||
|
set (WITH_CLIENT OFF CACHE BOOL "disable iOS client")
|
||||||
53
ci/cmake-preloads/config-linux-all.txt
Normal file
53
ci/cmake-preloads/config-linux-all.txt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
message("PRELOADING cache")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "testing")
|
||||||
|
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||||
|
set (WITH_PULSE ON CACHE BOOL "pulse")
|
||||||
|
set (WITH_CHANNELS ON CACHE BOOL "channels")
|
||||||
|
set (BUILTIN_CHANNELS ON CACHE BOOL "static channels")
|
||||||
|
set (WITH_CUPS ON CACHE BOOL "cups")
|
||||||
|
set (WITH_WAYLAND ON CACHE BOOL "wayland")
|
||||||
|
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
|
||||||
|
set (WITH_PCSC ON CACHE BOOL "PCSC")
|
||||||
|
set (WITH_JPEG ON CACHE BOOL "jpeg")
|
||||||
|
set (WITH_GSTREAMER_0_10 ON CACHE BOOL "gstreamer")
|
||||||
|
set (WITH_GSM ON CACHE BOOL "gsm")
|
||||||
|
set (CHANNEL_URBDRC ON CACHE BOOL "urbdrc")
|
||||||
|
set (CHANNEL_URBDRC_CLIENT ON CACHE BOOL "urbdrc client")
|
||||||
|
set (WITH_SERVER ON CACHE BOOL "server side")
|
||||||
|
set (WITH_DEBUG_ALL OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_CAPABILITIES OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_CERTIFICATE OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_CHANNELS OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_CLIPRDR OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RDPGFX OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_DVC OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_KBD OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_LICENSE OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_NEGO OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_NLA OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_NTLM OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RAIL OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RDP OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RDPEI OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_REDIR OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RDPDR OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_RFX OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_SCARD OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_SND OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_SVC OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_THREADS OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_TIMEZONE OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_TRANSPORT OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_TSG OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_TSMF OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_WND OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_X11 OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_X11_CLIPRDR OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_X11_LOCAL_MOVESIZE OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_DEBUG_XV OFF CACHE BOOL "enable debug")
|
||||||
|
set (WITH_SAMPLE ON CACHE BOOL "samples")
|
||||||
|
set (WITH_NO_UNDEFINED ON CACHE BOOL "don't link with undefined symbols")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON)
|
||||||
|
set (WITH_PROXY_MODULES OFF CACHE BOOL "compile proxy modules")
|
||||||
8
ci/cmake-preloads/config-macosx.txt
Normal file
8
ci/cmake-preloads/config-macosx.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
message("PRELOADING mac cache")
|
||||||
|
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (WITH_CUPS ON CACHE BOOL "CUPS printing")
|
||||||
|
set (CHANNEL_URBDRC OFF CACHE BOOL "USB redirection")
|
||||||
|
set (WITH_X11 ON CACHE BOOL "Enable X11")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON)
|
||||||
11
ci/cmake-preloads/config-ubuntu-1204.txt
Normal file
11
ci/cmake-preloads/config-ubuntu-1204.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
message("PRELOADING cache")
|
||||||
|
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
|
||||||
|
set (WITH_GSSAPI ON CACHE BOOL "Kerberos support")
|
||||||
|
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
|
||||||
|
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
|
||||||
|
set (WITH_XV OFF CACHE BOOL "xvideo support")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||||
|
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON)
|
||||||
6
ci/cmake-preloads/config-windows.txt
Normal file
6
ci/cmake-preloads/config-windows.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
message("PRELOADING windows cache")
|
||||||
|
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||||
|
set (WITH_SERVER "ON" CACHE BOOL "Build server binaries")
|
||||||
|
set (CHANNEL_URBDRC OFF CACHE BOOL "USB redirection")
|
||||||
|
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||||
|
set (WITH_SANITIZE_ADDRESS ON)
|
||||||
@ -43,10 +43,7 @@ if(FREERDP_VENDOR AND WITH_CLIENT)
|
|||||||
add_subdirectory(iOS)
|
add_subdirectory(iOS)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
option(WITH_CLIENT_MAC "Build native mac client" ON)
|
add_subdirectory(Mac)
|
||||||
if (WITH_CLIENT_MAC)
|
|
||||||
add_subdirectory(Mac)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@ -80,7 +80,6 @@ struct wlf_clipboard
|
|||||||
FILE* responseFile;
|
FILE* responseFile;
|
||||||
UINT32 responseFormat;
|
UINT32 responseFormat;
|
||||||
const char* responseMime;
|
const char* responseMime;
|
||||||
CRITICAL_SECTION lock;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static BOOL wlf_mime_is_text(const char* mime)
|
static BOOL wlf_mime_is_text(const char* mime)
|
||||||
@ -276,7 +275,7 @@ BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent*
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case UWAC_EVENT_CLIPBOARD_OFFER:
|
case UWAC_EVENT_CLIPBOARD_OFFER:
|
||||||
WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime);
|
WLog_Print(clipboard->log, WLOG_INFO, "client announces mime %s", event->mime);
|
||||||
wlf_cliprdr_add_client_format(clipboard, event->mime);
|
wlf_cliprdr_add_client_format(clipboard, event->mime);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -388,8 +387,6 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
|
|||||||
wfClipboard* clipboard = (wfClipboard*)context;
|
wfClipboard* clipboard = (wfClipboard*)context;
|
||||||
size_t x;
|
size_t x;
|
||||||
WINPR_UNUSED(seat);
|
WINPR_UNUSED(seat);
|
||||||
|
|
||||||
EnterCriticalSection(&clipboard->lock);
|
|
||||||
clipboard->responseMime = NULL;
|
clipboard->responseMime = NULL;
|
||||||
|
|
||||||
for (x = 0; x < ARRAYSIZE(mime_html); x++)
|
for (x = 0; x < ARRAYSIZE(mime_html); x++)
|
||||||
@ -430,8 +427,6 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
|
|||||||
|
|
||||||
if (clipboard->responseMime != NULL)
|
if (clipboard->responseMime != NULL)
|
||||||
{
|
{
|
||||||
if (clipboard->responseFile != NULL)
|
|
||||||
fclose(clipboard->responseFile);
|
|
||||||
clipboard->responseFile = fdopen(fd, "w");
|
clipboard->responseFile = fdopen(fd, "w");
|
||||||
|
|
||||||
if (clipboard->responseFile)
|
if (clipboard->responseFile)
|
||||||
@ -441,7 +436,6 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char*
|
|||||||
"failed to open clipboard file descriptor for MIME %s",
|
"failed to open clipboard file descriptor for MIME %s",
|
||||||
clipboard->responseMime);
|
clipboard->responseMime);
|
||||||
}
|
}
|
||||||
LeaveCriticalSection(&clipboard->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
|
static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context)
|
||||||
@ -679,8 +673,6 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData;
|
const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData;
|
||||||
wfClipboard* clipboard = (wfClipboard*)context->custom;
|
wfClipboard* clipboard = (wfClipboard*)context->custom;
|
||||||
|
|
||||||
EnterCriticalSection(&clipboard->lock);
|
|
||||||
|
|
||||||
if (size > INT_MAX * sizeof(WCHAR))
|
if (size > INT_MAX * sizeof(WCHAR))
|
||||||
return ERROR_INTERNAL_ERROR;
|
return ERROR_INTERNAL_ERROR;
|
||||||
|
|
||||||
@ -702,16 +694,10 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clipboard->responseFile)
|
fwrite(data, 1, size, clipboard->responseFile);
|
||||||
{
|
fclose(clipboard->responseFile);
|
||||||
fwrite(data, 1, size, clipboard->responseFile);
|
|
||||||
fclose(clipboard->responseFile);
|
|
||||||
clipboard->responseFile = NULL;
|
|
||||||
}
|
|
||||||
rc = CHANNEL_RC_OK;
|
rc = CHANNEL_RC_OK;
|
||||||
free(cdata);
|
free(cdata);
|
||||||
|
|
||||||
LeaveCriticalSection(&clipboard->lock);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,24 +829,17 @@ static UINT wlf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegat
|
|||||||
wfClipboard* wlf_clipboard_new(wlfContext* wfc)
|
wfClipboard* wlf_clipboard_new(wlfContext* wfc)
|
||||||
{
|
{
|
||||||
rdpChannels* channels;
|
rdpChannels* channels;
|
||||||
wfClipboard* clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
|
wfClipboard* clipboard;
|
||||||
|
|
||||||
if (!clipboard)
|
if (!(clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard))))
|
||||||
goto fail;
|
return NULL;
|
||||||
|
|
||||||
InitializeCriticalSection(&clipboard->lock);
|
|
||||||
clipboard->wfc = wfc;
|
clipboard->wfc = wfc;
|
||||||
channels = wfc->context.channels;
|
channels = wfc->context.channels;
|
||||||
clipboard->log = WLog_Get(TAG);
|
clipboard->log = WLog_Get(TAG);
|
||||||
clipboard->channels = channels;
|
clipboard->channels = channels;
|
||||||
clipboard->system = ClipboardCreate();
|
clipboard->system = ClipboardCreate();
|
||||||
if (!clipboard->system)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
|
clipboard->delegate = ClipboardGetDelegate(clipboard->system);
|
||||||
if (!clipboard->delegate)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
clipboard->delegate->custom = clipboard;
|
clipboard->delegate->custom = clipboard;
|
||||||
/* TODO: set up a filesystem base path for local URI */
|
/* TODO: set up a filesystem base path for local URI */
|
||||||
/* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */
|
/* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */
|
||||||
@ -869,10 +848,6 @@ wfClipboard* wlf_clipboard_new(wlfContext* wfc)
|
|||||||
clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success;
|
clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success;
|
||||||
clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure;
|
clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure;
|
||||||
return clipboard;
|
return clipboard;
|
||||||
|
|
||||||
fail:
|
|
||||||
wlf_clipboard_free(clipboard);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlf_clipboard_free(wfClipboard* clipboard)
|
void wlf_clipboard_free(wfClipboard* clipboard)
|
||||||
@ -883,12 +858,6 @@ void wlf_clipboard_free(wfClipboard* clipboard)
|
|||||||
wlf_cliprdr_free_server_formats(clipboard);
|
wlf_cliprdr_free_server_formats(clipboard);
|
||||||
wlf_cliprdr_free_client_formats(clipboard);
|
wlf_cliprdr_free_client_formats(clipboard);
|
||||||
ClipboardDestroy(clipboard->system);
|
ClipboardDestroy(clipboard->system);
|
||||||
|
|
||||||
EnterCriticalSection(&clipboard->lock);
|
|
||||||
if (clipboard->responseFile)
|
|
||||||
fclose(clipboard->responseFile);
|
|
||||||
LeaveCriticalSection(&clipboard->lock);
|
|
||||||
DeleteCriticalSection(&clipboard->lock);
|
|
||||||
free(clipboard);
|
free(clipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -134,44 +134,26 @@ BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent*
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
|
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
|
||||||
{
|
|
||||||
wlfContext* context;
|
|
||||||
if (!instance || !instance->context || !ev)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
context = (wlfContext*)instance->context;
|
|
||||||
ArrayList_Add(context->events, ev);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev)
|
|
||||||
{
|
|
||||||
wlfContext* context;
|
|
||||||
if (!instance || !instance->context || !ev)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
context = (wlfContext*)instance->context;
|
|
||||||
ArrayList_Add(context->events, ev);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL wlf_handle_wheel(freerdp* instance, uint32_t x, uint32_t y, uint32_t axis,
|
|
||||||
int32_t value)
|
|
||||||
{
|
{
|
||||||
rdpInput* input;
|
rdpInput* input;
|
||||||
UINT16 flags = 0;
|
UINT16 flags = 0;
|
||||||
int32_t direction;
|
int32_t direction;
|
||||||
uint32_t avalue = abs(value);
|
uint32_t x, y;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
input = instance->input;
|
if (!instance || !ev || !instance->input)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
x = ev->x;
|
||||||
|
y = ev->y;
|
||||||
|
|
||||||
if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE))
|
if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
input = instance->input;
|
input = instance->input;
|
||||||
|
|
||||||
direction = value;
|
direction = ev->value;
|
||||||
switch (axis)
|
switch (ev->axis)
|
||||||
{
|
{
|
||||||
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
case WL_POINTER_AXIS_VERTICAL_SCROLL:
|
||||||
flags |= PTR_FLAGS_WHEEL;
|
flags |= PTR_FLAGS_WHEEL;
|
||||||
@ -194,102 +176,16 @@ static BOOL wlf_handle_wheel(freerdp* instance, uint32_t x, uint32_t y, uint32_t
|
|||||||
* positive: 0 ... 0xFF -> slow ... fast
|
* positive: 0 ... 0xFF -> slow ... fast
|
||||||
* negative: 0 ... 0xFF -> fast ... slow
|
* negative: 0 ... 0xFF -> fast ... slow
|
||||||
*/
|
*/
|
||||||
|
for (i = 0; i < abs(direction); i++)
|
||||||
while (avalue > 0)
|
|
||||||
{
|
{
|
||||||
const uint32_t cval = avalue > 0xFF ? 0xFF : avalue;
|
uint32_t cflags = flags | 0x78;
|
||||||
uint32_t cflags = flags | cval;
|
|
||||||
/* Convert negative values to 9bit twos complement */
|
/* Convert negative values to 9bit twos complement */
|
||||||
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
|
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
|
||||||
cflags = (flags & 0xFF00) | (0x100 - cval);
|
cflags = (flags & 0xFF00) | (0x100 - (cflags & 0xFF));
|
||||||
if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y))
|
if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
avalue -= cval;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev)
|
|
||||||
{
|
|
||||||
BOOL success = TRUE;
|
|
||||||
BOOL handle = FALSE;
|
|
||||||
size_t x;
|
|
||||||
wlfContext* context;
|
|
||||||
enum wl_pointer_axis_source source;
|
|
||||||
|
|
||||||
if (!instance || !ev || !instance->input || !instance->context)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
context = (wlfContext*)instance->context;
|
|
||||||
|
|
||||||
for (x = 0; x < ArrayList_Count(context->events); x++)
|
|
||||||
{
|
|
||||||
UwacEvent* ev = ArrayList_GetItem(context->events, x);
|
|
||||||
if (!ev)
|
|
||||||
continue;
|
|
||||||
if (ev->type == UWAC_EVENT_POINTER_SOURCE)
|
|
||||||
{
|
|
||||||
handle = TRUE;
|
|
||||||
source = ev->mouse_source.axis_source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need source events to determine how to interpret the data */
|
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
for (x = 0; x < ArrayList_Count(context->events); x++)
|
|
||||||
{
|
|
||||||
UwacEvent* ev = ArrayList_GetItem(context->events, x);
|
|
||||||
if (!ev)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (source)
|
|
||||||
{
|
|
||||||
/* If we have a mouse wheel, just use discrete data */
|
|
||||||
case WL_POINTER_AXIS_SOURCE_WHEEL:
|
|
||||||
#if defined(WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION)
|
|
||||||
case WL_POINTER_AXIS_SOURCE_WHEEL_TILT:
|
|
||||||
#endif
|
|
||||||
if (ev->type == UWAC_EVENT_POINTER_AXIS_DISCRETE)
|
|
||||||
{
|
|
||||||
/* Get the number of steps, multiply by default step width of 120 */
|
|
||||||
int32_t val = ev->mouse_axis.value * 0x78;
|
|
||||||
/* No wheel event received, success! */
|
|
||||||
if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
|
|
||||||
ev->mouse_axis.axis, val))
|
|
||||||
success = FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
/* If we have a touch pad we get actual data, scale */
|
|
||||||
case WL_POINTER_AXIS_SOURCE_FINGER:
|
|
||||||
case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
|
|
||||||
if (ev->type == UWAC_EVENT_POINTER_AXIS)
|
|
||||||
{
|
|
||||||
double dval = wl_fixed_to_double(ev->mouse_axis.value);
|
|
||||||
int32_t val = dval * 0x78 / 10.0;
|
|
||||||
if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
|
|
||||||
ev->mouse_axis.axis, val))
|
|
||||||
success = FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ArrayList_Clear(context->events);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev)
|
|
||||||
{
|
|
||||||
wlfContext* context;
|
|
||||||
if (!instance || !instance->context || !ev)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
context = (wlfContext*)instance->context;
|
|
||||||
ArrayList_Add(context->events, ev);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,9 +30,6 @@ BOOL wlf_handle_pointer_enter(freerdp* instance, const UwacPointerEnterLeaveEven
|
|||||||
BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev);
|
BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev);
|
||||||
BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev);
|
BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev);
|
||||||
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev);
|
BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev);
|
||||||
BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev);
|
|
||||||
BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev);
|
|
||||||
BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev);
|
|
||||||
BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev);
|
BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev);
|
||||||
BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev);
|
BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev);
|
||||||
BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev);
|
BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ static BOOL wlf_Pointer_New(rdpContext* context, rdpPointer* pointer)
|
|||||||
if (!ptr)
|
if (!ptr)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
ptr->size = pointer->width * pointer->height * 4ULL;
|
ptr->size = pointer->width * pointer->height * 4;
|
||||||
ptr->data = _aligned_malloc(ptr->size, 16);
|
ptr->data = _aligned_malloc(ptr->size, 16);
|
||||||
|
|
||||||
if (!ptr->data)
|
if (!ptr->data)
|
||||||
@ -92,7 +92,7 @@ static BOOL wlf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
|
|||||||
!wlf_scale_coordinates(context, &w, &h, FALSE))
|
!wlf_scale_coordinates(context, &w, &h, FALSE))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
size = w * h * 4ULL;
|
size = w * h * 4;
|
||||||
data = malloc(size);
|
data = malloc(size);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
|
|||||||
@ -225,7 +225,6 @@ static BOOL wl_post_connect(freerdp* instance)
|
|||||||
wlfContext* context;
|
wlfContext* context;
|
||||||
rdpSettings* settings;
|
rdpSettings* settings;
|
||||||
char* title = "FreeRDP";
|
char* title = "FreeRDP";
|
||||||
char* app_id = "wlfreerdp";
|
|
||||||
UINT32 w, h;
|
UINT32 w, h;
|
||||||
|
|
||||||
if (!instance || !instance->context)
|
if (!instance || !instance->context)
|
||||||
@ -267,7 +266,6 @@ static BOOL wl_post_connect(freerdp* instance)
|
|||||||
|
|
||||||
UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen);
|
UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen);
|
||||||
UwacWindowSetTitle(window, title);
|
UwacWindowSetTitle(window, title);
|
||||||
UwacWindowSetAppId(window, app_id);
|
|
||||||
UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h);
|
UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h);
|
||||||
instance->update->BeginPaint = wl_begin_paint;
|
instance->update->BeginPaint = wl_begin_paint;
|
||||||
instance->update->EndPaint = wl_end_paint;
|
instance->update->EndPaint = wl_end_paint;
|
||||||
@ -333,15 +331,12 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case UWAC_EVENT_FRAME_DONE:
|
case UWAC_EVENT_FRAME_DONE:
|
||||||
{
|
|
||||||
UwacReturnCode r;
|
|
||||||
EnterCriticalSection(&context->critical);
|
EnterCriticalSection(&context->critical);
|
||||||
r = UwacWindowSubmitBuffer(context->window, false);
|
rc = UwacWindowSubmitBuffer(context->window, false);
|
||||||
LeaveCriticalSection(&context->critical);
|
LeaveCriticalSection(&context->critical);
|
||||||
if (r != UWAC_SUCCESS)
|
if (rc != UWAC_SUCCESS)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case UWAC_EVENT_POINTER_ENTER:
|
case UWAC_EVENT_POINTER_ENTER:
|
||||||
if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave))
|
if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave))
|
||||||
@ -362,22 +357,12 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case UWAC_EVENT_POINTER_AXIS:
|
case UWAC_EVENT_POINTER_AXIS:
|
||||||
if (!wlf_handle_pointer_axis(instance, &event.mouse_axis))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UWAC_EVENT_POINTER_AXIS_DISCRETE:
|
case UWAC_EVENT_POINTER_AXIS_DISCRETE:
|
||||||
if (!wlf_handle_pointer_axis_discrete(instance, &event.mouse_axis))
|
if (!wlf_handle_pointer_axis(instance, &event.mouse_axis))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
break;
|
|
||||||
|
|
||||||
case UWAC_EVENT_POINTER_FRAME:
|
|
||||||
if (!wlf_handle_pointer_frame(instance, &event.mouse_frame))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
case UWAC_EVENT_POINTER_SOURCE:
|
|
||||||
if (!wlf_handle_pointer_source(instance, &event.mouse_source))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UWAC_EVENT_KEY:
|
case UWAC_EVENT_KEY:
|
||||||
@ -571,37 +556,8 @@ static int wlf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wlf_client_free(freerdp* instance, rdpContext* context)
|
|
||||||
{
|
|
||||||
wlfContext* wlf = (wlfContext*)instance->context;
|
|
||||||
|
|
||||||
if (!context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (wlf->display)
|
|
||||||
UwacCloseDisplay(&wlf->display);
|
|
||||||
|
|
||||||
if (wlf->displayHandle)
|
|
||||||
CloseHandle(wlf->displayHandle);
|
|
||||||
ArrayList_Free(wlf->events);
|
|
||||||
DeleteCriticalSection(&wlf->critical);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* uwac_event_clone(const void* val)
|
|
||||||
{
|
|
||||||
UwacEvent* copy;
|
|
||||||
UwacEvent* ev = (UwacEvent*)val;
|
|
||||||
|
|
||||||
copy = calloc(1, sizeof(UwacEvent));
|
|
||||||
if (!copy)
|
|
||||||
return NULL;
|
|
||||||
*copy = *ev;
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
|
static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
|
||||||
{
|
{
|
||||||
wObject* obj;
|
|
||||||
UwacReturnCode status;
|
UwacReturnCode status;
|
||||||
wlfContext* wfl = (wlfContext*)context;
|
wlfContext* wfl = (wlfContext*)context;
|
||||||
|
|
||||||
@ -629,19 +585,26 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
|
|||||||
if (!wfl->displayHandle)
|
if (!wfl->displayHandle)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
wfl->events = ArrayList_New(FALSE);
|
|
||||||
if (!wfl->events)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
obj = ArrayList_Object(wfl->events);
|
|
||||||
obj->fnObjectNew = uwac_event_clone;
|
|
||||||
obj->fnObjectFree = free;
|
|
||||||
|
|
||||||
InitializeCriticalSection(&wfl->critical);
|
InitializeCriticalSection(&wfl->critical);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wlf_client_free(freerdp* instance, rdpContext* context)
|
||||||
|
{
|
||||||
|
wlfContext* wlf = (wlfContext*)instance->context;
|
||||||
|
|
||||||
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wlf->display)
|
||||||
|
UwacCloseDisplay(&wlf->display);
|
||||||
|
|
||||||
|
if (wlf->displayHandle)
|
||||||
|
CloseHandle(wlf->displayHandle);
|
||||||
|
DeleteCriticalSection(&wlf->critical);
|
||||||
|
}
|
||||||
|
|
||||||
static int wfl_client_start(rdpContext* context)
|
static int wfl_client_start(rdpContext* context)
|
||||||
{
|
{
|
||||||
WINPR_UNUSED(context);
|
WINPR_UNUSED(context);
|
||||||
|
|||||||
@ -53,7 +53,6 @@ struct wlf_context
|
|||||||
wlfDispContext* disp;
|
wlfDispContext* disp;
|
||||||
wLog* log;
|
wLog* log;
|
||||||
CRITICAL_SECTION critical;
|
CRITICAL_SECTION critical;
|
||||||
wArrayList* events;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP);
|
BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP);
|
||||||
|
|||||||
@ -59,9 +59,11 @@ int main(int argc, char* argv[])
|
|||||||
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
|
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
|
||||||
if (status)
|
if (status)
|
||||||
{
|
{
|
||||||
|
BOOL list = settings->ListMonitors;
|
||||||
|
|
||||||
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
|
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
|
||||||
|
|
||||||
if (settings->ListMonitors)
|
if (list)
|
||||||
xf_list_monitors(xfc);
|
xf_list_monitors(xfc);
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <winpr/sspicli.h>
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
@ -815,6 +814,8 @@ void xf_lock_x11_(xfContext* xfc, const char* fkt)
|
|||||||
else
|
else
|
||||||
XLockDisplay(xfc->display);
|
XLockDisplay(xfc->display);
|
||||||
|
|
||||||
|
if (xfc->locked)
|
||||||
|
WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt);
|
||||||
xfc->locked++;
|
xfc->locked++;
|
||||||
WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt);
|
WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt);
|
||||||
}
|
}
|
||||||
@ -1182,12 +1183,20 @@ static BOOL xf_pre_connect(freerdp* instance)
|
|||||||
|
|
||||||
if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon)
|
if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
char login_name[MAX_PATH] = { 0 };
|
char login_name[MAX_PATH] = { 0 };
|
||||||
ULONG size = sizeof(login_name) - 1;
|
|
||||||
|
|
||||||
if (GetUserNameExA(NameSamCompatible, login_name, &size))
|
#ifdef HAVE_GETLOGIN_R
|
||||||
|
rc = getlogin_r(login_name, sizeof(login_name));
|
||||||
|
#else
|
||||||
|
strncpy(login_name, getlogin(), sizeof(login_name));
|
||||||
|
rc = 0;
|
||||||
|
#endif
|
||||||
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
if (!freerdp_settings_set_string(settings, FreeRDP_Username, login_name))
|
settings->Username = _strdup(login_name);
|
||||||
|
|
||||||
|
if (!settings->Username)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username);
|
WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username);
|
||||||
@ -1254,7 +1263,6 @@ static BOOL xf_post_connect(freerdp* instance)
|
|||||||
context = instance->context;
|
context = instance->context;
|
||||||
settings = instance->settings;
|
settings = instance->settings;
|
||||||
update = context->update;
|
update = context->update;
|
||||||
BOOL serverIsWindowsPlatform;
|
|
||||||
|
|
||||||
if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
|
if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1324,8 +1332,7 @@ static BOOL xf_post_connect(freerdp* instance)
|
|||||||
update->SetKeyboardIndicators = xf_keyboard_set_indicators;
|
update->SetKeyboardIndicators = xf_keyboard_set_indicators;
|
||||||
update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
|
update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
|
||||||
|
|
||||||
serverIsWindowsPlatform = (settings->OsMajorType == OSMAJORTYPE_WINDOWS);
|
if (!(xfc->clipboard = xf_clipboard_new(xfc)))
|
||||||
if (!(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform)))
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!(xfc->xfDisp = xf_disp_new(xfc)))
|
if (!(xfc->xfDisp = xf_disp_new(xfc)))
|
||||||
@ -1384,10 +1391,7 @@ static int xf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
|||||||
const char* str_data = freerdp_get_logon_error_info_data(data);
|
const char* str_data = freerdp_get_logon_error_info_data(data);
|
||||||
const char* str_type = freerdp_get_logon_error_info_type(type);
|
const char* str_type = freerdp_get_logon_error_info_type(type);
|
||||||
WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
|
WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
|
||||||
if(type != LOGON_MSG_SESSION_CONTINUE)
|
xf_rail_disable_remoteapp_mode(xfc);
|
||||||
{
|
|
||||||
xf_rail_disable_remoteapp_mode(xfc);
|
|
||||||
}
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1397,8 +1401,11 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
|
|||||||
DWORD status;
|
DWORD status;
|
||||||
DWORD nCount;
|
DWORD nCount;
|
||||||
HANDLE events[3];
|
HANDLE events[3];
|
||||||
|
XEvent xevent;
|
||||||
wMessage msg;
|
wMessage msg;
|
||||||
wMessageQueue* queue;
|
wMessageQueue* queue;
|
||||||
|
int pending_status = 1;
|
||||||
|
int process_status = 1;
|
||||||
freerdp* instance = (freerdp*)arg;
|
freerdp* instance = (freerdp*)arg;
|
||||||
xfContext* xfc = (xfContext*)instance->context;
|
xfContext* xfc = (xfContext*)instance->context;
|
||||||
queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
|
queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
|
||||||
@ -1427,7 +1434,26 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
|
|||||||
|
|
||||||
if (WaitForSingleObject(events[1], 0) == WAIT_OBJECT_0)
|
if (WaitForSingleObject(events[1], 0) == WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
if (!xf_process_x_events(xfc->context.instance))
|
do
|
||||||
|
{
|
||||||
|
xf_lock_x11(xfc);
|
||||||
|
pending_status = XPending(xfc->display);
|
||||||
|
xf_unlock_x11(xfc);
|
||||||
|
|
||||||
|
if (pending_status)
|
||||||
|
{
|
||||||
|
xf_lock_x11(xfc);
|
||||||
|
ZeroMemory(&xevent, sizeof(xevent));
|
||||||
|
XNextEvent(xfc->display, &xevent);
|
||||||
|
process_status = xf_event_process(instance, &xevent);
|
||||||
|
xf_unlock_x11(xfc);
|
||||||
|
|
||||||
|
if (!process_status)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (pending_status);
|
||||||
|
|
||||||
|
if (!process_status)
|
||||||
{
|
{
|
||||||
running = FALSE;
|
running = FALSE;
|
||||||
break;
|
break;
|
||||||
@ -1446,7 +1472,6 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MessageQueue_PostQuit(queue, 0);
|
MessageQueue_PostQuit(queue, 0);
|
||||||
freerdp_abort_connect(xfc->context.instance);
|
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1464,7 +1489,7 @@ static BOOL handle_window_events(freerdp* instance)
|
|||||||
{
|
{
|
||||||
if (!xf_process_x_events(instance))
|
if (!xf_process_x_events(instance))
|
||||||
{
|
{
|
||||||
WLog_DBG(TAG, "Closed from X11");
|
WLog_INFO(TAG, "Closed from X11");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1508,84 +1533,6 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
|||||||
else if (freerdp_get_last_error(instance->context) ==
|
else if (freerdp_get_last_error(instance->context) ==
|
||||||
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
|
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
|
||||||
exit_code = XF_EXIT_NEGO_FAILURE;
|
exit_code = XF_EXIT_NEGO_FAILURE;
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_LOGON_FAILURE)
|
|
||||||
exit_code = XF_EXIT_LOGON_FAILURE;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT)
|
|
||||||
exit_code = XF_EXIT_ACCOUNT_LOCKED_OUT;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_PRE_CONNECT_FAILED)
|
|
||||||
exit_code = XF_EXIT_PRE_CONNECT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_UNDEFINED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_UNDEFINED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_POST_CONNECT_FAILED)
|
|
||||||
exit_code = XF_EXIT_POST_CONNECT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_DNS_ERROR)
|
|
||||||
exit_code = XF_EXIT_DNS_ERROR;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_DNS_NAME_NOT_FOUND)
|
|
||||||
exit_code = XF_EXIT_DNS_NAME_NOT_FOUND;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_FAILED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR)
|
|
||||||
exit_code = XF_EXIT_MCS_CONNECT_INITIAL_ERROR;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_TLS_CONNECT_FAILED)
|
|
||||||
exit_code = XF_EXIT_TLS_CONNECT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_INSUFFICIENT_PRIVILEGES)
|
|
||||||
exit_code = XF_EXIT_INSUFFICIENT_PRIVILEGES;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_CANCELLED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_CANCELLED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
|
|
||||||
exit_code = XF_EXIT_SECURITY_NEGO_CONNECT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_TRANSPORT_FAILED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_TRANSPORT_FAILED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_PASSWORD_EXPIRED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE)
|
|
||||||
exit_code = XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_KDC_UNREACHABLE)
|
|
||||||
exit_code = XF_EXIT_CONNECT_KDC_UNREACHABLE;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_ACCOUNT_DISABLED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_CLIENT_REVOKED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_CLIENT_REVOKED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_WRONG_PASSWORD)
|
|
||||||
exit_code = XF_EXIT_CONNECT_WRONG_PASSWORD;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_ACCESS_DENIED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_ACCESS_DENIED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION)
|
|
||||||
exit_code = XF_EXIT_CONNECT_ACCOUNT_RESTRICTION;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_ACCOUNT_EXPIRED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED)
|
|
||||||
exit_code = XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED;
|
|
||||||
else if (freerdp_get_last_error(instance->context) ==
|
|
||||||
FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS)
|
|
||||||
exit_code = XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS;
|
|
||||||
else
|
else
|
||||||
exit_code = XF_EXIT_CONN_FAILED;
|
exit_code = XF_EXIT_CONN_FAILED;
|
||||||
}
|
}
|
||||||
@ -1750,7 +1697,7 @@ end:
|
|||||||
|
|
||||||
DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
|
DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
|
||||||
{
|
{
|
||||||
if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS))
|
if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_NEGO_FAILURE))
|
||||||
return reason;
|
return reason;
|
||||||
/* License error set */
|
/* License error set */
|
||||||
else if (reason >= 0x100 && reason <= 0x10A)
|
else if (reason >= 0x100 && reason <= 0x10A)
|
||||||
|
|||||||
@ -1034,8 +1034,7 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard,
|
|||||||
|
|
||||||
if (!delayRespond)
|
if (!delayRespond)
|
||||||
{
|
{
|
||||||
union
|
union {
|
||||||
{
|
|
||||||
XEvent* ev;
|
XEvent* ev;
|
||||||
XSelectionEvent* sev;
|
XSelectionEvent* sev;
|
||||||
} conv;
|
} conv;
|
||||||
@ -1424,10 +1423,7 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
|
ret = xf_cliprdr_send_client_format_list_response(clipboard, TRUE);
|
||||||
if (xfc->remote_app)
|
xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
|
||||||
xf_cliprdr_set_selection_owner(xfc, clipboard, CurrentTime);
|
|
||||||
else
|
|
||||||
xf_cliprdr_prepare_to_set_selection_owner(xfc, clipboard);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1570,7 +1566,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
{
|
{
|
||||||
if (SrcSize == 0)
|
if (SrcSize == 0)
|
||||||
{
|
{
|
||||||
WLog_DBG(TAG, "skipping, empty data detected!");
|
WLog_INFO(TAG, "skipping, empty data detected!!!");
|
||||||
free(clipboard->respond);
|
free(clipboard->respond);
|
||||||
clipboard->respond = NULL;
|
clipboard->respond = NULL;
|
||||||
return CHANNEL_RC_OK;
|
return CHANNEL_RC_OK;
|
||||||
@ -1615,8 +1611,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context,
|
|||||||
|
|
||||||
xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize);
|
xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize);
|
||||||
{
|
{
|
||||||
union
|
union {
|
||||||
{
|
|
||||||
XEvent* ev;
|
XEvent* ev;
|
||||||
XSelectionEvent* sev;
|
XSelectionEvent* sev;
|
||||||
} conv;
|
} conv;
|
||||||
@ -1756,27 +1751,7 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate
|
|||||||
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
|
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL xf_cliprdr_clipboard_is_valid_unix_filename(LPCWSTR filename)
|
xfClipboard* xf_clipboard_new(xfContext* xfc)
|
||||||
{
|
|
||||||
LPCWSTR c;
|
|
||||||
|
|
||||||
if (!filename)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (filename[0] == L'\0')
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Reserved characters */
|
|
||||||
for (c = filename; *c; ++c)
|
|
||||||
{
|
|
||||||
if (*c == L'/')
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction)
|
|
||||||
{
|
{
|
||||||
int i, n = 0;
|
int i, n = 0;
|
||||||
rdpChannels* channels;
|
rdpChannels* channels;
|
||||||
@ -1905,13 +1880,6 @@ xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction)
|
|||||||
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
|
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
|
||||||
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
|
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
|
||||||
clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure;
|
clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure;
|
||||||
|
|
||||||
if (relieveFilenameRestriction)
|
|
||||||
{
|
|
||||||
WLog_DBG(TAG, "Relieving CLIPRDR filename restriction");
|
|
||||||
clipboard->delegate->IsFileNameComponentValid = xf_cliprdr_clipboard_is_valid_unix_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
return clipboard;
|
return clipboard;
|
||||||
error:
|
error:
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#include <freerdp/client/cliprdr.h>
|
#include <freerdp/client/cliprdr.h>
|
||||||
|
|
||||||
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction);
|
xfClipboard* xf_clipboard_new(xfContext* xfc);
|
||||||
void xf_clipboard_free(xfClipboard* clipboard);
|
void xf_clipboard_free(xfClipboard* clipboard);
|
||||||
|
|
||||||
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr);
|
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr);
|
||||||
|
|||||||
@ -93,7 +93,7 @@ static BOOL xf_update_last_sent(xfDispContext* xfDisp)
|
|||||||
|
|
||||||
static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
|
static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
|
||||||
{
|
{
|
||||||
DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 };
|
DISPLAY_CONTROL_MONITOR_LAYOUT layout;
|
||||||
xfContext* xfc;
|
xfContext* xfc;
|
||||||
rdpSettings* settings;
|
rdpSettings* settings;
|
||||||
|
|
||||||
@ -242,24 +242,7 @@ static void xf_disp_OnTimer(void* context, TimerEventArgs* e)
|
|||||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!xfDisp->activated || xfc->fullscreen)
|
if (!xfDisp->activated || settings->Fullscreen)
|
||||||
return;
|
|
||||||
|
|
||||||
xf_disp_sendResize(xfDisp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xf_disp_OnWindowStateChange(void* context, WindowStateChangeEventArgs* e)
|
|
||||||
{
|
|
||||||
xfContext* xfc;
|
|
||||||
xfDispContext* xfDisp;
|
|
||||||
rdpSettings* settings;
|
|
||||||
|
|
||||||
WINPR_UNUSED(e);
|
|
||||||
|
|
||||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!xfDisp->activated || !xfc->fullscreen)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
xf_disp_sendResize(xfDisp);
|
xf_disp_sendResize(xfDisp);
|
||||||
@ -291,7 +274,6 @@ xfDispContext* xf_disp_new(xfContext* xfc)
|
|||||||
PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated);
|
PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated);
|
||||||
PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
||||||
PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer);
|
PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer);
|
||||||
PubSub_SubscribeWindowStateChange(xfc->context.pubSub, xf_disp_OnWindowStateChange);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +287,6 @@ void xf_disp_free(xfDispContext* disp)
|
|||||||
PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
|
PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
|
||||||
PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
||||||
PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
|
PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
|
||||||
PubSub_UnsubscribeWindowStateChange(disp->xfc->context.pubSub, xf_disp_OnWindowStateChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(disp);
|
free(disp);
|
||||||
|
|||||||
@ -24,8 +24,6 @@
|
|||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
#include <winpr/assert.h>
|
|
||||||
|
|
||||||
#include <freerdp/log.h>
|
#include <freerdp/log.h>
|
||||||
#include <freerdp/locale/keyboard.h>
|
#include <freerdp/locale/keyboard.h>
|
||||||
|
|
||||||
@ -35,7 +33,6 @@
|
|||||||
#include "xf_disp.h"
|
#include "xf_disp.h"
|
||||||
#include "xf_input.h"
|
#include "xf_input.h"
|
||||||
#include "xf_gfx.h"
|
#include "xf_gfx.h"
|
||||||
#include "xf_graphics.h"
|
|
||||||
|
|
||||||
#include "xf_event.h"
|
#include "xf_event.h"
|
||||||
#include "xf_input.h"
|
#include "xf_input.h"
|
||||||
@ -265,37 +262,6 @@ static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xf_adjust_coordinates_to_screen(xfContext* xfc, UINT32* x, UINT32* y)
|
|
||||||
{
|
|
||||||
rdpSettings* settings;
|
|
||||||
INT64 tx, ty;
|
|
||||||
|
|
||||||
if (!xfc || !xfc->context.settings || !y || !x)
|
|
||||||
return;
|
|
||||||
|
|
||||||
settings = xfc->context.settings;
|
|
||||||
tx = *x;
|
|
||||||
ty = *y;
|
|
||||||
if (!xfc->remote_app)
|
|
||||||
{
|
|
||||||
#ifdef WITH_XRENDER
|
|
||||||
|
|
||||||
if (xf_picture_transform_required(xfc))
|
|
||||||
{
|
|
||||||
double xScalingFactor = xfc->scaledWidth / (double)settings->DesktopWidth;
|
|
||||||
double yScalingFactor = xfc->scaledHeight / (double)settings->DesktopHeight;
|
|
||||||
tx = ((tx + xfc->offset_x) * xScalingFactor);
|
|
||||||
ty = ((ty + xfc->offset_y) * yScalingFactor);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
CLAMP_COORDINATES(tx, ty);
|
|
||||||
*x = tx;
|
|
||||||
*y = ty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
|
void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
|
||||||
{
|
{
|
||||||
rdpSettings* settings;
|
rdpSettings* settings;
|
||||||
@ -521,29 +487,6 @@ static BOOL xf_event_KeyRelease(xfContext* xfc, const XKeyEvent* event, BOOL app
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release a key, but ignore the event in case of autorepeat.
|
|
||||||
*/
|
|
||||||
static BOOL xf_event_KeyReleaseOrIgnore(xfContext* xfc, const XKeyEvent* event, BOOL app)
|
|
||||||
{
|
|
||||||
WINPR_ASSERT(xfc);
|
|
||||||
WINPR_ASSERT(event);
|
|
||||||
|
|
||||||
if ((event->type == KeyRelease) && XEventsQueued(xfc->display, QueuedAfterReading))
|
|
||||||
{
|
|
||||||
XEvent nev = { 0 };
|
|
||||||
XPeekEvent(xfc->display, &nev);
|
|
||||||
|
|
||||||
if ((nev.type == KeyPress) && (nev.xkey.time == event->time) &&
|
|
||||||
(nev.xkey.keycode == event->keycode))
|
|
||||||
{
|
|
||||||
/* Key wasn’t actually released */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return xf_event_KeyRelease(xfc, event, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app)
|
static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app)
|
||||||
{
|
{
|
||||||
if (event->mode == NotifyGrab)
|
if (event->mode == NotifyGrab)
|
||||||
@ -563,7 +506,6 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap
|
|||||||
/* Release all keys, should already be done at FocusOut but might be missed
|
/* Release all keys, should already be done at FocusOut but might be missed
|
||||||
* if the WM decided to use an alternate event order */
|
* if the WM decided to use an alternate event order */
|
||||||
xf_keyboard_release_all_keypress(xfc);
|
xf_keyboard_release_all_keypress(xfc);
|
||||||
xf_pointer_update_scale(xfc);
|
|
||||||
|
|
||||||
if (app)
|
if (app)
|
||||||
{
|
{
|
||||||
@ -695,9 +637,6 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
|
|||||||
rdpSettings* settings;
|
rdpSettings* settings;
|
||||||
settings = xfc->context.settings;
|
settings = xfc->context.settings;
|
||||||
|
|
||||||
WLog_DBG(TAG, "%s: x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32, __func__, event->x,
|
|
||||||
event->y, event->width, event->height);
|
|
||||||
|
|
||||||
if (!app)
|
if (!app)
|
||||||
{
|
{
|
||||||
if (!xfc->window)
|
if (!xfc->window)
|
||||||
@ -740,42 +679,43 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
|
|||||||
/* ask the server to resize using the display channel */
|
/* ask the server to resize using the display channel */
|
||||||
xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
|
xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
|
||||||
|
|
||||||
|
if (appWindow)
|
||||||
{
|
{
|
||||||
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
|
/*
|
||||||
|
* ConfigureNotify coordinates are expressed relative to the window parent.
|
||||||
|
* Translate these to root window coordinates.
|
||||||
|
*/
|
||||||
|
XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), 0,
|
||||||
|
0, &appWindow->x, &appWindow->y, &childWindow);
|
||||||
|
appWindow->width = event->width;
|
||||||
|
appWindow->height = event->height;
|
||||||
|
|
||||||
if (appWindow)
|
/*
|
||||||
|
* Additional checks for not in a local move and not ignoring configure to send
|
||||||
|
* position update to server, also should the window not be focused then do not
|
||||||
|
* send to server yet (i.e. resizing using window decoration).
|
||||||
|
* The server will be updated when the window gets refocused.
|
||||||
|
*/
|
||||||
|
if (appWindow->decorations)
|
||||||
{
|
{
|
||||||
/*
|
/* moving resizing using window decoration */
|
||||||
* ConfigureNotify coordinates are expressed relative to the window parent.
|
xf_rail_adjust_position(xfc, appWindow);
|
||||||
* Translate these to root window coordinates.
|
}
|
||||||
*/
|
else
|
||||||
XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
|
{
|
||||||
0, 0, &appWindow->x, &appWindow->y, &childWindow);
|
if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
|
||||||
appWindow->width = event->width;
|
!appWindow->rail_ignore_configure && xfc->focused)
|
||||||
appWindow->height = event->height;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Additional checks for not in a local move and not ignoring configure to send
|
|
||||||
* position update to server, also should the window not be focused then do not
|
|
||||||
* send to server yet (i.e. resizing using window decoration).
|
|
||||||
* The server will be updated when the window gets refocused.
|
|
||||||
*/
|
|
||||||
if (appWindow->decorations)
|
|
||||||
{
|
|
||||||
/* moving resizing using window decoration */
|
|
||||||
xf_rail_adjust_position(xfc, appWindow);
|
xf_rail_adjust_position(xfc, appWindow);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
|
|
||||||
!appWindow->rail_ignore_configure && xfc->focused)
|
|
||||||
xf_rail_adjust_position(xfc, appWindow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return xf_pointer_update_scale(xfc);
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
|
static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
|
||||||
@ -788,14 +728,14 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
|
|||||||
{
|
{
|
||||||
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
|
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
|
||||||
|
|
||||||
if (appWindow && (appWindow->rail_state == WINDOW_SHOW))
|
if (appWindow)
|
||||||
{
|
{
|
||||||
/* local restore event */
|
/* local restore event */
|
||||||
/* This is now handled as part of the PropertyNotify
|
/* This is now handled as part of the PropertyNotify
|
||||||
* Doing this here would inhibit the ability to restore a maximized window
|
* Doing this here would inhibit the ability to restore a maximized window
|
||||||
* that is minimized back to the maximized state
|
* that is minimized back to the maximized state
|
||||||
*/
|
*/
|
||||||
// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
|
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
|
||||||
appWindow->is_mapped = TRUE;
|
appWindow->is_mapped = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -806,10 +746,6 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
|
|||||||
static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
|
static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
|
||||||
{
|
{
|
||||||
xfAppWindow* appWindow;
|
xfAppWindow* appWindow;
|
||||||
|
|
||||||
WINPR_ASSERT(xfc);
|
|
||||||
WINPR_ASSERT(event);
|
|
||||||
|
|
||||||
xf_keyboard_release_all_keypress(xfc);
|
xf_keyboard_release_all_keypress(xfc);
|
||||||
|
|
||||||
if (!app)
|
if (!app)
|
||||||
@ -829,9 +765,6 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
|
|||||||
|
|
||||||
static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
|
static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(xfc);
|
|
||||||
WINPR_ASSERT(event);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This section handles sending the appropriate commands to the rail server
|
* This section handles sending the appropriate commands to the rail server
|
||||||
* when the window has been minimized, maximized, restored locally
|
* when the window has been minimized, maximized, restored locally
|
||||||
@ -866,27 +799,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
|
|||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
{
|
{
|
||||||
if (appWindow)
|
|
||||||
{
|
|
||||||
appWindow->maxVert = FALSE;
|
|
||||||
appWindow->maxHorz = FALSE;
|
|
||||||
}
|
|
||||||
for (i = 0; i < nitems; i++)
|
for (i = 0; i < nitems; i++)
|
||||||
{
|
{
|
||||||
if ((Atom)((UINT16**)prop)[i] ==
|
if ((Atom)((UINT16**)prop)[i] ==
|
||||||
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False))
|
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False))
|
||||||
{
|
{
|
||||||
maxVert = TRUE;
|
maxVert = TRUE;
|
||||||
if (appWindow)
|
|
||||||
appWindow->maxVert = TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Atom)((UINT16**)prop)[i] ==
|
if ((Atom)((UINT16**)prop)[i] ==
|
||||||
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False))
|
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False))
|
||||||
{
|
{
|
||||||
maxHorz = TRUE;
|
maxHorz = TRUE;
|
||||||
if (appWindow)
|
|
||||||
appWindow->maxHorz = TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,17 +827,9 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
|
|||||||
{
|
{
|
||||||
/* If the window is in the iconic state */
|
/* If the window is in the iconic state */
|
||||||
if (((UINT32)*prop == 3))
|
if (((UINT32)*prop == 3))
|
||||||
{
|
|
||||||
minimized = TRUE;
|
minimized = TRUE;
|
||||||
if (appWindow)
|
|
||||||
appWindow->minimized = TRUE;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
minimized = FALSE;
|
minimized = FALSE;
|
||||||
if (appWindow)
|
|
||||||
appWindow->minimized = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
minimizedChanged = TRUE;
|
minimizedChanged = TRUE;
|
||||||
XFree(prop);
|
XFree(prop);
|
||||||
@ -922,30 +838,22 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
|
|||||||
|
|
||||||
if (app)
|
if (app)
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(appWindow);
|
if (maxVert && maxHorz && !minimized &&
|
||||||
if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized)
|
(appWindow->rail_state != WINDOW_SHOW_MAXIMIZED))
|
||||||
{
|
{
|
||||||
if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)
|
appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
|
||||||
{
|
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
|
||||||
appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
|
|
||||||
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (appWindow->minimized)
|
else if (minimized && (appWindow->rail_state != WINDOW_SHOW_MINIMIZED))
|
||||||
{
|
{
|
||||||
if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
|
appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
|
||||||
{
|
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
|
||||||
appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
|
|
||||||
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (!minimized && !maxVert && !maxHorz && (appWindow->rail_state != WINDOW_SHOW) &&
|
||||||
|
(appWindow->rail_state != WINDOW_HIDE))
|
||||||
{
|
{
|
||||||
if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE)
|
appWindow->rail_state = WINDOW_SHOW;
|
||||||
{
|
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
|
||||||
appWindow->rail_state = WINDOW_SHOW;
|
|
||||||
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (minimizedChanged)
|
else if (minimizedChanged)
|
||||||
@ -1109,7 +1017,7 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
status = xf_event_KeyReleaseOrIgnore(xfc, &event->xkey, xfc->remote_app);
|
status = xf_event_KeyRelease(xfc, &event->xkey, xfc->remote_app);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FocusIn:
|
case FocusIn:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user