New upstream version 2.0.0~git20181120.1.e21b72c95+dfsg1
This commit is contained in:
parent
0b837fff17
commit
5bc908def1
1
.source_version
Normal file
1
.source_version
Normal file
@ -0,0 +1 @@
|
||||
e21b72c95+debian
|
||||
213
CMakeLists.txt
213
CMakeLists.txt
@ -68,6 +68,7 @@ include(GNUInstallDirsWrapper)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(InstallFreeRDPMan)
|
||||
include(GetGitRevisionDescription)
|
||||
include(SetFreeRDPCMakeInstallDir)
|
||||
|
||||
# Soname versioning
|
||||
set(BUILD_NUMBER 0)
|
||||
@ -76,7 +77,7 @@ if ($ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
set(WITH_LIBRARY_VERSIONING "ON")
|
||||
|
||||
set(RAW_VERSTION_STRING "2.0.0-rc2")
|
||||
set(RAW_VERSTION_STRING "2.0.0-rc4")
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSTION_STRING)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
@ -198,6 +199,16 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "DragonFly")
|
||||
set(BSD TRUE)
|
||||
set(FREEBSD TRUE)
|
||||
endif()
|
||||
|
||||
if(FREEBSD)
|
||||
find_path(EPOLLSHIM_INCLUDE_DIR NAMES sys/epoll.h sys/timerfd.h HINTS /usr/local/include/libepoll-shim)
|
||||
find_library(EPOLLSHIM_LIBS NAMES epoll-shim libepoll-shim HINTS /usr/local/lib)
|
||||
endif()
|
||||
|
||||
# Configure MSVC Runtime
|
||||
if(MSVC)
|
||||
include(MSVCRuntime)
|
||||
@ -219,8 +230,8 @@ if(MSVC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable 64bit file support on linux.
|
||||
if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
|
||||
# Enable 64bit file support on linux and FreeBSD.
|
||||
if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux" OR FREEBSD)
|
||||
add_definitions("-D_FILE_OFFSET_BITS=64")
|
||||
endif()
|
||||
|
||||
@ -328,102 +339,104 @@ endif()
|
||||
|
||||
# Enable address sanitizer, where supported and when required
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_REQUIRED_FLAGS_SAVED ${CMAKE_REQUIRED_FLAGS})
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/foo.txt "")
|
||||
if(WITH_SANITIZE_ADDRESS)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=address" fsanitize-address)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-address-use-after-scope" fsanitize-address-use-after-scope)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(fsanitize-address)
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize-address-use-after-scope")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
else()
|
||||
message(FATAL_ERROR "Missing support for address sanitizer!")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-address-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
|
||||
if(fsanitize-address-use-after-scope)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-address-use-after-scope")
|
||||
endif(fsanitize-address-use-after-scope)
|
||||
|
||||
else(fsanitize-address)
|
||||
message(WARNING "Missing support for address sanitizer!")
|
||||
endif(fsanitize-address)
|
||||
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
elseif(WITH_SANITIZE_MEMORY)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=memory")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=memory" fsanitize-memory)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-memory-use-after-dtor" fsanitize-memory-use-after-dtor)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-memory-track-origins" fsanitize-memory-track-origins)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(fsanitize-memory)
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=memory")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory")
|
||||
else()
|
||||
message(FATAL_ERROR "Missing support for memory sanitizer!")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-memory-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
|
||||
if (fsanitize-memory-use-after-dtor)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-use-after-dtor")
|
||||
endif(fsanitize-memory-use-after-dtor)
|
||||
|
||||
if (fsanitize-memory-track-origins)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
|
||||
endif(fsanitize-memory-track-origins)
|
||||
|
||||
else(fsanitize-memory)
|
||||
message(WARNING "Missing support for memory sanitizer!")
|
||||
endif(fsanitize-memory)
|
||||
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
elseif(WITH_SANITIZE_THREAD)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=thread")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=thread" fsanitize-thread)
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize-blacklist=${CMAKE_BINARY_DIR}/foo.txt" fsanitize-blacklist)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if(fsanitize-thread)
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-thread-sanitizer.txt")
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=thread")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
|
||||
if(fsanitize-blacklist)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/scripts/blacklist-thread-sanitizer.txt")
|
||||
endif(fsanitize-blacklist)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=thread")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread")
|
||||
else()
|
||||
message(FATAL_ERROR "Missing support for thread sanitizer!")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
else(fsanitize-thread)
|
||||
message(WARNING "Missing support for thread sanitizer!")
|
||||
endif(fsanitize-thread)
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(REMOVE ${CMAKE_BINARY_DIR}/foo.txt)
|
||||
|
||||
if (WITH_NO_UNDEFINED)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-Wl,--no-undefined")
|
||||
CHECK_C_COMPILER_FLAG (-Wl,--no-undefined no-undefined)
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
unset(SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(no-undefined)
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVED})
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
@ -469,7 +482,7 @@ if(WIN32)
|
||||
add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
set(CMAKE_USE_RELATIVE_PATH ON)
|
||||
if (${CMAKE_GENERATOR} MATCHES "NMake Makefile*")
|
||||
if (${CMAKE_GENERATOR} MATCHES "NMake Makefile*" OR ${CMAKE_GENERATOR} MATCHES "Ninja*")
|
||||
set(CMAKE_PDB_BINARY_DIR ${CMAKE_BINARY_DIR})
|
||||
elseif (${CMAKE_GENERATOR} MATCHES "Visual Studio*")
|
||||
set(CMAKE_PDB_BINARY_DIR "${CMAKE_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}")
|
||||
@ -547,11 +560,6 @@ if(APPLE)
|
||||
if(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
|
||||
set(CMAKE_OSX_ARCHITECTURES i386 x86_64)
|
||||
endif()
|
||||
|
||||
if(IS_DIRECTORY /opt/local/include)
|
||||
include_directories(/opt/local/include)
|
||||
link_directories(/opt/local/lib)
|
||||
endif()
|
||||
endif(IOS)
|
||||
|
||||
# Temporarily disabled, causes the cmake script to be reexecuted, causing the compilation to fail.
|
||||
@ -646,7 +654,13 @@ if(UNIX OR CYGWIN)
|
||||
if (HAVE_SYS_EVENTFD_H)
|
||||
check_symbol_exists(eventfd_read sys/eventfd.h WITH_EVENTFD_READ_WRITE)
|
||||
endif()
|
||||
if (FREEBSD)
|
||||
list(APPEND CMAKE_REQUIRED_INCLUDES ${EPOLLSHIM_INCLUDE_DIR})
|
||||
endif()
|
||||
check_include_files(sys/timerfd.h HAVE_SYS_TIMERFD_H)
|
||||
if (FREEBSD)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES ${EPOLLSHIM_INCLUDE_DIR})
|
||||
endif()
|
||||
check_include_files(poll.h HAVE_POLL_H)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
|
||||
check_symbol_exists(ceill math.h HAVE_MATH_C99_LONG_DOUBLE)
|
||||
@ -744,6 +758,22 @@ set(GSM_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSM_FEATURE_PURPOSE "codec")
|
||||
set(GSM_FEATURE_DESCRIPTION "GSM audio codec library")
|
||||
|
||||
set(LAME_FEATURE_TYPE "OPTIONAL")
|
||||
set(LAME_FEATURE_PURPOSE "codec")
|
||||
set(LAME_FEATURE_DESCRIPTION "lame MP3 audio codec library")
|
||||
|
||||
set(FAAD2_FEATURE_TYPE "OPTIONAL")
|
||||
set(FAAD2_FEATURE_PURPOSE "codec")
|
||||
set(FAAD2_FEATURE_DESCRIPTION "FAAD2 AAC audio codec library")
|
||||
|
||||
set(FAAC_FEATURE_TYPE "OPTIONAL")
|
||||
set(FAAC_FEATURE_PURPOSE "codec")
|
||||
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library")
|
||||
|
||||
set(SOXR_FEATURE_TYPE "OPTIONAL")
|
||||
set(SOXR_FEATURE_PURPOSE "codec")
|
||||
set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
|
||||
|
||||
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSSAPI_FEATURE_PURPOSE "auth")
|
||||
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support")
|
||||
@ -844,13 +874,54 @@ find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DE
|
||||
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||
find_feature(LAME ${LAME_FEATURE_TYPE} ${LAME_FEATURE_PURPOSE} ${LAME_FEATURE_DESCRIPTION})
|
||||
find_feature(FAAD2 ${FAAD2_FEATURE_TYPE} ${FAAD2_FEATURE_PURPOSE} ${FAAD2_FEATURE_DESCRIPTION})
|
||||
find_feature(FAAC ${FAAC_FEATURE_TYPE} ${FAAC_FEATURE_PURPOSE} ${FAAC_FEATURE_DESCRIPTION})
|
||||
find_feature(soxr ${SOXR_FEATURE_TYPE} ${SOXR_FEATURE_PURPOSE} ${SOXR_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(GSSAPI ${GSSAPI_FEATURE_TYPE} ${GSSAPI_FEATURE_PURPOSE} ${GSSAPI_FEATURE_DESCRIPTION})
|
||||
|
||||
if ((WITH_FFMPEG OR WITH_DSP_FFMPEG) AND NOT FFMPEG_FOUND)
|
||||
message(FATAL_ERROR "FFMPEG support requested but not detected")
|
||||
endif()
|
||||
set(WITH_FFMPEG ${FFMPEG_FOUND})
|
||||
|
||||
# Version check, if we have detected FFMPEG but the version is too old
|
||||
# deactivate it as sound backend.
|
||||
if (WITH_DSP_FFMPEG)
|
||||
# Deactivate FFmpeg backend for sound, if the version is too old.
|
||||
# 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]+")
|
||||
FOREACH(item ${AV_VERSION_FILE})
|
||||
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
||||
IF(litem)
|
||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
list(GET VSPLIT_LINE 0 VNAME)
|
||||
list(GET VSPLIT_LINE 1 VVALUE)
|
||||
set(${VNAME} ${VVALUE})
|
||||
ENDIF(litem)
|
||||
ENDFOREACH(item ${AV_VERSION_FILE})
|
||||
|
||||
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
||||
if (AVCODEC_VERSION VERSION_LESS "57.48.101")
|
||||
message(WARNING "FFmpeg version detected (${AVCODEC_VERSION}) is too old. (Require at least 57.48.101 for sound). Deactivating")
|
||||
set(WITH_DSP_FFMPEG OFF)
|
||||
endif()
|
||||
endif (WITH_DSP_FFMPEG)
|
||||
|
||||
if (WITH_OPENH264 AND NOT OPENH264_FOUND)
|
||||
message(FATAL_ERROR "OpenH264 support requested but not detected")
|
||||
endif()
|
||||
set(WITH_OPENH264 ${OPENH264_FOUND})
|
||||
|
||||
if ( (WITH_GSSAPI) AND (NOT GSS_FOUND))
|
||||
message(WARNING "-DWITH_GSSAPI=ON is set, but not GSSAPI implementation was found, disabling")
|
||||
elseif(WITH_GSSAPI)
|
||||
if(GSS_FLAVOUR STREQUAL "MIT")
|
||||
if(GSS_FLAVOUR STREQUAL "MIT")
|
||||
add_definitions("-DWITH_GSSAPI -DWITH_GSSAPI_MIT")
|
||||
if(GSS_VERSION_1_13)
|
||||
add_definitions("-DHAVE_AT_LEAST_KRB_V1_13")
|
||||
@ -926,7 +997,9 @@ if (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
|
||||
else (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||
if (NOT FREEBSD)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..")
|
||||
endif()
|
||||
endif(APPLE)
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
@ -969,6 +1042,10 @@ else()
|
||||
set(PRIVATE_KEYWORD "PRIVATE")
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_DLL")
|
||||
endif()
|
||||
|
||||
add_subdirectory(winpr)
|
||||
|
||||
# Sub-directories
|
||||
|
||||
2
README
2
README
@ -8,7 +8,7 @@ interoperability can finally liberate your computing experience.
|
||||
Resources
|
||||
---------
|
||||
|
||||
Project website: http://www.freerdp.com/
|
||||
Project website: https://www.freerdp.com/
|
||||
Issue tracker: https://github.com/FreeRDP/FreeRDP/issues
|
||||
Sources: https://github.com/FreeRDP/FreeRDP/
|
||||
Downloads: https://pub.freerdp.com/releases/
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
@ -46,229 +46,129 @@ typedef struct _AudinALSADevice
|
||||
|
||||
char* device_name;
|
||||
UINT32 frames_per_packet;
|
||||
UINT32 target_rate;
|
||||
UINT32 actual_rate;
|
||||
snd_pcm_format_t format;
|
||||
UINT32 target_channels;
|
||||
UINT32 actual_channels;
|
||||
int bytes_per_channel;
|
||||
int wformat;
|
||||
int block_size;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
AUDIO_FORMAT aformat;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
BYTE* buffer;
|
||||
int buffer_frames;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
int bytes_per_frame;
|
||||
} AudinALSADevice;
|
||||
|
||||
static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChannel)
|
||||
{
|
||||
switch (wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (bitPerChannel)
|
||||
{
|
||||
case 16:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
|
||||
case 8:
|
||||
return SND_PCM_FORMAT_S8;
|
||||
|
||||
default:
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return SND_PCM_FORMAT_A_LAW;
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return SND_PCM_FORMAT_MU_LAW;
|
||||
|
||||
default:
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_set_params(AudinALSADevice* alsa,
|
||||
snd_pcm_t* capture_handle)
|
||||
{
|
||||
int error;
|
||||
UINT32 channels = alsa->aformat.nChannels;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
snd_pcm_format_t format = audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
|
||||
|
||||
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_hw_params_malloc (%s)",
|
||||
snd_strerror(error));
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_hw_params_malloc (%s)",
|
||||
snd_strerror(error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_any(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_set_access(capture_handle, hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(capture_handle, hw_params, alsa->format);
|
||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate,
|
||||
NULL);
|
||||
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
|
||||
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params,
|
||||
&alsa->aformat.nSamplesPerSec, NULL);
|
||||
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
|
||||
&alsa->actual_channels);
|
||||
&channels);
|
||||
snd_pcm_hw_params(capture_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
snd_pcm_prepare(capture_handle);
|
||||
|
||||
if ((alsa->actual_rate != alsa->target_rate) ||
|
||||
(alsa->actual_channels != alsa->target_channels))
|
||||
{
|
||||
DEBUG_DVC("actual rate %"PRIu32" / channel %"PRIu32" is "
|
||||
"different from target rate %"PRIu32" / channel %"PRIu32", resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->target_rate, alsa->target_channels);
|
||||
}
|
||||
|
||||
alsa->aformat.nChannels = channels;
|
||||
alsa->bytes_per_frame = snd_pcm_format_size(format, 1) * channels;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src,
|
||||
int size)
|
||||
{
|
||||
int frames;
|
||||
int cframes;
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
int encoded_size;
|
||||
BYTE* encoded_data;
|
||||
int rbytes_per_frame;
|
||||
int tbytes_per_frame;
|
||||
int status;
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||
|
||||
if ((alsa->target_rate == alsa->actual_rate) &&
|
||||
(alsa->target_channels == alsa->actual_channels))
|
||||
{
|
||||
frames = size / rbytes_per_frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
alsa->dsp_context->resample(alsa->dsp_context, src, alsa->bytes_per_channel,
|
||||
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
|
||||
alsa->target_channels, alsa->target_rate);
|
||||
frames = alsa->dsp_context->resampled_frames;
|
||||
DEBUG_DVC("resampled %d frames at %"PRIu32" to %d frames at %"PRIu32"",
|
||||
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
|
||||
src = alsa->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
while (frames > 0)
|
||||
{
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
ret = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
cframes = alsa->frames_per_packet - alsa->buffer_frames;
|
||||
|
||||
if (cframes > frames)
|
||||
cframes = frames;
|
||||
|
||||
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src,
|
||||
cframes * tbytes_per_frame);
|
||||
alsa->buffer_frames += cframes;
|
||||
|
||||
if (alsa->buffer_frames >= alsa->frames_per_packet)
|
||||
{
|
||||
if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
if (!alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
|
||||
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
|
||||
alsa->target_channels, alsa->block_size))
|
||||
{
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = alsa->dsp_context->adpcm_buffer;
|
||||
encoded_size = alsa->dsp_context->adpcm_size;
|
||||
DEBUG_DVC("encoded %d to %d",
|
||||
alsa->buffer_frames * tbytes_per_frame, encoded_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded_data = alsa->buffer;
|
||||
encoded_size = alsa->buffer_frames * tbytes_per_frame;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
ret = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
DEBUG_DVC("encoded %d [%d] to %d [%X]", alsa->buffer_frames,
|
||||
tbytes_per_frame, encoded_size,
|
||||
alsa->wformat);
|
||||
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
|
||||
}
|
||||
|
||||
alsa->buffer_frames = 0;
|
||||
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
src += cframes * tbytes_per_frame;
|
||||
frames -= cframes;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
{
|
||||
long error;
|
||||
BYTE* buffer;
|
||||
int rbytes_per_frame;
|
||||
snd_pcm_t* capture_handle = NULL;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) arg;
|
||||
DWORD status;
|
||||
DEBUG_DVC("in");
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
buffer = (BYTE*) calloc(alsa->frames_per_packet, rbytes_per_frame);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
||||
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name,
|
||||
SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!audin_alsa_set_params(alsa, capture_handle))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_alsa_set_params failed");
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_set_params failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = (BYTE*) calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t frames = alsa->frames_per_packet;
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %ld!", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %ld!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
|
||||
error = snd_pcm_readi(capture_handle, buffer, frames);
|
||||
|
||||
if (error == 0)
|
||||
continue;
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
@ -277,13 +177,16 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_readi (%s)", snd_strerror(error));
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_readi (%s)", snd_strerror(error));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame)))
|
||||
error = alsa->receive(&alsa->aformat,
|
||||
buffer, error * alsa->bytes_per_frame, alsa->user_data);
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "audin_alsa_thread_receive failed with error %ld", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_thread_receive failed with error %ld", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -294,7 +197,7 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
snd_pcm_close(capture_handle);
|
||||
|
||||
out:
|
||||
DEBUG_DVC("out");
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
||||
|
||||
if (error && alsa->rdpcontext)
|
||||
setChannelError(alsa->rdpcontext, error,
|
||||
@ -312,15 +215,20 @@ out:
|
||||
static UINT audin_alsa_free(IAudinDevice* device)
|
||||
{
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
free(alsa->device_name);
|
||||
|
||||
if (alsa)
|
||||
free(alsa->device_name);
|
||||
|
||||
free(alsa);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
audinFormat* format)
|
||||
const AUDIO_FORMAT* format)
|
||||
{
|
||||
if (!device || !format)
|
||||
return FALSE;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
@ -334,15 +242,12 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if ((format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return TRUE;
|
||||
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@ -353,47 +258,20 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_alsa_set_format(IAudinDevice* device, audinFormat* format,
|
||||
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
alsa->target_rate = format->nSamplesPerSec;
|
||||
alsa->actual_rate = format->nSamplesPerSec;
|
||||
alsa->target_channels = format->nChannels;
|
||||
alsa->actual_channels = format->nChannels;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
alsa->format = SND_PCM_FORMAT_S8;
|
||||
alsa->bytes_per_channel = 1;
|
||||
break;
|
||||
if (!alsa || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
case 16:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
break;
|
||||
}
|
||||
alsa->aformat = *format;
|
||||
alsa->frames_per_packet = FramesPerPacket;
|
||||
|
||||
break;
|
||||
if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
|
||||
alsa->frames_per_packet = (alsa->frames_per_packet * format->nChannels * 2 /
|
||||
bs + 1) * bs / (format->nChannels * 2);
|
||||
DEBUG_DVC("aligned FramesPerPacket=%"PRIu32"",
|
||||
alsa->frames_per_packet);
|
||||
break;
|
||||
}
|
||||
|
||||
alsa->wformat = format->wFormatTag;
|
||||
alsa->block_size = format->nBlockAlign;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -405,38 +283,29 @@ static UINT audin_alsa_set_format(IAudinDevice* device, audinFormat* format,
|
||||
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
{
|
||||
int tbytes_per_frame;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
if (!device || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
alsa->receive = receive;
|
||||
alsa->user_data = user_data;
|
||||
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||
alsa->buffer = (BYTE*) calloc(alsa->frames_per_packet, tbytes_per_frame);
|
||||
|
||||
if (!alsa->buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
alsa->buffer_frames = 0;
|
||||
|
||||
if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateEvent failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(alsa->thread = CreateThread(NULL, 0,
|
||||
audin_alsa_thread_func, alsa, 0, NULL)))
|
||||
audin_alsa_thread_func, alsa, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "CreateThread failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
free(alsa->buffer);
|
||||
alsa->buffer = NULL;
|
||||
CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -452,6 +321,9 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
|
||||
if (!alsa)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (alsa->stopEvent)
|
||||
{
|
||||
SetEvent(alsa->stopEvent);
|
||||
@ -459,7 +331,7 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -469,14 +341,12 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
alsa->thread = NULL;
|
||||
}
|
||||
|
||||
free(alsa->buffer);
|
||||
alsa->buffer = NULL;
|
||||
alsa->receive = NULL;
|
||||
alsa->user_data = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
|
||||
static COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
@ -496,7 +366,7 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_alsa_args, flags, alsa, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -516,7 +386,7 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device,
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
@ -552,6 +422,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
alsa->log = WLog_Get(TAG);
|
||||
alsa->iface.Open = audin_alsa_open;
|
||||
alsa->iface.FormatSupported = audin_alsa_format_supported;
|
||||
alsa->iface.SetFormat = audin_alsa_set_format;
|
||||
@ -562,7 +433,8 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
|
||||
if ((error = audin_alsa_parse_addin_args(alsa, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_alsa_parse_addin_args failed with errorcode %"PRIu32"!", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "audin_alsa_parse_addin_args failed with errorcode %"PRIu32"!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -572,38 +444,27 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->frames_per_packet = 128;
|
||||
alsa->target_rate = 22050;
|
||||
alsa->actual_rate = 22050;
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->target_channels = 2;
|
||||
alsa->actual_channels = 2;
|
||||
alsa->bytes_per_channel = 2;
|
||||
alsa->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!alsa->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
alsa->aformat.nChannels = 2;
|
||||
alsa->aformat.wBitsPerSample = 16;
|
||||
alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
alsa->aformat.nSamplesPerSec = 44100;
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) alsa)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
free(alsa->device_name);
|
||||
free(alsa);
|
||||
return error;
|
||||
|
||||
@ -32,11 +32,14 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MSG_SNDIN_VERSION 0x01
|
||||
@ -70,7 +73,7 @@ struct _AUDIN_CHANNEL_CALLBACK
|
||||
* be stored as reference when the server sends the format index in
|
||||
* Open PDU and Format Change PDU
|
||||
*/
|
||||
audinFormat* formats;
|
||||
AUDIO_FORMAT* formats;
|
||||
UINT32 formats_count;
|
||||
};
|
||||
|
||||
@ -82,9 +85,7 @@ struct _AUDIN_PLUGIN
|
||||
AUDIN_LISTENER_CALLBACK* listener_callback;
|
||||
|
||||
/* Parsed plugin data */
|
||||
UINT16 fixed_format;
|
||||
UINT16 fixed_channel;
|
||||
UINT32 fixed_rate;
|
||||
AUDIO_FORMAT* fixed_format;
|
||||
char* subsystem;
|
||||
char* device_name;
|
||||
|
||||
@ -93,23 +94,39 @@ struct _AUDIN_PLUGIN
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
BOOL attached;
|
||||
wStream* data;
|
||||
AUDIO_FORMAT* format;
|
||||
UINT32 FramesPerPacket;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
wLog* log;
|
||||
};
|
||||
|
||||
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
|
||||
|
||||
static UINT audin_write_and_free_stream(AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
|
||||
BOOL freeStream)
|
||||
{
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
const size_t length = Stream_GetPosition(s);
|
||||
const BYTE* data = Stream_Buffer(s);
|
||||
UINT error;
|
||||
|
||||
if (callback && callback->channel && callback->channel->Write)
|
||||
error = callback->channel->Write(callback->channel, length, data, NULL);
|
||||
if (!callback || !out)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!callback->channel || !callback->channel->Write)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_SealLength(out);
|
||||
error = callback->channel->Write(callback->channel,
|
||||
Stream_Length(out),
|
||||
Stream_Buffer(out), NULL);
|
||||
|
||||
if (freeStream)
|
||||
Stream_Free(out, TRUE);
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -118,9 +135,25 @@ static UINT audin_write_and_free_stream(AUDIN_CHANNEL_CALLBACK* callback, wStrea
|
||||
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
wStream* out;
|
||||
UINT32 Version;
|
||||
Stream_Read_UINT32(s, Version);
|
||||
DEBUG_DVC("Version=%"PRIu32"", Version);
|
||||
const UINT32 ClientVersion = 0x01;
|
||||
UINT32 ServerVersion;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, ServerVersion);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%"PRIu32", ClientVersion=%"PRIu32, ServerVersion,
|
||||
ClientVersion);
|
||||
|
||||
/* Do not answer server packet, we do not support the channel version. */
|
||||
if (ServerVersion != ClientVersion)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_WARN,
|
||||
"Incompatible channel version server=%"PRIu32", client supports version=%"PRIu32, ServerVersion,
|
||||
ClientVersion);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
out = Stream_New(NULL, 5);
|
||||
|
||||
if (!out)
|
||||
@ -130,8 +163,8 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
|
||||
Stream_Write_UINT32(out, Version);
|
||||
return audin_write_and_free_stream(callback, out);
|
||||
Stream_Write_UINT32(out, ClientVersion);
|
||||
return audin_channel_write_and_free(callback, out, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,18 +190,16 @@ static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
|
||||
static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT32 i;
|
||||
BYTE* fm;
|
||||
UINT error;
|
||||
wStream* out;
|
||||
UINT32 NumFormats;
|
||||
audinFormat format;
|
||||
size_t cbSizeFormatsPacket;
|
||||
UINT32 cbSizeFormatsPacket;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_NO_DATA;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, NumFormats);
|
||||
DEBUG_DVC("NumFormats %"PRIu32"", NumFormats);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %"PRIu32"", NumFormats);
|
||||
|
||||
if ((NumFormats < 1) || (NumFormats > 1000))
|
||||
{
|
||||
@ -177,7 +208,7 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
}
|
||||
|
||||
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
|
||||
callback->formats = (audinFormat*) calloc(NumFormats, sizeof(audinFormat));
|
||||
callback->formats = audio_formats_new(NumFormats);
|
||||
|
||||
if (!callback->formats)
|
||||
{
|
||||
@ -199,52 +230,38 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
/* SoundFormats (variable) */
|
||||
for (i = 0; i < NumFormats; i++)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 18)
|
||||
return ERROR_NO_DATA;
|
||||
AUDIO_FORMAT format = { 0 };
|
||||
|
||||
Stream_GetPointer(s, fm);
|
||||
Stream_Read_UINT16(s, format.wFormatTag);
|
||||
Stream_Read_UINT16(s, format.nChannels);
|
||||
Stream_Read_UINT32(s, format.nSamplesPerSec);
|
||||
Stream_Seek_UINT32(s); /* nAvgBytesPerSec */
|
||||
Stream_Read_UINT16(s, format.nBlockAlign);
|
||||
Stream_Read_UINT16(s, format.wBitsPerSample);
|
||||
Stream_Read_UINT16(s, format.cbSize);
|
||||
format.data = Stream_Pointer(s);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < format.cbSize)
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Seek(s, format.cbSize);
|
||||
DEBUG_DVC("wFormatTag=%"PRIu16" nChannels=%"PRIu16" nSamplesPerSec=%"PRIu32" "
|
||||
"nBlockAlign=%"PRIu16" wBitsPerSample=%"PRIu16" cbSize=%"PRIu16"",
|
||||
format.wFormatTag, format.nChannels, format.nSamplesPerSec,
|
||||
format.nBlockAlign, format.wBitsPerSample, format.cbSize);
|
||||
|
||||
if (audin->fixed_format > 0 && audin->fixed_format != format.wFormatTag)
|
||||
continue;
|
||||
|
||||
if (audin->fixed_channel > 0 && audin->fixed_channel != format.nChannels)
|
||||
continue;
|
||||
|
||||
if (audin->fixed_rate > 0 && audin->fixed_rate != format.nSamplesPerSec)
|
||||
continue;
|
||||
|
||||
if (audin->device && audin->device->FormatSupported(audin->device, &format))
|
||||
if (!audio_format_read(s, &format))
|
||||
{
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
audio_format_print(audin->log, WLOG_DEBUG, &format);
|
||||
|
||||
if (!audio_format_compatible(audin->fixed_format, &format))
|
||||
{
|
||||
audio_format_free(&format);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (freerdp_dsp_supports_format(&format, TRUE) ||
|
||||
audin->device->FormatSupported(audin->device, &format))
|
||||
{
|
||||
DEBUG_DVC("format ok");
|
||||
/* Store the agreed format in the corresponding index */
|
||||
callback->formats[callback->formats_count++] = format;
|
||||
|
||||
/* Put the format to output buffer */
|
||||
if (!Stream_EnsureRemainingCapacity(out, 18 + format.cbSize))
|
||||
if (!audio_format_write(out, &format))
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_Write(out, fm, 18 + format.cbSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
audio_format_free(&format);
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,21 +271,22 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
|
||||
goto out;
|
||||
}
|
||||
|
||||
cbSizeFormatsPacket = Stream_GetPosition(out);
|
||||
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
|
||||
Stream_SetPosition(out, 0);
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
|
||||
Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
|
||||
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
|
||||
Stream_SetPosition(out, cbSizeFormatsPacket);
|
||||
error = audin_write_and_free_stream(callback, out);
|
||||
error = audin_channel_write_and_free(callback, out, FALSE);
|
||||
out:
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
free(callback->formats);
|
||||
audio_formats_free(callback->formats, NumFormats);
|
||||
callback->formats = NULL;
|
||||
}
|
||||
|
||||
Stream_Free(out, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -290,7 +308,7 @@ static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALL
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
|
||||
Stream_Write_UINT32(out, NewFormat);
|
||||
return audin_write_and_free_stream(callback, out);
|
||||
return audin_channel_write_and_free(callback, out, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,7 +329,7 @@ static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBAC
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
|
||||
Stream_Write_UINT32(out, Result);
|
||||
return audin_write_and_free_stream(callback, out);
|
||||
return audin_channel_write_and_free(callback, out, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,10 +337,11 @@ static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBAC
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
|
||||
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
|
||||
const BYTE* data, size_t size, void* user_data)
|
||||
{
|
||||
UINT error;
|
||||
wStream* out;
|
||||
BOOL compatible;
|
||||
AUDIN_PLUGIN* audin;
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
|
||||
|
||||
@ -337,25 +356,114 @@ static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
|
||||
if (!audin->attached)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
Stream_SetPosition(audin->data, 0);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(audin->data, 1))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
|
||||
|
||||
compatible = audio_format_compatible(format, audin->format);
|
||||
if (compatible && audin->device->FormatSupported(audin->device, audin->format))
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(audin->data, size))
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
Stream_Write(audin->data, data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* Did not encode anything, skip this, the codec is not ready for output. */
|
||||
if (Stream_GetPosition(audin->data) <= 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
audio_format_print(audin->log, WLOG_TRACE, audin->format);
|
||||
WLog_Print(audin->log, WLOG_TRACE, "[%"PRIdz"/%"PRIdz"]", size,
|
||||
Stream_GetPosition(audin->data) - 1);
|
||||
|
||||
if ((error = audin_send_incoming_data_pdu(callback)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
|
||||
return error;
|
||||
}
|
||||
|
||||
out = Stream_New(NULL, size + 1);
|
||||
|
||||
if (!out)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_DATA);
|
||||
Stream_Write(out, data, size);
|
||||
return audin_write_and_free_stream(callback, out);
|
||||
return audin_channel_write_and_free(callback, audin->data, FALSE);
|
||||
}
|
||||
|
||||
static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
|
||||
{
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
BOOL supported;
|
||||
AUDIO_FORMAT format;
|
||||
|
||||
if (!audin || !audin->device)
|
||||
return FALSE;
|
||||
|
||||
format = *audin->format;
|
||||
supported = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "microphone uses %s codec",
|
||||
audio_format_get_tag_string(format.wFormatTag));
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
/* Default sample rates supported by most backends. */
|
||||
const UINT32 samplerates[] = {
|
||||
96000,
|
||||
48000,
|
||||
44100,
|
||||
22050
|
||||
};
|
||||
BOOL test = FALSE;
|
||||
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.wBitsPerSample = 16;
|
||||
test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
|
||||
if (!test)
|
||||
{
|
||||
size_t x;
|
||||
for (x=0; x<ARRAYSIZE(samplerates); x++)
|
||||
{
|
||||
format.nSamplesPerSec = samplerates[x];
|
||||
test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
|
||||
if (test)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!test)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->SetFormat, error,
|
||||
audin->device, &format,
|
||||
audin->FramesPerPacket);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "SetFormat failed with errorcode %"PRIu32"", error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->Open, error, audin->device,
|
||||
audin_receive_wave_data, callback);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Open failed with errorcode %"PRIu32"", error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -363,49 +471,30 @@ static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
|
||||
*/
|
||||
static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
audinFormat* format;
|
||||
UINT32 initialFormat;
|
||||
UINT32 FramesPerPacket;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!audin || !callback || !s)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_NO_DATA;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, FramesPerPacket);
|
||||
Stream_Read_UINT32(s, initialFormat);
|
||||
DEBUG_DVC("FramesPerPacket=%"PRIu32" initialFormat=%"PRIu32"",
|
||||
FramesPerPacket, initialFormat);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%"PRIu32" initialFormat=%"PRIu32"",
|
||||
FramesPerPacket, initialFormat);
|
||||
audin->FramesPerPacket = FramesPerPacket;
|
||||
|
||||
if (initialFormat >= (UINT32) callback->formats_count)
|
||||
if (initialFormat >= callback->formats_count)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %"PRIu32" (total %d)",
|
||||
initialFormat, callback->formats_count);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
format = &callback->formats[initialFormat];
|
||||
audin->format = &callback->formats[initialFormat];
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
IFCALLRET(audin->device->SetFormat, error, audin->device, format, FramesPerPacket);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "SetFormat failed with errorcode %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Open failed with errorcode %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
if (!audin_open_device(audin, callback))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
|
||||
{
|
||||
@ -428,17 +517,13 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
||||
wStream* s)
|
||||
{
|
||||
UINT32 NewFormat;
|
||||
audinFormat* format;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!audin || !callback || !s)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_NO_DATA;
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, NewFormat);
|
||||
DEBUG_DVC("NewFormat=%"PRIu32"", NewFormat);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%"PRIu32"", NewFormat);
|
||||
|
||||
if (NewFormat >= callback->formats_count)
|
||||
{
|
||||
@ -447,7 +532,7 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
format = &callback->formats[NewFormat];
|
||||
audin->format = &callback->formats[NewFormat];
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
@ -455,29 +540,16 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->SetFormat, error, audin->device, format, 0);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "SetFormat failed with errorcode %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Open failed with errorcode %"PRIu32"", error);
|
||||
WLog_ERR(TAG, "Close failed with errorcode %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!audin_open_device(audin, callback))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
|
||||
WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
|
||||
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -506,7 +578,7 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(data, MessageId);
|
||||
DEBUG_DVC("MessageId=0x%02"PRIx8"", MessageId);
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02"PRIx8"", MessageId);
|
||||
|
||||
switch (MessageId)
|
||||
{
|
||||
@ -545,7 +617,7 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DEBUG_DVC("...");
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
@ -555,7 +627,8 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %"PRIu32"", error);
|
||||
}
|
||||
|
||||
free(callback->formats);
|
||||
audin->format = NULL;
|
||||
audio_formats_free(callback->formats, callback->formats_count);
|
||||
free(callback);
|
||||
return error;
|
||||
}
|
||||
@ -577,7 +650,7 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
audin = (AUDIN_PLUGIN*) listener_callback->plugin;
|
||||
DEBUG_DVC("...");
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
callback = (AUDIN_CHANNEL_CALLBACK*) calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
@ -603,7 +676,14 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
|
||||
static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
DEBUG_DVC("...");
|
||||
|
||||
if (!audin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
if (!pChannelMgr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) calloc(1, sizeof(AUDIN_LISTENER_CALLBACK));
|
||||
|
||||
if (!audin->listener_callback)
|
||||
@ -628,7 +708,12 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DEBUG_DVC("...");
|
||||
|
||||
if (!audin)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
WLog_Print(audin->log, WLOG_TRACE, "...");
|
||||
audio_format_free(audin->fixed_format);
|
||||
|
||||
if (audin->device)
|
||||
{
|
||||
@ -643,10 +728,10 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
audin->device = NULL;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_free(audin->dsp_context);
|
||||
Stream_Free(audin->data, TRUE);
|
||||
free(audin->subsystem);
|
||||
audin->subsystem = NULL;
|
||||
free(audin->device_name);
|
||||
audin->device_name = NULL;
|
||||
free(audin->listener_callback);
|
||||
free(audin);
|
||||
return CHANNEL_RC_OK;
|
||||
@ -691,7 +776,7 @@ static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* devi
|
||||
return ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
DEBUG_DVC("device registered.");
|
||||
WLog_Print(audin->log, WLOG_DEBUG, "device registered.");
|
||||
audin->device = device;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -706,7 +791,7 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, char* name, ADDIN_ARGV
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY entry;
|
||||
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
||||
UINT error;
|
||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", name, NULL,
|
||||
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL,
|
||||
0);
|
||||
|
||||
if (entry == NULL)
|
||||
@ -756,7 +841,7 @@ static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
|
||||
{
|
||||
free(audin->device_name);
|
||||
audin->device_name = _strdup(device_name);
|
||||
@ -791,7 +876,7 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
return TRUE;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_args, flags, audin, NULL, NULL);
|
||||
|
||||
if (status != 0)
|
||||
@ -829,7 +914,7 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
if ((errno != 0) || (val > UINT16_MAX))
|
||||
return FALSE;
|
||||
|
||||
audin->fixed_format = val;
|
||||
audin->fixed_format->wFormatTag = val;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "rate")
|
||||
{
|
||||
@ -838,14 +923,14 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
|
||||
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
|
||||
return FALSE;
|
||||
|
||||
audin->fixed_rate = val;
|
||||
audin->fixed_format->nSamplesPerSec = val;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "channel")
|
||||
{
|
||||
unsigned long val = strtoul(arg->Value, NULL, 0);
|
||||
|
||||
if ((errno != 0) || (val > UINT16_MAX))
|
||||
audin->fixed_channel = val;
|
||||
audin->fixed_format->nChannels = val;
|
||||
}
|
||||
CommandLineSwitchDefault(arg)
|
||||
{
|
||||
@ -917,6 +1002,20 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
}
|
||||
|
||||
audin->log = WLog_Get(TAG);
|
||||
audin->data = Stream_New(NULL, 4096);
|
||||
audin->fixed_format = audio_format_new();
|
||||
|
||||
if (!audin->fixed_format)
|
||||
goto out;
|
||||
|
||||
if (!audin->data)
|
||||
goto out;
|
||||
|
||||
audin->dsp_context = freerdp_dsp_context_new(TRUE);
|
||||
|
||||
if (!audin->dsp_context)
|
||||
goto out;
|
||||
|
||||
audin->attached = TRUE;
|
||||
audin->iface.Initialize = audin_plugin_initialize;
|
||||
audin->iface.Connected = NULL;
|
||||
|
||||
@ -32,11 +32,5 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("audin.client")
|
||||
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_MAIN_H */
|
||||
|
||||
|
||||
@ -42,85 +42,79 @@
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
#define MAC_AUDIO_QUEUE_BUFFER_SIZE 32768
|
||||
|
||||
/* Fix for #4462: Provide type alias if not declared (Mac OS < 10.10)
|
||||
* https://developer.apple.com/documentation/coreaudio/audioformatid
|
||||
*/
|
||||
#ifndef AudioFormatID
|
||||
typedef UInt32 AudioFormatID;
|
||||
#endif
|
||||
|
||||
#ifndef AudioFormatFlags
|
||||
typedef UInt32 AudioFormatFlags;
|
||||
#endif
|
||||
|
||||
typedef struct _AudinMacDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
IAudinDevice iface;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
audinFormat format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
} AudinMacDevice;
|
||||
|
||||
static AudioFormatID audin_mac_get_format(const audinFormat* format)
|
||||
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
/*
|
||||
case WAVE_FORMAT_GSM610:
|
||||
return kAudioFormatMicrosoftGSM;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return kAudioFormatALaw;
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return kAudioFormatULaw;
|
||||
case WAVE_FORMAT_AAC_MS:
|
||||
return kAudioFormatMPEG4AAC;
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
*/
|
||||
}
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const audinFormat* format)
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch(format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
static BOOL audin_mac_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudioFormatID req_fmt = 0;
|
||||
AudioFormatID req_fmt = 0;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return FALSE;
|
||||
if (device == NULL || format == NULL)
|
||||
return FALSE;
|
||||
|
||||
req_fmt = audin_mac_get_format(format);
|
||||
req_fmt = audin_mac_get_format(format);
|
||||
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,279 +122,246 @@ static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_mac_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
mac->FramesPerPacket = FramesPerPacket;
|
||||
CopyMemory(&(mac->format), format, sizeof(audinFormat));
|
||||
mac->FramesPerPacket = FramesPerPacket;
|
||||
mac->format = *format;
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
audio_format_get_tag_string(format->wFormatTag),
|
||||
format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
|
||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
rdpsnd_get_audio_tag_string(format->wFormatTag),
|
||||
format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
|
||||
if (format->wBitsPerSample == 0)
|
||||
mac->audioFormat.mBitsPerChannel = 16;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
mac->FramesPerPacket *= 4; /* Compression ratio. */
|
||||
mac->format.wBitsPerSample *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
mac->audioFormat.mBitsPerChannel = mac->format.wBitsPerSample;
|
||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||
mac->audioFormat.mBytesPerFrame =
|
||||
mac->audioFormat.mChannelsPerFrame * mac->audioFormat.mBitsPerChannel / 8;
|
||||
mac->audioFormat.mBytesPerPacket =
|
||||
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
mac->audioFormat.mBytesPerFrame = 0;
|
||||
mac->audioFormat.mBytesPerPacket = 0;
|
||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mReserved = 0;
|
||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void mac_audio_queue_input_cb(void *aqData,
|
||||
static void mac_audio_queue_input_cb(void* aqData,
|
||||
AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime,
|
||||
const AudioTimeStamp* inStartTime,
|
||||
UInt32 inNumPackets,
|
||||
const AudioStreamPacketDescription *inPacketDesc)
|
||||
const AudioStreamPacketDescription* inPacketDesc)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)aqData;
|
||||
UINT error;
|
||||
int encoded_size = 0;
|
||||
const BYTE *encoded_data;
|
||||
BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)aqData;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
const BYTE* buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
if (buffer_size > 0)
|
||||
error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data);
|
||||
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
|
||||
|
||||
/* Process. */
|
||||
switch (mac->format.wFormatTag) {
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
if (!mac->dsp_context->encode_ms_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (!mac->dsp_context->encode_ima_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
default:
|
||||
encoded_data = buffer;
|
||||
encoded_size = buffer_size;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = mac->receive(encoded_data, encoded_size, mac->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %"PRIu32"", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %"PRIu32"", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static UINT audin_mac_close(IAudinDevice *device)
|
||||
static UINT audin_mac_close(IAudinDevice* device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(mac->audioQueue, true);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
mac->isOpen = false;
|
||||
}
|
||||
if (mac->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(mac->audioQueue, true);
|
||||
|
||||
if (mac->audioQueue)
|
||||
{
|
||||
devStat = AudioQueueDispose(mac->audioQueue, true);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->audioQueue = NULL;
|
||||
}
|
||||
mac->isOpen = false;
|
||||
}
|
||||
|
||||
mac->receive = NULL;
|
||||
mac->user_data = NULL;
|
||||
if (mac->audioQueue)
|
||||
{
|
||||
devStat = AudioQueueDispose(mac->audioQueue, true);
|
||||
|
||||
return errCode;
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->audioQueue = NULL;
|
||||
}
|
||||
|
||||
mac->receive = NULL;
|
||||
mac->user_data = NULL;
|
||||
return errCode;
|
||||
}
|
||||
|
||||
static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data) {
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
size_t index;
|
||||
static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
size_t index;
|
||||
mac->receive = receive;
|
||||
mac->user_data = user_data;
|
||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
|
||||
mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||
|
||||
mac->receive = receive;
|
||||
mac->user_data = user_data;
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
|
||||
mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(mac->audioQueue,
|
||||
mac->FramesPerPacket * 2 * mac->format.nChannels,
|
||||
&mac->audioBuffers[index]);
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
|
||||
&mac->audioBuffers[index]);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
|
||||
mac->audioBuffers[index],
|
||||
0,
|
||||
NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
|
||||
mac->audioBuffers[index],
|
||||
0,
|
||||
NULL);
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
devStat = AudioQueueStart(mac->audioQueue, NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
mac->isOpen = true;
|
||||
devStat = AudioQueueStart(mac->audioQueue, NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
mac->isOpen = true;
|
||||
return CHANNEL_RC_OK;
|
||||
err_out:
|
||||
audin_mac_close(device);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
audin_mac_close(device);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
static UINT audin_mac_free(IAudinDevice* device)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
int error;
|
||||
|
||||
int error;
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if ((error = audin_mac_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
|
||||
if ((error = audin_mac_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
free(mac);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_mac_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (args->argc == 1)
|
||||
return CHANNEL_RC_OK;
|
||||
if (args->argc == 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_mac_args, flags, mac, NULL, NULL);
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags,
|
||||
mac, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_mac_args;
|
||||
arg = audin_mac_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
if (!str_num)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "_strdup failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
mac->dev_unit = strtol(str_num, &eptr, 10);
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
|
||||
if (mac->dev_unit < 0 || *eptr != '\0')
|
||||
mac->dev_unit = -1;
|
||||
if (!str_num)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "_strdup failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
mac->dev_unit = strtol(str_num, &eptr, 10);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
if (mac->dev_unit < 0 || *eptr != '\0')
|
||||
mac->dev_unit = -1;
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
@ -411,56 +372,44 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
|
||||
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
ADDIN_ARGV *args;
|
||||
AudinMacDevice *mac;
|
||||
UINT error;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
ADDIN_ARGV* args;
|
||||
AudinMacDevice* mac;
|
||||
UINT error;
|
||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
||||
|
||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%"PRIu32"]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
mac->iface.Open = audin_mac_open;
|
||||
mac->iface.FormatSupported = audin_mac_format_supported;
|
||||
mac->iface.SetFormat = audin_mac_set_format;
|
||||
mac->iface.Close = audin_mac_close;
|
||||
mac->iface.Free = audin_mac_free;
|
||||
mac->rdpcontext = pEntryPoints->rdpcontext;
|
||||
mac->iface.Open = audin_mac_open;
|
||||
mac->iface.FormatSupported = audin_mac_format_supported;
|
||||
mac->iface.SetFormat = audin_mac_set_format;
|
||||
mac->iface.Close = audin_mac_close;
|
||||
mac->iface.Free = audin_mac_free;
|
||||
mac->rdpcontext = pEntryPoints->rdpcontext;
|
||||
mac->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
mac->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
if ((error = audin_mac_parse_addin_args(mac, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = audin_mac_parse_addin_args(mac, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
mac->dsp_context = freerdp_dsp_context_new();
|
||||
if (!mac->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
return error;
|
||||
|
||||
free(mac);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -29,12 +29,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
@ -48,133 +45,38 @@ typedef struct _AudinOpenSLESDevice
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
OPENSL_STREAM *stream;
|
||||
OPENSL_STREAM* stream;
|
||||
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 frames_per_packet;
|
||||
UINT32 rate;
|
||||
UINT32 channels;
|
||||
|
||||
UINT32 bytes_per_channel;
|
||||
|
||||
UINT32 format;
|
||||
UINT32 block_size;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
AudinReceive receive;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinOpenSLESDevice;
|
||||
|
||||
static DWORD WINAPI audin_opensles_thread_func(LPVOID arg)
|
||||
static UINT audin_opensles_close(IAudinDevice* device);
|
||||
|
||||
static void audin_receive(void* context, const void* data, size_t size)
|
||||
{
|
||||
union
|
||||
{
|
||||
void *v;
|
||||
short* s;
|
||||
BYTE *b;
|
||||
} buffer;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
|
||||
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
|
||||
int rc = CHANNEL_RC_OK;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
UINT error;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) context;
|
||||
|
||||
DEBUG_DVC("opensles=%p", (void*) opensles);
|
||||
|
||||
assert(opensles);
|
||||
assert(opensles->frames_per_packet > 0);
|
||||
assert(opensles->dsp_context);
|
||||
assert(opensles->stopEvent);
|
||||
assert(opensles->stream);
|
||||
|
||||
buffer.v = calloc(1, raw_size);
|
||||
if (!buffer.v)
|
||||
if (!opensles || !data)
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
if (opensles->rdpcontext)
|
||||
setChannelError(opensles->rdpcontext, CHANNEL_RC_NO_MEMORY, "audin_opensles_thread_func reported an error");
|
||||
goto out;
|
||||
WLog_ERR(TAG, "[%s] Invalid arguments context=%p, data=%p", __FUNCTION__, opensles, data);
|
||||
return;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(opensles->dsp_context);
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
status = WaitForSingleObject(opensles->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
size_t encoded_size;
|
||||
void *encoded_data;
|
||||
|
||||
rc = android_RecIn(opensles->stream, buffer.s, raw_size);
|
||||
if (rc < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "android_RecIn %d", rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(rc == raw_size);
|
||||
if (opensles->format == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
if (!opensles->dsp_context->encode_ms_adpcm(opensles->dsp_context,
|
||||
buffer.b, rc, opensles->channels, opensles->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = opensles->dsp_context->adpcm_buffer;
|
||||
encoded_size = opensles->dsp_context->adpcm_size;
|
||||
}
|
||||
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
if (!opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context,
|
||||
buffer.b, rc,
|
||||
opensles->channels, opensles->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = opensles->dsp_context->adpcm_buffer;
|
||||
encoded_size = opensles->dsp_context->adpcm_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded_data = buffer.v;
|
||||
encoded_size = rc;
|
||||
}
|
||||
|
||||
error = opensles->receive(encoded_data, encoded_size, opensles->user_data);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
free(buffer.v);
|
||||
out:
|
||||
DEBUG_DVC("thread shutdown.");
|
||||
error = opensles->receive(&opensles->format, data, size, opensles->user_data);
|
||||
|
||||
if (error && opensles->rdpcontext)
|
||||
setChannelError(opensles->rdpcontext, error, "audin_opensles_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
setChannelError(opensles->rdpcontext, error, "audin_receive reported an error");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,61 +88,49 @@ static UINT audin_opensles_free(IAudinDevice* device)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p", (void*) device);
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*) device);
|
||||
|
||||
/* The function may have been called out of order,
|
||||
* ignore duplicate requests. */
|
||||
if (!opensles)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
assert(opensles);
|
||||
assert(opensles->dsp_context);
|
||||
assert(!opensles->stream);
|
||||
|
||||
freerdp_dsp_context_free(opensles->dsp_context);
|
||||
|
||||
free(opensles->device_name);
|
||||
|
||||
free(opensles);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
static BOOL audin_opensles_format_supported(IAudinDevice* device,
|
||||
const AUDIO_FORMAT* format)
|
||||
{
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
#endif
|
||||
|
||||
DEBUG_DVC("device=%p, format=%p", (void*) opensles, (void*) format);
|
||||
|
||||
if (!opensles || !format)
|
||||
return FALSE;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p", (void*) opensles, (void*) format);
|
||||
assert(format);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
/* TODO: Deactivated format, does not work, find out why */
|
||||
// case WAVE_FORMAT_ADPCM: /* IMA ADPCM */
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if ((format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_DVC("Encoding '%s' [0x%04X"PRIX16"] not supported",
|
||||
rdpsnd_get_audio_tag_string(format->wFormatTag),
|
||||
format->wFormatTag);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "Encoding '%s' [0x%04X"PRIX16"] not supported",
|
||||
audio_format_get_tag_string(format->wFormatTag),
|
||||
format->wFormatTag);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -253,14 +143,15 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
audinFormat* format, UINT32 FramesPerPacket)
|
||||
const AUDIO_FORMAT* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%"PRIu32"",
|
||||
(void*) device, (void*) format, FramesPerPacket);
|
||||
if (!opensles || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, format=%p, FramesPerPacket=%"PRIu32"",
|
||||
(void*) device, (void*) format, FramesPerPacket);
|
||||
assert(format);
|
||||
|
||||
/* The function may have been called out of order, ignore
|
||||
@ -268,52 +159,42 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
if (!opensles)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
opensles->format = *format;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
opensles->frames_per_packet = FramesPerPacket;
|
||||
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 4:
|
||||
opensles->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
opensles->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
opensles->bytes_per_channel = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
break;
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
opensles->bytes_per_channel = 2;
|
||||
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
|
||||
|
||||
opensles->frames_per_packet =
|
||||
(FramesPerPacket * format->nChannels * 2 /
|
||||
bs + 1) * bs / (format->nChannels * 2);
|
||||
break;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
opensles->frames_per_packet = FramesPerPacket;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Encoding '%"PRIu16"' [%04"PRIX16"] not supported",
|
||||
format->wFormatTag,
|
||||
format->wFormatTag);
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "Encoding '%"PRIu16"' [%04"PRIX16"] not supported",
|
||||
format->wFormatTag,
|
||||
format->wFormatTag);
|
||||
return ERROR_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
opensles->rate = format->nSamplesPerSec;
|
||||
opensles->channels = format->nChannels;
|
||||
|
||||
opensles->format = format->wFormatTag;
|
||||
opensles->block_size = format->nBlockAlign;
|
||||
|
||||
DEBUG_DVC("aligned frames_per_packet=%"PRIu32", block_size=%"PRIu32"",
|
||||
opensles->frames_per_packet, opensles->block_size);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "frames_per_packet=%"PRIu32,
|
||||
opensles->frames_per_packet);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -323,51 +204,36 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
|
||||
void* user_data)
|
||||
void* user_data)
|
||||
{
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p, receive=%p, user_data=%p", (void*) device, (void*) receive, (void*) user_data);
|
||||
if (!opensles || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
assert(opensles);
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*) device,
|
||||
(void*) receive,
|
||||
(void*) user_data);
|
||||
|
||||
/* The function may have been called out of order,
|
||||
* ignore duplicate open requests. */
|
||||
if(opensles->stream)
|
||||
return CHANNEL_RC_OK;
|
||||
if (opensles->stream)
|
||||
goto error_out;
|
||||
|
||||
if(!(opensles->stream = android_OpenRecDevice(
|
||||
opensles->device_name,
|
||||
opensles->rate,
|
||||
opensles->channels,
|
||||
opensles->frames_per_packet,
|
||||
opensles->bytes_per_channel * 8)))
|
||||
if (!(opensles->stream = android_OpenRecDevice(
|
||||
opensles, audin_receive,
|
||||
opensles->format.nSamplesPerSec,
|
||||
opensles->format.nChannels,
|
||||
opensles->frames_per_packet,
|
||||
opensles->format.wBitsPerSample)))
|
||||
{
|
||||
WLog_ERR(TAG, "android_OpenRecDevice failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
opensles->receive = receive;
|
||||
opensles->user_data = user_data;
|
||||
|
||||
if (!(opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
goto error_out;
|
||||
}
|
||||
if (!(opensles->thread = CreateThread(NULL, 0,
|
||||
audin_opensles_thread_func,
|
||||
opensles, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
goto error_out;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
android_CloseRecDevice(opensles->stream);
|
||||
opensles->stream = NULL;
|
||||
CloseHandle(opensles->stopEvent);
|
||||
opensles->stopEvent = NULL;
|
||||
audin_opensles_close(opensles);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -376,52 +242,27 @@ error_out:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_close(IAudinDevice* device)
|
||||
UINT audin_opensles_close(IAudinDevice* device)
|
||||
{
|
||||
UINT error;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p", (void*) device);
|
||||
|
||||
assert(opensles);
|
||||
|
||||
/* The function may have been called out of order,
|
||||
* ignore duplicate requests. */
|
||||
if (!opensles->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "[ERROR] function called without matching open.");
|
||||
return ERROR_REQUEST_OUT_OF_SEQUENCE;
|
||||
}
|
||||
|
||||
assert(opensles->stopEvent);
|
||||
assert(opensles->thread);
|
||||
assert(opensles->stream);
|
||||
|
||||
SetEvent(opensles->stopEvent);
|
||||
if (WaitForSingleObject(opensles->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
CloseHandle(opensles->stopEvent);
|
||||
CloseHandle(opensles->thread);
|
||||
if (!opensles)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*) device);
|
||||
android_CloseRecDevice(opensles->stream);
|
||||
|
||||
opensles->stopEvent = NULL;
|
||||
opensles->thread = NULL;
|
||||
opensles->receive = NULL;
|
||||
opensles->user_data = NULL;
|
||||
opensles->stream = NULL;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name" },
|
||||
{
|
||||
"dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name"
|
||||
},
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
@ -431,19 +272,17 @@ static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
ADDIN_ARGV* args)
|
||||
ADDIN_ARGV* args)
|
||||
{
|
||||
UINT status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
|
||||
|
||||
DEBUG_DVC("device=%p, args=%p", (void*) device, (void*) args);
|
||||
|
||||
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, args=%p", (void*) device, (void*) args);
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_opensles_args, flags, opensles, NULL, NULL);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
audin_opensles_args, flags, opensles, NULL, NULL);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
@ -455,17 +294,16 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
opensles->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!opensles->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
@ -487,53 +325,43 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinOpenSLESDevice* opensles;
|
||||
UINT error;
|
||||
|
||||
DEBUG_DVC("pEntryPoints=%p", (void*) pEntryPoints);
|
||||
|
||||
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
|
||||
|
||||
if (!opensles)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
opensles->log = WLog_Get(TAG);
|
||||
opensles->iface.Open = audin_opensles_open;
|
||||
opensles->iface.FormatSupported = audin_opensles_format_supported;
|
||||
opensles->iface.SetFormat = audin_opensles_set_format;
|
||||
opensles->iface.Close = audin_opensles_close;
|
||||
opensles->iface.Free = audin_opensles_free;
|
||||
opensles->rdpcontext = pEntryPoints->rdpcontext;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_opensles_parse_addin_args(opensles, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_opensles_parse_addin_args failed with errorcode %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
opensles->dsp_context = freerdp_dsp_context_new();
|
||||
if (!opensles->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_Print(opensles->log, WLOG_ERROR,
|
||||
"audin_opensles_parse_addin_args failed with errorcode %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) opensles)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(opensles->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(opensles->dsp_context);
|
||||
free(opensles);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -34,6 +34,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define CONV16BIT 32768
|
||||
#define CONVMYFLT (1./32768.)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t size;
|
||||
void* data;
|
||||
} queue_element;
|
||||
|
||||
struct opensl_stream
|
||||
{
|
||||
// engine interfaces
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engineEngine;
|
||||
|
||||
// device interfaces
|
||||
SLDeviceVolumeItf deviceVolume;
|
||||
|
||||
// recorder interfaces
|
||||
SLObjectItf recorderObject;
|
||||
SLRecordItf recorderRecord;
|
||||
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
|
||||
|
||||
unsigned int inchannels;
|
||||
unsigned int sr;
|
||||
unsigned int buffersize;
|
||||
unsigned int bits_per_sample;
|
||||
|
||||
queue_element* prep;
|
||||
queue_element* next;
|
||||
|
||||
void* context;
|
||||
opensl_receive_t receive;
|
||||
};
|
||||
|
||||
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
|
||||
|
||||
// creates the OpenSL ES audio engine
|
||||
@ -42,27 +75,23 @@ static SLresult openSLCreateEngine(OPENSL_STREAM* p)
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
|
||||
DEBUG_DVC("engineObject=%p", (void*) p->engineObject);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS) goto engine_end;
|
||||
|
||||
// realize the engine
|
||||
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
|
||||
DEBUG_DVC("Realize=%"PRIu32"", result);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS) goto engine_end;
|
||||
|
||||
// get the engine interface, which is needed in order to create other objects
|
||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE,
|
||||
&(p->engineEngine));
|
||||
DEBUG_DVC("engineEngine=%p", (void*) p->engineEngine);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS) goto engine_end;
|
||||
|
||||
// get the volume interface - important, this is optional!
|
||||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME,
|
||||
&(p->deviceVolume));
|
||||
DEBUG_DVC("deviceVolume=%p", (void*) p->deviceVolume);
|
||||
|
||||
if (result != SL_RESULT_SUCCESS)
|
||||
{
|
||||
@ -180,14 +209,12 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
const SLboolean req[] = {SL_BOOLEAN_TRUE};
|
||||
result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine,
|
||||
&(p->recorderObject), &audioSrc, &audioSnk, 1, id, req);
|
||||
DEBUG_DVC("p->recorderObject=%p", (void*) p->recorderObject);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
|
||||
// realize the audio recorder
|
||||
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
|
||||
DEBUG_DVC("Realize=%"PRIu32"", result);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
@ -195,7 +222,6 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
// get the record interface
|
||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
||||
SL_IID_RECORD, &(p->recorderRecord));
|
||||
DEBUG_DVC("p->recorderRecord=%p", (void*) p->recorderRecord);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
@ -204,7 +230,6 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
result = (*p->recorderObject)->GetInterface(p->recorderObject,
|
||||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&(p->recorderBufferQueue));
|
||||
DEBUG_DVC("p->recorderBufferQueue=%p", (void*) p->recorderBufferQueue);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) goto end_recopen;
|
||||
@ -212,7 +237,6 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
// register callback on the buffer queue
|
||||
result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue,
|
||||
bqRecorderCallback, p);
|
||||
DEBUG_DVC("p->recorderBufferQueue=%p", (void*) p->recorderBufferQueue);
|
||||
assert(!result);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
@ -227,8 +251,6 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
|
||||
// close the OpenSL IO and destroy the audio engine
|
||||
static void openSLDestroyEngine(OPENSL_STREAM* p)
|
||||
{
|
||||
DEBUG_DVC("p=%p", (void*) p);
|
||||
|
||||
// destroy audio recorder object, and invalidate all associated interfaces
|
||||
if (p->recorderObject != NULL)
|
||||
{
|
||||
@ -247,76 +269,94 @@ static void openSLDestroyEngine(OPENSL_STREAM* p)
|
||||
}
|
||||
}
|
||||
|
||||
static queue_element* opensles_queue_element_new(size_t size)
|
||||
{
|
||||
queue_element* q = calloc(1, sizeof(queue_element));
|
||||
|
||||
if (!q)
|
||||
goto fail;
|
||||
|
||||
q->size = size;
|
||||
q->data = malloc(size);
|
||||
|
||||
if (!q->data)
|
||||
goto fail;
|
||||
|
||||
return q;
|
||||
fail:
|
||||
free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void opensles_queue_element_free(void* obj)
|
||||
{
|
||||
queue_element* e = (queue_element*)obj;
|
||||
|
||||
if (e)
|
||||
free(e->data);
|
||||
|
||||
free(e);
|
||||
}
|
||||
|
||||
// open the android audio device for input
|
||||
OPENSL_STREAM* android_OpenRecDevice(char* name, int sr, int inchannels,
|
||||
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
|
||||
int sr,
|
||||
int inchannels,
|
||||
int bufferframes, int bits_per_sample)
|
||||
{
|
||||
OPENSL_STREAM* p;
|
||||
|
||||
if (!context || !receive)
|
||||
return NULL;
|
||||
|
||||
p = (OPENSL_STREAM*) calloc(1, sizeof(OPENSL_STREAM));
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
p->context = context;
|
||||
p->receive = receive;
|
||||
p->inchannels = inchannels;
|
||||
p->sr = sr;
|
||||
p->queue = Queue_New(TRUE, -1, -1);
|
||||
p->buffersize = bufferframes;
|
||||
p->bits_per_sample = bits_per_sample;
|
||||
|
||||
if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16))
|
||||
{
|
||||
android_CloseRecDevice(p);
|
||||
return NULL;
|
||||
}
|
||||
goto fail;
|
||||
|
||||
if (openSLCreateEngine(p) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
android_CloseRecDevice(p);
|
||||
return NULL;
|
||||
}
|
||||
goto fail;
|
||||
|
||||
if (openSLRecOpen(p) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
android_CloseRecDevice(p);
|
||||
return NULL;
|
||||
}
|
||||
goto fail;
|
||||
|
||||
/* Create receive buffers, prepare them and start recording */
|
||||
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
|
||||
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
|
||||
|
||||
if (!p->prep || !p->next)
|
||||
goto fail;
|
||||
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
p->next->data, p->next->size);
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
p->prep->data, p->prep->size);
|
||||
(*p->recorderRecord)->SetRecordState(p->recorderRecord,
|
||||
SL_RECORDSTATE_RECORDING);
|
||||
return p;
|
||||
fail:
|
||||
android_CloseRecDevice(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// close the android audio device
|
||||
void android_CloseRecDevice(OPENSL_STREAM* p)
|
||||
{
|
||||
DEBUG_DVC("p=%p", (void*) p);
|
||||
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
if (p->queue)
|
||||
{
|
||||
while (Queue_Count(p->queue) > 0)
|
||||
{
|
||||
queue_element* e = Queue_Dequeue(p->queue);
|
||||
free(e->data);
|
||||
free(e);
|
||||
}
|
||||
|
||||
Queue_Free(p->queue);
|
||||
}
|
||||
|
||||
if (p->next)
|
||||
{
|
||||
free(p->next->data);
|
||||
free(p->next);
|
||||
}
|
||||
|
||||
if (p->prep)
|
||||
{
|
||||
free(p->prep->data);
|
||||
free(p->prep);
|
||||
}
|
||||
|
||||
opensles_queue_element_free(p->next);
|
||||
opensles_queue_element_free(p->prep);
|
||||
openSLDestroyEngine(p);
|
||||
free(p);
|
||||
}
|
||||
@ -324,87 +364,25 @@ void android_CloseRecDevice(OPENSL_STREAM* p)
|
||||
// this callback handler is called every time a buffer finishes recording
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||
{
|
||||
queue_element* e;
|
||||
OPENSL_STREAM* p = (OPENSL_STREAM*) context;
|
||||
DEBUG_DVC("p=%p", (void*) p);
|
||||
assert(p);
|
||||
assert(p->next);
|
||||
assert(p->prep);
|
||||
assert(p->queue);
|
||||
e = calloc(1, sizeof(queue_element));
|
||||
queue_element* e;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
e = p->next;
|
||||
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
e->data = calloc(p->buffersize, p->bits_per_sample / 8);
|
||||
if (!p->context || !p->receive)
|
||||
WLog_WARN(TAG, "Missing receive callback=%p, context=%p", p->receive, p->context);
|
||||
else
|
||||
p->receive(p->context, e->data, e->size);
|
||||
|
||||
if (!e->data)
|
||||
{
|
||||
free(e);
|
||||
return;
|
||||
}
|
||||
|
||||
e->size = p->buffersize * p->bits_per_sample / 8;
|
||||
Queue_Enqueue(p->queue, p->next);
|
||||
p->next = p->prep;
|
||||
p->prep = e;
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
e->data, e->size);
|
||||
}
|
||||
|
||||
// gets a buffer of size samples from the device
|
||||
int android_RecIn(OPENSL_STREAM* p, short* buffer, int size)
|
||||
{
|
||||
queue_element* e;
|
||||
int rc;
|
||||
DWORD status;
|
||||
assert(p);
|
||||
assert(buffer);
|
||||
assert(size > 0);
|
||||
|
||||
/* Initial trigger for the queue. */
|
||||
if (!p->prep)
|
||||
{
|
||||
p->prep = calloc(1, sizeof(queue_element));
|
||||
p->prep->data = calloc(p->buffersize, p->bits_per_sample / 8);
|
||||
p->prep->size = p->buffersize * p->bits_per_sample / 8;
|
||||
p->next = calloc(1, sizeof(queue_element));
|
||||
p->next->data = calloc(p->buffersize, p->bits_per_sample / 8);
|
||||
p->next->size = p->buffersize * p->bits_per_sample / 8;
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
p->next->data, p->next->size);
|
||||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
|
||||
p->prep->data, p->prep->size);
|
||||
(*p->recorderRecord)->SetRecordState(p->recorderRecord,
|
||||
SL_RECORDSTATE_RECORDING);
|
||||
}
|
||||
|
||||
/* Wait for queue to be filled... */
|
||||
if (!Queue_Count(p->queue))
|
||||
{
|
||||
status = WaitForSingleObject(p->queue->event, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
e = Queue_Dequeue(p->queue);
|
||||
|
||||
if (!e)
|
||||
{
|
||||
WLog_ERR(TAG, "[ERROR] got e=NULL from queue");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = (e->size < size) ? e->size : size;
|
||||
assert(size == e->size);
|
||||
assert(p->buffersize * p->bits_per_sample / 8 == size);
|
||||
memcpy(buffer, e->data, rc);
|
||||
free(e->data);
|
||||
free(e);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -33,9 +33,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -44,51 +41,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t size;
|
||||
void* data;
|
||||
} queue_element;
|
||||
typedef struct opensl_stream OPENSL_STREAM;
|
||||
|
||||
typedef struct opensl_stream
|
||||
{
|
||||
// engine interfaces
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engineEngine;
|
||||
|
||||
// device interfaces
|
||||
SLDeviceVolumeItf deviceVolume;
|
||||
|
||||
// recorder interfaces
|
||||
SLObjectItf recorderObject;
|
||||
SLRecordItf recorderRecord;
|
||||
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
|
||||
|
||||
unsigned int inchannels;
|
||||
unsigned int sr;
|
||||
unsigned int buffersize;
|
||||
unsigned int bits_per_sample;
|
||||
|
||||
wQueue* queue;
|
||||
queue_element* prep;
|
||||
queue_element* next;
|
||||
} OPENSL_STREAM;
|
||||
typedef void (*opensl_receive_t)(void* context, const void* data, size_t size);
|
||||
|
||||
/*
|
||||
Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size
|
||||
in frames. Returns a handle to the OpenSL stream
|
||||
*/
|
||||
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(char* name, int sr,
|
||||
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context,
|
||||
opensl_receive_t receive, int sr,
|
||||
int inchannels,
|
||||
int bufferframes, int bits_per_sample);
|
||||
/*
|
||||
Close the audio device
|
||||
*/
|
||||
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
|
||||
/*
|
||||
Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read.
|
||||
*/
|
||||
FREERDP_LOCAL int android_RecIn(OPENSL_STREAM* p, short* buffer, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -47,7 +47,6 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
@ -56,12 +55,10 @@ typedef struct _AudinOSSDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
|
||||
audinFormat format;
|
||||
AUDIO_FORMAT format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
@ -76,7 +73,7 @@ typedef struct _AudinOSSDevice
|
||||
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
|
||||
|
||||
|
||||
static int audin_oss_get_format(audinFormat* format)
|
||||
static int audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
@ -94,25 +91,17 @@ static int audin_oss_get_format(audinFormat* format)
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return AFMT_A_LAW;
|
||||
#if 0 /* This does not work on my desktop. */
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return AFMT_MU_LAW;
|
||||
#endif
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return AFMT_S16_LE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
audinFormat* format)
|
||||
const AUDIO_FORMAT* format)
|
||||
{
|
||||
int req_fmt = 0;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return FALSE;
|
||||
|
||||
@ -127,21 +116,14 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (format->nSamplesPerSec > 48000 ||
|
||||
format->wBitsPerSample != 4 ||
|
||||
(format->nChannels != 1 && format->nChannels != 2))
|
||||
return FALSE;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return TRUE;
|
||||
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
req_fmt = audin_oss_get_format(format);
|
||||
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -150,7 +132,7 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_oss_set_format(IAudinDevice* device, audinFormat* format,
|
||||
static UINT audin_oss_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
@ -159,17 +141,7 @@ static UINT audin_oss_set_format(IAudinDevice* device, audinFormat* format,
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
oss->FramesPerPacket = FramesPerPacket;
|
||||
CopyMemory(&(oss->format), format, sizeof(audinFormat));
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
oss->FramesPerPacket *= 4; /* Compression ratio. */
|
||||
oss->format.wBitsPerSample *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
oss->format = *format;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -178,13 +150,14 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
char dev_name[PATH_MAX] = "/dev/dsp";
|
||||
char mixer_name[PATH_MAX] = "/dev/mixer";
|
||||
int pcm_handle = -1, mixer_handle;
|
||||
BYTE* buffer = NULL, *encoded_data = NULL;
|
||||
int tmp, buffer_size, encoded_size;
|
||||
BYTE* buffer = NULL;
|
||||
int tmp;
|
||||
size_t buffer_size;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
|
||||
UINT error = 0;
|
||||
DWORD status;
|
||||
|
||||
if (arg == NULL)
|
||||
if (oss == NULL)
|
||||
{
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto err_out;
|
||||
@ -271,8 +244,6 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForSingleObject(oss->stopEvent, 0);
|
||||
@ -299,40 +270,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
if (tmp < buffer_size) /* Not enouth data. */
|
||||
continue;
|
||||
|
||||
/* Process. */
|
||||
switch (oss->format.wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
if (!oss->dsp_context->encode_ms_adpcm(oss->dsp_context,
|
||||
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
encoded_data = oss->dsp_context->adpcm_buffer;
|
||||
encoded_size = oss->dsp_context->adpcm_size;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (!oss->dsp_context->encode_ima_adpcm(oss->dsp_context,
|
||||
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
encoded_data = oss->dsp_context->adpcm_buffer;
|
||||
encoded_size = oss->dsp_context->adpcm_size;
|
||||
break;
|
||||
|
||||
default:
|
||||
encoded_data = buffer;
|
||||
encoded_size = buffer_size;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = oss->receive(encoded_data, encoded_size, oss->user_data)))
|
||||
if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "oss->receive failed with error %"PRIu32"", error);
|
||||
break;
|
||||
@ -438,12 +376,11 @@ static UINT audin_oss_free(IAudinDevice* device)
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
|
||||
freerdp_dsp_context_free(oss->dsp_context);
|
||||
free(oss);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
|
||||
static COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
@ -463,7 +400,7 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, ADDIN_ARGV* args)
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
audin_oss_args, flags, oss, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -552,15 +489,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
oss->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!oss->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
|
||||
(IAudinDevice*) oss)))
|
||||
{
|
||||
@ -570,7 +498,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
freerdp_dsp_context_free(oss->dsp_context);
|
||||
free(oss);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -29,12 +29,13 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/codec/audio.h>
|
||||
#include <freerdp/client/audin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
@ -49,19 +50,16 @@ typedef struct _AudinPulseDevice
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream* stream;
|
||||
int format;
|
||||
int block_size;
|
||||
AUDIO_FORMAT format;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
int bytes_per_frame;
|
||||
BYTE* buffer;
|
||||
int buffer_frames;
|
||||
size_t bytes_per_frame;
|
||||
size_t buffer_frames;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinPulseDevice;
|
||||
|
||||
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
@ -73,18 +71,18 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
DEBUG_DVC("PA_CONTEXT_READY");
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_CONTEXT_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
DEBUG_DVC("state %d", state);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_DVC("state %d", state);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -104,8 +102,8 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -114,8 +112,8 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -128,8 +126,8 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
WLog_ERR(TAG, "bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_disconnect(pulse->context);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
@ -138,7 +136,7 @@ static UINT audin_pulse_connect(IAudinDevice* device)
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_DVC("connected");
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -172,21 +170,23 @@ static UINT audin_pulse_free(IAudinDevice* device)
|
||||
pulse->mainloop = NULL;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_free(pulse->dsp_context);
|
||||
free(pulse);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return FALSE;
|
||||
|
||||
if (!pulse->context)
|
||||
return 0;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case 1: /* PCM */
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
@ -197,8 +197,8 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
|
||||
|
||||
break;
|
||||
|
||||
case 6: /* A-LAW */
|
||||
case 7: /* U-LAW */
|
||||
case WAVE_FORMAT_ALAW: /* A-LAW */
|
||||
case WAVE_FORMAT_MULAW: /* U-LAW */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8) &&
|
||||
@ -209,15 +209,8 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
|
||||
|
||||
break;
|
||||
|
||||
case 0x11: /* IMA ADPCM */
|
||||
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@ -228,27 +221,27 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format,
|
||||
static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
int bs;
|
||||
pa_sample_spec sample_spec = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (FramesPerPacket > 0)
|
||||
{
|
||||
pulse->frames_per_packet = FramesPerPacket;
|
||||
}
|
||||
|
||||
sample_spec.rate = format->nSamplesPerSec;
|
||||
sample_spec.channels = format->nChannels;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case 1: /* PCM */
|
||||
case WAVE_FORMAT_PCM: /* PCM */
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
@ -258,31 +251,27 @@ static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format,
|
||||
case 16:
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6: /* A-LAW */
|
||||
case WAVE_FORMAT_ALAW: /* A-LAW */
|
||||
sample_spec.format = PA_SAMPLE_ALAW;
|
||||
break;
|
||||
|
||||
case 7: /* U-LAW */
|
||||
case WAVE_FORMAT_MULAW: /* U-LAW */
|
||||
sample_spec.format = PA_SAMPLE_ULAW;
|
||||
break;
|
||||
|
||||
case 0x11: /* IMA ADPCM */
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
|
||||
pulse->frames_per_packet = (pulse->frames_per_packet * format->nChannels * 2 /
|
||||
bs + 1) * bs / (format->nChannels * 2);
|
||||
DEBUG_DVC("aligned FramesPerPacket=%"PRIu32"",
|
||||
pulse->frames_per_packet);
|
||||
break;
|
||||
default:
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
pulse->sample_spec = sample_spec;
|
||||
pulse->format = format->wFormatTag;
|
||||
pulse->block_size = format->nBlockAlign;
|
||||
pulse->format = *format;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -295,99 +284,33 @@ static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
DEBUG_DVC("PA_STREAM_READY");
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "PA_STREAM_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
DEBUG_DVC("state %d", state);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_DVC("state %d", state);
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
int frames;
|
||||
int cframes;
|
||||
const void* data;
|
||||
const BYTE* src;
|
||||
int encoded_size;
|
||||
BYTE* encoded_data;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
/* There is a race condition here where we may receive this callback
|
||||
* before the buffer has been set up in the main code. It's probably
|
||||
* possible to fix with additional locking, but it's easier just to
|
||||
* ignore input until the buffer is ready.
|
||||
*/
|
||||
if (pulse->buffer == NULL)
|
||||
{
|
||||
/* WLog_ERR(TAG, "%s: ignoring input, pulse buffer not ready.\n", __func__); */
|
||||
return;
|
||||
}
|
||||
|
||||
pa_stream_peek(stream, &data, &length);
|
||||
frames = length / pulse->bytes_per_frame;
|
||||
DEBUG_DVC("length %"PRIdz" frames %d", length, frames);
|
||||
src = (const BYTE*) data;
|
||||
|
||||
while (frames > 0)
|
||||
{
|
||||
cframes = pulse->frames_per_packet - pulse->buffer_frames;
|
||||
|
||||
if (cframes > frames)
|
||||
cframes = frames;
|
||||
|
||||
memcpy(pulse->buffer + pulse->buffer_frames * pulse->bytes_per_frame,
|
||||
src, cframes * pulse->bytes_per_frame);
|
||||
pulse->buffer_frames += cframes;
|
||||
|
||||
if (pulse->buffer_frames >= pulse->frames_per_packet)
|
||||
{
|
||||
if (pulse->format == 0x11)
|
||||
{
|
||||
if (!pulse->dsp_context->encode_ima_adpcm(pulse->dsp_context,
|
||||
pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
|
||||
pulse->sample_spec.channels, pulse->block_size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded_data = pulse->dsp_context->adpcm_buffer;
|
||||
encoded_size = pulse->dsp_context->adpcm_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded_data = pulse->buffer;
|
||||
encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
|
||||
}
|
||||
|
||||
DEBUG_DVC("encoded %d [%d] to %d [%X]",
|
||||
pulse->buffer_frames, pulse->bytes_per_frame, encoded_size,
|
||||
pulse->format);
|
||||
error = pulse->receive(encoded_data, encoded_size, pulse->user_data);
|
||||
pulse->buffer_frames = 0;
|
||||
|
||||
if (!error)
|
||||
break;
|
||||
}
|
||||
|
||||
src += cframes * pulse->bytes_per_frame;
|
||||
frames -= cframes;
|
||||
}
|
||||
|
||||
error = IFCALLRESULT(CHANNEL_RC_OK, pulse->receive, &pulse->format, data, length, pulse->user_data);
|
||||
pa_stream_drop(stream);
|
||||
|
||||
if (error && pulse->rdpcontext)
|
||||
setChannelError(pulse->rdpcontext, error, "audin_oss_thread_func reported an error");
|
||||
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
|
||||
}
|
||||
|
||||
|
||||
@ -400,6 +323,9 @@ static UINT audin_pulse_close(IAudinDevice* device)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
@ -411,14 +337,6 @@ static UINT audin_pulse_close(IAudinDevice* device)
|
||||
|
||||
pulse->receive = NULL;
|
||||
pulse->user_data = NULL;
|
||||
|
||||
if (pulse->buffer)
|
||||
{
|
||||
free(pulse->buffer);
|
||||
pulse->buffer = NULL;
|
||||
pulse->buffer_frames = 0;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -433,13 +351,15 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
pa_buffer_attr buffer_attr = { 0 };
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
|
||||
if (!pulse || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->context)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!pulse->sample_spec.rate || pulse->stream)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
pulse->buffer = NULL;
|
||||
pulse->receive = receive;
|
||||
pulse->user_data = user_data;
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
@ -449,8 +369,8 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
DEBUG_DVC("pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
|
||||
@ -464,15 +384,19 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
buffer_attr.prebuf = (UINT32) - 1;
|
||||
buffer_attr.minreq = (UINT32) - 1;
|
||||
/* 500ms latency */
|
||||
buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
|
||||
buffer_attr.fragsize = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||
|
||||
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
||||
buffer_attr.fragsize += pulse->format.nBlockAlign - buffer_attr.fragsize %
|
||||
pulse->format.nBlockAlign;
|
||||
|
||||
if (pa_stream_connect_record(pulse->stream,
|
||||
pulse->device_name,
|
||||
&buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
|
||||
@ -486,8 +410,8 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
audin_pulse_close(device);
|
||||
WLog_ERR(TAG, "bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
@ -496,17 +420,8 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
|
||||
pulse->buffer = calloc(pulse->frames_per_packet, pulse->bytes_per_frame);
|
||||
|
||||
if (!pulse->buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
pulse->buffer_frames = 0;
|
||||
DEBUG_DVC("connected");
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "connected");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -528,7 +443,7 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_pulse_args, flags,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_pulse_args, flags,
|
||||
pulse, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -548,7 +463,7 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
|
||||
|
||||
if (!pulse->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
@ -583,6 +498,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
pulse->log = WLog_Get(TAG);
|
||||
pulse->iface.Open = audin_pulse_open;
|
||||
pulse->iface.FormatSupported = audin_pulse_format_supported;
|
||||
pulse->iface.SetFormat = audin_pulse_set_format;
|
||||
@ -593,16 +509,8 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
if ((error = audin_pulse_parse_addin_args(pulse, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_pulse_parse_addin_args failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pulse->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!pulse->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_parse_addin_args failed with error %"PRIu32"!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -610,7 +518,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_threaded_mainloop_new failed");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
@ -619,7 +527,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
if (!pulse->context)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_new failed");
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "pa_context_new failed");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
@ -628,13 +536,13 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
if ((error = audin_pulse_connect((IAudinDevice*) pulse)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_pulse_connect failed");
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "audin_pulse_connect failed");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(pulse->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -47,41 +47,56 @@ typedef struct _AudinWinmmDevice
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
HWAVEIN hWaveIn;
|
||||
PWAVEFORMATEX *ppwfx;
|
||||
PWAVEFORMATEX* ppwfx;
|
||||
PWAVEFORMATEX pwfx_cur;
|
||||
UINT32 ppwfx_size;
|
||||
UINT32 cFormats;
|
||||
UINT32 frames_per_packet;
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} AudinWinmmDevice;
|
||||
|
||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
|
||||
PWAVEHDR pWaveHdr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
MMRESULT mmResult;
|
||||
|
||||
switch(uMsg)
|
||||
switch (uMsg)
|
||||
{
|
||||
case WIM_CLOSE:
|
||||
break;
|
||||
|
||||
case WIM_DATA:
|
||||
pWaveHdr = (WAVEHDR *)dwParam1;
|
||||
pWaveHdr = (WAVEHDR*)dwParam1;
|
||||
|
||||
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
|
||||
{
|
||||
if (pWaveHdr->dwBytesRecorded
|
||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
{
|
||||
if ((error = winmm->receive(pWaveHdr->lpData, pWaveHdr->dwBytesRecorded, winmm->user_data)))
|
||||
AUDIO_FORMAT format;
|
||||
format.cbSize = winmm->pwfx_cur->cbSize;
|
||||
format.nBlockAlign = winmm->pwfx_cur->nBlockAlign;
|
||||
format.nAvgBytesPerSec = winmm->pwfx_cur->nAvgBytesPerSec;
|
||||
format.nChannels = winmm->pwfx_cur->nChannels;
|
||||
format.nSamplesPerSec = winmm->pwfx_cur->nSamplesPerSec;
|
||||
format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
|
||||
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
|
||||
|
||||
if ((error = winmm->receive(&format, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded,
|
||||
winmm->user_data)))
|
||||
break;
|
||||
|
||||
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WIM_OPEN:
|
||||
@ -90,6 +105,7 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||
}
|
||||
@ -97,7 +113,7 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) arg;
|
||||
char *buffer;
|
||||
char* buffer;
|
||||
int size, i;
|
||||
WAVEHDR waveHdr[4];
|
||||
DWORD status;
|
||||
@ -106,87 +122,113 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
||||
{
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
return 0;
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
size = (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet + 7) / 8;
|
||||
size = (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
|
||||
7) / 8;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
buffer = (char *) malloc(size);
|
||||
buffer = (char*) malloc(size);
|
||||
|
||||
if (!buffer)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
waveHdr[i].dwBufferLength = size;
|
||||
waveHdr[i].dwFlags = 0;
|
||||
waveHdr[i].lpData = buffer;
|
||||
|
||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInPrepareHeader failed. %"PRIu32"", 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");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInAddBuffer failed. %"PRIu32"", 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");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
}
|
||||
|
||||
rc = waveInStart(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInStart failed. %"PRIu32"", 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");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
DEBUG_DVC("WaitForSingleObject failed.");
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "WaitForSingleObject failed.");
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInReset(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInReset failed. %"PRIu32"", 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");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInUnprepareHeader failed. %"PRIu32"", 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");
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
free(waveHdr[i].lpData);
|
||||
}
|
||||
|
||||
rc = waveInClose(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
{
|
||||
DEBUG_DVC("waveInClose failed. %"PRIu32"", rc);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
|
||||
}
|
||||
winmm->hWaveIn = NULL;
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -200,6 +242,9 @@ static UINT audin_winmm_free(IAudinDevice* device)
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
free(winmm->ppwfx[i]);
|
||||
@ -208,7 +253,6 @@ static UINT audin_winmm_free(IAudinDevice* device)
|
||||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -219,29 +263,29 @@ static UINT audin_winmm_free(IAudinDevice* device)
|
||||
*/
|
||||
static UINT audin_winmm_close(IAudinDevice* device)
|
||||
{
|
||||
DWORD status;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
SetEvent(winmm->stopEvent);
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
SetEvent(winmm->stopEvent);
|
||||
status = WaitForSingleObject(winmm->thread, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(winmm->thread);
|
||||
CloseHandle(winmm->stopEvent);
|
||||
|
||||
winmm->thread = NULL;
|
||||
winmm->stopEvent = NULL;
|
||||
winmm->receive = NULL;
|
||||
winmm->user_data = NULL;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -250,62 +294,73 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
|
||||
UINT32 FramesPerPacket)
|
||||
{
|
||||
UINT32 i;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
if (!winmm || !format)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
winmm->frames_per_packet = FramesPerPacket;
|
||||
|
||||
for (i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag
|
||||
&& winmm->ppwfx[i]->nChannels == format->nChannels
|
||||
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
||||
&& winmm->ppwfx[i]->nChannels == format->nChannels
|
||||
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
||||
{
|
||||
winmm->pwfx_cur = winmm->ppwfx[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
PWAVEFORMATEX pwfx;
|
||||
BYTE *data;
|
||||
BYTE* data;
|
||||
|
||||
if (!winmm || !format)
|
||||
return FALSE;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
|
||||
if (!pwfx)
|
||||
return FALSE;
|
||||
|
||||
pwfx->cbSize = format->cbSize;
|
||||
pwfx->wFormatTag = format->wFormatTag;
|
||||
pwfx->nChannels = format->nChannels;
|
||||
pwfx->nSamplesPerSec = format->nSamplesPerSec;
|
||||
pwfx->nBlockAlign = format->nBlockAlign;
|
||||
pwfx->wBitsPerSample = format->wBitsPerSample;
|
||||
data = (BYTE *)pwfx + sizeof(WAVEFORMATEX);
|
||||
|
||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||
memcpy(data, format->data, format->cbSize);
|
||||
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
|
||||
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
|
||||
{
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
PWAVEFORMATEX *tmp_ppwfx;
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
|
||||
if (!tmp_ppwfx)
|
||||
return FALSE;
|
||||
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -323,22 +378,27 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
if (!winmm || !receive || !user_data)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
winmm->receive = receive;
|
||||
winmm->user_data = user_data;
|
||||
|
||||
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
if (!(winmm->thread = CreateThread(NULL, 0,
|
||||
audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
||||
CloseHandle(winmm->stopEvent);
|
||||
winmm->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -359,11 +419,9 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_winmm_args, flags, winmm, NULL, NULL);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags,
|
||||
winmm, NULL, NULL);
|
||||
arg = audin_winmm_args;
|
||||
|
||||
do
|
||||
@ -372,17 +430,16 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
winmm->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
@ -406,35 +463,37 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
ADDIN_ARGV* args;
|
||||
AudinWinmmDevice* winmm;
|
||||
UINT error;
|
||||
|
||||
winmm = (AudinWinmmDevice*) calloc(1, sizeof(AudinWinmmDevice));
|
||||
|
||||
if (!winmm)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
winmm->log = WLog_Get(TAG);
|
||||
winmm->iface.Open = audin_winmm_open;
|
||||
winmm->iface.FormatSupported = audin_winmm_format_supported;
|
||||
winmm->iface.SetFormat = audin_winmm_set_format;
|
||||
winmm->iface.Close = audin_winmm_close;
|
||||
winmm->iface.Free = audin_winmm_free;
|
||||
winmm->rdpcontext = pEntryPoints->rdpcontext;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_winmm_parse_addin_args(winmm, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_winmm_parse_addin_args failed with error %"PRIu32"!", error);
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "audin_winmm_parse_addin_args failed with error %"PRIu32"!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
winmm->device_name = _strdup("default");
|
||||
|
||||
if (!winmm->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "_strdup failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
@ -442,16 +501,17 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
|
||||
if (!winmm->ppwfx)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "malloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %"PRIu32"!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ typedef struct _audin_server
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_server_select_format(audin_server_context* context,
|
||||
int client_format_index)
|
||||
size_t client_format_index)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
|
||||
@ -81,7 +81,14 @@ static UINT audin_server_select_format(audin_server_context* context,
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
context->selected_client_format = client_format_index;
|
||||
context->selected_client_format = (SSIZE_T)client_format_index;
|
||||
|
||||
if (!freerdp_dsp_context_reset(audin->dsp_context,
|
||||
&audin->context.client_formats[client_format_index]))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to reset dsp context format!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (audin->opened)
|
||||
{
|
||||
@ -147,8 +154,7 @@ static UINT audin_server_recv_version(audin_server* audin, wStream* s,
|
||||
*/
|
||||
static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
{
|
||||
int i;
|
||||
UINT32 nAvgBytesPerSec;
|
||||
size_t i;
|
||||
ULONG written;
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, MSG_SNDIN_FORMATS);
|
||||
@ -159,35 +165,15 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
|
||||
for (i = 0; i < audin->context.num_server_formats; i++)
|
||||
{
|
||||
nAvgBytesPerSec = audin->context.server_formats[i].nSamplesPerSec *
|
||||
audin->context.server_formats[i].nChannels *
|
||||
audin->context.server_formats[i].wBitsPerSample / 8;
|
||||
AUDIO_FORMAT format = audin->context.server_formats[i];
|
||||
// TODO: Eliminate this
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 18))
|
||||
if (!audio_format_write(s, &format))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].wFormatTag);
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].nChannels);
|
||||
Stream_Write_UINT32(s, audin->context.server_formats[i].nSamplesPerSec);
|
||||
Stream_Write_UINT32(s, nAvgBytesPerSec);
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].nBlockAlign);
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].wBitsPerSample);
|
||||
Stream_Write_UINT16(s, audin->context.server_formats[i].cbSize);
|
||||
|
||||
if (audin->context.server_formats[i].cbSize)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, audin->context.server_formats[i].cbSize))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(s, audin->context.server_formats[i].data,
|
||||
audin->context.server_formats[i].cbSize);
|
||||
}
|
||||
}
|
||||
|
||||
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s),
|
||||
@ -202,7 +188,7 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
|
||||
static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
UINT32 length)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
|
||||
if (length < 8)
|
||||
@ -224,34 +210,24 @@ static UINT audin_server_recv_formats(audin_server* audin, wStream* s,
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
audin->context.client_formats = calloc(audin->context.num_client_formats,
|
||||
sizeof(AUDIO_FORMAT));
|
||||
audin->context.client_formats = audio_formats_new(audin->context.num_client_formats);
|
||||
|
||||
if (!audin->context.client_formats)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
for (i = 0; i < audin->context.num_client_formats; i++)
|
||||
{
|
||||
if (length < 18)
|
||||
AUDIO_FORMAT* format = &audin->context.client_formats[i];
|
||||
|
||||
if (!audio_format_read(s, format))
|
||||
{
|
||||
free(audin->context.client_formats);
|
||||
audio_formats_free(audin->context.client_formats, i);
|
||||
audin->context.client_formats = NULL;
|
||||
WLog_ERR(TAG, "expected length at least 18, but got %"PRIu32"", length);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].wFormatTag);
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].nChannels);
|
||||
Stream_Read_UINT32(s, audin->context.client_formats[i].nSamplesPerSec);
|
||||
Stream_Seek_UINT32(s); /* nAvgBytesPerSec */
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].nBlockAlign);
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].wBitsPerSample);
|
||||
Stream_Read_UINT16(s, audin->context.client_formats[i].cbSize);
|
||||
|
||||
if (audin->context.client_formats[i].cbSize > 0)
|
||||
{
|
||||
Stream_Seek(s, audin->context.client_formats[i].cbSize);
|
||||
}
|
||||
audio_format_print(WLog_Get(TAG), WLOG_DEBUG, format);
|
||||
}
|
||||
|
||||
IFCALLRET(audin->context.Opening, success, &audin->context);
|
||||
@ -339,10 +315,9 @@ static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
||||
AUDIO_FORMAT* format;
|
||||
int sbytes_per_sample;
|
||||
int sbytes_per_frame;
|
||||
BYTE* src;
|
||||
int size;
|
||||
int frames;
|
||||
UINT success = CHANNEL_RC_OK;
|
||||
wStream* out;
|
||||
UINT success = ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (audin->context.selected_client_format < 0)
|
||||
{
|
||||
@ -351,53 +326,32 @@ static UINT audin_server_recv_data(audin_server* audin, wStream* s,
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
out = Stream_New(NULL, 4096);
|
||||
|
||||
if (!out)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
format = &audin->context.client_formats[audin->context.selected_client_format];
|
||||
|
||||
if (format->wFormatTag == WAVE_FORMAT_ADPCM)
|
||||
if (freerdp_dsp_decode(audin->dsp_context, format, Stream_Pointer(s), length, out))
|
||||
{
|
||||
audin->dsp_context->decode_ms_adpcm(audin->dsp_context,
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
size = audin->dsp_context->adpcm_size;
|
||||
src = audin->dsp_context->adpcm_buffer;
|
||||
sbytes_per_sample = 2;
|
||||
sbytes_per_frame = format->nChannels * 2;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
audin->dsp_context->decode_ima_adpcm(audin->dsp_context,
|
||||
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
|
||||
size = audin->dsp_context->adpcm_size;
|
||||
src = audin->dsp_context->adpcm_buffer;
|
||||
sbytes_per_sample = 2;
|
||||
sbytes_per_frame = format->nChannels * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = length;
|
||||
src = Stream_Pointer(s);
|
||||
AUDIO_FORMAT dformat = *format;
|
||||
dformat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
dformat.wBitsPerSample = 16;
|
||||
Stream_SealLength(out);
|
||||
Stream_SetPosition(out, 0);
|
||||
sbytes_per_sample = format->wBitsPerSample / 8;
|
||||
sbytes_per_frame = format->nChannels * sbytes_per_sample;
|
||||
}
|
||||
frames = Stream_Length(out) / sbytes_per_frame;
|
||||
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, &dformat, out, frames);
|
||||
|
||||
if (format->nSamplesPerSec == audin->context.dst_format.nSamplesPerSec
|
||||
&& format->nChannels == audin->context.dst_format.nChannels)
|
||||
{
|
||||
frames = size / sbytes_per_frame;
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
|
||||
}
|
||||
else
|
||||
{
|
||||
audin->dsp_context->resample(audin->dsp_context, src, sbytes_per_sample,
|
||||
format->nChannels, format->nSamplesPerSec, size / sbytes_per_frame,
|
||||
audin->context.dst_format.nChannels, audin->context.dst_format.nSamplesPerSec);
|
||||
frames = audin->dsp_context->resampled_frames;
|
||||
src = audin->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, src, frames);
|
||||
|
||||
if (success)
|
||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
|
||||
WLog_ERR(TAG, "freerdp_dsp_decode failed!");
|
||||
|
||||
Stream_Free(out, TRUE);
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -647,6 +601,16 @@ static BOOL audin_server_open(audin_server_context* context)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL audin_server_is_open(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
|
||||
if (!audin)
|
||||
return FALSE;
|
||||
|
||||
return audin->thread != NULL;
|
||||
}
|
||||
|
||||
static BOOL audin_server_close(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
@ -693,8 +657,9 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
audin->context.frames_per_packet = 4096;
|
||||
audin->context.SelectFormat = audin_server_select_format;
|
||||
audin->context.Open = audin_server_open;
|
||||
audin->context.IsOpen = audin_server_is_open;
|
||||
audin->context.Close = audin_server_close;
|
||||
audin->dsp_context = freerdp_dsp_context_new();
|
||||
audin->dsp_context = freerdp_dsp_context_new(FALSE);
|
||||
|
||||
if (!audin->dsp_context)
|
||||
{
|
||||
@ -709,11 +674,13 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
void audin_server_context_free(audin_server_context* context)
|
||||
{
|
||||
audin_server* audin = (audin_server*) context;
|
||||
|
||||
if (!audin)
|
||||
return;
|
||||
|
||||
audin_server_close(context);
|
||||
|
||||
if (audin->dsp_context)
|
||||
freerdp_dsp_context_free(audin->dsp_context);
|
||||
|
||||
free(audin->context.client_formats);
|
||||
freerdp_dsp_context_free(audin->dsp_context);
|
||||
audio_formats_free(audin->context.client_formats, audin->context.num_client_formats);
|
||||
audio_formats_free(audin->context.server_formats, audin->context.num_server_formats);
|
||||
free(audin);
|
||||
}
|
||||
|
||||
@ -87,9 +87,8 @@ extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
|
||||
FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR pszSubsystem,
|
||||
LPSTR pszType, DWORD dwFlags)
|
||||
{
|
||||
int i, j;
|
||||
size_t i, j;
|
||||
DWORD nAddins;
|
||||
FREERDP_ADDIN* pAddin = NULL;
|
||||
FREERDP_ADDIN** ppAddins = NULL;
|
||||
STATIC_SUBSYSTEM_ENTRY* subsystems;
|
||||
nAddins = 0;
|
||||
@ -105,7 +104,7 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
|
||||
for (i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
|
||||
{
|
||||
pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*) calloc(1, sizeof(FREERDP_ADDIN));
|
||||
|
||||
if (!pAddin)
|
||||
{
|
||||
@ -113,7 +112,7 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
@ -130,8 +129,8 @@ FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPSTR pszName, LPSTR
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
strcpy(pAddin->cName, CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
strcpy(pAddin->cSubsystem, subsystems[j].name);
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", CLIENT_STATIC_ADDIN_TABLE[i].name);
|
||||
sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s", subsystems[j].name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
@ -220,6 +219,7 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
|
||||
if (!ppAddins)
|
||||
{
|
||||
FindClose(hFind);
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return NULL;
|
||||
}
|
||||
@ -298,6 +298,7 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
ppAddins[nAddins] = NULL;
|
||||
return ppAddins;
|
||||
error_out:
|
||||
FindClose(hFind);
|
||||
freerdp_channels_addin_list_free(ppAddins);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -239,7 +239,7 @@ static UINT cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s,
|
||||
Stream_Read_UINT16(s, capabilitySetType); /* capabilitySetType (2 bytes) */
|
||||
Stream_Read_UINT16(s, lengthCapability); /* lengthCapability (2 bytes) */
|
||||
|
||||
if (lengthCapability < 4 || Stream_GetRemainingLength(s) < lengthCapability-4)
|
||||
if (lengthCapability < 4 || Stream_GetRemainingLength(s) < lengthCapability - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
switch (capabilitySetType)
|
||||
@ -337,7 +337,6 @@ static UINT cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr,
|
||||
request.msgFlags = flags;
|
||||
request.dataLen = length;
|
||||
request.haveClipDataId = FALSE;
|
||||
|
||||
Stream_Read_UINT32(s, request.streamId); /* streamId (4 bytes) */
|
||||
Stream_Read_UINT32(s, request.listIndex); /* listIndex (4 bytes) */
|
||||
Stream_Read_UINT32(s, request.dwFlags); /* dwFlags (4 bytes) */
|
||||
@ -633,6 +632,7 @@ static UINT cliprdr_temp_directory(CliprdrClientContext* context,
|
||||
}
|
||||
|
||||
length = ConvertToUnicode(CP_UTF8, 0, tempDirectory->szTempDir, -1, &wszTempDir, 0);
|
||||
|
||||
if (length < 0)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
@ -924,7 +924,6 @@ static UINT cliprdr_client_file_contents_request(CliprdrClientContext* context,
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG,
|
||||
"ClientFileContentsRequest: streamId: 0x%08"PRIX32"",
|
||||
fileContentsRequest->streamId);
|
||||
|
||||
return cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
@ -938,7 +937,6 @@ static UINT cliprdr_client_file_contents_response(CliprdrClientContext* context,
|
||||
{
|
||||
wStream* s;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, fileContentsResponse->msgFlags,
|
||||
4 + fileContentsResponse->cbRequested);
|
||||
|
||||
@ -1045,7 +1043,6 @@ static VOID VCAPITYPE cliprdr_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -1131,7 +1128,8 @@ static UINT cliprdr_virtual_channel_event_connected(cliprdrPlugin* cliprdr,
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
if (!(cliprdr->thread = CreateThread(NULL, 0, cliprdr_virtual_channel_client_thread, (void*) cliprdr,
|
||||
if (!(cliprdr->thread = CreateThread(NULL, 0, cliprdr_virtual_channel_client_thread,
|
||||
(void*) cliprdr,
|
||||
0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
@ -1152,6 +1150,9 @@ static UINT cliprdr_virtual_channel_event_disconnected(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
UINT rc;
|
||||
|
||||
if (cliprdr->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(cliprdr->queue, 0)
|
||||
&& (WaitForSingleObject(cliprdr->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
@ -1258,7 +1259,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
strcpy(cliprdr->channelDef.name, "cliprdr");
|
||||
sprintf_s(cliprdr->channelDef.name, ARRAYSIZE(cliprdr->channelDef.name), "cliprdr");
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||
|
||||
@ -639,8 +639,7 @@ static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
|
||||
if (channel->dvc_data)
|
||||
{
|
||||
/* Fragmented data */
|
||||
if (Stream_GetPosition(channel->dvc_data) + dataSize > (UINT32) Stream_Capacity(
|
||||
channel->dvc_data))
|
||||
if (Stream_GetPosition(channel->dvc_data) + dataSize > Stream_Capacity(channel->dvc_data))
|
||||
{
|
||||
WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
|
||||
Stream_Release(channel->dvc_data);
|
||||
@ -648,7 +647,7 @@ static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Write(channel->dvc_data, Stream_Pointer(data), dataSize);
|
||||
Stream_Copy(data, channel->dvc_data, dataSize);
|
||||
|
||||
if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
|
||||
{
|
||||
@ -713,9 +712,11 @@ static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s)
|
||||
{
|
||||
case CHANNEL_RC_OK:
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
case CHANNEL_RC_NOT_CONNECTED:
|
||||
Stream_Free(s, TRUE);
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
case CHANNEL_RC_BAD_CHANNEL_HANDLE:
|
||||
Stream_Free(s, TRUE);
|
||||
WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
|
||||
@ -878,6 +879,9 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
if (!drdynvc)
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 3)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
|
||||
Stream_Seek(s, 1); /* pad */
|
||||
Stream_Read_UINT16(s, drdynvc->version);
|
||||
@ -887,6 +891,9 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
*/
|
||||
if ((drdynvc->version == 2) || (drdynvc->version == 3))
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
|
||||
Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
|
||||
Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
|
||||
@ -898,6 +905,21 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
return status;
|
||||
}
|
||||
|
||||
static UINT32 drdynvc_cblen_to_bytes(int cbLen)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
|
||||
{
|
||||
UINT32 val;
|
||||
@ -933,6 +955,8 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
UINT32 ChannelId;
|
||||
wStream* data_out;
|
||||
UINT channel_status;
|
||||
char* name;
|
||||
size_t length;
|
||||
|
||||
if (!drdynvc)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
@ -955,13 +979,20 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
drdynvc->state = DRDYNVC_STATE_READY;
|
||||
}
|
||||
|
||||
if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
pos = Stream_GetPosition(s);
|
||||
name = (char*)Stream_Pointer(s);
|
||||
length = Stream_GetRemainingLength(s);
|
||||
|
||||
if (strnlen(name, length) >= length)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s",
|
||||
ChannelId,
|
||||
Stream_Pointer(s));
|
||||
channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId,
|
||||
(char*) Stream_Pointer(s));
|
||||
ChannelId, name);
|
||||
channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name);
|
||||
data_out = Stream_New(NULL, pos + 4);
|
||||
|
||||
if (!data_out)
|
||||
@ -1022,6 +1053,10 @@ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp,
|
||||
UINT status;
|
||||
UINT32 Length;
|
||||
UINT32 ChannelId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
Length = drdynvc_read_variable_uint(s, Sp);
|
||||
WLog_Print(drdynvc->log, WLOG_DEBUG,
|
||||
@ -1045,6 +1080,10 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId,
|
||||
wStream* s)
|
||||
{
|
||||
UINT32 ChannelId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", Sp,
|
||||
cbChId,
|
||||
@ -1064,6 +1103,10 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp,
|
||||
UINT error;
|
||||
UINT32 ChannelId;
|
||||
wStream* data_out;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
ChannelId = drdynvc_read_variable_uint(s, cbChId);
|
||||
WLog_Print(drdynvc->log, WLOG_DEBUG, "process_close_request: Sp=%d cbChId=%d, ChannelId=%"PRIu32"",
|
||||
Sp,
|
||||
@ -1106,6 +1149,10 @@ static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s)
|
||||
int Cmd;
|
||||
int Sp;
|
||||
int cbChId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT8(s, value);
|
||||
Cmd = (value & 0xf0) >> 4;
|
||||
Sp = (value & 0x0c) >> 2;
|
||||
@ -1164,7 +1211,7 @@ static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc,
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength))
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
|
||||
{
|
||||
WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
|
||||
Stream_Free(drdynvc->data_in, TRUE);
|
||||
@ -1205,7 +1252,6 @@ static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
if (!drdynvc || (drdynvc->OpenHandle != openHandle))
|
||||
{
|
||||
WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1220,7 +1266,6 @@ static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -1380,7 +1425,8 @@ static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVO
|
||||
|
||||
drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
|
||||
|
||||
if (!(drdynvc->thread = CreateThread(NULL, 0, drdynvc_virtual_channel_client_thread, (void*) drdynvc,
|
||||
if (!(drdynvc->thread = CreateThread(NULL, 0, drdynvc_virtual_channel_client_thread,
|
||||
(void*) drdynvc,
|
||||
0, NULL)))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
@ -1401,6 +1447,9 @@ static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
|
||||
{
|
||||
UINT status;
|
||||
|
||||
if (drdynvc->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!drdynvc)
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
|
||||
@ -1611,7 +1660,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints, PVOI
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP;
|
||||
strcpy(drdynvc->channelDef.name, "drdynvc");
|
||||
sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name), "drdynvc");
|
||||
drdynvc->state = DRDYNVC_STATE_INITIAL;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
|
||||
@ -53,8 +53,8 @@
|
||||
|
||||
static void drive_file_fix_path(WCHAR* path)
|
||||
{
|
||||
int i;
|
||||
int length;
|
||||
size_t i;
|
||||
size_t length;
|
||||
length = (int) _wcslen(path);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
@ -332,11 +332,6 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (DesiredAccess & 0x1000L)
|
||||
{
|
||||
DesiredAccess = (DesiredAccess & ~0x1000L) | GENERIC_WRITE;
|
||||
}
|
||||
|
||||
file->file_handle = INVALID_HANDLE_VALUE;
|
||||
file->find_handle = INVALID_HANDLE_VALUE;
|
||||
file->id = id;
|
||||
@ -360,6 +355,7 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
|
||||
BOOL drive_file_free(DRIVE_FILE* file)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
|
||||
if (!file)
|
||||
return FALSE;
|
||||
|
||||
@ -387,7 +383,6 @@ BOOL drive_file_free(DRIVE_FILE* file)
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
|
||||
fail:
|
||||
DEBUG_WSTR("Free %s", file->fullpath);
|
||||
free(file->fullpath);
|
||||
@ -447,30 +442,14 @@ BOOL drive_file_write(DRIVE_FILE* file, BYTE* buffer, UINT32 Length)
|
||||
|
||||
BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, wStream* output)
|
||||
{
|
||||
WIN32_FIND_DATAW findFileData;
|
||||
HANDLE hFind;
|
||||
WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
|
||||
DEBUG_WSTR("FindFirstFile %s", file->fullpath);
|
||||
|
||||
if (!file || !output)
|
||||
return FALSE;
|
||||
|
||||
if ((hFind = FindFirstFileW(file->fullpath, &findFileData)) == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ZeroMemory(&findFileData, sizeof(findFileData));
|
||||
findFileData.dwFileAttributes = GetFileAttributesW(file->fullpath);
|
||||
|
||||
if (findFileData.dwFileAttributes == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
#else
|
||||
if (!GetFileAttributesExW(file->fullpath, GetFileExInfoStandard, &fileAttributes))
|
||||
goto out_fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
FindClose(hFind);
|
||||
|
||||
switch (FsInformationClass)
|
||||
{
|
||||
@ -481,15 +460,15 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 36); /* Length */
|
||||
Stream_Write_UINT32(output, findFileData.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, findFileData.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwLowDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftCreationTime.dwHighDateTime); /* CreationTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwLowDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.ftLastWriteTime.dwHighDateTime); /* ChangeTime */
|
||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||
/* Reserved(4), MUST NOT be added! */
|
||||
break;
|
||||
|
||||
@ -500,13 +479,13 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 22); /* Length */
|
||||
Stream_Write_UINT32(output, findFileData.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, findFileData.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, findFileData.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, findFileData.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeHigh); /* AllocationSize */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeLow); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, fileAttributes.nFileSizeHigh); /* EndOfFile */
|
||||
Stream_Write_UINT32(output, 0); /* NumberOfLinks */
|
||||
Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */
|
||||
Stream_Write_UINT8(output, findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? TRUE :
|
||||
Stream_Write_UINT8(output, fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? TRUE :
|
||||
FALSE); /* Directory */
|
||||
/* Reserved(2), MUST NOT be added! */
|
||||
break;
|
||||
@ -518,7 +497,7 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
goto out_fail;
|
||||
|
||||
Stream_Write_UINT32(output, 8); /* Length */
|
||||
Stream_Write_UINT32(output, findFileData.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, fileAttributes.dwFileAttributes); /* FileAttributes */
|
||||
Stream_Write_UINT32(output, 0); /* ReparseTag */
|
||||
break;
|
||||
|
||||
@ -636,8 +615,6 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
liSize.QuadPart = size & 0xFFFFFFFF;
|
||||
|
||||
if (!SetFilePointerEx(file->file_handle, liSize, NULL, FILE_BEGIN))
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to truncate %s to %d (%"PRId32")", file->fullpath, size, GetLastError());
|
||||
@ -700,6 +677,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
|
||||
FileNameLength);
|
||||
|
||||
if (!fullpath)
|
||||
{
|
||||
WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
|
||||
|
||||
@ -53,6 +53,7 @@ struct _DRIVE_DEVICE
|
||||
DEVICE device;
|
||||
|
||||
WCHAR* path;
|
||||
BOOL automount;
|
||||
UINT32 PathLength;
|
||||
wListDictionary* files;
|
||||
|
||||
@ -97,7 +98,7 @@ static DWORD drive_map_windows_err(DWORD fs_errno)
|
||||
|
||||
case ERROR_NOT_READY:
|
||||
rc = STATUS_NO_SUCH_DEVICE;
|
||||
break;
|
||||
break;
|
||||
|
||||
case ERROR_FILE_EXISTS:
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
@ -167,7 +168,7 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
if (!drive || !irp || !irp->devman || !irp->Complete)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (Stream_GetRemainingLength(irp->input) < 6*4+8)
|
||||
if (Stream_GetRemainingLength(irp->input) < 6 * 4 + 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(irp->input, DesiredAccess);
|
||||
@ -177,6 +178,7 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT32(irp->input, CreateDisposition);
|
||||
Stream_Read_UINT32(irp->input, CreateOptions);
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
|
||||
if (Stream_GetRemainingLength(irp->input) < PathLength)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
@ -238,6 +240,7 @@ static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
void* key;
|
||||
DRIVE_FILE* file;
|
||||
|
||||
if (!drive || !irp || !irp->Complete || !irp->output)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
@ -279,8 +282,8 @@ static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -302,6 +305,7 @@ static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
|
||||
else
|
||||
{
|
||||
BYTE* buffer = Stream_Pointer(irp->output) + sizeof(UINT32);
|
||||
|
||||
if (!drive_file_read(file, buffer, &Length))
|
||||
{
|
||||
irp->IoStatus = drive_map_windows_err(GetLastError());
|
||||
@ -337,8 +341,8 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -377,8 +381,8 @@ static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(irp->input, FsInformationClass);
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -411,8 +415,8 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT32(irp->input, FsInformationClass);
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Seek(irp->input, 24); /* Padding */
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -523,6 +527,7 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive,
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(output, 12 + length))
|
||||
{
|
||||
free(outStr);
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
@ -595,7 +600,6 @@ static UINT drive_process_irp_silent_ignore(DRIVE_DEVICE* drive, IRP* irp)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(irp->input, FsInformationClass);
|
||||
|
||||
Stream_Write_UINT32(irp->output, 0); /* Length */
|
||||
return irp->Complete(irp);
|
||||
}
|
||||
@ -624,8 +628,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
Stream_Seek(irp->input, 23); /* Padding */
|
||||
path = (WCHAR*) Stream_Pointer(irp->input);
|
||||
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
@ -689,6 +693,7 @@ static UINT drive_process_irp_device_control(DRIVE_DEVICE* drive, IRP* irp)
|
||||
static UINT drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
if (!drive || !irp)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
@ -752,7 +757,7 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if(!drive)
|
||||
if (!drive)
|
||||
{
|
||||
error = ERROR_INVALID_PARAMETER;
|
||||
goto fail;
|
||||
@ -790,6 +795,7 @@ static DWORD WINAPI drive_thread_func(LPVOID arg)
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
if (error && drive && drive->rdpcontext)
|
||||
setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error");
|
||||
|
||||
@ -821,6 +827,7 @@ static UINT drive_irp_request(DEVICE* device, IRP* irp)
|
||||
static UINT drive_free_int(DRIVE_DEVICE* drive)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!drive)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
@ -857,32 +864,25 @@ static UINT drive_free(DEVICE* device)
|
||||
return drive_free_int(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function used for freeing list dictionary value object
|
||||
*/
|
||||
static void drive_file_objfree(void* obj)
|
||||
{
|
||||
drive_file_free((DRIVE_FILE*) obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
char* name, char* path)
|
||||
const char* name, const char* path, BOOL automount)
|
||||
{
|
||||
size_t i, length;
|
||||
DRIVE_DEVICE* drive;
|
||||
UINT error;
|
||||
#ifdef WIN32
|
||||
|
||||
/*
|
||||
* We cannot enter paths like c:\ because : is an arg separator
|
||||
* thus, paths are entered as c+\ and the + is substituted here
|
||||
*/
|
||||
if (path[1] == '+')
|
||||
{
|
||||
if ((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
|
||||
{
|
||||
path[1] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (name[0] && path[0])
|
||||
{
|
||||
@ -900,6 +900,7 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
drive->device.IRPRequest = drive_irp_request;
|
||||
drive->device.Free = drive_free;
|
||||
drive->rdpcontext = pEntryPoints->rdpcontext;
|
||||
drive->automount = automount;
|
||||
length = strlen(name);
|
||||
drive->device.data = Stream_New(NULL, length + 1);
|
||||
|
||||
@ -913,8 +914,8 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
for (i = 0; i <= length; i++)
|
||||
Stream_Write_UINT8(drive->device.data, name[i] < 0 ? '_' : name[i]);
|
||||
|
||||
if ((pathLength > 1) && (path[pathLength-1] == '/'))
|
||||
pathLength --;
|
||||
if ((pathLength > 1) && (path[pathLength - 1] == '/'))
|
||||
pathLength --;
|
||||
|
||||
if (ConvertToUnicode(sys_code_page, 0, path, pathLength, &drive->path, 0) <= 0)
|
||||
{
|
||||
@ -932,7 +933,7 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
ListDictionary_ValueObject(drive->files)->fnObjectFree = (OBJECT_FREE_FN) drive_file_free;
|
||||
ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree;
|
||||
drive->IrpQueue = MessageQueue_New(NULL);
|
||||
|
||||
if (!drive->IrpQueue)
|
||||
@ -1015,7 +1016,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
}
|
||||
}
|
||||
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path, drive->automount);
|
||||
#else
|
||||
sys_code_page = GetACP();
|
||||
|
||||
@ -1034,7 +1035,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path, drive->automount);
|
||||
}
|
||||
else if (strcmp(drive->Path, "*") == 0)
|
||||
{
|
||||
@ -1065,7 +1066,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = drive_register_drive_path(pEntryPoints, bufdup, devdup)))
|
||||
if ((error = drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -1074,7 +1075,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
}
|
||||
else
|
||||
{
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
error = drive_register_drive_path(pEntryPoints, drive->Name, drive->Path, drive->automount);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -998,7 +998,6 @@ static VOID VCAPITYPE encomsp_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -1087,7 +1086,7 @@ static UINT encomsp_virtual_channel_event_connected(encomspPlugin* encomsp,
|
||||
}
|
||||
|
||||
if (!(encomsp->thread = CreateThread(NULL, 0,
|
||||
encomsp_virtual_channel_client_thread, (void*) encomsp,
|
||||
encomsp_virtual_channel_client_thread, (void*) encomsp,
|
||||
0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
@ -1107,6 +1106,9 @@ static UINT encomsp_virtual_channel_event_disconnected(encomspPlugin* encomsp)
|
||||
{
|
||||
UINT rc;
|
||||
|
||||
if (encomsp->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(encomsp->queue, 0)
|
||||
&& (WaitForSingleObject(encomsp->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
@ -1217,7 +1219,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints, PVOI
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
strcpy(encomsp->channelDef.name, "encomsp");
|
||||
sprintf_s(encomsp->channelDef.name, ARRAYSIZE(encomsp->channelDef.name), "encomsp");
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||
|
||||
@ -68,6 +68,7 @@ struct _PRINTER_DEVICE
|
||||
|
||||
HANDLE thread;
|
||||
rdpContext* rdpcontext;
|
||||
char port[64];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -322,7 +323,6 @@ static UINT printer_free(DEVICE* device)
|
||||
if (printer_dev->printer)
|
||||
printer_dev->printer->Free(printer_dev->printer);
|
||||
|
||||
free(printer_dev->device.name);
|
||||
Stream_Free(printer_dev->device.data, TRUE);
|
||||
free(printer_dev);
|
||||
return CHANNEL_RC_OK;
|
||||
@ -336,7 +336,6 @@ static UINT printer_free(DEVICE* device)
|
||||
UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
rdpPrinter* printer)
|
||||
{
|
||||
char* port;
|
||||
UINT32 Flags;
|
||||
int DriverNameLen;
|
||||
WCHAR* DriverName = NULL;
|
||||
@ -346,26 +345,17 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
BYTE* CachedPrinterConfigData;
|
||||
PRINTER_DEVICE* printer_dev;
|
||||
UINT error;
|
||||
port = malloc(10);
|
||||
|
||||
if (!port)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
sprintf_s(port, 10, "PRN%d", printer->id);
|
||||
printer_dev = (PRINTER_DEVICE*) calloc(1, sizeof(PRINTER_DEVICE));
|
||||
|
||||
if (!printer_dev)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
free(port);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%d", printer->id);
|
||||
printer_dev->device.type = RDPDR_DTYP_PRINT;
|
||||
printer_dev->device.name = port;
|
||||
printer_dev->device.name = printer_dev->port;
|
||||
printer_dev->device.IRPRequest = printer_irp_request;
|
||||
printer_dev->device.Free = printer_free;
|
||||
printer_dev->rdpcontext = pEntryPoints->rdpcontext;
|
||||
@ -445,7 +435,8 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(printer_dev->thread = CreateThread(NULL, 0, printer_thread_func, (void*) printer_dev, 0, NULL)))
|
||||
if (!(printer_dev->thread = CreateThread(NULL, 0, printer_thread_func, (void*) printer_dev, 0,
|
||||
NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
@ -454,12 +445,7 @@ UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
CloseHandle(printer_dev->stopEvent);
|
||||
CloseHandle(printer_dev->event);
|
||||
_aligned_free(printer_dev->pIrpList);
|
||||
Stream_Free(printer_dev->device.data, TRUE);
|
||||
free(printer_dev);
|
||||
free(port);
|
||||
printer_free(&printer_dev->device);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -95,23 +95,6 @@ UINT rail_send_channel_data(railPlugin* rail, void* data, size_t length)
|
||||
return rail_send(rail, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* used by rail_client_execute() to free RAIL_EXEC_ORDER's
|
||||
* internal malloced memory;
|
||||
*/
|
||||
static void rail_client_clean_exec_order(RAIL_EXEC_ORDER* exec)
|
||||
{
|
||||
if (!exec)
|
||||
return;
|
||||
|
||||
free(exec->exeOrFile.string);
|
||||
exec->exeOrFile.string = NULL;
|
||||
free(exec->workingDir.string);
|
||||
exec->workingDir.string = NULL;
|
||||
free(exec->arguments.string);
|
||||
exec->arguments.string = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback Interface
|
||||
*/
|
||||
@ -122,17 +105,22 @@ static void rail_client_clean_exec_order(RAIL_EXEC_ORDER* exec)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_execute(RailClientContext* context,
|
||||
RAIL_EXEC_ORDER* exec)
|
||||
const RAIL_EXEC_ORDER* exec)
|
||||
{
|
||||
char* exeOrFile;
|
||||
UINT error;
|
||||
railPlugin* rail;
|
||||
UINT16 flags;
|
||||
RAIL_UNICODE_STRING ruExeOrFile = { 0 };
|
||||
RAIL_UNICODE_STRING ruWorkingDir = { 0 };
|
||||
RAIL_UNICODE_STRING ruArguments = { 0 };
|
||||
|
||||
if (!context || !exec)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
rail = (railPlugin*) context->handle;
|
||||
exeOrFile = exec->RemoteApplicationProgram;
|
||||
flags = exec->flags;
|
||||
|
||||
if (!exeOrFile)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -140,17 +128,22 @@ static UINT rail_client_execute(RailClientContext* context,
|
||||
if (strnlen(exeOrFile, MAX_PATH) >= 2)
|
||||
{
|
||||
if (strncmp(exeOrFile, "||", 2) != 0)
|
||||
exec->flags |= RAIL_EXEC_FLAG_FILE;
|
||||
flags |= RAIL_EXEC_FLAG_FILE;
|
||||
}
|
||||
|
||||
rail_string_to_unicode_string(exec->RemoteApplicationProgram,
|
||||
&exec->exeOrFile); /* RemoteApplicationProgram */
|
||||
rail_string_to_unicode_string(exec->RemoteApplicationWorkingDir,
|
||||
&exec->workingDir); /* ShellWorkingDirectory */
|
||||
rail_string_to_unicode_string(exec->RemoteApplicationArguments,
|
||||
&exec->arguments); /* RemoteApplicationCmdLine */
|
||||
error = rail_send_client_exec_order(rail, exec);
|
||||
rail_client_clean_exec_order(exec);
|
||||
if (!rail_string_to_unicode_string(exec->RemoteApplicationProgram,
|
||||
&ruExeOrFile) || /* RemoteApplicationProgram */
|
||||
!rail_string_to_unicode_string(exec->RemoteApplicationWorkingDir,
|
||||
&ruWorkingDir) || /* ShellWorkingDirectory */
|
||||
!rail_string_to_unicode_string(exec->RemoteApplicationArguments,
|
||||
&ruArguments)) /* RemoteApplicationCmdLine */
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
else
|
||||
error = rail_send_client_exec_order(rail, flags, &ruExeOrFile, &ruWorkingDir, &ruArguments);
|
||||
|
||||
free(ruExeOrFile.string);
|
||||
free(ruWorkingDir.string);
|
||||
free(ruArguments.string);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -160,7 +153,7 @@ static UINT rail_client_execute(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_activate(RailClientContext* context,
|
||||
RAIL_ACTIVATE_ORDER* activate)
|
||||
const RAIL_ACTIVATE_ORDER* activate)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -243,84 +236,87 @@ static UINT rail_send_client_sysparam(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_system_param(RailClientContext* context,
|
||||
RAIL_SYSPARAM_ORDER* sysparam)
|
||||
const RAIL_SYSPARAM_ORDER* sysInParam)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
RAIL_SYSPARAM_ORDER sysparam;
|
||||
|
||||
if (!context || !sysparam)
|
||||
if (!context || !sysInParam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_HIGH_CONTRAST)
|
||||
{
|
||||
sysparam->param = SPI_SET_HIGH_CONTRAST;
|
||||
sysparam = *sysInParam;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if (sysparam.params & SPI_MASK_SET_HIGH_CONTRAST)
|
||||
{
|
||||
sysparam.param = SPI_SET_HIGH_CONTRAST;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_TASKBAR_POS)
|
||||
if (sysparam.params & SPI_MASK_TASKBAR_POS)
|
||||
{
|
||||
sysparam->param = SPI_TASKBAR_POS;
|
||||
sysparam.param = SPI_TASKBAR_POS;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_MOUSE_BUTTON_SWAP)
|
||||
if (sysparam.params & SPI_MASK_SET_MOUSE_BUTTON_SWAP)
|
||||
{
|
||||
sysparam->param = SPI_SET_MOUSE_BUTTON_SWAP;
|
||||
sysparam.param = SPI_SET_MOUSE_BUTTON_SWAP;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_KEYBOARD_PREF)
|
||||
if (sysparam.params & SPI_MASK_SET_KEYBOARD_PREF)
|
||||
{
|
||||
sysparam->param = SPI_SET_KEYBOARD_PREF;
|
||||
sysparam.param = SPI_SET_KEYBOARD_PREF;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_DRAG_FULL_WINDOWS)
|
||||
if (sysparam.params & SPI_MASK_SET_DRAG_FULL_WINDOWS)
|
||||
{
|
||||
sysparam->param = SPI_SET_DRAG_FULL_WINDOWS;
|
||||
sysparam.param = SPI_SET_DRAG_FULL_WINDOWS;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_KEYBOARD_CUES)
|
||||
if (sysparam.params & SPI_MASK_SET_KEYBOARD_CUES)
|
||||
{
|
||||
sysparam->param = SPI_SET_KEYBOARD_CUES;
|
||||
sysparam.param = SPI_SET_KEYBOARD_CUES;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysparam->params & SPI_MASK_SET_WORK_AREA)
|
||||
if (sysparam.params & SPI_MASK_SET_WORK_AREA)
|
||||
{
|
||||
sysparam->param = SPI_SET_WORK_AREA;
|
||||
sysparam.param = SPI_SET_WORK_AREA;
|
||||
|
||||
if ((error = rail_send_client_sysparam(context, sysparam)))
|
||||
if ((error = rail_send_client_sysparam(context, &sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_client_sysparam failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
@ -336,7 +332,7 @@ static UINT rail_client_system_param(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_system_param(RailClientContext* context,
|
||||
RAIL_SYSPARAM_ORDER* sysparam)
|
||||
const RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
if (!context || !sysparam)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -350,7 +346,7 @@ static UINT rail_server_system_param(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_system_command(RailClientContext* context,
|
||||
RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
const RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -367,7 +363,7 @@ static UINT rail_client_system_command(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_handshake(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_ORDER* handshake)
|
||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -384,7 +380,7 @@ static UINT rail_client_handshake(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_handshake(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_ORDER* handshake)
|
||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
if (!context || !handshake)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -398,7 +394,7 @@ static UINT rail_server_handshake(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_handshake_ex(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -415,7 +411,7 @@ static UINT rail_client_handshake_ex(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_handshake_ex(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
if (!context || !handshakeEx)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -429,7 +425,7 @@ static UINT rail_server_handshake_ex(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_notify_event(RailClientContext* context,
|
||||
RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -446,7 +442,7 @@ static UINT rail_client_notify_event(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_window_move(RailClientContext* context,
|
||||
RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
const RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -463,7 +459,7 @@ static UINT rail_client_window_move(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_local_move_size(RailClientContext* context,
|
||||
RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
|
||||
const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
|
||||
{
|
||||
if (!context || !localMoveSize)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -477,7 +473,7 @@ static UINT rail_server_local_move_size(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_min_max_info(RailClientContext* context,
|
||||
RAIL_MINMAXINFO_ORDER* minMaxInfo)
|
||||
const RAIL_MINMAXINFO_ORDER* minMaxInfo)
|
||||
{
|
||||
if (!context || !minMaxInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -491,7 +487,7 @@ static UINT rail_server_min_max_info(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_information(RailClientContext* context,
|
||||
RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
const RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -508,7 +504,7 @@ static UINT rail_client_information(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_system_menu(RailClientContext* context,
|
||||
RAIL_SYSMENU_ORDER* sysmenu)
|
||||
const RAIL_SYSMENU_ORDER* sysmenu)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -525,7 +521,7 @@ static UINT rail_client_system_menu(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_language_bar_info(RailClientContext* context,
|
||||
RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -542,7 +538,7 @@ static UINT rail_client_language_bar_info(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_language_bar_info(RailClientContext* context,
|
||||
RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
{
|
||||
if (!context || !langBarInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -556,7 +552,7 @@ static UINT rail_server_language_bar_info(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_execute_result(RailClientContext* context,
|
||||
RAIL_EXEC_RESULT_ORDER* execResult)
|
||||
const RAIL_EXEC_RESULT_ORDER* execResult)
|
||||
{
|
||||
if (!context || !execResult)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -570,7 +566,7 @@ static UINT rail_server_execute_result(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_client_get_appid_request(RailClientContext* context,
|
||||
RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
{
|
||||
railPlugin* rail;
|
||||
|
||||
@ -587,7 +583,7 @@ static UINT rail_client_get_appid_request(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_server_get_appid_response(RailClientContext* context,
|
||||
RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
|
||||
const RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
|
||||
{
|
||||
if (!context || !getAppIdResp)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -680,7 +676,6 @@ static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWO
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -770,7 +765,7 @@ static UINT rail_virtual_channel_event_connected(railPlugin* rail, LPVOID pData,
|
||||
}
|
||||
|
||||
if (!(rail->thread = CreateThread(NULL, 0,
|
||||
rail_virtual_channel_client_thread, (void*) rail, 0,
|
||||
rail_virtual_channel_client_thread, (void*) rail, 0,
|
||||
NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
@ -791,6 +786,9 @@ static UINT rail_virtual_channel_event_disconnected(railPlugin* rail)
|
||||
{
|
||||
UINT rc;
|
||||
|
||||
if (rail->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(rail->queue, 0)
|
||||
&& (WaitForSingleObject(rail->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
@ -895,7 +893,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
strcpy(rail->channelDef.name, "rail");
|
||||
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), "rail");
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_unicode_string(wStream* s, RAIL_UNICODE_STRING* unicode_string)
|
||||
static UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
if (!s || !unicode_string)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -58,20 +58,24 @@ static UINT rail_write_unicode_string(wStream* s, RAIL_UNICODE_STRING* unicode_s
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_unicode_string_value(wStream* s, RAIL_UNICODE_STRING* unicode_string)
|
||||
static UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (!s || !unicode_string)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (unicode_string->length > 0)
|
||||
length = unicode_string->length;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, unicode_string->length))
|
||||
if (!Stream_EnsureRemainingCapacity(s, length))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write(s, unicode_string->string, unicode_string->length); /* string */
|
||||
Stream_Write(s, unicode_string->string, length); /* string */
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
@ -103,14 +107,16 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_high_contrast(wStream* s, RAIL_HIGH_CONTRAST* highContrast)
|
||||
static UINT rail_write_high_contrast(wStream* s, const RAIL_HIGH_CONTRAST* highContrast)
|
||||
{
|
||||
UINT32 colorSchemeLength;
|
||||
|
||||
if (!s || !highContrast)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
highContrast->colorSchemeLength = highContrast->colorScheme.length + 2;
|
||||
colorSchemeLength = highContrast->colorScheme.length + 2;
|
||||
Stream_Write_UINT32(s, highContrast->flags); /* flags (4 bytes) */
|
||||
Stream_Write_UINT32(s, highContrast->colorSchemeLength); /* colorSchemeLength (4 bytes) */
|
||||
Stream_Write_UINT32(s, colorSchemeLength); /* colorSchemeLength (4 bytes) */
|
||||
return rail_write_unicode_string(s, &highContrast->colorScheme); /* colorScheme */
|
||||
}
|
||||
|
||||
@ -275,7 +281,7 @@ static UINT rail_read_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* la
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_status_order(wStream* s, RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
static UINT rail_write_client_status_order(wStream* s, const RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
{
|
||||
if (!s || !clientStatus)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -289,31 +295,44 @@ static UINT rail_write_client_status_order(wStream* s, RAIL_CLIENT_STATUS_ORDER*
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_write_client_exec_order(wStream* s, RAIL_EXEC_ORDER* exec)
|
||||
static UINT rail_write_client_exec_order(wStream* s, UINT16 flags,
|
||||
const RAIL_UNICODE_STRING* exeOrFile, const RAIL_UNICODE_STRING* workingDir,
|
||||
const RAIL_UNICODE_STRING* arguments)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
if (!s || !exec)
|
||||
if (!s || !exeOrFile || !workingDir || !arguments)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
Stream_Write_UINT16(s, exec->flags); /* flags (2 bytes) */
|
||||
Stream_Write_UINT16(s, exec->exeOrFile.length); /* exeOrFileLength (2 bytes) */
|
||||
Stream_Write_UINT16(s, exec->workingDir.length); /* workingDirLength (2 bytes) */
|
||||
Stream_Write_UINT16(s, exec->arguments.length); /* argumentsLength (2 bytes) */
|
||||
/* [MS-RDPERP] 2.2.2.3.1 Client Execute PDU (TS_RAIL_ORDER_EXEC)
|
||||
* Check argument limits */
|
||||
if ((exeOrFile->length > 520) || (workingDir->length > 520) ||
|
||||
(arguments->length > 16000))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"TS_RAIL_ORDER_EXEC argument limits exceeded: ExeOrFile=%"PRIu16" [max=520], WorkingDir=%"PRIu16" [max=520], Arguments=%"PRIu16" [max=16000]",
|
||||
exeOrFile->length, workingDir->length, arguments->length);
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
if ((error = rail_write_unicode_string_value(s, &exec->exeOrFile)))
|
||||
Stream_Write_UINT16(s, flags); /* flags (2 bytes) */
|
||||
Stream_Write_UINT16(s, exeOrFile->length); /* exeOrFileLength (2 bytes) */
|
||||
Stream_Write_UINT16(s, workingDir->length); /* workingDirLength (2 bytes) */
|
||||
Stream_Write_UINT16(s, arguments->length); /* argumentsLength (2 bytes) */
|
||||
|
||||
if ((error = rail_write_unicode_string_value(s, exeOrFile)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = rail_write_unicode_string_value(s, &exec->workingDir)))
|
||||
if ((error = rail_write_unicode_string_value(s, workingDir)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %"PRIu32"", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = rail_write_unicode_string_value(s, &exec->arguments)))
|
||||
if ((error = rail_write_unicode_string_value(s, arguments)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_unicode_string_value failed with error %"PRIu32"", error);
|
||||
return error;
|
||||
@ -327,7 +346,7 @@ static UINT rail_write_client_exec_order(wStream* s, RAIL_EXEC_ORDER* exec)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam)
|
||||
UINT rail_write_client_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
BYTE body;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
@ -340,22 +359,22 @@ UINT rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam)
|
||||
switch (sysparam->param)
|
||||
{
|
||||
case SPI_SET_DRAG_FULL_WINDOWS:
|
||||
body = sysparam->dragFullWindows;
|
||||
body = sysparam->dragFullWindows ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_CUES:
|
||||
body = sysparam->keyboardCues;
|
||||
body = sysparam->keyboardCues ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_KEYBOARD_PREF:
|
||||
body = sysparam->keyboardPref;
|
||||
body = sysparam->keyboardPref ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
case SPI_SET_MOUSE_BUTTON_SWAP:
|
||||
body = sysparam->mouseButtonSwap;
|
||||
body = sysparam->mouseButtonSwap ? 1 : 0;
|
||||
Stream_Write_UINT8(s, body);
|
||||
break;
|
||||
|
||||
@ -388,7 +407,7 @@ UINT rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam)
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_activate_order(wStream* s, RAIL_ACTIVATE_ORDER* activate)
|
||||
static UINT rail_write_client_activate_order(wStream* s, const RAIL_ACTIVATE_ORDER* activate)
|
||||
{
|
||||
BYTE enabled;
|
||||
|
||||
@ -396,12 +415,12 @@ static UINT rail_write_client_activate_order(wStream* s, RAIL_ACTIVATE_ORDER* ac
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
Stream_Write_UINT32(s, activate->windowId); /* windowId (4 bytes) */
|
||||
enabled = activate->enabled;
|
||||
enabled = activate->enabled ? 1 : 0;
|
||||
Stream_Write_UINT8(s, enabled); /* enabled (1 byte) */
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_sysmenu_order(wStream* s, RAIL_SYSMENU_ORDER* sysmenu)
|
||||
static UINT rail_write_client_sysmenu_order(wStream* s, const RAIL_SYSMENU_ORDER* sysmenu)
|
||||
{
|
||||
if (!s || !sysmenu)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -412,7 +431,7 @@ static UINT rail_write_client_sysmenu_order(wStream* s, RAIL_SYSMENU_ORDER* sysm
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_syscommand_order(wStream* s, RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
static UINT rail_write_client_syscommand_order(wStream* s, const RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
{
|
||||
if (!s || !syscommand)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -422,7 +441,8 @@ static UINT rail_write_client_syscommand_order(wStream* s, RAIL_SYSCOMMAND_ORDER
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_notify_event_order(wStream* s, RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
static UINT rail_write_client_notify_event_order(wStream* s,
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
{
|
||||
if (!s || !notifyEvent)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -433,7 +453,8 @@ static UINT rail_write_client_notify_event_order(wStream* s, RAIL_NOTIFY_EVENT_O
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_window_move_order(wStream* s, RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
static UINT rail_write_client_window_move_order(wStream* s,
|
||||
const RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
{
|
||||
if (!s || !windowMove)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -446,7 +467,8 @@ static UINT rail_write_client_window_move_order(wStream* s, RAIL_WINDOW_MOVE_ORD
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_client_get_appid_req_order(wStream* s, RAIL_GET_APPID_REQ_ORDER* getAppidReq)
|
||||
static UINT rail_write_client_get_appid_req_order(wStream* s,
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppidReq)
|
||||
{
|
||||
if (!s || !getAppidReq)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -455,7 +477,7 @@ static UINT rail_write_client_get_appid_req_order(wStream* s, RAIL_GET_APPID_REQ
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT rail_write_langbar_info_order(wStream* s, RAIL_LANGBAR_INFO_ORDER* langbarInfo)
|
||||
static UINT rail_write_langbar_info_order(wStream* s, const RAIL_LANGBAR_INFO_ORDER* langbarInfo)
|
||||
{
|
||||
if (!s || !langbarInfo)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -753,7 +775,7 @@ UINT rail_order_recv(railPlugin* rail, wStream* s)
|
||||
|
||||
case RDP_RAIL_ORDER_EXEC_RESULT:
|
||||
{
|
||||
RAIL_EXEC_RESULT_ORDER execResult;
|
||||
RAIL_EXEC_RESULT_ORDER execResult = { 0 };
|
||||
error = rail_recv_exec_result_order(rail, &execResult, s);
|
||||
free(execResult.exeOrFile.string);
|
||||
return error;
|
||||
@ -802,7 +824,7 @@ UINT rail_order_recv(railPlugin* rail, wStream* s)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_handshake_order(railPlugin* rail, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -829,7 +851,7 @@ UINT rail_send_handshake_order(railPlugin* rail, RAIL_HANDSHAKE_ORDER* handshake
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -856,7 +878,7 @@ UINT rail_send_handshake_ex_order(railPlugin* rail, RAIL_HANDSHAKE_EX_ORDER* han
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_status_order(railPlugin* rail, RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -886,19 +908,21 @@ UINT rail_send_client_status_order(railPlugin* rail, RAIL_CLIENT_STATUS_ORDER* c
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec)
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
|
||||
const RAIL_UNICODE_STRING* exeOrFile, const RAIL_UNICODE_STRING* workingDir,
|
||||
const RAIL_UNICODE_STRING* arguments)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
size_t length;
|
||||
|
||||
if (!rail || !exec)
|
||||
if (!rail || !exeOrFile || !workingDir || !arguments)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
length = RAIL_EXEC_ORDER_LENGTH +
|
||||
exec->exeOrFile.length +
|
||||
exec->workingDir.length +
|
||||
exec->arguments.length;
|
||||
exeOrFile->length +
|
||||
workingDir->length +
|
||||
arguments->length;
|
||||
s = rail_pdu_init(length);
|
||||
|
||||
if (!s)
|
||||
@ -907,18 +931,19 @@ UINT rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((error = rail_write_client_exec_order(s, exec)))
|
||||
if ((error = rail_write_client_exec_order(s, flags, exeOrFile, workingDir, arguments)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_client_exec_order failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = rail_send_pdu(rail, s, RDP_RAIL_ORDER_EXEC)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
@ -928,7 +953,7 @@ UINT rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rail_send_client_sysparam_order(railPlugin* rail, RAIL_SYSPARAM_ORDER* sysparam)
|
||||
static UINT rail_send_client_sysparam_order(railPlugin* rail, const RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
wStream* s;
|
||||
size_t length = RAIL_SYSPARAM_ORDER_LENGTH;
|
||||
@ -972,15 +997,16 @@ static UINT rail_send_client_sysparam_order(railPlugin* rail, RAIL_SYSPARAM_ORDE
|
||||
if ((error = rail_write_client_sysparam_order(s, sysparam)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_write_client_sysparam_order failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = rail_send_pdu(rail, s, RDP_RAIL_ORDER_SYSPARAM)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
@ -1082,7 +1108,7 @@ static UINT rail_send_client_sysparams_order(railPlugin* rail, RAIL_SYSPARAM_ORD
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, RAIL_ACTIVATE_ORDER* activate)
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1112,7 +1138,7 @@ UINT rail_send_client_activate_order(railPlugin* rail, RAIL_ACTIVATE_ORDER* acti
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, RAIL_SYSMENU_ORDER* sysmenu)
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER* sysmenu)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1142,7 +1168,7 @@ UINT rail_send_client_sysmenu_order(railPlugin* rail, RAIL_SYSMENU_ORDER* sysmen
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1172,7 +1198,8 @@ UINT rail_send_client_syscommand_order(railPlugin* rail, RAIL_SYSCOMMAND_ORDER*
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail, RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail,
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1202,7 +1229,7 @@ UINT rail_send_client_notify_event_order(railPlugin* rail, RAIL_NOTIFY_EVENT_ORD
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1232,7 +1259,8 @@ UINT rail_send_client_window_move_order(railPlugin* rail, RAIL_WINDOW_MOVE_ORDER
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail, RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail,
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
@ -1262,7 +1290,8 @@ UINT rail_send_client_get_appid_req_order(railPlugin* rail, RAIL_GET_APPID_REQ_O
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail, RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail,
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
{
|
||||
wStream* s;
|
||||
UINT error;
|
||||
|
||||
@ -29,22 +29,27 @@
|
||||
|
||||
#define TAG CHANNELS_TAG("rail.client")
|
||||
|
||||
UINT rail_write_client_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam);
|
||||
UINT rail_write_client_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam);
|
||||
|
||||
UINT rail_order_recv(railPlugin* rail, wStream* s);
|
||||
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType);
|
||||
|
||||
UINT rail_send_handshake_order(railPlugin* rail, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
UINT rail_send_client_status_order(railPlugin* rail, RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, RAIL_EXEC_ORDER* exec);
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, RAIL_ACTIVATE_ORDER* activate);
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, RAIL_SYSMENU_ORDER* sysmenu);
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_ORDER* clientStatus);
|
||||
UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
|
||||
const RAIL_UNICODE_STRING* exeOrFile, const RAIL_UNICODE_STRING* workingDir,
|
||||
const RAIL_UNICODE_STRING* arguments);
|
||||
UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER* activate);
|
||||
UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER* sysmenu);
|
||||
UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_ORDER* syscommand);
|
||||
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail, RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail, RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail, RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
UINT rail_send_client_notify_event_order(railPlugin* rail,
|
||||
const RAIL_NOTIFY_EVENT_ORDER* notifyEvent);
|
||||
UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE_ORDER* windowMove);
|
||||
UINT rail_send_client_get_appid_req_order(railPlugin* rail,
|
||||
const RAIL_GET_APPID_REQ_ORDER* getAppIdReq);
|
||||
UINT rail_send_client_langbar_info_order(railPlugin* rail,
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H */
|
||||
|
||||
@ -51,7 +51,7 @@ const char* const RAIL_ORDER_TYPE_STRINGS[] =
|
||||
""
|
||||
};
|
||||
|
||||
void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_string)
|
||||
BOOL rail_string_to_unicode_string(const char* string, RAIL_UNICODE_STRING* unicode_string)
|
||||
{
|
||||
WCHAR* buffer = NULL;
|
||||
int length = 0;
|
||||
@ -60,11 +60,19 @@ void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_st
|
||||
unicode_string->length = 0;
|
||||
|
||||
if (!string || strlen(string) < 1)
|
||||
return;
|
||||
return TRUE;
|
||||
|
||||
length = ConvertToUnicode(CP_UTF8, 0, string, -1, &buffer, 0);
|
||||
|
||||
if ((length < 0) || ((size_t)length * sizeof(WCHAR) > UINT16_MAX))
|
||||
{
|
||||
free(buffer);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
length = ConvertToUnicode(CP_UTF8, 0, string, -1, &buffer, 0) * 2;
|
||||
unicode_string->string = (BYTE*) buffer;
|
||||
unicode_string->length = (UINT16) length;
|
||||
unicode_string->length = (UINT16) length * sizeof(WCHAR);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +125,7 @@ UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
|
||||
void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
Stream_Write_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
|
||||
}
|
||||
@ -137,7 +145,7 @@ UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshake
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rail_write_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
Stream_Write_UINT32(s, handshakeEx->buildNumber); /* buildNumber (4 bytes) */
|
||||
Stream_Write_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
|
||||
|
||||
@ -44,11 +44,11 @@ extern const char* const RAIL_ORDER_TYPE_STRINGS[];
|
||||
#define RAIL_GET_APPID_REQ_ORDER_LENGTH 4 /* fixed */
|
||||
#define RAIL_LANGBAR_INFO_ORDER_LENGTH 4 /* fixed */
|
||||
|
||||
void rail_string_to_unicode_string(char* string, RAIL_UNICODE_STRING* unicode_string);
|
||||
BOOL rail_string_to_unicode_string(const char* string, RAIL_UNICODE_STRING* unicode_string);
|
||||
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
void rail_write_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake);
|
||||
void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake);
|
||||
UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
void rail_write_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx);
|
||||
|
||||
wStream* rail_pdu_init(size_t length);
|
||||
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength);
|
||||
|
||||
@ -41,8 +41,10 @@
|
||||
|
||||
#include "devman.h"
|
||||
|
||||
void devman_device_free(DEVICE* device)
|
||||
static void devman_device_free(void* obj)
|
||||
{
|
||||
DEVICE* device = (DEVICE*) obj;
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
@ -75,8 +77,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListDictionary_ValueObject(devman->devices)->fnObjectFree =
|
||||
(OBJECT_FREE_FN) devman_device_free;
|
||||
ListDictionary_ValueObject(devman->devices)->fnObjectFree = devman_device_free;
|
||||
|
||||
return devman;
|
||||
}
|
||||
|
||||
@ -68,11 +68,12 @@
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
typedef struct _DEVICE_DRIVE_EXT DEVICE_DRIVE_EXT;
|
||||
|
||||
/* IMPORTANT: Keep in sync with DRIVE_DEVICE */
|
||||
struct _DEVICE_DRIVE_EXT
|
||||
{
|
||||
DEVICE device;
|
||||
WCHAR* path;
|
||||
BOOL automount;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -134,7 +135,7 @@ BOOL check_path(char* path)
|
||||
{
|
||||
UINT type = GetDriveTypeA(path);
|
||||
|
||||
if (!(type == DRIVE_REMOVABLE || type == DRIVE_CDROM || type == DRIVE_REMOTE))
|
||||
if (!(type == DRIVE_FIXED ||type == DRIVE_REMOVABLE || type == DRIVE_CDROM || type == DRIVE_REMOTE))
|
||||
return FALSE;
|
||||
|
||||
return GetVolumeInformationA(path, NULL, 0, NULL, NULL, NULL, NULL, 0);
|
||||
@ -162,6 +163,7 @@ void first_hotplug(rdpdrPlugin* rdpdr)
|
||||
drive->Path = _strdup(drive_path);
|
||||
drive_path[1] = '\0';
|
||||
drive->Name = _strdup(drive_path);
|
||||
drive->automount = TRUE;
|
||||
devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE*)drive,
|
||||
rdpdr->rdpcontext);
|
||||
}
|
||||
@ -206,6 +208,7 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->Path = _strdup(drive_path);
|
||||
drive_path[1] = '\0';
|
||||
drive->automount = TRUE;
|
||||
drive->Name = _strdup(drive_path);
|
||||
devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE*)drive,
|
||||
rdpdr->rdpcontext);
|
||||
@ -226,7 +229,7 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
DWORD unitmask = lpdbv->dbcv_unitmask;
|
||||
int i, j, count;
|
||||
char drive_name_upper, drive_name_lower;
|
||||
ULONG_PTR* keys;
|
||||
ULONG_PTR* keys = NULL;
|
||||
DEVICE_DRIVE_EXT* device_ext;
|
||||
UINT32 ids[1];
|
||||
|
||||
@ -246,19 +249,24 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
if (device_ext->path[0] == drive_name_upper
|
||||
|| device_ext->path[0] == drive_name_lower)
|
||||
{
|
||||
devman_unregister_device(rdpdr->devman, (void*)keys[j]);
|
||||
ids[0] = keys[j];
|
||||
|
||||
if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
|
||||
if (device_ext->automount)
|
||||
{
|
||||
// dont end on error, just report ?
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_remove_request failed with error %"PRIu32"!",
|
||||
error);
|
||||
}
|
||||
devman_unregister_device(rdpdr->devman, (void*)keys[j]);
|
||||
ids[0] = keys[j];
|
||||
|
||||
break;
|
||||
if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
|
||||
{
|
||||
// dont end on error, just report ?
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_remove_request failed with error %"PRIu32"!",
|
||||
error);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(keys);
|
||||
}
|
||||
|
||||
unitmask = unitmask >> 1;
|
||||
@ -378,7 +386,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
hotplug_dev dev_array[MAX_USB_DEVICES];
|
||||
int count;
|
||||
DEVICE_DRIVE_EXT* device_ext;
|
||||
ULONG_PTR* keys;
|
||||
ULONG_PTR* keys = NULL;
|
||||
int i, j;
|
||||
int size = 0;
|
||||
UINT error;
|
||||
@ -395,7 +403,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
if (pDirent->d_name[0] != '.')
|
||||
{
|
||||
sprintf(fullpath, "%s/%s", szdir, pDirent->d_name);
|
||||
sprintf_s(fullpath, ARRAYSIZE(fullpath), "%s/%s", szdir, pDirent->d_name);
|
||||
lstat(fullpath, &buf);
|
||||
|
||||
if (S_ISDIR(buf.st_mode))
|
||||
@ -425,13 +433,13 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
device_ext = (DEVICE_DRIVE_EXT*)ListDictionary_GetItemValue(
|
||||
rdpdr->devman->devices, (void*)keys[j]);
|
||||
|
||||
if (!device_ext)
|
||||
if (!device_ext || !device_ext->automount)
|
||||
continue;
|
||||
|
||||
if (device_ext->path == NULL)
|
||||
continue;
|
||||
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, device_ext->path, 0, &path, 0, NULL, FALSE) <= 0)
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, device_ext->path, -1, &path, 0, NULL, FALSE) <= 0)
|
||||
continue;
|
||||
|
||||
/* not plugable device */
|
||||
@ -486,6 +494,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->Path = dev_array[i].path;
|
||||
drive->automount = TRUE;
|
||||
dev_array[i].path = NULL;
|
||||
name = strrchr(drive->Path, '/') + 1;
|
||||
drive->Name = _strdup(name);
|
||||
@ -513,6 +522,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(keys);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
free(dev_array[i].path);
|
||||
@ -692,7 +702,10 @@ static char* next_line(FILE* fd, size_t* len)
|
||||
c = fgetc(fd);
|
||||
|
||||
if (ferror(fd))
|
||||
{
|
||||
free(newbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (c == EOF)
|
||||
{
|
||||
@ -781,7 +794,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
int i, j;
|
||||
int size = 0;
|
||||
int count;
|
||||
ULONG_PTR* keys;
|
||||
ULONG_PTR* keys = NULL;
|
||||
UINT32 ids[1];
|
||||
UINT error = 0;
|
||||
memset(dev_array, 0, sizeof(dev_array));
|
||||
@ -823,7 +836,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
DEVICE_DRIVE_EXT* device_ext = (DEVICE_DRIVE_EXT*)ListDictionary_GetItemValue(
|
||||
rdpdr->devman->devices, (void*)keys[j]);
|
||||
|
||||
if (!device_ext || !device_ext->path)
|
||||
if (!device_ext || !device_ext->path || !device_ext->automount)
|
||||
continue;
|
||||
|
||||
ConvertFromUnicode(CP_UTF8, 0, device_ext->path, -1, &path, 0, NULL, NULL);
|
||||
@ -880,6 +893,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->Path = dev_array[i].path;
|
||||
drive->automount = TRUE;
|
||||
dev_array[i].path = NULL;
|
||||
name = strrchr(drive->Path, '/') + 1;
|
||||
drive->Name = _strdup(name);
|
||||
@ -906,6 +920,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(keys);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
free(dev_array[i].path);
|
||||
@ -1069,7 +1084,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
|
||||
first_hotplug(rdpdr);
|
||||
|
||||
if (!(rdpdr->hotplugThread = CreateThread(NULL, 0,
|
||||
drive_hotplug_thread_func, rdpdr, 0, NULL)))
|
||||
drive_hotplug_thread_func, rdpdr, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -1147,6 +1162,7 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
|
||||
|
||||
if (!s)
|
||||
{
|
||||
free(computerNameW);
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
@ -1207,7 +1223,7 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr,
|
||||
size_t count_pos;
|
||||
DEVICE* device;
|
||||
int keyCount;
|
||||
ULONG_PTR* pKeys;
|
||||
ULONG_PTR* pKeys = NULL;
|
||||
s = Stream_New(NULL, 256);
|
||||
|
||||
if (!s)
|
||||
@ -1243,6 +1259,8 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr,
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 20 + data_len))
|
||||
{
|
||||
free(pKeys);
|
||||
Stream_Free(s, TRUE);
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
@ -1316,7 +1334,7 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
|
||||
int index;
|
||||
int keyCount;
|
||||
DEVICE* device;
|
||||
ULONG_PTR* pKeys;
|
||||
ULONG_PTR* pKeys = NULL;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
pKeys = NULL;
|
||||
keyCount = ListDictionary_GetKeys(rdpdr->devman->devices, &pKeys);
|
||||
@ -1609,7 +1627,6 @@ static VOID VCAPITYPE rdpdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DW
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -1709,7 +1726,7 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr,
|
||||
}
|
||||
|
||||
if (!(rdpdr->thread = CreateThread(NULL, 0,
|
||||
rdpdr_virtual_channel_client_thread, (void*) rdpdr, 0,
|
||||
rdpdr_virtual_channel_client_thread, (void*) rdpdr, 0,
|
||||
NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
@ -1728,6 +1745,9 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
UINT error;
|
||||
|
||||
if (rdpdr->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(rdpdr->queue, 0)
|
||||
&& (WaitForSingleObject(rdpdr->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
@ -1846,7 +1866,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP;
|
||||
strcpy(rdpdr->channelDef.name, "rdpdr");
|
||||
sprintf_s(rdpdr->channelDef.name, ARRAYSIZE(rdpdr->channelDef.name), "rdpdr");
|
||||
rdpdr->sequenceId = 0;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
|
||||
@ -563,7 +563,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"rdpdr_server_write_general_capability_set failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (context->supportsDrives)
|
||||
@ -572,7 +572,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_server_write_drive_capability_set failed with error %"PRIu32"!",
|
||||
error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,7 +582,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_server_write_port_capability_set failed with error %"PRIu32"!",
|
||||
error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,7 +592,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"rdpdr_server_write_printer_capability_set failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,7 +602,7 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"rdpdr_server_write_printer_capability_set failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,6 +612,9 @@ static UINT rdpdr_server_send_core_capability_request(RdpdrServerContext*
|
||||
(PCHAR) Stream_Buffer(s), Stream_Length(s), &written);
|
||||
Stream_Free(s, TRUE);
|
||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1260,7 +1263,7 @@ static UINT rdpdr_server_start(RdpdrServerContext* context)
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
||||
rdpdr_server_thread, (void*) context, 0, NULL)))
|
||||
rdpdr_server_thread, (void*) context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
@ -1737,7 +1740,7 @@ static UINT rdpdr_server_drive_create_directory(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_create_directory_callback1;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName));
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
|
||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||
@ -1848,7 +1851,7 @@ static UINT rdpdr_server_drive_delete_directory(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_delete_directory_callback1;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName));
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
|
||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||
@ -2016,7 +2019,7 @@ static UINT rdpdr_server_drive_query_directory(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_query_directory_callback1;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName));
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
|
||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||
@ -2090,7 +2093,7 @@ static UINT rdpdr_server_drive_open_file(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_open_file_callback;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName));
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
|
||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||
@ -2417,7 +2420,7 @@ static UINT rdpdr_server_drive_delete_file(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_delete_file_callback1;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName));
|
||||
strncpy(irp->PathName, path, sizeof(irp->PathName) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
|
||||
if (!rdpdr_server_enqueue_irp(context, irp))
|
||||
@ -2567,8 +2570,8 @@ static UINT rdpdr_server_drive_rename_file(RdpdrServerContext* context,
|
||||
irp->Callback = rdpdr_server_drive_rename_file_callback1;
|
||||
irp->CallbackData = callbackData;
|
||||
irp->DeviceId = deviceId;
|
||||
strncpy(irp->PathName, oldPath, sizeof(irp->PathName));
|
||||
strncpy(irp->ExtraBuffer, newPath, sizeof(irp->ExtraBuffer));
|
||||
strncpy(irp->PathName, oldPath, sizeof(irp->PathName) - 1);
|
||||
strncpy(irp->ExtraBuffer, newPath, sizeof(irp->ExtraBuffer) - 1);
|
||||
rdpdr_server_convert_slashes(irp->PathName, sizeof(irp->PathName));
|
||||
rdpdr_server_convert_slashes(irp->ExtraBuffer, sizeof(irp->ExtraBuffer));
|
||||
|
||||
|
||||
@ -139,8 +139,8 @@ static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd
|
||||
wStream* s;
|
||||
RDPGFX_AVC420_BITMAP_STREAM h264;
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface;
|
||||
|
||||
s = Stream_New(cmd->data, cmd->length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
@ -149,6 +149,7 @@ static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd
|
||||
|
||||
if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta))))
|
||||
{
|
||||
Stream_Free(s, FALSE);
|
||||
WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
@ -184,8 +185,8 @@ static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd
|
||||
wStream* s;
|
||||
RDPGFX_AVC444_BITMAP_STREAM h264 = { 0 };
|
||||
RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface;
|
||||
|
||||
s = Stream_New(cmd->data, cmd->length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
@ -197,6 +198,7 @@ static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, tmp);
|
||||
h264.cbAvc420EncodedBitstream1 = tmp & 0x3FFFFFFFUL;
|
||||
h264.LC = (tmp >> 30UL) & 0x03UL;
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
|
||||
define_channel("rdpsnd")
|
||||
|
||||
include_directories(common)
|
||||
add_subdirectory(common)
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
@ -23,10 +23,9 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
|
||||
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
winpr freerdp ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
@ -57,3 +56,5 @@ endif()
|
||||
if(WITH_OPENSLES)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
|
||||
endif()
|
||||
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "fake" "")
|
||||
|
||||
@ -47,22 +47,18 @@ struct rdpsnd_alsa_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
|
||||
int latency;
|
||||
int wformat;
|
||||
int block_size;
|
||||
UINT32 latency;
|
||||
AUDIO_FORMAT aformat;
|
||||
char* device_name;
|
||||
snd_pcm_t* pcm_handle;
|
||||
snd_mixer_t* mixer_handle;
|
||||
UINT32 source_rate;
|
||||
|
||||
UINT32 actual_rate;
|
||||
UINT32 wLocalTimeClose;
|
||||
snd_pcm_format_t format;
|
||||
UINT32 source_channels;
|
||||
UINT32 actual_channels;
|
||||
int bytes_per_channel;
|
||||
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_uframes_t period_size;
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
|
||||
#define SND_PCM_CHECK(_func, _status) \
|
||||
@ -77,33 +73,25 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
|
||||
int status;
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
snd_pcm_uframes_t buffer_size_max;
|
||||
|
||||
status = snd_pcm_hw_params_malloc(&hw_params);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_malloc", status);
|
||||
|
||||
status = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_any", status);
|
||||
|
||||
/* Set interleaved read/write access */
|
||||
status = snd_pcm_hw_params_set_access(alsa->pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_access", status);
|
||||
|
||||
/* Set sample format */
|
||||
status = snd_pcm_hw_params_set_format(alsa->pcm_handle, hw_params, alsa->format);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_format", status);
|
||||
|
||||
/* Set sample rate */
|
||||
status = snd_pcm_hw_params_set_rate_near(alsa->pcm_handle, hw_params, &alsa->actual_rate, NULL);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_rate_near", status);
|
||||
|
||||
/* Set number of channels */
|
||||
status = snd_pcm_hw_params_set_channels(alsa->pcm_handle, hw_params, alsa->actual_channels);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_channels", status);
|
||||
|
||||
/* Get maximum buffer size */
|
||||
status = snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_get_buffer_size_max", status);
|
||||
|
||||
/**
|
||||
* ALSA Parameters
|
||||
*
|
||||
@ -122,33 +110,28 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
|
||||
* Commonly this is (2 * period_size), but some hardware can do 8 periods per buffer.
|
||||
* It is also possible for the buffer size to not be an integer multiple of the period size.
|
||||
*/
|
||||
|
||||
int interrupts_per_sec_near = 50;
|
||||
int bytes_per_sec = (alsa->actual_rate * alsa->bytes_per_channel * alsa->actual_channels);
|
||||
|
||||
int bytes_per_sec = (alsa->actual_rate * alsa->aformat.wBitsPerSample / 8 * alsa->actual_channels);
|
||||
alsa->buffer_size = buffer_size_max;
|
||||
alsa->period_size = (bytes_per_sec / interrupts_per_sec_near);
|
||||
|
||||
if (alsa->period_size > buffer_size_max)
|
||||
{
|
||||
WLog_ERR(TAG, "Warning: requested sound buffer size %lu, got %lu instead\n",
|
||||
alsa->buffer_size, buffer_size_max);
|
||||
alsa->buffer_size, buffer_size_max);
|
||||
alsa->period_size = (buffer_size_max / 8);
|
||||
}
|
||||
|
||||
/* Set buffer size */
|
||||
status = snd_pcm_hw_params_set_buffer_size_near(alsa->pcm_handle, hw_params, &alsa->buffer_size);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_buffer_size_near", status);
|
||||
|
||||
/* Set period size */
|
||||
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size, NULL);
|
||||
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size,
|
||||
NULL);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_set_period_size_near", status);
|
||||
|
||||
status = snd_pcm_hw_params(alsa->pcm_handle, hw_params);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params", status);
|
||||
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -156,27 +139,21 @@ static int rdpsnd_alsa_set_sw_params(rdpsndAlsaPlugin* alsa)
|
||||
{
|
||||
int status;
|
||||
snd_pcm_sw_params_t* sw_params;
|
||||
|
||||
status = snd_pcm_sw_params_malloc(&sw_params);
|
||||
SND_PCM_CHECK("snd_pcm_sw_params_malloc", status);
|
||||
|
||||
status = snd_pcm_sw_params_current(alsa->pcm_handle, sw_params);
|
||||
SND_PCM_CHECK("snd_pcm_sw_params_current", status);
|
||||
|
||||
status = snd_pcm_sw_params_set_avail_min(alsa->pcm_handle, sw_params, (alsa->bytes_per_channel * alsa->actual_channels));
|
||||
status = snd_pcm_sw_params_set_avail_min(alsa->pcm_handle, sw_params,
|
||||
(alsa->aformat.nChannels * alsa->actual_channels));
|
||||
SND_PCM_CHECK("snd_pcm_sw_params_set_avail_min", status);
|
||||
|
||||
status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->block_size);
|
||||
status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params,
|
||||
alsa->aformat.nBlockAlign);
|
||||
SND_PCM_CHECK("snd_pcm_sw_params_set_start_threshold", status);
|
||||
|
||||
status = snd_pcm_sw_params(alsa->pcm_handle, sw_params);
|
||||
SND_PCM_CHECK("snd_pcm_sw_params", status);
|
||||
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
|
||||
status = snd_pcm_prepare(alsa->pcm_handle);
|
||||
SND_PCM_CHECK("snd_pcm_prepare", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -185,10 +162,8 @@ static int rdpsnd_alsa_validate_params(rdpsndAlsaPlugin* alsa)
|
||||
int status;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_uframes_t period_size;
|
||||
|
||||
status = snd_pcm_get_params(alsa->pcm_handle, &buffer_size, &period_size);
|
||||
SND_PCM_CHECK("snd_pcm_get_params", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -205,15 +180,15 @@ static int rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
|
||||
return rdpsnd_alsa_validate_params(alsa);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
if (format)
|
||||
{
|
||||
alsa->source_rate = format->nSamplesPerSec;
|
||||
alsa->aformat = *format;
|
||||
alsa->actual_rate = format->nSamplesPerSec;
|
||||
alsa->source_channels = format->nChannels;
|
||||
alsa->actual_channels = format->nChannels;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
@ -223,32 +198,40 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* for
|
||||
{
|
||||
case 8:
|
||||
alsa->format = SND_PCM_FORMAT_S8;
|
||||
alsa->bytes_per_channel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
alsa->wformat = format->wFormatTag;
|
||||
alsa->block_size = format->nBlockAlign;
|
||||
}
|
||||
|
||||
alsa->latency = latency;
|
||||
|
||||
return (rdpsnd_alsa_set_params(alsa) == 0);
|
||||
}
|
||||
|
||||
static void rdpsnd_alsa_close_mixer(rdpsndAlsaPlugin* alsa)
|
||||
{
|
||||
if (alsa && alsa->mixer_handle)
|
||||
{
|
||||
snd_mixer_close(alsa->mixer_handle);
|
||||
alsa->mixer_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
|
||||
{
|
||||
int status;
|
||||
@ -257,40 +240,54 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
|
||||
return TRUE;
|
||||
|
||||
status = snd_mixer_open(&alsa->mixer_handle, 0);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_mixer_open failed");
|
||||
return FALSE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = snd_mixer_attach(alsa->mixer_handle, alsa->device_name);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_mixer_attach failed");
|
||||
snd_mixer_close(alsa->mixer_handle);
|
||||
return FALSE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = snd_mixer_selem_register(alsa->mixer_handle, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_mixer_selem_register failed");
|
||||
snd_mixer_close(alsa->mixer_handle);
|
||||
return FALSE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = snd_mixer_load(alsa->mixer_handle);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_mixer_load failed");
|
||||
snd_mixer_close(alsa->mixer_handle);
|
||||
return FALSE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
fail:
|
||||
rdpsnd_alsa_close_mixer(alsa);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static void rdpsnd_alsa_pcm_close(rdpsndAlsaPlugin* alsa)
|
||||
{
|
||||
if (alsa && alsa->pcm_handle)
|
||||
{
|
||||
snd_pcm_drain(alsa->pcm_handle);
|
||||
snd_pcm_close(alsa->pcm_handle);
|
||||
alsa->pcm_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
int mode;
|
||||
int status;
|
||||
@ -301,91 +298,50 @@ static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, i
|
||||
|
||||
mode = 0;
|
||||
/*mode |= SND_PCM_NONBLOCK;*/
|
||||
|
||||
status = snd_pcm_open(&alsa->pcm_handle, alsa->device_name, SND_PCM_STREAM_PLAYBACK, mode);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_open failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
|
||||
|
||||
return rdpsnd_alsa_set_format(device, format, latency) &&
|
||||
rdpsnd_alsa_open_mixer(alsa);
|
||||
rdpsnd_alsa_open_mixer(alsa);
|
||||
}
|
||||
|
||||
static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
int status;
|
||||
snd_htimestamp_t tstamp;
|
||||
snd_pcm_uframes_t frames;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
if (!alsa->pcm_handle)
|
||||
if (!alsa)
|
||||
return;
|
||||
|
||||
status = snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp);
|
||||
|
||||
if (status != 0)
|
||||
frames = 0;
|
||||
|
||||
alsa->wLocalTimeClose = GetTickCount();
|
||||
alsa->wLocalTimeClose += (((frames * 1000) / alsa->actual_rate) / alsa->actual_channels);
|
||||
rdpsnd_alsa_close_mixer(alsa);
|
||||
}
|
||||
|
||||
static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
if (alsa->pcm_handle)
|
||||
{
|
||||
snd_pcm_drain(alsa->pcm_handle);
|
||||
snd_pcm_close(alsa->pcm_handle);
|
||||
alsa->pcm_handle = 0;
|
||||
}
|
||||
|
||||
if (alsa->mixer_handle)
|
||||
{
|
||||
snd_mixer_close(alsa->mixer_handle);
|
||||
alsa->mixer_handle = NULL;
|
||||
}
|
||||
|
||||
rdpsnd_alsa_pcm_close(alsa);
|
||||
rdpsnd_alsa_close_mixer(alsa);
|
||||
free(alsa->device_name);
|
||||
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
|
||||
free(alsa);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 &&
|
||||
format->nSamplesPerSec <= 48000 &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
format->nSamplesPerSec <= 48000 &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (format->nSamplesPerSec <= 48000 &&
|
||||
format->wBitsPerSample == 4 &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
case WAVE_FORMAT_GSM610:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -403,12 +359,11 @@ static UINT32 rdpsnd_alsa_get_volume(rdpsndDevicePlugin* device)
|
||||
UINT16 dwVolumeRight;
|
||||
snd_mixer_elem_t* elem;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
|
||||
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
|
||||
|
||||
if (!alsa->mixer_handle)
|
||||
rdpsnd_alsa_open_mixer(alsa);
|
||||
if (!rdpsnd_alsa_open_mixer(alsa))
|
||||
return 0;
|
||||
|
||||
for (elem = snd_mixer_first_elem(alsa->mixer_handle); elem; elem = snd_mixer_elem_next(elem))
|
||||
{
|
||||
@ -417,16 +372,13 @@ static UINT32 rdpsnd_alsa_get_volume(rdpsndDevicePlugin* device)
|
||||
snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max);
|
||||
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &volume_left);
|
||||
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &volume_right);
|
||||
|
||||
dwVolumeLeft = (UINT16) (((volume_left * 0xFFFF) - volume_min) / (volume_max - volume_min));
|
||||
dwVolumeRight = (UINT16) (((volume_right * 0xFFFF) - volume_min) / (volume_max - volume_min));
|
||||
|
||||
dwVolumeLeft = (UINT16)(((volume_left * 0xFFFF) - volume_min) / (volume_max - volume_min));
|
||||
dwVolumeRight = (UINT16)(((volume_right * 0xFFFF) - volume_min) / (volume_max - volume_min));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
|
||||
|
||||
return dwVolume;
|
||||
}
|
||||
|
||||
@ -441,12 +393,12 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
snd_mixer_elem_t* elem;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
if (!alsa->mixer_handle && !rdpsnd_alsa_open_mixer(alsa))
|
||||
return FALSE;
|
||||
if (!rdpsnd_alsa_open_mixer(alsa))
|
||||
return FALSE;
|
||||
|
||||
left = (value & 0xFFFF);
|
||||
right = ((value >> 16) & 0xFFFF);
|
||||
|
||||
|
||||
for (elem = snd_mixer_first_elem(alsa->mixer_handle); elem; elem = snd_mixer_elem_next(elem))
|
||||
{
|
||||
if (snd_mixer_selem_has_playback_volume(elem))
|
||||
@ -454,8 +406,9 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max);
|
||||
volume_left = volume_min + (left * (volume_max - volume_min)) / 0xFFFF;
|
||||
volume_right = volume_min + (right * (volume_max - volume_min)) / 0xFFFF;
|
||||
|
||||
if ((snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, volume_left) < 0) ||
|
||||
(snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, volume_right) < 0))
|
||||
(snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, volume_right) < 0))
|
||||
{
|
||||
WLog_ERR(TAG, "error setting the volume\n");
|
||||
return FALSE;
|
||||
@ -466,135 +419,47 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BYTE* rdpsnd_alsa_process_audio_sample(rdpsndDevicePlugin* device, BYTE* data, int* size)
|
||||
static UINT rdpsnd_alsa_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
int frames;
|
||||
BYTE* srcData;
|
||||
int srcFrameSize;
|
||||
int dstFrameSize;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
if (alsa->wformat == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context,
|
||||
data, *size, alsa->source_channels, alsa->block_size);
|
||||
|
||||
*size = alsa->dsp_context->adpcm_size;
|
||||
srcData = alsa->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context,
|
||||
data, *size, alsa->source_channels, alsa->block_size);
|
||||
|
||||
*size = alsa->dsp_context->adpcm_size;
|
||||
srcData = alsa->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
srcData = data;
|
||||
}
|
||||
|
||||
srcFrameSize = alsa->source_channels * alsa->bytes_per_channel;
|
||||
dstFrameSize = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
|
||||
if ((*size % srcFrameSize) != 0)
|
||||
return NULL;
|
||||
|
||||
if (!((alsa->source_rate == alsa->actual_rate) && (alsa->source_channels == alsa->actual_channels)))
|
||||
{
|
||||
alsa->dsp_context->resample(alsa->dsp_context, srcData, alsa->bytes_per_channel,
|
||||
alsa->source_channels, alsa->source_rate, *size / srcFrameSize,
|
||||
alsa->actual_channels, alsa->actual_rate);
|
||||
|
||||
frames = alsa->dsp_context->resampled_frames;
|
||||
|
||||
*size = frames * dstFrameSize;
|
||||
srcData = alsa->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
data = srcData;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_alsa_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
int size;
|
||||
BYTE* data;
|
||||
|
||||
size = wave->length;
|
||||
data = rdpsnd_alsa_process_audio_sample(device, wave->data, &size);
|
||||
|
||||
wave->data = (BYTE*) malloc(size);
|
||||
if (!wave->data)
|
||||
return FALSE;
|
||||
CopyMemory(wave->data, data, size);
|
||||
wave->length = size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
BYTE* data;
|
||||
int offset;
|
||||
int length;
|
||||
int status;
|
||||
UINT latency;
|
||||
size_t offset;
|
||||
int frame_size;
|
||||
UINT32 wCurrentTime;
|
||||
snd_htimestamp_t tstamp;
|
||||
snd_pcm_uframes_t frames;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
offset = 0;
|
||||
data = wave->data;
|
||||
length = wave->length;
|
||||
frame_size = alsa->actual_channels * alsa->bytes_per_channel;
|
||||
frame_size = alsa->actual_channels * alsa->aformat.wBitsPerSample / 8;
|
||||
|
||||
if (alsa->wLocalTimeClose)
|
||||
while (offset < size)
|
||||
{
|
||||
wCurrentTime = GetTickCount();
|
||||
snd_pcm_sframes_t status = snd_pcm_writei(alsa->pcm_handle, &data[offset],
|
||||
(size - offset) / frame_size);
|
||||
|
||||
if (snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp) == -EPIPE)
|
||||
{
|
||||
if (wCurrentTime > alsa->wLocalTimeClose)
|
||||
snd_pcm_recover(alsa->pcm_handle, -EPIPE, 1);
|
||||
}
|
||||
if (status < 0)
|
||||
status = snd_pcm_recover(alsa->pcm_handle, status, 0);
|
||||
|
||||
alsa->wLocalTimeClose = 0;
|
||||
}
|
||||
|
||||
while (offset < length)
|
||||
{
|
||||
status = snd_pcm_writei(alsa->pcm_handle, &data[offset], (length - offset) / frame_size);
|
||||
|
||||
if (status == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(alsa->pcm_handle, status, 0);
|
||||
status = 0;
|
||||
}
|
||||
else if (status == -EAGAIN)
|
||||
{
|
||||
status = 0;
|
||||
}
|
||||
else if (status < 0)
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "status: %d\n", status);
|
||||
snd_pcm_close(alsa->pcm_handle);
|
||||
alsa->pcm_handle = NULL;
|
||||
rdpsnd_alsa_open((rdpsndDevicePlugin*) alsa, NULL, alsa->latency);
|
||||
rdpsnd_alsa_close(device);
|
||||
rdpsnd_alsa_open(device, NULL, alsa->latency);
|
||||
break;
|
||||
}
|
||||
|
||||
offset += status * frame_size;
|
||||
}
|
||||
|
||||
free(data);
|
||||
{
|
||||
snd_pcm_sframes_t available, delay;
|
||||
int rc = snd_pcm_avail_delay(alsa->pcm_handle, &available, &delay);
|
||||
|
||||
/* From rdpsnd_main.c */
|
||||
wave->wTimeStampB = wave->wTimeStampA + wave->wAudioLength + 65;
|
||||
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + 65;
|
||||
if (rc != 0)
|
||||
latency = 0;
|
||||
else if (available == 0) /* Get [ms] from number of samples */
|
||||
latency = delay * 1000 / alsa->actual_rate;
|
||||
else
|
||||
latency = 0;
|
||||
}
|
||||
|
||||
return latency + alsa->latency;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] =
|
||||
@ -614,10 +479,10 @@ static UINT rdpsnd_alsa_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV*
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_alsa_args, flags,
|
||||
alsa, NULL, NULL);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, rdpsnd_alsa_args, flags, alsa, NULL, NULL);
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "CommandLineParseArgumentsA failed!");
|
||||
@ -632,14 +497,13 @@ static UINT rdpsnd_alsa_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV*
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
alsa->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!alsa->device_name)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
@ -663,8 +527,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndAlsaPlugin* alsa;
|
||||
UINT error;
|
||||
|
||||
alsa = (rdpsndAlsaPlugin*) calloc(1, sizeof(rdpsndAlsaPlugin));
|
||||
|
||||
if (!alsa)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
@ -673,28 +537,26 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
|
||||
alsa->device.Open = rdpsnd_alsa_open;
|
||||
alsa->device.FormatSupported = rdpsnd_alsa_format_supported;
|
||||
alsa->device.SetFormat = rdpsnd_alsa_set_format;
|
||||
alsa->device.GetVolume = rdpsnd_alsa_get_volume;
|
||||
alsa->device.SetVolume = rdpsnd_alsa_set_volume;
|
||||
alsa->device.WaveDecode = rdpsnd_alsa_wave_decode;
|
||||
alsa->device.WavePlay = rdpsnd_alsa_wave_play;
|
||||
alsa->device.Play = rdpsnd_alsa_play;
|
||||
alsa->device.Close = rdpsnd_alsa_close;
|
||||
alsa->device.Free = rdpsnd_alsa_free;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if (args->argc > 1)
|
||||
{
|
||||
if ((error = rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin *) alsa, args)))
|
||||
if ((error = rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin*) alsa, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpsnd_alsa_parse_addin_args failed with error %"PRIu32"", error);
|
||||
goto error_parse_args;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
alsa->device_name = _strdup("default");
|
||||
|
||||
if (!alsa->device_name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
@ -704,27 +566,11 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
}
|
||||
|
||||
alsa->pcm_handle = 0;
|
||||
alsa->source_rate = 22050;
|
||||
alsa->actual_rate = 22050;
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->source_channels = 2;
|
||||
alsa->actual_channels = 2;
|
||||
alsa->bytes_per_channel = 2;
|
||||
|
||||
alsa->dsp_context = freerdp_dsp_context_new();
|
||||
if (!alsa->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_dsp_context;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) alsa);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
error_dsp_context:
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
error_strdup:
|
||||
free(alsa->device_name);
|
||||
error_parse_args:
|
||||
|
||||
33
channels/rdpsnd/client/fake/CMakeLists.txt
Normal file
33
channels/rdpsnd/client/fake/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2019 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client_subsystem("rdpsnd" "fake" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
rdpsnd_fake.c)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/Fake")
|
||||
172
channels/rdpsnd/client/fake/rdpsnd_fake.c
Normal file
172
channels/rdpsnd/client/fake/rdpsnd_fake.c
Normal file
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Output Virtual Channel
|
||||
*
|
||||
* Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2019 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/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
typedef struct rdpsnd_fake_plugin rdpsndFakePlugin;
|
||||
|
||||
struct rdpsnd_fake_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
};
|
||||
|
||||
static BOOL rdpsnd_fake_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_fake_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_fake_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_fake_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndFakePlugin* fake = (rdpsndFakePlugin*) device;
|
||||
|
||||
if (!fake)
|
||||
return;
|
||||
|
||||
free(fake);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_fake_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_fake_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
int latency)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT rdpsnd_fake_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void rdpsnd_fake_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A rdpsnd_fake_args[] =
|
||||
{
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_fake_parse_addin_args(rdpsndFakePlugin* fake, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
rdpsnd_fake_args, flags, fake, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
arg = rdpsnd_fake_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
#define freerdp_rdpsnd_client_subsystem_entry fake_freerdp_rdpsnd_client_subsystem_entry
|
||||
#else
|
||||
#define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndFakePlugin* fake;
|
||||
UINT ret;
|
||||
fake = (rdpsndFakePlugin*) calloc(1, sizeof(rdpsndFakePlugin));
|
||||
|
||||
if (!fake)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
fake->device.Open = rdpsnd_fake_open;
|
||||
fake->device.FormatSupported = rdpsnd_fake_format_supported;
|
||||
fake->device.SetVolume = rdpsnd_fake_set_volume;
|
||||
fake->device.Play = rdpsnd_fake_play;
|
||||
fake->device.Start = rdpsnd_fake_start;
|
||||
fake->device.Close = rdpsnd_fake_close;
|
||||
fake->device.Free = rdpsnd_fake_free;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if (args->argc > 1)
|
||||
{
|
||||
ret = rdpsnd_fake_parse_addin_args(fake, args);
|
||||
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing arguments");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = CHANNEL_RC_NO_MEMORY;
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, &fake->device);
|
||||
return CHANNEL_RC_OK;
|
||||
error:
|
||||
rdpsnd_fake_free(&fake->device);
|
||||
return ret;
|
||||
}
|
||||
@ -48,12 +48,12 @@ typedef struct rdpsnd_ios_plugin
|
||||
#define THIS(__ptr) ((rdpsndIOSPlugin*)__ptr)
|
||||
|
||||
static OSStatus rdpsnd_ios_render_cb(
|
||||
void *inRefCon,
|
||||
AudioUnitRenderActionFlags __unused *ioActionFlags,
|
||||
const AudioTimeStamp __unused *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 __unused inNumberFrames,
|
||||
AudioBufferList *ioData
|
||||
void* inRefCon,
|
||||
AudioUnitRenderActionFlags __unused* ioActionFlags,
|
||||
const AudioTimeStamp __unused* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 __unused inNumberFrames,
|
||||
AudioBufferList* ioData
|
||||
)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -63,21 +63,19 @@ static OSStatus rdpsnd_ios_render_cb(
|
||||
return noErr;
|
||||
}
|
||||
|
||||
rdpsndIOSPlugin *p = THIS(inRefCon);
|
||||
rdpsndIOSPlugin* p = THIS(inRefCon);
|
||||
|
||||
for (i = 0; i < ioData->mNumberBuffers; i++)
|
||||
{
|
||||
AudioBuffer* target_buffer = &ioData->mBuffers[i];
|
||||
|
||||
int32_t available_bytes = 0;
|
||||
const void *buffer = TPCircularBufferTail(&p->buffer, &available_bytes);
|
||||
const void* buffer = TPCircularBufferTail(&p->buffer, &available_bytes);
|
||||
|
||||
if (buffer != NULL && available_bytes > 0)
|
||||
{
|
||||
const int bytes_to_copy = MIN((int32_t)target_buffer->mDataByteSize, available_bytes);
|
||||
|
||||
memcpy(target_buffer->mData, buffer, bytes_to_copy);
|
||||
target_buffer->mDataByteSize = bytes_to_copy;
|
||||
|
||||
TPCircularBufferConsume(&p->buffer, bytes_to_copy);
|
||||
}
|
||||
else
|
||||
@ -97,10 +95,12 @@ static BOOL rdpsnd_ios_format_supported(rdpsndDevicePlugin* __unused device, AUD
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_ios_set_format(rdpsndDevicePlugin* __unused device, AUDIO_FORMAT* __unused format, int __unused latency)
|
||||
static BOOL rdpsnd_ios_set_format(rdpsndDevicePlugin* __unused device,
|
||||
AUDIO_FORMAT* __unused format, int __unused latency)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@ -112,7 +112,7 @@ static BOOL rdpsnd_ios_set_volume(rdpsndDevicePlugin* __unused device, UINT32 __
|
||||
|
||||
static void rdpsnd_ios_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
|
||||
/* If this device is not playing... */
|
||||
if (!p->is_playing)
|
||||
@ -120,6 +120,7 @@ static void rdpsnd_ios_start(rdpsndDevicePlugin* device)
|
||||
/* Start the device. */
|
||||
int32_t available_bytes = 0;
|
||||
TPCircularBufferTail(&p->buffer, &available_bytes);
|
||||
|
||||
if (available_bytes > 0)
|
||||
{
|
||||
p->is_playing = 1;
|
||||
@ -130,7 +131,7 @@ static void rdpsnd_ios_start(rdpsndDevicePlugin* device)
|
||||
|
||||
static void rdpsnd_ios_stop(rdpsndDevicePlugin* __unused device)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
|
||||
/* If the device is playing... */
|
||||
if (p->is_playing)
|
||||
@ -138,28 +139,26 @@ static void rdpsnd_ios_stop(rdpsndDevicePlugin* __unused device)
|
||||
/* Stop the device. */
|
||||
AudioOutputUnitStop(p->audio_unit);
|
||||
p->is_playing = 0;
|
||||
|
||||
/* Free all buffers. */
|
||||
TPCircularBufferClear(&p->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_ios_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
static UINT rdpsnd_ios_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
const BOOL ok = TPCircularBufferProduceBytes(&p->buffer, data, size);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return 0;
|
||||
|
||||
rdpsnd_ios_start(device);
|
||||
return 10; /* TODO: Get real latencry in [ms] */
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int __unused latency)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
|
||||
if (p->is_opened)
|
||||
return TRUE;
|
||||
@ -171,13 +170,14 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
AudioComponent audioComponent = AudioComponentFindNext(NULL, &desc);
|
||||
|
||||
if (audioComponent == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Open the audio unit. */
|
||||
OSStatus status = AudioComponentInstanceNew(audioComponent, &p->audio_unit);
|
||||
|
||||
if (status != 0)
|
||||
return FALSE;
|
||||
|
||||
@ -191,14 +191,14 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
|
||||
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
|
||||
|
||||
status = AudioUnitSetProperty(
|
||||
p->audio_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&audioFormat,
|
||||
sizeof(audioFormat));
|
||||
p->audio_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&audioFormat,
|
||||
sizeof(audioFormat));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
@ -211,12 +211,13 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
callbackStruct.inputProc = rdpsnd_ios_render_cb;
|
||||
callbackStruct.inputProcRefCon = p;
|
||||
status = AudioUnitSetProperty(
|
||||
p->audio_unit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&callbackStruct,
|
||||
sizeof(callbackStruct));
|
||||
p->audio_unit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&callbackStruct,
|
||||
sizeof(callbackStruct));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
@ -226,6 +227,7 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
|
||||
/* Initialize the AudioUnit. */
|
||||
status = AudioUnitInitialize(p->audio_unit);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
@ -235,6 +237,7 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
|
||||
/* Allocate the circular buffer. */
|
||||
const BOOL ok = TPCircularBufferInit(&p->buffer, CIRCULAR_BUFFER_SIZE);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
AudioUnitUninitialize(p->audio_unit);
|
||||
@ -249,8 +252,7 @@ static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
|
||||
static void rdpsnd_ios_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
/* Make sure the device is stopped. */
|
||||
rdpsnd_ios_stop(device);
|
||||
|
||||
@ -262,7 +264,6 @@ static void rdpsnd_ios_close(rdpsndDevicePlugin* device)
|
||||
AudioComponentInstanceDispose(p->audio_unit);
|
||||
p->audio_unit = NULL;
|
||||
p->is_opened = 0;
|
||||
|
||||
/* Destroy the circular buffer. */
|
||||
TPCircularBufferCleanup(&p->buffer);
|
||||
}
|
||||
@ -270,11 +271,9 @@ static void rdpsnd_ios_close(rdpsndDevicePlugin* device)
|
||||
|
||||
static void rdpsnd_ios_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndIOSPlugin *p = THIS(device);
|
||||
|
||||
rdpsndIOSPlugin* p = THIS(device);
|
||||
/* Ensure the device is closed. */
|
||||
rdpsnd_ios_close(device);
|
||||
|
||||
/* Free memory associated with the device. */
|
||||
free(p);
|
||||
}
|
||||
@ -305,8 +304,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
p->device.Start = rdpsnd_ios_start;
|
||||
p->device.Close = rdpsnd_ios_close;
|
||||
p->device.Free = rdpsnd_ios_free;
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)p);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
#define __COREFOUNDATION_CFPLUGINCOM__ 1
|
||||
#define IUNKNOWN_C_GUTS void *_reserved; void* QueryInterface; void* AddRef; void* Release
|
||||
@ -51,144 +50,176 @@ struct rdpsnd_mac_plugin
|
||||
|
||||
BOOL isOpen;
|
||||
BOOL isPlaying;
|
||||
|
||||
|
||||
UINT32 latency;
|
||||
AUDIO_FORMAT format;
|
||||
size_t lastAudioBufferIndex;
|
||||
size_t audioBufferIndex;
|
||||
|
||||
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
|
||||
Float64 lastStartTime;
|
||||
|
||||
int wformat;
|
||||
int block_size;
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
typedef struct rdpsnd_mac_plugin rdpsndMacPlugin;
|
||||
|
||||
static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||
static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*)inUserData;
|
||||
|
||||
if (inBuffer == mac->audioBuffers[mac->lastAudioBufferIndex]) {
|
||||
if (inBuffer == mac->audioBuffers[mac->lastAudioBufferIndex])
|
||||
{
|
||||
AudioQueuePause(mac->audioQueue);
|
||||
mac->isPlaying = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
mac->latency = (UINT32) latency;
|
||||
CopyMemory(&(mac->format), format, sizeof(AUDIO_FORMAT));
|
||||
|
||||
mac->audioFormat.mSampleRate = format->nSamplesPerSec;
|
||||
mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mChannelsPerFrame = format->nChannels;
|
||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
|
||||
mac->audioFormat.mBytesPerPacket = format->nBlockAlign;
|
||||
mac->audioFormat.mBytesPerFrame = mac->audioFormat.mBitsPerChannel *
|
||||
mac->audioFormat.mChannelsPerFrame / 8;
|
||||
mac->audioFormat.mBytesPerPacket = mac->audioFormat.mBytesPerFrame *
|
||||
mac->audioFormat.mFramesPerPacket;
|
||||
mac->audioFormat.mReserved = 0;
|
||||
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ALAW:
|
||||
mac->audioFormat.mFormatID = kAudioFormatALaw;
|
||||
break;
|
||||
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
mac->audioFormat.mFormatID = kAudioFormatULaw;
|
||||
break;
|
||||
|
||||
|
||||
case WAVE_FORMAT_PCM:
|
||||
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
mac->audioFormat.mBitsPerChannel = 16;
|
||||
mac->audioFormat.mBytesPerFrame = (16 * format->nChannels) / 8;
|
||||
mac->audioFormat.mBytesPerPacket = mac->audioFormat.mFramesPerPacket * mac->audioFormat.mBytesPerFrame;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_GSM610:
|
||||
mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mac->wformat = format->wFormatTag;
|
||||
mac->block_size = format->nBlockAlign;
|
||||
|
||||
rdpsnd_print_audio_format(format);
|
||||
|
||||
audio_format_print(WLog_Get(TAG), WLOG_DEBUG, format);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static char* FormatError(OSStatus st)
|
||||
{
|
||||
switch (st)
|
||||
{
|
||||
case kAudioFileUnspecifiedError:
|
||||
return "kAudioFileUnspecifiedError";
|
||||
|
||||
case kAudioFileUnsupportedFileTypeError:
|
||||
return "kAudioFileUnsupportedFileTypeError";
|
||||
|
||||
case kAudioFileUnsupportedDataFormatError:
|
||||
return "kAudioFileUnsupportedDataFormatError";
|
||||
|
||||
case kAudioFileUnsupportedPropertyError:
|
||||
return "kAudioFileUnsupportedPropertyError";
|
||||
|
||||
case kAudioFileBadPropertySizeError:
|
||||
return "kAudioFileBadPropertySizeError";
|
||||
|
||||
case kAudioFilePermissionsError:
|
||||
return "kAudioFilePermissionsError";
|
||||
|
||||
case kAudioFileNotOptimizedError:
|
||||
return "kAudioFileNotOptimizedError";
|
||||
|
||||
case kAudioFileInvalidChunkError:
|
||||
return "kAudioFileInvalidChunkError";
|
||||
|
||||
case kAudioFileDoesNotAllow64BitDataSizeError:
|
||||
return "kAudioFileDoesNotAllow64BitDataSizeError";
|
||||
|
||||
case kAudioFileInvalidPacketOffsetError:
|
||||
return "kAudioFileInvalidPacketOffsetError";
|
||||
|
||||
case kAudioFileInvalidFileError:
|
||||
return "kAudioFileInvalidFileError";
|
||||
|
||||
case kAudioFileOperationNotSupportedError:
|
||||
return "kAudioFileOperationNotSupportedError";
|
||||
|
||||
case kAudioFileNotOpenError:
|
||||
return "kAudioFileNotOpenError";
|
||||
|
||||
case kAudioFileEndOfFileError:
|
||||
return "kAudioFileEndOfFileError";
|
||||
|
||||
case kAudioFilePositionError:
|
||||
return "kAudioFilePositionError";
|
||||
|
||||
case kAudioFileFileNotFoundError:
|
||||
return "kAudioFileFileNotFoundError";
|
||||
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
int index;
|
||||
OSStatus status;
|
||||
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
|
||||
if (mac->isOpen)
|
||||
return TRUE;
|
||||
|
||||
|
||||
mac->audioBufferIndex = 0;
|
||||
|
||||
if (!device->SetFormat(device, format, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "SetFormat failure\n");
|
||||
|
||||
if (!rdpsnd_mac_set_format(device, format, latency))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
|
||||
|
||||
status = AudioQueueNewOutput(&(mac->audioFormat),
|
||||
mac_audio_queue_output_cb, mac,
|
||||
NULL, NULL, 0, &(mac->audioQueue));
|
||||
|
||||
mac_audio_queue_output_cb, mac,
|
||||
NULL, NULL, 0, &(mac->audioQueue));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "AudioQueueNewOutput failure\n");
|
||||
const char* err = FormatError(status);
|
||||
WLog_ERR(TAG, "AudioQueueNewOutput failure %s", err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
UInt32 DecodeBufferSizeFrames;
|
||||
UInt32 propertySize = sizeof(DecodeBufferSizeFrames);
|
||||
|
||||
status = AudioQueueGetProperty(mac->audioQueue,
|
||||
kAudioQueueProperty_DecodeBufferSizeFrames,
|
||||
&DecodeBufferSizeFrames,
|
||||
&propertySize);
|
||||
|
||||
kAudioQueueProperty_DecodeBufferSizeFrames,
|
||||
&DecodeBufferSizeFrames,
|
||||
&propertySize);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_DBG(TAG, "AudioQueueGetProperty failure: kAudioQueueProperty_DecodeBufferSizeFrames\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE, &mac->audioBuffers[index]);
|
||||
|
||||
status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
|
||||
&mac->audioBuffers[index]);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
mac->lastStartTime = 0;
|
||||
|
||||
|
||||
mac->isOpen = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
@ -196,14 +227,13 @@ static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
size_t index;
|
||||
mac->isOpen = FALSE;
|
||||
|
||||
AudioQueueStop(mac->audioQueue, true);
|
||||
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
AudioQueueFreeBuffer(mac->audioQueue, mac->audioBuffers[index]);
|
||||
@ -211,7 +241,6 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
|
||||
|
||||
AudioQueueDispose(mac->audioQueue, true);
|
||||
mac->audioQueue = NULL;
|
||||
|
||||
mac->isPlaying = FALSE;
|
||||
}
|
||||
}
|
||||
@ -219,29 +248,22 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
|
||||
static void rdpsnd_mac_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
device->Close(device);
|
||||
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
|
||||
free(mac);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return TRUE;
|
||||
case WAVE_FORMAT_GSM610:
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
@ -251,17 +273,15 @@ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
UINT16 volumeLeft;
|
||||
UINT16 volumeRight;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
|
||||
if (!mac->audioQueue)
|
||||
return FALSE;
|
||||
|
||||
|
||||
volumeLeft = (value & 0xFFFF);
|
||||
volumeRight = ((value >> 16) & 0xFFFF);
|
||||
|
||||
fVolume = ((float) volumeLeft) / 65535.0;
|
||||
|
||||
status = AudioQueueSetParameter(mac->audioQueue, kAudioQueueParam_Volume, fVolume);
|
||||
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "AudioQueueSetParameter kAudioQueueParam_Volume failed: %f\n", fVolume);
|
||||
@ -274,87 +294,47 @@ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
static void rdpsnd_mac_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
|
||||
if (!mac->isPlaying)
|
||||
{
|
||||
OSStatus status;
|
||||
|
||||
|
||||
if (!mac->audioQueue)
|
||||
return;
|
||||
|
||||
|
||||
status = AudioQueueStart(mac->audioQueue, NULL);
|
||||
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "AudioQueueStart failed\n");
|
||||
}
|
||||
|
||||
|
||||
mac->isPlaying = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
static UINT rdpsnd_mac_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
int length;
|
||||
BYTE* data;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (mac->wformat == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
mac->dsp_context->decode_ms_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
|
||||
length = mac->dsp_context->adpcm_size;
|
||||
data = mac->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (mac->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
mac->dsp_context->decode_ima_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
|
||||
length = mac->dsp_context->adpcm_size;
|
||||
data = mac->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = wave->length;
|
||||
data = wave->data;
|
||||
}
|
||||
|
||||
wave->data = (BYTE*) malloc(length);
|
||||
CopyMemory(wave->data, data, length);
|
||||
wave->length = length;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_mac_waveplay(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
int length;
|
||||
size_t length;
|
||||
AudioQueueBufferRef audioBuffer;
|
||||
AudioTimeStamp outActualStartTime;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
|
||||
if (!mac->isOpen)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
audioBuffer = mac->audioBuffers[mac->audioBufferIndex];
|
||||
|
||||
length = wave->length > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : wave->length;
|
||||
|
||||
CopyMemory(audioBuffer->mAudioData, wave->data, length);
|
||||
free(wave->data);
|
||||
wave->data = NULL;
|
||||
length = size > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : size;
|
||||
CopyMemory(audioBuffer->mAudioData, data, length);
|
||||
audioBuffer->mAudioDataByteSize = length;
|
||||
audioBuffer->mUserData = wave;
|
||||
|
||||
AudioQueueEnqueueBufferWithParameters(mac->audioQueue, audioBuffer, 0, 0, 0, 0, 0, NULL, NULL, &outActualStartTime);
|
||||
UInt64 startTimeDelta = (outActualStartTime.mSampleTime - mac->lastStartTime) / 100.0;
|
||||
wave->wLocalTimeB = wave->wLocalTimeA + startTimeDelta + wave->wAudioLength;
|
||||
wave->wTimeStampB = wave->wTimeStampA + wave->wLocalTimeB - wave->wLocalTimeA;
|
||||
mac->lastStartTime = outActualStartTime.mSampleTime;
|
||||
|
||||
audioBuffer->mUserData = mac;
|
||||
AudioQueueEnqueueBufferWithParameters(mac->audioQueue, audioBuffer, 0, 0, 0, 0, 0, NULL, NULL,
|
||||
&outActualStartTime);
|
||||
mac->lastAudioBufferIndex = mac->audioBufferIndex;
|
||||
mac->audioBufferIndex++;
|
||||
mac->audioBufferIndex %= MAC_AUDIO_QUEUE_NUM_BUFFERS;
|
||||
|
||||
device->Start(device);
|
||||
rdpsnd_mac_start(device);
|
||||
return 10; /* TODO: Get real latencry in [ms] */
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
@ -371,25 +351,17 @@ static void rdpsnd_mac_waveplay(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
rdpsndMacPlugin* mac;
|
||||
|
||||
mac = (rdpsndMacPlugin*) calloc(1, sizeof(rdpsndMacPlugin));
|
||||
|
||||
|
||||
if (!mac)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
|
||||
mac->device.Open = rdpsnd_mac_open;
|
||||
mac->device.FormatSupported = rdpsnd_mac_format_supported;
|
||||
mac->device.SetFormat = rdpsnd_mac_set_format;
|
||||
mac->device.SetVolume = rdpsnd_mac_set_volume;
|
||||
mac->device.WaveDecode = rdpsnd_mac_wave_decode;
|
||||
mac->device.WavePlay = rdpsnd_mac_waveplay;
|
||||
mac->device.Start = rdpsnd_mac_start;
|
||||
mac->device.Play = rdpsnd_mac_play;
|
||||
mac->device.Close = rdpsnd_mac_close;
|
||||
mac->device.Free = rdpsnd_mac_free;
|
||||
|
||||
mac->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) mac);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -36,7 +36,6 @@
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "opensl_io.h"
|
||||
@ -48,7 +47,7 @@ struct rdpsnd_opensles_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
|
||||
int latency;
|
||||
UINT32 latency;
|
||||
int wformat;
|
||||
int block_size;
|
||||
char* device_name;
|
||||
@ -60,7 +59,6 @@ struct rdpsnd_opensles_plugin
|
||||
UINT32 rate;
|
||||
UINT32 channels;
|
||||
int format;
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
|
||||
static int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max)
|
||||
@ -91,9 +89,6 @@ static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin* hdl)
|
||||
rc = false;
|
||||
else
|
||||
{
|
||||
if (!hdl->dsp_context)
|
||||
rc = false;
|
||||
|
||||
if (!hdl->stream)
|
||||
rc = false;
|
||||
}
|
||||
@ -120,11 +115,11 @@ static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
|
||||
AUDIO_FORMAT* format, int latency)
|
||||
const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
|
||||
rdpsnd_opensles_check_handle(opensles);
|
||||
DEBUG_SND("opensles=%p format=%p, latency=%d", (void*) opensles, (void*) format, latency);
|
||||
DEBUG_SND("opensles=%p format=%p, latency=%"PRIu32, (void*) opensles, (void*) format, latency);
|
||||
|
||||
if (format)
|
||||
{
|
||||
@ -143,11 +138,11 @@ static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device,
|
||||
AUDIO_FORMAT* format, int latency)
|
||||
const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
|
||||
DEBUG_SND("opensles=%p format=%p, latency=%d, rate=%"PRIu32"",
|
||||
(void*) opensles, (void*) format, latency, opensles->rate);
|
||||
DEBUG_SND("opensles=%p format=%p, latency=%"PRIu32", rate=%"PRIu32"",
|
||||
(void*) opensles, (void*) format, latency, opensles->rate);
|
||||
|
||||
if (rdpsnd_opensles_check_handle(opensles))
|
||||
return TRUE;
|
||||
@ -161,8 +156,7 @@ static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device,
|
||||
else
|
||||
rdpsnd_opensles_set_volume(device, opensles->volume);
|
||||
|
||||
rdpsnd_opensles_set_format(device, format, latency);
|
||||
return TRUE;
|
||||
return rdpsnd_opensles_set_format(device, format, latency);
|
||||
}
|
||||
|
||||
static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
|
||||
@ -184,13 +178,11 @@ static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
|
||||
assert(opensles);
|
||||
assert(opensles->device_name);
|
||||
free(opensles->device_name);
|
||||
assert(opensles->dsp_context);
|
||||
freerdp_dsp_context_free(opensles->dsp_context);
|
||||
free(opensles);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
|
||||
AUDIO_FORMAT* format)
|
||||
const AUDIO_FORMAT* format)
|
||||
{
|
||||
DEBUG_SND("format=%"PRIu16", cbsize=%"PRIu16", samples=%"PRIu32", bits=%"PRIu16", channels=%"PRIu16", align=%"PRIu16"",
|
||||
format->wFormatTag, format->cbSize, format->nSamplesPerSec,
|
||||
@ -211,20 +203,6 @@ static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (format->nSamplesPerSec <= 48000 &&
|
||||
format->wBitsPerSample == 4 &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
case WAVE_FORMAT_GSM610:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -283,46 +261,22 @@ static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
|
||||
BYTE* data, int size)
|
||||
static UINT rdpsnd_opensles_play(rdpsndDevicePlugin* device,
|
||||
const BYTE* data, size_t size)
|
||||
{
|
||||
union
|
||||
{
|
||||
BYTE* b;
|
||||
short* s;
|
||||
const BYTE* b;
|
||||
const short* s;
|
||||
} src;
|
||||
int ret;
|
||||
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
|
||||
DEBUG_SND("opensles=%p, data=%p, size=%d", (void*) opensles, (void*) data, size);
|
||||
|
||||
if (!rdpsnd_opensles_check_handle(opensles))
|
||||
return;
|
||||
|
||||
if (opensles->format == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
DEBUG_SND("dsp_context=%p, channels=%"PRIu32", block_size=%d",
|
||||
(void*) opensles->dsp_context, opensles->channels, opensles->block_size);
|
||||
|
||||
opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context,
|
||||
data, size, opensles->channels, opensles->block_size);
|
||||
size = opensles->dsp_context->adpcm_size;
|
||||
src.b = opensles->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
DEBUG_SND("dsp_context=%p, channels=%"PRIu32", block_size=%d",
|
||||
(void*) opensles->dsp_context, opensles->channels, opensles->block_size);
|
||||
|
||||
opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context,
|
||||
data, size, opensles->channels, opensles->block_size);
|
||||
size = opensles->dsp_context->adpcm_size;
|
||||
src.b = opensles->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
src.b = data;
|
||||
}
|
||||
return 0;
|
||||
|
||||
src.b = data;
|
||||
DEBUG_SND("size=%d, src=%p", size, (void*) src.b);
|
||||
assert(0 == size % 2);
|
||||
assert(size > 0);
|
||||
@ -331,6 +285,8 @@ static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
|
||||
|
||||
if (ret < 0)
|
||||
WLog_ERR(TAG, "android_AudioOut failed (%d)", ret);
|
||||
|
||||
return 10; /* TODO: Get real latencry in [ms] */
|
||||
}
|
||||
|
||||
static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
|
||||
@ -361,7 +317,7 @@ static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device,
|
||||
DEBUG_SND("opensles=%p, args=%p", (void*) opensles, (void*) args);
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON |
|
||||
COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
rdpsnd_opensles_args, flags, opensles, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -416,7 +372,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(
|
||||
|
||||
opensles->device.Open = rdpsnd_opensles_open;
|
||||
opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
|
||||
opensles->device.SetFormat = rdpsnd_opensles_set_format;
|
||||
opensles->device.GetVolume = rdpsnd_opensles_get_volume;
|
||||
opensles->device.SetVolume = rdpsnd_opensles_set_volume;
|
||||
opensles->device.Start = rdpsnd_opensles_start;
|
||||
@ -440,20 +395,10 @@ UINT freerdp_rdpsnd_client_subsystem_entry(
|
||||
opensles->rate = 44100;
|
||||
opensles->channels = 2;
|
||||
opensles->format = WAVE_FORMAT_ADPCM;
|
||||
opensles->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!opensles->dsp_context)
|
||||
{
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out_dsp_new;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd,
|
||||
(rdpsndDevicePlugin*) opensles);
|
||||
DEBUG_SND("success");
|
||||
return CHANNEL_RC_OK;
|
||||
out_dsp_new:
|
||||
free(opensles->device_name);
|
||||
outstrdup:
|
||||
free(opensles);
|
||||
return error;
|
||||
|
||||
@ -47,7 +47,6 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
@ -64,10 +63,8 @@ struct rdpsnd_oss_plugin
|
||||
|
||||
int supported_formats;
|
||||
|
||||
int latency;
|
||||
UINT32 latency;
|
||||
AUDIO_FORMAT format;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
|
||||
#define OSS_LOG_ERR(_text, _error) \
|
||||
@ -77,7 +74,7 @@ struct rdpsnd_oss_plugin
|
||||
}
|
||||
|
||||
|
||||
static int rdpsnd_oss_get_format(AUDIO_FORMAT* format)
|
||||
static int rdpsnd_oss_get_format(const AUDIO_FORMAT* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
@ -95,21 +92,15 @@ static int rdpsnd_oss_get_format(AUDIO_FORMAT* format)
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return AFMT_A_LAW;
|
||||
#if 0 /* This does not work on my desktop. */
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return AFMT_MU_LAW;
|
||||
#endif
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return AFMT_S16_LE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
int req_fmt = 0;
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
@ -128,14 +119,12 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (format->nSamplesPerSec > 48000 ||
|
||||
format->wBitsPerSample != 4 ||
|
||||
(format->nChannels != 1 && format->nChannels != 2))
|
||||
return FALSE;
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
case WAVE_FORMAT_ALAW:
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
req_fmt = rdpsnd_oss_get_format(format);
|
||||
@ -155,7 +144,8 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_oss_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_oss_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
int tmp;
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
@ -227,7 +217,7 @@ static void rdpsnd_oss_open_mixer(rdpsndOssPlugin* oss)
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
|
||||
{
|
||||
char dev_name[PATH_MAX] = "/dev/dsp";
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
@ -272,7 +262,6 @@ static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
|
||||
rdpsnd_oss_set_format(device, format, latency);
|
||||
rdpsnd_oss_open_mixer(oss);
|
||||
return TRUE;
|
||||
@ -308,7 +297,6 @@ static void rdpsnd_oss_free(rdpsndDevicePlugin* device)
|
||||
return;
|
||||
|
||||
rdpsnd_oss_close(device);
|
||||
freerdp_dsp_context_free(oss->dsp_context);
|
||||
free(oss);
|
||||
}
|
||||
|
||||
@ -370,68 +358,36 @@ static BOOL rdpsnd_oss_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_oss_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
static UINT rdpsnd_oss_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
|
||||
if (device == NULL || wave == NULL)
|
||||
return FALSE;
|
||||
if (device == NULL || oss->mixer_handle == -1)
|
||||
return 0;
|
||||
|
||||
switch (oss->format.wFormatTag)
|
||||
while (size > 0)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
oss->dsp_context->decode_ms_adpcm(oss->dsp_context,
|
||||
wave->data, wave->length, oss->format.nChannels, oss->format.nBlockAlign);
|
||||
wave->length = oss->dsp_context->adpcm_size;
|
||||
wave->data = oss->dsp_context->adpcm_buffer;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
oss->dsp_context->decode_ima_adpcm(oss->dsp_context,
|
||||
wave->data, wave->length, oss->format.nChannels, oss->format.nBlockAlign);
|
||||
wave->length = oss->dsp_context->adpcm_size;
|
||||
wave->data = oss->dsp_context->adpcm_buffer;
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_oss_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
BYTE* data;
|
||||
int offset, size, status, latency;
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
|
||||
if (device == NULL || wave == NULL)
|
||||
return;
|
||||
|
||||
offset = 0;
|
||||
data = wave->data;
|
||||
size = wave->length;
|
||||
latency = oss->latency;
|
||||
|
||||
while (offset < size)
|
||||
{
|
||||
status = write(oss->pcm_handle, &data[offset], (size - offset));
|
||||
ssize_t status = write(oss->pcm_handle, data, size);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
OSS_LOG_ERR("write fail", errno);
|
||||
rdpsnd_oss_close(device);
|
||||
rdpsnd_oss_open(device, NULL, latency);
|
||||
rdpsnd_oss_open(device, NULL, oss->latency);
|
||||
break;
|
||||
}
|
||||
|
||||
offset += status;
|
||||
data += status;
|
||||
|
||||
if (status <= size)
|
||||
size -= status;
|
||||
else
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/* From rdpsnd_main.c */
|
||||
wave->wTimeStampB = wave->wTimeStampA + wave->wAudioLength + 65 + latency;
|
||||
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + 65 + latency;
|
||||
return 10; /* TODO: Get real latency in [ms] */
|
||||
}
|
||||
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A rdpsnd_oss_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
@ -446,7 +402,7 @@ static int rdpsnd_oss_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* a
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, rdpsnd_oss_args, flags,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_oss_args, flags,
|
||||
oss, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -514,11 +470,9 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
|
||||
oss->device.Open = rdpsnd_oss_open;
|
||||
oss->device.FormatSupported = rdpsnd_oss_format_supported;
|
||||
oss->device.SetFormat = rdpsnd_oss_set_format;
|
||||
oss->device.GetVolume = rdpsnd_oss_get_volume;
|
||||
oss->device.SetVolume = rdpsnd_oss_set_volume;
|
||||
oss->device.WaveDecode = rdpsnd_oss_wave_decode;
|
||||
oss->device.WavePlay = rdpsnd_oss_wave_play;
|
||||
oss->device.Play = rdpsnd_oss_play;
|
||||
oss->device.Close = rdpsnd_oss_close;
|
||||
oss->device.Free = rdpsnd_oss_free;
|
||||
oss->pcm_handle = -1;
|
||||
@ -526,14 +480,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
oss->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
rdpsnd_oss_parse_addin_args((rdpsndDevicePlugin*)oss, args);
|
||||
oss->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
if (!oss->dsp_context)
|
||||
{
|
||||
free(oss);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)oss);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -25,16 +25,10 @@ include_directories(${PULSE_INCLUDE_DIR})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
|
||||
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${PULSE_LIBRARY})
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS winpr)
|
||||
|
||||
if(GSM_FOUND)
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${GSM_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/Pulse")
|
||||
|
||||
@ -33,10 +33,6 @@
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#ifdef WITH_GSM
|
||||
#include <gsm/gsm.h>
|
||||
#endif
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
|
||||
@ -53,23 +49,52 @@ struct rdpsnd_pulse_plugin
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream* stream;
|
||||
int format;
|
||||
int block_size;
|
||||
int latency;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
#ifdef WITH_GSM
|
||||
gsm gsm_context;
|
||||
wStream* gsmBuffer;
|
||||
#endif
|
||||
UINT32 latency;
|
||||
UINT32 volume;
|
||||
};
|
||||
|
||||
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,
|
||||
void* userdata)
|
||||
{
|
||||
uint8_t x;
|
||||
UINT16 dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */;
|
||||
UINT16 dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
|
||||
|
||||
if (!pulse || !c || !i)
|
||||
return;
|
||||
|
||||
for (x = 0; x < i->volume.channels; x++)
|
||||
{
|
||||
pa_volume_t volume = i->volume.values[x];
|
||||
|
||||
if (volume >= PA_VOLUME_NORM)
|
||||
volume = PA_VOLUME_NORM - 1;
|
||||
|
||||
switch (x)
|
||||
{
|
||||
case 0:
|
||||
dwVolumeLeft = (UINT16)volume;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
dwVolumeRight = (UINT16)volume;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pulse->volume = ((UINT32)dwVolumeLeft << 16U) | dwVolumeRight;
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
|
||||
|
||||
state = pa_context_get_state(context);
|
||||
|
||||
switch (state)
|
||||
@ -90,6 +115,7 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
||||
|
||||
static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||
{
|
||||
pa_operation* o;
|
||||
pa_context_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
@ -124,6 +150,11 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
|
||||
|
||||
if (o)
|
||||
pa_operation_unref(o);
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
@ -140,7 +171,6 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
@ -161,7 +191,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
|
||||
|
||||
state = pa_stream_get_state(stream);
|
||||
|
||||
switch (state)
|
||||
@ -183,7 +212,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)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
@ -191,30 +219,27 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
#ifdef WITH_GSM
|
||||
if (pulse->gsm_context)
|
||||
gsm_destroy(pulse->gsm_context);
|
||||
#endif
|
||||
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
rdpsnd_pulse_wait_for_operation(pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
|
||||
rdpsnd_pulse_wait_for_operation(pulse, pa_stream_drain(pulse->stream,
|
||||
rdpsnd_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT* format)
|
||||
static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_FORMAT* format)
|
||||
{
|
||||
pa_sample_spec sample_spec = { 0 };
|
||||
|
||||
if (!pulse->context)
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
if (!rdpsnd_pulse_format_supported(&pulse->device, format))
|
||||
return FALSE;
|
||||
|
||||
sample_spec.rate = format->nSamplesPerSec;
|
||||
sample_spec.channels = format->nChannels;
|
||||
@ -227,15 +252,15 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT*
|
||||
case 8:
|
||||
sample_spec.format = PA_SAMPLE_U8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
@ -246,17 +271,16 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT*
|
||||
sample_spec.format = PA_SAMPLE_ULAW;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_GSM610:
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pulse->sample_spec = sample_spec;
|
||||
pulse->format = format->wFormatTag;
|
||||
pulse->block_size = format->nBlockAlign;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
pa_stream_flags_t flags;
|
||||
@ -267,7 +291,9 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
if (!pulse->context || pulse->stream)
|
||||
return TRUE;
|
||||
|
||||
rdpsnd_pulse_set_format_spec(pulse, format);
|
||||
if (!rdpsnd_pulse_set_format_spec(pulse, format))
|
||||
return FALSE;
|
||||
|
||||
pulse->latency = latency;
|
||||
|
||||
if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
|
||||
@ -277,8 +303,8 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
|
||||
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
@ -288,21 +314,20 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
/* register essential callbacks */
|
||||
pa_stream_set_state_callback(pulse->stream, rdpsnd_pulse_stream_state_callback, pulse);
|
||||
pa_stream_set_write_callback(pulse->stream, rdpsnd_pulse_stream_request_callback, pulse);
|
||||
|
||||
flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
|
||||
|
||||
if (pulse->latency > 0)
|
||||
{
|
||||
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.prebuf = (UINT32) -1;
|
||||
buffer_attr.minreq = (UINT32) -1;
|
||||
buffer_attr.fragsize = (UINT32) -1;
|
||||
buffer_attr.prebuf = (UINT32) - 1;
|
||||
buffer_attr.minreq = (UINT32) - 1;
|
||||
buffer_attr.fragsize = (UINT32) - 1;
|
||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||
}
|
||||
|
||||
if (pa_stream_connect_playback(pulse->stream,
|
||||
pulse->device_name, pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
|
||||
pulse->device_name, pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return TRUE;
|
||||
@ -326,17 +351,7 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
{
|
||||
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
|
||||
|
||||
#ifdef WITH_GSM
|
||||
if (pulse->gsm_context)
|
||||
gsm_destroy(pulse->gsm_context);
|
||||
|
||||
pulse->gsm_context = gsm_create();
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
rdpsnd_pulse_close(device);
|
||||
return FALSE;
|
||||
@ -369,83 +384,57 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
||||
pulse->mainloop = NULL;
|
||||
}
|
||||
|
||||
#ifdef WITH_GSM
|
||||
Stream_Free(pulse->gsmBuffer, TRUE);
|
||||
#endif
|
||||
|
||||
free(pulse->device_name);
|
||||
freerdp_dsp_context_free(pulse->dsp_context);
|
||||
free(pulse);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
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))
|
||||
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 8) &&
|
||||
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->wBitsPerSample == 4) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef WITH_GSM
|
||||
case WAVE_FORMAT_GSM610:
|
||||
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||
(format->nBlockAlign == 65) && (format->nChannels == 1))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device)
|
||||
{
|
||||
pa_operation* o;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
if (!pulse)
|
||||
return 0;
|
||||
|
||||
return rdpsnd_pulse_open(device, format, latency);
|
||||
if (!pulse->context || !pulse->mainloop)
|
||||
return 0;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return pulse->volume;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
@ -459,17 +448,15 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return FALSE;
|
||||
|
||||
left = (pa_volume_t) (value & 0xFFFF);
|
||||
right = (pa_volume_t) ((value >> 16) & 0xFFFF);
|
||||
|
||||
left = (pa_volume_t)(value & 0xFFFF);
|
||||
right = (pa_volume_t)((value >> 16) & 0xFFFF);
|
||||
pa_cvolume_init(&cv);
|
||||
cv.channels = 2;
|
||||
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)) / 0xFFFF;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream), &cv, NULL, NULL);
|
||||
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
|
||||
&cv, NULL, NULL);
|
||||
|
||||
if (operation)
|
||||
pa_operation_unref(operation);
|
||||
@ -478,112 +465,46 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BYTE* rdpsnd_pulse_convert_audio(rdpsndDevicePlugin* device, BYTE* data, int* size)
|
||||
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
BYTE* pcmData = NULL;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
if (pulse->format == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context,
|
||||
data, *size, pulse->sample_spec.channels, pulse->block_size);
|
||||
|
||||
*size = pulse->dsp_context->adpcm_size;
|
||||
pcmData = pulse->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (pulse->format == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context,
|
||||
data, *size, pulse->sample_spec.channels, pulse->block_size);
|
||||
|
||||
*size = pulse->dsp_context->adpcm_size;
|
||||
pcmData = pulse->dsp_context->adpcm_buffer;
|
||||
}
|
||||
#ifdef WITH_GSM
|
||||
else if (pulse->format == WAVE_FORMAT_GSM610)
|
||||
{
|
||||
int inPos = 0;
|
||||
int inSize = *size;
|
||||
UINT16 gsmBlockBuffer[160];
|
||||
|
||||
Stream_SetPosition(pulse->gsmBuffer, 0);
|
||||
|
||||
while (inSize)
|
||||
{
|
||||
ZeroMemory(gsmBlockBuffer, sizeof(gsmBlockBuffer));
|
||||
gsm_decode(pulse->gsm_context, (gsm_byte*) &data[inPos], (gsm_signal*) gsmBlockBuffer);
|
||||
|
||||
if ((inPos % 65) == 0)
|
||||
{
|
||||
inPos += 33;
|
||||
inSize -= 33;
|
||||
}
|
||||
else
|
||||
{
|
||||
inPos += 32;
|
||||
inSize -= 32;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(pulse->gsmBuffer, 160 * 2))
|
||||
return NULL;
|
||||
Stream_Write(pulse->gsmBuffer, (void*) gsmBlockBuffer, 160 * 2);
|
||||
}
|
||||
|
||||
Stream_SealLength(pulse->gsmBuffer);
|
||||
|
||||
pcmData = Stream_Buffer(pulse->gsmBuffer);
|
||||
*size = Stream_Length(pulse->gsmBuffer);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
pcmData = data;
|
||||
}
|
||||
|
||||
return pcmData;
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
{
|
||||
int length;
|
||||
size_t length;
|
||||
int status;
|
||||
BYTE* pcmData;
|
||||
pa_usec_t latency;
|
||||
int negative;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
if (!pulse->stream)
|
||||
return;
|
||||
|
||||
pcmData = rdpsnd_pulse_convert_audio(device, data, &size);
|
||||
if (!pcmData)
|
||||
return;
|
||||
if (!pulse->stream || !data)
|
||||
return 0;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
while ((length = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
if (length == (size_t) -1)
|
||||
break;
|
||||
|
||||
if (length > size)
|
||||
length = size;
|
||||
|
||||
status = pa_stream_write(pulse->stream, pcmData, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pcmData += length;
|
||||
data += length;
|
||||
size -= length;
|
||||
}
|
||||
|
||||
if (pa_stream_get_latency(pulse->stream, &latency, &negative) != 0)
|
||||
latency = 0;
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return latency / 1000;
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_start(rdpsndDevicePlugin* device)
|
||||
@ -598,7 +519,7 @@ static void rdpsnd_pulse_start(rdpsndDevicePlugin* device)
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] =
|
||||
static COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
@ -615,29 +536,28 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
rdpsnd_pulse_args, flags, pulse, NULL, NULL);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
rdpsnd_pulse_args, flags, pulse, NULL, NULL);
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
arg = rdpsnd_pulse_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
pulse->device_name = _strdup(arg->Value);
|
||||
|
||||
if (!pulse->device_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
@ -661,24 +581,25 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndPulsePlugin* pulse;
|
||||
UINT ret;
|
||||
|
||||
pulse = (rdpsndPulsePlugin*) calloc(1, sizeof(rdpsndPulsePlugin));
|
||||
|
||||
if (!pulse)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
pulse->device.Open = rdpsnd_pulse_open;
|
||||
pulse->device.FormatSupported = rdpsnd_pulse_format_supported;
|
||||
pulse->device.SetFormat = rdpsnd_pulse_set_format;
|
||||
pulse->device.GetVolume = rdpsnd_pulse_get_volume;
|
||||
pulse->device.SetVolume = rdpsnd_pulse_set_volume;
|
||||
pulse->device.Play = rdpsnd_pulse_play;
|
||||
pulse->device.Start = rdpsnd_pulse_start;
|
||||
pulse->device.Close = rdpsnd_pulse_close;
|
||||
pulse->device.Free = rdpsnd_pulse_free;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if (args->argc > 1)
|
||||
{
|
||||
ret = rdpsnd_pulse_parse_addin_args((rdpsndDevicePlugin *) pulse, args);
|
||||
ret = rdpsnd_pulse_parse_addin_args((rdpsndDevicePlugin*) pulse, args);
|
||||
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing arguments");
|
||||
@ -687,17 +608,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
}
|
||||
|
||||
ret = CHANNEL_RC_NO_MEMORY;
|
||||
pulse->dsp_context = freerdp_dsp_context_new();
|
||||
if (!pulse->dsp_context)
|
||||
goto error;
|
||||
|
||||
#ifdef WITH_GSM
|
||||
pulse->gsmBuffer = Stream_New(NULL, 4096);
|
||||
if (!pulse->gsmBuffer)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
|
||||
if (!pulse->mainloop)
|
||||
@ -709,14 +619,13 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
goto error;
|
||||
|
||||
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
|
||||
|
||||
ret = ERROR_INVALID_OPERATION;
|
||||
|
||||
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
|
||||
goto error;
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) pulse);
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
error:
|
||||
rdpsnd_pulse_free((rdpsndDevicePlugin*)pulse);
|
||||
return ret;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,4 @@
|
||||
#define DEBUG_SND(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
UINT rdpsnd_virtual_channel_write(rdpsndPlugin* rdpsnd, wStream* s);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPSND_CLIENT_MAIN_H */
|
||||
|
||||
@ -36,7 +36,6 @@
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
@ -49,15 +48,14 @@ struct rdpsnd_winmm_plugin
|
||||
|
||||
HWAVEOUT hWaveOut;
|
||||
WAVEFORMATEX format;
|
||||
int wformat;
|
||||
int block_size;
|
||||
UINT32 volume;
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
HANDLE next;
|
||||
};
|
||||
|
||||
static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out)
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
if (!in || !out)
|
||||
return FALSE;
|
||||
|
||||
ZeroMemory(out, sizeof(WAVEFORMATEX));
|
||||
out->wFormatTag = WAVE_FORMAT_PCM;
|
||||
@ -68,40 +66,31 @@ static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* ou
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
out->wBitsPerSample = in->wBitsPerSample;
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
out->wBitsPerSample = 16;
|
||||
result = TRUE;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
|
||||
out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (format)
|
||||
{
|
||||
if (!rdpsnd_winmm_convert_format(format, &winmm->format))
|
||||
return FALSE;
|
||||
|
||||
winmm->wformat = format->wFormatTag;
|
||||
winmm->block_size = format->nBlockAlign;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (!rdpsnd_winmm_convert_format(format, &winmm->format))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
RDPSND_WAVE* wave;
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) dwInstance;
|
||||
|
||||
@ -110,42 +99,27 @@ static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWO
|
||||
case MM_WOM_OPEN:
|
||||
WLog_DBG(TAG, "MM_WOM_OPEN");
|
||||
break;
|
||||
|
||||
|
||||
case MM_WOM_CLOSE:
|
||||
WLog_DBG(TAG, "MM_WOM_CLOSE");
|
||||
SetEvent(winmm->next);
|
||||
break;
|
||||
|
||||
case MM_WOM_DONE:
|
||||
{
|
||||
UINT32 wTimeDelta;
|
||||
lpWaveHdr = (LPWAVEHDR) dwParam1;
|
||||
WLog_DBG(TAG, "MM_WOM_DONE");
|
||||
lpWaveHdr = (LPWAVEHDR) dwParam1;
|
||||
free(lpWaveHdr);
|
||||
SetEvent(winmm->next);
|
||||
break;
|
||||
|
||||
if (!lpWaveHdr)
|
||||
return;
|
||||
|
||||
wave = (RDPSND_WAVE*) lpWaveHdr->dwUser;
|
||||
|
||||
if (!wave)
|
||||
return;
|
||||
|
||||
WLog_DBG(TAG, "MM_WOM_DONE: dwBufferLength: %"PRIu32" cBlockNo: %"PRIu8"",
|
||||
lpWaveHdr->dwBufferLength, wave->cBlockNo);
|
||||
wave->wLocalTimeB = GetTickCount();
|
||||
wTimeDelta = wave->wLocalTimeB - wave->wLocalTimeA;
|
||||
wave->wTimeStampB = wave->wTimeStampA + wTimeDelta;
|
||||
|
||||
winmm->device.WaveConfirm(&(winmm->device), wave);
|
||||
|
||||
free(lpWaveHdr->lpData);
|
||||
free(lpWaveHdr);
|
||||
|
||||
free(wave);
|
||||
}
|
||||
default:
|
||||
WLog_DBG(TAG, "UNKNOWN [0x%08"PRIx32"]", uMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
MMRESULT mmResult;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
@ -153,15 +127,15 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
if (winmm->hWaveOut)
|
||||
return TRUE;
|
||||
|
||||
rdpsnd_winmm_set_format(device, format, latency);
|
||||
freerdp_dsp_context_reset_adpcm(winmm->dsp_context);
|
||||
if (!rdpsnd_winmm_set_format(device, format, latency))
|
||||
return FALSE;
|
||||
|
||||
mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
|
||||
(DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION);
|
||||
(DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION);
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
WLog_ERR(TAG, "waveOutOpen failed: %"PRIu32"", mmResult);
|
||||
WLog_ERR(TAG, "waveOutOpen failed: %"PRIu32"", mmResult);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -169,7 +143,7 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
WLog_ERR(TAG, "waveOutSetVolume failed: %"PRIu32"", mmResult);
|
||||
WLog_ERR(TAG, "waveOutSetVolume failed: %"PRIu32"", mmResult);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -184,14 +158,13 @@ static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
|
||||
if (winmm->hWaveOut)
|
||||
{
|
||||
mmResult = waveOutReset(winmm->hWaveOut);
|
||||
|
||||
mmResult = waveOutClose(winmm->hWaveOut);
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
WLog_ERR(TAG, "waveOutClose failure: %"PRIu32"", mmResult);
|
||||
}
|
||||
|
||||
|
||||
winmm->hWaveOut = NULL;
|
||||
}
|
||||
}
|
||||
@ -203,14 +176,12 @@ static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
|
||||
if (winmm)
|
||||
{
|
||||
rdpsnd_winmm_close(device);
|
||||
|
||||
freerdp_dsp_context_free(winmm->dsp_context);
|
||||
|
||||
CloseHandle(winmm->next);
|
||||
free(winmm);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
MMRESULT result;
|
||||
WAVEFORMATEX out;
|
||||
@ -231,9 +202,7 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
|
||||
DWORD dwVolume;
|
||||
UINT16 dwVolumeLeft;
|
||||
UINT16 dwVolumeRight;
|
||||
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
|
||||
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
|
||||
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
|
||||
@ -242,14 +211,12 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
|
||||
return dwVolume;
|
||||
|
||||
waveOutGetVolume(winmm->hWaveOut, &dwVolume);
|
||||
|
||||
return dwVolume;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
winmm->volume = value;
|
||||
|
||||
if (!winmm->hWaveOut)
|
||||
@ -258,72 +225,38 @@ static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
return (waveOutSetVolume(winmm->hWaveOut, value) == MMSYSERR_NOERROR);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
int length;
|
||||
BYTE* data;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (winmm->wformat == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
winmm->dsp_context->decode_ms_adpcm(winmm->dsp_context,
|
||||
wave->data, wave->length, winmm->format.nChannels, winmm->block_size);
|
||||
length = winmm->dsp_context->adpcm_size;
|
||||
data = winmm->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (winmm->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
winmm->dsp_context->decode_ima_adpcm(winmm->dsp_context,
|
||||
wave->data, wave->length, winmm->format.nChannels, winmm->block_size);
|
||||
length = winmm->dsp_context->adpcm_size;
|
||||
data = winmm->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = wave->length;
|
||||
data = wave->data;
|
||||
}
|
||||
|
||||
wave->data = (BYTE*) malloc(length);
|
||||
if (!wave->data)
|
||||
return FALSE;
|
||||
CopyMemory(wave->data, data, length);
|
||||
wave->length = length;
|
||||
|
||||
return TRUE;
|
||||
//rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
}
|
||||
|
||||
void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
MMRESULT mmResult;
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (!winmm->hWaveOut)
|
||||
return;
|
||||
|
||||
wave->AutoConfirm = FALSE;
|
||||
return 0;
|
||||
|
||||
lpWaveHdr = (LPWAVEHDR) malloc(sizeof(WAVEHDR));
|
||||
|
||||
if (!lpWaveHdr)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
ZeroMemory(lpWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
lpWaveHdr->dwFlags = 0;
|
||||
lpWaveHdr->dwLoops = 0;
|
||||
lpWaveHdr->lpData = (LPSTR) wave->data;
|
||||
lpWaveHdr->dwBufferLength = wave->length;
|
||||
lpWaveHdr->dwUser = (DWORD_PTR) wave;
|
||||
lpWaveHdr->lpData = (LPSTR) data;
|
||||
lpWaveHdr->dwBufferLength = size;
|
||||
lpWaveHdr->dwUser = NULL;
|
||||
lpWaveHdr->lpNext = NULL;
|
||||
|
||||
mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
WLog_ERR(TAG, "waveOutPrepareHeader failure: %"PRIu32"", mmResult);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
@ -333,18 +266,15 @@ void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
WLog_ERR(TAG, "waveOutWrite failure: %"PRIu32"", mmResult);
|
||||
waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
free(lpWaveHdr);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
//rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
WaitForSingleObject(winmm->next, INFINITE);
|
||||
return 10; /* TODO: Get real latencry in [ms] */
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
@ -362,38 +292,30 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndWinmmPlugin* winmm;
|
||||
|
||||
winmm = (rdpsndWinmmPlugin*) calloc(1, sizeof(rdpsndWinmmPlugin));
|
||||
|
||||
if (!winmm)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
winmm->device.DisableConfirmThread = TRUE;
|
||||
|
||||
winmm->device.Open = rdpsnd_winmm_open;
|
||||
winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
|
||||
winmm->device.SetFormat = rdpsnd_winmm_set_format;
|
||||
winmm->device.GetVolume = rdpsnd_winmm_get_volume;
|
||||
winmm->device.SetVolume = rdpsnd_winmm_set_volume;
|
||||
winmm->device.WaveDecode = rdpsnd_winmm_wave_decode;
|
||||
winmm->device.WavePlay = rdpsnd_winmm_wave_play;
|
||||
winmm->device.Start = rdpsnd_winmm_start;
|
||||
winmm->device.Play = rdpsnd_winmm_play;
|
||||
winmm->device.Close = rdpsnd_winmm_close;
|
||||
winmm->device.Free = rdpsnd_winmm_free;
|
||||
winmm->next = CreateEventA(NULL, FALSE, FALSE, "winmm-play-event");
|
||||
|
||||
args = pEntryPoints->args;
|
||||
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args);
|
||||
|
||||
winmm->dsp_context = freerdp_dsp_context_new();
|
||||
if (!winmm->dsp_context)
|
||||
if (!winmm->next)
|
||||
{
|
||||
free(winmm);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
args = pEntryPoints->args;
|
||||
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args);
|
||||
winmm->volume = 0xFFFFFFFF;
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) winmm);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
24
channels/rdpsnd/common/CMakeLists.txt
Normal file
24
channels/rdpsnd/common/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2018 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2018 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(SRCS
|
||||
rdpsnd_common.h
|
||||
rdpsnd_common.c)
|
||||
|
||||
# Library currently header only
|
||||
#add_library(rdpsnd-common STATIC ${SRCS})
|
||||
43
channels/rdpsnd/common/rdpsnd_common.h
Normal file
43
channels/rdpsnd/common/rdpsnd_common.h
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Server Audio Virtual Channel
|
||||
*
|
||||
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2018 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_RDPSND_COMMON_MAIN_H
|
||||
#define FREERDP_CHANNEL_RDPSND_COMMON_MAIN_H
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/server/rdpsnd.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CHANNEL_VERSION_WIN_XP = 0x02,
|
||||
CHANNEL_VERSION_WIN_XP_SP1 = 0x05,
|
||||
CHANNEL_VERSION_WIN_VISTA = 0x05,
|
||||
CHANNEL_VERSION_WIN_7 = 0x06,
|
||||
CHANNEL_VERSION_WIN_8 = 0x08,
|
||||
CHANNEL_VERSION_WIN_MAX = CHANNEL_VERSION_WIN_8
|
||||
} RdpSndChannelVersion;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPSND_COMMON_MAIN_H */
|
||||
@ -23,9 +23,4 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#include "rdpsnd_common.h"
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
/**
|
||||
@ -40,11 +41,11 @@
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
{
|
||||
size_t pos;
|
||||
UINT16 i;
|
||||
BOOL status;
|
||||
BOOL status = FALSE;
|
||||
ULONG written;
|
||||
Stream_Write_UINT8(s, SNDC_FORMATS);
|
||||
Stream_Write_UINT8(s, 0);
|
||||
@ -55,30 +56,17 @@ UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
Stream_Write_UINT16(s, 0); /* wDGramPort */
|
||||
Stream_Write_UINT16(s, context->num_server_formats); /* wNumberOfFormats */
|
||||
Stream_Write_UINT8(s, context->block_no); /* cLastBlockConfirmed */
|
||||
Stream_Write_UINT16(s, 0x06); /* wVersion */
|
||||
Stream_Write_UINT16(s, CHANNEL_VERSION_WIN_MAX); /* wVersion */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
|
||||
for (i = 0; i < context->num_server_formats; i++)
|
||||
{
|
||||
Stream_Write_UINT16(s,
|
||||
context->server_formats[i].wFormatTag); /* wFormatTag (WAVE_FORMAT_PCM) */
|
||||
Stream_Write_UINT16(s, context->server_formats[i].nChannels); /* nChannels */
|
||||
Stream_Write_UINT32(s,
|
||||
context->server_formats[i].nSamplesPerSec); /* nSamplesPerSec */
|
||||
Stream_Write_UINT32(s, context->server_formats[i].nSamplesPerSec *
|
||||
context->server_formats[i].nChannels *
|
||||
context->server_formats[i].wBitsPerSample / 8); /* nAvgBytesPerSec */
|
||||
Stream_Write_UINT16(s,
|
||||
context->server_formats[i].nBlockAlign); /* nBlockAlign */
|
||||
Stream_Write_UINT16(s,
|
||||
context->server_formats[i].wBitsPerSample); /* wBitsPerSample */
|
||||
Stream_Write_UINT16(s, context->server_formats[i].cbSize); /* cbSize */
|
||||
AUDIO_FORMAT format = context->server_formats[i];
|
||||
// TODO: Eliminate this!!!
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||
|
||||
if (context->server_formats[i].cbSize > 0)
|
||||
{
|
||||
Stream_Write(s, context->server_formats[i].data,
|
||||
context->server_formats[i].cbSize);
|
||||
}
|
||||
if (!audio_format_write(s, &format))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pos = Stream_GetPosition(s);
|
||||
@ -88,6 +76,7 @@ UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
|
||||
(PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
Stream_SetPosition(s, 0);
|
||||
fail:
|
||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -149,7 +138,7 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context,
|
||||
*/
|
||||
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
||||
{
|
||||
int i, num_known_format = 0;
|
||||
UINT16 i, num_known_format = 0;
|
||||
UINT32 flags, vol, pitch;
|
||||
UINT16 udpPort;
|
||||
BYTE lastblock;
|
||||
@ -183,8 +172,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
context->client_formats = (AUDIO_FORMAT*)calloc(context->num_client_formats,
|
||||
sizeof(AUDIO_FORMAT));
|
||||
context->client_formats = audio_formats_new(context->num_client_formats);
|
||||
|
||||
if (!context->client_formats)
|
||||
{
|
||||
@ -246,17 +234,10 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
|
||||
RdpsndServerContext* context;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
context = (RdpsndServerContext*)arg;
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = context->priv->channelEvent;
|
||||
events[nCount++] = context->priv->StopEvent;
|
||||
|
||||
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %"PRIu32"", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
@ -287,8 +268,6 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
if (error && context->rdpcontext)
|
||||
setChannelError(context->rdpcontext, error,
|
||||
"rdpsnd_server_thread reported an error");
|
||||
@ -315,24 +294,24 @@ static UINT rdpsnd_server_initialize(RdpsndServerContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_select_format(RdpsndServerContext* context,
|
||||
int client_format_index)
|
||||
UINT16 client_format_index)
|
||||
{
|
||||
int bs;
|
||||
int out_buffer_size;
|
||||
AUDIO_FORMAT* format;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (client_format_index < 0
|
||||
|| client_format_index >= context->num_client_formats)
|
||||
if ((client_format_index >= context->num_client_formats)
|
||||
|| (!context->src_format))
|
||||
{
|
||||
WLog_ERR(TAG, "index %d is not correct.", client_format_index);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&context->priv->lock);
|
||||
context->priv->src_bytes_per_sample = context->src_format.wBitsPerSample / 8;
|
||||
context->priv->src_bytes_per_sample = context->src_format->wBitsPerSample / 8;
|
||||
context->priv->src_bytes_per_frame = context->priv->src_bytes_per_sample *
|
||||
context->src_format.nChannels;
|
||||
context->src_format->nChannels;
|
||||
context->selected_client_format = client_format_index;
|
||||
format = &context->client_formats[client_format_index];
|
||||
|
||||
@ -346,7 +325,7 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context,
|
||||
if (context->latency <= 0)
|
||||
context->latency = 50;
|
||||
|
||||
context->priv->out_frames = context->src_format.nSamplesPerSec *
|
||||
context->priv->out_frames = context->src_format->nSamplesPerSec *
|
||||
context->latency / 1000;
|
||||
|
||||
if (context->priv->out_frames < 1)
|
||||
@ -393,120 +372,101 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context,
|
||||
context->priv->out_buffer_size = out_buffer_size;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(context->priv->dsp_context);
|
||||
freerdp_dsp_context_reset(context->priv->dsp_context, format);
|
||||
out:
|
||||
LeaveCriticalSection(&context->priv->lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
|
||||
{
|
||||
size_t size;
|
||||
Stream_SealLength(s);
|
||||
size = Stream_Length(s);
|
||||
|
||||
if ((size % alignment) != 0)
|
||||
{
|
||||
size_t offset = alignment - size % alignment;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, offset))
|
||||
return FALSE;
|
||||
|
||||
Stream_Zero(s, offset);
|
||||
}
|
||||
|
||||
Stream_SealLength(s);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* context->priv->lock should be obtained before calling this function
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context,
|
||||
UINT16 wTimestamp)
|
||||
static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context,
|
||||
UINT16 wTimestamp)
|
||||
{
|
||||
int size;
|
||||
BYTE* src;
|
||||
int frames;
|
||||
int fill_size;
|
||||
BOOL status;
|
||||
size_t length;
|
||||
size_t start, end = 0;
|
||||
const BYTE* src;
|
||||
AUDIO_FORMAT* format;
|
||||
int tbytes_per_frame;
|
||||
ULONG written;
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
format = &context->client_formats[context->selected_client_format];
|
||||
tbytes_per_frame = format->nChannels * context->priv->src_bytes_per_sample;
|
||||
|
||||
if ((format->nSamplesPerSec == context->src_format.nSamplesPerSec) &&
|
||||
(format->nChannels == context->src_format.nChannels))
|
||||
{
|
||||
src = context->priv->out_buffer;
|
||||
frames = context->priv->out_pending_frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->priv->dsp_context->resample(context->priv->dsp_context,
|
||||
context->priv->out_buffer,
|
||||
context->priv->src_bytes_per_sample, context->src_format.nChannels,
|
||||
context->src_format.nSamplesPerSec, context->priv->out_pending_frames,
|
||||
format->nChannels, format->nSamplesPerSec);
|
||||
frames = context->priv->dsp_context->resampled_frames;
|
||||
src = context->priv->dsp_context->resampled_buffer;
|
||||
}
|
||||
|
||||
size = frames * tbytes_per_frame;
|
||||
|
||||
if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
context->priv->dsp_context->encode_ima_adpcm(context->priv->dsp_context,
|
||||
src, size, format->nChannels, format->nBlockAlign);
|
||||
src = context->priv->dsp_context->adpcm_buffer;
|
||||
size = context->priv->dsp_context->adpcm_size;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
context->priv->dsp_context->encode_ms_adpcm(context->priv->dsp_context,
|
||||
src, size, format->nChannels, format->nBlockAlign);
|
||||
src = context->priv->dsp_context->adpcm_buffer;
|
||||
size = context->priv->dsp_context->adpcm_size;
|
||||
}
|
||||
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
/* Fill to nBlockAlign for the last audio packet */
|
||||
fill_size = 0;
|
||||
|
||||
if ((format->wFormatTag == WAVE_FORMAT_DVI_ADPCM
|
||||
|| format->wFormatTag == WAVE_FORMAT_ADPCM) &&
|
||||
(context->priv->out_pending_frames < context->priv->out_frames)
|
||||
&& ((size % format->nBlockAlign) != 0))
|
||||
{
|
||||
fill_size = format->nBlockAlign - (size % format->nBlockAlign);
|
||||
}
|
||||
|
||||
/* WaveInfo PDU */
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT16(s, size + fill_size + 8); /* BodySize */
|
||||
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(s, src, 4);
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
|
||||
(PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
start = Stream_GetPosition(s);
|
||||
src = context->priv->out_buffer;
|
||||
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
||||
|
||||
if (!status)
|
||||
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
else
|
||||
{
|
||||
/* Set stream size */
|
||||
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 - start + 8);
|
||||
Stream_SetPosition(s, end);
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
|
||||
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle,
|
||||
(PCHAR) Stream_Buffer(s), start + 4, &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
/* Wave PDU */
|
||||
if (!Stream_EnsureRemainingCapacity(s, size + fill_size))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, start);
|
||||
Stream_Write_UINT32(s, 0); /* bPad */
|
||||
Stream_Write(s, src + 4, size - 4);
|
||||
Stream_SetPosition(s, start);
|
||||
|
||||
if (fill_size > 0)
|
||||
Stream_Zero(s, fill_size);
|
||||
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
|
||||
(PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
|
||||
|
||||
if (!status)
|
||||
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle,
|
||||
(PCHAR) Stream_Pointer(s), end - start, &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
@ -518,6 +478,80 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* context->priv->lock should be obtained before calling this function
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context,
|
||||
UINT16 wTimestamp)
|
||||
{
|
||||
size_t length;
|
||||
size_t end = 0;
|
||||
const BYTE* src;
|
||||
AUDIO_FORMAT* format;
|
||||
ULONG written;
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
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;
|
||||
else
|
||||
{
|
||||
BOOL rc;
|
||||
|
||||
/* Set stream size */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
context->priv->out_pending_frames = 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
|
||||
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context,
|
||||
UINT16 wTimestamp)
|
||||
{
|
||||
if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
|
||||
return rdpsnd_server_send_wave2_pdu(context, wTimestamp);
|
||||
else
|
||||
return rdpsnd_server_send_wave_pdu(context, wTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -531,7 +565,7 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context,
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
EnterCriticalSection(&context->priv->lock);
|
||||
|
||||
if (context->selected_client_format < 0)
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
{
|
||||
/* It's possible while format negotiation has not been done */
|
||||
WLog_WARN(TAG, "Drop samples because client format has not been negotiated.");
|
||||
@ -578,7 +612,6 @@ static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left,
|
||||
BOOL status;
|
||||
ULONG written;
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
|
||||
Stream_Write_UINT8(s, SNDC_SETVOLUME);
|
||||
Stream_Write_UINT8(s, 0);
|
||||
Stream_Seek_UINT16(s);
|
||||
@ -610,7 +643,7 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
|
||||
|
||||
if (context->priv->out_pending_frames > 0)
|
||||
{
|
||||
if (context->selected_client_format < 0)
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
{
|
||||
WLog_ERR(TAG, "Pending audio frame exists while no format selected.");
|
||||
error = ERROR_INVALID_DATA;
|
||||
@ -626,7 +659,7 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
context->selected_client_format = -1;
|
||||
context->selected_client_format = 0xFFFF;
|
||||
Stream_Write_UINT8(s, SNDC_CLOSE);
|
||||
Stream_Write_UINT8(s, 0);
|
||||
Stream_Seek_UINT16(s);
|
||||
@ -690,6 +723,12 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
|
||||
goto out_pdu;
|
||||
}
|
||||
|
||||
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %"PRIu32"", error);
|
||||
goto out_lock;
|
||||
}
|
||||
|
||||
if (priv->ownThread)
|
||||
{
|
||||
context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
@ -701,7 +740,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
|
||||
}
|
||||
|
||||
context->priv->Thread = CreateThread(NULL, 0,
|
||||
rdpsnd_server_thread, (void*) context, 0, NULL);
|
||||
rdpsnd_server_thread, (void*) context, 0, NULL);
|
||||
|
||||
if (!context->priv->Thread)
|
||||
{
|
||||
@ -775,7 +814,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
||||
context->vcm = vcm;
|
||||
context->Start = rdpsnd_server_start;
|
||||
context->Stop = rdpsnd_server_stop;
|
||||
context->selected_client_format = -1;
|
||||
context->selected_client_format = 0xFFFF;
|
||||
context->Initialize = rdpsnd_server_initialize;
|
||||
context->SelectFormat = rdpsnd_server_select_format;
|
||||
context->SendSamples = rdpsnd_server_send_samples;
|
||||
@ -790,7 +829,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
priv->dsp_context = freerdp_dsp_context_new();
|
||||
priv->dsp_context = freerdp_dsp_context_new(TRUE);
|
||||
|
||||
if (!priv->dsp_context)
|
||||
{
|
||||
@ -927,7 +966,7 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
||||
case SNDC_FORMATS:
|
||||
ret = rdpsnd_server_recv_formats(context, s);
|
||||
|
||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion < 6))
|
||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion < CHANNEL_VERSION_WIN_7))
|
||||
IFCALL(context->Activated, context);
|
||||
|
||||
break;
|
||||
@ -937,7 +976,7 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
||||
Stream_SetPosition(s,
|
||||
0); /* in case the Activated callback tries to treat some messages */
|
||||
|
||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= 6))
|
||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
|
||||
IFCALL(context->Activated, context);
|
||||
|
||||
break;
|
||||
|
||||
@ -109,6 +109,7 @@ static UINT remdesk_generate_expert_blob(remdeskPlugin* remdesk)
|
||||
}
|
||||
|
||||
remdesk->ExpertBlob = freerdp_assistance_construct_expert_blob(name, pass);
|
||||
free(pass);
|
||||
|
||||
if (!remdesk->ExpertBlob)
|
||||
{
|
||||
@ -219,7 +220,7 @@ static UINT remdesk_prepare_ctl_header(REMDESK_CTL_HEADER* ctlHeader,
|
||||
UINT32 msgType, UINT32 msgSize)
|
||||
{
|
||||
ctlHeader->msgType = msgType;
|
||||
strcpy(ctlHeader->ChannelName, REMDESK_CHANNEL_CTL_NAME);
|
||||
sprintf_s(ctlHeader->ChannelName, ARRAYSIZE(ctlHeader->ChannelName), REMDESK_CHANNEL_CTL_NAME);
|
||||
ctlHeader->DataLength = 4 + msgSize;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -788,7 +789,6 @@ static VOID VCAPITYPE remdesk_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
@ -882,7 +882,7 @@ static UINT remdesk_virtual_channel_event_connected(remdeskPlugin* remdesk,
|
||||
}
|
||||
|
||||
remdesk->thread = CreateThread(NULL, 0,
|
||||
remdesk_virtual_channel_client_thread, (void*) remdesk,
|
||||
remdesk_virtual_channel_client_thread, (void*) remdesk,
|
||||
0, NULL);
|
||||
|
||||
if (!remdesk->thread)
|
||||
@ -908,6 +908,9 @@ static UINT remdesk_virtual_channel_event_disconnected(remdeskPlugin* remdesk)
|
||||
{
|
||||
UINT rc;
|
||||
|
||||
if (remdesk->OpenHandle == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (MessageQueue_PostQuit(remdesk->queue, 0)
|
||||
&& (WaitForSingleObject(remdesk->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
@ -1019,7 +1022,7 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
strcpy(remdesk->channelDef.name, "remdesk");
|
||||
sprintf_s(remdesk->channelDef.name, ARRAYSIZE(remdesk->channelDef.name), "remdesk");
|
||||
remdesk->Version = 2;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*) pEntryPoints;
|
||||
|
||||
|
||||
@ -152,7 +152,7 @@ static UINT remdesk_prepare_ctl_header(REMDESK_CTL_HEADER* ctlHeader,
|
||||
UINT32 msgType, UINT32 msgSize)
|
||||
{
|
||||
ctlHeader->msgType = msgType;
|
||||
strcpy(ctlHeader->ChannelName, REMDESK_CHANNEL_CTL_NAME);
|
||||
sprintf_s(ctlHeader->ChannelName, ARRAYSIZE(ctlHeader->ChannelName), REMDESK_CHANNEL_CTL_NAME);
|
||||
ctlHeader->DataLength = 4 + msgSize;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -592,7 +592,6 @@ static DWORD WINAPI remdesk_server_thread(LPVOID arg)
|
||||
RemdeskServerContext* context;
|
||||
UINT error;
|
||||
context = (RemdeskServerContext*) arg;
|
||||
|
||||
buffer = NULL;
|
||||
BytesReturned = 0;
|
||||
ChannelEvent = NULL;
|
||||
@ -727,7 +726,7 @@ static UINT remdesk_server_start(RemdeskServerContext* context)
|
||||
}
|
||||
|
||||
if (!(context->priv->Thread = CreateThread(NULL, 0,
|
||||
remdesk_server_thread, (void*) context, 0, NULL)))
|
||||
remdesk_server_thread, (void*) context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
@ -790,7 +789,7 @@ void remdesk_server_context_free(RemdeskServerContext* context)
|
||||
{
|
||||
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
}
|
||||
|
||||
@ -575,6 +575,8 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
|
||||
serial->IrpThreadToBeTerminatedCount);
|
||||
Sleep(1); /* 1 ms */
|
||||
}
|
||||
|
||||
free(ids);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&serial->TerminatingIrpThreadsLock);
|
||||
@ -694,6 +696,7 @@ static void terminate_pending_irp_threads(SERIAL_DEVICE* serial)
|
||||
}
|
||||
|
||||
ListDictionary_Clear(serial->IrpThreads);
|
||||
free(ids);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -51,37 +51,38 @@
|
||||
#include <freerdp/server/encomsp.h>
|
||||
#include <freerdp/server/rdpgfx.h>
|
||||
|
||||
void freerdp_channels_dummy()
|
||||
void freerdp_channels_dummy()
|
||||
{
|
||||
audin_server_context_new(NULL);
|
||||
audin_server_context_free(NULL);
|
||||
|
||||
rdpsnd_server_context_new(NULL);
|
||||
rdpsnd_server_context_free(NULL);
|
||||
|
||||
cliprdr_server_context_new(NULL);
|
||||
cliprdr_server_context_free(NULL);
|
||||
|
||||
echo_server_context_new(NULL);
|
||||
echo_server_context_free(NULL);
|
||||
|
||||
rdpdr_server_context_new(NULL);
|
||||
rdpdr_server_context_free(NULL);
|
||||
|
||||
drdynvc_server_context_new(NULL);
|
||||
drdynvc_server_context_free(NULL);
|
||||
|
||||
rdpei_server_context_new(NULL);
|
||||
rdpei_server_context_free(NULL);
|
||||
|
||||
remdesk_server_context_new(NULL);
|
||||
remdesk_server_context_free(NULL);
|
||||
|
||||
encomsp_server_context_new(NULL);
|
||||
encomsp_server_context_free(NULL);
|
||||
|
||||
rdpgfx_server_context_new(NULL);
|
||||
rdpgfx_server_context_free(NULL);
|
||||
audin_server_context* audin;
|
||||
RdpsndServerContext* rdpsnd;
|
||||
CliprdrServerContext* cliprdr;
|
||||
echo_server_context* echo;
|
||||
RdpdrServerContext* rdpdr;
|
||||
DrdynvcServerContext* drdynvc;
|
||||
RdpeiServerContext* rdpei;
|
||||
RemdeskServerContext* remdesk;
|
||||
EncomspServerContext* encomsp;
|
||||
RdpgfxServerContext* rdpgfx;
|
||||
audin = audin_server_context_new(NULL);
|
||||
audin_server_context_free(audin);
|
||||
rdpsnd = rdpsnd_server_context_new(NULL);
|
||||
rdpsnd_server_context_free(rdpsnd);
|
||||
cliprdr = cliprdr_server_context_new(NULL);
|
||||
cliprdr_server_context_free(cliprdr);
|
||||
echo = echo_server_context_new(NULL);
|
||||
echo_server_context_free(echo);
|
||||
rdpdr = rdpdr_server_context_new(NULL);
|
||||
rdpdr_server_context_free(rdpdr);
|
||||
drdynvc = drdynvc_server_context_new(NULL);
|
||||
drdynvc_server_context_free(drdynvc);
|
||||
rdpei = rdpei_server_context_new(NULL);
|
||||
rdpei_server_context_free(rdpei);
|
||||
remdesk = remdesk_server_context_new(NULL);
|
||||
remdesk_server_context_free(remdesk);
|
||||
encomsp = encomsp_server_context_new(NULL);
|
||||
encomsp_server_context_free(encomsp);
|
||||
rdpgfx = rdpgfx_server_context_new(NULL);
|
||||
rdpgfx_server_context_free(rdpgfx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -34,6 +34,29 @@
|
||||
|
||||
#include "smartcard_main.h"
|
||||
|
||||
#define CAST_FROM_DEVICE(device) cast_device_from(device, __FUNCTION__, __FILE__, __LINE__)
|
||||
|
||||
static SMARTCARD_DEVICE* sSmartcard = NULL;
|
||||
|
||||
static SMARTCARD_DEVICE* cast_device_from(DEVICE* device, const char* fkt, const char* file,
|
||||
int line)
|
||||
{
|
||||
if (!device)
|
||||
{
|
||||
WLog_ERR(TAG, "%s [%s:%d] Called smartcard channel with NULL device", fkt, file, line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (device->type != RDPDR_DTYP_SMARTCARD)
|
||||
{
|
||||
WLog_ERR(TAG, "%s [%s:%d] Called smartcard channel with invalid device of type %"PRIx32,
|
||||
fkt, file, line, device->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (SMARTCARD_DEVICE*)device;
|
||||
}
|
||||
|
||||
static DWORD WINAPI smartcard_context_thread(LPVOID arg)
|
||||
{
|
||||
SMARTCARD_CONTEXT* pContext = (SMARTCARD_CONTEXT*)arg;
|
||||
@ -135,7 +158,7 @@ SMARTCARD_CONTEXT* smartcard_context_new(SMARTCARD_DEVICE* smartcard,
|
||||
}
|
||||
|
||||
pContext->thread = CreateThread(NULL, 0,
|
||||
smartcard_context_thread,
|
||||
smartcard_context_thread,
|
||||
pContext, 0, NULL);
|
||||
|
||||
if (!pContext->thread)
|
||||
@ -152,8 +175,10 @@ error_irpqueue:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void smartcard_context_free(SMARTCARD_CONTEXT* pContext)
|
||||
void smartcard_context_free(void* pCtx)
|
||||
{
|
||||
SMARTCARD_CONTEXT* pContext = pCtx;
|
||||
|
||||
if (!pContext)
|
||||
return;
|
||||
|
||||
@ -169,7 +194,6 @@ void smartcard_context_free(SMARTCARD_CONTEXT* pContext)
|
||||
free(pContext);
|
||||
}
|
||||
|
||||
|
||||
static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
|
||||
{
|
||||
int index;
|
||||
@ -249,7 +273,29 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
|
||||
}
|
||||
}
|
||||
|
||||
static UINT smartcard_free_(SMARTCARD_DEVICE* smartcard)
|
||||
{
|
||||
if (!smartcard)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (smartcard->IrpQueue)
|
||||
{
|
||||
MessageQueue_Free(smartcard->IrpQueue);
|
||||
CloseHandle(smartcard->thread);
|
||||
}
|
||||
|
||||
Stream_Free(smartcard->device.data, TRUE);
|
||||
LinkedList_Free(smartcard->names);
|
||||
ListDictionary_Free(smartcard->rgSCardContextList);
|
||||
ListDictionary_Free(smartcard->rgOutstandingMessages);
|
||||
Queue_Free(smartcard->CompletedIrpQueue);
|
||||
|
||||
if (smartcard->StartedEvent)
|
||||
SCardReleaseStartedEvent();
|
||||
|
||||
free(smartcard);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -258,7 +304,11 @@ static void smartcard_release_all_contexts(SMARTCARD_DEVICE* smartcard)
|
||||
static UINT smartcard_free(DEVICE* device)
|
||||
{
|
||||
UINT error;
|
||||
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
|
||||
SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
|
||||
|
||||
if (!smartcard)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
/**
|
||||
* Calling smartcard_release_all_contexts to unblock all operations waiting for transactions
|
||||
* to unlock.
|
||||
@ -276,31 +326,12 @@ static UINT smartcard_free(DEVICE* device)
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
MessageQueue_Free(smartcard->IrpQueue);
|
||||
smartcard->IrpQueue = NULL;
|
||||
CloseHandle(smartcard->thread);
|
||||
smartcard->thread = NULL;
|
||||
}
|
||||
|
||||
if (smartcard->device.data)
|
||||
{
|
||||
Stream_Free(smartcard->device.data, TRUE);
|
||||
smartcard->device.data = NULL;
|
||||
}
|
||||
if (sSmartcard == smartcard)
|
||||
sSmartcard = NULL;
|
||||
|
||||
ListDictionary_Free(smartcard->rgSCardContextList);
|
||||
ListDictionary_Free(smartcard->rgOutstandingMessages);
|
||||
Queue_Free(smartcard->CompletedIrpQueue);
|
||||
|
||||
if (smartcard->StartedEvent)
|
||||
{
|
||||
SCardReleaseStartedEvent();
|
||||
smartcard->StartedEvent = NULL;
|
||||
}
|
||||
|
||||
free(device);
|
||||
return CHANNEL_RC_OK;
|
||||
return smartcard_free_(smartcard);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +346,11 @@ static UINT smartcard_free(DEVICE* device)
|
||||
*/
|
||||
static UINT smartcard_init(DEVICE* device)
|
||||
{
|
||||
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
|
||||
SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
|
||||
|
||||
if (!smartcard)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
smartcard_release_all_contexts(smartcard);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -325,7 +360,7 @@ static UINT smartcard_init(DEVICE* device)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
static UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
{
|
||||
void* key;
|
||||
key = (void*)(size_t) irp->CompletionId;
|
||||
@ -343,7 +378,7 @@ UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
static UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
{
|
||||
void* key;
|
||||
LONG status;
|
||||
@ -377,10 +412,12 @@ UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
|
||||
if (!Queue_Enqueue(smartcard->CompletedIrpQueue, (void*) irp))
|
||||
{
|
||||
free(operation);
|
||||
WLog_ERR(TAG, "Queue_Enqueue failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
free(operation);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -460,6 +497,7 @@ UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp)
|
||||
|
||||
if (!Queue_Enqueue(smartcard->CompletedIrpQueue, (void*) irp))
|
||||
{
|
||||
free(operation);
|
||||
WLog_ERR(TAG, "Queue_Enqueue failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -502,8 +540,12 @@ static DWORD WINAPI smartcard_thread_func(LPVOID arg)
|
||||
DWORD status;
|
||||
HANDLE hEvents[2];
|
||||
wMessage message;
|
||||
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(arg);
|
||||
|
||||
if (!smartcard)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
nCount = 0;
|
||||
hEvents[nCount++] = MessageQueue_Event(smartcard->IrpQueue);
|
||||
hEvents[nCount++] = Queue_Event(smartcard->CompletedIrpQueue);
|
||||
@ -657,7 +699,10 @@ out:
|
||||
*/
|
||||
static UINT smartcard_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
|
||||
SMARTCARD_DEVICE* smartcard = CAST_FROM_DEVICE(device);
|
||||
|
||||
if (!smartcard)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!MessageQueue_Post(smartcard->IrpQueue, NULL, 0, (void*) irp, NULL))
|
||||
{
|
||||
@ -678,99 +723,101 @@ static UINT smartcard_irp_request(DEVICE* device, IRP* irp)
|
||||
*/
|
||||
UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
SMARTCARD_DEVICE* smartcard = NULL;
|
||||
size_t length;
|
||||
SMARTCARD_DEVICE* smartcard;
|
||||
UINT error = CHANNEL_RC_NO_MEMORY;
|
||||
smartcard = (SMARTCARD_DEVICE*) calloc(1, sizeof(SMARTCARD_DEVICE));
|
||||
|
||||
if (!smartcard)
|
||||
if (!sSmartcard)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
wObject* obj;
|
||||
smartcard = (SMARTCARD_DEVICE*) calloc(1, sizeof(SMARTCARD_DEVICE));
|
||||
|
||||
if (!smartcard)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
smartcard->device.type = RDPDR_DTYP_SMARTCARD;
|
||||
smartcard->device.name = "SCARD";
|
||||
smartcard->device.IRPRequest = smartcard_irp_request;
|
||||
smartcard->device.Init = smartcard_init;
|
||||
smartcard->device.Free = smartcard_free;
|
||||
smartcard->names = LinkedList_New();
|
||||
smartcard->rdpcontext = pEntryPoints->rdpcontext;
|
||||
length = strlen(smartcard->device.name);
|
||||
smartcard->device.data = Stream_New(NULL, length + 1);
|
||||
|
||||
if (!smartcard->device.data || !smartcard->names)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
Stream_Write(smartcard->device.data, "SCARD", 6);
|
||||
smartcard->IrpQueue = MessageQueue_New(NULL);
|
||||
|
||||
if (!smartcard->IrpQueue)
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
smartcard->CompletedIrpQueue = Queue_New(TRUE, -1, -1);
|
||||
|
||||
if (!smartcard->CompletedIrpQueue)
|
||||
{
|
||||
WLog_ERR(TAG, "Queue_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
smartcard->rgSCardContextList = ListDictionary_New(TRUE);
|
||||
|
||||
if (!smartcard->rgSCardContextList)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
obj = ListDictionary_ValueObject(smartcard->rgSCardContextList);
|
||||
obj->fnObjectFree = smartcard_context_free;
|
||||
smartcard->rgOutstandingMessages = ListDictionary_New(TRUE);
|
||||
|
||||
if (!smartcard->rgOutstandingMessages)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &smartcard->device)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
smartcard->thread = CreateThread(NULL, 0,
|
||||
smartcard_thread_func,
|
||||
smartcard, CREATE_SUSPENDED, NULL);
|
||||
|
||||
if (!smartcard->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ResumeThread(smartcard->thread);
|
||||
}
|
||||
else
|
||||
smartcard = sSmartcard;
|
||||
|
||||
smartcard->device.type = RDPDR_DTYP_SMARTCARD;
|
||||
smartcard->device.name = "SCARD";
|
||||
smartcard->device.IRPRequest = smartcard_irp_request;
|
||||
smartcard->device.Init = smartcard_init;
|
||||
smartcard->device.Free = smartcard_free;
|
||||
smartcard->rdpcontext = pEntryPoints->rdpcontext;
|
||||
length = strlen(smartcard->device.name);
|
||||
smartcard->device.data = Stream_New(NULL, length + 1);
|
||||
if (pEntryPoints->device->Name)
|
||||
LinkedList_AddLast(smartcard->names, pEntryPoints->device->Name);
|
||||
|
||||
if (!smartcard->device.data)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
goto error_device_data;
|
||||
}
|
||||
|
||||
Stream_Write(smartcard->device.data, "SCARD", 6);
|
||||
smartcard->IrpQueue = MessageQueue_New(NULL);
|
||||
|
||||
if (!smartcard->IrpQueue)
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_New failed!");
|
||||
goto error_irp_queue;
|
||||
}
|
||||
|
||||
smartcard->CompletedIrpQueue = Queue_New(TRUE, -1, -1);
|
||||
|
||||
if (!smartcard->CompletedIrpQueue)
|
||||
{
|
||||
WLog_ERR(TAG, "Queue_New failed!");
|
||||
goto error_completed_irp_queue;
|
||||
}
|
||||
|
||||
smartcard->rgSCardContextList = ListDictionary_New(TRUE);
|
||||
|
||||
if (!smartcard->rgSCardContextList)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
goto error_context_list;
|
||||
}
|
||||
|
||||
ListDictionary_ValueObject(smartcard->rgSCardContextList)->fnObjectFree =
|
||||
(OBJECT_FREE_FN) smartcard_context_free;
|
||||
smartcard->rgOutstandingMessages = ListDictionary_New(TRUE);
|
||||
|
||||
if (!smartcard->rgOutstandingMessages)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
goto error_outstanding_messages;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman,
|
||||
(DEVICE*) smartcard)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed!");
|
||||
goto error_outstanding_messages;
|
||||
}
|
||||
|
||||
smartcard->thread = CreateThread(NULL, 0,
|
||||
smartcard_thread_func,
|
||||
smartcard, CREATE_SUSPENDED, NULL);
|
||||
|
||||
if (!smartcard->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_New failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_thread;
|
||||
}
|
||||
|
||||
ResumeThread(smartcard->thread);
|
||||
sSmartcard = smartcard;
|
||||
return CHANNEL_RC_OK;
|
||||
error_thread:
|
||||
ListDictionary_Free(smartcard->rgOutstandingMessages);
|
||||
error_outstanding_messages:
|
||||
ListDictionary_Free(smartcard->rgSCardContextList);
|
||||
error_context_list:
|
||||
Queue_Free(smartcard->CompletedIrpQueue);
|
||||
error_completed_irp_queue:
|
||||
MessageQueue_Free(smartcard->IrpQueue);
|
||||
error_irp_queue:
|
||||
Stream_Free(smartcard->device.data, TRUE);
|
||||
error_device_data:
|
||||
free(smartcard);
|
||||
fail:
|
||||
smartcard_free_(smartcard);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -117,15 +117,14 @@ struct _SMARTCARD_DEVICE
|
||||
wListDictionary* rgSCardContextList;
|
||||
wListDictionary* rgOutstandingMessages;
|
||||
rdpContext* rdpcontext;
|
||||
wLinkedList* names;
|
||||
};
|
||||
|
||||
SMARTCARD_CONTEXT* smartcard_context_new(SMARTCARD_DEVICE* smartcard, SCARDCONTEXT hContext);
|
||||
void smartcard_context_free(SMARTCARD_CONTEXT* pContext);
|
||||
void smartcard_context_free(void* pContext);
|
||||
|
||||
UINT smartcard_complete_irp(SMARTCARD_DEVICE* smartcard, IRP* irp);
|
||||
UINT smartcard_process_irp(SMARTCARD_DEVICE* smartcard, IRP* irp);
|
||||
|
||||
LONG smartcard_irp_device_control_decode(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation);
|
||||
LONG smartcard_irp_device_control_decode(SMARTCARD_DEVICE* smartcard,
|
||||
SMARTCARD_OPERATION* operation);
|
||||
LONG smartcard_irp_device_control_call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERATION* operation);
|
||||
|
||||
#include "smartcard_pack.h"
|
||||
|
||||
@ -432,6 +432,86 @@ static LONG smartcard_ListReaderGroupsW_Call(SMARTCARD_DEVICE* smartcard,
|
||||
return ret.ReturnCode;
|
||||
}
|
||||
|
||||
static BOOL filter_match(wLinkedList* list, LPCSTR reader, size_t readerLen)
|
||||
{
|
||||
if (readerLen < 1)
|
||||
return FALSE;
|
||||
|
||||
LinkedList_Enumerator_Reset(list);
|
||||
|
||||
while (LinkedList_Enumerator_MoveNext(list))
|
||||
{
|
||||
const char* filter = LinkedList_Enumerator_Current(list);
|
||||
|
||||
if (filter)
|
||||
{
|
||||
if (strstr(reader, filter) != NULL)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DWORD filter_device_by_name_a(wLinkedList* list, LPSTR* mszReaders, DWORD cchReaders)
|
||||
{
|
||||
size_t rpos = 0, wpos = 0;
|
||||
|
||||
if (LinkedList_Count(list) < 1)
|
||||
return cchReaders;
|
||||
|
||||
do
|
||||
{
|
||||
LPCSTR rreader = &(*mszReaders)[rpos];
|
||||
LPSTR wreader = &(*mszReaders)[wpos];
|
||||
size_t readerLen = strnlen(rreader, cchReaders - rpos);
|
||||
rpos += readerLen + 1;
|
||||
|
||||
if (filter_match(list, rreader, readerLen))
|
||||
{
|
||||
if (rreader != wreader)
|
||||
memmove(wreader, rreader, readerLen);
|
||||
|
||||
wpos += readerLen + 1;
|
||||
}
|
||||
}
|
||||
while (rpos < cchReaders);
|
||||
|
||||
/* this string must be double 0 terminated */
|
||||
if (rpos != wpos)
|
||||
{
|
||||
if (wpos >= cchReaders)
|
||||
return 0;
|
||||
|
||||
(*mszReaders)[wpos++] = '\0';
|
||||
}
|
||||
|
||||
return wpos;
|
||||
}
|
||||
|
||||
static DWORD filter_device_by_name_w(wLinkedList* list, LPWSTR* mszReaders, DWORD cchReaders)
|
||||
{
|
||||
DWORD rc;
|
||||
LPSTR readers;
|
||||
|
||||
if (LinkedList_Count(list) < 1)
|
||||
return cchReaders;
|
||||
|
||||
if (ConvertFromUnicode(CP_UTF8, 0, *mszReaders, (int)cchReaders, &readers, 0, NULL,
|
||||
NULL) != cchReaders)
|
||||
return 0;
|
||||
|
||||
free(*mszReaders);
|
||||
*mszReaders = NULL;
|
||||
rc = filter_device_by_name_a(list, &readers, cchReaders);
|
||||
|
||||
if (ConvertToUnicode(CP_UTF8, 0, readers, (int)rc, mszReaders, 0) != rc)
|
||||
rc = 0;
|
||||
|
||||
free(readers);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static LONG smartcard_ListReadersA_Decode(SMARTCARD_DEVICE* smartcard,
|
||||
SMARTCARD_OPERATION* operation)
|
||||
{
|
||||
@ -462,6 +542,7 @@ static LONG smartcard_ListReadersA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O
|
||||
cchReaders = SCARD_AUTOALLOCATE;
|
||||
status = ret.ReturnCode = SCardListReadersA(operation->hContext, (LPCSTR) call->mszGroups,
|
||||
(LPSTR) &mszReaders, &cchReaders);
|
||||
cchReaders = filter_device_by_name_a(smartcard->names, &mszReaders, cchReaders);
|
||||
ret.msz = (BYTE*) mszReaders;
|
||||
ret.cBytes = cchReaders;
|
||||
|
||||
@ -524,6 +605,7 @@ static LONG smartcard_ListReadersW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_O
|
||||
cchReaders = SCARD_AUTOALLOCATE;
|
||||
status = ret.ReturnCode = SCardListReadersW(operation->hContext,
|
||||
(LPCWSTR) call->mszGroups, (LPWSTR) &mszReaders, &cchReaders);
|
||||
cchReaders = filter_device_by_name_w(smartcard->names, &mszReaders, cchReaders);
|
||||
ret.msz = (BYTE*) mszReaders;
|
||||
ret.cBytes = cchReaders * 2;
|
||||
|
||||
@ -1053,7 +1135,6 @@ static LONG smartcard_StatusA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT
|
||||
IRP* irp = operation->irp;
|
||||
Status_Call* call = operation->call;
|
||||
ZeroMemory(ret.pbAtr, 32);
|
||||
|
||||
call->cbAtrLen = 32;
|
||||
cbAtrLen = call->cbAtrLen;
|
||||
|
||||
@ -1142,7 +1223,13 @@ static LONG smartcard_StatusW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD_OPERAT
|
||||
if (!call->fmszReaderNamesIsNULL)
|
||||
ret.mszReaderNames = (BYTE*) mszReaderNames;
|
||||
|
||||
// WinScard returns the number of CHARACTERS whereas pcsc-lite returns the
|
||||
// number of BYTES.
|
||||
#ifdef _WIN32
|
||||
ret.cBytes = cchReaderLen * 2;
|
||||
#else
|
||||
ret.cBytes = cchReaderLen;
|
||||
#endif
|
||||
|
||||
if (call->cbAtrLen)
|
||||
ret.cbAtrLen = cbAtrLen;
|
||||
|
||||
@ -110,8 +110,8 @@
|
||||
|
||||
#define _PATH_DEVNULL "/dev/null"
|
||||
|
||||
char socket_name[PATH_MAX];
|
||||
char socket_dir[PATH_MAX];
|
||||
static char socket_name[PATH_MAX];
|
||||
static char socket_dir[PATH_MAX];
|
||||
static int sa_uds_fd = -1;
|
||||
static int is_going = 1;
|
||||
|
||||
@ -156,7 +156,7 @@ setup_ssh_agent(struct sockaddr_un* addr)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
|
||||
snprintf(socket_name, sizeof(socket_name), "%s/agent.%ld", socket_dir,
|
||||
(long)getpid());
|
||||
/* Create unix domain socket */
|
||||
unlink(socket_name);
|
||||
|
||||
@ -40,33 +40,34 @@ typedef struct _TSMFALSAAudioDevice
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
snd_pcm_t *out_handle;
|
||||
snd_pcm_t* out_handle;
|
||||
UINT32 source_rate;
|
||||
UINT32 actual_rate;
|
||||
UINT32 source_channels;
|
||||
UINT32 actual_channels;
|
||||
UINT32 bytes_per_sample;
|
||||
|
||||
FREERDP_DSP_CONTEXT *dsp_context;
|
||||
} TSMFAlsaAudioDevice;
|
||||
|
||||
static BOOL tsmf_alsa_open_device(TSMFAlsaAudioDevice *alsa)
|
||||
static BOOL tsmf_alsa_open_device(TSMFAlsaAudioDevice* alsa)
|
||||
{
|
||||
int error;
|
||||
error = snd_pcm_open(&alsa->out_handle, alsa->device, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if(error < 0)
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to open device %s", alsa->device);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("open device %s", alsa->device);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_open(ITSMFAudioDevice *audio, const char *device)
|
||||
static BOOL tsmf_alsa_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
|
||||
if(!device)
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
|
||||
|
||||
if (!device)
|
||||
{
|
||||
strncpy(alsa->device, "default", sizeof(alsa->device));
|
||||
}
|
||||
@ -74,114 +75,109 @@ static BOOL tsmf_alsa_open(ITSMFAudioDevice *audio, const char *device)
|
||||
{
|
||||
strncpy(alsa->device, device, sizeof(alsa->device) - 1);
|
||||
}
|
||||
|
||||
return tsmf_alsa_open_device(alsa);
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_set_format(ITSMFAudioDevice *audio,
|
||||
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
|
||||
static BOOL tsmf_alsa_set_format(ITSMFAudioDevice* audio,
|
||||
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
|
||||
{
|
||||
int error;
|
||||
snd_pcm_uframes_t frames;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
|
||||
if(!alsa->out_handle)
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
snd_pcm_sw_params_t* sw_params;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
|
||||
|
||||
if (!alsa->out_handle)
|
||||
return FALSE;
|
||||
|
||||
snd_pcm_drop(alsa->out_handle);
|
||||
alsa->actual_rate = alsa->source_rate = sample_rate;
|
||||
alsa->actual_channels = alsa->source_channels = channels;
|
||||
alsa->bytes_per_sample = bits_per_sample / 8;
|
||||
error = snd_pcm_hw_params_malloc(&hw_params);
|
||||
if(error < 0)
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_hw_params_malloc failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_any(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_set_access(alsa->out_handle, hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
snd_pcm_hw_params_set_format(alsa->out_handle, hw_params,
|
||||
SND_PCM_FORMAT_S16_LE);
|
||||
SND_PCM_FORMAT_S16_LE);
|
||||
snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params,
|
||||
&alsa->actual_rate, NULL);
|
||||
&alsa->actual_rate, NULL);
|
||||
snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params,
|
||||
&alsa->actual_channels);
|
||||
&alsa->actual_channels);
|
||||
frames = sample_rate;
|
||||
snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params,
|
||||
&frames);
|
||||
&frames);
|
||||
snd_pcm_hw_params(alsa->out_handle, hw_params);
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
error = snd_pcm_sw_params_malloc(&sw_params);
|
||||
if(error < 0)
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "snd_pcm_sw_params_malloc");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_current(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params,
|
||||
frames / 2);
|
||||
frames / 2);
|
||||
snd_pcm_sw_params(alsa->out_handle, sw_params);
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
snd_pcm_prepare(alsa->out_handle);
|
||||
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
sample_rate, channels, bits_per_sample);
|
||||
DEBUG_TSMF("hardware buffer %lu frames", frames);
|
||||
if((alsa->actual_rate != alsa->source_rate) ||
|
||||
(alsa->actual_channels != alsa->source_channels))
|
||||
|
||||
if ((alsa->actual_rate != alsa->source_rate) ||
|
||||
(alsa->actual_channels != alsa->source_channels))
|
||||
{
|
||||
DEBUG_TSMF("actual rate %"PRIu32" / channel %"PRIu32" is different "
|
||||
"from source rate %"PRIu32" / channel %"PRIu32", resampling required.",
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->source_rate, alsa->source_channels);
|
||||
alsa->actual_rate, alsa->actual_channels,
|
||||
alsa->source_rate, alsa->source_channels);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size)
|
||||
static BOOL tsmf_alsa_play(ITSMFAudioDevice* audio, const BYTE* src, UINT32 data_size)
|
||||
{
|
||||
int len;
|
||||
int error;
|
||||
int frames;
|
||||
BYTE *end;
|
||||
BYTE *src;
|
||||
BYTE *pindex;
|
||||
const BYTE* end;
|
||||
const BYTE* pindex;
|
||||
int rbytes_per_frame;
|
||||
int sbytes_per_frame;
|
||||
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
|
||||
DEBUG_TSMF("data_size %"PRIu32"", data_size);
|
||||
if(alsa->out_handle)
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
sbytes_per_frame = alsa->source_channels * alsa->bytes_per_sample;
|
||||
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_sample;
|
||||
if((alsa->source_rate == alsa->actual_rate) &&
|
||||
(alsa->source_channels == alsa->actual_channels))
|
||||
{
|
||||
src = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
alsa->dsp_context->resample(alsa->dsp_context, data, alsa->bytes_per_sample,
|
||||
alsa->source_channels, alsa->source_rate, data_size / sbytes_per_frame,
|
||||
alsa->actual_channels, alsa->actual_rate);
|
||||
frames = alsa->dsp_context->resampled_frames;
|
||||
DEBUG_TSMF("resampled %"PRIu32" frames at %"PRIu32" to %d frames at %"PRIu32"",
|
||||
data_size / sbytes_per_frame, alsa->source_rate, frames, alsa->actual_rate);
|
||||
data_size = frames * rbytes_per_frame;
|
||||
src = alsa->dsp_context->resampled_buffer;
|
||||
}
|
||||
pindex = src;
|
||||
end = pindex + data_size;
|
||||
while(pindex < end)
|
||||
|
||||
while (pindex < end)
|
||||
{
|
||||
len = end - pindex;
|
||||
frames = len / rbytes_per_frame;
|
||||
error = snd_pcm_writei(alsa->out_handle, pindex, frames);
|
||||
if(error == -EPIPE)
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(alsa->out_handle, error, 0);
|
||||
error = 0;
|
||||
}
|
||||
else if(error < 0)
|
||||
else if (error < 0)
|
||||
{
|
||||
DEBUG_TSMF("error len %d", error);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
@ -189,45 +185,51 @@ static BOOL tsmf_alsa_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size
|
||||
tsmf_alsa_open_device(alsa);
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("%d frames played.", error);
|
||||
if(error == 0)
|
||||
|
||||
if (error == 0)
|
||||
break;
|
||||
|
||||
pindex += error * rbytes_per_frame;
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 tsmf_alsa_get_latency(ITSMFAudioDevice *audio)
|
||||
static UINT64 tsmf_alsa_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
UINT64 latency = 0;
|
||||
snd_pcm_sframes_t frames = 0;
|
||||
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
|
||||
if(alsa->out_handle && alsa->actual_rate > 0 &&
|
||||
snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
|
||||
frames > 0)
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
|
||||
|
||||
if (alsa->out_handle && alsa->actual_rate > 0 &&
|
||||
snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
|
||||
frames > 0)
|
||||
{
|
||||
latency = ((UINT64)frames) * 10000000LL / (UINT64) alsa->actual_rate;
|
||||
}
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
static BOOL tsmf_alsa_flush(ITSMFAudioDevice *audio)
|
||||
static BOOL tsmf_alsa_flush(ITSMFAudioDevice* audio)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_alsa_free(ITSMFAudioDevice *audio)
|
||||
static void tsmf_alsa_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
|
||||
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
|
||||
DEBUG_TSMF("");
|
||||
if(alsa->out_handle)
|
||||
|
||||
if (alsa->out_handle)
|
||||
{
|
||||
snd_pcm_drain(alsa->out_handle);
|
||||
snd_pcm_close(alsa->out_handle);
|
||||
}
|
||||
freerdp_dsp_context_free(alsa->dsp_context);
|
||||
|
||||
free(alsa);
|
||||
}
|
||||
|
||||
@ -237,10 +239,10 @@ static void tsmf_alsa_free(ITSMFAudioDevice *audio)
|
||||
#define freerdp_tsmf_client_audio_subsystem_entry FREERDP_API freerdp_tsmf_client_audio_subsystem_entry
|
||||
#endif
|
||||
|
||||
ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
ITSMFAudioDevice* freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
{
|
||||
TSMFAlsaAudioDevice *alsa;
|
||||
alsa = (TSMFAlsaAudioDevice *) malloc(sizeof(TSMFAlsaAudioDevice));
|
||||
TSMFAlsaAudioDevice* alsa;
|
||||
alsa = (TSMFAlsaAudioDevice*) malloc(sizeof(TSMFAlsaAudioDevice));
|
||||
ZeroMemory(alsa, sizeof(TSMFAlsaAudioDevice));
|
||||
alsa->iface.Open = tsmf_alsa_open;
|
||||
alsa->iface.SetFormat = tsmf_alsa_set_format;
|
||||
@ -248,6 +250,5 @@ ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
alsa->iface.GetLatency = tsmf_alsa_get_latency;
|
||||
alsa->iface.Flush = tsmf_alsa_flush;
|
||||
alsa->iface.Free = tsmf_alsa_free;
|
||||
alsa->dsp_context = freerdp_dsp_context_new();
|
||||
return (ITSMFAudioDevice *) alsa;
|
||||
return (ITSMFAudioDevice*) alsa;
|
||||
}
|
||||
|
||||
@ -35,9 +35,9 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__OpenBSD__)
|
||||
#include <soundcard.h>
|
||||
#include <soundcard.h>
|
||||
#else
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
@ -129,7 +129,8 @@ static BOOL tsmf_oss_open(ITSMFAudioDevice* audio, const char* device)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
|
||||
static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
|
||||
UINT32 bits_per_sample)
|
||||
{
|
||||
int tmp;
|
||||
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
|
||||
@ -161,11 +162,11 @@ static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UIN
|
||||
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
|
||||
|
||||
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
sample_rate, channels, bits_per_sample);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
|
||||
static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
|
||||
{
|
||||
int status;
|
||||
UINT32 offset;
|
||||
@ -176,10 +177,7 @@ static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
|
||||
return FALSE;
|
||||
|
||||
if (data == NULL || data_size == 0)
|
||||
{
|
||||
free(data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
oss->data_size_last = data_size;
|
||||
@ -191,14 +189,12 @@ static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
|
||||
if (status < 0)
|
||||
{
|
||||
OSS_LOG_ERR("write fail", errno);
|
||||
free(data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
offset += status;
|
||||
}
|
||||
|
||||
free(data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@ -37,68 +37,81 @@ typedef struct _TSMFPulseAudioDevice
|
||||
ITSMFAudioDevice iface;
|
||||
|
||||
char device[32];
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_threaded_mainloop* mainloop;
|
||||
pa_context* context;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_stream *stream;
|
||||
pa_stream* stream;
|
||||
} TSMFPulseAudioDevice;
|
||||
|
||||
static void tsmf_pulse_context_state_callback(pa_context *context, void *userdata)
|
||||
static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
pa_context_state_t state;
|
||||
state = pa_context_get_state(context);
|
||||
switch(state)
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
DEBUG_TSMF("PA_CONTEXT_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice *pulse)
|
||||
static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_context_state_t state;
|
||||
if(!pulse->context)
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
if(pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_connect failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
if(pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
for(;;)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_context_get_state(pulse->context);
|
||||
if(state == PA_CONTEXT_READY)
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
if(!PA_CONTEXT_IS_GOOD(state))
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
DEBUG_TSMF("bad context state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if(state == PA_CONTEXT_READY)
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
{
|
||||
DEBUG_TSMF("connected");
|
||||
return TRUE;
|
||||
@ -110,90 +123,104 @@ static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice *pulse)
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_open(ITSMFAudioDevice *audio, const char *device)
|
||||
static BOOL tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
if(device)
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
if (device)
|
||||
{
|
||||
strncpy(pulse->device, device, sizeof(pulse->device) - 1);
|
||||
}
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
if(!pulse->mainloop)
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||
if(!pulse->context)
|
||||
|
||||
if (!pulse->context)
|
||||
{
|
||||
WLog_ERR(TAG, "pa_context_new failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
|
||||
if(!tsmf_pulse_connect(pulse))
|
||||
|
||||
if (!tsmf_pulse_connect(pulse))
|
||||
{
|
||||
WLog_ERR(TAG, "tsmf_pulse_connect failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DEBUG_TSMF("open device %s", pulse->device);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_success_callback(pa_stream *stream, int success, void *userdata)
|
||||
static void tsmf_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice *pulse, pa_operation *operation)
|
||||
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
|
||||
{
|
||||
if(operation == NULL)
|
||||
if (operation == NULL)
|
||||
return;
|
||||
while(pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
|
||||
|
||||
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_operation_unref(operation);
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_state_callback(pa_stream *stream, void *userdata)
|
||||
static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
pa_stream_state_t state;
|
||||
state = pa_stream_get_state(stream);
|
||||
switch(state)
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
DEBUG_TSMF("PA_STREAM_READY");
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_TSMF("state %d", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_pulse_stream_request_callback(pa_stream *stream, size_t length, void *userdata)
|
||||
static void tsmf_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
|
||||
DEBUG_TSMF("%"PRIdz"", length);
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice *pulse)
|
||||
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
if(!pulse->context || !pulse->stream)
|
||||
if (!pulse->context || !pulse->stream)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pa_stream_set_write_callback(pulse->stream, NULL, NULL);
|
||||
tsmf_pulse_wait_for_operation(pulse,
|
||||
pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
@ -201,57 +228,68 @@ static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice *pulse)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice *pulse)
|
||||
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
pa_buffer_attr buffer_attr = { 0 };
|
||||
if(!pulse->context)
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
pulse->stream = pa_stream_new(pulse->context, "freerdp",
|
||||
&pulse->sample_spec, NULL);
|
||||
if(!pulse->stream)
|
||||
&pulse->sample_spec, NULL);
|
||||
|
||||
if (!pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_new failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_stream_set_state_callback(pulse->stream,
|
||||
tsmf_pulse_stream_state_callback, pulse);
|
||||
tsmf_pulse_stream_state_callback, pulse);
|
||||
pa_stream_set_write_callback(pulse->stream,
|
||||
tsmf_pulse_stream_request_callback, pulse);
|
||||
tsmf_pulse_stream_request_callback, pulse);
|
||||
buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
|
||||
buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
|
||||
buffer_attr.prebuf = (UINT32) -1;
|
||||
buffer_attr.minreq = (UINT32) -1;
|
||||
buffer_attr.fragsize = (UINT32) -1;
|
||||
if(pa_stream_connect_playback(pulse->stream,
|
||||
pulse->device[0] ? pulse->device : NULL, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
|
||||
NULL, NULL) < 0)
|
||||
buffer_attr.prebuf = (UINT32) - 1;
|
||||
buffer_attr.minreq = (UINT32) - 1;
|
||||
buffer_attr.fragsize = (UINT32) - 1;
|
||||
|
||||
if (pa_stream_connect_playback(pulse->stream,
|
||||
pulse->device[0] ? pulse->device : NULL, &buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
|
||||
NULL, NULL) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
return FALSE;
|
||||
}
|
||||
for(;;)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
state = pa_stream_get_state(pulse->stream);
|
||||
if(state == PA_STREAM_READY)
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
if(!PA_STREAM_IS_GOOD(state))
|
||||
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
WLog_ERR(TAG, "bad stream state (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if(state == PA_STREAM_READY)
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
{
|
||||
DEBUG_TSMF("connected");
|
||||
return TRUE;
|
||||
@ -263,118 +301,133 @@ static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice *pulse)
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_set_format(ITSMFAudioDevice *audio,
|
||||
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
|
||||
static BOOL tsmf_pulse_set_format(ITSMFAudioDevice* audio,
|
||||
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
|
||||
sample_rate, channels, bits_per_sample);
|
||||
sample_rate, channels, bits_per_sample);
|
||||
pulse->sample_spec.rate = sample_rate;
|
||||
pulse->sample_spec.channels = channels;
|
||||
pulse->sample_spec.format = PA_SAMPLE_S16LE;
|
||||
return tsmf_pulse_open_stream(pulse);
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size)
|
||||
static BOOL tsmf_pulse_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
BYTE *src;
|
||||
int len;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
const BYTE* src;
|
||||
size_t len;
|
||||
int ret;
|
||||
DEBUG_TSMF("data_size %"PRIu32"", data_size);
|
||||
if(pulse->stream)
|
||||
|
||||
if (pulse->stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
src = data;
|
||||
while(data_size > 0)
|
||||
|
||||
while (data_size > 0)
|
||||
{
|
||||
while((len = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
while ((len = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
{
|
||||
DEBUG_TSMF("waiting");
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
if(len < 0)
|
||||
|
||||
if (len == (size_t) -1)
|
||||
break;
|
||||
if(len > data_size)
|
||||
|
||||
if (len > data_size)
|
||||
len = data_size;
|
||||
|
||||
ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
if(ret < 0)
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
DEBUG_TSMF("pa_stream_write failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
src += len;
|
||||
data_size -= len;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
free(data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT64 tsmf_pulse_get_latency(ITSMFAudioDevice *audio)
|
||||
static UINT64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
|
||||
{
|
||||
pa_usec_t usec;
|
||||
UINT64 latency = 0;
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
if(pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
|
||||
if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
|
||||
{
|
||||
latency = ((UINT64)usec) * 10LL;
|
||||
}
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
static BOOL tsmf_pulse_flush(ITSMFAudioDevice *audio)
|
||||
static BOOL tsmf_pulse_flush(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
tsmf_pulse_wait_for_operation(pulse,
|
||||
pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tsmf_pulse_free(ITSMFAudioDevice *audio)
|
||||
static void tsmf_pulse_free(ITSMFAudioDevice* audio)
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
|
||||
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
|
||||
DEBUG_TSMF("");
|
||||
tsmf_pulse_close_stream(pulse);
|
||||
if(pulse->mainloop)
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
}
|
||||
if(pulse->context)
|
||||
|
||||
if (pulse->context)
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
pa_context_unref(pulse->context);
|
||||
pulse->context = NULL;
|
||||
}
|
||||
if(pulse->mainloop)
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_free(pulse->mainloop);
|
||||
pulse->mainloop = NULL;
|
||||
}
|
||||
|
||||
free(pulse);
|
||||
}
|
||||
|
||||
#ifdef BUILTIN_CHANNELS
|
||||
ITSMFAudioDevice *pulse_freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
ITSMFAudioDevice* pulse_freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
#else
|
||||
FREERDP_API ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
FREERDP_API ITSMFAudioDevice* freerdp_tsmf_client_audio_subsystem_entry(void)
|
||||
#endif
|
||||
{
|
||||
TSMFPulseAudioDevice *pulse;
|
||||
TSMFPulseAudioDevice* pulse;
|
||||
pulse = (TSMFPulseAudioDevice*) calloc(1, sizeof(TSMFPulseAudioDevice));
|
||||
|
||||
pulse = (TSMFPulseAudioDevice *) calloc(1, sizeof(TSMFPulseAudioDevice));
|
||||
if (!pulse)
|
||||
return NULL;
|
||||
|
||||
pulse->iface.Open = tsmf_pulse_open;
|
||||
pulse->iface.SetFormat = tsmf_pulse_set_format;
|
||||
pulse->iface.Play = tsmf_pulse_play;
|
||||
pulse->iface.GetLatency = tsmf_pulse_get_latency;
|
||||
pulse->iface.Flush = tsmf_pulse_flush;
|
||||
pulse->iface.Free = tsmf_pulse_free;
|
||||
return (ITSMFAudioDevice *) pulse;
|
||||
return (ITSMFAudioDevice*) pulse;
|
||||
}
|
||||
|
||||
|
||||
@ -27,23 +27,24 @@ typedef struct _ITSMFAudioDevice ITSMFAudioDevice;
|
||||
struct _ITSMFAudioDevice
|
||||
{
|
||||
/* Open the audio device. */
|
||||
BOOL (*Open) (ITSMFAudioDevice* audio, const char* device);
|
||||
BOOL (*Open)(ITSMFAudioDevice* audio, const char* device);
|
||||
/* Set the audio data format. */
|
||||
BOOL (*SetFormat) (ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample);
|
||||
BOOL (*SetFormat)(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
|
||||
UINT32 bits_per_sample);
|
||||
/* Play audio data. */
|
||||
BOOL (*Play) (ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size);
|
||||
BOOL (*Play)(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size);
|
||||
/* Get the latency of the last written sample, in 100ns */
|
||||
UINT64 (*GetLatency) (ITSMFAudioDevice* audio);
|
||||
UINT64(*GetLatency)(ITSMFAudioDevice* audio);
|
||||
/* Change the playback volume level */
|
||||
BOOL (*ChangeVolume) (ITSMFAudioDevice* audio, UINT32 newVolume, UINT32 muted);
|
||||
BOOL (*ChangeVolume)(ITSMFAudioDevice* audio, UINT32 newVolume, UINT32 muted);
|
||||
/* Flush queued audio data */
|
||||
BOOL (*Flush) (ITSMFAudioDevice* audio);
|
||||
BOOL (*Flush)(ITSMFAudioDevice* audio);
|
||||
/* Free the audio device */
|
||||
void (*Free) (ITSMFAudioDevice* audio);
|
||||
void (*Free)(ITSMFAudioDevice* audio);
|
||||
};
|
||||
|
||||
#define TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME "TSMFAudioDeviceEntry"
|
||||
typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY) (void);
|
||||
typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY)(void);
|
||||
|
||||
ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device);
|
||||
|
||||
|
||||
@ -51,8 +51,10 @@ BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32
|
||||
if (callback && callback->stream_id && callback->channel && callback->channel->Write)
|
||||
{
|
||||
s = Stream_New(NULL, 24);
|
||||
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
|
||||
Stream_Write_UINT32(s, message_id);
|
||||
Stream_Write_UINT32(s, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
@ -60,29 +62,31 @@ BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32
|
||||
Stream_Write_UINT32(s, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */
|
||||
Stream_Write_UINT32(s, 0); /* cbData */
|
||||
DEBUG_TSMF("EOS response size %"PRIuz"", Stream_GetPosition(s));
|
||||
|
||||
status = callback->channel->Write(callback->channel, Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
|
||||
if (status)
|
||||
{
|
||||
WLog_ERR(TAG, "response error %d", status);
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
UINT32 message_id, UINT64 duration, UINT32 data_size)
|
||||
BOOL tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
UINT32 message_id, UINT64 duration, UINT32 data_size)
|
||||
{
|
||||
wStream *s = NULL;
|
||||
wStream* s = NULL;
|
||||
int status = -1;
|
||||
TSMF_CHANNEL_CALLBACK *callback = (TSMF_CHANNEL_CALLBACK *) pChannelCallback;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
if (!callback)
|
||||
if (!callback)
|
||||
return FALSE;
|
||||
|
||||
s = Stream_New(NULL, 32);
|
||||
|
||||
if (!s)
|
||||
return FALSE;
|
||||
|
||||
@ -92,7 +96,6 @@ BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
|
||||
Stream_Write_UINT64(s, duration); /* DataDuration */
|
||||
Stream_Write_UINT64(s, data_size); /* cbData */
|
||||
|
||||
DEBUG_TSMF("ACK response size %"PRIuz"", Stream_GetPosition(s));
|
||||
|
||||
if (!callback->channel || !callback->channel->Write)
|
||||
@ -104,7 +107,7 @@ BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
else
|
||||
{
|
||||
status = callback->channel->Write(callback->channel,
|
||||
Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
}
|
||||
|
||||
if (status)
|
||||
@ -121,11 +124,11 @@ BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
|
||||
static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
||||
{
|
||||
size_t length;
|
||||
wStream *input;
|
||||
wStream *output;
|
||||
wStream* input;
|
||||
wStream* output;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
BOOL processed = FALSE;
|
||||
TSMF_IFMAN ifman;
|
||||
@ -144,16 +147,16 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
|
||||
input = data;
|
||||
output = Stream_New(NULL, 256);
|
||||
|
||||
if (!output)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Seek(output, 8);
|
||||
Stream_Read_UINT32(input, InterfaceId); /* InterfaceId (4 bytes) */
|
||||
Stream_Read_UINT32(input, MessageId); /* MessageId (4 bytes) */
|
||||
Stream_Read_UINT32(input, FunctionId); /* FunctionId (4 bytes) */
|
||||
|
||||
DEBUG_TSMF("cbSize=%"PRIu32" InterfaceId=0x%"PRIX32" MessageId=0x%"PRIX32" FunctionId=0x%"PRIX32"",
|
||||
cbSize, InterfaceId, MessageId, FunctionId);
|
||||
|
||||
cbSize, InterfaceId, MessageId, FunctionId);
|
||||
ZeroMemory(&ifman, sizeof(TSMF_IFMAN));
|
||||
ifman.channel_callback = pChannelCallback;
|
||||
ifman.decoder_name = ((TSMF_PLUGIN*) callback->plugin)->decoder_name;
|
||||
@ -179,12 +182,15 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
error = tsmf_ifman_rim_exchange_capability_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case RIMCALL_RELEASE:
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TSMF_INTERFACE_DEFAULT | STREAM_ID_PROXY:
|
||||
@ -196,6 +202,7 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
error = ERROR_INVALID_DATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CopyMemory(callback->presentation_id, Stream_Pointer(input), GUID_SIZE);
|
||||
Stream_Seek(input, GUID_SIZE);
|
||||
Stream_Read_UINT32(input, callback->stream_id);
|
||||
@ -203,100 +210,125 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
ifman.output_pending = TRUE;
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case EXCHANGE_CAPABILITIES_REQ:
|
||||
error = tsmf_ifman_exchange_capability_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case CHECK_FORMAT_SUPPORT_REQ:
|
||||
error = tsmf_ifman_check_format_support_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_NEW_PRESENTATION:
|
||||
error = tsmf_ifman_on_new_presentation(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ADD_STREAM:
|
||||
error = tsmf_ifman_add_stream(&ifman, ((TSMF_PLUGIN*) callback->plugin)->rdpcontext);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_TOPOLOGY_REQ:
|
||||
error = tsmf_ifman_set_topology_request(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case REMOVE_STREAM:
|
||||
error = tsmf_ifman_remove_stream(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_SOURCE_VIDEO_RECT:
|
||||
error = tsmf_ifman_set_source_video_rect(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SHUTDOWN_PRESENTATION_REQ:
|
||||
error = tsmf_ifman_shutdown_presentation(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_STREAM_VOLUME:
|
||||
error = tsmf_ifman_on_stream_volume(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_CHANNEL_VOLUME:
|
||||
error = tsmf_ifman_on_channel_volume(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_VIDEO_WINDOW:
|
||||
error = tsmf_ifman_set_video_window(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case UPDATE_GEOMETRY_INFO:
|
||||
error = tsmf_ifman_update_geometry_info(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case SET_ALLOCATOR:
|
||||
error = tsmf_ifman_set_allocator(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case NOTIFY_PREROLL:
|
||||
error = tsmf_ifman_notify_preroll(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_SAMPLE:
|
||||
error = tsmf_ifman_on_sample(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_FLUSH:
|
||||
error = tsmf_ifman_on_flush(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_END_OF_STREAM:
|
||||
error = tsmf_ifman_on_end_of_stream(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_STARTED:
|
||||
error = tsmf_ifman_on_playback_started(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_PAUSED:
|
||||
error = tsmf_ifman_on_playback_paused(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_RESTARTED:
|
||||
error = tsmf_ifman_on_playback_restarted(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_STOPPED:
|
||||
error = tsmf_ifman_on_playback_stopped(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case ON_PLAYBACK_RATE_CHANGED:
|
||||
error = tsmf_ifman_on_playback_rate_changed(&ifman);
|
||||
processed = TRUE;
|
||||
break;
|
||||
|
||||
case RIMCALL_RELEASE:
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -321,6 +353,7 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
processed = TRUE;
|
||||
ifman.output_pending = 1;
|
||||
break;
|
||||
|
||||
case RIMCALL_QUERYINTERFACE:
|
||||
/* [MS-RDPEXPS] 2.2.2.1.2 Query Interface Response (QI_RSP)
|
||||
This message is not supported in this channel. */
|
||||
@ -330,7 +363,9 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
WLog_ERR(TAG, "Unknown InterfaceId: 0x%08"PRIX32" MessageId: 0x%08"PRIX32" FunctionId: 0x%08"PRIX32"\n", InterfaceId, MessageId, FunctionId);
|
||||
WLog_ERR(TAG,
|
||||
"Unknown InterfaceId: 0x%08"PRIX32" MessageId: 0x%08"PRIX32" FunctionId: 0x%08"PRIX32"\n",
|
||||
InterfaceId, MessageId, FunctionId);
|
||||
/* When a request is not implemented we return empty response indicating error */
|
||||
}
|
||||
|
||||
@ -363,12 +398,11 @@ out:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_close(IWTSVirtualChannelCallback *pChannelCallback)
|
||||
static UINT tsmf_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
TSMF_STREAM* stream;
|
||||
TSMF_PRESENTATION* presentation;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
|
||||
if (callback->stream_id)
|
||||
@ -378,6 +412,7 @@ static UINT tsmf_on_close(IWTSVirtualChannelCallback *pChannelCallback)
|
||||
if (presentation)
|
||||
{
|
||||
stream = tsmf_stream_find_by_id(presentation, callback->stream_id);
|
||||
|
||||
if (stream)
|
||||
tsmf_stream_free(stream);
|
||||
}
|
||||
@ -392,17 +427,15 @@ static UINT tsmf_on_close(IWTSVirtualChannelCallback *pChannelCallback)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_on_new_channel_connection(IWTSListenerCallback *pListenerCallback,
|
||||
IWTSVirtualChannel *pChannel,
|
||||
BYTE *Data,
|
||||
BOOL *pbAccept,
|
||||
IWTSVirtualChannelCallback **ppCallback)
|
||||
static UINT tsmf_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel,
|
||||
BYTE* Data,
|
||||
BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
TSMF_CHANNEL_CALLBACK* callback;
|
||||
TSMF_LISTENER_CALLBACK* listener_callback = (TSMF_LISTENER_CALLBACK*) pListenerCallback;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
|
||||
callback = (TSMF_CHANNEL_CALLBACK*) calloc(1, sizeof(TSMF_CHANNEL_CALLBACK));
|
||||
|
||||
if (!callback)
|
||||
@ -414,9 +447,7 @@ static UINT tsmf_on_new_channel_connection(IWTSListenerCallback *pListenerCallba
|
||||
callback->plugin = listener_callback->plugin;
|
||||
callback->channel_mgr = listener_callback->channel_mgr;
|
||||
callback->channel = pChannel;
|
||||
|
||||
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -429,9 +460,7 @@ static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
{
|
||||
UINT status;
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
|
||||
tsmf->listener_callback = (TSMF_LISTENER_CALLBACK*) calloc(1, sizeof(TSMF_LISTENER_CALLBACK));
|
||||
|
||||
if (!tsmf->listener_callback)
|
||||
@ -440,12 +469,9 @@ static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
tsmf->listener_callback->iface.OnNewChannelConnection = tsmf_on_new_channel_connection;
|
||||
tsmf->listener_callback->plugin = pPlugin;
|
||||
tsmf->listener_callback->channel_mgr = pChannelMgr;
|
||||
|
||||
status = pChannelMgr->CreateListener(pChannelMgr, "TSMF", 0,
|
||||
(IWTSListenerCallback*) tsmf->listener_callback, &(tsmf->listener));
|
||||
|
||||
(IWTSListenerCallback*) tsmf->listener_callback, &(tsmf->listener));
|
||||
tsmf->listener->pInterface = tsmf->iface.pInterface;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -457,12 +483,9 @@ static UINT tsmf_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
static UINT tsmf_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin;
|
||||
|
||||
DEBUG_TSMF("");
|
||||
|
||||
free(tsmf->listener_callback);
|
||||
free(tsmf);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -479,17 +502,16 @@ COMMAND_LINE_ARGUMENT_A tsmf_args[] =
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT tsmf_process_addin_args(IWTSPlugin *pPlugin, ADDIN_ARGV *args)
|
||||
static UINT tsmf_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
TSMF_PLUGIN* tsmf = (TSMF_PLUGIN*) pPlugin;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
tsmf_args, flags, tsmf, NULL, NULL);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
tsmf_args, flags, tsmf, NULL, NULL);
|
||||
if (status != 0)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
@ -499,22 +521,26 @@ static UINT tsmf_process_addin_args(IWTSPlugin *pPlugin, ADDIN_ARGV *args)
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "sys")
|
||||
{
|
||||
tsmf->audio_name = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->audio_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
tsmf->audio_device = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->audio_device)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "decoder")
|
||||
{
|
||||
tsmf->decoder_name = _strdup(arg->Value);
|
||||
|
||||
if (!tsmf->decoder_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
@ -545,33 +571,32 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
TSMF_PLUGIN* tsmf;
|
||||
TsmfClientContext* context;
|
||||
UINT error = CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
tsmf = (TSMF_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "tsmf");
|
||||
|
||||
if (!tsmf)
|
||||
{
|
||||
tsmf = (TSMF_PLUGIN*) calloc(1, sizeof(TSMF_PLUGIN));
|
||||
|
||||
if (!tsmf)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
tsmf->iface.Initialize = tsmf_plugin_initialize;
|
||||
tsmf->iface.Connected = NULL;
|
||||
tsmf->iface.Disconnected = NULL;
|
||||
tsmf->iface.Terminated = tsmf_plugin_terminated;
|
||||
tsmf->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(pEntryPoints))->instance)->context;
|
||||
|
||||
tsmf->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(
|
||||
pEntryPoints))->instance)->context;
|
||||
context = (TsmfClientContext*) calloc(1, sizeof(TsmfClientContext));
|
||||
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
goto error_context;
|
||||
}
|
||||
|
||||
|
||||
context->handle = (void*) tsmf;
|
||||
tsmf->iface.pInterface = (void*) context;
|
||||
|
||||
@ -590,7 +615,6 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
error_init:
|
||||
free(context);
|
||||
error_context:
|
||||
|
||||
@ -159,8 +159,8 @@ struct _TSMF_SAMPLE
|
||||
static wArrayList* presentation_list = NULL;
|
||||
static int TERMINATING = 0;
|
||||
|
||||
static void _tsmf_presentation_free(TSMF_PRESENTATION* presentation);
|
||||
static void _tsmf_stream_free(TSMF_STREAM* stream);
|
||||
static void _tsmf_presentation_free(void* obj);
|
||||
static void _tsmf_stream_free(void* obj);
|
||||
|
||||
static UINT64 get_current_time(void)
|
||||
{
|
||||
@ -367,7 +367,7 @@ TSMF_PRESENTATION* tsmf_presentation_new(const BYTE* guid,
|
||||
goto error_stream_list;
|
||||
|
||||
ArrayList_Object(presentation->stream_list)->fnObjectFree =
|
||||
(OBJECT_FREE_FN) _tsmf_stream_free;
|
||||
_tsmf_stream_free;
|
||||
|
||||
if (ArrayList_Add(presentation_list, presentation) < 0)
|
||||
goto error_add;
|
||||
@ -528,6 +528,7 @@ static BOOL tsmf_sample_playback_audio(TSMF_SAMPLE* sample)
|
||||
{
|
||||
ret = sample->stream->audio->Play(sample->stream->audio, sample->data,
|
||||
sample->decoded_size);
|
||||
free(sample->data);
|
||||
sample->data = NULL;
|
||||
sample->decoded_size = 0;
|
||||
|
||||
@ -1193,14 +1194,19 @@ BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void _tsmf_presentation_free(TSMF_PRESENTATION* presentation)
|
||||
void _tsmf_presentation_free(void* obj)
|
||||
{
|
||||
tsmf_presentation_stop(presentation);
|
||||
ArrayList_Clear(presentation->stream_list);
|
||||
ArrayList_Free(presentation->stream_list);
|
||||
free(presentation->rects);
|
||||
ZeroMemory(presentation, sizeof(TSMF_PRESENTATION));
|
||||
free(presentation);
|
||||
TSMF_PRESENTATION* presentation = (TSMF_PRESENTATION*)obj;
|
||||
|
||||
if (presentation)
|
||||
{
|
||||
tsmf_presentation_stop(presentation);
|
||||
ArrayList_Clear(presentation->stream_list);
|
||||
ArrayList_Free(presentation->stream_list);
|
||||
free(presentation->rects);
|
||||
ZeroMemory(presentation, sizeof(TSMF_PRESENTATION));
|
||||
free(presentation);
|
||||
}
|
||||
}
|
||||
|
||||
void tsmf_presentation_free(TSMF_PRESENTATION* presentation)
|
||||
@ -1414,8 +1420,10 @@ void tsmf_stream_end(TSMF_STREAM* stream, UINT32 message_id,
|
||||
stream->eos_channel_callback = pChannelCallback;
|
||||
}
|
||||
|
||||
void _tsmf_stream_free(TSMF_STREAM* stream)
|
||||
void _tsmf_stream_free(void* obj)
|
||||
{
|
||||
TSMF_STREAM* stream = (TSMF_STREAM*)obj;
|
||||
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
@ -1552,8 +1560,7 @@ BOOL tsmf_media_init(void)
|
||||
if (!presentation_list)
|
||||
return FALSE;
|
||||
|
||||
ArrayList_Object(presentation_list)->fnObjectFree = (OBJECT_FREE_FN)
|
||||
_tsmf_presentation_free;
|
||||
ArrayList_Object(presentation_list)->fnObjectFree = _tsmf_presentation_free;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include "libusb_udevice.h"
|
||||
|
||||
@ -532,7 +533,7 @@ static int udev_get_hub_handle(UDEVICE* pdev, UINT16 bus_number, UINT16 dev_numb
|
||||
error = 0;
|
||||
WLog_DBG(TAG, " Port: %d", pdev->port_number);
|
||||
/* gen device path */
|
||||
sprintf(pdev->path, "ugen%"PRIu16".%"PRIu16"", bus_number, dev_number);
|
||||
sprintf_s(pdev->path, ARRAYSIZE(pdev->path), "ugen%"PRIu16".%"PRIu16"", bus_number, dev_number);
|
||||
WLog_DBG(TAG, " DevPath: %s", pdev->path);
|
||||
break;
|
||||
}
|
||||
@ -661,8 +662,7 @@ static int udev_get_hub_handle(UDEVICE* pdev, UINT16 bus_number, UINT16 dev_numb
|
||||
}
|
||||
while (p1 != NULL);
|
||||
|
||||
memset(pdev->path, 0, 17);
|
||||
strcpy(pdev->path, p2);
|
||||
sprintf_s(pdev->path, ARRAYSIZE(pdev->path), "%s", p2);
|
||||
WLog_DBG(TAG, " DevPath: %s", pdev->path);
|
||||
/* query parent hub info */
|
||||
dev = udev_device_get_parent(dev);
|
||||
@ -1036,7 +1036,8 @@ static int libusb_udev_control_query_device_text(IUDEVICE* idev, UINT32 TextType
|
||||
case DeviceTextLocationInformation:
|
||||
bus_number = libusb_get_bus_number(pdev->libusb_dev);
|
||||
device_address = libusb_get_device_address(pdev->libusb_dev);
|
||||
sprintf(deviceLocation, "Port_#%04"PRIu8".Hub_#%04"PRIu8"", device_address, bus_number);
|
||||
sprintf_s(deviceLocation, ARRAYSIZE(deviceLocation), "Port_#%04"PRIu8".Hub_#%04"PRIu8"",
|
||||
device_address, bus_number);
|
||||
|
||||
for (i = 0; i < strlen(deviceLocation); i++)
|
||||
{
|
||||
|
||||
@ -484,7 +484,7 @@ static void urbdrc_udevman_register_devices(UDEVMAN* udevman, char* devices)
|
||||
dev_number = 0;
|
||||
idVendor = 0;
|
||||
idProduct = 0;
|
||||
strcpy(hardware_id, token);
|
||||
sprintf_s(hardware_id, ARRAYSIZE(hardware_id), "%s", token);
|
||||
token = strtok(NULL, "#");
|
||||
|
||||
if (udevman->flags & UDEVMAN_FLAG_ADD_BY_VID_PID)
|
||||
@ -513,7 +513,7 @@ static void urbdrc_udevman_parse_addin_args(UDEVMAN* udevman, ADDIN_ARGV* args)
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
urbdrc_udevman_args, flags, udevman, NULL, NULL);
|
||||
arg = urbdrc_udevman_args;
|
||||
|
||||
|
||||
@ -59,14 +59,14 @@ static int func_hardware_id_format(IUDEVICE* pdev, char(*HardwareIds)[DEVICE_HAR
|
||||
idProduct = (UINT16)pdev->query_device_descriptor(pdev, ID_PRODUCT);
|
||||
bcdDevice = (UINT16)pdev->query_device_descriptor(pdev, BCD_DEVICE);
|
||||
sprintf_s(str, sizeof(str), "USB\\VID_%04"PRIX16"&PID_%04"PRIX16"", idVendor, idProduct);
|
||||
strcpy(HardwareIds[1], str);
|
||||
strncpy(HardwareIds[1], str, DEVICE_HARDWARE_ID_SIZE);
|
||||
sprintf_s(str, sizeof(str), "%s&REV_%04"PRIX16"", HardwareIds[1], bcdDevice);
|
||||
strcpy(HardwareIds[0], str);
|
||||
strncpy(HardwareIds[0], str, DEVICE_HARDWARE_ID_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int func_compat_id_format(IUDEVICE* pdev,
|
||||
char (*CompatibilityIds)[DEVICE_COMPATIBILITY_ID_SIZE])
|
||||
char(*CompatibilityIds)[DEVICE_COMPATIBILITY_ID_SIZE])
|
||||
{
|
||||
char str[DEVICE_COMPATIBILITY_ID_SIZE];
|
||||
UINT8 bDeviceClass, bDeviceSubClass, bDeviceProtocol;
|
||||
@ -77,20 +77,20 @@ static int func_compat_id_format(IUDEVICE* pdev,
|
||||
if (!(pdev->isCompositeDevice(pdev)))
|
||||
{
|
||||
sprintf_s(str, sizeof(str), "USB\\Class_%02"PRIX8"", bDeviceClass);
|
||||
strcpy(CompatibilityIds[2], str);
|
||||
strncpy(CompatibilityIds[2], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
sprintf_s(str, sizeof(str), "%s&SubClass_%02"PRIX8"", CompatibilityIds[2], bDeviceSubClass);
|
||||
strcpy(CompatibilityIds[1], str);
|
||||
strncpy(CompatibilityIds[1], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
sprintf_s(str, sizeof(str), "%s&Prot_%02"PRIX8"", CompatibilityIds[1], bDeviceProtocol);
|
||||
strcpy(CompatibilityIds[0], str);
|
||||
strncpy(CompatibilityIds[0], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(str, sizeof(str), "USB\\DevClass_00");
|
||||
strcpy(CompatibilityIds[2], str);
|
||||
strncpy(CompatibilityIds[2], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
sprintf_s(str, sizeof(str), "%s&SubClass_00", CompatibilityIds[2]);
|
||||
strcpy(CompatibilityIds[1], str);
|
||||
strncpy(CompatibilityIds[1], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
sprintf_s(str, sizeof(str), "%s&Prot_00", CompatibilityIds[1]);
|
||||
strcpy(CompatibilityIds[0], str);
|
||||
strncpy(CompatibilityIds[0], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1522,7 +1522,7 @@ static UINT urbdrc_process_addin_args(URBDRC_PLUGIN* urbdrc, ADDIN_ARGV* args)
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
||||
urbdrc_args, flags, urbdrc, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/primitives.h>
|
||||
#include <freerdp/client/geometry.h>
|
||||
#include <freerdp/client/video.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/codec/h264.h>
|
||||
|
||||
@ -89,7 +89,7 @@ if(${CMAKE_VERSION} VERSION_GREATER "2.8.10")
|
||||
|
||||
export(PACKAGE freerdp-client)
|
||||
|
||||
set(FREERDP_CLIENT_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/FreeRDP-Client${FREERDP_VERSION_MAJOR}")
|
||||
SetFreeRDPCMakeInstallDir(FREERDP_CLIENT_CMAKE_INSTALL_DIR "FreeRDP-Client${FREERDP_VERSION_MAJOR}")
|
||||
|
||||
configure_package_config_file(FreeRDP-ClientConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ClientConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_CLIENT_CMAKE_INSTALL_DIR}
|
||||
|
||||
@ -204,7 +204,6 @@ BOOL df_post_connect(freerdp* instance)
|
||||
instance->update->BeginPaint = df_begin_paint;
|
||||
instance->update->EndPaint = df_end_paint;
|
||||
df_keyboard_init();
|
||||
pointer_cache_register_callbacks(instance->update);
|
||||
df_register_graphics(instance->context->graphics);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -39,9 +39,6 @@ static UINT wlf_encomsp_participant_created(EncomspClientContext* context,
|
||||
|
||||
static void wlf_encomsp_init(wlfContext* wlf, EncomspClientContext* encomsp)
|
||||
{
|
||||
if (!wlf)
|
||||
return;
|
||||
|
||||
wlf->encomsp = encomsp;
|
||||
encomsp->custom = (void*) wlf;
|
||||
encomsp->ParticipantCreated = wlf_encomsp_participant_created;
|
||||
@ -49,10 +46,14 @@ static void wlf_encomsp_init(wlfContext* wlf, EncomspClientContext* encomsp)
|
||||
|
||||
static void wlf_encomsp_uninit(wlfContext* wlf, EncomspClientContext* encomsp)
|
||||
{
|
||||
if (!wlf)
|
||||
return;
|
||||
if (encomsp)
|
||||
{
|
||||
encomsp->custom = NULL;
|
||||
encomsp->ParticipantCreated = NULL;
|
||||
}
|
||||
|
||||
wlf->encomsp = NULL;
|
||||
if (wlf)
|
||||
wlf->encomsp = NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +61,9 @@ void wlf_OnChannelConnectedEventHandler(void* context,
|
||||
ChannelConnectedEventArgs* e)
|
||||
{
|
||||
wlfContext* wlf = (wlfContext*) context;
|
||||
rdpSettings* settings = wlf->context.settings;
|
||||
rdpSettings* settings;
|
||||
|
||||
settings = wlf->context.settings;
|
||||
|
||||
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
@ -90,7 +93,9 @@ void wlf_OnChannelDisconnectedEventHandler(void* context,
|
||||
ChannelDisconnectedEventArgs* e)
|
||||
{
|
||||
wlfContext* wlf = (wlfContext*) context;
|
||||
rdpSettings* settings = wlf->context.settings;
|
||||
rdpSettings* settings;
|
||||
|
||||
settings = wlf->context.settings;
|
||||
|
||||
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
|
||||
@ -25,22 +25,30 @@
|
||||
|
||||
#include "wlf_input.h"
|
||||
|
||||
BOOL wlf_handle_pointer_enter(freerdp *instance, UwacPointerEnterLeaveEvent *ev) {
|
||||
rdpInput* input = instance->input;
|
||||
BOOL wlf_handle_pointer_enter(freerdp* instance, UwacPointerEnterLeaveEvent* ev)
|
||||
{
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
return input->MouseEvent(input, PTR_FLAGS_MOVE, ev->x, ev->y);
|
||||
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, ev->x, ev->y);
|
||||
}
|
||||
|
||||
BOOL wlf_handle_pointer_motion(freerdp *instance, UwacPointerMotionEvent *ev) {
|
||||
rdpInput* input = instance->input;
|
||||
BOOL wlf_handle_pointer_motion(freerdp* instance, UwacPointerMotionEvent* ev)
|
||||
{
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
return input->MouseEvent(input, PTR_FLAGS_MOVE, ev->x, ev->y);
|
||||
return freerdp_input_send_mouse_event(instance->input, PTR_FLAGS_MOVE, ev->x, ev->y);
|
||||
}
|
||||
|
||||
BOOL wlf_handle_pointer_buttons(freerdp *instance, UwacPointerButtonEvent *ev) {
|
||||
BOOL wlf_handle_pointer_buttons(freerdp* instance, UwacPointerButtonEvent* ev)
|
||||
{
|
||||
rdpInput* input;
|
||||
UINT16 flags;
|
||||
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
input = instance->input;
|
||||
|
||||
if (ev->state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
@ -53,45 +61,57 @@ BOOL wlf_handle_pointer_buttons(freerdp *instance, UwacPointerButtonEvent *ev) {
|
||||
case BTN_LEFT:
|
||||
flags |= PTR_FLAGS_BUTTON1;
|
||||
break;
|
||||
|
||||
case BTN_RIGHT:
|
||||
flags |= PTR_FLAGS_BUTTON2;
|
||||
break;
|
||||
|
||||
case BTN_MIDDLE:
|
||||
flags |= PTR_FLAGS_BUTTON3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return input->MouseEvent(input, flags, ev->x, ev->y);
|
||||
return freerdp_input_send_mouse_event(input, flags, ev->x, ev->y);
|
||||
}
|
||||
|
||||
|
||||
BOOL wlf_handle_pointer_axis(freerdp *instance, UwacPointerAxisEvent *ev) {
|
||||
BOOL wlf_handle_pointer_axis(freerdp* instance, UwacPointerAxisEvent* ev)
|
||||
{
|
||||
rdpInput* input;
|
||||
UINT16 flags;
|
||||
int direction;
|
||||
|
||||
input = instance->input;
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
input = instance->input;
|
||||
flags = PTR_FLAGS_WHEEL;
|
||||
|
||||
if (ev->axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
{
|
||||
direction = wl_fixed_to_int(ev->value);
|
||||
|
||||
if (direction < 0)
|
||||
flags |= 0x0078;
|
||||
else
|
||||
flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
|
||||
}
|
||||
|
||||
return input->MouseEvent(input, flags, ev->x, ev->y);
|
||||
return freerdp_input_send_mouse_event(input, flags, ev->x, ev->y);
|
||||
}
|
||||
|
||||
BOOL wlf_handle_key(freerdp *instance, UwacKeyEvent *ev) {
|
||||
rdpInput* input = instance->input;
|
||||
BOOL wlf_handle_key(freerdp* instance, UwacKeyEvent* ev)
|
||||
{
|
||||
rdpInput* input;
|
||||
DWORD rdp_scancode;
|
||||
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
input = instance->input;
|
||||
rdp_scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(ev->raw_key + 8);
|
||||
|
||||
if (rdp_scancode == RDP_SCANCODE_UNKNOWN)
|
||||
@ -100,10 +120,14 @@ BOOL wlf_handle_key(freerdp *instance, UwacKeyEvent *ev) {
|
||||
return freerdp_input_send_keyboard_event_ex(input, ev->pressed, rdp_scancode);
|
||||
}
|
||||
|
||||
BOOL wlf_keyboard_enter(freerdp *instance, UwacKeyboardEnterLeaveEvent *ev) {
|
||||
rdpInput* input = instance->input;
|
||||
BOOL wlf_keyboard_enter(freerdp* instance, UwacKeyboardEnterLeaveEvent* ev)
|
||||
{
|
||||
rdpInput* input;
|
||||
|
||||
return input->FocusInEvent(input, 0) &&
|
||||
input->MouseEvent(input, PTR_FLAGS_MOVE, 0, 0);
|
||||
if (!instance || !ev || !instance->input)
|
||||
return FALSE;
|
||||
|
||||
input = instance->input;
|
||||
return freerdp_input_send_focus_in_event(input, 0) &&
|
||||
freerdp_input_send_mouse_event(input, PTR_FLAGS_MOVE, 0, 0);
|
||||
}
|
||||
|
||||
@ -40,6 +40,9 @@
|
||||
|
||||
static BOOL wl_update_content(wlfContext* context_w)
|
||||
{
|
||||
if (!context_w)
|
||||
return FALSE;
|
||||
|
||||
if (!context_w->waitingFrameDone && context_w->haveDamage)
|
||||
{
|
||||
UwacWindowSubmitBuffer(context_w->window, true);
|
||||
@ -53,7 +56,15 @@ static BOOL wl_update_content(wlfContext* context_w)
|
||||
static BOOL wl_begin_paint(rdpContext* context)
|
||||
{
|
||||
rdpGdi* gdi;
|
||||
|
||||
if (!context || !context->gdi)
|
||||
return FALSE;
|
||||
|
||||
gdi = context->gdi;
|
||||
|
||||
if (!gdi->primary)
|
||||
return FALSE;
|
||||
|
||||
gdi->primary->hdc->hwnd->invalid->null = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
@ -66,7 +77,11 @@ static BOOL wl_end_paint(rdpContext* context)
|
||||
wlfContext* context_w;
|
||||
INT32 x, y;
|
||||
UINT32 w, h;
|
||||
int i;
|
||||
UINT32 i;
|
||||
|
||||
if (!context || !context->gdi || !context->gdi->primary)
|
||||
return FALSE;
|
||||
|
||||
gdi = context->gdi;
|
||||
|
||||
if (gdi->primary->hdc->hwnd->invalid->null)
|
||||
@ -117,7 +132,6 @@ static BOOL wl_pre_connect(freerdp* instance)
|
||||
|
||||
settings->OsMajorType = OSMAJORTYPE_UNIX;
|
||||
settings->OsMinorType = OSMINORTYPE_NATIVE_WAYLAND;
|
||||
settings->SoftwareGdi = TRUE;
|
||||
ZeroMemory(settings->OrderSupport, 32);
|
||||
settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE;
|
||||
@ -132,9 +146,9 @@ static BOOL wl_pre_connect(freerdp* instance)
|
||||
settings->OrderSupport[NEG_LINETO_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_MEM3BLT_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_MEM3BLT_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE;
|
||||
settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE;
|
||||
settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE;
|
||||
@ -177,6 +191,9 @@ static BOOL wl_post_connect(freerdp* instance)
|
||||
UwacWindow* window;
|
||||
wlfContext* context;
|
||||
|
||||
if (!instance || !instance->context)
|
||||
return FALSE;
|
||||
|
||||
if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
|
||||
return FALSE;
|
||||
|
||||
@ -245,7 +262,7 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
|
||||
context = (wlfContext*)instance->context;
|
||||
context->waitingFrameDone = FALSE;
|
||||
|
||||
if (context->haveDamage && !wl_end_paint(instance->context))
|
||||
if (context->haveDamage && !wl_update_content(context))
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
@ -299,7 +316,7 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
wlfContext* context;
|
||||
DWORD count;
|
||||
HANDLE handles[64];
|
||||
DWORD status;
|
||||
DWORD status = WAIT_ABANDONED;
|
||||
|
||||
if (!instance)
|
||||
return -1;
|
||||
@ -315,20 +332,18 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle_uwac_events(instance, context->display);
|
||||
|
||||
while (!freerdp_shall_disconnect(instance))
|
||||
{
|
||||
handles[0] = context->displayHandle;
|
||||
count = freerdp_get_event_handles(instance->context, &handles[1], 63);
|
||||
count = freerdp_get_event_handles(instance->context, &handles[1], 63) + 1;
|
||||
|
||||
if (!count)
|
||||
if (count <= 1)
|
||||
{
|
||||
printf("Failed to get FreeRDP file descriptor\n");
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForMultipleObjects(count + 1, handles, FALSE, INFINITE);
|
||||
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
|
||||
|
||||
if (WAIT_FAILED == status)
|
||||
{
|
||||
@ -342,7 +357,6 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
break;
|
||||
}
|
||||
|
||||
//if (WaitForMultipleObjects(count, &handles[1], FALSE, INFINITE)) {
|
||||
if (freerdp_check_event_handles(instance->context) != TRUE)
|
||||
{
|
||||
if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
|
||||
@ -350,15 +364,13 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
freerdp_disconnect(instance);
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
static BOOL wlf_client_global_init()
|
||||
static BOOL wlf_client_global_init(void)
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
@ -368,10 +380,24 @@ static BOOL wlf_client_global_init()
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void wlf_client_global_uninit()
|
||||
static void wlf_client_global_uninit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int wlf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
|
||||
{
|
||||
wlfContext* wlf;
|
||||
const char* str_data = freerdp_get_logon_error_info_data(data);
|
||||
const char* str_type = freerdp_get_logon_error_info_type(type);
|
||||
|
||||
if (!instance || !instance->context)
|
||||
return -1;
|
||||
|
||||
wlf = (wlfContext*) instance->context;
|
||||
WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
|
||||
{
|
||||
UwacReturnCode status;
|
||||
@ -387,7 +413,7 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
|
||||
instance->GatewayAuthenticate = client_cli_gw_authenticate;
|
||||
instance->VerifyCertificate = client_cli_verify_certificate;
|
||||
instance->VerifyChangedCertificate = client_cli_verify_changed_certificate;
|
||||
instance->LogonErrorInfo = NULL;
|
||||
instance->LogonErrorInfo = wlf_logon_error_info;
|
||||
wfl->display = UwacOpenDisplay(NULL, &status);
|
||||
|
||||
if (!wfl->display || (status != UWAC_SUCCESS))
|
||||
@ -448,8 +474,6 @@ int main(int argc, char* argv[])
|
||||
DWORD status;
|
||||
RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
|
||||
rdpContext* context;
|
||||
//if (!handle_uwac_events(NULL, g_display))
|
||||
// exit(1);
|
||||
RdpClientEntry(&clientEntryPoints);
|
||||
context = freerdp_client_context_new(&clientEntryPoints);
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ set(${MODULE_PREFIX}_SRCS
|
||||
xf_input.h
|
||||
xf_event.c
|
||||
xf_event.h
|
||||
xf_floatbar.c
|
||||
xf_floatbar.h
|
||||
xf_input.c
|
||||
xf_input.h
|
||||
xf_channels.c
|
||||
|
||||
@ -11,65 +11,87 @@ LPSTR tr_esc_str(LPCSTR arg, bool format)
|
||||
LPSTR tmp = NULL;
|
||||
size_t cs = 0, x, ds, len;
|
||||
size_t s;
|
||||
if(NULL == arg)
|
||||
|
||||
if (NULL == arg)
|
||||
return NULL;
|
||||
|
||||
s = strlen(arg);
|
||||
|
||||
/* Find trailing whitespaces */
|
||||
while((s > 0) && isspace(arg[s-1]))
|
||||
while ((s > 0) && isspace(arg[s - 1]))
|
||||
s--;
|
||||
|
||||
/* Prepare a initial buffer with the size of the result string. */
|
||||
ds = s + 1;
|
||||
if(s)
|
||||
|
||||
if (s)
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not allocate string buffer.\n");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
/* Copy character for character and check, if it is necessary to escape. */
|
||||
memset(tmp, 0, ds * sizeof(CHAR));
|
||||
for(x=0; x<s; x++)
|
||||
|
||||
for (x = 0; x < s; x++)
|
||||
{
|
||||
switch(arg[x])
|
||||
switch (arg[x])
|
||||
{
|
||||
case '<':
|
||||
len = format ? 13 : 4;
|
||||
ds += len - 1;
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not reallocate string buffer.\n");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
if (format)
|
||||
strncpy (&tmp[cs], "<replaceable>", len);
|
||||
else
|
||||
strncpy (&tmp[cs], "<", len);
|
||||
/* coverity[buffer_size] */
|
||||
strncpy(&tmp[cs], "<replaceable>", len);
|
||||
else
|
||||
/* coverity[buffer_size] */
|
||||
strncpy(&tmp[cs], "<", len);
|
||||
|
||||
cs += len;
|
||||
break;
|
||||
|
||||
case '>':
|
||||
len = format ? 14 : 4;
|
||||
ds += len - 1;
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not reallocate string buffer.\n");
|
||||
exit(-4);
|
||||
}
|
||||
|
||||
if (format)
|
||||
strncpy (&tmp[cs], "</replaceable>", len);
|
||||
else
|
||||
strncpy (&tmp[cs], "<", len);
|
||||
/* coverity[buffer_size] */
|
||||
strncpy(&tmp[cs], "</replaceable>", len);
|
||||
else
|
||||
/* coverity[buffer_size] */
|
||||
strncpy(&tmp[cs], "<", len);
|
||||
|
||||
cs += len;
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
ds += 5;
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not reallocate string buffer.\n");
|
||||
exit(-5);
|
||||
}
|
||||
|
||||
tmp[cs++] = '&';
|
||||
tmp[cs++] = 'a';
|
||||
tmp[cs++] = 'p';
|
||||
@ -77,14 +99,17 @@ LPSTR tr_esc_str(LPCSTR arg, bool format)
|
||||
tmp[cs++] = 's';
|
||||
tmp[cs++] = ';';
|
||||
break;
|
||||
|
||||
case '"':
|
||||
ds += 5;
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not reallocate string buffer.\n");
|
||||
exit(-6);
|
||||
}
|
||||
|
||||
tmp[cs++] = '&';
|
||||
tmp[cs++] = 'q';
|
||||
tmp[cs++] = 'u';
|
||||
@ -92,88 +117,101 @@ LPSTR tr_esc_str(LPCSTR arg, bool format)
|
||||
tmp[cs++] = 't';
|
||||
tmp[cs++] = ';';
|
||||
break;
|
||||
|
||||
case '&':
|
||||
ds += 4;
|
||||
tmp = (LPSTR)realloc(tmp, ds * sizeof(CHAR));
|
||||
if(NULL == tmp)
|
||||
|
||||
if (NULL == tmp)
|
||||
{
|
||||
fprintf(stderr, "Could not reallocate string buffer.\n");
|
||||
exit(-7);
|
||||
}
|
||||
|
||||
tmp[cs++] = '&';
|
||||
tmp[cs++] = 'a';
|
||||
tmp[cs++] = 'm';
|
||||
tmp[cs++] = 'p';
|
||||
tmp[cs++] = ';';
|
||||
break;
|
||||
|
||||
default:
|
||||
tmp[cs++] = arg[x];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Assure, the string is '\0' terminated. */
|
||||
tmp[ds-1] = '\0';
|
||||
tmp[ds - 1] = '\0';
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
size_t elements = sizeof(args)/sizeof(args[0]);
|
||||
size_t elements = sizeof(args) / sizeof(args[0]);
|
||||
size_t x;
|
||||
const char *fname = "xfreerdp-argument.1.xml";
|
||||
FILE *fp = NULL;
|
||||
const char* fname = "xfreerdp-argument.1.xml";
|
||||
FILE* fp = NULL;
|
||||
/* Open output file for writing, truncate if existing. */
|
||||
fp = fopen(fname, "w");
|
||||
if(NULL == fp)
|
||||
|
||||
if (NULL == fp)
|
||||
{
|
||||
fprintf(stderr, "Could not open '%s' for writing.\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The tag used as header in the manpage */
|
||||
fprintf(fp, "<refsect1>\n");
|
||||
fprintf(fp, "\t<title>Options</title>\n");
|
||||
fprintf(fp, "\t\t<variablelist>\n");
|
||||
|
||||
/* Iterate over argument struct and write data to docbook 4.5
|
||||
* compatible XML */
|
||||
if(elements < 2)
|
||||
if (elements < 2)
|
||||
{
|
||||
fprintf(stderr, "The argument array 'args' is empty, writing an empty file.\n");
|
||||
elements = 1;
|
||||
}
|
||||
for(x=0; x<elements - 1; x++)
|
||||
{
|
||||
const COMMAND_LINE_ARGUMENT_A *arg = &args[x];
|
||||
char *name = tr_esc_str((LPSTR) arg->Name, FALSE);
|
||||
char *alias = tr_esc_str((LPSTR) arg->Alias, FALSE);
|
||||
char *format = tr_esc_str(arg->Format, TRUE);
|
||||
char *text = tr_esc_str((LPSTR) arg->Text, FALSE);
|
||||
|
||||
for (x = 0; x < elements - 1; x++)
|
||||
{
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = &args[x];
|
||||
char* name = tr_esc_str((LPSTR) arg->Name, FALSE);
|
||||
char* alias = tr_esc_str((LPSTR) arg->Alias, FALSE);
|
||||
char* format = tr_esc_str(arg->Format, TRUE);
|
||||
char* text = tr_esc_str((LPSTR) arg->Text, FALSE);
|
||||
fprintf(fp, "\t\t\t<varlistentry>\n");
|
||||
|
||||
do
|
||||
{
|
||||
fprintf(fp, "\t\t\t\t<term><option>");
|
||||
|
||||
if (arg->Flags == COMMAND_LINE_VALUE_BOOL)
|
||||
fprintf(fp, "%s", arg->Default ? "-" : "+");
|
||||
else
|
||||
fprintf(fp, "/");
|
||||
|
||||
fprintf(fp, "%s</option>", name);
|
||||
|
||||
if (format)
|
||||
{
|
||||
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
|
||||
fprintf(fp, "[");
|
||||
|
||||
fprintf(fp, ":%s", format);
|
||||
|
||||
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
|
||||
fprintf(fp, "]");
|
||||
}
|
||||
|
||||
fprintf(fp, "</term>\n");
|
||||
|
||||
|
||||
if (alias == name)
|
||||
break;
|
||||
|
||||
free (name);
|
||||
free(name);
|
||||
name = alias;
|
||||
}
|
||||
while (alias);
|
||||
@ -190,19 +228,21 @@ int main(int argc, char *argv[])
|
||||
fprintf(fp, " (default:%s)", arg->Default ? "on" : "off");
|
||||
else if (arg->Default)
|
||||
{
|
||||
char *value = tr_esc_str((LPSTR) arg->Default, FALSE);
|
||||
char* value = tr_esc_str((LPSTR) arg->Default, FALSE);
|
||||
fprintf(fp, " (default:%s)", value);
|
||||
free (value);
|
||||
free(value);
|
||||
}
|
||||
|
||||
fprintf(fp, "</para>\n");
|
||||
fprintf(fp, "\t\t\t\t</listitem>\n");
|
||||
}
|
||||
|
||||
fprintf(fp, "\t\t\t</varlistentry>\n");
|
||||
free(name);
|
||||
free(format);
|
||||
free(text);
|
||||
}
|
||||
|
||||
fprintf(fp, "\t\t</variablelist>\n");
|
||||
fprintf(fp, "\t</refsect1>\n");
|
||||
fclose(fp);
|
||||
|
||||
11
client/X11/resource/close.xbm
Normal file
11
client/X11/resource/close.xbm
Normal file
@ -0,0 +1,11 @@
|
||||
#define close_width 24
|
||||
#define close_height 24
|
||||
static unsigned char close_bits[] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x7c, 0xfe, 0xff, 0x38, 0xfe, 0xff, 0x11, 0xff, 0xff, 0x83, 0xff,
|
||||
0xff, 0xc7, 0xff, 0xff, 0x83, 0xff, 0xff, 0x11, 0xff, 0xff, 0x38, 0xfe,
|
||||
0xff, 0x7c, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
11
client/X11/resource/lock.xbm
Normal file
11
client/X11/resource/lock.xbm
Normal file
@ -0,0 +1,11 @@
|
||||
#define lock_width 24
|
||||
#define lock_height 24
|
||||
static unsigned char lock_bits[] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff,
|
||||
0xff, 0x83, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xc7, 0xff,
|
||||
0xff, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xef, 0xff, 0xff, 0xef, 0xff,
|
||||
0xff, 0xef, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
11
client/X11/resource/minimize.xbm
Normal file
11
client/X11/resource/minimize.xbm
Normal file
@ -0,0 +1,11 @@
|
||||
#define minimize_width 24
|
||||
#define minimize_height 24
|
||||
static unsigned char minimize_bits[] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc,
|
||||
0x3f, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
11
client/X11/resource/restore.xbm
Normal file
11
client/X11/resource/restore.xbm
Normal file
@ -0,0 +1,11 @@
|
||||
#define restore_width 24
|
||||
#define restore_height 24
|
||||
static unsigned char restore_bits[] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x03, 0xff, 0xff, 0x03, 0xff, 0xff, 0x3b, 0xff, 0x7f, 0x20, 0xff,
|
||||
0x7f, 0x20, 0xff, 0x7f, 0x07, 0xff, 0x7f, 0xe7, 0xff, 0x7f, 0xe7, 0xff,
|
||||
0x7f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
11
client/X11/resource/unlock.xbm
Normal file
11
client/X11/resource/unlock.xbm
Normal file
@ -0,0 +1,11 @@
|
||||
#define unlock_width 24
|
||||
#define unlock_height 24
|
||||
static unsigned char unlock_bits[] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xf3, 0xff, 0xff, 0xf3, 0xff, 0xff, 0x73, 0xfe, 0xff, 0x03, 0xfe,
|
||||
0x3f, 0x00, 0xfe, 0xff, 0x03, 0xfe, 0xff, 0x73, 0xfe, 0xff, 0xf3, 0xff,
|
||||
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
@ -68,7 +68,7 @@ void xf_OnChannelConnectedEventHandler(void* context, ChannelConnectedEventArgs*
|
||||
}
|
||||
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
xf_disp_init(xfc, (DispClientContext*)e->pInterface);
|
||||
xf_disp_init(xfc->xfDisp, (DispClientContext*)e->pInterface);
|
||||
}
|
||||
else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
@ -96,6 +96,10 @@ void xf_OnChannelDisconnectedEventHandler(void* context, ChannelDisconnectedEven
|
||||
{
|
||||
xfc->rdpei = NULL;
|
||||
}
|
||||
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
xf_disp_uninit(xfc->xfDisp, (DispClientContext*)e->pInterface);
|
||||
}
|
||||
else if (strcmp(e->name, TSMF_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
xf_tsmf_uninit(xfc, (TsmfClientContext*) e->pInterface);
|
||||
|
||||
@ -32,9 +32,6 @@
|
||||
#include <freerdp/client/geometry.h>
|
||||
#include <freerdp/client/video.h>
|
||||
|
||||
int xf_on_channel_connected(freerdp* instance, const char* name, void* pInterface);
|
||||
int xf_on_channel_disconnected(freerdp* instance, const char* name, void* pInterface);
|
||||
|
||||
void xf_OnChannelConnectedEventHandler(void* context, ChannelConnectedEventArgs* e);
|
||||
void xf_OnChannelDisconnectedEventHandler(void* context, ChannelDisconnectedEventArgs* e);
|
||||
|
||||
|
||||
@ -50,10 +50,6 @@
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XI
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#endif
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
#include <errno.h>
|
||||
@ -105,6 +101,7 @@
|
||||
#include "xf_channels.h"
|
||||
#include "xfreerdp.h"
|
||||
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG CLIENT_TAG("x11")
|
||||
|
||||
@ -287,11 +284,6 @@ static BOOL xf_desktop_resize(rdpContext* context)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_sw_begin_paint(rdpContext* context)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_sw_end_paint(rdpContext* context)
|
||||
{
|
||||
int i;
|
||||
@ -394,11 +386,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL xf_hw_begin_paint(rdpContext* context)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_hw_end_paint(rdpContext* context)
|
||||
{
|
||||
INT32 x, y;
|
||||
@ -555,16 +542,26 @@ BOOL xf_create_window(xfContext* xfc)
|
||||
}
|
||||
else if (settings->ServerPort == 3389)
|
||||
{
|
||||
windowTitle = malloc(1 + sizeof("FreeRDP: ") + strlen(
|
||||
settings->ServerHostname));
|
||||
sprintf(windowTitle, "FreeRDP: %s", settings->ServerHostname);
|
||||
size_t size = 1 + sizeof("FreeRDP: ") + strlen(
|
||||
settings->ServerHostname);
|
||||
windowTitle = malloc(size);
|
||||
|
||||
if (!windowTitle)
|
||||
return FALSE;
|
||||
|
||||
sprintf_s(windowTitle, size, "FreeRDP: %s", settings->ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
windowTitle = malloc(1 + sizeof("FreeRDP: ") + strlen(settings->ServerHostname)
|
||||
+ sizeof(":00000"));
|
||||
sprintf(windowTitle, "FreeRDP: %s:%i", settings->ServerHostname,
|
||||
settings->ServerPort);
|
||||
size_t size = 1 + sizeof("FreeRDP: ") + strlen(settings->ServerHostname)
|
||||
+ sizeof(":00000");
|
||||
windowTitle = malloc(size);
|
||||
|
||||
if (!windowTitle)
|
||||
return FALSE;
|
||||
|
||||
sprintf_s(windowTitle, size, "FreeRDP: %s:%i", settings->ServerHostname,
|
||||
settings->ServerPort);
|
||||
}
|
||||
|
||||
#ifdef WITH_XRENDER
|
||||
@ -707,6 +704,16 @@ void xf_toggle_fullscreen(xfContext* xfc)
|
||||
WindowStateChangeEventArgs e;
|
||||
rdpContext* context = (rdpContext*) xfc;
|
||||
rdpSettings* settings = context->settings;
|
||||
|
||||
/*
|
||||
when debugging, ungrab keyboard when toggling fullscreen
|
||||
to allow keyboard usage on the debugger
|
||||
*/
|
||||
if (xfc->debug)
|
||||
{
|
||||
XUngrabKeyboard(xfc->display, CurrentTime);
|
||||
}
|
||||
|
||||
xfc->fullscreen = (xfc->fullscreen) ? FALSE : TRUE;
|
||||
xfc->decorations = (xfc->fullscreen) ? FALSE : settings->Decorations;
|
||||
xf_SetWindowFullscreen(xfc, xfc->window, xfc->fullscreen);
|
||||
@ -1111,9 +1118,9 @@ static BOOL xf_pre_connect(freerdp* instance)
|
||||
settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = settings->BitmapCacheEnabled;
|
||||
settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE;
|
||||
settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE;
|
||||
settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = settings->GlyphSupportLevel != GLYPH_SUPPORT_NONE;
|
||||
settings->OrderSupport[NEG_FAST_INDEX_INDEX] = settings->GlyphSupportLevel != GLYPH_SUPPORT_NONE;
|
||||
settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = settings->GlyphSupportLevel != GLYPH_SUPPORT_NONE;
|
||||
settings->OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE;
|
||||
settings->OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE;
|
||||
settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE;
|
||||
@ -1126,7 +1133,7 @@ static BOOL xf_pre_connect(freerdp* instance)
|
||||
if (!freerdp_client_load_addins(channels, instance->settings))
|
||||
return FALSE;
|
||||
|
||||
if (!settings->Username && !settings->CredentialsFromStdin)
|
||||
if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon)
|
||||
{
|
||||
char* login_name = getlogin();
|
||||
|
||||
@ -1182,6 +1189,7 @@ static BOOL xf_pre_connect(freerdp* instance)
|
||||
xfc->decorations = settings->Decorations;
|
||||
xfc->grab_keyboard = settings->GrabKeyboard;
|
||||
xfc->fullscreen_toggle = settings->ToggleFullscreen;
|
||||
xfc->floatbar = settings->Floatbar;
|
||||
xf_button_map_init(xfc);
|
||||
return TRUE;
|
||||
}
|
||||
@ -1257,18 +1265,15 @@ static BOOL xf_post_connect(freerdp* instance)
|
||||
|
||||
if (settings->SoftwareGdi)
|
||||
{
|
||||
update->BeginPaint = xf_sw_begin_paint;
|
||||
update->EndPaint = xf_sw_end_paint;
|
||||
update->DesktopResize = xf_sw_desktop_resize;
|
||||
}
|
||||
else
|
||||
{
|
||||
update->BeginPaint = xf_hw_begin_paint;
|
||||
update->EndPaint = xf_hw_end_paint;
|
||||
update->DesktopResize = xf_hw_desktop_resize;
|
||||
}
|
||||
|
||||
pointer_cache_register_callbacks(update);
|
||||
update->PlaySound = xf_play_sound;
|
||||
update->SetKeyboardIndicators = xf_keyboard_set_indicators;
|
||||
update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
|
||||
@ -1299,6 +1304,10 @@ static void xf_post_disconnect(freerdp* instance)
|
||||
|
||||
context = instance->context;
|
||||
xfc = (xfContext*) context;
|
||||
PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
|
||||
xf_OnChannelConnectedEventHandler);
|
||||
PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
|
||||
xf_OnChannelDisconnectedEventHandler);
|
||||
gdi_free(instance);
|
||||
|
||||
if (xfc->clipboard)
|
||||
@ -1414,46 +1423,25 @@ static DWORD WINAPI xf_input_thread(LPVOID arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL xf_auto_reconnect(freerdp* instance)
|
||||
static BOOL handle_window_events(freerdp* instance)
|
||||
{
|
||||
UINT32 maxRetries;
|
||||
UINT32 numRetries = 0;
|
||||
rdpSettings* settings = instance->settings;
|
||||
maxRetries = settings->AutoReconnectMaxRetries;
|
||||
rdpSettings* settings;
|
||||
|
||||
/* Only auto reconnect on network disconnects. */
|
||||
if (freerdp_error_info(instance) != 0)
|
||||
if (!instance || !instance->settings)
|
||||
return FALSE;
|
||||
|
||||
/* A network disconnect was detected */
|
||||
WLog_INFO(TAG, "Network disconnect!");
|
||||
settings = instance->settings;
|
||||
|
||||
if (!settings->AutoReconnectionEnabled)
|
||||
if (!settings->AsyncInput)
|
||||
{
|
||||
/* No auto-reconnect - just quit */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Perform an auto-reconnect. */
|
||||
while (TRUE)
|
||||
{
|
||||
/* Quit retrying if max retries has been exceeded */
|
||||
if ((maxRetries > 0) && (numRetries++ >= maxRetries))
|
||||
if (!xf_process_x_events(instance))
|
||||
{
|
||||
WLog_INFO(TAG, "Closed from X11");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Attempt the next reconnect */
|
||||
WLog_INFO(TAG, "Attempting reconnect (%"PRIu32" of %"PRIu32")", numRetries, maxRetries);
|
||||
|
||||
if (freerdp_reconnect(instance))
|
||||
return TRUE;
|
||||
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "Maximum reconnect retries exceeded");
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/** Main loop for the rdp connection.
|
||||
@ -1490,6 +1478,8 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
if (freerdp_get_last_error(instance->context) ==
|
||||
FREERDP_ERROR_AUTHENTICATION_FAILED)
|
||||
exit_code = XF_EXIT_AUTH_FAILURE;
|
||||
else if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
|
||||
exit_code = XF_EXIT_NEGO_FAILURE;
|
||||
else
|
||||
exit_code = XF_EXIT_CONN_FAILED;
|
||||
}
|
||||
@ -1536,12 +1526,9 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
handles[0] = timer;
|
||||
|
||||
if (!settings->AsyncInput)
|
||||
{
|
||||
inputEvent = xfc->x11event;
|
||||
handles[1] = inputEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1555,6 +1542,12 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
|
||||
while (!freerdp_shall_disconnect(instance))
|
||||
{
|
||||
nCount = 0;
|
||||
handles[nCount++] = timer;
|
||||
|
||||
if (!settings->AsyncInput)
|
||||
handles[nCount++] = inputEvent;
|
||||
|
||||
/*
|
||||
* win8 and server 2k12 seem to have some timing issue/race condition
|
||||
* when a initial sync request is send to sync the keyboard indicators
|
||||
@ -1566,11 +1559,8 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
xf_keyboard_focus_in(xfc);
|
||||
}
|
||||
|
||||
nCount = (settings->AsyncInput) ? 1 : 2;
|
||||
|
||||
if (!settings->AsyncTransport)
|
||||
{
|
||||
DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], 64 - nCount);
|
||||
DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], ARRAYSIZE(handles) - nCount);
|
||||
|
||||
if (tmp == 0)
|
||||
{
|
||||
@ -1581,17 +1571,28 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
nCount += tmp;
|
||||
}
|
||||
|
||||
if (xfc->floatbar && xfc->fullscreen && !xfc->remote_app)
|
||||
xf_floatbar_hide_and_show(xfc);
|
||||
|
||||
waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
|
||||
|
||||
if (waitStatus == WAIT_FAILED)
|
||||
break;
|
||||
|
||||
if (!settings->AsyncTransport)
|
||||
{
|
||||
if (!freerdp_check_event_handles(context))
|
||||
{
|
||||
if (xf_auto_reconnect(instance))
|
||||
if (client_auto_reconnect_ex(instance, handle_window_events))
|
||||
continue;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Indicate an unsuccessful connection attempt if reconnect
|
||||
* did not succeed and no other error was specified.
|
||||
*/
|
||||
if (freerdp_error_info(instance) == 0)
|
||||
exit_code = XF_EXIT_CONN_FAILED;
|
||||
}
|
||||
|
||||
if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS)
|
||||
WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
|
||||
@ -1600,14 +1601,8 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
}
|
||||
}
|
||||
|
||||
if (!settings->AsyncInput)
|
||||
{
|
||||
if (!xf_process_x_events(instance))
|
||||
{
|
||||
WLog_INFO(TAG, "Closed from X11");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handle_window_events(instance))
|
||||
break;
|
||||
|
||||
if ((status != WAIT_TIMEOUT) && (waitStatus == WAIT_OBJECT_0))
|
||||
{
|
||||
@ -1623,8 +1618,19 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
}
|
||||
|
||||
if (!exit_code)
|
||||
{
|
||||
exit_code = freerdp_error_info(instance);
|
||||
|
||||
if (exit_code == XF_EXIT_DISCONNECT &&
|
||||
freerdp_get_disconnect_ultimatum(context) == Disconnect_Ultimatum_user_requested)
|
||||
{
|
||||
/* This situation might be limited to Windows XP. */
|
||||
WLog_INFO(TAG,
|
||||
"Error info says user did not initiate but disconnect ultimatum says they did; treat this as a user logoff");
|
||||
exit_code = XF_EXIT_LOGOFF;
|
||||
}
|
||||
}
|
||||
|
||||
disconnect:
|
||||
|
||||
if (timer)
|
||||
@ -1639,7 +1645,7 @@ end:
|
||||
DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
|
||||
{
|
||||
if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS
|
||||
&& reason <= XF_EXIT_AUTH_FAILURE))
|
||||
&& reason <= XF_EXIT_NEGO_FAILURE))
|
||||
return reason;
|
||||
/* License error set */
|
||||
else if (reason >= 0x100 && reason <= 0x10A)
|
||||
@ -1794,7 +1800,8 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
|
||||
xf_PanningChangeEventHandler);
|
||||
#endif
|
||||
xfc->UseXThreads = TRUE;
|
||||
//xfc->debug = TRUE;
|
||||
/* uncomment below if debugging to prevent keyboard grap */
|
||||
/* xfc->debug = TRUE; */
|
||||
|
||||
if (xfc->UseXThreads)
|
||||
{
|
||||
@ -1876,6 +1883,8 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
|
||||
"_NET_WM_WINDOW_TYPE_DIALOG", False);
|
||||
xfc->_NET_WM_WINDOW_TYPE_POPUP = XInternAtom(xfc->display,
|
||||
"_NET_WM_WINDOW_TYPE_POPUP", False);
|
||||
xfc->_NET_WM_WINDOW_TYPE_POPUP_MENU = XInternAtom(xfc->display,
|
||||
"_NET_WM_WINDOW_TYPE_POPUP_MENU", False);
|
||||
xfc->_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(xfc->display,
|
||||
"_NET_WM_WINDOW_TYPE_UTILITY", False);
|
||||
xfc->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU = XInternAtom(xfc->display,
|
||||
@ -1945,6 +1954,15 @@ static void xfreerdp_client_free(freerdp* instance, rdpContext* context)
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
PubSub_UnsubscribeTerminate(context->pubSub,
|
||||
xf_TerminateEventHandler);
|
||||
#ifdef WITH_XRENDER
|
||||
PubSub_UnsubscribeZoomingChange(context->pubSub,
|
||||
xf_ZoomingChangeEventHandler);
|
||||
PubSub_UnsubscribePanningChange(context->pubSub,
|
||||
xf_PanningChangeEventHandler);
|
||||
#endif
|
||||
|
||||
if (xfc->display)
|
||||
{
|
||||
XCloseDisplay(xfc->display);
|
||||
|
||||
@ -1358,7 +1358,7 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext*
|
||||
UINT32 dstFormatId;
|
||||
BOOL nullTerminated = FALSE;
|
||||
UINT32 size = formatDataResponse->dataLen;
|
||||
BYTE* data = formatDataResponse->requestedFormatData;
|
||||
const BYTE* data = formatDataResponse->requestedFormatData;
|
||||
xfClipboard* clipboard = (xfClipboard*) context->custom;
|
||||
xfContext* xfc = clipboard->xfc;
|
||||
|
||||
@ -1411,6 +1411,9 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext*
|
||||
srcFormatId = CF_DIB;
|
||||
dstFormatId = ClipboardGetFormatId(clipboard->system, "image/bmp");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1419,9 +1422,23 @@ static UINT xf_cliprdr_server_format_data_response(CliprdrClientContext*
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
if (SrcSize == 0)
|
||||
{
|
||||
WLog_INFO(TAG, "skipping, empty data detected!!!");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
DstSize = 0;
|
||||
pDstData = (BYTE*) ClipboardGetData(clipboard->system, dstFormatId, &DstSize);
|
||||
|
||||
if (!pDstData)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to get clipboard data in format %s [source format %s]",
|
||||
ClipboardGetFormatName(clipboard->system, dstFormatId),
|
||||
ClipboardGetFormatName(clipboard->system, srcFormatId));
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (nullTerminated)
|
||||
{
|
||||
while (DstSize > 0 && pDstData[DstSize - 1] == '\0')
|
||||
|
||||
@ -39,7 +39,8 @@
|
||||
|
||||
struct _xfDispContext
|
||||
{
|
||||
xfContext *xfc;
|
||||
xfContext* xfc;
|
||||
DispClientContext* disp;
|
||||
BOOL haveXRandr;
|
||||
int eventBase, errorBase;
|
||||
int lastSentWidth, lastSentHeight;
|
||||
@ -47,37 +48,107 @@ struct _xfDispContext
|
||||
int targetWidth, targetHeight;
|
||||
BOOL activated;
|
||||
BOOL waitingResize;
|
||||
BOOL fullscreen;
|
||||
UINT16 lastSentDesktopOrientation;
|
||||
UINT32 lastSentDesktopScaleFactor;
|
||||
UINT32 lastSentDeviceScaleFactor;
|
||||
};
|
||||
|
||||
static UINT xf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, int nmonitors);
|
||||
|
||||
static BOOL xf_disp_sendResize(xfDispContext *xfDisp, int width, int height)
|
||||
static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT layout;
|
||||
xfContext *xfc = xfDisp->xfc;
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
rdpSettings* settings = xfDisp->xfc->context.settings;
|
||||
|
||||
xfDisp->lastSentDate = GetTickCount64();
|
||||
xfDisp->lastSentWidth = width;
|
||||
xfDisp->lastSentHeight = height;
|
||||
xfDisp->waitingResize = TRUE;
|
||||
if (xfDisp->lastSentWidth != xfDisp->targetWidth)
|
||||
return TRUE;
|
||||
|
||||
layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
|
||||
layout.Top = layout.Left = 0;
|
||||
layout.Width = width;
|
||||
layout.Height = height;
|
||||
layout.Orientation = ORIENTATION_LANDSCAPE;
|
||||
layout.DesktopScaleFactor = settings->DesktopScaleFactor;
|
||||
layout.DeviceScaleFactor = settings->DeviceScaleFactor;
|
||||
layout.PhysicalWidth = width;
|
||||
layout.PhysicalHeight = height;
|
||||
if (xfDisp->lastSentHeight != xfDisp->targetHeight)
|
||||
return TRUE;
|
||||
|
||||
return xfc->disp->SendMonitorLayout(xfc->disp, 1, &layout) == CHANNEL_RC_OK;
|
||||
if (xfDisp->lastSentDesktopOrientation != settings->DesktopOrientation)
|
||||
return TRUE;
|
||||
|
||||
if (xfDisp->lastSentDesktopScaleFactor != settings->DesktopScaleFactor)
|
||||
return TRUE;
|
||||
|
||||
if (xfDisp->lastSentDeviceScaleFactor != settings->DeviceScaleFactor)
|
||||
return TRUE;
|
||||
|
||||
if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static BOOL xf_disp_set_window_resizable(xfDispContext *xfDisp)
|
||||
static BOOL xf_update_last_sent(xfDispContext* xfDisp)
|
||||
{
|
||||
XSizeHints *size_hints;
|
||||
rdpSettings* settings = xfDisp->xfc->context.settings;
|
||||
xfDisp->lastSentWidth = xfDisp->targetWidth;
|
||||
xfDisp->lastSentHeight = xfDisp->targetHeight;
|
||||
xfDisp->lastSentDesktopOrientation = settings->DesktopOrientation;
|
||||
xfDisp->lastSentDesktopScaleFactor = settings->DesktopScaleFactor;
|
||||
xfDisp->lastSentDeviceScaleFactor = settings->DeviceScaleFactor;
|
||||
xfDisp->fullscreen = xfDisp->xfc->fullscreen;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT layout;
|
||||
xfContext* xfc;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!xfDisp || !xfDisp->xfc)
|
||||
return FALSE;
|
||||
|
||||
xfc = xfDisp->xfc;
|
||||
settings = xfc->context.settings;
|
||||
|
||||
if (!settings)
|
||||
return FALSE;
|
||||
|
||||
if (!xfDisp->activated || !xfDisp->disp)
|
||||
return TRUE;
|
||||
|
||||
if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return TRUE;
|
||||
|
||||
xfDisp->lastSentDate = GetTickCount64();
|
||||
|
||||
if (!xf_disp_settings_changed(xfDisp))
|
||||
return TRUE;
|
||||
|
||||
if (xfc->fullscreen && (settings->MonitorCount > 0))
|
||||
{
|
||||
if (xf_disp_sendLayout(xfDisp->disp, settings->MonitorDefArray,
|
||||
settings->MonitorCount) != CHANNEL_RC_OK)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xfDisp->waitingResize = TRUE;
|
||||
layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
|
||||
layout.Top = layout.Left = 0;
|
||||
layout.Width = xfDisp->targetWidth;
|
||||
layout.Height = xfDisp->targetHeight;
|
||||
layout.Orientation = settings->DesktopOrientation;
|
||||
layout.DesktopScaleFactor = settings->DesktopScaleFactor;
|
||||
layout.DeviceScaleFactor = settings->DeviceScaleFactor;
|
||||
layout.PhysicalWidth = xfDisp->targetWidth;
|
||||
layout.PhysicalHeight = xfDisp->targetHeight;
|
||||
|
||||
if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
|
||||
&layout) != CHANNEL_RC_OK)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return xf_update_last_sent(xfDisp);
|
||||
}
|
||||
|
||||
static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
|
||||
{
|
||||
XSizeHints* size_hints;
|
||||
|
||||
if (!(size_hints = XAllocSizeHints()))
|
||||
return FALSE;
|
||||
@ -86,18 +157,44 @@ static BOOL xf_disp_set_window_resizable(xfDispContext *xfDisp)
|
||||
size_hints->win_gravity = NorthWestGravity;
|
||||
size_hints->min_width = size_hints->min_height = 320;
|
||||
size_hints->max_width = size_hints->max_height = 8192;
|
||||
|
||||
if (xfDisp->xfc->window)
|
||||
XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
|
||||
|
||||
XFree(size_hints);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
|
||||
rdpSettings** ppSettings)
|
||||
{
|
||||
xfContext* xfc;
|
||||
|
||||
if (!context)
|
||||
return FALSE;
|
||||
|
||||
xfc = (xfContext*)context;
|
||||
|
||||
if (!(xfc->xfDisp))
|
||||
return FALSE;
|
||||
|
||||
if (!xfc->context.settings)
|
||||
return FALSE;
|
||||
|
||||
*ppXfc = xfc;
|
||||
*ppXfDisp = xfc->xfDisp;
|
||||
*ppSettings = xfc->context.settings;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void xf_disp_OnActivated(void* context, ActivatedEventArgs* e)
|
||||
{
|
||||
xfContext *xfc = (xfContext *)context;
|
||||
xfDispContext *xfDisp = xfc->xfDisp;
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
xfContext* xfc;
|
||||
xfDispContext* xfDisp;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||
return;
|
||||
|
||||
xfDisp->waitingResize = FALSE;
|
||||
|
||||
@ -108,102 +205,96 @@ static void xf_disp_OnActivated(void* context, ActivatedEventArgs* e)
|
||||
if (e->firstActivation)
|
||||
return;
|
||||
|
||||
/* if a resize has been done recently don't do anything and let the timer
|
||||
* perform the resize */
|
||||
if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return;
|
||||
|
||||
if ((xfDisp->lastSentWidth != xfDisp->targetWidth) || (xfDisp->lastSentHeight != xfDisp->targetHeight))
|
||||
{
|
||||
WLog_DBG(TAG, "performing delayed resize to %dx%d", xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
xf_disp_sendResize(xfDisp, xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
}
|
||||
xf_disp_sendResize(xfDisp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void xf_disp_OnGraphicsReset(void* context, GraphicsResetEventArgs* e)
|
||||
{
|
||||
xfContext *xfc = (xfContext *)context;
|
||||
xfDispContext *xfDisp = xfc->xfDisp;
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
xfContext* xfc;
|
||||
xfDispContext* xfDisp;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||
return;
|
||||
|
||||
xfDisp->waitingResize = FALSE;
|
||||
|
||||
if (xfDisp->activated && !settings->Fullscreen)
|
||||
{
|
||||
xf_disp_set_window_resizable(xfDisp);
|
||||
|
||||
/* if a resize has been done recently don't do anything and let the timer
|
||||
* perform the resize */
|
||||
if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return;
|
||||
|
||||
if ((xfDisp->lastSentWidth != xfDisp->targetWidth) || (xfDisp->lastSentHeight != xfDisp->targetHeight))
|
||||
{
|
||||
WLog_DBG(TAG, "performing delayed resize to %dx%d", xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
xf_disp_sendResize(xfDisp, xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
}
|
||||
xf_disp_sendResize(xfDisp);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_disp_OnTimer(void* context, TimerEventArgs* e)
|
||||
{
|
||||
xfContext *xfc = (xfContext *)context;
|
||||
xfDispContext *xfDisp = xfc->xfDisp;
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
xfContext* xfc;
|
||||
xfDispContext* xfDisp;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||
return;
|
||||
|
||||
if (!xfDisp->activated || settings->Fullscreen)
|
||||
return;
|
||||
|
||||
if (e->now - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return;
|
||||
|
||||
if ((xfDisp->lastSentWidth != xfDisp->targetWidth) || (xfDisp->lastSentHeight != xfDisp->targetHeight))
|
||||
{
|
||||
WLog_DBG(TAG, "timer performing delayed resize to %dx%d", xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
xf_disp_sendResize(xfDisp, xfDisp->targetWidth, xfDisp->targetHeight);
|
||||
}
|
||||
xf_disp_sendResize(xfDisp);
|
||||
}
|
||||
|
||||
xfDispContext *xf_disp_new(xfContext* xfc)
|
||||
xfDispContext* xf_disp_new(xfContext* xfc)
|
||||
{
|
||||
xfDispContext *ret = calloc(1, sizeof(xfDispContext));
|
||||
xfDispContext* ret;
|
||||
|
||||
if (!xfc || !xfc->context.settings || !xfc->context.pubSub)
|
||||
return NULL;
|
||||
|
||||
ret = calloc(1, sizeof(xfDispContext));
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->xfc = xfc;
|
||||
#ifdef USABLE_XRANDR
|
||||
|
||||
if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
|
||||
{
|
||||
ret->haveXRandr = TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
ret->lastSentWidth = ret->targetWidth = xfc->context.settings->DesktopWidth;
|
||||
ret->lastSentHeight = ret->targetHeight = xfc->context.settings->DesktopHeight;
|
||||
|
||||
PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated);
|
||||
PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
||||
PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xf_disp_free(xfDispContext *disp)
|
||||
void xf_disp_free(xfDispContext* disp)
|
||||
{
|
||||
PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
|
||||
PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
|
||||
if (!disp)
|
||||
return;
|
||||
|
||||
if (disp->xfc)
|
||||
{
|
||||
PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
|
||||
PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset);
|
||||
PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
|
||||
}
|
||||
|
||||
free(disp);
|
||||
}
|
||||
|
||||
static UINT xf_disp_sendLayout(DispClientContext *disp, rdpMonitor *monitors, int nmonitors)
|
||||
UINT xf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, int nmonitors)
|
||||
{
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT *layouts;
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT* layouts;
|
||||
int i;
|
||||
xfDispContext *xfDisp = (xfDispContext *)disp->custom;
|
||||
rdpSettings *settings = xfDisp->xfc->context.settings;
|
||||
|
||||
xfDispContext* xfDisp = (xfDispContext*)disp->custom;
|
||||
rdpSettings* settings = xfDisp->xfc->context.settings;
|
||||
layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
|
||||
|
||||
if (!layouts)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
@ -218,89 +309,101 @@ static UINT xf_disp_sendLayout(DispClientContext *disp, rdpMonitor *monitors, in
|
||||
layouts[i].PhysicalWidth = monitors[i].attributes.physicalWidth;
|
||||
layouts[i].PhysicalHeight = monitors[i].attributes.physicalHeight;
|
||||
|
||||
switch(monitors[i].attributes.orientation)
|
||||
switch (monitors[i].attributes.orientation)
|
||||
{
|
||||
case 90:
|
||||
layouts[i].Orientation = ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
case 180:
|
||||
layouts[i].Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case 270:
|
||||
layouts[i].Orientation = ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
/* MS-RDPEDISP - 2.2.2.2.1:
|
||||
* Orientation (4 bytes): A 32-bit unsigned integer that specifies the
|
||||
* orientation of the monitor in degrees. Valid values are 0, 90, 180
|
||||
* or 270
|
||||
*
|
||||
* So we default to ORIENTATION_LANDSCAPE
|
||||
*/
|
||||
layouts[i].Orientation = ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case 90:
|
||||
layouts[i].Orientation = ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
|
||||
case 180:
|
||||
layouts[i].Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
|
||||
case 270:
|
||||
layouts[i].Orientation = ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
/* MS-RDPEDISP - 2.2.2.2.1:
|
||||
* Orientation (4 bytes): A 32-bit unsigned integer that specifies the
|
||||
* orientation of the monitor in degrees. Valid values are 0, 90, 180
|
||||
* or 270
|
||||
*
|
||||
* So we default to ORIENTATION_LANDSCAPE
|
||||
*/
|
||||
layouts[i].Orientation = ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
}
|
||||
|
||||
layouts[i].DesktopScaleFactor = settings->DesktopScaleFactor;
|
||||
layouts[i].DeviceScaleFactor = settings->DeviceScaleFactor;
|
||||
}
|
||||
|
||||
ret = disp->SendMonitorLayout(disp, nmonitors, layouts);
|
||||
|
||||
ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
|
||||
free(layouts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event)
|
||||
BOOL xf_disp_handle_xevent(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfDispContext *xfDisp = xfc->xfDisp;
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
xfDispContext* xfDisp;
|
||||
rdpSettings* settings;
|
||||
UINT32 maxWidth, maxHeight;
|
||||
|
||||
if (!xfDisp->haveXRandr)
|
||||
if (!xfc || !event)
|
||||
return FALSE;
|
||||
|
||||
xfDisp = xfc->xfDisp;
|
||||
|
||||
if (!xfDisp)
|
||||
return FALSE;
|
||||
|
||||
settings = xfc->context.settings;
|
||||
|
||||
if (!settings)
|
||||
return FALSE;
|
||||
|
||||
if (!xfDisp->haveXRandr || !xfDisp->disp)
|
||||
return TRUE;
|
||||
|
||||
#ifdef USABLE_XRANDR
|
||||
|
||||
if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
|
||||
return TRUE;
|
||||
|
||||
#endif
|
||||
|
||||
xf_detect_monitors(xfc, &maxWidth, &maxHeight);
|
||||
return xf_disp_sendLayout(xfc->disp, settings->MonitorDefArray, settings->MonitorCount) == CHANNEL_RC_OK;
|
||||
return xf_disp_sendLayout(xfDisp->disp, settings->MonitorDefArray,
|
||||
settings->MonitorCount) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
|
||||
BOOL xf_disp_handle_configureNotify(xfContext *xfc, int width, int height)
|
||||
BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height)
|
||||
{
|
||||
xfDispContext *xfDisp = xfc->xfDisp;
|
||||
xfDispContext* xfDisp;
|
||||
|
||||
if (xfDisp->lastSentWidth == width && xfDisp->lastSentHeight == height)
|
||||
return TRUE;
|
||||
if (!xfc)
|
||||
return FALSE;
|
||||
|
||||
if (xfDisp->waitingResize || !xfDisp->activated ||
|
||||
(GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY))
|
||||
{
|
||||
WLog_DBG(TAG, "delaying resize to %dx%d", width, height);
|
||||
xfDisp->targetWidth = width;
|
||||
xfDisp->targetHeight = height;
|
||||
return TRUE;
|
||||
}
|
||||
xfDisp = xfc->xfDisp;
|
||||
|
||||
WLog_DBG(TAG, "resizing on ConfigureNotify to %dx%d", width, height);
|
||||
return xf_disp_sendResize(xfDisp, width, height);
|
||||
if (!xfDisp)
|
||||
return FALSE;
|
||||
|
||||
xfDisp->targetWidth = width;
|
||||
xfDisp->targetHeight = height;
|
||||
return xf_disp_sendResize(xfDisp);
|
||||
}
|
||||
|
||||
|
||||
UINT xf_DisplayControlCaps(DispClientContext *disp, UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
|
||||
static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
|
||||
UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
|
||||
{
|
||||
/* we're called only if dynamic resolution update is activated */
|
||||
xfDispContext *xfDisp = (xfDispContext *)disp->custom;
|
||||
rdpSettings *settings = xfDisp->xfc->context.settings;
|
||||
|
||||
WLog_DBG(TAG, "DisplayControlCapsPdu: MaxNumMonitors: %"PRIu32" MaxMonitorAreaFactorA: %"PRIu32" MaxMonitorAreaFactorB: %"PRIu32"",
|
||||
maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
|
||||
|
||||
xfDispContext* xfDisp = (xfDispContext*)disp->custom;
|
||||
rdpSettings* settings = xfDisp->xfc->context.settings;
|
||||
WLog_DBG(TAG,
|
||||
"DisplayControlCapsPdu: MaxNumMonitors: %"PRIu32" MaxMonitorAreaFactorA: %"PRIu32" MaxMonitorAreaFactorB: %"PRIu32"",
|
||||
maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
|
||||
xfDisp->activated = TRUE;
|
||||
|
||||
if (settings->Fullscreen)
|
||||
@ -310,24 +413,44 @@ UINT xf_DisplayControlCaps(DispClientContext *disp, UINT32 maxNumMonitors, UINT3
|
||||
return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp)
|
||||
BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
|
||||
{
|
||||
rdpSettings *settings = xfc->context.settings;
|
||||
xfc->disp = disp;
|
||||
disp->custom = (void*) xfc->xfDisp;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!xfDisp || !xfDisp->xfc || !disp)
|
||||
return FALSE;
|
||||
|
||||
settings = xfDisp->xfc->context.settings;
|
||||
|
||||
if (!settings)
|
||||
return FALSE;
|
||||
|
||||
xfDisp->disp = disp;
|
||||
disp->custom = (void*) xfDisp;
|
||||
|
||||
if (settings->DynamicResolutionUpdate)
|
||||
{
|
||||
disp->DisplayControlCaps = xf_DisplayControlCaps;
|
||||
#ifdef USABLE_XRANDR
|
||||
|
||||
if (settings->Fullscreen)
|
||||
{
|
||||
/* ask X11 to notify us of screen changes */
|
||||
XRRSelectInput(xfc->display, DefaultRootWindow(xfc->display), RRScreenChangeNotifyMask);
|
||||
XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
|
||||
RRScreenChangeNotifyMask);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
|
||||
{
|
||||
if (!xfDisp || !disp)
|
||||
return FALSE;
|
||||
|
||||
xfDisp->disp = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -25,12 +25,13 @@
|
||||
#include "xf_client.h"
|
||||
#include "xfreerdp.h"
|
||||
|
||||
FREERDP_API BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp);
|
||||
FREERDP_API BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp);
|
||||
FREERDP_API BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp);
|
||||
|
||||
xfDispContext *xf_disp_new(xfContext* xfc);
|
||||
void xf_disp_free(xfDispContext *disp);
|
||||
BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event);
|
||||
BOOL xf_disp_handle_configureNotify(xfContext *xfc, int width, int height);
|
||||
void xf_disp_resized(xfDispContext *disp);
|
||||
xfDispContext* xf_disp_new(xfContext* xfc);
|
||||
void xf_disp_free(xfDispContext* disp);
|
||||
BOOL xf_disp_handle_xevent(xfContext* xfc, XEvent* event);
|
||||
BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height);
|
||||
void xf_disp_resized(xfDispContext* disp);
|
||||
|
||||
#endif /* FREERDP_CLIENT_X11_DISP_H */
|
||||
|
||||
@ -183,6 +183,7 @@ BOOL xf_event_action_script_init(xfContext* xfc)
|
||||
|
||||
if (!xevent || ArrayList_Add(xfc->xevents, xevent) < 0)
|
||||
{
|
||||
pclose(actionScript);
|
||||
ArrayList_Free(xfc->xevents);
|
||||
xfc->xevents = NULL;
|
||||
return FALSE;
|
||||
@ -366,6 +367,9 @@ static BOOL xf_event_MotionNotify(xfContext* xfc, XEvent* event, BOOL app)
|
||||
if (xfc->use_xinput)
|
||||
return TRUE;
|
||||
|
||||
if(xfc->floatbar && !(app))
|
||||
xf_floatbar_set_root_y(xfc, event->xmotion.y);
|
||||
|
||||
return xf_generic_MotionNotify(xfc, event->xmotion.x, event->xmotion.y,
|
||||
event->xmotion.state, event->xmotion.window, app);
|
||||
}
|
||||
@ -828,6 +832,15 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, XEvent* event, BOOL app)
|
||||
unsigned long nitems;
|
||||
unsigned long bytes;
|
||||
unsigned char* prop;
|
||||
xfAppWindow* appWindow = NULL;
|
||||
|
||||
if (app)
|
||||
{
|
||||
appWindow = xf_AppWindowFromX11Window(xfc, event->xany.window);
|
||||
|
||||
if (!appWindow)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((Atom) event->xproperty.atom == xfc->_NET_WM_STATE)
|
||||
{
|
||||
@ -875,12 +888,6 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, XEvent* event, BOOL app)
|
||||
|
||||
if (app)
|
||||
{
|
||||
xfAppWindow* appWindow;
|
||||
appWindow = xf_AppWindowFromX11Window(xfc, event->xany.window);
|
||||
|
||||
if (!appWindow)
|
||||
return TRUE;
|
||||
|
||||
if (maxVert && maxHorz && !minimized
|
||||
&& (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED))
|
||||
{
|
||||
@ -1011,6 +1018,12 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
if (xfc->floatbar && xf_floatbar_check_event(xfc, event))
|
||||
{
|
||||
xf_floatbar_event_process(xfc, event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
xf_event_execute_action_script(xfc, event);
|
||||
|
||||
if (event->type != MotionNotify)
|
||||
@ -1099,7 +1112,7 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
|
||||
break;
|
||||
|
||||
default:
|
||||
if (settings->SupportDisplayControl && xfc->xfDisp)
|
||||
if (settings->SupportDisplayControl)
|
||||
xf_disp_handle_xevent(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
722
client/X11/xf_floatbar.c
Normal file
722
client/X11/xf_floatbar.c
Normal file
@ -0,0 +1,722 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* X11 Windows
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");n
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/cursorfont.h>
|
||||
|
||||
#include "xf_floatbar.h"
|
||||
#include "resource/close.xbm"
|
||||
#include "resource/lock.xbm"
|
||||
#include "resource/unlock.xbm"
|
||||
#include "resource/minimize.xbm"
|
||||
#include "resource/restore.xbm"
|
||||
|
||||
#define TAG CLIENT_TAG("x11")
|
||||
|
||||
#define FLOATBAR_HEIGHT 26
|
||||
#define FLOATBAR_DEFAULT_WIDTH 576
|
||||
#define FLOATBAR_MIN_WIDTH 200
|
||||
#define FLOATBAR_BORDER 24
|
||||
#define FLOATBAR_BUTTON_WIDTH 24
|
||||
#define FLOATBAR_COLOR_BACKGROUND "RGB:31/6c/a9"
|
||||
#define FLOATBAR_COLOR_BORDER "RGB:75/9a/c8"
|
||||
#define FLOATBAR_COLOR_FOREGROUND "RGB:FF/FF/FF"
|
||||
|
||||
#ifdef WITH_DEBUG_X11
|
||||
#define DEBUG_X11(...) WLog_DBG(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_X11(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define XF_FLOATBAR_MODE_NONE 0
|
||||
#define XF_FLOATBAR_MODE_DRAGGING 1
|
||||
#define XF_FLOATBAR_MODE_RESIZE_LEFT 2
|
||||
#define XF_FLOATBAR_MODE_RESIZE_RIGHT 3
|
||||
|
||||
|
||||
#define XF_FLOATBAR_BUTTON_CLOSE 1
|
||||
#define XF_FLOATBAR_BUTTON_RESTORE 2
|
||||
#define XF_FLOATBAR_BUTTON_MINIMIZE 3
|
||||
#define XF_FLOATBAR_BUTTON_LOCKED 4
|
||||
|
||||
typedef struct xf_floatbar_button xfFloatbarButton;
|
||||
|
||||
struct xf_floatbar
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
int mode;
|
||||
int last_motion_x_root;
|
||||
int last_motion_y_root;
|
||||
bool locked;
|
||||
xfFloatbarButton* buttons[4];
|
||||
Window handle;
|
||||
};
|
||||
|
||||
struct xf_floatbar_button
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int type;
|
||||
bool focus;
|
||||
bool clicked;
|
||||
OnClick onclick;
|
||||
Window handle;
|
||||
};
|
||||
|
||||
static void xf_floatbar_button_onclick_close(xfContext* xfc)
|
||||
{
|
||||
ExitProcess(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_onclick_minimize(xfContext* xfc)
|
||||
{
|
||||
xf_SetWindowMinimized(xfc, xfc->window);
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_onclick_restore(xfContext* xfc)
|
||||
{
|
||||
xf_toggle_fullscreen(xfc);
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_onclick_locked(xfContext* xfc)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
floatbar->locked = (floatbar->locked) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
void xf_floatbar_set_root_y(xfContext* xfc, int y)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
floatbar->last_motion_y_root = y;
|
||||
}
|
||||
|
||||
void xf_floatbar_hide_and_show(xfContext* xfc)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
if (!floatbar->locked)
|
||||
{
|
||||
if ((floatbar->mode == 0) && (floatbar->last_motion_y_root > 10) &&
|
||||
(floatbar->y > (FLOATBAR_HEIGHT * -1)))
|
||||
{
|
||||
floatbar->y = floatbar->y - 1;
|
||||
XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
|
||||
}
|
||||
else if (floatbar->y < 0 && (floatbar->last_motion_y_root < 10))
|
||||
{
|
||||
floatbar->y = floatbar->y + 1;
|
||||
XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xf_floatbar_toggle_visibility(xfContext* xfc, bool visible)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
int i, size;
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
XMapWindow(xfc->display, floatbar->handle);
|
||||
size = ARRAYSIZE(floatbar->buttons);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
XMapWindow(xfc->display, floatbar->buttons[i]->handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
XUnmapSubwindows(xfc->display, floatbar->handle);
|
||||
XUnmapWindow(xfc->display, floatbar->handle);
|
||||
}
|
||||
}
|
||||
|
||||
static xfFloatbarButton* xf_floatbar_new_button(xfContext* xfc, xfFloatbar* floatbar, int type)
|
||||
{
|
||||
xfFloatbarButton* button;
|
||||
button = (xfFloatbarButton*) calloc(1, sizeof(xfFloatbarButton));
|
||||
button->type = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case XF_FLOATBAR_BUTTON_CLOSE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
|
||||
button->onclick = xf_floatbar_button_onclick_close;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_RESTORE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
|
||||
button->onclick = xf_floatbar_button_onclick_restore;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_MINIMIZE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
|
||||
button->onclick = xf_floatbar_button_onclick_minimize;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_LOCKED:
|
||||
button->x = FLOATBAR_BORDER;
|
||||
button->onclick = xf_floatbar_button_onclick_locked;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
button->y = 0;
|
||||
button->focus = FALSE;
|
||||
button->handle = XCreateWindow(xfc->display, floatbar->handle, button->x, 0, FLOATBAR_BUTTON_WIDTH,
|
||||
FLOATBAR_BUTTON_WIDTH, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
|
||||
XSelectInput(xfc->display, button->handle, ExposureMask | ButtonPressMask | ButtonReleaseMask |
|
||||
FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask);
|
||||
return button;
|
||||
}
|
||||
|
||||
xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
XWindowAttributes attr;
|
||||
int i, width;
|
||||
if (!xfc)
|
||||
return NULL;
|
||||
|
||||
floatbar = (xfFloatbar*) calloc(1, sizeof(xfFloatbar));
|
||||
floatbar->locked = TRUE;
|
||||
XGetWindowAttributes(xfc->display, window, &attr);
|
||||
|
||||
for (i = 0; i < xfc->vscreen.nmonitors; i++)
|
||||
{
|
||||
if (attr.x >= xfc->vscreen.monitors[i].area.left && attr.x <= xfc->vscreen.monitors[i].area.right)
|
||||
{
|
||||
width = xfc->vscreen.monitors[i].area.right - xfc->vscreen.monitors[i].area.left;
|
||||
floatbar->x = width / 2 + xfc->vscreen.monitors[i].area.left - FLOATBAR_DEFAULT_WIDTH / 2;
|
||||
}
|
||||
}
|
||||
|
||||
floatbar->y = 0;
|
||||
floatbar->handle = XCreateWindow(xfc->display, window, floatbar->x, 0, FLOATBAR_DEFAULT_WIDTH,
|
||||
FLOATBAR_HEIGHT, 0,
|
||||
CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
|
||||
floatbar->width = FLOATBAR_DEFAULT_WIDTH;
|
||||
floatbar->height = FLOATBAR_HEIGHT;
|
||||
floatbar->mode = XF_FLOATBAR_MODE_NONE;
|
||||
floatbar->buttons[0] = xf_floatbar_new_button(xfc, floatbar, XF_FLOATBAR_BUTTON_CLOSE);
|
||||
floatbar->buttons[1] = xf_floatbar_new_button(xfc, floatbar, XF_FLOATBAR_BUTTON_RESTORE);
|
||||
floatbar->buttons[2] = xf_floatbar_new_button(xfc, floatbar, XF_FLOATBAR_BUTTON_MINIMIZE);
|
||||
floatbar->buttons[3] = xf_floatbar_new_button(xfc, floatbar, XF_FLOATBAR_BUTTON_LOCKED);
|
||||
XSelectInput(xfc->display, floatbar->handle, ExposureMask | ButtonPressMask | ButtonReleaseMask |
|
||||
PointerMotionMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask |
|
||||
PropertyChangeMask);
|
||||
return floatbar;
|
||||
}
|
||||
|
||||
static unsigned long xf_floatbar_get_color(xfContext* xfc, char* rgb_value)
|
||||
{
|
||||
Colormap cmap;
|
||||
XColor color;
|
||||
cmap = DefaultColormap(xfc->display, XDefaultScreen(xfc->display));
|
||||
XParseColor(xfc->display, cmap, rgb_value, &color);
|
||||
XAllocColor(xfc->display, cmap, &color);
|
||||
XFreeColormap(xfc->display, cmap);
|
||||
return color.pixel;
|
||||
}
|
||||
|
||||
static void xf_floatbar_event_expose(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
GC gc, shape_gc;
|
||||
Pixmap pmap;
|
||||
XPoint shape[5], border[5];
|
||||
xfFloatbar* floatbar;
|
||||
int len;
|
||||
floatbar = xfc->window->floatbar;
|
||||
/* create the pixmap that we'll use for shaping the window */
|
||||
pmap = XCreatePixmap(xfc->display, floatbar->handle, floatbar->width, floatbar->height, 1);
|
||||
gc = XCreateGC(xfc->display, floatbar->handle, 0, 0);
|
||||
shape_gc = XCreateGC(xfc->display, pmap, 0, 0);
|
||||
/* points for drawing the floatbar */
|
||||
shape[0].x = 0;
|
||||
shape[0].y = 0;
|
||||
shape[1].x = floatbar->width;
|
||||
shape[1].y = 0;
|
||||
shape[2].x = shape[1].x - FLOATBAR_BORDER;
|
||||
shape[2].y = FLOATBAR_HEIGHT;
|
||||
shape[3].x = shape[0].x + FLOATBAR_BORDER;
|
||||
shape[3].y = FLOATBAR_HEIGHT;
|
||||
shape[4].x = shape[0].x;
|
||||
shape[4].y = shape[0].y;
|
||||
/* points for drawing the border of the floatbar */
|
||||
border[0].x = shape[0].x;
|
||||
border[0].y = shape[0].y - 1;
|
||||
border[1].x = shape[1].x - 1;
|
||||
border[1].y = shape[1].y - 1;
|
||||
border[2].x = shape[2].x;
|
||||
border[2].y = shape[2].y - 1;
|
||||
border[3].x = shape[3].x - 1;
|
||||
border[3].y = shape[3].y - 1;
|
||||
border[4].x = border[0].x;
|
||||
border[4].y = border[0].y;
|
||||
/* Fill all pixels with 0 */
|
||||
XSetForeground(xfc->display, shape_gc, 0);
|
||||
XFillRectangle(xfc->display, pmap, shape_gc, 0, 0, floatbar->width,
|
||||
floatbar->height);
|
||||
/* Fill all pixels which should be shown with 1 */
|
||||
XSetForeground(xfc->display, shape_gc, 1);
|
||||
XFillPolygon(xfc->display, pmap, shape_gc, shape, 5, 0, CoordModeOrigin);
|
||||
XShapeCombineMask(xfc->display, floatbar->handle, ShapeBounding, 0, 0, pmap, ShapeSet);
|
||||
/* draw the float bar */
|
||||
XSetForeground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_BACKGROUND));
|
||||
XFillPolygon(xfc->display, floatbar->handle, gc, shape, 4, 0, CoordModeOrigin);
|
||||
/* draw an border for the floatbar */
|
||||
XSetForeground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_BORDER));
|
||||
XDrawLines(xfc->display, floatbar->handle, gc, border, 5, CoordModeOrigin);
|
||||
/* draw the host name connected to */
|
||||
len = strlen(xfc->context.settings->ServerHostname);
|
||||
XSetForeground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_FOREGROUND));
|
||||
XDrawString(xfc->display, floatbar->handle, gc, floatbar->width / 2 - len * 2, 15,
|
||||
xfc->context.settings->ServerHostname, len);
|
||||
XFreeGC(xfc->display, gc);
|
||||
XFreeGC(xfc->display, shape_gc);
|
||||
}
|
||||
|
||||
static xfFloatbarButton* xf_floatbar_get_button(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
int i, size;
|
||||
size = ARRAYSIZE(floatbar->buttons);
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (floatbar->buttons[i]->handle == event->xany.window)
|
||||
{
|
||||
return floatbar->buttons[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_update_positon(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
xfFloatbarButton* button;
|
||||
int i, size;
|
||||
floatbar = xfc->window->floatbar;
|
||||
size = ARRAYSIZE(floatbar->buttons);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
button = floatbar->buttons[i];
|
||||
|
||||
switch (button->type)
|
||||
{
|
||||
case XF_FLOATBAR_BUTTON_CLOSE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_RESTORE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_MINIMIZE:
|
||||
button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
XMoveWindow(xfc->display, button->handle, button->x, button->y);
|
||||
xf_floatbar_event_expose(xfc, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_event_expose(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
xfFloatbarButton* button;
|
||||
static unsigned char* bits;
|
||||
GC gc;
|
||||
Pixmap pattern;
|
||||
button = xf_floatbar_get_button(xfc, event);
|
||||
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
gc = XCreateGC(xfc->display, button->handle, 0, 0);
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
switch (button->type)
|
||||
{
|
||||
case XF_FLOATBAR_BUTTON_CLOSE:
|
||||
bits = close_bits;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_RESTORE:
|
||||
bits = restore_bits;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_MINIMIZE:
|
||||
bits = minimize_bits;
|
||||
break;
|
||||
|
||||
case XF_FLOATBAR_BUTTON_LOCKED:
|
||||
if (floatbar->locked)
|
||||
bits = lock_bits;
|
||||
else
|
||||
bits = unlock_bits;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pattern = XCreateBitmapFromData(xfc->display, button->handle, (const char*)bits,
|
||||
FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH);
|
||||
|
||||
if (!(button->focus))
|
||||
XSetForeground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_BACKGROUND));
|
||||
else
|
||||
XSetForeground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_BORDER));
|
||||
|
||||
XSetBackground(xfc->display, gc, xf_floatbar_get_color(xfc, FLOATBAR_COLOR_FOREGROUND));
|
||||
XCopyPlane(xfc->display, pattern, button->handle, gc, 0, 0, FLOATBAR_BUTTON_WIDTH,
|
||||
FLOATBAR_BUTTON_WIDTH, 0, 0, 1);
|
||||
XFreePixmap(xfc->display, pattern);
|
||||
XFreeGC(xfc->display, gc);
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_event_buttonpress(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbarButton* button;
|
||||
button = xf_floatbar_get_button(xfc, event);
|
||||
|
||||
if (button)
|
||||
button->clicked = TRUE;
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_event_buttonrelease(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbarButton* button;
|
||||
button = xf_floatbar_get_button(xfc, event);
|
||||
|
||||
if (button)
|
||||
{
|
||||
if (button->clicked)
|
||||
button->onclick(xfc);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_event_buttonpress(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
switch (event->xbutton.button)
|
||||
{
|
||||
case Button1:
|
||||
if (event->xmotion.x <= FLOATBAR_BORDER)
|
||||
floatbar->mode = XF_FLOATBAR_MODE_RESIZE_LEFT;
|
||||
else if (event->xmotion.x >= (floatbar->width - FLOATBAR_BORDER))
|
||||
floatbar->mode = XF_FLOATBAR_MODE_RESIZE_RIGHT;
|
||||
else
|
||||
floatbar->mode = XF_FLOATBAR_MODE_DRAGGING;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_event_buttonrelease(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
switch (event->xbutton.button)
|
||||
{
|
||||
case Button1:
|
||||
xfc->window->floatbar->mode = XF_FLOATBAR_MODE_NONE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_resize(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
int x, width, movement;
|
||||
/* calculate movement which happened on the root window */
|
||||
movement = event->xmotion.x_root - floatbar->last_motion_x_root;
|
||||
|
||||
/* set x and width depending if movement happens on the left or right */
|
||||
if (floatbar->mode == XF_FLOATBAR_MODE_RESIZE_LEFT)
|
||||
{
|
||||
x = floatbar->x + movement;
|
||||
width = floatbar->width + movement * -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = floatbar->x;
|
||||
width = floatbar->width + movement;
|
||||
}
|
||||
|
||||
/* only resize and move window if still above minimum width */
|
||||
if (FLOATBAR_MIN_WIDTH < width)
|
||||
{
|
||||
XMoveResizeWindow(xfc->display, floatbar->handle, x, 0, width, floatbar->height);
|
||||
floatbar->x = x;
|
||||
floatbar->width = width;
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_dragging(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
floatbar = xfc->window->floatbar;
|
||||
int x, movement;
|
||||
/* calculate movement and new x position */
|
||||
movement = event->xmotion.x_root - floatbar->last_motion_x_root;
|
||||
x = floatbar->x + movement;
|
||||
|
||||
/* do nothing if floatbar would be moved out of the window */
|
||||
if (x < 0 || (x + floatbar->width) > xfc->window->width)
|
||||
return;
|
||||
|
||||
/* move window to new x position */
|
||||
XMoveWindow(xfc->display, floatbar->handle, x, 0);
|
||||
/* update struct values for the next event */
|
||||
floatbar->last_motion_x_root = floatbar->last_motion_x_root + movement;
|
||||
floatbar->x = x;
|
||||
}
|
||||
|
||||
static void xf_floatbar_event_motionnotify(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
int mode;
|
||||
xfFloatbar* floatbar;
|
||||
Cursor cursor;
|
||||
mode = xfc->window->floatbar->mode;
|
||||
floatbar = xfc->window->floatbar;
|
||||
cursor = XCreateFontCursor(xfc->display, XC_arrow);
|
||||
|
||||
if ((event->xmotion.state & Button1Mask) && (mode > XF_FLOATBAR_MODE_DRAGGING))
|
||||
{
|
||||
xf_floatbar_resize(xfc, event);
|
||||
}
|
||||
else if ((event->xmotion.state & Button1Mask) && (mode == XF_FLOATBAR_MODE_DRAGGING))
|
||||
{
|
||||
xf_floatbar_dragging(xfc, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (event->xmotion.x <= FLOATBAR_BORDER ||
|
||||
event->xmotion.x >= xfc->window->floatbar->width - FLOATBAR_BORDER)
|
||||
cursor = XCreateFontCursor(xfc->display, XC_sb_h_double_arrow);
|
||||
}
|
||||
|
||||
XDefineCursor(xfc->display, xfc->window->handle, cursor);
|
||||
XFreeCursor(xfc->display, cursor);
|
||||
xfc->window->floatbar->last_motion_x_root = event->xmotion.x_root;
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_event_focusin(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbarButton* button;
|
||||
button = xf_floatbar_get_button(xfc, event);
|
||||
|
||||
if (button)
|
||||
{
|
||||
button->focus = TRUE;
|
||||
xf_floatbar_button_event_expose(xfc, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_event_focusout(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbarButton* button;
|
||||
button = xf_floatbar_get_button(xfc, event);
|
||||
|
||||
if (button)
|
||||
{
|
||||
button->focus = FALSE;
|
||||
button->clicked = FALSE;
|
||||
xf_floatbar_button_event_expose(xfc, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_floatbar_event_focusout(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
Cursor cursor;
|
||||
cursor = XCreateFontCursor(xfc->display, XC_arrow);
|
||||
XDefineCursor(xfc->display, xfc->window->handle, cursor);
|
||||
XFreeCursor(xfc->display, cursor);
|
||||
}
|
||||
|
||||
BOOL xf_floatbar_check_event(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
xfFloatbarButton* button;
|
||||
size_t i, size;
|
||||
|
||||
if (!xfc || !event || !xfc->window)
|
||||
return FALSE;
|
||||
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
if (event->xany.window == floatbar->handle)
|
||||
return TRUE;
|
||||
|
||||
size = ARRAYSIZE(floatbar->buttons);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
button = floatbar->buttons[i];
|
||||
|
||||
if (event->xany.window == button->handle)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL xf_floatbar_event_process(xfContext* xfc, XEvent* event)
|
||||
{
|
||||
xfFloatbar* floatbar;
|
||||
|
||||
if (!xfc || !xfc->window || !event)
|
||||
return FALSE;
|
||||
|
||||
floatbar = xfc->window->floatbar;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case Expose:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_event_expose(xfc, event);
|
||||
else
|
||||
xf_floatbar_button_event_expose(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
xf_floatbar_event_motionnotify(xfc, event);
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_event_buttonpress(xfc, event);
|
||||
else
|
||||
xf_floatbar_button_event_buttonpress(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case ButtonRelease:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_event_buttonrelease(xfc, event);
|
||||
else
|
||||
xf_floatbar_button_event_buttonrelease(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case EnterNotify:
|
||||
case FocusIn:
|
||||
if (event->xany.window != floatbar->handle)
|
||||
xf_floatbar_button_event_focusin(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case LeaveNotify:
|
||||
case FocusOut:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_event_focusout(xfc, event);
|
||||
else
|
||||
xf_floatbar_button_event_focusout(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_button_update_positon(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
case PropertyNotify:
|
||||
if (event->xany.window == floatbar->handle)
|
||||
xf_floatbar_button_update_positon(xfc, event);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return floatbar->handle == event->xany.window;
|
||||
}
|
||||
|
||||
static void xf_floatbar_button_free(xfContext* xfc, xfFloatbarButton* button)
|
||||
{
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
if (button->handle)
|
||||
{
|
||||
XUnmapWindow(xfc->display, button->handle);
|
||||
XDestroyWindow(xfc->display, button->handle);
|
||||
}
|
||||
|
||||
free(button);
|
||||
}
|
||||
|
||||
void xf_floatbar_free(xfContext* xfc, xfWindow* window, xfFloatbar* floatbar)
|
||||
{
|
||||
size_t i, size;
|
||||
size = ARRAYSIZE(floatbar->buttons);
|
||||
|
||||
if (!floatbar)
|
||||
return;
|
||||
|
||||
if (window->floatbar == floatbar)
|
||||
window->floatbar = NULL;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
xf_floatbar_button_free(xfc, floatbar->buttons[i]);
|
||||
floatbar->buttons[i] = NULL;
|
||||
}
|
||||
|
||||
if (floatbar->handle)
|
||||
{
|
||||
XUnmapWindow(xfc->display, floatbar->handle);
|
||||
XDestroyWindow(xfc->display, floatbar->handle);
|
||||
}
|
||||
|
||||
free(floatbar);
|
||||
}
|
||||
34
client/X11/xf_floatbar.h
Normal file
34
client/X11/xf_floatbar.h
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* X11 Windows
|
||||
*
|
||||
* 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_CLIENT_X11_FLOATBAR_H
|
||||
#define FREERDP_CLIENT_X11_FLOATBAR_H
|
||||
|
||||
typedef struct xf_floatbar xfFloatbar;
|
||||
|
||||
#include "xfreerdp.h"
|
||||
|
||||
typedef void(*OnClick)(xfContext*);
|
||||
xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window);
|
||||
BOOL xf_floatbar_event_process(xfContext* xfc, XEvent* event);
|
||||
BOOL xf_floatbar_check_event(xfContext* xfc, XEvent* event);
|
||||
void xf_floatbar_toggle_visibility(xfContext* xfc, bool visible);
|
||||
void xf_floatbar_free(xfContext* xfc, xfWindow* window, xfFloatbar* floatbar);
|
||||
void xf_floatbar_hide_and_show(xfContext* xfc);
|
||||
void xf_floatbar_set_root_y(xfContext* xfc, int y);
|
||||
|
||||
#endif /* FREERDP_CLIENT_X11_FLOATBAR_H */
|
||||
@ -245,7 +245,6 @@ static Pixmap xf_brush_new(xfContext* xfc, UINT32 width, UINT32 height,
|
||||
image->bitmap_bit_order = LSBFirst;
|
||||
gc = XCreateGC(xfc->display, xfc->drawable, 0, NULL);
|
||||
XPutImage(xfc->display, bitmap, gc, image, 0, 0, 0, 0, width, height);
|
||||
|
||||
image->data = NULL;
|
||||
XDestroyImage(image);
|
||||
|
||||
@ -577,7 +576,7 @@ static BOOL xf_gdi_invalidate_poly_region(xfContext* xfc, XPoint* points,
|
||||
static BOOL xf_gdi_polyline(rdpContext* context,
|
||||
const POLYLINE_ORDER* polyline)
|
||||
{
|
||||
int i;
|
||||
UINT32 i;
|
||||
int npoints;
|
||||
XColor color;
|
||||
XPoint* points;
|
||||
@ -1011,6 +1010,7 @@ static BOOL xf_gdi_update_screen(xfContext* xfc, const BYTE* pSrcData,
|
||||
bpp = 2;
|
||||
else
|
||||
bpp = 1;
|
||||
|
||||
XSetFunction(xfc->display, xfc->gc, GXcopy);
|
||||
XSetFillStyle(xfc->display, xfc->gc, FillSolid);
|
||||
|
||||
@ -1021,15 +1021,14 @@ static BOOL xf_gdi_update_screen(xfContext* xfc, const BYTE* pSrcData,
|
||||
UINT32 width = rects[i].right - rects[i].left;
|
||||
UINT32 height = rects[i].bottom - rects[i].top;
|
||||
const BYTE* src = pSrcData + top * scanline + bpp * left;
|
||||
|
||||
image = XCreateImage(xfc->display, xfc->visual, xfc->depth, ZPixmap, 0,
|
||||
(char*) src, width, height, xfc->scanline_pad, scanline);
|
||||
|
||||
if (!image)
|
||||
break;
|
||||
|
||||
image->byte_order = LSBFirst;
|
||||
image->bitmap_bit_order = LSBFirst;
|
||||
|
||||
XPutImage(xfc->display, xfc->primary, xfc->gc, image, 0, 0, left, top, width, height);
|
||||
image->data = NULL;
|
||||
XDestroyImage(image);
|
||||
@ -1059,17 +1058,14 @@ static BOOL xf_gdi_surface_bits(rdpContext* context,
|
||||
cmdRect.top = cmd->destTop;
|
||||
cmdRect.right = cmdRect.left + cmd->bmp.width;
|
||||
cmdRect.bottom = cmdRect.top + cmd->bmp.height;
|
||||
|
||||
|
||||
gdi = context->gdi;
|
||||
|
||||
xf_lock_x11(xfc, FALSE);
|
||||
|
||||
switch (cmd->bmp.codecID)
|
||||
{
|
||||
case RDP_CODEC_ID_REMOTEFX:
|
||||
if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData,
|
||||
cmd->bmp.bitmapDataLength, cmd->destLeft, cmd->destTop,
|
||||
cmd->bmp.bitmapDataLength, cmd->destLeft, cmd->destTop,
|
||||
gdi->primary_buffer, gdi->dstFormat, gdi->stride,
|
||||
gdi->height, ®ion))
|
||||
goto fail;
|
||||
@ -1078,9 +1074,9 @@ static BOOL xf_gdi_surface_bits(rdpContext* context,
|
||||
|
||||
case RDP_CODEC_ID_NSCODEC:
|
||||
if (!nsc_process_message(context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width,
|
||||
cmd->bmp.height, cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength,
|
||||
cmd->bmp.height, cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength,
|
||||
gdi->primary_buffer, gdi->dstFormat, gdi->stride,
|
||||
0, 0, cmd->bmp.width, cmd->bmp.height, FREERDP_FLIP_VERTICAL))
|
||||
0, 0, cmd->bmp.width, cmd->bmp.height, FREERDP_FLIP_VERTICAL))
|
||||
goto fail;
|
||||
|
||||
region16_union_rect(®ion, ®ion, &cmdRect);
|
||||
@ -1091,7 +1087,7 @@ static BOOL xf_gdi_surface_bits(rdpContext* context,
|
||||
format = gdi_get_pixel_format(cmd->bmp.bpp);
|
||||
|
||||
if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride,
|
||||
cmd->destLeft, cmd->destTop, cmd->bmp.width, cmd->bmp.height,
|
||||
cmd->destLeft, cmd->destTop, cmd->bmp.width, cmd->bmp.height,
|
||||
pSrcData, format, 0, 0, 0,
|
||||
&xfc->context.gdi->palette, FREERDP_FLIP_VERTICAL))
|
||||
goto fail;
|
||||
|
||||
@ -144,12 +144,12 @@ static BOOL xf_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
|
||||
xbitmap->image = XCreateImage(xfc->display, xfc->visual, xfc->depth,
|
||||
ZPixmap, 0, (char*) bitmap->data, bitmap->width, bitmap->height,
|
||||
xfc->scanline_pad, 0);
|
||||
|
||||
if (!xbitmap->image)
|
||||
goto unlock;
|
||||
|
||||
xbitmap->image->byte_order = LSBFirst;
|
||||
xbitmap->image->bitmap_bit_order = LSBFirst;
|
||||
|
||||
XPutImage(xfc->display, xbitmap->pixmap, xfc->gc, xbitmap->image, 0, 0, 0, 0, bitmap->width,
|
||||
bitmap->height);
|
||||
}
|
||||
@ -425,8 +425,8 @@ static void xf_Glyph_Free(rdpContext* context, rdpGlyph* glyph)
|
||||
free(glyph);
|
||||
}
|
||||
|
||||
static BOOL xf_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, UINT32 x,
|
||||
UINT32 y, UINT32 w, UINT32 h, UINT32 sx, UINT32 sy,
|
||||
static BOOL xf_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x,
|
||||
INT32 y, INT32 w, INT32 h, INT32 sx, INT32 sy,
|
||||
BOOL fOpRedundant)
|
||||
{
|
||||
xfGlyph* xf_glyph;
|
||||
@ -453,8 +453,8 @@ static BOOL xf_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, UINT32 x,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_Glyph_BeginDraw(rdpContext* context, UINT32 x, UINT32 y,
|
||||
UINT32 width, UINT32 height, UINT32 bgcolor,
|
||||
static BOOL xf_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y,
|
||||
INT32 width, INT32 height, UINT32 bgcolor,
|
||||
UINT32 fgcolor, BOOL fOpRedundant)
|
||||
{
|
||||
xfContext* xfc = (xfContext*) context;
|
||||
@ -487,8 +487,8 @@ static BOOL xf_Glyph_BeginDraw(rdpContext* context, UINT32 x, UINT32 y,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_Glyph_EndDraw(rdpContext* context, UINT32 x, UINT32 y,
|
||||
UINT32 width, UINT32 height,
|
||||
static BOOL xf_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y,
|
||||
INT32 width, INT32 height,
|
||||
UINT32 bgcolor, UINT32 fgcolor)
|
||||
{
|
||||
xfContext* xfc = (xfContext*) context;
|
||||
|
||||
@ -160,7 +160,7 @@ void xf_keyboard_key_release(xfContext* xfc, BYTE keycode, KeySym keysym)
|
||||
|
||||
void xf_keyboard_release_all_keypress(xfContext* xfc)
|
||||
{
|
||||
int keycode;
|
||||
size_t keycode;
|
||||
DWORD rdp_scancode;
|
||||
|
||||
for (keycode = 0; keycode < ARRAYSIZE(xfc->KeyboardState); keycode++)
|
||||
@ -317,6 +317,26 @@ UINT32 xf_keyboard_get_toggle_keys_state(xfContext* xfc)
|
||||
return toggleKeysState;
|
||||
}
|
||||
|
||||
static void xk_keyboard_update_modifier_keys(xfContext* xfc)
|
||||
{
|
||||
int state;
|
||||
size_t i;
|
||||
KeyCode keycode;
|
||||
int keysyms[] = {XK_Shift_L, XK_Shift_R, XK_Alt_L, XK_Alt_R,
|
||||
XK_Control_L, XK_Control_R, XK_Super_L, XK_Super_R
|
||||
};
|
||||
state = xf_keyboard_read_keyboard_state(xfc);
|
||||
|
||||
for (i = 0; i < ARRAYSIZE(keysyms); i++)
|
||||
{
|
||||
if (xf_keyboard_get_key_state(xfc, state, keysyms[i]))
|
||||
{
|
||||
keycode = XKeysymToKeycode(xfc->display, keysyms[i]);
|
||||
xfc->KeyboardState[keycode] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xf_keyboard_focus_in(xfContext* xfc)
|
||||
{
|
||||
rdpInput* input;
|
||||
@ -330,6 +350,7 @@ void xf_keyboard_focus_in(xfContext* xfc)
|
||||
input = xfc->context.input;
|
||||
syncFlags = xf_keyboard_get_toggle_keys_state(xfc);
|
||||
input->FocusInEvent(input, syncFlags);
|
||||
xk_keyboard_update_modifier_keys(xfc);
|
||||
|
||||
/* finish with a mouse pointer position like mstsc.exe if required */
|
||||
|
||||
@ -359,7 +380,7 @@ static int xf_keyboard_execute_action_script(xfContext* xfc,
|
||||
BOOL match = FALSE;
|
||||
char* keyCombination;
|
||||
char buffer[1024] = { 0 };
|
||||
char command[1024] = { 0 };
|
||||
char command[2048] = { 0 };
|
||||
char combination[1024] = { 0 };
|
||||
|
||||
if (!xfc->actionScriptExists)
|
||||
@ -388,6 +409,9 @@ static int xf_keyboard_execute_action_script(xfContext* xfc,
|
||||
if (mod->Alt)
|
||||
strcat(combination, "Alt+");
|
||||
|
||||
if (mod->Super)
|
||||
strcat(combination, "Super+");
|
||||
|
||||
strcat(combination, keyStr);
|
||||
count = ArrayList_Count(xfc->keyCombinations);
|
||||
|
||||
@ -447,7 +471,6 @@ BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym)
|
||||
{
|
||||
XF_MODIFIER_KEYS mod = { 0 };
|
||||
xk_keyboard_get_modifier_keys(xfc, &mod);
|
||||
rdpContext* ctx = &xfc->context;
|
||||
|
||||
// remember state of RightCtrl to ungrab keyboard if next action is release of RightCtrl
|
||||
// do not return anything such that the key could be used by client if ungrab is not the goal
|
||||
@ -500,6 +523,8 @@ BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym)
|
||||
|
||||
if (!xfc->remote_app && xfc->settings->MultiTouchGestures)
|
||||
{
|
||||
rdpContext* ctx = &xfc->context;
|
||||
|
||||
if (mod.Ctrl && mod.Alt)
|
||||
{
|
||||
int pdx = 0;
|
||||
@ -597,6 +622,7 @@ void xf_keyboard_handle_special_keys_release(xfContext* xfc, KeySym keysym)
|
||||
xf_toggle_control(xfc);
|
||||
}
|
||||
|
||||
xfc->mouse_active = FALSE;
|
||||
XUngrabKeyboard(xfc->display, CurrentTime);
|
||||
}
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ static BOOL xf_is_monitor_id_active(xfContext* xfc, UINT32 id)
|
||||
|
||||
BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
{
|
||||
int i;
|
||||
UINT32 i;
|
||||
int nmonitors = 0;
|
||||
int monitor_index = 0;
|
||||
BOOL primaryMonitorFound = FALSE;
|
||||
@ -146,6 +146,7 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
Window _dummy_w;
|
||||
int current_monitor = 0;
|
||||
Screen* screen;
|
||||
MONITOR_INFO* monitor;
|
||||
#if defined WITH_XINERAMA || defined WITH_XRANDR
|
||||
int major, minor;
|
||||
#endif
|
||||
@ -208,13 +209,6 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
vscreen->monitors[i].area.top = screenInfo[i].y_org;
|
||||
vscreen->monitors[i].area.right = screenInfo[i].x_org + screenInfo[i].width - 1;
|
||||
vscreen->monitors[i].area.bottom = screenInfo[i].y_org + screenInfo[i].height - 1;
|
||||
|
||||
/* Determine which monitor that the mouse cursor is on */
|
||||
if ((mouse_x >= vscreen->monitors[i].area.left) &&
|
||||
(mouse_x <= vscreen->monitors[i].area.right) &&
|
||||
(mouse_y >= vscreen->monitors[i].area.top) &&
|
||||
(mouse_y <= vscreen->monitors[i].area.bottom))
|
||||
current_monitor = i;
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,6 +219,46 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
xfc->fullscreenMonitors.top = xfc->fullscreenMonitors.bottom =
|
||||
xfc->fullscreenMonitors.left = xfc->fullscreenMonitors.right = 0;
|
||||
|
||||
/* Determine which monitor that the mouse cursor is on */
|
||||
if (vscreen->monitors)
|
||||
{
|
||||
for (i = 0; i < vscreen->nmonitors; i++)
|
||||
{
|
||||
if ((mouse_x >= vscreen->monitors[i].area.left) &&
|
||||
(mouse_x <= vscreen->monitors[i].area.right) &&
|
||||
(mouse_y >= vscreen->monitors[i].area.top) &&
|
||||
(mouse_y <= vscreen->monitors[i].area.bottom))
|
||||
{
|
||||
current_monitor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Even for a single monitor, we need to calculate the virtual screen to support
|
||||
window managers that do not implement all X window state hints.
|
||||
|
||||
If the user did not request multiple monitor or is using workarea
|
||||
without remote app, we force the number of monitors be 1 so later
|
||||
the rest of the client don't end up using more monitors than the user desires.
|
||||
*/
|
||||
if ((!settings->UseMultimon && !settings->SpanMonitors) ||
|
||||
(settings->Workarea && !settings->RemoteApplicationMode))
|
||||
{
|
||||
/* If no monitors were specified on the command-line then set the current monitor as active */
|
||||
if (!settings->NumMonitorIds)
|
||||
{
|
||||
settings->MonitorIds[0] = current_monitor;
|
||||
}
|
||||
|
||||
/* Always sets number of monitors from command-line to just 1.
|
||||
* If the monitor is invalid then we will default back to current monitor
|
||||
* later as a fallback. So, there is no need to validate command-line entry here.
|
||||
*/
|
||||
settings->NumMonitorIds = 1;
|
||||
}
|
||||
|
||||
/* WORKAROUND: With Remote Application Mode - using NET_WM_WORKAREA
|
||||
* causes issues with the ability to fully size the window vertically
|
||||
* (the bottom of the window area is never updated). So, we just set
|
||||
@ -232,10 +266,26 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
*/
|
||||
if (settings->RemoteApplicationMode || !xf_GetWorkArea(xfc))
|
||||
{
|
||||
xfc->workArea.x = 0;
|
||||
xfc->workArea.y = 0;
|
||||
xfc->workArea.width = WidthOfScreen(xfc->screen);
|
||||
xfc->workArea.height = HeightOfScreen(xfc->screen);
|
||||
/*
|
||||
if only 1 monitor is enabled, use monitor area
|
||||
this is required in case of a screen composed of more than one monitor
|
||||
but user did not enable multimonitor
|
||||
*/
|
||||
if (settings->NumMonitorIds == 1)
|
||||
{
|
||||
monitor = vscreen->monitors + current_monitor;
|
||||
xfc->workArea.x = monitor->area.left;
|
||||
xfc->workArea.y = monitor->area.top;
|
||||
xfc->workArea.width = monitor->area.right - monitor->area.left + 1;
|
||||
xfc->workArea.height = monitor->area.bottom - monitor->area.top + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xfc->workArea.x = 0;
|
||||
xfc->workArea.y = 0;
|
||||
xfc->workArea.width = WidthOfScreen(xfc->screen);
|
||||
xfc->workArea.height = HeightOfScreen(xfc->screen);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings->Fullscreen)
|
||||
@ -288,26 +338,6 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
*pMaxHeight = settings->DesktopHeight;
|
||||
}
|
||||
|
||||
if (!settings->Fullscreen && !settings->Workarea && !settings->UseMultimon)
|
||||
goto out;
|
||||
|
||||
/* If single monitor fullscreen OR workarea without remote app */
|
||||
if ((settings->Fullscreen && !settings->UseMultimon && !settings->SpanMonitors) ||
|
||||
(settings->Workarea && !settings->RemoteApplicationMode))
|
||||
{
|
||||
/* If no monitors were specified on the command-line then set the current monitor as active */
|
||||
if (!settings->NumMonitorIds)
|
||||
{
|
||||
settings->MonitorIds[0] = current_monitor;
|
||||
}
|
||||
|
||||
/* Always sets number of monitors from command-line to just 1.
|
||||
* If the monitor is invalid then we will default back to current monitor
|
||||
* later as a fallback. So, there is no need to validate command-line entry here.
|
||||
*/
|
||||
settings->NumMonitorIds = 1;
|
||||
}
|
||||
|
||||
/* Create array of all active monitors by taking into account monitors requested on the command-line */
|
||||
for (i = 0; i < vscreen->nmonitors; i++)
|
||||
{
|
||||
@ -340,6 +370,8 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
if (i == settings->MonitorIds[0])
|
||||
{
|
||||
settings->MonitorDefArray[nmonitors].is_primary = TRUE;
|
||||
settings->MonitorLocalShiftX = settings->MonitorDefArray[nmonitors].x;
|
||||
settings->MonitorLocalShiftY = settings->MonitorDefArray[nmonitors].y;
|
||||
primaryMonitorFound = TRUE;
|
||||
}
|
||||
|
||||
@ -406,8 +438,6 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
vB = destB;
|
||||
}
|
||||
|
||||
settings->DesktopPosX = vX;
|
||||
settings->DesktopPosY = vY;
|
||||
vscreen->area.left = 0;
|
||||
vscreen->area.right = vR - vX - 1;
|
||||
vscreen->area.top = 0;
|
||||
@ -489,7 +519,6 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
||||
if (settings->MonitorCount)
|
||||
settings->SupportMonitorLayoutPdu = TRUE;
|
||||
|
||||
out:
|
||||
#ifdef USABLE_XRANDR
|
||||
|
||||
if (rrmonitors)
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
@ -61,6 +62,22 @@ static const char* movetype_names[] =
|
||||
};
|
||||
#endif
|
||||
|
||||
struct xf_rail_icon
|
||||
{
|
||||
long* data;
|
||||
int length;
|
||||
};
|
||||
typedef struct xf_rail_icon xfRailIcon;
|
||||
|
||||
struct xf_rail_icon_cache
|
||||
{
|
||||
xfRailIcon* entries;
|
||||
UINT32 numCaches;
|
||||
UINT32 numCacheEntries;
|
||||
xfRailIcon scratch;
|
||||
};
|
||||
typedef struct xf_rail_icon_cache xfRailIconCache;
|
||||
|
||||
void xf_rail_enable_remoteapp_mode(xfContext* xfc)
|
||||
{
|
||||
if (!xfc->remote_app)
|
||||
@ -91,6 +108,11 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
|
||||
if (!appWindow)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
|
||||
else
|
||||
xf_SetWindowStyle(xfc, appWindow, 0, 0);
|
||||
|
||||
activate.windowId = appWindow->windowId;
|
||||
activate.enabled = enabled;
|
||||
xfc->rail->ClientActivate(xfc->rail, &activate);
|
||||
@ -195,7 +217,6 @@ static void xf_rail_invalidate_region(xfContext* xfc, REGION16* invalidRegion)
|
||||
xfAppWindow* appWindow;
|
||||
const RECTANGLE_16* extents;
|
||||
REGION16 windowInvalidRegion;
|
||||
|
||||
region16_init(&windowInvalidRegion);
|
||||
count = HashTable_GetKeys(xfc->railWindows, &pKeys);
|
||||
|
||||
@ -527,22 +548,332 @@ static BOOL xf_rail_window_delete(rdpContext* context,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static xfRailIconCache* RailIconCache_New(rdpSettings* settings)
|
||||
{
|
||||
xfRailIconCache* cache;
|
||||
cache = calloc(1, sizeof(xfRailIconCache));
|
||||
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
||||
cache->numCaches = settings->RemoteAppNumIconCaches;
|
||||
cache->numCacheEntries = settings->RemoteAppNumIconCacheEntries;
|
||||
cache->entries = calloc(cache->numCaches * cache->numCacheEntries,
|
||||
sizeof(xfRailIcon));
|
||||
|
||||
if (!cache->entries)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to allocate icon cache %d x %d entries",
|
||||
cache->numCaches, cache->numCacheEntries);
|
||||
free(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void RailIconCache_Free(xfRailIconCache* cache)
|
||||
{
|
||||
UINT32 i;
|
||||
|
||||
if (cache)
|
||||
{
|
||||
for (i = 0; i < cache->numCaches * cache->numCacheEntries; i++)
|
||||
{
|
||||
free(cache->entries[i].data);
|
||||
}
|
||||
|
||||
free(cache->scratch.data);
|
||||
free(cache->entries);
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
static xfRailIcon* RailIconCache_Lookup(xfRailIconCache* cache,
|
||||
UINT8 cacheId, UINT16 cacheEntry)
|
||||
{
|
||||
/*
|
||||
* MS-RDPERP 2.2.1.2.3 Icon Info (TS_ICON_INFO)
|
||||
*
|
||||
* CacheId (1 byte):
|
||||
* If the value is 0xFFFF, the icon SHOULD NOT be cached.
|
||||
*
|
||||
* Yes, the spec says "0xFFFF" in the 2018-03-16 revision,
|
||||
* but the actual protocol field is 1-byte wide.
|
||||
*/
|
||||
if (cacheId == 0xFF)
|
||||
return &cache->scratch;
|
||||
|
||||
if (cacheId >= cache->numCaches)
|
||||
return NULL;
|
||||
|
||||
if (cacheEntry >= cache->numCacheEntries)
|
||||
return NULL;
|
||||
|
||||
return &cache->entries[cache->numCacheEntries * cacheId + cacheEntry];
|
||||
}
|
||||
|
||||
/*
|
||||
* DIB color palettes are arrays of RGBQUAD structs with colors in BGRX format.
|
||||
* They are used only by 1, 2, 4, and 8-bit bitmaps.
|
||||
*/
|
||||
static void fill_gdi_palette_for_icon(ICON_INFO* iconInfo, gdiPalette* palette)
|
||||
{
|
||||
UINT32 i;
|
||||
palette->format = PIXEL_FORMAT_BGRX32;
|
||||
ZeroMemory(palette->palette, sizeof(palette->palette));
|
||||
|
||||
if (!iconInfo->cbColorTable)
|
||||
return;
|
||||
|
||||
if ((iconInfo->cbColorTable % 4 != 0) || (iconInfo->cbColorTable / 4 > 256))
|
||||
{
|
||||
WLog_WARN(TAG, "weird palette size: %u", iconInfo->cbColorTable);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < iconInfo->cbColorTable / 4; i++)
|
||||
{
|
||||
palette->palette[i] = ReadColor(&iconInfo->colorTable[4 * i], palette->format);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL convert_icon_color_to_argb(ICON_INFO* iconInfo, BYTE* argbPixels)
|
||||
{
|
||||
DWORD format;
|
||||
gdiPalette palette;
|
||||
|
||||
/*
|
||||
* Color formats used by icons are DIB bitmap formats (2-bit format
|
||||
* is not used by MS-RDPERP). Note that 16-bit is RGB555, not RGB565,
|
||||
* and that 32-bit format uses BGRA order.
|
||||
*/
|
||||
switch (iconInfo->bpp)
|
||||
{
|
||||
case 1:
|
||||
case 4:
|
||||
/*
|
||||
* These formats are not supported by freerdp_image_copy().
|
||||
* PIXEL_FORMAT_MONO and PIXEL_FORMAT_A4 are *not* correct
|
||||
* color formats for this. Please fix freerdp_image_copy()
|
||||
* if you came here to fix a broken icon of some weird app
|
||||
* that still uses 1 or 4bpp format in the 21st century.
|
||||
*/
|
||||
WLog_WARN(TAG, "1bpp and 4bpp icons are not supported");
|
||||
return FALSE;
|
||||
|
||||
case 8:
|
||||
format = PIXEL_FORMAT_RGB8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
format = PIXEL_FORMAT_RGB15;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
format = PIXEL_FORMAT_RGB24;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
format = PIXEL_FORMAT_BGRA32;
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_WARN(TAG, "invalid icon bpp: %d", iconInfo->bpp);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fill_gdi_palette_for_icon(iconInfo, &palette);
|
||||
return freerdp_image_copy(
|
||||
argbPixels,
|
||||
PIXEL_FORMAT_ARGB32,
|
||||
0, 0, 0,
|
||||
iconInfo->width,
|
||||
iconInfo->height,
|
||||
iconInfo->bitsColor,
|
||||
format,
|
||||
0, 0, 0,
|
||||
&palette,
|
||||
FREERDP_FLIP_VERTICAL
|
||||
);
|
||||
}
|
||||
|
||||
static inline UINT32 div_ceil(UINT32 a, UINT32 b)
|
||||
{
|
||||
return (a + (b - 1)) / b;
|
||||
}
|
||||
|
||||
static inline UINT32 round_up(UINT32 a, UINT32 b)
|
||||
{
|
||||
return b * div_ceil(a, b);
|
||||
}
|
||||
|
||||
static void apply_icon_alpha_mask(ICON_INFO* iconInfo, BYTE* argbPixels)
|
||||
{
|
||||
BYTE nextBit;
|
||||
BYTE* maskByte;
|
||||
UINT32 x, y;
|
||||
UINT32 stride;
|
||||
|
||||
if (!iconInfo->cbBitsMask)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Each byte encodes 8 adjacent pixels (with LSB padding as needed).
|
||||
* And due to hysterical raisins, stride of DIB bitmaps must be
|
||||
* a multiple of 4 bytes.
|
||||
*/
|
||||
stride = round_up(div_ceil(iconInfo->width, 8), 4);
|
||||
|
||||
for (y = 0; y < iconInfo->height; y++)
|
||||
{
|
||||
/* ɐᴉlɐɹʇsn∀ uᴉ ǝɹ,ǝʍ ʇɐɥʇ ʇǝƃɹoɟ ʇ,uop */
|
||||
maskByte = &iconInfo->bitsMask[stride * (iconInfo->height - 1 - y)];
|
||||
nextBit = 0x80;
|
||||
|
||||
for (x = 0; x < iconInfo->width; x++)
|
||||
{
|
||||
BYTE alpha = (*maskByte & nextBit) ? 0x00 : 0xFF;
|
||||
argbPixels[4 * (x + y * iconInfo->width)] &= alpha;
|
||||
nextBit >>= 1;
|
||||
|
||||
if (!nextBit)
|
||||
{
|
||||
nextBit = 0x80;
|
||||
maskByte++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _NET_WM_ICON format is defined as "array of CARDINAL" values which for
|
||||
* Xlib must be represented with an array of C's "long" values. Note that
|
||||
* "long" != "INT32" on 64-bit systems. Therefore we can't simply cast
|
||||
* the bitmap data as (unsigned char*), we have to copy all the pixels.
|
||||
*
|
||||
* The first two values are width and height followed by actual color data
|
||||
* in ARGB format (e.g., 0xFFFF0000L is opaque red), pixels are in normal,
|
||||
* left-to-right top-down order.
|
||||
*/
|
||||
static BOOL convert_rail_icon(ICON_INFO* iconInfo, xfRailIcon* railIcon)
|
||||
{
|
||||
BYTE* argbPixels;
|
||||
BYTE* nextPixel;
|
||||
long* pixels;
|
||||
int i;
|
||||
int nelements;
|
||||
argbPixels = calloc(iconInfo->width * iconInfo->height, 4);
|
||||
|
||||
if (!argbPixels)
|
||||
goto error;
|
||||
|
||||
if (!convert_icon_color_to_argb(iconInfo, argbPixels))
|
||||
goto error;
|
||||
|
||||
apply_icon_alpha_mask(iconInfo, argbPixels);
|
||||
nelements = 2 + iconInfo->width * iconInfo->height;
|
||||
pixels = realloc(railIcon->data, nelements * sizeof(long));
|
||||
|
||||
if (!pixels)
|
||||
goto error;
|
||||
|
||||
railIcon->data = pixels;
|
||||
railIcon->length = nelements;
|
||||
pixels[0] = iconInfo->width;
|
||||
pixels[1] = iconInfo->height;
|
||||
nextPixel = argbPixels;
|
||||
|
||||
for (i = 2; i < nelements; i++)
|
||||
{
|
||||
pixels[i] = ReadColor(nextPixel, PIXEL_FORMAT_BGRA32);
|
||||
nextPixel += 4;
|
||||
}
|
||||
|
||||
free(argbPixels);
|
||||
return TRUE;
|
||||
error:
|
||||
free(argbPixels);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void xf_rail_set_window_icon(xfContext* xfc,
|
||||
xfAppWindow* railWindow, xfRailIcon* icon,
|
||||
BOOL replace)
|
||||
{
|
||||
XChangeProperty(xfc->display, railWindow->handle, xfc->_NET_WM_ICON,
|
||||
XA_CARDINAL, 32, replace ? PropModeReplace : PropModeAppend,
|
||||
(unsigned char*) icon->data, icon->length);
|
||||
XFlush(xfc->display);
|
||||
}
|
||||
|
||||
static xfAppWindow* xf_rail_get_window_by_id(xfContext* xfc, UINT32 windowId)
|
||||
{
|
||||
return (xfAppWindow*) HashTable_GetItemValue(xfc->railWindows,
|
||||
(void*)(UINT_PTR) windowId);
|
||||
}
|
||||
|
||||
static BOOL xf_rail_window_icon(rdpContext* context,
|
||||
WINDOW_ORDER_INFO* orderInfo, WINDOW_ICON_ORDER* windowIcon)
|
||||
{
|
||||
xfContext* xfc = (xfContext*) context;
|
||||
xfAppWindow* railWindow = (xfAppWindow*) HashTable_GetItemValue(xfc->railWindows,
|
||||
(void*)(UINT_PTR) orderInfo->windowId);
|
||||
xfAppWindow* railWindow;
|
||||
xfRailIcon* icon;
|
||||
BOOL replaceIcon;
|
||||
railWindow = xf_rail_get_window_by_id(xfc, orderInfo->windowId);
|
||||
|
||||
if (!railWindow)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
|
||||
icon = RailIconCache_Lookup(xfc->railIconCache,
|
||||
windowIcon->iconInfo->cacheId,
|
||||
windowIcon->iconInfo->cacheEntry);
|
||||
|
||||
if (!icon)
|
||||
{
|
||||
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
|
||||
windowIcon->iconInfo->cacheId,
|
||||
windowIcon->iconInfo->cacheEntry);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!convert_rail_icon(windowIcon->iconInfo, icon))
|
||||
{
|
||||
WLog_WARN(TAG, "failed to convert icon for window %08X", orderInfo->windowId);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
|
||||
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_rail_window_cached_icon(rdpContext* context,
|
||||
WINDOW_ORDER_INFO* orderInfo, WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
|
||||
{
|
||||
xfContext* xfc = (xfContext*) context;
|
||||
xfAppWindow* railWindow;
|
||||
xfRailIcon* icon;
|
||||
BOOL replaceIcon;
|
||||
railWindow = xf_rail_get_window_by_id(xfc, orderInfo->windowId);
|
||||
|
||||
if (!railWindow)
|
||||
return TRUE;
|
||||
|
||||
icon = RailIconCache_Lookup(xfc->railIconCache,
|
||||
windowCachedIcon->cachedIcon.cacheId,
|
||||
windowCachedIcon->cachedIcon.cacheEntry);
|
||||
|
||||
if (!icon)
|
||||
{
|
||||
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
|
||||
windowCachedIcon->cachedIcon.cacheId,
|
||||
windowCachedIcon->cachedIcon.cacheEntry);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
|
||||
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -631,7 +962,7 @@ static void xf_rail_register_update_callbacks(rdpUpdate* update)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_execute_result(RailClientContext* context,
|
||||
RAIL_EXEC_RESULT_ORDER* execResult)
|
||||
const RAIL_EXEC_RESULT_ORDER* execResult)
|
||||
{
|
||||
xfContext* xfc = (xfContext*) context->custom;
|
||||
|
||||
@ -655,7 +986,7 @@ static UINT xf_rail_server_execute_result(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_system_param(RailClientContext* context,
|
||||
RAIL_SYSPARAM_ORDER* sysparam)
|
||||
const RAIL_SYSPARAM_ORDER* sysparam)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -666,7 +997,7 @@ static UINT xf_rail_server_system_param(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_handshake(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_ORDER* handshake)
|
||||
const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
RAIL_EXEC_ORDER exec;
|
||||
RAIL_SYSPARAM_ORDER sysparam;
|
||||
@ -722,7 +1053,7 @@ static UINT xf_rail_server_handshake(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_handshake_ex(RailClientContext* context,
|
||||
RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -733,7 +1064,7 @@ static UINT xf_rail_server_handshake_ex(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_local_move_size(RailClientContext* context,
|
||||
RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
|
||||
const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
int direction = 0;
|
||||
@ -832,7 +1163,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_min_max_info(RailClientContext* context,
|
||||
RAIL_MINMAXINFO_ORDER* minMaxInfo)
|
||||
const RAIL_MINMAXINFO_ORDER* minMaxInfo)
|
||||
{
|
||||
xfAppWindow* appWindow = NULL;
|
||||
xfContext* xfc = (xfContext*) context->custom;
|
||||
@ -857,7 +1188,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_language_bar_info(RailClientContext* context,
|
||||
RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -868,7 +1199,7 @@ static UINT xf_rail_server_language_bar_info(RailClientContext* context,
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT xf_rail_server_get_appid_response(RailClientContext* context,
|
||||
RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
|
||||
const RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
|
||||
{
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -907,6 +1238,14 @@ int xf_rail_init(xfContext* xfc, RailClientContext* rail)
|
||||
return 0;
|
||||
|
||||
xfc->railWindows->valueFree = rail_window_free;
|
||||
xfc->railIconCache = RailIconCache_New(xfc->context.settings);
|
||||
|
||||
if (!xfc->railIconCache)
|
||||
{
|
||||
HashTable_Free(xfc->railWindows);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -924,5 +1263,11 @@ int xf_rail_uninit(xfContext* xfc, RailClientContext* rail)
|
||||
xfc->railWindows = NULL;
|
||||
}
|
||||
|
||||
if (xfc->railIconCache)
|
||||
{
|
||||
RailIconCache_Free(xfc->railIconCache);
|
||||
xfc->railIconCache = NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -146,15 +146,26 @@ void xf_SendClientEvent(xfContext* xfc, Window window, Atom atom,
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void xf_SetWindowMinimized(xfContext* xfc, xfWindow* window)
|
||||
{
|
||||
XIconifyWindow(xfc->display, window->handle, xfc->screen_number);
|
||||
}
|
||||
|
||||
void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
|
||||
{
|
||||
int i;
|
||||
UINT32 i;
|
||||
rdpSettings* settings = xfc->context.settings;
|
||||
int startX, startY;
|
||||
UINT32 width = window->width;
|
||||
UINT32 height = window->height;
|
||||
/* xfc->decorations is set by caller depending on settings and whether it is fullscreen or not */
|
||||
window->decorations = xfc->decorations;
|
||||
/* show/hide decorations (e.g. title bar) as guided by xfc->decorations */
|
||||
xf_SetWindowDecorations(xfc, window->handle, window->decorations);
|
||||
DEBUG_X11(TAG, "X window decoration set to %d", (int)window->decorations);
|
||||
|
||||
if (xfc->floatbar)
|
||||
xf_floatbar_toggle_visibility(xfc, fullscreen);
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
@ -162,8 +173,8 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
|
||||
xfc->savedHeight = xfc->window->height;
|
||||
xfc->savedPosX = xfc->window->left;
|
||||
xfc->savedPosY = xfc->window->top;
|
||||
startX = settings->DesktopPosX;
|
||||
startY = settings->DesktopPosY;
|
||||
startX = (settings->DesktopPosX != UINT32_MAX) ? settings->DesktopPosX : 0;
|
||||
startY = (settings->DesktopPosY != UINT32_MAX) ? settings->DesktopPosY : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -194,7 +205,12 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
|
||||
startY += xfc->context.settings->MonitorLocalShiftY;
|
||||
}
|
||||
|
||||
if (xfc->_NET_WM_FULLSCREEN_MONITORS != None)
|
||||
/*
|
||||
It is safe to proceed with simply toogling _NET_WM_STATE_FULLSCREEN window state on the following conditions:
|
||||
- The window manager supports multiple monitor full screen
|
||||
- The user requested to use a single monitor to render the remote desktop
|
||||
*/
|
||||
if (xfc->_NET_WM_FULLSCREEN_MONITORS != None || settings->MonitorCount == 1)
|
||||
{
|
||||
xf_ResizeDesktopWindow(xfc, window, width, height);
|
||||
|
||||
@ -286,6 +302,7 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
|
||||
|
||||
width = xfc->vscreen.area.right - xfc->vscreen.area.left + 1;
|
||||
height = xfc->vscreen.area.bottom - xfc->vscreen.area.top + 1;
|
||||
DEBUG_X11("X window move and resize %dx%d@%dx%d", startX, startY, width, height);
|
||||
xf_ResizeDesktopWindow(xfc, window, width, height);
|
||||
XMoveWindow(xfc->display, window->handle, startX, startY);
|
||||
}
|
||||
@ -541,6 +558,7 @@ xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width,
|
||||
|
||||
XSelectInput(xfc->display, window->handle, input_mask);
|
||||
XClearWindow(xfc->display, window->handle);
|
||||
xf_SetWindowTitleText(xfc, window->handle, name);
|
||||
XMapWindow(xfc->display, window->handle);
|
||||
xf_input_init(xfc, window->handle);
|
||||
|
||||
@ -563,13 +581,13 @@ xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width,
|
||||
{
|
||||
XMoveWindow(xfc->display, window->handle, 0, 0);
|
||||
}
|
||||
else if (settings->DesktopPosX || settings->DesktopPosY)
|
||||
else if (settings->DesktopPosX != UINT32_MAX && settings->DesktopPosY != UINT32_MAX)
|
||||
{
|
||||
XMoveWindow(xfc->display, window->handle, settings->DesktopPosX,
|
||||
settings->DesktopPosY);
|
||||
}
|
||||
|
||||
xf_SetWindowTitleText(xfc, window->handle, name);
|
||||
window->floatbar = xf_floatbar_new(xfc, window->handle);
|
||||
return window;
|
||||
}
|
||||
|
||||
@ -591,11 +609,10 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
|
||||
size_hints->win_gravity = NorthWestGravity;
|
||||
size_hints->min_width = size_hints->min_height = 1;
|
||||
size_hints->max_width = size_hints->max_height = 16384;
|
||||
XSetWMNormalHints(xfc->display, window->handle, size_hints);
|
||||
XResizeWindow(xfc->display, window->handle, width, height);
|
||||
#ifdef WITH_XRENDER
|
||||
|
||||
if (!settings->SmartSizing)
|
||||
if (!settings->SmartSizing && !settings->DynamicResolutionUpdate)
|
||||
#endif
|
||||
{
|
||||
if (!xfc->fullscreen)
|
||||
@ -604,10 +621,10 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
|
||||
* not be resizable */
|
||||
size_hints->min_width = size_hints->max_width = width;
|
||||
size_hints->min_height = size_hints->max_height = height;
|
||||
XSetWMNormalHints(xfc->display, window->handle, size_hints);
|
||||
}
|
||||
}
|
||||
|
||||
XSetWMNormalHints(xfc->display, window->handle, size_hints);
|
||||
XFree(size_hints);
|
||||
}
|
||||
|
||||
@ -619,6 +636,9 @@ void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window)
|
||||
if (xfc->window == window)
|
||||
xfc->window = NULL;
|
||||
|
||||
if (window->floatbar)
|
||||
xf_floatbar_free(xfc, window, window->floatbar);
|
||||
|
||||
if (window->gc)
|
||||
XFreeGC(xfc->display, window->gc);
|
||||
|
||||
@ -644,25 +664,14 @@ void xf_SetWindowStyle(xfContext* xfc, xfAppWindow* appWindow, UINT32 style,
|
||||
UINT32 ex_style)
|
||||
{
|
||||
Atom window_type;
|
||||
BOOL redirect = FALSE;
|
||||
|
||||
if ((ex_style & WS_EX_NOACTIVATE) || (ex_style & WS_EX_TOOLWINDOW))
|
||||
{
|
||||
/*
|
||||
* Tooltips and menu items should be unmanaged windows
|
||||
* (called "override redirect" in X windows parlance)
|
||||
* If they are managed, there are issues with window focus that
|
||||
* cause the windows to behave improperly. For example, a mouse
|
||||
* press will dismiss a drop-down menu because the RDP server
|
||||
* sees that as a focus out event from the window owning the
|
||||
* dropdown.
|
||||
*/
|
||||
XSetWindowAttributes attrs;
|
||||
attrs.override_redirect = True;
|
||||
XChangeWindowAttributes(xfc->display, appWindow->handle, CWOverrideRedirect,
|
||||
&attrs);
|
||||
redirect = TRUE;
|
||||
appWindow->is_transient = TRUE;
|
||||
xf_SetWindowUnlisted(xfc, appWindow->handle);
|
||||
window_type = xfc->_NET_WM_WINDOW_TYPE_POPUP;
|
||||
window_type = xfc->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
|
||||
}
|
||||
/*
|
||||
* TOPMOST window that is not a tool window is treated like a regular window (i.e. task manager).
|
||||
@ -684,6 +693,22 @@ void xf_SetWindowStyle(xfContext* xfc, xfAppWindow* appWindow, UINT32 style,
|
||||
window_type = xfc->_NET_WM_WINDOW_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* Tooltips and menu items should be unmanaged windows
|
||||
* (called "override redirect" in X windows parlance)
|
||||
* If they are managed, there are issues with window focus that
|
||||
* cause the windows to behave improperly. For example, a mouse
|
||||
* press will dismiss a drop-down menu because the RDP server
|
||||
* sees that as a focus out event from the window owning the
|
||||
* dropdown.
|
||||
*/
|
||||
XSetWindowAttributes attrs;
|
||||
attrs.override_redirect = redirect ? True : False;
|
||||
XChangeWindowAttributes(xfc->display, appWindow->handle, CWOverrideRedirect,
|
||||
&attrs);
|
||||
}
|
||||
|
||||
XChangeProperty(xfc->display, appWindow->handle, xfc->_NET_WM_WINDOW_TYPE,
|
||||
XA_ATOM, 32, PropModeReplace, (BYTE*) &window_type, 1);
|
||||
}
|
||||
@ -971,43 +996,6 @@ void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state)
|
||||
XFlush(xfc->display);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void xf_SetWindowIcon(xfContext* xfc, xfAppWindow* appWindow, rdpIcon* icon)
|
||||
{
|
||||
int x, y;
|
||||
int pixels;
|
||||
int propsize;
|
||||
long* propdata;
|
||||
long* dstp;
|
||||
UINT32* srcp;
|
||||
|
||||
if (!icon->big)
|
||||
return;
|
||||
|
||||
pixels = icon->entry->width * icon->entry->height;
|
||||
propsize = 2 + pixels;
|
||||
propdata = malloc(propsize * sizeof(long));
|
||||
propdata[0] = icon->entry->width;
|
||||
propdata[1] = icon->entry->height;
|
||||
dstp = &(propdata[2]);
|
||||
srcp = (UINT32*) icon->extra;
|
||||
|
||||
for (y = 0; y < icon->entry->height; y++)
|
||||
{
|
||||
for (x = 0; x < icon->entry->width; x++)
|
||||
{
|
||||
*dstp++ = *srcp++;
|
||||
}
|
||||
}
|
||||
|
||||
XChangeProperty(xfc->display, appWindow->handle, xfc->_NET_WM_ICON, XA_CARDINAL,
|
||||
32,
|
||||
PropModeReplace, (BYTE*) propdata, propsize);
|
||||
XFlush(xfc->display);
|
||||
free(propdata);
|
||||
}
|
||||
#endif
|
||||
|
||||
void xf_SetWindowRects(xfContext* xfc, xfAppWindow* appWindow,
|
||||
RECTANGLE_16* rects, int nrects)
|
||||
{
|
||||
|
||||
@ -30,6 +30,7 @@ typedef struct xf_localmove xfLocalMove;
|
||||
typedef struct xf_window xfWindow;
|
||||
|
||||
#include "xf_client.h"
|
||||
#include "xf_floatbar.h"
|
||||
#include "xfreerdp.h"
|
||||
|
||||
// Extended ICCM flags http://standards.freedesktop.org/wm-spec/wm-spec-latest.html
|
||||
@ -80,6 +81,7 @@ struct xf_window
|
||||
int shmid;
|
||||
Window handle;
|
||||
Window* xfwin;
|
||||
xfFloatbar* floatbar;
|
||||
BOOL decorations;
|
||||
BOOL is_mapped;
|
||||
BOOL is_transient;
|
||||
@ -143,6 +145,7 @@ BOOL xf_GetCurrentDesktop(xfContext* xfc);
|
||||
BOOL xf_GetWorkArea(xfContext* xfc);
|
||||
|
||||
void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen);
|
||||
void xf_SetWindowMinimized(xfContext* xfc, xfWindow* window);
|
||||
void xf_SetWindowDecorations(xfContext* xfc, Window window, BOOL show);
|
||||
void xf_SetWindowUnlisted(xfContext* xfc, Window window);
|
||||
|
||||
@ -154,7 +157,7 @@ Window xf_CreateDummyWindow(xfContext* xfc);
|
||||
void xf_DestroyDummyWindow(xfContext* xfc, Window window);
|
||||
|
||||
BOOL xf_GetWindowProperty(xfContext* xfc, Window window, Atom property, int length,
|
||||
unsigned long* nitems, unsigned long* bytes, BYTE** prop);
|
||||
unsigned long* nitems, unsigned long* bytes, BYTE** prop);
|
||||
void xf_SendClientEvent(xfContext* xfc, Window window, Atom atom, unsigned int numArgs, ...);
|
||||
|
||||
int xf_AppWindowInit(xfContext* xfc, xfAppWindow* appWindow);
|
||||
@ -163,13 +166,15 @@ void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int wid
|
||||
void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state);
|
||||
//void xf_SetWindowIcon(xfContext* xfc, xfAppWindow* appWindow, rdpIcon* icon);
|
||||
void xf_SetWindowRects(xfContext* xfc, xfAppWindow* appWindow, RECTANGLE_16* rects, int nrects);
|
||||
void xf_SetWindowVisibilityRects(xfContext* xfc, xfAppWindow* appWindow, UINT32 rectsOffsetX, UINT32 rectsOffsetY, RECTANGLE_16* rects, int nrects);
|
||||
void xf_SetWindowVisibilityRects(xfContext* xfc, xfAppWindow* appWindow, UINT32 rectsOffsetX,
|
||||
UINT32 rectsOffsetY, RECTANGLE_16* rects, int nrects);
|
||||
void xf_SetWindowStyle(xfContext* xfc, xfAppWindow* appWindow, UINT32 style, UINT32 ex_style);
|
||||
void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width, int height);
|
||||
void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width,
|
||||
int height);
|
||||
void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow);
|
||||
void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow,
|
||||
int maxWidth, int maxHeight, int maxPosX, int maxPosY,
|
||||
int minTrackWidth, int minTrackHeight, int maxTrackWidth, int maxTrackHeight);
|
||||
int maxWidth, int maxHeight, int maxPosX, int maxPosY,
|
||||
int minTrackWidth, int minTrackHeight, int maxTrackWidth, int maxTrackHeight);
|
||||
void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y);
|
||||
void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow);
|
||||
xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
|
||||
|
||||
@ -83,6 +83,7 @@ typedef struct xf_glyph xfGlyph;
|
||||
typedef struct xf_clipboard xfClipboard;
|
||||
typedef struct _xfDispContext xfDispContext;
|
||||
typedef struct _xfVideoContext xfVideoContext;
|
||||
typedef struct xf_rail_icon_cache xfRailIconCache;
|
||||
|
||||
/* Value of the first logical button number in X11 which must be */
|
||||
/* subtracted to go from a button number in X11 to an index into */
|
||||
@ -160,6 +161,7 @@ struct xf_context
|
||||
BOOL use_xinput;
|
||||
BOOL mouse_active;
|
||||
BOOL fullscreen_toggle;
|
||||
BOOL floatbar;
|
||||
BOOL controlToggle;
|
||||
UINT32 KeyboardLayout;
|
||||
BOOL KeyboardState[256];
|
||||
@ -203,6 +205,7 @@ struct xf_context
|
||||
Atom _NET_WM_WINDOW_TYPE_DIALOG;
|
||||
Atom _NET_WM_WINDOW_TYPE_UTILITY;
|
||||
Atom _NET_WM_WINDOW_TYPE_POPUP;
|
||||
Atom _NET_WM_WINDOW_TYPE_POPUP_MENU;
|
||||
Atom _NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
|
||||
|
||||
Atom _NET_WM_MOVERESIZE;
|
||||
@ -220,10 +223,10 @@ struct xf_context
|
||||
RdpeiClientContext* rdpei;
|
||||
EncomspClientContext* encomsp;
|
||||
xfDispContext* xfDisp;
|
||||
DispClientContext* disp;
|
||||
|
||||
RailClientContext* rail;
|
||||
wHashTable* railWindows;
|
||||
xfRailIconCache* railIconCache;
|
||||
|
||||
BOOL xkbAvailable;
|
||||
BOOL xrenderAvailable;
|
||||
@ -278,6 +281,7 @@ enum XF_EXIT_CODE
|
||||
XF_EXIT_PROTOCOL = 130,
|
||||
XF_EXIT_CONN_FAILED = 131,
|
||||
XF_EXIT_AUTH_FAILURE = 132,
|
||||
XF_EXIT_NEGO_FAILURE = 133,
|
||||
|
||||
XF_EXIT_UNKNOWN = 255,
|
||||
};
|
||||
|
||||
@ -306,6 +306,7 @@ int freerdp_client_settings_parse_assistance_file(rdpSettings* settings,
|
||||
const char* filename)
|
||||
{
|
||||
int status;
|
||||
int ret = -1;
|
||||
rdpAssistanceFile* file;
|
||||
file = freerdp_assistance_file_new();
|
||||
|
||||
@ -315,15 +316,17 @@ int freerdp_client_settings_parse_assistance_file(rdpSettings* settings,
|
||||
status = freerdp_assistance_parse_file(file, filename);
|
||||
|
||||
if (status < 0)
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
status = freerdp_client_populate_settings_from_assistance_file(file, settings);
|
||||
|
||||
if (status < 0)
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
freerdp_assistance_file_free(file);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Callback set in the rdp_freerdp structure, and used to get the user's password,
|
||||
@ -422,6 +425,12 @@ fail:
|
||||
BOOL client_cli_authenticate(freerdp* instance, char** username,
|
||||
char** password, char** domain)
|
||||
{
|
||||
if (instance->settings->SmartcardLogon)
|
||||
{
|
||||
WLog_INFO(TAG, "Authentication via smartcard");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return client_cli_authenticate_raw(instance, FALSE, username, password, domain);
|
||||
}
|
||||
|
||||
@ -544,4 +553,64 @@ DWORD client_cli_verify_changed_certificate(freerdp* instance,
|
||||
return client_cli_accept_certificate(instance->settings);
|
||||
}
|
||||
|
||||
BOOL client_auto_reconnect(freerdp* instance)
|
||||
{
|
||||
return client_auto_reconnect_ex(instance, NULL);
|
||||
}
|
||||
|
||||
BOOL client_auto_reconnect_ex(freerdp* instance, BOOL(*window_events)(freerdp* instance))
|
||||
{
|
||||
UINT32 maxRetries;
|
||||
UINT32 numRetries = 0;
|
||||
rdpSettings* settings;
|
||||
|
||||
if (!instance || !instance->settings)
|
||||
return FALSE;
|
||||
|
||||
settings = instance->settings;
|
||||
maxRetries = settings->AutoReconnectMaxRetries;
|
||||
|
||||
/* Only auto reconnect on network disconnects. */
|
||||
if (freerdp_error_info(instance) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* A network disconnect was detected */
|
||||
WLog_INFO(TAG, "Network disconnect!");
|
||||
|
||||
if (!settings->AutoReconnectionEnabled)
|
||||
{
|
||||
/* No auto-reconnect - just quit */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Perform an auto-reconnect. */
|
||||
while (TRUE)
|
||||
{
|
||||
UINT32 x;
|
||||
|
||||
/* Quit retrying if max retries has been exceeded */
|
||||
if ((maxRetries > 0) && (numRetries++ >= maxRetries))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Attempt the next reconnect */
|
||||
WLog_INFO(TAG, "Attempting reconnect (%"PRIu32" of %"PRIu32")", numRetries, maxRetries);
|
||||
|
||||
if (freerdp_reconnect(instance))
|
||||
return TRUE;
|
||||
|
||||
for (x = 0; x < 50; x++)
|
||||
{
|
||||
if (!IFCALLRESULT(TRUE, window_events, instance))
|
||||
return FALSE;
|
||||
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "Maximum reconnect retries exceeded");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "a", COMMAND_LINE_VALUE_REQUIRED, "<addin>[,<options>]", NULL, NULL, -1, "addin", "Addin" },
|
||||
{ "action-script", COMMAND_LINE_VALUE_REQUIRED, "<file-name>", "~/.config/freerdp/action.sh", NULL, -1, NULL, "Action script" },
|
||||
{ "admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "console", "Admin (or console) session" },
|
||||
{ "aero", COMMAND_LINE_VALUE_BOOL, NULL, NULL, BoolValueFalse, -1, NULL, "Enable desktop composition" },
|
||||
{ "aero", COMMAND_LINE_VALUE_BOOL, NULL, NULL, BoolValueFalse, -1, NULL, "desktop composition" },
|
||||
{ "app", COMMAND_LINE_VALUE_REQUIRED, "<path> or ||<alias>", NULL, NULL, -1, NULL, "Remote application program" },
|
||||
{ "app-cmd", COMMAND_LINE_VALUE_REQUIRED, "<parameters>", NULL, NULL, -1, NULL, "Remote application command-line parameters" },
|
||||
{ "app-file", COMMAND_LINE_VALUE_REQUIRED, "<file-name>", NULL, NULL, -1, NULL, "File to open with remote application" },
|
||||
@ -37,14 +37,13 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "assistance", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Remote assistance password" },
|
||||
{ "async-channels", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Asynchronous channels (experimental)" },
|
||||
{ "async-input", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Asynchronous input" },
|
||||
{ "async-transport", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Asynchronous transport (experimental)" },
|
||||
{ "async-update", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Asynchronous update" },
|
||||
{ "audio-mode", COMMAND_LINE_VALUE_REQUIRED, "<mode>", NULL, NULL, -1, NULL, "Audio output mode" },
|
||||
{ "auth-only", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Authenticate only" },
|
||||
{ "authentication", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Authentication (expermiental)" },
|
||||
{ "auto-reconnect", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Automatic reconnection" },
|
||||
{ "auto-reconnect-max-retries", COMMAND_LINE_VALUE_REQUIRED, "<retries>", NULL, NULL, -1, NULL, "Automatic reconnection maximum retries, 0 for unlimited [0,1000]" },
|
||||
{ "bitmap-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable bitmap cache" },
|
||||
{ "bitmap-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "bitmap cache" },
|
||||
{ "bpp", COMMAND_LINE_VALUE_REQUIRED, "<depth>", "16", NULL, -1, NULL, "Session bpp (color depth)" },
|
||||
{ "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, NULL, NULL, NULL, -1, NULL, "Print the build configuration" },
|
||||
{ "cert-ignore", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Ignore certificate" },
|
||||
@ -53,9 +52,9 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "client-hostname", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL, "Client Hostname to send to server" },
|
||||
{ "clipboard", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Redirect clipboard" },
|
||||
{ "codec-cache", COMMAND_LINE_VALUE_REQUIRED, "rfx|nsc|jpeg", NULL, NULL, -1, NULL, "Bitmap codec cache" },
|
||||
{ "compression", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, "z", "Enable compression" },
|
||||
{ "compression", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, "z", "compression" },
|
||||
{ "compression-level", COMMAND_LINE_VALUE_REQUIRED, "<level>", NULL, NULL, -1, NULL, "Compression level (0,1,2)" },
|
||||
{ "credentials-delegation", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Disable credentials delegation" },
|
||||
{ "credentials-delegation", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "credentials delegation" },
|
||||
{ "d", COMMAND_LINE_VALUE_REQUIRED, "<domain>", NULL, NULL, -1, NULL, "Domain" },
|
||||
{ "decorations", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Window decorations" },
|
||||
{ "disp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Display control" },
|
||||
@ -67,9 +66,10 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "encryption", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Encryption (experimental)" },
|
||||
{ "encryption-methods", COMMAND_LINE_VALUE_REQUIRED, "[40,][56,][128,][FIPS]", NULL, NULL, -1, NULL, "RDP standard security encryption methods" },
|
||||
{ "f", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Fullscreen mode (<Ctrl>+<Alt>+<Enter> toggles fullscreen)" },
|
||||
{ "fast-path", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable fast-path input/output" },
|
||||
{ "fipsmode", COMMAND_LINE_VALUE_BOOL, NULL, NULL, NULL, -1, NULL, "Enable FIPS mode" },
|
||||
{ "fonts", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Enable smooth fonts (ClearType)" },
|
||||
{ "fast-path", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "fast-path input/output" },
|
||||
{ "fipsmode", COMMAND_LINE_VALUE_BOOL, NULL, NULL, NULL, -1, NULL, "FIPS mode" },
|
||||
{ "floatbar", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "floatbar in fullscreen mode" },
|
||||
{ "fonts", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "smooth fonts (ClearType)" },
|
||||
{ "frame-ack", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Number of frame acknowledgement" },
|
||||
{ "from-stdin", COMMAND_LINE_VALUE_OPTIONAL, "force", NULL, NULL, -1, NULL, "Read credentials from stdin. With <force> the prompt is done before connection, otherwise on server request." },
|
||||
{ "g", COMMAND_LINE_VALUE_REQUIRED, "<gateway>[:<port>]", NULL, NULL, -1, NULL, "Gateway Hostname" },
|
||||
@ -99,7 +99,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "home-drive", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Redirect user home as share" },
|
||||
{ "ipv6", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "6", "Prefer IPv6 AAA record over IPv4 A record"},
|
||||
#if defined(WITH_JPEG)
|
||||
{ "jpeg", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Enable JPEG codec" },
|
||||
{ "jpeg", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "JPEG codec support" },
|
||||
{ "jpeg-quality", COMMAND_LINE_VALUE_REQUIRED, "<percentage>", NULL, NULL, -1, NULL, "JPEG quality" },
|
||||
#endif
|
||||
{ "kbd", COMMAND_LINE_VALUE_REQUIRED, "0x<id> or <name>", NULL, NULL, -1, NULL, "Keyboard layout" },
|
||||
@ -112,7 +112,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "log-level", COMMAND_LINE_VALUE_REQUIRED, "OFF|FATAL|ERROR|WARN|INFO|DEBUG|TRACE", NULL, NULL, -1, NULL, "Set the default log level, see wLog(7) for details" },
|
||||
{ "max-fast-path-size", COMMAND_LINE_VALUE_REQUIRED, "<size>", NULL, NULL, -1, NULL, "Specify maximum fast-path update size" },
|
||||
{ "max-loop-time", COMMAND_LINE_VALUE_REQUIRED, "<time>", NULL, NULL, -1, NULL, "Specify maximum time in milliseconds spend treating packets"},
|
||||
{ "menu-anims", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Enable menu animations" },
|
||||
{ "menu-anims", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "menu animations" },
|
||||
{ "microphone", COMMAND_LINE_VALUE_OPTIONAL, "[sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,][channel:<channel>]", NULL, NULL, -1, "mic", "Audio input (microphone)" },
|
||||
{ "monitor-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, NULL, NULL, NULL, -1, NULL, "List detected monitors" },
|
||||
{ "monitors", COMMAND_LINE_VALUE_REQUIRED, "<id>[,<id>[,...]]", NULL, NULL, -1, NULL, "Select monitors to use" },
|
||||
@ -121,11 +121,12 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "multimon", COMMAND_LINE_VALUE_OPTIONAL, "force", NULL, NULL, -1, NULL, "Use multiple monitors" },
|
||||
{ "multitouch", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Redirect multitouch input" },
|
||||
{ "multitransport", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Support multitransport protocol" },
|
||||
{ "nego", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable protocol security negotiation" },
|
||||
{ "nego", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "protocol security negotiation" },
|
||||
{ "network", COMMAND_LINE_VALUE_REQUIRED, "modem|broadband|broadband-low|broadband-high|wan|lan|auto", NULL, NULL, -1, NULL, "Network connection type" },
|
||||
{ "nsc", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "nscodec", "Enable NSCodec" },
|
||||
{ "offscreen-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable offscreen bitmap cache" },
|
||||
{ "nsc", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "nscodec", "NSCodec support" },
|
||||
{ "offscreen-cache", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "offscreen bitmap cache" },
|
||||
{ "orientation", COMMAND_LINE_VALUE_REQUIRED, "0|90|180|270", NULL, NULL, -1, NULL, "Orientation of display in degrees" },
|
||||
{ "old-license", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Use the old license workflow (no CAL and hwId set to 0)"},
|
||||
{ "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Password" },
|
||||
{ "parallel", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>]", NULL, NULL, -1, NULL, "Redirect parallel device" },
|
||||
{ "parent-window", COMMAND_LINE_VALUE_REQUIRED, "<window-id>", NULL, NULL, -1, NULL, "Parent window id" },
|
||||
@ -137,10 +138,12 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
|
||||
{ "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Print base64 reconnect cookie after connecting" },
|
||||
{ "printer", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<driver>]", NULL, NULL, -1, NULL, "Redirect printer device" },
|
||||
{ "proxy", COMMAND_LINE_VALUE_REQUIRED, "[<proto>://]<host>:<port>", NULL, NULL, -1, NULL, "Proxy (see also environment variable below)" },
|
||||
{ "proxy", COMMAND_LINE_VALUE_REQUIRED, "[<proto>://][<user>:<password>@]<host>:<port>", NULL, NULL, -1, NULL, "Proxy settings: override env.var (see also environment variable below).\n\tProtocol \"socks5\" should be given explicitly where \"http\" is default.\n\tNote: socks proxy is not supported by env. variable" },
|
||||
{ "pth", COMMAND_LINE_VALUE_REQUIRED, "<password-hash>", NULL, NULL, -1, "pass-the-hash", "Pass the hash (restricted admin mode)" },
|
||||
{ "pwidth", COMMAND_LINE_VALUE_REQUIRED, "<width>", NULL, NULL, -1, NULL, "Physical width of display (in millimeters)" },
|
||||
{ "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "<base64-cookie>", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" },
|
||||
{ "redirect-prefer", COMMAND_LINE_VALUE_REQUIRED, "<FQDN|IP|NETBIOS>[,<FQDN|IP|NETBIOS>[,<FQDN|IP|NETBIOS>]]", NULL, NULL, -1, NULL, "Override the preferred redirection order" },
|
||||
{ "relax-order-checks", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "relax-order-checks", "Do not check if a RDP order was announced during capability exchange, only use when connecting to a buggy server" },
|
||||
{ "restricted-admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "restrictedAdmin", "Restricted admin mode" },
|
||||
{ "rfx", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "RemoteFX" },
|
||||
{ "rfx-mode", COMMAND_LINE_VALUE_REQUIRED, "image|video", NULL, NULL, -1, NULL, "RemoteFX mode" },
|
||||
@ -157,14 +160,16 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "shell-dir", COMMAND_LINE_VALUE_REQUIRED, "<dir>", NULL, NULL, -1, NULL, "Shell working directory" },
|
||||
{ "size", COMMAND_LINE_VALUE_REQUIRED, "<width>x<height> or <percent>%[wh]", "1024x768", NULL, -1, NULL, "Screen size" },
|
||||
{ "smart-sizing", COMMAND_LINE_VALUE_OPTIONAL, "<width>x<height>", NULL, NULL, -1, NULL, "Scale remote desktop to window size" },
|
||||
{ "smartcard", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>]", NULL, NULL, -1, NULL, "Redirect smartcard device" },
|
||||
{ "smartcard", COMMAND_LINE_VALUE_OPTIONAL, "<str>[,<str>…]", NULL, NULL, -1, NULL, "Redirect the smartcard devices containing any of the <str> in their names." },
|
||||
{ "smartcard-logon", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Activates Smartcard Logon authentication. (EXPERIMENTAL: NLA not supported)" },
|
||||
{ "sound", COMMAND_LINE_VALUE_OPTIONAL, "[sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,][channel:<channel>,][latency:<latency>,][quality:<quality>]", NULL, NULL, -1, "audio", "Audio output (sound)" },
|
||||
{ "span", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Span screen over multiple monitors" },
|
||||
{ "spn-class", COMMAND_LINE_VALUE_REQUIRED, "<service-class>", NULL, NULL, -1, NULL, "SPN authentication service class" },
|
||||
{ "ssh-agent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "ssh-agent", "SSH Agent forwarding channel" },
|
||||
{ "t", COMMAND_LINE_VALUE_REQUIRED, "<title>", NULL, NULL, -1, "title", "Window title" },
|
||||
{ "themes", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable themes" },
|
||||
{ "themes", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "themes" },
|
||||
{ "tls-ciphers", COMMAND_LINE_VALUE_REQUIRED, "netmon|ma|ciphers", NULL, NULL, -1, NULL, "Allowed TLS ciphers" },
|
||||
{ "tls-seclevel", COMMAND_LINE_VALUE_REQUIRED, "<level>", "1", NULL, -1, NULL, "TLS security level - defaults to 1" },
|
||||
{ "toggle-fullscreen", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Alt+Ctrl+Enter toggles fullscreen" },
|
||||
{ "u", COMMAND_LINE_VALUE_REQUIRED, "[<domain>\\]<user> or <user>[@<domain>]", NULL, NULL, -1, NULL, "Username" },
|
||||
{ "unmap-buttons", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Let server see real physical pointer button"},
|
||||
@ -175,8 +180,9 @@ static COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "video", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Video optimized remoting channel" },
|
||||
{ "vmconnect", COMMAND_LINE_VALUE_OPTIONAL, "<vmid>", NULL, NULL, -1, NULL, "Hyper-V console (use port 2179, disable negotiation)" },
|
||||
{ "w", COMMAND_LINE_VALUE_REQUIRED, "<width>", "1024", NULL, -1, NULL, "Width" },
|
||||
{ "wallpaper", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable wallpaper" },
|
||||
{ "window-drag", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Enable full window drag" },
|
||||
{ "wallpaper", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "wallpaper" },
|
||||
{ "window-drag", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "full window drag" },
|
||||
{ "window-position", COMMAND_LINE_VALUE_REQUIRED, "<xpos>x<ypos>", NULL, NULL, -1, NULL, "window position" },
|
||||
{ "wm-class", COMMAND_LINE_VALUE_REQUIRED, "<class-name>", NULL, NULL, -1, NULL, "Set the WM_CLASS hint for the window instance" },
|
||||
{ "workarea", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Use available work area" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
|
||||
@ -232,7 +232,7 @@ static int freerdp_client_old_process_plugin(rdpSettings* settings, ADDIN_ARGV*
|
||||
return args_handled;
|
||||
}
|
||||
static int freerdp_client_old_command_line_pre_filter(void* context, int index, int argc,
|
||||
LPCSTR* argv)
|
||||
LPSTR* argv)
|
||||
{
|
||||
rdpSettings* settings = (rdpSettings*) context;
|
||||
|
||||
@ -251,7 +251,7 @@ static int freerdp_client_old_command_line_pre_filter(void* context, int index,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!freerdp_client_old_parse_hostname((char*) argv[index],
|
||||
if (!freerdp_client_old_parse_hostname(argv[index],
|
||||
&settings->ServerHostname, &settings->ServerPort))
|
||||
return -1;
|
||||
|
||||
@ -310,7 +310,7 @@ static int freerdp_client_old_command_line_pre_filter(void* context, int index,
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0, p = (char*) argv[index]; (j < 4) && (p != NULL); j++)
|
||||
for (j = 0, p = argv[index]; (j < 4) && (p != NULL); j++)
|
||||
{
|
||||
if (*p == '\'')
|
||||
{
|
||||
@ -422,7 +422,7 @@ int freerdp_detect_old_command_line_syntax(int argc, char** argv, int* count)
|
||||
return -1;
|
||||
|
||||
CommandLineClearArgumentsA(old_args);
|
||||
status = CommandLineParseArgumentsA(argc, (const char**) argv, old_args, flags, settings,
|
||||
status = CommandLineParseArgumentsA(argc, argv, old_args, flags, settings,
|
||||
freerdp_client_old_command_line_pre_filter, NULL);
|
||||
|
||||
if (status < 0)
|
||||
@ -481,7 +481,7 @@ int freerdp_client_parse_old_command_line_arguments(int argc, char** argv, rdpSe
|
||||
flags |= COMMAND_LINE_SIGIL_DASH | COMMAND_LINE_SIGIL_DOUBLE_DASH;
|
||||
flags |= COMMAND_LINE_SIGIL_ENABLE_DISABLE;
|
||||
flags |= COMMAND_LINE_SIGIL_NOT_ESCAPED;
|
||||
status = CommandLineParseArgumentsA(argc, (const char**) argv, old_args, flags, settings,
|
||||
status = CommandLineParseArgumentsA(argc, argv, old_args, flags, settings,
|
||||
freerdp_client_old_command_line_pre_filter, freerdp_client_old_command_line_post_filter);
|
||||
|
||||
if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user