Merge branch 'master' of github.com:awakecoding/FreeRDP into smartcard
Conflicts: channels/smartcard/client/smartcard_main.c
This commit is contained in:
commit
8cdb3576c2
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,7 +5,7 @@ CMakeCache.txt
|
||||
config.h
|
||||
install_manifest*.txt
|
||||
CTestTestfile.cmake
|
||||
freerdp.pc
|
||||
*.pc
|
||||
Makefile
|
||||
Testing
|
||||
cmake_install.cmake
|
||||
@ -19,6 +19,7 @@ LICENSE.txt
|
||||
!external/README
|
||||
*Config.cmake
|
||||
*ConfigVersion.cmake
|
||||
include/freerdp/version.h
|
||||
|
||||
*.a.objlist.cmake
|
||||
*.a.objlist
|
||||
|
110
CMakeLists.txt
110
CMakeLists.txt
@ -55,7 +55,7 @@ include(CMakePackageConfigHelpers)
|
||||
|
||||
# Soname versioning
|
||||
set(FREERDP_VERSION_MAJOR "1")
|
||||
set(FREERDP_VERSION_MINOR "1")
|
||||
set(FREERDP_VERSION_MINOR "2")
|
||||
set(FREERDP_VERSION_REVISION "0")
|
||||
set(FREERDP_VERSION_SUFFIX "beta1")
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}")
|
||||
@ -68,11 +68,11 @@ endif()
|
||||
|
||||
# Allow to search the host machine for git
|
||||
if(ANDROID OR IOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER, BOTH)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
endif(ANDROID OR IOS)
|
||||
include(GetGitRevisionDescription)
|
||||
if(ANDROID OR IOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER, ONLY)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
endif(ANDROID OR IOS)
|
||||
|
||||
git_describe(GIT_REVISION --match "[0-9]*" --abbrev=4 --tags --always)
|
||||
@ -133,6 +133,16 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
|
||||
# Hack to strip absolute paths out of the __FILE__ macro
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILE__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILE__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
|
||||
CHECK_C_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefined)
|
||||
if(Wno-builtin-macro-redefined)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined")
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG (-Wno-unused-result Wno-unused-result)
|
||||
if(Wno-unused-result)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result")
|
||||
@ -144,6 +154,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
CHECK_C_COMPILER_FLAG(-Wno-deprecated-declarations Wno-deprecated-declarations)
|
||||
if(Wno-deprecated-declarations)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
|
||||
endif()
|
||||
if(NOT EXPORT_ALL_SYMBOLS)
|
||||
message(STATUS "GCC default symbol visibility: hidden")
|
||||
@ -160,6 +171,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
endif()
|
||||
if(WITH_SSE2)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2")
|
||||
@ -176,22 +188,22 @@ if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||
CHECK_C_COMPILER_FLAG(-Wno-deprecated-declarations Wno-deprecated-declarations)
|
||||
if(Wno-deprecated-declarations)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gd")
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MD")
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /O2")
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ob2")
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W2")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_AMD64_")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_X86_")
|
||||
endif()
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
else()
|
||||
@ -214,28 +226,29 @@ endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_EXPORTS")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFREERDP_EXPORTS")
|
||||
|
||||
# Include files
|
||||
if(NOT IOS)
|
||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
else()
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_UNISTD_H 1)
|
||||
set(HAVE_STDINT_H 1)
|
||||
set(HAVE_INTTYPES_H 1)
|
||||
set(HAVE_SYS_FILIO_H 1)
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_UNISTD_H 1)
|
||||
set(HAVE_STDINT_H 1)
|
||||
set(HAVE_INTTYPES_H 1)
|
||||
set(HAVE_SYS_FILIO_H 1)
|
||||
endif()
|
||||
|
||||
if(NOT IOS)
|
||||
check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
|
||||
check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
|
||||
else()
|
||||
set(HAVE_TM_GMTOFF 1)
|
||||
set(HAVE_TM_GMTOFF 1)
|
||||
endif()
|
||||
|
||||
# Mac OS X
|
||||
@ -273,7 +286,11 @@ endif(APPLE)
|
||||
if(ANDROID)
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
add_definitions(-DNDK_DEBUG=1)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG}")
|
||||
|
||||
# NOTE: Manually add -gdwarf-3, as newer toolchains default to -gdwarf-4,
|
||||
# which is not supported by the gdbserver binary shipped with
|
||||
# the android NDK (tested with r9b)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
|
||||
endif()
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -llog")
|
||||
if (NOT FREERDP_ANDROID_EXTERNAL_SSL_PATH)
|
||||
@ -294,9 +311,13 @@ if(ANDROID)
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_ANDROID_EXTERNAL_SSL_PATH})
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/libs/${ANDROID_ABI})
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh @ONLY)
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/jni/${ANDROID_ABI})
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake
|
||||
${CMAKE_BINARY_DIR}/scripts/regenerate_jni_headers.sh @ONLY)
|
||||
if (WITH_GPROF)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/gprof_generate.sh.cmake ${CMAKE_BINARY_DIR}/scripts/gprof_generate.sh @ONLY)
|
||||
endif(WITH_GPROF)
|
||||
|
||||
endif()
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
@ -305,6 +326,12 @@ if(NOT IOS AND NOT ANDROID)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
|
||||
check_library_exists(pthread pthread_tryjoin_np "" HAVE_PTHREAD_GNU_EXT)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
|
||||
endif()
|
||||
|
||||
if(UNIX OR CYGWIN)
|
||||
check_include_files(sys/eventfd.h HAVE_AIO_H)
|
||||
check_include_files(sys/eventfd.h HAVE_EVENTFD_H)
|
||||
@ -361,14 +388,14 @@ set(IPP_FEATURE_TYPE "OPTIONAL")
|
||||
set(IPP_FEATURE_PURPOSE "performance")
|
||||
set(IPP_FEATURE_DESCRIPTION "Intel Integrated Performance Primitives library")
|
||||
|
||||
set(NPP_FEATURE_TYPE "OPTIONAL")
|
||||
set(NPP_FEATURE_PURPOSE "performance")
|
||||
set(NPP_FEATURE_DESCRIPTION "NVIDIA Performance Primitives library")
|
||||
|
||||
set(JPEG_FEATURE_TYPE "OPTIONAL")
|
||||
set(JPEG_FEATURE_PURPOSE "codec")
|
||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
||||
|
||||
set(GSM_FEATURE_TYPE "OPTIONAL")
|
||||
set(GSM_FEATURE_PURPOSE "codec")
|
||||
set(GSM_FEATURE_DESCRIPTION "GSM audio codec library")
|
||||
|
||||
if(WIN32)
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
set(ZLIB_FEATURE_TYPE "DISABLED")
|
||||
@ -414,9 +441,7 @@ endif()
|
||||
find_feature(X11 ${X11_FEATURE_TYPE} ${X11_FEATURE_PURPOSE} ${X11_FEATURE_DESCRIPTION})
|
||||
find_feature(DirectFB ${DIRECTFB_FEATURE_TYPE} ${DIRECTFB_FEATURE_PURPOSE} ${DIRECTFB_FEATURE_DESCRIPTION})
|
||||
if (${WITH_DIRECTFB})
|
||||
message(WARNING "
|
||||
DIRECTFB is orphaned and not maintained see docs/README.directfb for details
|
||||
")
|
||||
message(WARNING "DIRECTFB is orphaned and not maintained see docs/README.directfb for details")
|
||||
endif()
|
||||
|
||||
find_feature(ZLIB ${ZLIB_FEATURE_TYPE} ${ZLIB_FEATURE_PURPOSE} ${ZLIB_FEATURE_DESCRIPTION})
|
||||
@ -433,19 +458,13 @@ find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FE
|
||||
find_feature(Gstreamer ${GSTREAMER_FEATURE_TYPE} ${GSTREAMER_FEATURE_PURPOSE} ${GSTREAMER_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION})
|
||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||
|
||||
if(TARGET_ARCH MATCHES "x86|x64")
|
||||
if (NOT APPLE)
|
||||
# Intel Performance Primitives
|
||||
find_feature(IPP ${IPP_FEATURE_TYPE} ${IPP_FEATURE_PURPOSE} ${IPP_FEATURE_DESCRIPTION})
|
||||
endif()
|
||||
find_feature(NPP ${NPP_FEATURE_TYPE} ${NPP_FEATURE_PURPOSE} ${NPP_FEATURE_DESCRIPTION})
|
||||
endif()
|
||||
|
||||
# Installation Paths
|
||||
if(WIN32)
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
set(CMAKE_INSTALL_LIBDIR ".")
|
||||
endif()
|
||||
|
||||
# Path to put FreeRDP data
|
||||
@ -463,6 +482,7 @@ set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp/extensions")
|
||||
|
||||
# Include directories
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Configure files
|
||||
@ -507,12 +527,6 @@ include_directories("${CMAKE_BINARY_DIR}/winpr/include")
|
||||
|
||||
add_subdirectory(winpr)
|
||||
|
||||
# Generate pkg-config
|
||||
if(NOT MSVC)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
endif()
|
||||
|
||||
if(WITH_CUNIT)
|
||||
message(FATAL_ERROR "cunit (WITH_CUNIT) is deprecated please use BUILD_TESTING to build ctest tests.
|
||||
The cunit directory contains the old tests and is kept until all tests are converted.")
|
||||
|
@ -49,3 +49,7 @@ endif()
|
||||
if(WITH_OPENSLES)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
|
||||
endif()
|
||||
|
||||
if(WITH_WINMM)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "")
|
||||
endif()
|
||||
|
@ -36,10 +36,5 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${ALSA_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
if(NOT STATIC_CHANNELS)
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT FreeRDPTargets)
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT FreeRDPTargets)
|
||||
endif()
|
||||
|
@ -102,7 +102,7 @@ static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, w
|
||||
out = Stream_New(NULL, 5);
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
|
||||
Stream_Write_UINT32(out, Version);
|
||||
error = callback->channel->Write(callback->channel, Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
Stream_Free(out, TRUE);
|
||||
|
||||
return error;
|
||||
@ -183,7 +183,7 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
|
||||
|
||||
audin_send_incoming_data_pdu(pChannelCallback);
|
||||
|
||||
cbSizeFormatsPacket = Stream_GetPosition(out);
|
||||
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
|
||||
Stream_SetPosition(out, 0);
|
||||
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
|
||||
@ -240,7 +240,7 @@ static BOOL audin_receive_wave_data(BYTE* data, int size, void* user_data)
|
||||
out = Stream_New(NULL, size + 1);
|
||||
Stream_Write_UINT8(out, MSG_SNDIN_DATA);
|
||||
Stream_Write(out, data, size);
|
||||
error = callback->channel->Write(callback->channel, Stream_GetPosition(out), Stream_Buffer(out), NULL);
|
||||
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(out), Stream_Buffer(out), NULL);
|
||||
Stream_Free(out, TRUE);
|
||||
|
||||
return (error == 0 ? TRUE : FALSE);
|
||||
@ -607,6 +607,15 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_WINMM)
|
||||
if (!audin->device)
|
||||
{
|
||||
audin_set_subsystem(audin, "winmm");
|
||||
audin_set_device_name(audin, "default");
|
||||
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (audin->device == NULL)
|
||||
{
|
||||
DEBUG_WARN("no sound device.");
|
||||
|
41
channels/audin/client/winmm/CMakeLists.txt
Normal file
41
channels/audin/client/winmm/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client_subsystem("audin" "winmm" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
audin_winmm.c)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
|
||||
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE freerdp
|
||||
MODULES freerdp-utils)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winmm.lib)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/winmm")
|
309
channels/audin/client/winmm/audin_winmm.c
Normal file
309
channels/audin/client/winmm/audin_winmm.c
Normal file
@ -0,0 +1,309 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - WinMM implementation
|
||||
*
|
||||
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <MMSystem.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <freerdp/addin.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
typedef struct _AudinWinmmDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
char* device_name;
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
HANDLE thread;
|
||||
HANDLE stopEvent;
|
||||
HWAVEIN hWaveIn;
|
||||
PWAVEFORMATEX *ppwfx;
|
||||
PWAVEFORMATEX pwfx_cur;
|
||||
UINT32 ppwfx_size;
|
||||
UINT32 cFormats;
|
||||
UINT32 frames_per_packet;
|
||||
} AudinWinmmDevice;
|
||||
|
||||
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
|
||||
PWAVEHDR pWaveHdr;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WIM_CLOSE:
|
||||
break;
|
||||
|
||||
case WIM_DATA:
|
||||
pWaveHdr = (WAVEHDR *)dwParam1;
|
||||
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
|
||||
{
|
||||
if (pWaveHdr->dwBytesRecorded
|
||||
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
|
||||
{
|
||||
winmm->receive(pWaveHdr->lpData, pWaveHdr->dwBytesRecorded, winmm->user_data);
|
||||
waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WIM_OPEN:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD audin_winmm_thread_func(void* arg)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) arg;
|
||||
char *buffer;
|
||||
int size, i;
|
||||
WAVEHDR waveHdr[4];
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
waveHdr[i].dwBufferLength = size;
|
||||
waveHdr[i].dwFlags = 0;
|
||||
waveHdr[i].lpData = buffer;
|
||||
if (MMSYSERR_NOERROR != waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
{
|
||||
DEBUG_DVC("waveInPrepareHeader failed.");
|
||||
}
|
||||
if (MMSYSERR_NOERROR != waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
{
|
||||
DEBUG_DVC("waveInAddBuffer failed.");
|
||||
}
|
||||
}
|
||||
waveInStart(winmm->hWaveIn);
|
||||
|
||||
WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
|
||||
waveInStop(winmm->hWaveIn);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i])))
|
||||
{
|
||||
DEBUG_DVC("waveInUnprepareHeader failed.");
|
||||
}
|
||||
free(waveHdr[i].lpData);
|
||||
}
|
||||
|
||||
waveInClose(winmm->hWaveIn);
|
||||
winmm->hWaveIn = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audin_winmm_free(IAudinDevice* device)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
free(winmm->ppwfx[i]);
|
||||
}
|
||||
|
||||
free(winmm->ppwfx);
|
||||
free(winmm->device_name);
|
||||
free(winmm);
|
||||
}
|
||||
|
||||
static void audin_winmm_close(IAudinDevice* device)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
SetEvent(winmm->stopEvent);
|
||||
|
||||
WaitForSingleObject(winmm->thread, INFINITE);
|
||||
|
||||
CloseHandle(winmm->thread);
|
||||
CloseHandle(winmm->stopEvent);
|
||||
|
||||
winmm->thread = NULL;
|
||||
winmm->stopEvent = NULL;
|
||||
winmm->receive = NULL;
|
||||
winmm->user_data = NULL;
|
||||
}
|
||||
|
||||
static void audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
int i;
|
||||
|
||||
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->pwfx_cur = winmm->ppwfx[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
PWAVEFORMATEX pwfx;
|
||||
BYTE *data;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
pwfx->cbSize = format->cbSize;
|
||||
pwfx->wFormatTag = format->wFormatTag;
|
||||
pwfx->nChannels = format->nChannels;
|
||||
pwfx->nSamplesPerSec = format->nSamplesPerSec;
|
||||
pwfx->nBlockAlign = format->nBlockAlign;
|
||||
pwfx->wBitsPerSample = format->wBitsPerSample;
|
||||
data = (BYTE *)pwfx + sizeof(WAVEFORMATEX);
|
||||
|
||||
memcpy(data, format->data, format->cbSize);
|
||||
|
||||
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)
|
||||
{
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
}
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(pwfx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
DEBUG_DVC("");
|
||||
|
||||
winmm->receive = receive;
|
||||
winmm->user_data = user_data;
|
||||
|
||||
winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
winmm->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) audin_winmm_thread_func, winmm, 0, NULL);
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
|
||||
{
|
||||
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static void audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_winmm_args, flags, winmm, NULL, NULL);
|
||||
|
||||
arg = audin_winmm_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
|
||||
CommandLineSwitchCase(arg, "audio-dev")
|
||||
{
|
||||
winmm->device_name = _strdup(arg->Value);
|
||||
}
|
||||
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
AudinWinmmDevice* winmm;
|
||||
|
||||
winmm = (AudinWinmmDevice*) malloc(sizeof(AudinWinmmDevice));
|
||||
ZeroMemory(winmm, sizeof(AudinWinmmDevice));
|
||||
|
||||
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;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
audin_winmm_parse_addin_args(winmm, args);
|
||||
|
||||
if (!winmm->device_name)
|
||||
winmm->device_name = _strdup("default");
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
|
||||
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm);
|
||||
|
||||
return 0;
|
||||
}
|
@ -29,6 +29,8 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MODULE freerdp
|
||||
MODULES freerdp-codec freerdp-utils)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -55,6 +55,8 @@ typedef struct _audin_server
|
||||
HANDLE thread;
|
||||
void* audin_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
} audin_server;
|
||||
@ -397,7 +399,20 @@ static BOOL audin_server_open(audin_server_context* context)
|
||||
|
||||
if (!audin->thread)
|
||||
{
|
||||
audin->audin_channel = WTSVirtualChannelManagerOpenEx(context->vcm, "AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
PULONG pSessionId = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
|
||||
audin->SessionId = WTS_CURRENT_SESSION;
|
||||
|
||||
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION,
|
||||
WTSSessionId, (LPSTR*) pSessionId, &BytesReturned))
|
||||
{
|
||||
audin->SessionId = (DWORD) *pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
}
|
||||
|
||||
audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId,
|
||||
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!audin->audin_channel)
|
||||
return FALSE;
|
||||
@ -438,12 +453,11 @@ static BOOL audin_server_close(audin_server_context* context)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
audin_server_context* audin_server_context_new(WTSVirtualChannelManager* vcm)
|
||||
audin_server_context* audin_server_context_new(HANDLE vcm)
|
||||
{
|
||||
audin_server* audin;
|
||||
|
||||
audin = (audin_server*) malloc(sizeof(audin_server));
|
||||
ZeroMemory(audin, sizeof(audin_server));
|
||||
audin = (audin_server*) calloc(1, sizeof(audin_server));
|
||||
|
||||
audin->context.vcm = vcm;
|
||||
audin->context.selected_client_format = -1;
|
||||
|
@ -22,13 +22,7 @@ set(${MODULE_PREFIX}_SRCS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tables.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tables.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/addin.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/addin.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/init.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/init.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/open.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/open.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/channels.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/channels.h)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/addin.h)
|
||||
|
||||
list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
|
||||
|
||||
@ -40,7 +34,11 @@ foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_NAME})
|
||||
|
||||
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${${STATIC_MODULE}_CLIENT_ENTRY}")
|
||||
set(ENTRY_POINT_IMPORT "extern void ${ENTRY_POINT_NAME}();")
|
||||
if(${${STATIC_MODULE}_CLIENT_ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern void ${ENTRY_POINT_NAME}();")
|
||||
endif()
|
||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
|
||||
endif()
|
||||
|
@ -1,135 +0,0 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Client Channels
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "channels.h"
|
||||
|
||||
#include "init.h"
|
||||
|
||||
extern int g_open_handle_sequence;
|
||||
|
||||
extern void* g_pInterface;
|
||||
extern CHANNEL_INIT_DATA g_ChannelInitData;
|
||||
|
||||
UINT32 FreeRDP_VirtualChannelInit(void** ppInitHandle, PCHANNEL_DEF pChannel,
|
||||
int channelCount, UINT32 versionRequested, PCHANNEL_INIT_EVENT_FN pChannelInitEventProc)
|
||||
{
|
||||
int index;
|
||||
void* pInterface;
|
||||
rdpChannel* channel;
|
||||
rdpChannels* channels;
|
||||
PCHANNEL_DEF pChannelDef;
|
||||
CHANNEL_INIT_DATA* pChannelInitData;
|
||||
CHANNEL_OPEN_DATA* pChannelOpenData;
|
||||
CHANNEL_CLIENT_DATA* pChannelClientData;
|
||||
|
||||
if (!ppInitHandle)
|
||||
{
|
||||
DEBUG_CHANNELS("error bad init handle");
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
|
||||
channels = g_ChannelInitData.channels;
|
||||
pInterface = g_pInterface;
|
||||
|
||||
pChannelInitData = &(channels->initDataList[channels->initDataCount]);
|
||||
*ppInitHandle = pChannelInitData;
|
||||
channels->initDataCount++;
|
||||
|
||||
pChannelInitData->channels = channels;
|
||||
pChannelInitData->pInterface = pInterface;
|
||||
|
||||
DEBUG_CHANNELS("enter");
|
||||
|
||||
if (!channels->can_call_init)
|
||||
{
|
||||
DEBUG_CHANNELS("error not in entry");
|
||||
return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY;
|
||||
}
|
||||
|
||||
if (channels->openDataCount + channelCount >= CHANNEL_MAX_COUNT)
|
||||
{
|
||||
DEBUG_CHANNELS("error too many channels");
|
||||
return CHANNEL_RC_TOO_MANY_CHANNELS;
|
||||
}
|
||||
|
||||
if (!pChannel)
|
||||
{
|
||||
DEBUG_CHANNELS("error bad channel");
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
}
|
||||
|
||||
if (channels->is_connected)
|
||||
{
|
||||
DEBUG_CHANNELS("error already connected");
|
||||
return CHANNEL_RC_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
if (versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000)
|
||||
{
|
||||
DEBUG_CHANNELS("warning version");
|
||||
}
|
||||
|
||||
for (index = 0; index < channelCount; index++)
|
||||
{
|
||||
pChannelDef = &pChannel[index];
|
||||
|
||||
if (freerdp_channels_find_channel_open_data_by_name(channels, pChannelDef->name) != 0)
|
||||
{
|
||||
DEBUG_CHANNELS("error channel already used");
|
||||
return CHANNEL_RC_BAD_CHANNEL;
|
||||
}
|
||||
}
|
||||
|
||||
pChannelClientData = &channels->clientDataList[channels->clientDataCount];
|
||||
pChannelClientData->pChannelInitEventProc = pChannelInitEventProc;
|
||||
pChannelClientData->pInitHandle = *ppInitHandle;
|
||||
channels->clientDataCount++;
|
||||
|
||||
for (index = 0; index < channelCount; index++)
|
||||
{
|
||||
pChannelDef = &pChannel[index];
|
||||
pChannelOpenData = &channels->openDataList[channels->openDataCount];
|
||||
|
||||
pChannelOpenData->OpenHandle = g_open_handle_sequence++;
|
||||
|
||||
pChannelOpenData->flags = 1; /* init */
|
||||
strncpy(pChannelOpenData->name, pChannelDef->name, CHANNEL_NAME_LEN);
|
||||
pChannelOpenData->options = pChannelDef->options;
|
||||
|
||||
if (channels->settings->ChannelCount < CHANNEL_MAX_COUNT)
|
||||
{
|
||||
channel = channels->settings->ChannelDefArray + channels->settings->ChannelCount;
|
||||
strncpy(channel->Name, pChannelDef->name, 7);
|
||||
channel->options = pChannelDef->options;
|
||||
channels->settings->ChannelCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CHANNELS("warning more than %d channels", CHANNEL_MAX_COUNT);
|
||||
}
|
||||
|
||||
channels->openDataCount++;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Client Channels
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "open.h"
|
||||
|
||||
UINT32 FreeRDP_VirtualChannelOpen(void* pInitHandle, UINT32* pOpenHandle,
|
||||
char* pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc)
|
||||
{
|
||||
void* pInterface;
|
||||
rdpChannels* channels;
|
||||
CHANNEL_INIT_DATA* pChannelInitData;
|
||||
CHANNEL_OPEN_DATA* pChannelOpenData;
|
||||
|
||||
DEBUG_CHANNELS("enter");
|
||||
|
||||
pChannelInitData = (CHANNEL_INIT_DATA*) pInitHandle;
|
||||
channels = pChannelInitData->channels;
|
||||
pInterface = pChannelInitData->pInterface;
|
||||
|
||||
if (!pOpenHandle)
|
||||
{
|
||||
DEBUG_CHANNELS("error bad channel handle");
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
}
|
||||
|
||||
if (!pChannelOpenEventProc)
|
||||
{
|
||||
DEBUG_CHANNELS("error bad proc");
|
||||
return CHANNEL_RC_BAD_PROC;
|
||||
}
|
||||
|
||||
if (!channels->is_connected)
|
||||
{
|
||||
DEBUG_CHANNELS("error not connected");
|
||||
return CHANNEL_RC_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
pChannelOpenData = freerdp_channels_find_channel_open_data_by_name(channels, pChannelName);
|
||||
|
||||
if (!pChannelOpenData)
|
||||
{
|
||||
DEBUG_CHANNELS("error channel name");
|
||||
return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
|
||||
}
|
||||
|
||||
if (pChannelOpenData->flags == 2)
|
||||
{
|
||||
DEBUG_CHANNELS("error channel already open");
|
||||
return CHANNEL_RC_ALREADY_OPEN;
|
||||
}
|
||||
|
||||
pChannelOpenData->flags = 2; /* open */
|
||||
pChannelOpenData->pInterface = pInterface;
|
||||
pChannelOpenData->pChannelOpenEventProc = pChannelOpenEventProc;
|
||||
*pOpenHandle = pChannelOpenData->OpenHandle;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
UINT32 FreeRDP_VirtualChannelClose(UINT32 openHandle)
|
||||
{
|
||||
int index;
|
||||
rdpChannels* channels;
|
||||
CHANNEL_OPEN_DATA* pChannelOpenData;
|
||||
|
||||
DEBUG_CHANNELS("enter");
|
||||
|
||||
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
|
||||
|
||||
if ((channels == NULL) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
|
||||
{
|
||||
DEBUG_CHANNELS("error bad channels");
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
|
||||
}
|
||||
|
||||
pChannelOpenData = &channels->openDataList[index];
|
||||
|
||||
if (pChannelOpenData->flags != 2)
|
||||
{
|
||||
DEBUG_CHANNELS("error not open");
|
||||
return CHANNEL_RC_NOT_OPEN;
|
||||
}
|
||||
|
||||
pChannelOpenData->flags = 0;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
@ -213,119 +213,223 @@ void cliprdr_process_long_format_names(cliprdrPlugin* cliprdr, wStream* s, UINT3
|
||||
|
||||
void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
int i;
|
||||
UINT32 format;
|
||||
BOOL supported;
|
||||
CLIPRDR_FORMAT_NAME* format_name;
|
||||
RDP_CB_FORMAT_LIST_EVENT* cb_event;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FormatList, NULL, NULL);
|
||||
|
||||
if (dataLen > 0)
|
||||
if (context->custom)
|
||||
{
|
||||
cb_event->raw_format_data = (BYTE*) malloc(dataLen);
|
||||
memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen);
|
||||
cb_event->raw_format_data_size = dataLen;
|
||||
}
|
||||
UINT32 index;
|
||||
int formatNameLength;
|
||||
CLIPRDR_FORMAT* formats;
|
||||
CLIPRDR_FORMAT_LIST formatList;
|
||||
|
||||
if (cliprdr->use_long_format_names)
|
||||
cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
else
|
||||
cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
formatList.msgType = CB_FORMAT_LIST;
|
||||
formatList.msgFlags = msgFlags;
|
||||
formatList.dataLen = dataLen;
|
||||
|
||||
if (cliprdr->num_format_names > 0)
|
||||
cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names);
|
||||
formatList.cFormats = 0;
|
||||
|
||||
cb_event->num_formats = 0;
|
||||
|
||||
for (i = 0; i < cliprdr->num_format_names; i++)
|
||||
{
|
||||
supported = TRUE;
|
||||
format_name = &cliprdr->format_names[i];
|
||||
format = format_name->id;
|
||||
|
||||
switch (format)
|
||||
while (dataLen)
|
||||
{
|
||||
case CB_FORMAT_TEXT:
|
||||
case CB_FORMAT_DIB:
|
||||
case CB_FORMAT_UNICODETEXT:
|
||||
break;
|
||||
Stream_Seek(s, 4); /* formatId */
|
||||
dataLen -= 4;
|
||||
|
||||
default:
|
||||
|
||||
if (format_name->length > 0)
|
||||
{
|
||||
DEBUG_CLIPRDR("format: %s", format_name->name);
|
||||
|
||||
if (strcmp(format_name->name, "HTML Format") == 0)
|
||||
{
|
||||
format = CB_FORMAT_HTML;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "PNG") == 0)
|
||||
{
|
||||
format = CB_FORMAT_PNG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "JFIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_JPEG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "GIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_GIF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
supported = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
formatNameLength = _wcslen((WCHAR*) Stream_Pointer(s));
|
||||
Stream_Seek(s, (formatNameLength + 1) * 2);
|
||||
dataLen -= ((formatNameLength + 1) * 2);
|
||||
formatList.cFormats++;
|
||||
}
|
||||
|
||||
if (supported)
|
||||
cb_event->formats[cb_event->num_formats++] = format;
|
||||
index = 0;
|
||||
dataLen = formatList.dataLen;
|
||||
Stream_Rewind(s, dataLen);
|
||||
|
||||
if (format_name->length > 0)
|
||||
free(format_name->name);
|
||||
formats = (CLIPRDR_FORMAT*) malloc(sizeof(CLIPRDR_FORMAT) * formatList.cFormats);
|
||||
formatList.formats = formats;
|
||||
|
||||
while (dataLen)
|
||||
{
|
||||
Stream_Read_UINT32(s, formats[index].formatId); /* formatId */
|
||||
dataLen -= 4;
|
||||
|
||||
formats[index].formatName = NULL;
|
||||
formatNameLength = _wcslen((WCHAR*) Stream_Pointer(s));
|
||||
|
||||
if (formatNameLength)
|
||||
{
|
||||
formatNameLength = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s),
|
||||
-1, &(formats[index].formatName), 0, NULL, NULL);
|
||||
|
||||
Stream_Seek(s, formatNameLength * 2);
|
||||
dataLen -= (formatNameLength * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Seek(s, 2);
|
||||
dataLen -= 2;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (context->ServerFormatList)
|
||||
context->ServerFormatList(context, &formatList);
|
||||
|
||||
for (index = 0; index < formatList.cFormats; index++)
|
||||
free(formats[index].formatName);
|
||||
|
||||
free(formats);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
UINT32 format;
|
||||
BOOL supported;
|
||||
CLIPRDR_FORMAT_NAME* format_name;
|
||||
RDP_CB_FORMAT_LIST_EVENT* cb_event;
|
||||
|
||||
free(cliprdr->format_names);
|
||||
cliprdr->format_names = NULL;
|
||||
cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FormatList, NULL, NULL);
|
||||
|
||||
cliprdr->num_format_names = 0;
|
||||
if (dataLen > 0)
|
||||
{
|
||||
cb_event->raw_format_data = (BYTE*) malloc(dataLen);
|
||||
memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen);
|
||||
cb_event->raw_format_data_size = dataLen;
|
||||
cb_event->raw_format_unicode = (msgFlags & CB_ASCII_NAMES) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
cliprdr_send_format_list_response(cliprdr);
|
||||
if (cliprdr->use_long_format_names)
|
||||
cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
else
|
||||
cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags);
|
||||
|
||||
if (cliprdr->num_format_names > 0)
|
||||
cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names);
|
||||
|
||||
cb_event->num_formats = 0;
|
||||
|
||||
for (i = 0; i < cliprdr->num_format_names; i++)
|
||||
{
|
||||
supported = TRUE;
|
||||
format_name = &cliprdr->format_names[i];
|
||||
format = format_name->id;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case CB_FORMAT_TEXT:
|
||||
case CB_FORMAT_DIB:
|
||||
case CB_FORMAT_UNICODETEXT:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (format_name->length > 0)
|
||||
{
|
||||
DEBUG_CLIPRDR("format: %s", format_name->name);
|
||||
|
||||
if (strcmp(format_name->name, "HTML Format") == 0)
|
||||
{
|
||||
format = CB_FORMAT_HTML;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "PNG") == 0)
|
||||
{
|
||||
format = CB_FORMAT_PNG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "JFIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_JPEG;
|
||||
break;
|
||||
}
|
||||
if (strcmp(format_name->name, "GIF") == 0)
|
||||
{
|
||||
format = CB_FORMAT_GIF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
supported = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (supported)
|
||||
cb_event->formats[cb_event->num_formats++] = format;
|
||||
|
||||
if (format_name->length > 0)
|
||||
free(format_name->name);
|
||||
}
|
||||
|
||||
free(cliprdr->format_names);
|
||||
cliprdr->format_names = NULL;
|
||||
|
||||
cliprdr->num_format_names = 0;
|
||||
|
||||
cliprdr_send_format_list_response(cliprdr);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
/* http://msdn.microsoft.com/en-us/library/hh872154.aspx */
|
||||
wMessage* event;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
|
||||
/* http://msdn.microsoft.com/en-us/library/hh872154.aspx */
|
||||
|
||||
if (context->custom)
|
||||
{
|
||||
/* In case of an error the clipboard will not be synchronized with the server.
|
||||
* Post this event to restart format negociation and data transfer. */
|
||||
event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
|
||||
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
|
||||
|
||||
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
|
||||
formatListResponse.msgFlags = msgFlags;
|
||||
formatListResponse.dataLen = dataLen;
|
||||
|
||||
if (context->ServerFormatListResponse)
|
||||
context->ServerFormatListResponse(context, &formatListResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((msgFlags & CB_RESPONSE_FAIL) != 0)
|
||||
{
|
||||
/* In case of an error the clipboard will not be synchronized with the server.
|
||||
* Post this event to restart format negotiation and data transfer. */
|
||||
|
||||
wMessage* event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
RDP_CB_DATA_REQUEST_EVENT* cb_event;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataRequest, NULL, NULL);
|
||||
if (context->custom)
|
||||
{
|
||||
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->format);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
|
||||
formatDataRequest.msgFlags = msgFlags;
|
||||
formatDataRequest.dataLen = dataLen;
|
||||
|
||||
Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
|
||||
if (context->ServerFormatDataRequest)
|
||||
context->ServerFormatDataRequest(context, &formatDataRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_DATA_REQUEST_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataRequest, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->format);
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
||||
void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event)
|
||||
@ -360,17 +464,42 @@ void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DA
|
||||
|
||||
void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
||||
{
|
||||
RDP_CB_DATA_RESPONSE_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataResponse, NULL, NULL);
|
||||
|
||||
if (dataLen > 0)
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
if (context->custom)
|
||||
{
|
||||
cb_event->size = dataLen;
|
||||
cb_event->data = (BYTE*) malloc(dataLen);
|
||||
CopyMemory(cb_event->data, Stream_Pointer(s), dataLen);
|
||||
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
|
||||
|
||||
formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
|
||||
formatDataResponse.msgFlags = msgFlags;
|
||||
formatDataResponse.dataLen = dataLen;
|
||||
formatDataResponse.requestedFormatData = NULL;
|
||||
|
||||
if (dataLen)
|
||||
{
|
||||
formatDataResponse.requestedFormatData = (BYTE*) malloc(dataLen);
|
||||
Stream_Read(s, formatDataResponse.requestedFormatData, dataLen);
|
||||
}
|
||||
|
||||
if (context->ClientFormatDataResponse)
|
||||
context->ClientFormatDataResponse(context, &formatDataResponse);
|
||||
|
||||
free(formatDataResponse.requestedFormatData);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_DATA_RESPONSE_EVENT* cb_event;
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_DataResponse, NULL, NULL);
|
||||
|
||||
if (dataLen > 0)
|
||||
{
|
||||
cb_event->size = dataLen;
|
||||
cb_event->data = (BYTE*) malloc(dataLen);
|
||||
CopyMemory(cb_event->data, Stream_Pointer(s), dataLen);
|
||||
}
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,14 @@ static const char* const CB_MSG_TYPE_STRINGS[] =
|
||||
"CB_UNLOCK_CLIPDATA"
|
||||
};
|
||||
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
CliprdrClientContext* pInterface;
|
||||
rdpSvcPlugin* plugin = (rdpSvcPlugin*) cliprdr;
|
||||
pInterface = (CliprdrClientContext*) plugin->channel_entry_points.pInterface;
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen)
|
||||
{
|
||||
wStream* s;
|
||||
@ -110,6 +118,9 @@ static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream*
|
||||
{
|
||||
UINT32 version;
|
||||
UINT32 generalFlags;
|
||||
CliprdrClientContext* context;
|
||||
|
||||
context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
Stream_Read_UINT32(s, version); /* version (4 bytes) */
|
||||
Stream_Read_UINT32(s, generalFlags); /* generalFlags (4 bytes) */
|
||||
@ -133,6 +144,33 @@ static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream*
|
||||
cliprdr->can_lock_clipdata = TRUE;
|
||||
|
||||
cliprdr->received_caps = TRUE;
|
||||
|
||||
if (context->custom)
|
||||
{
|
||||
CLIPRDR_CAPABILITIES capabilities;
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
|
||||
|
||||
capabilities.cCapabilitiesSets = 1;
|
||||
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*) &(generalCapabilitySet);
|
||||
|
||||
generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
|
||||
generalCapabilitySet.capabilitySetLength = 12;
|
||||
|
||||
generalCapabilitySet.version = version;
|
||||
generalCapabilitySet.generalFlags = generalFlags;
|
||||
|
||||
if (context->ServerCapabilities)
|
||||
context->ServerCapabilities(context, &capabilities);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_CLIP_CAPS* caps_event;
|
||||
|
||||
caps_event = (RDP_CB_CLIP_CAPS*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_ClipCaps, NULL, NULL);
|
||||
caps_event->capabilities = generalFlags;
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) caps_event);
|
||||
}
|
||||
}
|
||||
|
||||
static void cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s, UINT16 length, UINT16 flags)
|
||||
@ -174,7 +212,12 @@ static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr)
|
||||
|
||||
DEBUG_CLIPRDR("Sending Capabilities");
|
||||
|
||||
flags = CB_USE_LONG_FORMAT_NAMES;
|
||||
flags = CB_USE_LONG_FORMAT_NAMES
|
||||
#ifdef _WIN32
|
||||
| CB_STREAM_FILECLIP_ENABLED
|
||||
| CB_FILECLIP_NO_FILE_PATHS
|
||||
#endif
|
||||
;
|
||||
|
||||
Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */
|
||||
Stream_Write_UINT16(s, 0); /* pad1 */
|
||||
@ -188,14 +231,91 @@ static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr)
|
||||
|
||||
static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UINT16 length, UINT16 flags)
|
||||
{
|
||||
wMessage* event;
|
||||
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
||||
|
||||
if (cliprdr->received_caps)
|
||||
cliprdr_send_clip_caps(cliprdr);
|
||||
if (context->custom)
|
||||
{
|
||||
CLIPRDR_MONITOR_READY monitorReady;
|
||||
|
||||
event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
monitorReady.msgType = CB_MONITOR_READY;
|
||||
monitorReady.msgFlags = flags;
|
||||
monitorReady.dataLen = length;
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
|
||||
if (context->MonitorReady)
|
||||
context->MonitorReady(context, &monitorReady);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDP_CB_MONITOR_READY_EVENT* event;
|
||||
|
||||
if (cliprdr->received_caps)
|
||||
cliprdr_send_clip_caps(cliprdr);
|
||||
|
||||
event = (RDP_CB_MONITOR_READY_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) event);
|
||||
}
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
|
||||
{
|
||||
RDP_CB_FILECONTENTS_REQUEST_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_FILECONTENTS_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FilecontentsRequest, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->streamId);
|
||||
Stream_Read_UINT32(s, cb_event->lindex);
|
||||
Stream_Read_UINT32(s, cb_event->dwFlags);
|
||||
Stream_Read_UINT32(s, cb_event->nPositionLow);
|
||||
Stream_Read_UINT32(s, cb_event->nPositionHigh);
|
||||
Stream_Read_UINT32(s, cb_event->cbRequested);
|
||||
//Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
|
||||
{
|
||||
RDP_CB_FILECONTENTS_RESPONSE_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_FILECONTENTS_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_FilecontentsResponse, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->streamId);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
cb_event->size = length - 4;
|
||||
cb_event->data = (BYTE*) malloc(cb_event->size);
|
||||
CopyMemory(cb_event->data, Stream_Pointer(s), cb_event->size);
|
||||
}
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
static void cliprdr_process_lock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
|
||||
{
|
||||
RDP_CB_LOCK_CLIPDATA_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_LOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_LockClipdata, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
static void cliprdr_process_unlock_clipdata(cliprdrPlugin* cliprdr, wStream* s, UINT32 length, UINT16 flags)
|
||||
{
|
||||
RDP_CB_UNLOCK_CLIPDATA_EVENT* cb_event;
|
||||
|
||||
cb_event = (RDP_CB_UNLOCK_CLIPDATA_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
||||
CliprdrChannel_UnLockClipdata, NULL, NULL);
|
||||
|
||||
Stream_Read_UINT32(s, cb_event->clipDataId);
|
||||
|
||||
svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event);
|
||||
}
|
||||
|
||||
static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
@ -211,7 +331,7 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
|
||||
DEBUG_CLIPRDR("msgType: %s (%d), msgFlags: %d dataLen: %d",
|
||||
CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen);
|
||||
|
||||
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
winpr_HexDump(Stream_Buffer(s), dataLen + 8);
|
||||
#endif
|
||||
@ -242,6 +362,22 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
cliprdr_process_format_data_response(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FILECONTENTS_REQUEST:
|
||||
cliprdr_process_filecontents_request(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_FILECONTENTS_RESPONSE:
|
||||
cliprdr_process_filecontents_response(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_LOCK_CLIPDATA:
|
||||
cliprdr_process_lock_clipdata(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
case CB_UNLOCK_CLIPDATA:
|
||||
cliprdr_process_unlock_clipdata(cliprdr, s, dataLen, msgFlags);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("unknown msgType %d", msgType);
|
||||
break;
|
||||
@ -250,6 +386,80 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_request_event(cliprdrPlugin* plugin, RDP_CB_FILECONTENTS_REQUEST_EVENT * event)
|
||||
{
|
||||
wStream *s;
|
||||
DEBUG_CLIPRDR("Sending File Contents Request.");
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0, 24);
|
||||
|
||||
Stream_Write_UINT32(s, event->streamId);
|
||||
Stream_Write_UINT32(s, event->lindex);
|
||||
Stream_Write_UINT32(s, event->dwFlags);
|
||||
Stream_Write_UINT32(s, event->nPositionLow);
|
||||
Stream_Write_UINT32(s, event->nPositionHigh);
|
||||
Stream_Write_UINT32(s, event->cbRequested);
|
||||
//Stream_Write_UINT32(s, event->clipDataId);
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_filecontents_response_event(cliprdrPlugin* plugin, RDP_CB_FILECONTENTS_RESPONSE_EVENT * event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending file contents response with size = %d", event->size);
|
||||
|
||||
if (event->size > 0)
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_OK, event->size + 4);
|
||||
Stream_Write_UINT32(s, event->streamId);
|
||||
Stream_Write(s, event->data, event->size);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_FAIL, 0);
|
||||
}
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_lock_clipdata_event(cliprdrPlugin* plugin, RDP_CB_LOCK_CLIPDATA_EVENT * event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending Lock Request");
|
||||
|
||||
s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
|
||||
Stream_Write_UINT32(s, event->clipDataId);
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_unlock_clipdata_event(cliprdrPlugin* plugin, RDP_CB_UNLOCK_CLIPDATA_EVENT * event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending UnLock Request");
|
||||
|
||||
s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
|
||||
Stream_Write_UINT32(s, event->clipDataId);
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_tempdir_event(cliprdrPlugin* plugin, RDP_CB_TEMPDIR_EVENT * event)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
DEBUG_CLIPRDR("Sending Temporary Directory.");
|
||||
s = cliprdr_packet_new(CB_TEMP_DIRECTORY, 0, 520);
|
||||
|
||||
Stream_Write(s, event->dirname, 520);
|
||||
|
||||
cliprdr_packet_send(plugin, s);
|
||||
}
|
||||
|
||||
static void cliprdr_process_event(rdpSvcPlugin* plugin, wMessage* event)
|
||||
{
|
||||
switch (GetMessageType(event->id))
|
||||
@ -266,6 +476,26 @@ static void cliprdr_process_event(rdpSvcPlugin* plugin, wMessage* event)
|
||||
cliprdr_process_format_data_response_event((cliprdrPlugin*) plugin, (RDP_CB_DATA_RESPONSE_EVENT*) event);
|
||||
break;
|
||||
|
||||
case CliprdrChannel_FilecontentsRequest:
|
||||
cliprdr_process_filecontents_request_event((cliprdrPlugin*) plugin, (RDP_CB_FILECONTENTS_REQUEST_EVENT *) event);
|
||||
break;
|
||||
|
||||
case CliprdrChannel_FilecontentsResponse:
|
||||
cliprdr_process_filecontents_response_event((cliprdrPlugin*) plugin, (RDP_CB_FILECONTENTS_RESPONSE_EVENT *) event);
|
||||
break;
|
||||
|
||||
case CliprdrChannel_LockClipdata:
|
||||
cliprdr_process_lock_clipdata_event((cliprdrPlugin*) plugin, (RDP_CB_LOCK_CLIPDATA_EVENT *) event);
|
||||
break;
|
||||
|
||||
case CliprdrChannel_UnLockClipdata:
|
||||
cliprdr_process_unlock_clipdata_event((cliprdrPlugin*) plugin, (RDP_CB_UNLOCK_CLIPDATA_EVENT *) event);
|
||||
break;
|
||||
|
||||
case CliprdrChannel_TemporaryDirectory:
|
||||
cliprdr_process_tempdir_event((cliprdrPlugin*) plugin, (RDP_CB_TEMPDIR_EVENT *) event);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("unknown event type %d", GetMessageType(event->id));
|
||||
break;
|
||||
@ -283,67 +513,173 @@ static void cliprdr_process_terminate(rdpSvcPlugin* plugin)
|
||||
* Callback Interface
|
||||
*/
|
||||
|
||||
int cliprdr_monitor_ready(CliprdrClientContext* context)
|
||||
int cliprdr_client_capabilities(CliprdrClientContext* context, CLIPRDR_CAPABILITIES* capabilities)
|
||||
{
|
||||
wStream* s;
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
|
||||
|
||||
Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */
|
||||
Stream_Write_UINT16(s, 0); /* pad1 */
|
||||
|
||||
generalCapabilitySet = (CLIPRDR_GENERAL_CAPABILITY_SET*) capabilities->capabilitySets;
|
||||
|
||||
Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetType); /* capabilitySetType */
|
||||
Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetLength); /* lengthCapability */
|
||||
Stream_Write_UINT32(s, generalCapabilitySet->version); /* version */
|
||||
Stream_Write_UINT32(s, generalCapabilitySet->generalFlags); /* generalFlags */
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cliprdr_format_list(CliprdrClientContext* context)
|
||||
int cliprdr_client_format_list(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList)
|
||||
{
|
||||
wStream* s;
|
||||
UINT32 index;
|
||||
int length = 0;
|
||||
int formatNameSize;
|
||||
CLIPRDR_FORMAT* format;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
for (index = 0; index < formatList->cFormats; index++)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*) &(formatList->formats[index]);
|
||||
|
||||
length += 4;
|
||||
|
||||
formatNameSize = 2;
|
||||
|
||||
if (format->formatName)
|
||||
formatNameSize = MultiByteToWideChar(CP_UTF8, 0, format->formatName, -1, NULL, 0) * 2;
|
||||
|
||||
length += formatNameSize;
|
||||
}
|
||||
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
|
||||
for (index = 0; index < formatList->cFormats; index++)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*) &(formatList->formats[index]);
|
||||
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
if (format->formatName)
|
||||
{
|
||||
int cchWideChar;
|
||||
LPWSTR lpWideCharStr;
|
||||
|
||||
lpWideCharStr = (LPWSTR) Stream_Pointer(s);
|
||||
cchWideChar = (Stream_Capacity(s) - Stream_GetPosition(s)) / 2;
|
||||
|
||||
formatNameSize = MultiByteToWideChar(CP_UTF8, 0,
|
||||
format->formatName, -1, lpWideCharStr, cchWideChar) * 2;
|
||||
|
||||
Stream_Seek(s, formatNameSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Write_UINT16(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cliprdr_data_request(CliprdrClientContext* context)
|
||||
int cliprdr_client_format_list_response(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
|
||||
{
|
||||
wStream* s;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
formatListResponse->msgType = CB_FORMAT_LIST_RESPONSE;
|
||||
formatListResponse->dataLen = 0;
|
||||
|
||||
s = cliprdr_packet_new(formatListResponse->msgType, formatListResponse->msgFlags, formatListResponse->dataLen);
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cliprdr_data_response(CliprdrClientContext* context)
|
||||
int cliprdr_client_format_data_request(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
|
||||
{
|
||||
wStream* s;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
formatDataRequest->msgType = CB_FORMAT_DATA_REQUEST;
|
||||
formatDataRequest->msgFlags = 0;
|
||||
formatDataRequest->dataLen = 4;
|
||||
|
||||
s = cliprdr_packet_new(formatDataRequest->msgType, formatDataRequest->msgFlags, formatDataRequest->dataLen);
|
||||
Stream_Write_UINT32(s, formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cliprdr_client_format_data_response(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
|
||||
{
|
||||
wStream* s;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle;
|
||||
|
||||
formatDataResponse->msgType = CB_FORMAT_DATA_RESPONSE;
|
||||
|
||||
s = cliprdr_packet_new(formatDataResponse->msgType, formatDataResponse->msgFlags, formatDataResponse->dataLen);
|
||||
Stream_Write(s, formatDataResponse->requestedFormatData, formatDataResponse->dataLen);
|
||||
|
||||
cliprdr_packet_send(cliprdr, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cliprdr is always built-in */
|
||||
#define VirtualChannelEntry cliprdr_VirtualChannelEntry
|
||||
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
cliprdrPlugin* _p;
|
||||
cliprdrPlugin* cliprdr;
|
||||
CliprdrClientContext* context;
|
||||
CHANNEL_ENTRY_POINTS_EX* pEntryPointsEx;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP* pEntryPointsEx;
|
||||
|
||||
_p = (cliprdrPlugin*) malloc(sizeof(cliprdrPlugin));
|
||||
ZeroMemory(_p, sizeof(cliprdrPlugin));
|
||||
cliprdr = (cliprdrPlugin*) calloc(1, sizeof(cliprdrPlugin));
|
||||
|
||||
_p->plugin.channel_def.options =
|
||||
cliprdr->plugin.channel_def.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
|
||||
strcpy(_p->plugin.channel_def.name, "cliprdr");
|
||||
strcpy(cliprdr->plugin.channel_def.name, "cliprdr");
|
||||
|
||||
_p->plugin.connect_callback = cliprdr_process_connect;
|
||||
_p->plugin.receive_callback = cliprdr_process_receive;
|
||||
_p->plugin.event_callback = cliprdr_process_event;
|
||||
_p->plugin.terminate_callback = cliprdr_process_terminate;
|
||||
cliprdr->plugin.connect_callback = cliprdr_process_connect;
|
||||
cliprdr->plugin.receive_callback = cliprdr_process_receive;
|
||||
cliprdr->plugin.event_callback = cliprdr_process_event;
|
||||
cliprdr->plugin.terminate_callback = cliprdr_process_terminate;
|
||||
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_EX*) pEntryPoints;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_EX)) &&
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP)) &&
|
||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||
{
|
||||
context = (CliprdrClientContext*) malloc(sizeof(CliprdrClientContext));
|
||||
context = (CliprdrClientContext*) calloc(1, sizeof(CliprdrClientContext));
|
||||
|
||||
context->MonitorReady = cliprdr_monitor_ready;
|
||||
context->FormatList = cliprdr_format_list;
|
||||
context->DataRequest = cliprdr_data_request;
|
||||
context->DataResponse = cliprdr_data_response;
|
||||
context->handle = (void*) cliprdr;
|
||||
|
||||
context->ClientCapabilities = cliprdr_client_capabilities;
|
||||
context->ClientFormatList = cliprdr_client_format_list;
|
||||
context->ClientFormatListResponse = cliprdr_client_format_list_response;
|
||||
context->ClientFormatDataRequest = cliprdr_client_format_data_request;
|
||||
context->ClientFormatDataResponse = cliprdr_client_format_data_response;
|
||||
|
||||
*(pEntryPointsEx->ppInterface) = (void*) context;
|
||||
}
|
||||
|
||||
svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints);
|
||||
svc_plugin_init((rdpSvcPlugin*) cliprdr, pEntryPoints);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ typedef struct cliprdr_plugin cliprdrPlugin;
|
||||
wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen);
|
||||
void cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* data_out);
|
||||
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
||||
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
#define DEBUG_CLIPRDR(fmt, ...) DEBUG_CLASS(CLIPRDR, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
|
@ -468,7 +468,7 @@ static void* cliprdr_server_thread(void* arg)
|
||||
|
||||
static int cliprdr_server_start(CliprdrServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelManagerOpenEx(context->vcm, "cliprdr", 0);
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "cliprdr");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
@ -491,16 +491,14 @@ static int cliprdr_server_stop(CliprdrServerContext* context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
CliprdrServerContext* cliprdr_server_context_new(WTSVirtualChannelManager* vcm)
|
||||
CliprdrServerContext* cliprdr_server_context_new(HANDLE vcm)
|
||||
{
|
||||
CliprdrServerContext* context;
|
||||
|
||||
context = (CliprdrServerContext*) malloc(sizeof(CliprdrServerContext));
|
||||
context = (CliprdrServerContext*) calloc(1, sizeof(CliprdrServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
ZeroMemory(context, sizeof(CliprdrServerContext));
|
||||
|
||||
context->vcm = vcm;
|
||||
|
||||
context->Start = cliprdr_server_start;
|
||||
|
@ -28,14 +28,6 @@
|
||||
|
||||
#define CLIPRDR_HEADER_LENGTH 8
|
||||
|
||||
struct _CLIPRDR_HEADER
|
||||
{
|
||||
UINT16 msgType;
|
||||
UINT16 msgFlags;
|
||||
UINT32 dataLen;
|
||||
};
|
||||
typedef struct _CLIPRDR_HEADER CLIPRDR_HEADER;
|
||||
|
||||
struct _cliprdr_server_private
|
||||
{
|
||||
HANDLE Thread;
|
||||
|
@ -73,10 +73,10 @@ typedef struct _DISP_PLUGIN DISP_PLUGIN;
|
||||
|
||||
int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
|
||||
{
|
||||
int index;
|
||||
int status;
|
||||
wStream* s;
|
||||
UINT32 type;
|
||||
UINT32 index;
|
||||
UINT32 length;
|
||||
DISP_PLUGIN* disp;
|
||||
UINT32 MonitorLayoutSize;
|
||||
@ -155,7 +155,7 @@ int disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback
|
||||
|
||||
Stream_SealLength(s);
|
||||
|
||||
status = callback->channel->Write(callback->channel, Stream_Length(s), Stream_Buffer(s), NULL);
|
||||
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL);
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
|
@ -356,7 +356,7 @@ static void drdynvc_process_receive(rdpSvcPlugin* plugin, wStream* s)
|
||||
|
||||
static void drdynvc_process_connect(rdpSvcPlugin* plugin)
|
||||
{
|
||||
int index;
|
||||
UINT32 index;
|
||||
ADDIN_ARGV* args;
|
||||
rdpSettings* settings;
|
||||
drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
|
||||
@ -407,11 +407,11 @@ int drdynvc_get_version(DrdynvcClientContext* context)
|
||||
/* drdynvc is always built-in */
|
||||
#define VirtualChannelEntry drdynvc_VirtualChannelEntry
|
||||
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
drdynvcPlugin* _p;
|
||||
DrdynvcClientContext* context;
|
||||
CHANNEL_ENTRY_POINTS_EX* pEntryPointsEx;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP* pEntryPointsEx;
|
||||
|
||||
_p = (drdynvcPlugin*) malloc(sizeof(drdynvcPlugin));
|
||||
ZeroMemory(_p, sizeof(drdynvcPlugin));
|
||||
@ -428,9 +428,9 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
_p->plugin.event_callback = drdynvc_process_event;
|
||||
_p->plugin.terminate_callback = drdynvc_process_terminate;
|
||||
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_EX*) pEntryPoints;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_EX)) &&
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP)) &&
|
||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||
{
|
||||
context = (DrdynvcClientContext*) malloc(sizeof(DrdynvcClientContext));
|
||||
|
@ -413,13 +413,13 @@ int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelI
|
||||
|
||||
IFCALL(context->OnChannelDisconnected, context, channel->channel_name, channel->pInterface);
|
||||
|
||||
free(channel->channel_name);
|
||||
|
||||
DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
|
||||
ichannel = (IWTSVirtualChannel*) channel;
|
||||
ichannel->Close(ichannel);
|
||||
}
|
||||
|
||||
free(channel->channel_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -469,7 +469,7 @@ int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, UINT32 C
|
||||
|
||||
Stream_Write(channel->dvc_data, data, data_size);
|
||||
|
||||
if (Stream_GetPosition(channel->dvc_data) >= Stream_Capacity(channel->dvc_data))
|
||||
if (((size_t) Stream_GetPosition(channel->dvc_data)) >= Stream_Capacity(channel->dvc_data))
|
||||
{
|
||||
error = channel->channel_callback->OnDataReceived(channel->channel_callback,
|
||||
Stream_Capacity(channel->dvc_data), Stream_Buffer(channel->dvc_data));
|
||||
|
@ -86,7 +86,7 @@ static void* drdynvc_server_thread(void* arg)
|
||||
|
||||
static int drdynvc_server_start(DrdynvcServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelManagerOpenEx(context->vcm, "rdpdr", 0);
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "drdynvc");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
@ -109,27 +109,20 @@ static int drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
DrdynvcServerContext* drdynvc_server_context_new(WTSVirtualChannelManager* vcm)
|
||||
DrdynvcServerContext* drdynvc_server_context_new(HANDLE vcm)
|
||||
{
|
||||
DrdynvcServerContext* context;
|
||||
|
||||
context = (DrdynvcServerContext*) malloc(sizeof(DrdynvcServerContext));
|
||||
context = (DrdynvcServerContext*) calloc(1, sizeof(DrdynvcServerContext));
|
||||
|
||||
if (context)
|
||||
{
|
||||
ZeroMemory(context, sizeof(DrdynvcServerContext));
|
||||
|
||||
context->vcm = vcm;
|
||||
|
||||
context->Start = drdynvc_server_start;
|
||||
context->Stop = drdynvc_server_stop;
|
||||
|
||||
context->priv = (DrdynvcServerPrivate*) malloc(sizeof(DrdynvcServerPrivate));
|
||||
|
||||
if (context->priv)
|
||||
{
|
||||
ZeroMemory(context->priv, sizeof(DrdynvcServerPrivate));
|
||||
}
|
||||
context->priv = (DrdynvcServerPrivate*) calloc(1, sizeof(DrdynvcServerPrivate));
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -64,7 +64,7 @@ static void drive_file_fix_path(char* path)
|
||||
int i;
|
||||
int length;
|
||||
|
||||
length = strlen(path);
|
||||
length = (int) strlen(path);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
@ -177,9 +177,9 @@ static BOOL drive_file_init(DRIVE_FILE* file, UINT32 DesiredAccess, UINT32 Creat
|
||||
struct STAT st;
|
||||
BOOL exists;
|
||||
#ifdef WIN32
|
||||
const static int mode = _S_IREAD | _S_IWRITE ;
|
||||
const static int mode = _S_IREAD | _S_IWRITE ;
|
||||
#else
|
||||
const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
|
||||
const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
|
||||
BOOL largeFile = FALSE;
|
||||
#endif
|
||||
int oflag = 0;
|
||||
@ -265,8 +265,10 @@ static BOOL drive_file_init(DRIVE_FILE* file, UINT32 DesiredAccess, UINT32 Creat
|
||||
#ifndef WIN32
|
||||
if (largeFile)
|
||||
{
|
||||
oflag |= O_LARGEFILE;
|
||||
oflag |= O_LARGEFILE;
|
||||
}
|
||||
#else
|
||||
oflag |= O_BINARY;
|
||||
#endif
|
||||
file->fd = OPEN(file->fullpath, oflag, mode);
|
||||
|
||||
@ -426,7 +428,7 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input)
|
||||
{
|
||||
char* s = NULL;
|
||||
mode_t m;
|
||||
mode_t m;
|
||||
UINT64 size;
|
||||
int status;
|
||||
char* fullpath;
|
||||
@ -456,7 +458,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime);
|
||||
tv[1].tv_usec = 0;
|
||||
#ifndef WIN32
|
||||
/* TODO on win32 */
|
||||
/* TODO on win32 */
|
||||
#ifdef ANDROID
|
||||
utimes(file->fullpath, tv);
|
||||
#else
|
||||
@ -474,15 +476,17 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
fchmod(file->fd, st.st_mode);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
break;
|
||||
|
||||
case FileEndOfFileInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232067.aspx */
|
||||
case FileAllocationInformation:
|
||||
/* http://msdn.microsoft.com/en-us/library/cc232076.aspx */
|
||||
#ifndef _WIN32
|
||||
Stream_Read_UINT64(input, size);
|
||||
if (ftruncate(file->fd, size) != 0)
|
||||
return FALSE;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case FileDispositionInformation:
|
||||
@ -509,10 +513,16 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
fullpath = drive_file_combine_fullpath(file->basepath, s);
|
||||
free(s);
|
||||
|
||||
/* TODO rename does not work on win32 */
|
||||
if (rename(file->fullpath, fullpath) == 0)
|
||||
#ifdef _WIN32
|
||||
if (file->fd)
|
||||
close(file->fd);
|
||||
#endif
|
||||
if (rename(file->fullpath, fullpath) == 0)
|
||||
{
|
||||
drive_file_set_fullpath(file, fullpath);
|
||||
#ifdef _WIN32
|
||||
file->fd = OPEN(fullpath, O_RDWR | O_BINARY);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -40,13 +40,13 @@
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define STAT stat
|
||||
#define STAT __stat64
|
||||
#define OPEN _open
|
||||
#define close _close
|
||||
#define read _read
|
||||
#define write _write
|
||||
#define LSEEK _lseek
|
||||
#define FSTAT fstat
|
||||
#define LSEEK _lseeki64
|
||||
#define FSTAT _fstat64
|
||||
#define STATVFS statvfs
|
||||
#define mkdir(a,b) _mkdir(a)
|
||||
#define rmdir _rmdir
|
||||
|
@ -56,9 +56,7 @@ struct _DRIVE_DEVICE
|
||||
wListDictionary* files;
|
||||
|
||||
HANDLE thread;
|
||||
HANDLE irpEvent;
|
||||
HANDLE stopEvent;
|
||||
PSLIST_HEADER pIrpList;
|
||||
wMessageQueue* IrpQueue;
|
||||
|
||||
DEVMAN* devman;
|
||||
};
|
||||
@ -576,46 +574,27 @@ static void drive_process_irp(DRIVE_DEVICE* drive, IRP* irp)
|
||||
}
|
||||
}
|
||||
|
||||
static void drive_process_irp_list(DRIVE_DEVICE* drive)
|
||||
{
|
||||
IRP* irp;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (WaitForSingleObject(drive->stopEvent, 0) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
irp = (IRP*) InterlockedPopEntrySList(drive->pIrpList);
|
||||
|
||||
if (irp == NULL)
|
||||
break;
|
||||
|
||||
drive_process_irp(drive, irp);
|
||||
}
|
||||
}
|
||||
|
||||
static void* drive_thread_func(void* arg)
|
||||
{
|
||||
DWORD status;
|
||||
DWORD nCount;
|
||||
HANDLE handles[8];
|
||||
IRP* irp;
|
||||
wMessage message;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) arg;
|
||||
|
||||
nCount = 0;
|
||||
handles[nCount++] = drive->stopEvent;
|
||||
handles[nCount++] = drive->irpEvent;
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
|
||||
|
||||
if (WaitForSingleObject(drive->stopEvent, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
if (!MessageQueue_Wait(drive->IrpQueue))
|
||||
break;
|
||||
}
|
||||
|
||||
ResetEvent(drive->irpEvent);
|
||||
drive_process_irp_list(drive);
|
||||
if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
|
||||
break;
|
||||
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
irp = (IRP*) message.wParam;
|
||||
|
||||
if (irp)
|
||||
drive_process_irp(drive, irp);
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
@ -625,28 +604,17 @@ static void* drive_thread_func(void* arg)
|
||||
static void drive_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
||||
|
||||
InterlockedPushEntrySList(drive->pIrpList, &(irp->ItemEntry));
|
||||
|
||||
SetEvent(drive->irpEvent);
|
||||
MessageQueue_Post(drive->IrpQueue, NULL, 0, (void*) irp, NULL);
|
||||
}
|
||||
|
||||
static void drive_free(DEVICE* device)
|
||||
{
|
||||
IRP* irp;
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*) device;
|
||||
|
||||
SetEvent(drive->stopEvent);
|
||||
MessageQueue_PostQuit(drive->IrpQueue, 0);
|
||||
WaitForSingleObject(drive->thread, INFINITE);
|
||||
|
||||
CloseHandle(drive->thread);
|
||||
CloseHandle(drive->irpEvent);
|
||||
CloseHandle(drive->stopEvent);
|
||||
|
||||
while ((irp = (IRP*) InterlockedPopEntrySList(drive->pIrpList)) != NULL)
|
||||
irp->Discard(irp);
|
||||
|
||||
_aligned_free(drive->pIrpList);
|
||||
|
||||
ListDictionary_Free(drive->files);
|
||||
|
||||
@ -682,7 +650,7 @@ void drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, char*
|
||||
drive->device.IRPRequest = drive_irp_request;
|
||||
drive->device.Free = drive_free;
|
||||
|
||||
length = strlen(name);
|
||||
length = (int) strlen(name);
|
||||
drive->device.data = Stream_New(NULL, length + 1);
|
||||
|
||||
for (i = 0; i <= length; i++)
|
||||
@ -693,11 +661,7 @@ void drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, char*
|
||||
drive->files = ListDictionary_New(TRUE);
|
||||
ListDictionary_Object(drive->files)->fnObjectFree = (OBJECT_FREE_FN) drive_file_free;
|
||||
|
||||
drive->pIrpList = (PSLIST_HEADER) _aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
InitializeSListHead(drive->pIrpList);
|
||||
|
||||
drive->irpEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
drive->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
drive->IrpQueue = MessageQueue_New(NULL);
|
||||
drive->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) drive_thread_func, drive, CREATE_SUSPENDED, NULL);
|
||||
|
||||
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) drive);
|
||||
@ -748,13 +712,13 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
drive->Path = _strdup("/");
|
||||
}
|
||||
|
||||
drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
|
||||
#else
|
||||
sys_code_page = GetACP();
|
||||
/* Special case: path[0] == '*' -> export all drives */
|
||||
/* Special case: path[0] == '*' -> export all drives */
|
||||
/* Special case: path[0] == '%' -> user home dir */
|
||||
if (strcmp(drive->Path, "%") == 0)
|
||||
if (strcmp(drive->Path, "%") == 0)
|
||||
{
|
||||
_snprintf(buf, sizeof(buf), "%s\\", getenv("USERPROFILE"));
|
||||
drive_register_drive_path(pEntryPoints, drive->Name, _strdup(buf));
|
||||
@ -769,10 +733,9 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
for (dev = devlist, i = 0; *dev; dev += 4, i++)
|
||||
{
|
||||
if (*dev > 'B')
|
||||
{
|
||||
{
|
||||
/* Suppress disk drives A and B to avoid pesty messages */
|
||||
_snprintf(buf, sizeof(buf) - 4, "%s", drive->Name);
|
||||
len = strlen(buf);
|
||||
len = _snprintf(buf, sizeof(buf) - 4, "%s", drive->Name);
|
||||
buf[len] = '_';
|
||||
buf[len + 1] = dev[0];
|
||||
buf[len + 2] = 0;
|
||||
@ -781,11 +744,11 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
drive_register_drive_path(pEntryPoints, drive->Name, drive->Path);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -285,10 +285,10 @@ static void parallel_free(DEVICE* device)
|
||||
|
||||
MessageQueue_PostQuit(parallel->queue, 0);
|
||||
WaitForSingleObject(parallel->thread, INFINITE);
|
||||
CloseHandle(parallel->thread);
|
||||
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
MessageQueue_Free(parallel->queue);
|
||||
CloseHandle(parallel->thread);
|
||||
|
||||
free(parallel);
|
||||
}
|
||||
@ -309,10 +309,18 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
name = device->Name;
|
||||
path = device->Path;
|
||||
|
||||
if (!name || (name[0] == '*'))
|
||||
{
|
||||
/* TODO: implement auto detection of parallel ports */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name[0] && path[0])
|
||||
{
|
||||
parallel = (PARALLEL_DEVICE*) malloc(sizeof(PARALLEL_DEVICE));
|
||||
ZeroMemory(parallel, sizeof(PARALLEL_DEVICE));
|
||||
parallel = (PARALLEL_DEVICE*) calloc(1, sizeof(PARALLEL_DEVICE));
|
||||
|
||||
if (!parallel)
|
||||
return -1;
|
||||
|
||||
parallel->device.type = RDPDR_DTYP_PARALLEL;
|
||||
parallel->device.name = name;
|
||||
@ -327,7 +335,7 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
parallel->path = path;
|
||||
|
||||
parallel->queue = MessageQueue_New();
|
||||
parallel->queue = MessageQueue_New(NULL);
|
||||
|
||||
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) parallel);
|
||||
|
||||
|
@ -42,7 +42,7 @@ RailClientContext* rail_get_client_interface(void* railObject)
|
||||
{
|
||||
RailClientContext* pInterface;
|
||||
rdpSvcPlugin* plugin = (rdpSvcPlugin*) railObject;
|
||||
pInterface = (RailClientContext*) *(plugin->channel_entry_points.ppInterface);
|
||||
pInterface = (RailClientContext*) plugin->channel_entry_points.pInterface;
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
@ -501,36 +501,36 @@ int rail_server_get_appid_response(RailClientContext* context, RAIL_GET_APPID_RE
|
||||
/* rail is always built-in */
|
||||
#define VirtualChannelEntry rail_VirtualChannelEntry
|
||||
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
railPlugin* _p;
|
||||
railPlugin* rail;
|
||||
RailClientContext* context;
|
||||
CHANNEL_ENTRY_POINTS_EX* pEntryPointsEx;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP* pEntryPointsEx;
|
||||
|
||||
_p = (railPlugin*) malloc(sizeof(railPlugin));
|
||||
ZeroMemory(_p, sizeof(railPlugin));
|
||||
rail = (railPlugin*) malloc(sizeof(railPlugin));
|
||||
ZeroMemory(rail, sizeof(railPlugin));
|
||||
|
||||
_p->plugin.channel_def.options =
|
||||
rail->plugin.channel_def.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP |
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
|
||||
strcpy(_p->plugin.channel_def.name, "rail");
|
||||
strcpy(rail->plugin.channel_def.name, "rail");
|
||||
|
||||
_p->plugin.connect_callback = rail_process_connect;
|
||||
_p->plugin.receive_callback = rail_process_receive;
|
||||
_p->plugin.event_callback = rail_process_event;
|
||||
_p->plugin.terminate_callback = rail_process_terminate;
|
||||
rail->plugin.connect_callback = rail_process_connect;
|
||||
rail->plugin.receive_callback = rail_process_receive;
|
||||
rail->plugin.event_callback = rail_process_event;
|
||||
rail->plugin.terminate_callback = rail_process_terminate;
|
||||
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_EX*) pEntryPoints;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_EX)) &&
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP)) &&
|
||||
(pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
|
||||
{
|
||||
context = (RailClientContext*) malloc(sizeof(RailClientContext));
|
||||
|
||||
context->handle = (void*) _p;
|
||||
context->handle = (void*) rail;
|
||||
|
||||
context->ClientExecute = rail_client_execute;
|
||||
context->ClientActivate = rail_client_activate;
|
||||
@ -557,11 +557,11 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
}
|
||||
|
||||
WLog_Init();
|
||||
_p->log = WLog_Get("com.freerdp.channels.rail.client");
|
||||
rail->log = WLog_Get("com.freerdp.channels.rail.client");
|
||||
|
||||
WLog_Print(_p->log, WLOG_DEBUG, "VirtualChannelEntry");
|
||||
WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntry");
|
||||
|
||||
svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints);
|
||||
svc_plugin_init((rdpSvcPlugin*) rail, pEntryPoints);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ void rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
||||
{
|
||||
UINT16 orderLength;
|
||||
|
||||
orderLength = Stream_GetPosition(s);
|
||||
orderLength = (UINT16) Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
rail_write_pdu_header(s, orderType, orderLength);
|
||||
|
@ -67,6 +67,15 @@ void devman_free(DEVMAN* devman)
|
||||
free(devman);
|
||||
}
|
||||
|
||||
void devman_unregister_device(DEVMAN* devman, void* key)
|
||||
{
|
||||
DEVICE* device;
|
||||
|
||||
device = (DEVICE *)ListDictionary_Remove(devman->devices, key);
|
||||
if (device)
|
||||
devman_device_free(device);
|
||||
}
|
||||
|
||||
static void devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
{
|
||||
void* key = NULL;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
void devman_unregister_device(DEVMAN* devman, void* key);
|
||||
BOOL devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device);
|
||||
DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id);
|
||||
|
||||
|
@ -48,7 +48,7 @@ static void irp_complete(IRP* irp)
|
||||
{
|
||||
int pos;
|
||||
|
||||
pos = Stream_GetPosition(irp->output);
|
||||
pos = (int) Stream_GetPosition(irp->output);
|
||||
Stream_SetPosition(irp->output, 12);
|
||||
Stream_Write_UINT32(irp->output, irp->IoStatus);
|
||||
Stream_SetPosition(irp->output, pos);
|
||||
|
@ -33,6 +33,15 @@
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@ -44,9 +53,442 @@
|
||||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
typedef struct _DEVICE_DRIVE_EXT DEVICE_DRIVE_EXT;
|
||||
|
||||
struct _DEVICE_DRIVE_EXT
|
||||
{
|
||||
DEVICE device;
|
||||
char* path;
|
||||
};
|
||||
|
||||
static void rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL userLoggedOn);
|
||||
|
||||
static void rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 count, UINT32 ids[])
|
||||
{
|
||||
wStream* s;
|
||||
int i;
|
||||
|
||||
s = Stream_New(NULL, 256);
|
||||
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE);
|
||||
Stream_Write_UINT16(s, PAKID_CORE_DEVICELIST_REMOVE);
|
||||
Stream_Write_UINT32(s, count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
Stream_Write_UINT32(s, ids[i]);
|
||||
|
||||
Stream_SealLength(s);
|
||||
|
||||
rdpdr_send(rdpdr, s);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
rdpdrPlugin *rdpdr;
|
||||
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
|
||||
|
||||
rdpdr = (rdpdrPlugin *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
|
||||
switch(Msg)
|
||||
{
|
||||
case WM_DEVICECHANGE:
|
||||
switch (wParam)
|
||||
{
|
||||
case DBT_DEVICEARRIVAL:
|
||||
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
{
|
||||
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
|
||||
DWORD unitmask = lpdbv->dbcv_unitmask;
|
||||
int i;
|
||||
char drive_path[4] = { 'c', ':', '/', '\0'};
|
||||
|
||||
for (i = 0; i < 26; i++)
|
||||
{
|
||||
if (unitmask & 0x01)
|
||||
{
|
||||
RDPDR_DRIVE* drive;
|
||||
|
||||
drive_path[0] = 'A' + i;
|
||||
|
||||
drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE));
|
||||
ZeroMemory(drive, sizeof(RDPDR_DRIVE));
|
||||
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
|
||||
drive->Path = _strdup(drive_path);
|
||||
drive_path[1] = '\0';
|
||||
drive->Name = _strdup(drive_path);
|
||||
devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive);
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
}
|
||||
unitmask = unitmask >> 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DBT_DEVICEREMOVECOMPLETE:
|
||||
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
{
|
||||
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
|
||||
DWORD unitmask = lpdbv->dbcv_unitmask;
|
||||
int i, j, count;
|
||||
char drive_name_upper, drive_name_lower;
|
||||
|
||||
ULONG_PTR *keys;
|
||||
DEVICE_DRIVE_EXT *device_ext;
|
||||
UINT32 ids[1];
|
||||
|
||||
for (i = 0; i < 26; i++)
|
||||
{
|
||||
if (unitmask & 0x01)
|
||||
{
|
||||
drive_name_upper = 'A' + i;
|
||||
drive_name_lower = 'a' + i;
|
||||
|
||||
count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
|
||||
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]);
|
||||
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];
|
||||
rdpdr_send_device_list_remove_request(rdpdr, 1, ids);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
unitmask = unitmask >> 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return DefWindowProc(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
return DefWindowProc(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
static void* drive_hotplug_thread_func(void* arg)
|
||||
{
|
||||
rdpdrPlugin *rdpdr;
|
||||
WNDCLASSEX wnd_cls;
|
||||
HWND hwnd;
|
||||
MSG msg;
|
||||
BOOL bRet;
|
||||
DEV_BROADCAST_HANDLE NotificationFilter;
|
||||
HDEVNOTIFY hDevNotify;
|
||||
|
||||
rdpdr = (rdpdrPlugin *)arg;
|
||||
|
||||
/* init windows class */
|
||||
wnd_cls.cbSize = sizeof(WNDCLASSEX);
|
||||
wnd_cls.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wnd_cls.lpfnWndProc = hotplug_proc;
|
||||
wnd_cls.cbClsExtra = 0;
|
||||
wnd_cls.cbWndExtra = 0;
|
||||
wnd_cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||
wnd_cls.hCursor = NULL;
|
||||
wnd_cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
wnd_cls.lpszMenuName = NULL;
|
||||
wnd_cls.lpszClassName = L"DRIVE_HOTPLUG";
|
||||
wnd_cls.hInstance = NULL;
|
||||
wnd_cls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
||||
RegisterClassEx(&wnd_cls);
|
||||
|
||||
/* create window */
|
||||
hwnd = CreateWindowEx(0, L"DRIVE_HOTPLUG", NULL,
|
||||
0, 0, 0, 0, 0,
|
||||
NULL, NULL, NULL, NULL);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)rdpdr);
|
||||
|
||||
rdpdr->hotplug_wnd = hwnd;
|
||||
/* register device interface to hwnd */
|
||||
NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
||||
NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
||||
hDevNotify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
/* message loop */
|
||||
while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
|
||||
{
|
||||
if (bRet == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
UnregisterDeviceNotification(hDevNotify);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
if (rdpdr->hotplug_wnd)
|
||||
PostMessage(rdpdr->hotplug_wnd, WM_QUIT, 0, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define MAX_USB_DEVICES 100
|
||||
|
||||
typedef struct _hotplug_dev {
|
||||
char* path;
|
||||
BOOL to_add;
|
||||
} hotplug_dev;
|
||||
|
||||
static char* next_line(FILE* fd, size_t* len)
|
||||
{
|
||||
size_t newsiz;
|
||||
int c;
|
||||
char* newbuf;
|
||||
char* lrbuf;
|
||||
int lrsiz;
|
||||
|
||||
*len = 0;
|
||||
lrsiz = 0;
|
||||
lrbuf = NULL;
|
||||
newbuf = NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
c = fgetc(fd);
|
||||
if (ferror(fd))
|
||||
return NULL;
|
||||
|
||||
if (c == EOF)
|
||||
{
|
||||
if (*len == 0)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
lrbuf[(*len)] = '\0';
|
||||
return lrbuf;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*len == lrsiz)
|
||||
{
|
||||
newsiz = lrsiz + 4096;
|
||||
newbuf = realloc(lrbuf, newsiz);
|
||||
if (newbuf == NULL)
|
||||
return NULL;
|
||||
lrbuf = newbuf;
|
||||
lrsiz = newsiz;
|
||||
}
|
||||
lrbuf[(*len)] = c;
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
lrbuf[(*len)] = '\0';
|
||||
return lrbuf;
|
||||
}
|
||||
|
||||
(*len)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char* get_word(char* str, unsigned int* offset)
|
||||
{
|
||||
char* p;
|
||||
char* tmp;
|
||||
int wlen;
|
||||
|
||||
if (*offset >= strlen(str))
|
||||
return NULL;
|
||||
|
||||
p = str + *offset;
|
||||
tmp = p;
|
||||
|
||||
while (*tmp != ' ' && *tmp != '\0')
|
||||
tmp++;
|
||||
|
||||
wlen = tmp - p;
|
||||
*offset += wlen;
|
||||
|
||||
/* in case there are more than one space between words */
|
||||
while (*(str + *offset) == ' ')
|
||||
(*offset)++;
|
||||
|
||||
return strndup(p, wlen);
|
||||
}
|
||||
|
||||
static void handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
FILE *f;
|
||||
size_t len;
|
||||
char *line;
|
||||
char *word;
|
||||
unsigned int wlen;
|
||||
|
||||
hotplug_dev dev_array[MAX_USB_DEVICES];
|
||||
int i, j;
|
||||
int size = 0;
|
||||
|
||||
int count;
|
||||
DEVICE_DRIVE_EXT *device_ext;
|
||||
ULONG_PTR *keys;
|
||||
UINT32 ids[1];
|
||||
|
||||
f = fopen("/proc/mounts", "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((line = next_line(f, &len)))
|
||||
{
|
||||
wlen = 0;
|
||||
while ((word = get_word(line, &wlen)))
|
||||
{
|
||||
/* copy hotpluged device mount point to the dev_array */
|
||||
if (strstr(word, "/mnt/") != NULL || strstr(word, "/media/") != NULL)
|
||||
{
|
||||
dev_array[size].path = strdup(word);
|
||||
dev_array[size++].to_add = TRUE;
|
||||
}
|
||||
free(word);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
/* delete removed devices */
|
||||
count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
|
||||
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
BOOL dev_found = FALSE;
|
||||
|
||||
device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]);
|
||||
|
||||
/* not plugable device */
|
||||
if (strstr(device_ext->path, "/mnt/") == NULL && strstr(device_ext->path, "/media/") == NULL)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (strstr(device_ext->path, dev_array[i].path) != NULL)
|
||||
{
|
||||
dev_found = TRUE;
|
||||
dev_array[i].to_add = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev_found)
|
||||
{
|
||||
devman_unregister_device(rdpdr->devman, (void *)keys[j]);
|
||||
ids[0] = keys[j];
|
||||
rdpdr_send_device_list_remove_request(rdpdr, 1, ids);
|
||||
}
|
||||
}
|
||||
|
||||
/* add new devices */
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
RDPDR_DRIVE* drive;
|
||||
|
||||
if (dev_array[i].to_add)
|
||||
{
|
||||
char* name;
|
||||
|
||||
drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE));
|
||||
ZeroMemory(drive, sizeof(RDPDR_DRIVE));
|
||||
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
|
||||
drive->Path = _strdup(dev_array[i].path);
|
||||
name = strrchr(drive->Path, '/') + 1;
|
||||
drive->Name = _strdup(name);
|
||||
devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive);
|
||||
}
|
||||
|
||||
free(dev_array[i].path);
|
||||
dev_array[i].path = NULL;
|
||||
}
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
}
|
||||
|
||||
static void* drive_hotplug_thread_func(void* arg)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
int mfd;
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int rv;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) arg;
|
||||
|
||||
rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
mfd = open("/proc/mounts", O_RDONLY, 0);
|
||||
|
||||
if (mfd < 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to open /proc/mounts.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(mfd, &rfds);
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
while ((rv = select(mfd+1, NULL, NULL, &rfds, &tv)) >= 0)
|
||||
{
|
||||
if (WaitForSingleObject(rdpdr->stopEvent, 0) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (FD_ISSET(mfd, &rfds))
|
||||
{
|
||||
/* file /proc/mounts changed, handle this */
|
||||
handle_hotplug(rdpdr);
|
||||
}
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(mfd, &rfds);
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
if (rdpdr->hotplugThread)
|
||||
{
|
||||
if (rdpdr->stopEvent)
|
||||
SetEvent(rdpdr->stopEvent);
|
||||
|
||||
WaitForSingleObject(rdpdr->hotplugThread, INFINITE);
|
||||
rdpdr->hotplugThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void rdpdr_process_connect(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
int index;
|
||||
UINT32 index;
|
||||
RDPDR_DEVICE* device;
|
||||
rdpSettings* settings;
|
||||
|
||||
@ -58,6 +500,14 @@ static void rdpdr_process_connect(rdpdrPlugin* rdpdr)
|
||||
for (index = 0; index < settings->DeviceCount; index++)
|
||||
{
|
||||
device = settings->DeviceArray[index];
|
||||
|
||||
if (device->Name && (strcmp(device->Name, "*") == 0))
|
||||
{
|
||||
rdpdr->hotplugThread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) drive_hotplug_thread_func, rdpdr, 0, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
devman_load_device_service(rdpdr->devman, device);
|
||||
}
|
||||
}
|
||||
@ -153,7 +603,7 @@ static void rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE);
|
||||
Stream_Write_UINT16(s, PAKID_CORE_DEVICELIST_ANNOUNCE);
|
||||
|
||||
count_pos = Stream_GetPosition(s);
|
||||
count_pos = (int) Stream_GetPosition(s);
|
||||
count = 0;
|
||||
|
||||
Stream_Seek_UINT32(s); /* deviceCount */
|
||||
@ -175,7 +625,7 @@ static void rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
if ((rdpdr->versionMinor == 0x0005) ||
|
||||
(device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn)
|
||||
{
|
||||
data_len = (device->data == NULL ? 0 : Stream_GetPosition(device->data));
|
||||
data_len = (int) (device->data == NULL ? 0 : Stream_GetPosition(device->data));
|
||||
Stream_EnsureRemainingCapacity(s, 20 + data_len);
|
||||
|
||||
Stream_Write_UINT32(s, device->type); /* deviceType */
|
||||
@ -207,7 +657,7 @@ static void rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
if (pKeys)
|
||||
free(pKeys);
|
||||
|
||||
pos = Stream_GetPosition(s);
|
||||
pos = (int) Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, count_pos);
|
||||
Stream_Write_UINT32(s, count);
|
||||
Stream_SetPosition(s, pos);
|
||||
@ -349,10 +799,14 @@ int rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
|
||||
rdpdrPlugin* plugin = (rdpdrPlugin*) rdpdr;
|
||||
|
||||
if (!plugin)
|
||||
{
|
||||
status = CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = plugin->channelEntryPoints.pVirtualChannelWrite(plugin->OpenHandle,
|
||||
Stream_Buffer(s), Stream_GetPosition(s), s);
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
@ -406,8 +860,8 @@ static void rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr,
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpdr_virtual_channel_open_event(UINT32 openHandle, UINT32 event,
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
static VOID VCAPITYPE rdpdr_virtual_channel_open_event(DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
|
||||
@ -464,7 +918,7 @@ static void* rdpdr_virtual_channel_client_thread(void* arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, void* pData, UINT32 dataLength)
|
||||
static void rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pData, UINT32 dataLength)
|
||||
{
|
||||
UINT32 status;
|
||||
|
||||
@ -493,6 +947,8 @@ static void rdpdr_virtual_channel_event_terminated(rdpdrPlugin* rdpdr)
|
||||
MessagePipe_Free(rdpdr->MsgPipe);
|
||||
CloseHandle(rdpdr->thread);
|
||||
|
||||
drive_hotplug_thread_terminate(rdpdr);
|
||||
|
||||
rdpdr->channelEntryPoints.pVirtualChannelClose(rdpdr->OpenHandle);
|
||||
|
||||
if (rdpdr->data_in)
|
||||
@ -511,7 +967,7 @@ static void rdpdr_virtual_channel_event_terminated(rdpdrPlugin* rdpdr)
|
||||
rdpdr_remove_init_handle_data(rdpdr->InitHandle);
|
||||
}
|
||||
|
||||
static void rdpdr_virtual_channel_init_event(void* pInitHandle, UINT32 event, void* pData, UINT32 dataLength)
|
||||
static VOID VCAPITYPE rdpdr_virtual_channel_init_event(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
|
||||
@ -541,12 +997,14 @@ static void rdpdr_virtual_channel_init_event(void* pInitHandle, UINT32 event, vo
|
||||
/* rdpdr is always built-in */
|
||||
#define VirtualChannelEntry rdpdr_VirtualChannelEntry
|
||||
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) malloc(sizeof(rdpdrPlugin));
|
||||
ZeroMemory(rdpdr, sizeof(rdpdrPlugin));
|
||||
rdpdr = (rdpdrPlugin*) calloc(1, sizeof(rdpdrPlugin));
|
||||
|
||||
if (!rdpdr)
|
||||
return -1;
|
||||
|
||||
rdpdr->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED |
|
||||
@ -555,7 +1013,7 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
strcpy(rdpdr->channelDef.name, "rdpdr");
|
||||
|
||||
CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, pEntryPoints->cbSize);
|
||||
CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP));
|
||||
|
||||
rdpdr->channelEntryPoints.pVirtualChannelInit(&rdpdr->InitHandle,
|
||||
&rdpdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpdr_virtual_channel_init_event);
|
||||
|
@ -38,12 +38,12 @@ typedef struct rdpdr_plugin rdpdrPlugin;
|
||||
struct rdpdr_plugin
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_EX channelEntryPoints;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP channelEntryPoints;
|
||||
|
||||
HANDLE thread;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
UINT32 OpenHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessagePipe* MsgPipe;
|
||||
|
||||
DEVMAN* devman;
|
||||
@ -52,6 +52,14 @@ struct rdpdr_plugin
|
||||
UINT16 versionMinor;
|
||||
UINT16 clientID;
|
||||
char computerName[256];
|
||||
|
||||
/* hotplug support */
|
||||
HANDLE hotplugThread;
|
||||
#ifdef _WIN32
|
||||
HWND hotplug_wnd;
|
||||
#else
|
||||
HANDLE stopEvent;
|
||||
#endif
|
||||
};
|
||||
|
||||
int rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
|
||||
|
@ -630,7 +630,7 @@ static void* rdpdr_server_thread(void* arg)
|
||||
|
||||
static int rdpdr_server_start(RdpdrServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelManagerOpenEx(context->vcm, "rdpdr", 0);
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpdr");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
@ -653,7 +653,7 @@ static int rdpdr_server_stop(RdpdrServerContext* context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
RdpdrServerContext* rdpdr_server_context_new(WTSVirtualChannelManager* vcm)
|
||||
RdpdrServerContext* rdpdr_server_context_new(HANDLE vcm)
|
||||
{
|
||||
RdpdrServerContext* context;
|
||||
|
||||
|
@ -196,7 +196,7 @@ int rdpei_send_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s, UINT16 eventId,
|
||||
Stream_Write_UINT32(s, pduLength); /* pduLength (4 bytes) */
|
||||
Stream_SetPosition(s, Stream_Length(s));
|
||||
|
||||
status = callback->channel->Write(callback->channel, Stream_Length(s), Stream_Buffer(s), NULL);
|
||||
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL);
|
||||
|
||||
#ifdef WITH_DEBUG_RDPEI
|
||||
fprintf(stderr, "rdpei_send_pdu: eventId: %d (%s) length: %d status: %d\n",
|
||||
@ -252,7 +252,7 @@ void rdpei_print_contact_flags(UINT32 contactFlags)
|
||||
|
||||
int rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
{
|
||||
int index;
|
||||
UINT32 index;
|
||||
int rectSize = 2;
|
||||
RDPINPUT_CONTACT_DATA* contact;
|
||||
|
||||
@ -269,7 +269,7 @@ int rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
*/
|
||||
rdpei_write_8byte_unsigned(s, frame->frameOffset * 1000); /* frameOffset (EIGHT_BYTE_UNSIGNED_INTEGER) */
|
||||
|
||||
Stream_EnsureRemainingCapacity(s, frame->contactCount * 64);
|
||||
Stream_EnsureRemainingCapacity(s, (size_t) frame->contactCount * 64);
|
||||
|
||||
for (index = 0; index < frame->contactCount; index++)
|
||||
{
|
||||
@ -345,7 +345,7 @@ int rdpei_send_touch_event_pdu(RDPEI_CHANNEL_CALLBACK* callback, RDPINPUT_TOUCH_
|
||||
* the time that has elapsed (in milliseconds) from when the oldest touch frame
|
||||
* was generated to when it was encoded for transmission by the client.
|
||||
*/
|
||||
rdpei_write_4byte_unsigned(s, frame->frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
|
||||
rdpei_write_4byte_unsigned(s, (UINT32) frame->frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
|
||||
|
||||
rdpei_write_2byte_unsigned(s, 1); /* (frameCount) TWO_BYTE_UNSIGNED_INTEGER */
|
||||
|
||||
@ -379,11 +379,21 @@ int rdpei_recv_sc_ready_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
|
||||
int rdpei_recv_suspend_touch_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
RdpeiClientContext* rdpei = (RdpeiClientContext*) callback->plugin->pInterface;
|
||||
|
||||
if (rdpei->SuspendTouch)
|
||||
rdpei->SuspendTouch(rdpei);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rdpei_recv_resume_touch_pdu(RDPEI_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
RdpeiClientContext* rdpei = (RdpeiClientContext*) callback->plugin->pInterface;
|
||||
|
||||
if (rdpei->ResumeTouch)
|
||||
rdpei->ResumeTouch(rdpei);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -490,7 +500,7 @@ static int rdpei_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
(IWTSListenerCallback*) rdpei->listener_callback, &(rdpei->listener));
|
||||
|
||||
rdpei->listener->pInterface = rdpei->iface.pInterface;
|
||||
|
||||
|
||||
InitializeCriticalSection(&rdpei->lock);
|
||||
rdpei->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
rdpei->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
@ -675,7 +685,7 @@ int rdpei_touch_end(RdpeiClientContext* context, int externalId, int x, int y)
|
||||
int i;
|
||||
int contactId = -1;
|
||||
RDPINPUT_CONTACT_DATA contact;
|
||||
RDPINPUT_CONTACT_POINT* contactPoint;
|
||||
RDPINPUT_CONTACT_POINT* contactPoint = NULL;
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*) context->handle;
|
||||
|
||||
for (i = 0; i < rdpei->maxTouchContacts; i++)
|
||||
|
@ -115,9 +115,7 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
|
||||
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);
|
||||
|
||||
/* Get period size */
|
||||
status = snd_pcm_hw_params_get_period_size_min(hw_params, &alsa->period_size, NULL);
|
||||
SND_PCM_CHECK("snd_pcm_hw_params_get_period_size_min", status);
|
||||
alsa->period_size = alsa->buffer_size / 2;
|
||||
|
||||
/* Set period size */
|
||||
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size, NULL);
|
||||
@ -191,12 +189,12 @@ static int rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
|
||||
* It is also possible for the buffer size to not be an integer multiple of the period size.
|
||||
*/
|
||||
|
||||
int interrupts_per_sec_near = 20;
|
||||
int bytes_per_sec = alsa->actual_rate * alsa->bytes_per_channel * alsa->actual_channels;
|
||||
|
||||
snd_pcm_drop(alsa->pcm_handle);
|
||||
|
||||
if (alsa->latency < 0)
|
||||
alsa->latency = 400;
|
||||
|
||||
alsa->buffer_size = alsa->latency * (alsa->actual_rate / 1000);
|
||||
alsa->buffer_size = bytes_per_sec / (interrupts_per_sec_near / 2);
|
||||
|
||||
if (rdpsnd_alsa_set_hw_params(alsa) < 0)
|
||||
return -1;
|
||||
@ -225,11 +223,6 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* for
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 4:
|
||||
alsa->format = SND_PCM_FORMAT_S16_LE;
|
||||
alsa->bytes_per_channel = 2;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
alsa->format = SND_PCM_FORMAT_S8;
|
||||
alsa->bytes_per_channel = 1;
|
||||
|
@ -17,10 +17,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Use AudioQueue to implement audio redirection
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@ -40,163 +36,242 @@
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
#define AQ_NUM_BUFFERS 10
|
||||
#define AQ_BUF_SIZE (32 * 1024)
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 10
|
||||
#define MAC_AUDIO_QUEUE_BUFFER_SIZE 32768
|
||||
|
||||
static void aq_playback_cb(void *user_data,
|
||||
AudioQueueRef aq_ref,
|
||||
AudioQueueBufferRef aq_buf_ref
|
||||
);
|
||||
|
||||
struct rdpsnd_audio_q_plugin
|
||||
struct rdpsnd_mac_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
|
||||
/* audio queue player state */
|
||||
int is_open; // true when audio_q has been inited
|
||||
char * device_name;
|
||||
int is_playing;
|
||||
int buf_index;
|
||||
BOOL isOpen;
|
||||
BOOL isPlaying;
|
||||
|
||||
UINT32 latency;
|
||||
AUDIO_FORMAT format;
|
||||
int audioBufferIndex;
|
||||
|
||||
AudioStreamBasicDescription data_format;
|
||||
AudioQueueRef aq_ref;
|
||||
AudioQueueBufferRef buffers[AQ_NUM_BUFFERS];
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
};
|
||||
typedef struct rdpsnd_audio_q_plugin rdpsndAudioQPlugin;
|
||||
typedef struct rdpsnd_mac_plugin rdpsndMacPlugin;
|
||||
|
||||
static void rdpsnd_audio_close(rdpsndDevicePlugin* device)
|
||||
static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin*) device;
|
||||
|
||||
AudioQueueStop(aq_plugin_p->aq_ref, 0);
|
||||
aq_plugin_p->is_open = 0;
|
||||
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
int rv;
|
||||
int i;
|
||||
|
||||
rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device;
|
||||
if (aq_plugin_p->is_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
aq_plugin_p->buf_index = 0;
|
||||
|
||||
// setup AudioStreamBasicDescription
|
||||
aq_plugin_p->data_format.mSampleRate = 44100;
|
||||
aq_plugin_p->data_format.mFormatID = kAudioFormatLinearPCM;
|
||||
aq_plugin_p->data_format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
|
||||
// until we know better, assume that one packet = one frame
|
||||
// one frame = bytes_per_sample x number_of_channels
|
||||
aq_plugin_p->data_format.mBytesPerPacket = 4;
|
||||
aq_plugin_p->data_format.mFramesPerPacket = 1;
|
||||
aq_plugin_p->data_format.mBytesPerFrame = 4;
|
||||
aq_plugin_p->data_format.mChannelsPerFrame = 2;
|
||||
aq_plugin_p->data_format.mBitsPerChannel = 16;
|
||||
|
||||
rv = AudioQueueNewOutput(
|
||||
&aq_plugin_p->data_format, // audio stream basic desc
|
||||
aq_playback_cb, // callback when more data is required
|
||||
aq_plugin_p, // data to pass to callback
|
||||
CFRunLoopGetCurrent(), // The current run loop, and the one on
|
||||
// which the audio queue playback callback
|
||||
// will be invoked
|
||||
kCFRunLoopCommonModes, // run loop modes in which callbacks can
|
||||
// be invoked
|
||||
0, // flags - reserved
|
||||
&aq_plugin_p->aq_ref
|
||||
);
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "rdpsnd_audio_open: AudioQueueNewOutput() failed with error %d\n", rv);
|
||||
aq_plugin_p->is_open = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < AQ_NUM_BUFFERS; i++)
|
||||
{
|
||||
rv = AudioQueueAllocateBuffer(aq_plugin_p->aq_ref, AQ_BUF_SIZE, &aq_plugin_p->buffers[i]);
|
||||
}
|
||||
|
||||
aq_plugin_p->is_open = 1;
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
mac->latency = (UINT32) latency;
|
||||
CopyMemory(&(mac->format), format, sizeof(AUDIO_FORMAT));
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case 1: /* PCM */
|
||||
if (format->cbSize == 0 &&
|
||||
(format->nSamplesPerSec <= 48000) &&
|
||||
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||
(format->nChannels == 1 || format->nChannels == 2))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
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_GSM610:
|
||||
mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
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.mReserved = 0;
|
||||
|
||||
rdpsnd_print_audio_format(format);
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
static void rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
{
|
||||
rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device;
|
||||
AudioQueueBufferRef aq_buf_ref;
|
||||
int len;
|
||||
int index;
|
||||
OSStatus status;
|
||||
|
||||
if (!aq_plugin_p->is_open) {
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (mac->isOpen)
|
||||
return;
|
||||
|
||||
mac->audioBufferIndex = 0;
|
||||
|
||||
device->SetFormat(device, format, 0);
|
||||
|
||||
status = AudioQueueNewOutput(&(mac->audioFormat),
|
||||
mac_audio_queue_output_cb, mac,
|
||||
NULL, NULL, 0, &(mac->audioQueue));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
fprintf(stderr, "AudioQueueNewOutput failure\n");
|
||||
return;
|
||||
}
|
||||
|
||||
UInt32 DecodeBufferSizeFrames;
|
||||
UInt32 propertySize = sizeof(DecodeBufferSizeFrames);
|
||||
|
||||
AudioQueueGetProperty(mac->audioQueue,
|
||||
kAudioQueueProperty_DecodeBufferSizeFrames,
|
||||
&DecodeBufferSizeFrames,
|
||||
&propertySize);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
printf("AudioQueueGetProperty failure: kAudioQueueProperty_DecodeBufferSizeFrames\n");
|
||||
}
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE, &mac->audioBuffers[index]);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
fprintf(stderr, "AudioQueueAllocateBuffer failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
mac->isOpen = TRUE;
|
||||
}
|
||||
|
||||
/* get next empty buffer */
|
||||
aq_buf_ref = aq_plugin_p->buffers[aq_plugin_p->buf_index];
|
||||
|
||||
// fill aq_buf_ref with audio data
|
||||
len = size > AQ_BUF_SIZE ? AQ_BUF_SIZE : size;
|
||||
|
||||
memcpy(aq_buf_ref->mAudioData, (char *) data, len);
|
||||
aq_buf_ref->mAudioDataByteSize = len;
|
||||
|
||||
// add buffer to audioqueue
|
||||
AudioQueueEnqueueBuffer(aq_plugin_p->aq_ref, aq_buf_ref, 0, 0);
|
||||
|
||||
// update buf_index
|
||||
aq_plugin_p->buf_index++;
|
||||
if (aq_plugin_p->buf_index >= AQ_NUM_BUFFERS) {
|
||||
aq_plugin_p->buf_index = 0;
|
||||
static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
mac->isOpen = FALSE;
|
||||
|
||||
AudioQueueStop(mac->audioQueue, true);
|
||||
|
||||
AudioQueueDispose(mac->audioQueue, true);
|
||||
mac->audioQueue = NULL;
|
||||
|
||||
mac->isPlaying = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_audio_start(rdpsndDevicePlugin* device)
|
||||
static void rdpsnd_mac_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device;
|
||||
|
||||
AudioQueueStart(aq_plugin_p->aq_ref, NULL);
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
device->Close(device);
|
||||
|
||||
free(mac);
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioQueue Playback callback
|
||||
*
|
||||
* our job here is to fill aq_buf_ref with audio data and enqueue it
|
||||
*/
|
||||
|
||||
static void aq_playback_cb(void* user_data, AudioQueueRef aq_ref, AudioQueueBufferRef aq_buf_ref)
|
||||
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
{
|
||||
if (format->wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_ALAW)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_MULAW)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_GSM610)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
OSStatus status;
|
||||
Float32 fVolume;
|
||||
UINT16 volumeLeft;
|
||||
UINT16 volumeRight;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (!mac->audioQueue)
|
||||
return;
|
||||
|
||||
volumeLeft = (value & 0xFFFF);
|
||||
volumeRight = ((value >> 16) & 0xFFFF);
|
||||
|
||||
fVolume = ((float) volumeLeft) / 65535.0;
|
||||
|
||||
status = AudioQueueSetParameter(mac->audioQueue, kAudioQueueParam_Volume, fVolume);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
fprintf(stderr, "AudioQueueSetParameter kAudioQueueParam_Volume failed: %f\n", fVolume);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "AudioQueueStart failed\n");
|
||||
}
|
||||
|
||||
mac->isPlaying = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
{
|
||||
int length;
|
||||
AudioQueueBufferRef audioBuffer;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (!mac->isOpen)
|
||||
return;
|
||||
|
||||
audioBuffer = mac->audioBuffers[mac->audioBufferIndex];
|
||||
|
||||
length = size > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : size;
|
||||
|
||||
CopyMemory(audioBuffer->mAudioData, data, length);
|
||||
audioBuffer->mAudioDataByteSize = length;
|
||||
|
||||
AudioQueueEnqueueBuffer(mac->audioQueue, audioBuffer, 0, 0);
|
||||
|
||||
mac->audioBufferIndex++;
|
||||
|
||||
if (mac->audioBufferIndex >= MAC_AUDIO_QUEUE_NUM_BUFFERS)
|
||||
{
|
||||
mac->audioBufferIndex = 0;
|
||||
}
|
||||
|
||||
device->Start(device);
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
@ -205,31 +280,25 @@ static void aq_playback_cb(void* user_data, AudioQueueRef aq_ref, AudioQueueBuff
|
||||
|
||||
int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
fprintf(stderr, "freerdp_rdpsnd_client_subsystem_entry()\n\n");
|
||||
rdpsndMacPlugin* mac;
|
||||
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndAudioQPlugin* aqPlugin;
|
||||
|
||||
aqPlugin = (rdpsndAudioQPlugin*) malloc(sizeof(rdpsndAudioQPlugin));
|
||||
ZeroMemory(aqPlugin, sizeof(rdpsndAudioQPlugin));
|
||||
|
||||
aqPlugin->device.Open = rdpsnd_audio_open;
|
||||
aqPlugin->device.FormatSupported = rdpsnd_audio_format_supported;
|
||||
aqPlugin->device.SetFormat = rdpsnd_audio_set_format;
|
||||
aqPlugin->device.SetVolume = rdpsnd_audio_set_volume;
|
||||
aqPlugin->device.Play = rdpsnd_audio_play;
|
||||
aqPlugin->device.Start = rdpsnd_audio_start;
|
||||
aqPlugin->device.Close = rdpsnd_audio_close;
|
||||
aqPlugin->device.Free = rdpsnd_audio_free;
|
||||
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if (args->argc > 2)
|
||||
mac = (rdpsndMacPlugin*) malloc(sizeof(rdpsndMacPlugin));
|
||||
|
||||
if (mac)
|
||||
{
|
||||
/* TODO: parse device name */
|
||||
}
|
||||
ZeroMemory(mac, sizeof(rdpsndMacPlugin));
|
||||
|
||||
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.Play = rdpsnd_mac_play;
|
||||
mac->device.Start = rdpsnd_mac_start;
|
||||
mac->device.Close = rdpsnd_mac_close;
|
||||
mac->device.Free = rdpsnd_mac_free;
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) aqPlugin);
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) mac);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -32,7 +32,11 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MODULE freerdp
|
||||
MODULES freerdp-codec freerdp-utils)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${PULSE_LIBRARY})
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${PULSE_LIBRARY})
|
||||
|
||||
if(GSM_FOUND)
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${GSM_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
@ -26,10 +26,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#ifdef WITH_GSM
|
||||
#include <gsm/gsm.h>
|
||||
#endif
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
@ -52,6 +57,11 @@ struct rdpsnd_pulse_plugin
|
||||
int latency;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
#ifdef WITH_GSM
|
||||
gsm gsm_context;
|
||||
wStream* gsmBuffer;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||
@ -241,6 +251,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT*
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_GSM610:
|
||||
sample_spec.format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -331,6 +342,14 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||
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
|
||||
|
||||
DEBUG_SVC("connected");
|
||||
}
|
||||
else
|
||||
@ -410,7 +429,18 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORM
|
||||
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;
|
||||
}
|
||||
|
||||
@ -459,63 +489,108 @@ static void rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
static BYTE* rdpsnd_pulse_convert_audio(rdpsndDevicePlugin* device, BYTE* data, int* 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;
|
||||
}
|
||||
|
||||
Stream_EnsureRemainingCapacity(pulse->gsmBuffer, 160 * 2);
|
||||
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 len;
|
||||
int ret;
|
||||
BYTE* src;
|
||||
int length;
|
||||
int status;
|
||||
BYTE* pcmData;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||
|
||||
if (!pulse->stream)
|
||||
return;
|
||||
|
||||
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;
|
||||
src = 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;
|
||||
src = pulse->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = data;
|
||||
}
|
||||
pcmData = rdpsnd_pulse_convert_audio(device, data, &size);
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
while ((len = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
while ((length = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (len < 0)
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (length > size)
|
||||
length = size;
|
||||
|
||||
ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
status = pa_stream_write(pulse->stream, pcmData, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
|
||||
if (ret < 0)
|
||||
if (status < 0)
|
||||
{
|
||||
DEBUG_WARN("pa_stream_write failed (%d)",
|
||||
pa_context_errno(pulse->context));
|
||||
break;
|
||||
}
|
||||
|
||||
src += len;
|
||||
size -= len;
|
||||
pcmData += length;
|
||||
size -= length;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
@ -595,6 +670,10 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
|
||||
|
||||
pulse->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
#ifdef WITH_GSM
|
||||
pulse->gsmBuffer = Stream_New(NULL, 4096);
|
||||
#endif
|
||||
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
|
||||
if (!pulse->mainloop)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/thread.h>
|
||||
@ -54,17 +55,19 @@
|
||||
struct rdpsnd_plugin
|
||||
{
|
||||
CHANNEL_DEF channelDef;
|
||||
CHANNEL_ENTRY_POINTS_EX channelEntryPoints;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP channelEntryPoints;
|
||||
|
||||
HANDLE thread;
|
||||
wStream* data_in;
|
||||
void* InitHandle;
|
||||
UINT32 OpenHandle;
|
||||
DWORD OpenHandle;
|
||||
wMessagePipe* MsgPipe;
|
||||
|
||||
wLog* log;
|
||||
HANDLE ScheduleThread;
|
||||
|
||||
BYTE cBlockNo;
|
||||
UINT16 wQualityMode;
|
||||
int wCurrentFormatNo;
|
||||
|
||||
AUDIO_FORMAT* ServerFormats;
|
||||
@ -91,7 +94,7 @@ struct rdpsnd_plugin
|
||||
rdpsndDevicePlugin* device;
|
||||
};
|
||||
|
||||
static void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo);
|
||||
static void rdpsnd_confirm_wave(rdpsndPlugin* rdpsnd, RDPSND_WAVE* wave);
|
||||
|
||||
static void* rdpsnd_schedule_thread(void* arg)
|
||||
{
|
||||
@ -123,9 +126,10 @@ static void* rdpsnd_schedule_thread(void* arg)
|
||||
Sleep(wTimeDiff);
|
||||
}
|
||||
|
||||
rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo);
|
||||
free(wave);
|
||||
rdpsnd_confirm_wave(rdpsnd, wave);
|
||||
|
||||
message.wParam = NULL;
|
||||
free(wave);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -139,9 +143,11 @@ void rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd)
|
||||
Stream_Write_UINT8(pdu, SNDC_QUALITYMODE); /* msgType */
|
||||
Stream_Write_UINT8(pdu, 0); /* bPad */
|
||||
Stream_Write_UINT16(pdu, 4); /* BodySize */
|
||||
Stream_Write_UINT16(pdu, HIGH_QUALITY); /* wQualityMode */
|
||||
Stream_Write_UINT16(pdu, rdpsnd->wQualityMode); /* wQualityMode */
|
||||
Stream_Write_UINT16(pdu, 0); /* Reserved */
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "QualityMode: %d", rdpsnd->wQualityMode);
|
||||
|
||||
rdpsnd_virtual_channel_write(rdpsnd, pdu);
|
||||
}
|
||||
|
||||
@ -258,6 +264,8 @@ void rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
|
||||
Stream_Write(pdu, clientFormat->data, clientFormat->cbSize);
|
||||
}
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Client Audio Formats");
|
||||
|
||||
rdpsnd_virtual_channel_write(rdpsnd, pdu);
|
||||
}
|
||||
|
||||
@ -267,14 +275,14 @@ void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
UINT16 wVersion;
|
||||
AUDIO_FORMAT* format;
|
||||
UINT16 wNumberOfFormats;
|
||||
UINT32 dwVolume;
|
||||
|
||||
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||
rdpsnd->NumberOfServerFormats = 0;
|
||||
rdpsnd->ServerFormats = NULL;
|
||||
|
||||
/* http://msdn.microsoft.com/en-us/library/cc240956.aspx */
|
||||
Stream_Seek_UINT32(s); /* dwFlags */
|
||||
Stream_Read_UINT32(s, dwVolume); /* dwVolume */
|
||||
Stream_Seek_UINT32(s); /* dwVolume */
|
||||
Stream_Seek_UINT32(s); /* dwPitch */
|
||||
Stream_Seek_UINT16(s); /* wDGramPort */
|
||||
Stream_Read_UINT16(s, wNumberOfFormats);
|
||||
@ -310,13 +318,12 @@ void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
|
||||
rdpsnd_select_supported_audio_formats(rdpsnd);
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Server Audio Formats");
|
||||
|
||||
rdpsnd_send_client_audio_formats(rdpsnd);
|
||||
|
||||
if (wVersion >= 6)
|
||||
rdpsnd_send_quality_mode_pdu(rdpsnd);
|
||||
|
||||
if (rdpsnd->device)
|
||||
IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
|
||||
}
|
||||
|
||||
void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, UINT16 wPackSize)
|
||||
@ -330,6 +337,9 @@ void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, U
|
||||
Stream_Write_UINT16(pdu, wTimeStamp);
|
||||
Stream_Write_UINT16(pdu, wPackSize);
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Training Response: wTimeStamp: %d wPackSize: %d",
|
||||
wTimeStamp, wPackSize);
|
||||
|
||||
rdpsnd_virtual_channel_write(rdpsnd, pdu);
|
||||
}
|
||||
|
||||
@ -341,6 +351,9 @@ static void rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
Stream_Read_UINT16(s, wTimeStamp);
|
||||
Stream_Read_UINT16(s, wPackSize);
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Training Request: wTimeStamp: %d wPackSize: %d",
|
||||
wTimeStamp, wPackSize);
|
||||
|
||||
rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
|
||||
}
|
||||
|
||||
@ -361,6 +374,9 @@ static void rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, wStream* s, UINT16 B
|
||||
|
||||
format = &rdpsnd->ClientFormats[wFormatNo];
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "WaveInfo: cBlockNo: %d wFormatNo: %d",
|
||||
rdpsnd->cBlockNo, wFormatNo);
|
||||
|
||||
if (!rdpsnd->isOpen)
|
||||
{
|
||||
rdpsnd->isOpen = TRUE;
|
||||
@ -399,9 +415,20 @@ void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE
|
||||
rdpsnd_virtual_channel_write(rdpsnd, pdu);
|
||||
}
|
||||
|
||||
void rdpsnd_confirm_wave(rdpsndPlugin* rdpsnd, RDPSND_WAVE* wave)
|
||||
{
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "WaveConfirm: cBlockNo: %d wTimeStamp: %d wTimeDiff: %d",
|
||||
wave->cBlockNo, wave->wTimeStampB, wave->wTimeStampB - wave->wTimeStampA);
|
||||
|
||||
rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo);
|
||||
}
|
||||
|
||||
static void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
MessageQueue_Post(device->rdpsnd->MsgPipe->Out, NULL, 0, (void*) wave, NULL);
|
||||
if (device->DisableConfirmThread)
|
||||
rdpsnd_confirm_wave(device->rdpsnd, wave);
|
||||
else
|
||||
MessageQueue_Post(device->rdpsnd->MsgPipe->Out, NULL, 0, (void*) wave, NULL);
|
||||
}
|
||||
|
||||
static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
@ -423,7 +450,7 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
CopyMemory(Stream_Buffer(s), rdpsnd->waveData, 4);
|
||||
|
||||
data = Stream_Buffer(s);
|
||||
size = Stream_Capacity(s);
|
||||
size = (int) Stream_Capacity(s);
|
||||
|
||||
wave = (RDPSND_WAVE*) malloc(sizeof(RDPSND_WAVE));
|
||||
|
||||
@ -434,12 +461,19 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
|
||||
wave->data = data;
|
||||
wave->length = size;
|
||||
wave->AutoConfirm = TRUE;
|
||||
|
||||
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
|
||||
wave->wAudioLength = rdpsnd_compute_audio_time_length(format, size);
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Wave: cBlockNo: %d wTimeStamp: %d",
|
||||
wave->cBlockNo, wave->wTimeStampA);
|
||||
|
||||
if (!rdpsnd->device)
|
||||
{
|
||||
wave->wLocalTimeB = wave->wLocalTimeA;
|
||||
wave->wTimeStampB = wave->wTimeStampA;
|
||||
rdpsnd_confirm_wave(rdpsnd, wave);
|
||||
free(wave);
|
||||
return;
|
||||
}
|
||||
@ -463,11 +497,15 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
wave->wTimeStampB = rdpsnd->wTimeStamp + wave->wAudioLength + TIME_DELAY_MS;
|
||||
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + TIME_DELAY_MS;
|
||||
}
|
||||
rdpsnd->device->WaveConfirm(rdpsnd->device, wave);
|
||||
|
||||
if (wave->AutoConfirm)
|
||||
rdpsnd->device->WaveConfirm(rdpsnd->device, wave);
|
||||
}
|
||||
|
||||
static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
|
||||
{
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Close");
|
||||
|
||||
if (rdpsnd->device)
|
||||
{
|
||||
IFCALL(rdpsnd->device->Close, rdpsnd->device);
|
||||
@ -482,6 +520,8 @@ static void rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
|
||||
Stream_Read_UINT32(s, dwVolume);
|
||||
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Volume: 0x%04X", dwVolume);
|
||||
|
||||
if (rdpsnd->device)
|
||||
{
|
||||
IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
|
||||
@ -597,6 +637,7 @@ COMMAND_LINE_ARGUMENT_A rdpsnd_args[] =
|
||||
{ "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", NULL, NULL, -1, NULL, "rate" },
|
||||
{ "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", NULL, NULL, -1, NULL, "channel" },
|
||||
{ "latency", COMMAND_LINE_VALUE_REQUIRED, "<latency>", NULL, NULL, -1, NULL, "latency" },
|
||||
{ "quality", COMMAND_LINE_VALUE_REQUIRED, "<quality mode>", NULL, NULL, -1, NULL, "quality mode" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
@ -606,10 +647,13 @@ static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args)
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
|
||||
rdpsnd->wQualityMode = HIGH_QUALITY; /* default quality mode */
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
|
||||
rdpsnd_args, flags, rdpsnd, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
return;
|
||||
|
||||
@ -646,6 +690,24 @@ static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args)
|
||||
{
|
||||
rdpsnd->latency = atoi(arg->Value);
|
||||
}
|
||||
CommandLineSwitchCase(arg, "quality")
|
||||
{
|
||||
int wQualityMode = DYNAMIC_QUALITY;
|
||||
|
||||
if (_stricmp(arg->Value, "dynamic") == 0)
|
||||
wQualityMode = DYNAMIC_QUALITY;
|
||||
else if (_stricmp(arg->Value, "medium") == 0)
|
||||
wQualityMode = MEDIUM_QUALITY;
|
||||
else if (_stricmp(arg->Value, "high") == 0)
|
||||
wQualityMode = HIGH_QUALITY;
|
||||
else
|
||||
wQualityMode = atoi(arg->Value);
|
||||
|
||||
if ((wQualityMode < 0) || (wQualityMode > 2))
|
||||
wQualityMode = DYNAMIC_QUALITY;
|
||||
|
||||
rdpsnd->wQualityMode = (UINT16) wQualityMode;
|
||||
}
|
||||
CommandLineSwitchDefault(arg)
|
||||
{
|
||||
|
||||
@ -662,10 +724,6 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd)
|
||||
|
||||
rdpsnd->latency = -1;
|
||||
|
||||
rdpsnd->ScheduleThread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread,
|
||||
(void*) rdpsnd, 0, NULL);
|
||||
|
||||
args = (ADDIN_ARGV*) rdpsnd->channelEntryPoints.pExtendedData;
|
||||
|
||||
if (args)
|
||||
@ -674,7 +732,9 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd)
|
||||
if (rdpsnd->subsystem)
|
||||
{
|
||||
if (strcmp(rdpsnd->subsystem, "fake") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
|
||||
}
|
||||
@ -718,7 +778,7 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd)
|
||||
#if defined(WITH_MACAUDIO)
|
||||
if (!rdpsnd->device)
|
||||
{
|
||||
rdpsnd_set_subsystem(rdpsnd, "macaudio");
|
||||
rdpsnd_set_subsystem(rdpsnd, "mac");
|
||||
rdpsnd_set_device_name(rdpsnd, "default");
|
||||
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
|
||||
}
|
||||
@ -738,31 +798,13 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd)
|
||||
DEBUG_WARN("no sound device.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_process_terminate(rdpsndPlugin* rdpsnd)
|
||||
{
|
||||
if (rdpsnd->device)
|
||||
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
||||
|
||||
MessageQueue_PostQuit(rdpsnd->MsgPipe->Out, 0);
|
||||
|
||||
WaitForSingleObject(rdpsnd->ScheduleThread, INFINITE);
|
||||
CloseHandle(rdpsnd->ScheduleThread);
|
||||
|
||||
if (rdpsnd->subsystem)
|
||||
free(rdpsnd->subsystem);
|
||||
|
||||
if (rdpsnd->device_name)
|
||||
free(rdpsnd->device_name);
|
||||
|
||||
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||
rdpsnd->NumberOfServerFormats = 0;
|
||||
rdpsnd->ServerFormats = NULL;
|
||||
|
||||
rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
|
||||
rdpsnd->NumberOfClientFormats = 0;
|
||||
rdpsnd->ClientFormats = NULL;
|
||||
if (!rdpsnd->device->DisableConfirmThread)
|
||||
{
|
||||
rdpsnd->ScheduleThread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread,
|
||||
(void*) rdpsnd, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -821,10 +863,14 @@ int rdpsnd_virtual_channel_write(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
UINT32 status = 0;
|
||||
|
||||
if (!rdpsnd)
|
||||
{
|
||||
status = CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = rdpsnd->channelEntryPoints.pVirtualChannelWrite(rdpsnd->OpenHandle,
|
||||
Stream_Buffer(s), Stream_GetPosition(s), s);
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
@ -872,8 +918,8 @@ static void rdpsnd_virtual_channel_event_data_received(rdpsndPlugin* plugin,
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_virtual_channel_open_event(UINT32 openHandle, UINT32 event,
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
static VOID VCAPITYPE rdpsnd_virtual_channel_open_event(DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
rdpsndPlugin* plugin;
|
||||
|
||||
@ -930,7 +976,7 @@ static void* rdpsnd_virtual_channel_client_thread(void* arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rdpsnd_virtual_channel_event_connected(rdpsndPlugin* plugin, void* pData, UINT32 dataLength)
|
||||
static void rdpsnd_virtual_channel_event_connected(rdpsndPlugin* plugin, LPVOID pData, UINT32 dataLength)
|
||||
{
|
||||
UINT32 status;
|
||||
|
||||
@ -951,29 +997,42 @@ static void rdpsnd_virtual_channel_event_connected(rdpsndPlugin* plugin, void* p
|
||||
(LPTHREAD_START_ROUTINE) rdpsnd_virtual_channel_client_thread, (void*) plugin, 0, NULL);
|
||||
}
|
||||
|
||||
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* plugin)
|
||||
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
||||
{
|
||||
MessagePipe_PostQuit(plugin->MsgPipe, 0);
|
||||
WaitForSingleObject(plugin->thread, INFINITE);
|
||||
MessagePipe_PostQuit(rdpsnd->MsgPipe, 0);
|
||||
WaitForSingleObject(rdpsnd->thread, INFINITE);
|
||||
CloseHandle(rdpsnd->thread);
|
||||
|
||||
MessagePipe_Free(plugin->MsgPipe);
|
||||
CloseHandle(plugin->thread);
|
||||
rdpsnd->channelEntryPoints.pVirtualChannelClose(rdpsnd->OpenHandle);
|
||||
|
||||
plugin->channelEntryPoints.pVirtualChannelClose(plugin->OpenHandle);
|
||||
|
||||
if (plugin->data_in)
|
||||
if (rdpsnd->data_in)
|
||||
{
|
||||
Stream_Free(plugin->data_in, TRUE);
|
||||
plugin->data_in = NULL;
|
||||
Stream_Free(rdpsnd->data_in, TRUE);
|
||||
rdpsnd->data_in = NULL;
|
||||
}
|
||||
|
||||
rdpsnd_process_terminate(plugin);
|
||||
if (rdpsnd->device)
|
||||
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
||||
|
||||
rdpsnd_remove_open_handle_data(plugin->OpenHandle);
|
||||
rdpsnd_remove_init_handle_data(plugin->InitHandle);
|
||||
if (rdpsnd->subsystem)
|
||||
free(rdpsnd->subsystem);
|
||||
|
||||
if (rdpsnd->device_name)
|
||||
free(rdpsnd->device_name);
|
||||
|
||||
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||
rdpsnd->NumberOfServerFormats = 0;
|
||||
rdpsnd->ServerFormats = NULL;
|
||||
|
||||
rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
|
||||
rdpsnd->NumberOfClientFormats = 0;
|
||||
rdpsnd->ClientFormats = NULL;
|
||||
|
||||
rdpsnd_remove_open_handle_data(rdpsnd->OpenHandle);
|
||||
rdpsnd_remove_init_handle_data(rdpsnd->InitHandle);
|
||||
}
|
||||
|
||||
static void rdpsnd_virtual_channel_init_event(void* pInitHandle, UINT32 event, void* pData, UINT32 dataLength)
|
||||
static VOID VCAPITYPE rdpsnd_virtual_channel_init_event(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength)
|
||||
{
|
||||
rdpsndPlugin* plugin;
|
||||
|
||||
@ -1003,7 +1062,7 @@ static void rdpsnd_virtual_channel_init_event(void* pInitHandle, UINT32 event, v
|
||||
/* rdpsnd is always built-in */
|
||||
#define VirtualChannelEntry rdpsnd_VirtualChannelEntry
|
||||
|
||||
int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
rdpsndPlugin* rdpsnd;
|
||||
|
||||
@ -1028,8 +1087,10 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
strcpy(rdpsnd->channelDef.name, "rdpsnd");
|
||||
|
||||
CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, pEntryPoints->cbSize);
|
||||
CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP));
|
||||
|
||||
rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
|
||||
|
||||
rdpsnd->channelEntryPoints.pVirtualChannelInit(&rdpsnd->InitHandle,
|
||||
&rdpsnd->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpsnd_virtual_channel_init_event);
|
||||
|
||||
|
@ -38,28 +38,16 @@
|
||||
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
typedef struct rdpsnd_winmm_datablock rdpsndWinmmDatablock;
|
||||
|
||||
struct rdpsnd_winmm_datablock
|
||||
{
|
||||
WAVEHDR header;
|
||||
rdpsndWinmmDatablock* next;
|
||||
};
|
||||
|
||||
typedef struct rdpsnd_winmm_plugin rdpsndWinmmPlugin;
|
||||
|
||||
struct rdpsnd_winmm_plugin
|
||||
{
|
||||
rdpsndDevicePlugin device;
|
||||
|
||||
HWAVEOUT out_handle;
|
||||
HWAVEOUT hWaveOut;
|
||||
WAVEFORMATEX format;
|
||||
int wformat;
|
||||
int block_size;
|
||||
int latency;
|
||||
HANDLE event;
|
||||
rdpsndWinmmDatablock* datablock_head;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
|
||||
@ -92,28 +80,9 @@ static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* ou
|
||||
return result;
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_clear_datablocks(rdpsndWinmmPlugin* winmm, BOOL drain)
|
||||
{
|
||||
rdpsndWinmmDatablock* datablock;
|
||||
|
||||
while ((datablock = winmm->datablock_head) != NULL)
|
||||
{
|
||||
if (!drain && (datablock->header.dwFlags & WHDR_DONE) == 0)
|
||||
break;
|
||||
|
||||
while ((datablock->header.dwFlags & WHDR_DONE) == 0)
|
||||
WaitForSingleObject(winmm->event, INFINITE);
|
||||
|
||||
winmm->datablock_head = datablock->next;
|
||||
|
||||
free(datablock->header.lpData);
|
||||
free(datablock);
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (format)
|
||||
{
|
||||
@ -122,57 +91,107 @@ static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* fo
|
||||
winmm->wformat = format->wFormatTag;
|
||||
winmm->block_size = format->nBlockAlign;
|
||||
}
|
||||
}
|
||||
|
||||
winmm->latency = latency;
|
||||
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;
|
||||
|
||||
switch (uMsg)
|
||||
{
|
||||
case MM_WOM_OPEN:
|
||||
fprintf(stderr, "MM_WOM_OPEN\n");
|
||||
break;
|
||||
|
||||
case MM_WOM_CLOSE:
|
||||
fprintf(stderr, "MM_WOM_CLOSE\n");
|
||||
break;
|
||||
|
||||
case MM_WOM_DONE:
|
||||
{
|
||||
UINT32 wTimeDelta;
|
||||
lpWaveHdr = (LPWAVEHDR) dwParam1;
|
||||
|
||||
if (!lpWaveHdr)
|
||||
return;
|
||||
|
||||
wave = (RDPSND_WAVE*) lpWaveHdr->dwUser;
|
||||
|
||||
if (!wave)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "MM_WOM_DONE: dwBufferLength: %d cBlockNo: %d\n",
|
||||
lpWaveHdr->dwBufferLength, wave->cBlockNo);
|
||||
|
||||
wave->wLocalTimeB = GetTickCount();
|
||||
wTimeDelta = wave->wLocalTimeB - wave->wLocalTimeA;
|
||||
wave->wTimeStampB = wave->wTimeStampA + wTimeDelta;
|
||||
|
||||
winmm->device.WaveConfirm(&(winmm->device), wave);
|
||||
|
||||
if (lpWaveHdr->lpData)
|
||||
free(lpWaveHdr->lpData);
|
||||
|
||||
free(wave);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
|
||||
{
|
||||
MMRESULT result;
|
||||
MMRESULT mmResult;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (winmm->out_handle != NULL)
|
||||
if (winmm->hWaveOut)
|
||||
return;
|
||||
|
||||
DEBUG_SVC("opening");
|
||||
|
||||
rdpsnd_winmm_set_format(device, format, latency);
|
||||
freerdp_dsp_context_reset_adpcm(winmm->dsp_context);
|
||||
|
||||
result = waveOutOpen(&winmm->out_handle, WAVE_MAPPER, &winmm->format, (DWORD_PTR) winmm->event, 0, CALLBACK_EVENT);
|
||||
mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
|
||||
(DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION);
|
||||
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
DEBUG_WARN("waveOutOpen failed: %d", result);
|
||||
fprintf(stderr, "waveOutOpen failed: %d\n", mmResult);
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
|
||||
MMRESULT mmResult;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (winmm->out_handle)
|
||||
if (winmm->hWaveOut)
|
||||
{
|
||||
DEBUG_SVC("close");
|
||||
rdpsnd_winmm_clear_datablocks(winmm, TRUE);
|
||||
mmResult = waveOutReset(winmm->hWaveOut);
|
||||
|
||||
if (waveOutClose(winmm->out_handle) != MMSYSERR_NOERROR)
|
||||
mmResult = waveOutClose(winmm->hWaveOut);
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
DEBUG_WARN("waveOutClose error");
|
||||
fprintf(stderr, "waveOutClose failure: %d\n", mmResult);
|
||||
}
|
||||
|
||||
winmm->out_handle = NULL;
|
||||
winmm->hWaveOut = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
rdpsnd_winmm_close(device);
|
||||
freerdp_dsp_context_free(winmm->dsp_context);
|
||||
CloseHandle(winmm->event);
|
||||
free(winmm);
|
||||
if (winmm)
|
||||
{
|
||||
rdpsnd_winmm_close(device);
|
||||
|
||||
freerdp_dsp_context_free(winmm->dsp_context);
|
||||
|
||||
free(winmm);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
@ -203,10 +222,10 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
|
||||
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
|
||||
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
|
||||
|
||||
if (!winmm->out_handle)
|
||||
if (!winmm->hWaveOut)
|
||||
return dwVolume;
|
||||
|
||||
waveOutGetVolume(winmm->out_handle, &dwVolume);
|
||||
waveOutGetVolume(winmm->hWaveOut, &dwVolume);
|
||||
|
||||
return dwVolume;
|
||||
}
|
||||
@ -215,82 +234,94 @@ static void rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (!winmm->out_handle)
|
||||
if (!winmm->hWaveOut)
|
||||
return;
|
||||
|
||||
waveOutSetVolume(winmm->out_handle, value);
|
||||
waveOutSetVolume(winmm->hWaveOut, value);
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
static void rdpsnd_winmm_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
BYTE* src;
|
||||
MMRESULT result;
|
||||
rdpsndWinmmDatablock* last;
|
||||
rdpsndWinmmDatablock* datablock;
|
||||
int length;
|
||||
BYTE* data;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (!winmm->out_handle)
|
||||
return;
|
||||
|
||||
if (winmm->wformat == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
winmm->dsp_context->decode_ms_adpcm(winmm->dsp_context,
|
||||
data, size, winmm->format.nChannels, winmm->block_size);
|
||||
size = winmm->dsp_context->adpcm_size;
|
||||
src = winmm->dsp_context->adpcm_buffer;
|
||||
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,
|
||||
data, size, winmm->format.nChannels, winmm->block_size);
|
||||
size = winmm->dsp_context->adpcm_size;
|
||||
src = winmm->dsp_context->adpcm_buffer;
|
||||
wave->data, wave->length, winmm->format.nChannels, winmm->block_size);
|
||||
length = winmm->dsp_context->adpcm_size;
|
||||
data = winmm->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = data;
|
||||
length = wave->length;
|
||||
data = wave->data;
|
||||
}
|
||||
|
||||
rdpsnd_winmm_clear_datablocks(winmm, FALSE);
|
||||
wave->data = (BYTE*) malloc(length);
|
||||
CopyMemory(wave->data, data, length);
|
||||
wave->length = length;
|
||||
}
|
||||
|
||||
for (last = winmm->datablock_head; last && last->next; last = last->next)
|
||||
void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
MMRESULT mmResult;
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
if (!winmm->hWaveOut)
|
||||
return;
|
||||
|
||||
wave->AutoConfirm = FALSE;
|
||||
|
||||
lpWaveHdr = (LPWAVEHDR) malloc(sizeof(WAVEHDR));
|
||||
|
||||
if (!lpWaveHdr)
|
||||
return;
|
||||
|
||||
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->lpNext = NULL;
|
||||
|
||||
mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
datablock = (rdpsndWinmmDatablock*) malloc(sizeof(rdpsndWinmmDatablock));
|
||||
ZeroMemory(datablock, sizeof(rdpsndWinmmDatablock));
|
||||
|
||||
if (last)
|
||||
last->next = datablock;
|
||||
else
|
||||
winmm->datablock_head = datablock;
|
||||
|
||||
datablock->header.dwBufferLength = size;
|
||||
datablock->header.lpData = (LPSTR) malloc(size);
|
||||
CopyMemory(datablock->header.lpData, src, size);
|
||||
|
||||
result = waveOutPrepareHeader(winmm->out_handle, &datablock->header, sizeof(datablock->header));
|
||||
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
DEBUG_WARN("waveOutPrepareHeader: %d", result);
|
||||
fprintf(stderr, "waveOutPrepareHeader failure: %d\n", mmResult);
|
||||
return;
|
||||
}
|
||||
|
||||
mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
|
||||
if (mmResult != MMSYSERR_NOERROR)
|
||||
{
|
||||
fprintf(stderr, "waveOutWrite failure: %d\n", mmResult);
|
||||
waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
|
||||
return;
|
||||
}
|
||||
|
||||
ResetEvent(winmm->event);
|
||||
waveOutWrite(winmm->out_handle, &datablock->header, sizeof(datablock->header));
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
|
||||
rdpsnd_winmm_clear_datablocks(winmm, FALSE);
|
||||
//rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
|
||||
}
|
||||
|
||||
static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
@ -305,12 +336,15 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
|
||||
winmm = (rdpsndWinmmPlugin*) malloc(sizeof(rdpsndWinmmPlugin));
|
||||
ZeroMemory(winmm, sizeof(rdpsndWinmmPlugin));
|
||||
|
||||
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.Play = rdpsnd_winmm_play;
|
||||
winmm->device.WaveDecode = rdpsnd_winmm_wave_decode;
|
||||
winmm->device.WavePlay = rdpsnd_winmm_wave_play;
|
||||
winmm->device.Start = rdpsnd_winmm_start;
|
||||
winmm->device.Close = rdpsnd_winmm_close;
|
||||
winmm->device.Free = rdpsnd_winmm_free;
|
||||
@ -319,7 +353,6 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
|
||||
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args);
|
||||
|
||||
winmm->dsp_context = freerdp_dsp_context_new();
|
||||
winmm->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) winmm);
|
||||
|
||||
|
@ -28,7 +28,7 @@ set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE freerdp
|
||||
MODULES freerdp-utils)
|
||||
MODULES freerdp-codec freerdp-utils)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
@ -201,12 +201,7 @@ static void* rdpsnd_server_thread(void* arg)
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
{
|
||||
if (BytesReturned)
|
||||
Stream_Seek(s, BytesReturned);
|
||||
}
|
||||
else
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
if (!BytesReturned)
|
||||
break;
|
||||
@ -488,7 +483,7 @@ static BOOL rdpsnd_server_close(RdpsndServerContext* context)
|
||||
|
||||
static int rdpsnd_server_start(RdpsndServerContext* context)
|
||||
{
|
||||
context->priv->ChannelHandle = WTSVirtualChannelManagerOpenEx(context->vcm, "rdpsnd", 0);
|
||||
context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd");
|
||||
|
||||
if (!context->priv->ChannelHandle)
|
||||
return -1;
|
||||
@ -513,7 +508,7 @@ static int rdpsnd_server_stop(RdpsndServerContext* context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
RdpsndServerContext* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm)
|
||||
RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
||||
{
|
||||
RdpsndServerContext* context;
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "serial_constants.h"
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/stream.h>
|
||||
@ -64,39 +65,27 @@ struct _SERIAL_DEVICE
|
||||
char* path;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
wLog* log;
|
||||
HANDLE thread;
|
||||
HANDLE mthread;
|
||||
HANDLE stopEvent;
|
||||
HANDLE newEvent;
|
||||
|
||||
wQueue* queue;
|
||||
LIST* pending_irps;
|
||||
|
||||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
UINT32 nfds;
|
||||
struct timeval tv;
|
||||
UINT32 select_timeout;
|
||||
UINT32 timeout_id;
|
||||
wMessageQueue* IrpQueue;
|
||||
};
|
||||
|
||||
static void serial_abort_single_io(SERIAL_DEVICE* serial, UINT32 file_id, UINT32 abort_io, UINT32 io_status);
|
||||
static void serial_check_for_events(SERIAL_DEVICE* serial);
|
||||
static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp);
|
||||
static BOOL serial_check_fds(SERIAL_DEVICE* serial);
|
||||
static void* serial_thread_mfunc(void* arg);
|
||||
|
||||
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
char* path = NULL;
|
||||
int status;
|
||||
SERIAL_TTY* tty;
|
||||
UINT32 PathLength;
|
||||
UINT32 FileId;
|
||||
UINT32 PathLength;
|
||||
char* path = NULL;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
Stream_Seek(irp->input, 28); /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
|
||||
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
Stream_Seek_UINT32(irp->input); /* DesiredAccess (4 bytes) */
|
||||
Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */
|
||||
Stream_Seek_UINT32(irp->input); /* FileAttributes (4 bytes) */
|
||||
Stream_Seek_UINT32(irp->input); /* SharedAccess (4 bytes) */
|
||||
Stream_Seek_UINT32(irp->input); /* CreateDisposition (4 bytes) */
|
||||
Stream_Seek_UINT32(irp->input); /* CreateOptions (4 bytes) */
|
||||
|
||||
Stream_Read_UINT32(irp->input, PathLength); /* PathLength (4 bytes) */
|
||||
|
||||
status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input),
|
||||
PathLength / 2, &path, 0, NULL, NULL);
|
||||
@ -108,7 +97,7 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
|
||||
|
||||
tty = serial_tty_new(serial->path, FileId);
|
||||
|
||||
if (tty == NULL)
|
||||
if (!tty)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
FileId = 0;
|
||||
@ -118,23 +107,11 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
|
||||
else
|
||||
{
|
||||
serial->tty = tty;
|
||||
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_NONE,
|
||||
STATUS_CANCELLED);
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_READ,
|
||||
STATUS_CANCELLED);
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_WRITE,
|
||||
STATUS_CANCELLED);
|
||||
|
||||
serial->mthread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) serial_thread_mfunc, (void*) serial,
|
||||
0, NULL);
|
||||
|
||||
DEBUG_SVC("%s(%d) created.", serial->path, FileId);
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, FileId);
|
||||
Stream_Write_UINT8(irp->output, 0);
|
||||
Stream_Write_UINT32(irp->output, FileId); /* FileId (4 bytes) */
|
||||
Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
|
||||
|
||||
free(path);
|
||||
|
||||
@ -143,11 +120,11 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
|
||||
|
||||
static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
SERIAL_TTY* tty = serial->tty;
|
||||
|
||||
tty = serial->tty;
|
||||
Stream_Seek(irp->input, 32); /* Padding (32 bytes) */
|
||||
|
||||
if (tty == NULL)
|
||||
if (!tty)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
DEBUG_WARN("tty not valid.");
|
||||
@ -156,35 +133,27 @@ static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
DEBUG_SVC("%s(%d) closed.", serial->path, tty->id);
|
||||
|
||||
TerminateThread(serial->mthread, 0);
|
||||
WaitForSingleObject(serial->mthread, INFINITE);
|
||||
CloseHandle(serial->mthread);
|
||||
serial->mthread = NULL;
|
||||
|
||||
serial_tty_free(tty);
|
||||
serial->tty = NULL;
|
||||
}
|
||||
|
||||
Stream_Zero(irp->output, 5); /* Padding(5) */
|
||||
Stream_Zero(irp->output, 5); /* Padding (5 bytes) */
|
||||
|
||||
irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
BYTE* buffer = NULL;
|
||||
SERIAL_TTY* tty = serial->tty;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
|
||||
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
|
||||
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
|
||||
|
||||
DEBUG_SVC("length %u offset %llu", Length, Offset);
|
||||
|
||||
tty = serial->tty;
|
||||
|
||||
if (tty == NULL)
|
||||
if (!tty)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
Length = 0;
|
||||
@ -210,7 +179,7 @@ static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
|
||||
}
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
|
||||
|
||||
if (Length > 0)
|
||||
{
|
||||
@ -225,59 +194,60 @@ static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
|
||||
|
||||
static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
int status;
|
||||
UINT32 Length;
|
||||
UINT64 Offset;
|
||||
SERIAL_TTY* tty = serial->tty;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
|
||||
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
|
||||
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
|
||||
|
||||
DEBUG_SVC("length %u offset %llu", Length, Offset);
|
||||
|
||||
tty = serial->tty;
|
||||
|
||||
if (tty == NULL)
|
||||
if (!tty)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
Length = 0;
|
||||
|
||||
DEBUG_WARN("tty not valid.");
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
|
||||
irp->Complete(irp);
|
||||
return;
|
||||
}
|
||||
else if (!serial_tty_write(tty, Stream_Pointer(irp->input), Length))
|
||||
|
||||
status = serial_tty_write(tty, Stream_Pointer(irp->input), Length);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
Length = 0;
|
||||
|
||||
DEBUG_WARN("write %s(%d) failed.", serial->path, tty->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_SVC("write %llu-%llu to %s(%d).", Offset, Offset + Length, serial->path, tty->id);
|
||||
printf("serial_tty_write failure: status: %d, errno: %d\n", status, errno);
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
|
||||
irp->Complete(irp);
|
||||
return;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding */
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
|
||||
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
|
||||
irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
UINT32 IoControlCode;
|
||||
UINT32 InputBufferLength;
|
||||
UINT32 OutputBufferLength;
|
||||
UINT32 abort_io = SERIAL_ABORT_IO_NONE;
|
||||
UINT32 abortIo = SERIAL_ABORT_IO_NONE;
|
||||
SERIAL_TTY* tty = serial->tty;
|
||||
|
||||
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
|
||||
|
||||
Stream_Read_UINT32(irp->input, InputBufferLength);
|
||||
Stream_Read_UINT32(irp->input, OutputBufferLength);
|
||||
Stream_Read_UINT32(irp->input, IoControlCode);
|
||||
Stream_Seek(irp->input, 20); /* Padding */
|
||||
|
||||
tty = serial->tty;
|
||||
Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */
|
||||
Stream_Read_UINT32(irp->input, InputBufferLength); /* InputBufferLength (4 bytes) */
|
||||
Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */
|
||||
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
|
||||
|
||||
if (!tty)
|
||||
{
|
||||
@ -288,23 +258,16 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abort_io);
|
||||
irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abortIo);
|
||||
}
|
||||
|
||||
if (abort_io & SERIAL_ABORT_IO_WRITE)
|
||||
serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_WRITE, STATUS_CANCELLED);
|
||||
if (abort_io & SERIAL_ABORT_IO_READ)
|
||||
serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_READ, STATUS_CANCELLED);
|
||||
|
||||
if (irp->IoStatus == STATUS_PENDING)
|
||||
list_enqueue(serial->pending_irps, irp);
|
||||
else
|
||||
irp->Complete(irp);
|
||||
irp->Complete(irp);
|
||||
}
|
||||
|
||||
static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
DEBUG_SVC("MajorFunction %u", irp->MajorFunction);
|
||||
WLog_Print(serial->log, WLOG_DEBUG, "IRP MajorFunction: 0x%04X MinorFunction: 0x%04X\n",
|
||||
irp->MajorFunction, irp->MinorFunction);
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
@ -317,13 +280,11 @@ static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
|
||||
break;
|
||||
|
||||
case IRP_MJ_READ:
|
||||
serial_handle_async_irp(serial, irp);
|
||||
//serial_process_irp_read(serial, irp);
|
||||
serial_process_irp_read(serial, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_WRITE:
|
||||
serial_handle_async_irp(serial, irp);
|
||||
//serial_process_irp_write(serial, irp);
|
||||
serial_process_irp_write(serial, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
@ -336,74 +297,29 @@ static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
|
||||
irp->Complete(irp);
|
||||
break;
|
||||
}
|
||||
|
||||
serial_check_for_events(serial);
|
||||
}
|
||||
|
||||
/* This thread is used as a workaround for the missing serial event
|
||||
* support in WaitForMultipleObjects.
|
||||
* It monitors the terminal for events and posts it in a supported
|
||||
* form that WaitForMultipleObjects can use it. */
|
||||
void* serial_thread_mfunc(void* arg)
|
||||
{
|
||||
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg;
|
||||
|
||||
assert(NULL != serial);
|
||||
while(1)
|
||||
{
|
||||
int sl;
|
||||
fd_set rd;
|
||||
|
||||
if(!serial->tty || serial->tty->fd <= 0)
|
||||
{
|
||||
DEBUG_WARN("Monitor thread still running, but no terminal opened!");
|
||||
sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
FD_ZERO(&rd);
|
||||
FD_SET(serial->tty->fd, &rd);
|
||||
sl = select(serial->tty->fd + 1, &rd, NULL, NULL, NULL);
|
||||
if( sl > 0 )
|
||||
SetEvent(serial->newEvent);
|
||||
}
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* serial_thread_func(void* arg)
|
||||
{
|
||||
IRP* irp;
|
||||
DWORD status;
|
||||
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg;
|
||||
HANDLE ev[] = {serial->stopEvent, Queue_Event(serial->queue), serial->newEvent};
|
||||
wMessage message;
|
||||
SERIAL_DEVICE* drive = (SERIAL_DEVICE*) arg;
|
||||
|
||||
assert(NULL != serial);
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(3, ev, FALSE, INFINITE);
|
||||
|
||||
if (WAIT_OBJECT_0 == status)
|
||||
if (!MessageQueue_Wait(drive->IrpQueue))
|
||||
break;
|
||||
else if (status == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
FD_ZERO(&serial->read_fds);
|
||||
FD_ZERO(&serial->write_fds);
|
||||
|
||||
serial->tv.tv_sec = 0;
|
||||
serial->tv.tv_usec = 0;
|
||||
serial->select_timeout = 0;
|
||||
if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
|
||||
break;
|
||||
|
||||
if ((irp = (IRP*) Queue_Dequeue(serial->queue)))
|
||||
serial_process_irp(serial, irp);
|
||||
}
|
||||
else if (status == WAIT_OBJECT_0 + 2)
|
||||
ResetEvent(serial->newEvent);
|
||||
if (message.id == WMQ_QUIT)
|
||||
break;
|
||||
|
||||
if(serial->tty)
|
||||
serial_check_fds(serial);
|
||||
irp = (IRP*) message.wParam;
|
||||
|
||||
if (irp)
|
||||
serial_process_irp(drive, irp);
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
@ -413,366 +329,28 @@ static void* serial_thread_func(void* arg)
|
||||
static void serial_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device;
|
||||
|
||||
Queue_Enqueue(serial->queue, irp);
|
||||
MessageQueue_Post(serial->IrpQueue, NULL, 0, (void*) irp, NULL);
|
||||
}
|
||||
|
||||
static void serial_free(DEVICE* device)
|
||||
{
|
||||
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device;
|
||||
|
||||
DEBUG_SVC("freeing device");
|
||||
WLog_Print(serial->log, WLOG_DEBUG, "freeing");
|
||||
|
||||
/* Stop thread */
|
||||
SetEvent(serial->stopEvent);
|
||||
if(serial->mthread)
|
||||
{
|
||||
TerminateThread(serial->mthread, 0);
|
||||
WaitForSingleObject(serial->mthread, INFINITE);
|
||||
CloseHandle(serial->mthread);
|
||||
}
|
||||
MessageQueue_PostQuit(serial->IrpQueue, 0);
|
||||
WaitForSingleObject(serial->thread, INFINITE);
|
||||
CloseHandle(serial->thread);
|
||||
|
||||
serial_tty_free(serial->tty);
|
||||
|
||||
/* Clean up resources */
|
||||
Stream_Free(serial->device.data, TRUE);
|
||||
Queue_Free(serial->queue);
|
||||
list_free(serial->pending_irps);
|
||||
CloseHandle(serial->stopEvent);
|
||||
CloseHandle(serial->newEvent);
|
||||
CloseHandle(serial->thread);
|
||||
MessageQueue_Free(serial->IrpQueue);
|
||||
|
||||
free(serial);
|
||||
}
|
||||
|
||||
static void serial_abort_single_io(SERIAL_DEVICE* serial, UINT32 file_id, UINT32 abort_io, UINT32 io_status)
|
||||
{
|
||||
UINT32 major;
|
||||
IRP* irp = NULL;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
|
||||
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (abort_io)
|
||||
{
|
||||
case SERIAL_ABORT_IO_NONE:
|
||||
major = 0;
|
||||
break;
|
||||
|
||||
case SERIAL_ABORT_IO_READ:
|
||||
major = IRP_MJ_READ;
|
||||
break;
|
||||
|
||||
case SERIAL_ABORT_IO_WRITE:
|
||||
major = IRP_MJ_WRITE;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_SVC("unexpected abort_io code %d", abort_io);
|
||||
return;
|
||||
}
|
||||
|
||||
irp = (IRP*) list_peek(serial->pending_irps);
|
||||
|
||||
while (irp)
|
||||
{
|
||||
if (irp->FileId != file_id || irp->MajorFunction != major)
|
||||
{
|
||||
irp = (IRP*) list_next(serial->pending_irps, irp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process a SINGLE FileId and MajorFunction */
|
||||
list_remove(serial->pending_irps, irp);
|
||||
irp->IoStatus = io_status;
|
||||
Stream_Write_UINT32(irp->output, 0);
|
||||
irp->Complete(irp);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps));
|
||||
}
|
||||
|
||||
static void serial_check_for_events(SERIAL_DEVICE* serial)
|
||||
{
|
||||
IRP* irp = NULL;
|
||||
IRP* prev;
|
||||
UINT32 result = 0;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
|
||||
|
||||
irp = (IRP*) list_peek(serial->pending_irps);
|
||||
|
||||
while (irp)
|
||||
{
|
||||
prev = NULL;
|
||||
|
||||
if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
|
||||
{
|
||||
if (serial_tty_get_event(tty, &result))
|
||||
{
|
||||
DEBUG_SVC("got event result %u", result);
|
||||
|
||||
irp->IoStatus = STATUS_SUCCESS;
|
||||
Stream_Write_UINT32(irp->output, result);
|
||||
irp->Complete(irp);
|
||||
|
||||
prev = irp;
|
||||
irp = (IRP*) list_next(serial->pending_irps, irp);
|
||||
list_remove(serial->pending_irps, prev);
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev)
|
||||
irp = (IRP*) list_next(serial->pending_irps, irp);
|
||||
}
|
||||
|
||||
DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps));
|
||||
}
|
||||
|
||||
void serial_get_timeouts(SERIAL_DEVICE* serial, IRP* irp, UINT32* timeout, UINT32* interval_timeout)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
UINT32 Length;
|
||||
UINT32 pos;
|
||||
|
||||
pos = Stream_GetPosition(irp->input);
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_SetPosition(irp->input, pos);
|
||||
|
||||
DEBUG_SVC("length read %u", Length);
|
||||
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
*timeout = (tty->read_total_timeout_multiplier * Length) + tty->read_total_timeout_constant;
|
||||
*interval_timeout = tty->read_interval_timeout;
|
||||
|
||||
DEBUG_SVC("timeouts %u %u", *timeout, *interval_timeout);
|
||||
}
|
||||
|
||||
static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
UINT32 timeout = 0;
|
||||
UINT32 itv_timeout = 0;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_WRITE:
|
||||
DEBUG_SVC("handling IRP_MJ_WRITE");
|
||||
break;
|
||||
|
||||
case IRP_MJ_READ:
|
||||
DEBUG_SVC("handling IRP_MJ_READ");
|
||||
|
||||
serial_get_timeouts(serial, irp, &timeout, &itv_timeout);
|
||||
|
||||
/* Check if io request timeout is smaller than current (but not 0). */
|
||||
if (timeout && ((serial->select_timeout == 0) || (timeout < serial->select_timeout)))
|
||||
{
|
||||
serial->select_timeout = timeout;
|
||||
serial->tv.tv_sec = serial->select_timeout / 1000;
|
||||
serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000;
|
||||
serial->timeout_id = tty->id;
|
||||
}
|
||||
if (itv_timeout && ((serial->select_timeout == 0) || (itv_timeout < serial->select_timeout)))
|
||||
{
|
||||
serial->select_timeout = itv_timeout;
|
||||
serial->tv.tv_sec = serial->select_timeout / 1000;
|
||||
serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000;
|
||||
serial->timeout_id = tty->id;
|
||||
}
|
||||
DEBUG_SVC("select_timeout %u, tv_sec %lu tv_usec %lu, timeout_id %u",
|
||||
serial->select_timeout, serial->tv.tv_sec, serial->tv.tv_usec, serial->timeout_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_SVC("no need to handle %d", irp->MajorFunction);
|
||||
return;
|
||||
}
|
||||
|
||||
irp->IoStatus = STATUS_PENDING;
|
||||
list_enqueue(serial->pending_irps, irp);
|
||||
}
|
||||
|
||||
static void __serial_check_fds(SERIAL_DEVICE* serial)
|
||||
{
|
||||
IRP* irp;
|
||||
IRP* prev;
|
||||
SERIAL_TTY* tty;
|
||||
UINT32 result = 0;
|
||||
BOOL irp_completed = FALSE;
|
||||
|
||||
ZeroMemory(&serial->tv, sizeof(struct timeval));
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
/* scan every pending */
|
||||
irp = list_peek(serial->pending_irps);
|
||||
|
||||
while (irp)
|
||||
{
|
||||
DEBUG_SVC("MajorFunction %u", irp->MajorFunction);
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_READ:
|
||||
if (FD_ISSET(tty->fd, &serial->read_fds))
|
||||
{
|
||||
irp->IoStatus = STATUS_SUCCESS;
|
||||
serial_process_irp_read(serial, irp);
|
||||
irp_completed = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case IRP_MJ_WRITE:
|
||||
if (FD_ISSET(tty->fd, &serial->write_fds))
|
||||
{
|
||||
irp->IoStatus = STATUS_SUCCESS;
|
||||
serial_process_irp_write(serial, irp);
|
||||
irp_completed = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
if (serial_tty_get_event(tty, &result))
|
||||
{
|
||||
DEBUG_SVC("got event result %u", result);
|
||||
|
||||
irp->IoStatus = STATUS_SUCCESS;
|
||||
Stream_Write_UINT32(irp->output, result);
|
||||
irp->Complete(irp);
|
||||
irp_completed = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_SVC("no request found");
|
||||
break;
|
||||
}
|
||||
|
||||
prev = irp;
|
||||
irp = (IRP*) list_next(serial->pending_irps, irp);
|
||||
|
||||
if (irp_completed || (prev->IoStatus == STATUS_SUCCESS))
|
||||
list_remove(serial->pending_irps, prev);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_set_fds(SERIAL_DEVICE* serial)
|
||||
{
|
||||
IRP* irp;
|
||||
fd_set* fds;
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
|
||||
|
||||
tty = serial->tty;
|
||||
if(!tty)
|
||||
{
|
||||
DEBUG_WARN("tty = %p", tty);
|
||||
return;
|
||||
}
|
||||
irp = (IRP*) list_peek(serial->pending_irps);
|
||||
|
||||
while (irp)
|
||||
{
|
||||
fds = NULL;
|
||||
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_WRITE:
|
||||
fds = &serial->write_fds;
|
||||
break;
|
||||
|
||||
case IRP_MJ_READ:
|
||||
fds = &serial->read_fds;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds && (tty->fd >= 0))
|
||||
{
|
||||
FD_SET(tty->fd, fds);
|
||||
serial->nfds = MAX(serial->nfds, tty->fd);
|
||||
}
|
||||
|
||||
irp = (IRP*) list_next(serial->pending_irps, irp);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL serial_check_fds(SERIAL_DEVICE* serial)
|
||||
{
|
||||
if (list_size(serial->pending_irps) == 0)
|
||||
return 1;
|
||||
|
||||
FD_ZERO(&serial->read_fds);
|
||||
FD_ZERO(&serial->write_fds);
|
||||
|
||||
serial->tv.tv_sec = 0;
|
||||
serial->tv.tv_usec = 0;
|
||||
serial->select_timeout = 0;
|
||||
|
||||
serial_set_fds(serial);
|
||||
DEBUG_SVC("waiting %lu %lu", serial->tv.tv_sec, serial->tv.tv_usec);
|
||||
|
||||
switch (select(serial->nfds + 1, &serial->read_fds, &serial->write_fds, NULL, &serial->tv))
|
||||
{
|
||||
case -1:
|
||||
DEBUG_SVC("select has returned -1 with error: %s", strerror(errno));
|
||||
return 0;
|
||||
|
||||
case 0:
|
||||
if (serial->select_timeout)
|
||||
{
|
||||
__serial_check_fds(serial);
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_NONE, STATUS_TIMEOUT);
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_READ, STATUS_TIMEOUT);
|
||||
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_WRITE, STATUS_TIMEOUT);
|
||||
}
|
||||
DEBUG_SVC("select has timed out");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
__serial_check_fds(serial);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#define DeviceServiceEntry serial_DeviceServiceEntry
|
||||
#endif
|
||||
@ -789,10 +367,18 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
name = device->Name;
|
||||
path = device->Path;
|
||||
|
||||
if (!name || (name[0] == '*'))
|
||||
{
|
||||
/* TODO: implement auto detection of parallel ports */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((name && name[0]) && (path && path[0]))
|
||||
{
|
||||
serial = (SERIAL_DEVICE*) malloc(sizeof(SERIAL_DEVICE));
|
||||
ZeroMemory(serial, sizeof(SERIAL_DEVICE));
|
||||
serial = (SERIAL_DEVICE*) calloc(1, sizeof(SERIAL_DEVICE));
|
||||
|
||||
if (!serial)
|
||||
return -1;
|
||||
|
||||
serial->device.type = RDPDR_DTYP_SERIAL;
|
||||
serial->device.name = name;
|
||||
@ -806,17 +392,16 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
Stream_Write_UINT8(serial->device.data, name[i] < 0 ? '_' : name[i]);
|
||||
|
||||
serial->path = path;
|
||||
serial->queue = Queue_New(TRUE, -1, -1);
|
||||
serial->pending_irps = list_new();
|
||||
serial->IrpQueue = MessageQueue_New(NULL);
|
||||
|
||||
serial->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
serial->newEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
WLog_Init();
|
||||
serial->log = WLog_Get("com.freerdp.channel.serial.client");
|
||||
WLog_Print(serial->log, WLOG_DEBUG, "initializing");
|
||||
|
||||
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) serial);
|
||||
|
||||
serial->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) serial_thread_func, (void*) serial, 0, NULL);
|
||||
serial->mthread = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -73,24 +73,47 @@
|
||||
#define TIOCOUTQ FIONWRITE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Refer to ReactOS's ntddser.h (public domain) for constant definitions
|
||||
*/
|
||||
|
||||
static UINT32 tty_write_data(SERIAL_TTY* tty, BYTE* data, int len);
|
||||
static void tty_set_termios(SERIAL_TTY* tty);
|
||||
static BOOL tty_get_termios(SERIAL_TTY* tty);
|
||||
static int tty_get_error_status();
|
||||
|
||||
UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input, wStream* output, UINT32* abort_io)
|
||||
UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input, wStream* output, UINT32* abortIo)
|
||||
{
|
||||
int purge_mask;
|
||||
UINT32 result;
|
||||
UINT32 modemstate;
|
||||
BYTE immediate;
|
||||
UINT32 ret = STATUS_SUCCESS;
|
||||
UINT32 length = 0;
|
||||
UINT32 pos;
|
||||
int purge_mask;
|
||||
UINT32 modemstate;
|
||||
UINT32 begPos, endPos;
|
||||
UINT32 OutputBufferLength;
|
||||
UINT32 status = STATUS_SUCCESS;
|
||||
UINT32 IoCtlDeviceType;
|
||||
UINT32 IoCtlFunction;
|
||||
UINT32 IoCtlMethod;
|
||||
UINT32 IoCtlAccess;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
IoCtlMethod = (IoControlCode & 0x3);
|
||||
IoCtlFunction = ((IoControlCode >> 2) & 0xFFF);
|
||||
IoCtlAccess = ((IoControlCode >> 14) & 0x3);
|
||||
IoCtlDeviceType = ((IoControlCode >> 16) & 0xFFFF);
|
||||
|
||||
Stream_Seek(output, sizeof(UINT32));
|
||||
/**
|
||||
* FILE_DEVICE_SERIAL_PORT 0x0000001B
|
||||
* FILE_DEVICE_UNKNOWN 0x00000022
|
||||
*/
|
||||
|
||||
if (IoCtlDeviceType == 0x00000022)
|
||||
{
|
||||
IoControlCode &= 0xFFFF;
|
||||
IoControlCode |= (0x0000001B << 16);
|
||||
}
|
||||
|
||||
Stream_Seek_UINT32(output); /* OutputBufferLength (4 bytes) */
|
||||
begPos = (UINT32) Stream_GetPosition(output);
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
@ -101,7 +124,7 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
break;
|
||||
|
||||
case IOCTL_SERIAL_GET_BAUD_RATE:
|
||||
length = 4;
|
||||
OutputBufferLength = 4;
|
||||
Stream_Write_UINT32(output, tty->baud_rate);
|
||||
DEBUG_SVC("SERIAL_GET_BAUD_RATE %d", tty->baud_rate);
|
||||
break;
|
||||
@ -123,7 +146,7 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
|
||||
case IOCTL_SERIAL_GET_LINE_CONTROL:
|
||||
DEBUG_SVC("SERIAL_GET_LINE_CONTROL");
|
||||
length = 3;
|
||||
OutputBufferLength = 3;
|
||||
Stream_Write_UINT8(output, tty->stop_bits);
|
||||
Stream_Write_UINT8(output, tty->parity);
|
||||
Stream_Write_UINT8(output, tty->word_length);
|
||||
@ -137,13 +160,13 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
|
||||
case IOCTL_SERIAL_CONFIG_SIZE:
|
||||
DEBUG_SVC("SERIAL_CONFIG_SIZE");
|
||||
length = 4;
|
||||
OutputBufferLength = 4;
|
||||
Stream_Write_UINT32(output, 0);
|
||||
break;
|
||||
|
||||
case IOCTL_SERIAL_GET_CHARS:
|
||||
DEBUG_SVC("SERIAL_GET_CHARS");
|
||||
length = 6;
|
||||
OutputBufferLength = 6;
|
||||
Stream_Write(output, tty->chars, 6);
|
||||
break;
|
||||
|
||||
@ -154,7 +177,7 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
break;
|
||||
|
||||
case IOCTL_SERIAL_GET_HANDFLOW:
|
||||
length = 16;
|
||||
OutputBufferLength = 16;
|
||||
tty_get_termios(tty);
|
||||
Stream_Write_UINT32(output, tty->control);
|
||||
Stream_Write_UINT32(output, tty->xonoff);
|
||||
@ -200,7 +223,7 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
tty->read_interval_timeout,
|
||||
tty->read_total_timeout_multiplier,
|
||||
tty->read_total_timeout_constant);
|
||||
length = 20;
|
||||
OutputBufferLength = 20;
|
||||
Stream_Write_UINT32(output, tty->read_interval_timeout);
|
||||
Stream_Write_UINT32(output, tty->read_total_timeout_multiplier);
|
||||
Stream_Write_UINT32(output, tty->read_total_timeout_constant);
|
||||
@ -210,7 +233,7 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
|
||||
case IOCTL_SERIAL_GET_WAIT_MASK:
|
||||
DEBUG_SVC("SERIAL_GET_WAIT_MASK %X", tty->wait_mask);
|
||||
length = 4;
|
||||
OutputBufferLength = 4;
|
||||
Stream_Write_UINT32(output, tty->wait_mask);
|
||||
break;
|
||||
|
||||
@ -269,12 +292,12 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
modemstate |= SERIAL_MS_RTS;
|
||||
#endif
|
||||
DEBUG_SVC("SERIAL_GET_MODEMSTATUS %X", modemstate);
|
||||
length = 4;
|
||||
OutputBufferLength = 4;
|
||||
Stream_Write_UINT32(output, modemstate);
|
||||
break;
|
||||
|
||||
case IOCTL_SERIAL_GET_COMMSTATUS:
|
||||
length = 18;
|
||||
OutputBufferLength = 18;
|
||||
Stream_Write_UINT32(output, 0); /* Errors */
|
||||
Stream_Write_UINT32(output, 0); /* Hold reasons */
|
||||
|
||||
@ -316,21 +339,21 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
#endif
|
||||
|
||||
if (purge_mask & SERIAL_PURGE_TXABORT)
|
||||
*abort_io |= SERIAL_ABORT_IO_WRITE;
|
||||
*abortIo |= SERIAL_ABORT_IO_WRITE;
|
||||
if (purge_mask & SERIAL_PURGE_RXABORT)
|
||||
*abort_io |= SERIAL_ABORT_IO_READ;
|
||||
*abortIo |= SERIAL_ABORT_IO_READ;
|
||||
break;
|
||||
case IOCTL_SERIAL_WAIT_ON_MASK:
|
||||
DEBUG_SVC("SERIAL_WAIT_ON_MASK %X", tty->wait_mask);
|
||||
tty->event_pending = 1;
|
||||
length = 4;
|
||||
OutputBufferLength = 4;
|
||||
if (serial_tty_get_event(tty, &result))
|
||||
{
|
||||
DEBUG_SVC("WAIT end event = %X", result);
|
||||
Stream_Write_UINT32(output, result);
|
||||
break;
|
||||
}
|
||||
ret = STATUS_PENDING;
|
||||
status = STATUS_PENDING;
|
||||
break;
|
||||
|
||||
case IOCTL_SERIAL_SET_BREAK_ON:
|
||||
@ -356,27 +379,33 @@ UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input,
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_SVC("NOT FOUND IoControlCode SERIAL IOCTL %d", IoControlCode);
|
||||
DEBUG_SVC("NOT FOUND IoControlCode SERIAL IOCTL 0x%08X", IoControlCode);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Write OutputBufferLength */
|
||||
pos = Stream_GetPosition(output);
|
||||
Stream_SetPosition(output, 16);
|
||||
Stream_Write_UINT32(output, length);
|
||||
Stream_SetPosition(output, pos);
|
||||
endPos = (UINT32) Stream_GetPosition(output);
|
||||
OutputBufferLength = endPos - begPos;
|
||||
|
||||
return ret;
|
||||
if (OutputBufferLength < 1)
|
||||
{
|
||||
Stream_Write_UINT8(output, 0); /* Padding (1 byte) */
|
||||
endPos = (UINT32) Stream_GetPosition(output);
|
||||
OutputBufferLength = endPos - begPos;
|
||||
}
|
||||
|
||||
Stream_SealLength(output);
|
||||
|
||||
Stream_SetPosition(output, 16);
|
||||
Stream_Write_UINT32(output, OutputBufferLength); /* OutputBufferLength (4 bytes) */
|
||||
Stream_SetPosition(output, endPos);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length)
|
||||
{
|
||||
ssize_t status;
|
||||
long timeout = 90;
|
||||
struct termios* ptermios;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
ptermios = tty->ptermios;
|
||||
|
||||
/* Set timeouts kind of like the windows serial timeout parameters. Multiply timeout
|
||||
with requested read size */
|
||||
@ -391,21 +420,35 @@ BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length)
|
||||
timeout = (tty->read_interval_timeout * (*Length) + 99) / 100;
|
||||
}
|
||||
|
||||
/* If a timeout is set, do a blocking read, which times out after some time.
|
||||
It will make FreeRDP less responsive, but it will improve serial performance,
|
||||
by not reading one character at a time. */
|
||||
if (timeout == 0)
|
||||
{
|
||||
ptermios->c_cc[VTIME] = 0;
|
||||
ptermios->c_cc[VMIN] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptermios->c_cc[VTIME] = timeout;
|
||||
ptermios->c_cc[VMIN] = 1;
|
||||
}
|
||||
if (tty->timeout != timeout)
|
||||
{
|
||||
struct termios* ptermios;
|
||||
|
||||
tcsetattr(tty->fd, TCSANOW, ptermios);
|
||||
ptermios = (struct termios*) calloc(1, sizeof(struct termios));
|
||||
|
||||
if (tcgetattr(tty->fd, ptermios) < 0)
|
||||
return FALSE;
|
||||
|
||||
/**
|
||||
* If a timeout is set, do a blocking read, which times out after some time.
|
||||
* It will make FreeRDP less responsive, but it will improve serial performance,
|
||||
* by not reading one character at a time.
|
||||
*/
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
ptermios->c_cc[VTIME] = 0;
|
||||
ptermios->c_cc[VMIN] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptermios->c_cc[VTIME] = timeout;
|
||||
ptermios->c_cc[VMIN] = 1;
|
||||
}
|
||||
|
||||
tcsetattr(tty->fd, TCSANOW, ptermios);
|
||||
tty->timeout = timeout;
|
||||
}
|
||||
|
||||
ZeroMemory(buffer, *Length);
|
||||
|
||||
@ -424,20 +467,21 @@ BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length)
|
||||
int serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length)
|
||||
{
|
||||
ssize_t status;
|
||||
ssize_t status = 0;
|
||||
UINT32 event_txempty = Length;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
|
||||
while (Length > 0)
|
||||
{
|
||||
status = write(tty->fd, buffer, Length);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
return FALSE;
|
||||
if (errno == EAGAIN)
|
||||
status = 0;
|
||||
else
|
||||
return status;
|
||||
}
|
||||
|
||||
Length -= status;
|
||||
@ -446,7 +490,7 @@ BOOL serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length)
|
||||
|
||||
tty->event_txempty = event_txempty;
|
||||
|
||||
return TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -458,9 +502,7 @@ BOOL serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length)
|
||||
*/
|
||||
void serial_tty_free(SERIAL_TTY* tty)
|
||||
{
|
||||
DEBUG_SVC("in");
|
||||
|
||||
if(!tty)
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
if (tty->fd >= 0)
|
||||
@ -480,8 +522,10 @@ SERIAL_TTY* serial_tty_new(const char* path, UINT32 id)
|
||||
{
|
||||
SERIAL_TTY* tty;
|
||||
|
||||
tty = (SERIAL_TTY*) malloc(sizeof(SERIAL_TTY));
|
||||
ZeroMemory(tty, sizeof(SERIAL_TTY));
|
||||
tty = (SERIAL_TTY*) calloc(1, sizeof(SERIAL_TTY));
|
||||
|
||||
if (!tty)
|
||||
return NULL;
|
||||
|
||||
tty->id = id;
|
||||
tty->fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
@ -498,19 +542,17 @@ SERIAL_TTY* serial_tty_new(const char* path, UINT32 id)
|
||||
DEBUG_SVC("tty fd %d successfully opened", tty->fd);
|
||||
}
|
||||
|
||||
tty->ptermios = (struct termios*) malloc(sizeof(struct termios));
|
||||
ZeroMemory(tty->ptermios, sizeof(struct termios));
|
||||
tty->ptermios = (struct termios*) calloc(1, sizeof(struct termios));
|
||||
|
||||
if (tty->ptermios == NULL)
|
||||
if (!tty->ptermios)
|
||||
{
|
||||
serial_tty_free(tty);
|
||||
return NULL ;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tty->pold_termios = (struct termios*) malloc(sizeof(struct termios));
|
||||
ZeroMemory(tty->pold_termios, sizeof(struct termios));
|
||||
tty->pold_termios = (struct termios*) calloc(1, sizeof(struct termios));
|
||||
|
||||
if (tty->pold_termios == NULL)
|
||||
if (!tty->pold_termios)
|
||||
{
|
||||
serial_tty_free(tty);
|
||||
return NULL;
|
||||
@ -526,11 +568,14 @@ SERIAL_TTY* serial_tty_new(const char* path, UINT32 id)
|
||||
}
|
||||
|
||||
tty->ptermios->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
tty->ptermios->c_iflag = IGNPAR | ICRNL;
|
||||
tty->ptermios->c_oflag &= ~OPOST;
|
||||
tty->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tty->ptermios->c_cflag &= ~(CSIZE | PARENB);
|
||||
tty->ptermios->c_cflag |= CLOCAL | CREAD | CS8;
|
||||
tty->ptermios->c_cflag |= CS8;
|
||||
|
||||
tty->ptermios->c_iflag = IGNPAR;
|
||||
tty->ptermios->c_cflag |= CLOCAL | CREAD;
|
||||
|
||||
tcsetattr(tty->fd, TCSANOW, tty->ptermios);
|
||||
|
||||
tty->event_txempty = 0;
|
||||
@ -558,8 +603,6 @@ BOOL serial_tty_get_event(SERIAL_TTY* tty, UINT32* result)
|
||||
int bytes;
|
||||
BOOL status = FALSE;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
|
||||
*result = 0;
|
||||
|
||||
#ifdef TIOCINQ
|
||||
@ -587,7 +630,6 @@ BOOL serial_tty_get_event(SERIAL_TTY* tty, UINT32* result)
|
||||
*result |= SERIAL_EV_RLSD;
|
||||
status = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((bytes > 1) && (tty->wait_mask & SERIAL_EV_RXFLAG))
|
||||
@ -603,7 +645,6 @@ BOOL serial_tty_get_event(SERIAL_TTY* tty, UINT32* result)
|
||||
*result |= SERIAL_EV_RXCHAR;
|
||||
status = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -802,16 +843,18 @@ static BOOL tty_get_termios(SERIAL_TTY* tty)
|
||||
tty->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR];
|
||||
tty->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL];
|
||||
|
||||
tty->timeout = ptermios->c_cc[VTIME];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tty_set_termios(SERIAL_TTY* tty)
|
||||
{
|
||||
speed_t speed;
|
||||
struct termios *ptermios;
|
||||
struct termios* ptermios;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
ptermios = tty->ptermios;
|
||||
|
||||
switch (tty->baud_rate)
|
||||
{
|
||||
#ifdef B75
|
||||
@ -1003,8 +1046,6 @@ static UINT32 tty_write_data(SERIAL_TTY* tty, BYTE* data, int len)
|
||||
{
|
||||
ssize_t status;
|
||||
|
||||
DEBUG_SVC("in");
|
||||
|
||||
status = write(tty->fd, data, len);
|
||||
|
||||
if (status < 0)
|
||||
@ -1017,8 +1058,6 @@ static UINT32 tty_write_data(SERIAL_TTY* tty, BYTE* data, int len)
|
||||
|
||||
static int tty_get_error_status()
|
||||
{
|
||||
DEBUG_SVC("in errno %d", errno);
|
||||
|
||||
switch (errno)
|
||||
{
|
||||
case EACCES:
|
||||
|
@ -65,13 +65,14 @@ struct _SERIAL_TTY
|
||||
int event_dsr;
|
||||
int event_rlsd;
|
||||
int event_pending;
|
||||
long timeout;
|
||||
};
|
||||
|
||||
SERIAL_TTY* serial_tty_new(const char* path, UINT32 id);
|
||||
void serial_tty_free(SERIAL_TTY* tty);
|
||||
|
||||
BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length);
|
||||
BOOL serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length);
|
||||
int serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length);
|
||||
UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input, wStream* output, UINT32* abort_io);
|
||||
|
||||
BOOL serial_tty_get_event(SERIAL_TTY* tty, UINT32* result);
|
||||
|
@ -67,840 +67,3 @@ void freerdp_channels_dummy()
|
||||
/**
|
||||
* end of ugly symbols import workaround
|
||||
*/
|
||||
|
||||
#define CREATE_REQUEST_PDU 0x01
|
||||
#define DATA_FIRST_PDU 0x02
|
||||
#define DATA_PDU 0x03
|
||||
#define CLOSE_REQUEST_PDU 0x04
|
||||
#define CAPABILITY_REQUEST_PDU 0x05
|
||||
|
||||
typedef struct wts_data_item
|
||||
{
|
||||
UINT16 channel_id;
|
||||
BYTE* buffer;
|
||||
UINT32 length;
|
||||
} wts_data_item;
|
||||
|
||||
static void wts_data_item_free(wts_data_item* item)
|
||||
{
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
}
|
||||
|
||||
void* freerdp_channels_server_find_static_entry(const char* name, const char* entry)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static rdpPeerChannel* wts_get_dvc_channel_by_id(WTSVirtualChannelManager* vcm, UINT32 ChannelId)
|
||||
{
|
||||
LIST_ITEM* item;
|
||||
rdpPeerChannel* channel = NULL;
|
||||
|
||||
for (item = vcm->dvc_channel_list->head; item; item = item->next)
|
||||
{
|
||||
channel = (rdpPeerChannel*) item->data;
|
||||
|
||||
if (channel->channel_id == ChannelId)
|
||||
break;
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
static void wts_queue_receive_data(rdpPeerChannel* channel, const BYTE* buffer, UINT32 length)
|
||||
{
|
||||
wts_data_item* item;
|
||||
|
||||
item = (wts_data_item*) malloc(sizeof(wts_data_item));
|
||||
ZeroMemory(item, sizeof(wts_data_item));
|
||||
|
||||
item->length = length;
|
||||
item->buffer = malloc(length);
|
||||
CopyMemory(item->buffer, buffer, length);
|
||||
|
||||
WaitForSingleObject(channel->mutex, INFINITE);
|
||||
list_enqueue(channel->receive_queue, item);
|
||||
ReleaseMutex(channel->mutex);
|
||||
|
||||
SetEvent(channel->receive_event);
|
||||
}
|
||||
|
||||
static void wts_queue_send_item(rdpPeerChannel* channel, wts_data_item* item)
|
||||
{
|
||||
WTSVirtualChannelManager* vcm;
|
||||
|
||||
vcm = channel->vcm;
|
||||
|
||||
item->channel_id = channel->channel_id;
|
||||
|
||||
WaitForSingleObject(vcm->mutex, INFINITE);
|
||||
list_enqueue(vcm->send_queue, item);
|
||||
ReleaseMutex(vcm->mutex);
|
||||
|
||||
SetEvent(vcm->send_event);
|
||||
}
|
||||
|
||||
static int wts_read_variable_uint(wStream* s, int cbLen, UINT32* val)
|
||||
{
|
||||
switch (cbLen)
|
||||
{
|
||||
case 0:
|
||||
if (Stream_GetRemainingLength(s) < 1)
|
||||
return 0;
|
||||
Stream_Read_UINT8(s, *val);
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return 0;
|
||||
Stream_Read_UINT16(s, *val);
|
||||
return 2;
|
||||
|
||||
default:
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return 0;
|
||||
Stream_Read_UINT32(s, *val);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_capabilities_response(rdpPeerChannel* channel, UINT32 length)
|
||||
{
|
||||
UINT16 Version;
|
||||
|
||||
if (length < 3)
|
||||
return;
|
||||
|
||||
Stream_Seek_UINT8(channel->receive_data); /* Pad (1 byte) */
|
||||
Stream_Read_UINT16(channel->receive_data, Version);
|
||||
|
||||
DEBUG_DVC("Version: %d", Version);
|
||||
|
||||
channel->vcm->drdynvc_state = DRDYNVC_STATE_READY;
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s, UINT32 length)
|
||||
{
|
||||
UINT32 CreationStatus;
|
||||
|
||||
if (length < 4)
|
||||
return;
|
||||
|
||||
Stream_Read_UINT32(s, CreationStatus);
|
||||
|
||||
if ((INT32) CreationStatus < 0)
|
||||
{
|
||||
DEBUG_DVC("ChannelId %d creation failed (%d)", channel->channel_id, (INT32) CreationStatus);
|
||||
channel->dvc_open_state = DVC_OPEN_STATE_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_DVC("ChannelId %d creation succeeded", channel->channel_id);
|
||||
channel->dvc_open_state = DVC_OPEN_STATE_SUCCEEDED;
|
||||
}
|
||||
|
||||
SetEvent(channel->receive_event);
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_data_first(rdpPeerChannel* channel, wStream* s, int cbLen, UINT32 length)
|
||||
{
|
||||
int value;
|
||||
|
||||
value = wts_read_variable_uint(s, cbLen, &channel->dvc_total_length);
|
||||
|
||||
if (value == 0)
|
||||
return;
|
||||
|
||||
length -= value;
|
||||
|
||||
if (length > channel->dvc_total_length)
|
||||
return;
|
||||
|
||||
Stream_SetPosition(channel->receive_data, 0);
|
||||
Stream_EnsureRemainingCapacity(channel->receive_data, (int) channel->dvc_total_length);
|
||||
Stream_Write(channel->receive_data, Stream_Pointer(s), length);
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_data(rdpPeerChannel* channel, wStream* s, UINT32 length)
|
||||
{
|
||||
if (channel->dvc_total_length > 0)
|
||||
{
|
||||
if (Stream_GetPosition(channel->receive_data) + length > channel->dvc_total_length)
|
||||
{
|
||||
channel->dvc_total_length = 0;
|
||||
fprintf(stderr, "wts_read_drdynvc_data: incorrect fragment data, discarded.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Stream_Write(channel->receive_data, Stream_Pointer(s), length);
|
||||
|
||||
if (Stream_GetPosition(channel->receive_data) >= (int) channel->dvc_total_length)
|
||||
{
|
||||
wts_queue_receive_data(channel, Stream_Buffer(channel->receive_data), channel->dvc_total_length);
|
||||
channel->dvc_total_length = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wts_queue_receive_data(channel, Stream_Pointer(s), length);
|
||||
}
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_close_response(rdpPeerChannel* channel)
|
||||
{
|
||||
DEBUG_DVC("ChannelId %d close response", channel->channel_id);
|
||||
channel->dvc_open_state = DVC_OPEN_STATE_CLOSED;
|
||||
}
|
||||
|
||||
static void wts_read_drdynvc_pdu(rdpPeerChannel* channel)
|
||||
{
|
||||
UINT32 length;
|
||||
int value;
|
||||
int Cmd;
|
||||
int Sp;
|
||||
int cbChId;
|
||||
UINT32 ChannelId;
|
||||
rdpPeerChannel* dvc;
|
||||
|
||||
length = Stream_GetPosition(channel->receive_data);
|
||||
|
||||
if (length < 1)
|
||||
return;
|
||||
|
||||
Stream_SetPosition(channel->receive_data, 0);
|
||||
Stream_Read_UINT8(channel->receive_data, value);
|
||||
|
||||
length--;
|
||||
Cmd = (value & 0xf0) >> 4;
|
||||
Sp = (value & 0x0c) >> 2;
|
||||
cbChId = (value & 0x03) >> 0;
|
||||
|
||||
if (Cmd == CAPABILITY_REQUEST_PDU)
|
||||
{
|
||||
wts_read_drdynvc_capabilities_response(channel, length);
|
||||
}
|
||||
else if (channel->vcm->drdynvc_state == DRDYNVC_STATE_READY)
|
||||
{
|
||||
value = wts_read_variable_uint(channel->receive_data, cbChId, &ChannelId);
|
||||
|
||||
if (value == 0)
|
||||
return;
|
||||
|
||||
length -= value;
|
||||
|
||||
DEBUG_DVC("Cmd %d ChannelId %d length %d", Cmd, ChannelId, length);
|
||||
dvc = wts_get_dvc_channel_by_id(channel->vcm, ChannelId);
|
||||
|
||||
if (dvc)
|
||||
{
|
||||
switch (Cmd)
|
||||
{
|
||||
case CREATE_REQUEST_PDU:
|
||||
wts_read_drdynvc_create_response(dvc, channel->receive_data, length);
|
||||
break;
|
||||
|
||||
case DATA_FIRST_PDU:
|
||||
wts_read_drdynvc_data_first(dvc, channel->receive_data, Sp, length);
|
||||
break;
|
||||
|
||||
case DATA_PDU:
|
||||
wts_read_drdynvc_data(dvc, channel->receive_data, length);
|
||||
break;
|
||||
|
||||
case CLOSE_REQUEST_PDU:
|
||||
wts_read_drdynvc_close_response(dvc);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "wts_read_drdynvc_pdu: Cmd %d not recognized.\n", Cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_DVC("ChannelId %d not exists.", ChannelId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "wts_read_drdynvc_pdu: received Cmd %d but channel is not ready.\n", Cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static int wts_write_variable_uint(wStream* stream, UINT32 val)
|
||||
{
|
||||
int cb;
|
||||
|
||||
if (val <= 0xFF)
|
||||
{
|
||||
cb = 0;
|
||||
Stream_Write_UINT8(stream, val);
|
||||
}
|
||||
else if (val <= 0xFFFF)
|
||||
{
|
||||
cb = 1;
|
||||
Stream_Write_UINT16(stream, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = 3;
|
||||
Stream_Write_UINT32(stream, val);
|
||||
}
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void wts_write_drdynvc_header(wStream *s, BYTE Cmd, UINT32 ChannelId)
|
||||
{
|
||||
BYTE* bm;
|
||||
int cbChId;
|
||||
|
||||
Stream_GetPointer(s, bm);
|
||||
Stream_Seek_UINT8(s);
|
||||
cbChId = wts_write_variable_uint(s, ChannelId);
|
||||
*bm = ((Cmd & 0x0F) << 4) | cbChId;
|
||||
}
|
||||
|
||||
static void wts_write_drdynvc_create_request(wStream *s, UINT32 ChannelId, const char *ChannelName)
|
||||
{
|
||||
UINT32 len;
|
||||
|
||||
wts_write_drdynvc_header(s, CREATE_REQUEST_PDU, ChannelId);
|
||||
len = strlen(ChannelName) + 1;
|
||||
Stream_EnsureRemainingCapacity(s, (int) len);
|
||||
Stream_Write(s, ChannelName, len);
|
||||
}
|
||||
|
||||
static void WTSProcessChannelData(rdpPeerChannel* channel, int channelId, BYTE* data, int size, int flags, int total_size)
|
||||
{
|
||||
if (flags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
Stream_SetPosition(channel->receive_data, 0);
|
||||
}
|
||||
|
||||
Stream_EnsureRemainingCapacity(channel->receive_data, size);
|
||||
Stream_Write(channel->receive_data, data, size);
|
||||
|
||||
if (flags & CHANNEL_FLAG_LAST)
|
||||
{
|
||||
if (Stream_GetPosition(channel->receive_data) != total_size)
|
||||
{
|
||||
fprintf(stderr, "WTSProcessChannelData: read error\n");
|
||||
}
|
||||
if (channel == channel->vcm->drdynvc_channel)
|
||||
{
|
||||
wts_read_drdynvc_pdu(channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
wts_queue_receive_data(channel, Stream_Buffer(channel->receive_data), Stream_GetPosition(channel->receive_data));
|
||||
}
|
||||
Stream_SetPosition(channel->receive_data, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int WTSReceiveChannelData(freerdp_peer* client, int channelId, BYTE* data, int size, int flags, int total_size)
|
||||
{
|
||||
int i;
|
||||
BOOL result = FALSE;
|
||||
rdpPeerChannel* channel;
|
||||
|
||||
for (i = 0; i < client->settings->ChannelCount; i++)
|
||||
{
|
||||
if (client->settings->ChannelDefArray[i].ChannelId == channelId)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < client->settings->ChannelCount)
|
||||
{
|
||||
channel = (rdpPeerChannel*) client->settings->ChannelDefArray[i].handle;
|
||||
|
||||
if (channel != NULL)
|
||||
{
|
||||
WTSProcessChannelData(channel, channelId, data, size, flags, total_size);
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WTSVirtualChannelManager* WTSCreateVirtualChannelManager(freerdp_peer* client)
|
||||
{
|
||||
WTSVirtualChannelManager* vcm;
|
||||
|
||||
vcm = (WTSVirtualChannelManager*) malloc(sizeof(WTSVirtualChannelManager));
|
||||
|
||||
if (vcm)
|
||||
{
|
||||
ZeroMemory(vcm, sizeof(WTSVirtualChannelManager));
|
||||
|
||||
vcm->client = client;
|
||||
vcm->send_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
vcm->send_queue = list_new();
|
||||
vcm->mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
vcm->dvc_channel_id_seq = 1;
|
||||
vcm->dvc_channel_list = list_new();
|
||||
|
||||
client->ReceiveChannelData = WTSReceiveChannelData;
|
||||
}
|
||||
|
||||
return vcm;
|
||||
}
|
||||
|
||||
void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm)
|
||||
{
|
||||
wts_data_item* item;
|
||||
rdpPeerChannel* channel;
|
||||
|
||||
if (vcm != NULL)
|
||||
{
|
||||
while ((channel = (rdpPeerChannel*) list_dequeue(vcm->dvc_channel_list)) != NULL)
|
||||
{
|
||||
WTSVirtualChannelClose(channel);
|
||||
}
|
||||
|
||||
list_free(vcm->dvc_channel_list);
|
||||
|
||||
if (vcm->drdynvc_channel != NULL)
|
||||
{
|
||||
WTSVirtualChannelClose(vcm->drdynvc_channel);
|
||||
vcm->drdynvc_channel = NULL;
|
||||
}
|
||||
|
||||
CloseHandle(vcm->send_event);
|
||||
|
||||
while ((item = (wts_data_item*) list_dequeue(vcm->send_queue)) != NULL)
|
||||
{
|
||||
wts_data_item_free(item);
|
||||
}
|
||||
|
||||
list_free(vcm->send_queue);
|
||||
CloseHandle(vcm->mutex);
|
||||
free(vcm);
|
||||
}
|
||||
}
|
||||
|
||||
void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, void** fds, int* fds_count)
|
||||
{
|
||||
void* fd;
|
||||
|
||||
fd = GetEventWaitObject(vcm->send_event);
|
||||
|
||||
if (fd)
|
||||
{
|
||||
fds[*fds_count] = fd;
|
||||
(*fds_count)++;
|
||||
}
|
||||
|
||||
if (vcm->drdynvc_channel)
|
||||
{
|
||||
fd = GetEventWaitObject(vcm->drdynvc_channel->receive_event);
|
||||
|
||||
if (fd)
|
||||
{
|
||||
fds[*fds_count] = fd;
|
||||
(*fds_count)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm)
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
wts_data_item* item;
|
||||
rdpPeerChannel* channel;
|
||||
UINT32 dynvc_caps;
|
||||
|
||||
if (vcm->drdynvc_state == DRDYNVC_STATE_NONE && vcm->client->activated)
|
||||
{
|
||||
/* Initialize drdynvc channel once and only once. */
|
||||
vcm->drdynvc_state = DRDYNVC_STATE_INITIALIZED;
|
||||
|
||||
channel = WTSVirtualChannelManagerOpenEx(vcm, "drdynvc", 0);
|
||||
|
||||
if (channel)
|
||||
{
|
||||
vcm->drdynvc_channel = channel;
|
||||
dynvc_caps = 0x00010050; /* DYNVC_CAPS_VERSION1 (4 bytes) */
|
||||
WTSVirtualChannelWrite(channel, (PCHAR) &dynvc_caps, sizeof(dynvc_caps), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ResetEvent(vcm->send_event);
|
||||
|
||||
WaitForSingleObject(vcm->mutex, INFINITE);
|
||||
|
||||
while ((item = (wts_data_item*) list_dequeue(vcm->send_queue)) != NULL)
|
||||
{
|
||||
if (vcm->client->SendChannelData(vcm->client, item->channel_id, item->buffer, item->length) == FALSE)
|
||||
{
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
wts_data_item_free(item);
|
||||
|
||||
if (result == FALSE)
|
||||
break;
|
||||
}
|
||||
|
||||
ReleaseMutex(vcm->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HANDLE WTSVirtualChannelManagerGetEventHandle(WTSVirtualChannelManager* vcm)
|
||||
{
|
||||
return vcm->send_event;
|
||||
}
|
||||
|
||||
HANDLE WTSVirtualChannelManagerOpenEx(WTSVirtualChannelManager* vcm, LPSTR pVirtualName, DWORD flags)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
wStream* s;
|
||||
rdpPeerChannel* channel;
|
||||
freerdp_peer* client = vcm->client;
|
||||
|
||||
if ((flags & WTS_CHANNEL_OPTION_DYNAMIC) != 0)
|
||||
{
|
||||
for (i = 0; i < client->settings->ChannelCount; i++)
|
||||
{
|
||||
if (client->settings->ChannelDefArray[i].joined &&
|
||||
strncmp(client->settings->ChannelDefArray[i].Name, "drdynvc", 7) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= client->settings->ChannelCount)
|
||||
{
|
||||
DEBUG_DVC("Dynamic virtual channel not registered.");
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vcm->drdynvc_channel == NULL || vcm->drdynvc_state != DRDYNVC_STATE_READY)
|
||||
{
|
||||
DEBUG_DVC("Dynamic virtual channel not ready.");
|
||||
SetLastError(ERROR_NOT_READY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
channel = (rdpPeerChannel*) malloc(sizeof(rdpPeerChannel));
|
||||
ZeroMemory(channel, sizeof(rdpPeerChannel));
|
||||
|
||||
channel->vcm = vcm;
|
||||
channel->client = client;
|
||||
channel->channel_type = RDP_PEER_CHANNEL_TYPE_DVC;
|
||||
channel->receive_data = Stream_New(NULL, client->settings->VirtualChannelChunkSize);
|
||||
channel->receive_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
channel->receive_queue = list_new();
|
||||
channel->mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
|
||||
WaitForSingleObject(vcm->mutex, INFINITE);
|
||||
channel->channel_id = vcm->dvc_channel_id_seq++;
|
||||
list_enqueue(vcm->dvc_channel_list, channel);
|
||||
ReleaseMutex(vcm->mutex);
|
||||
|
||||
s = Stream_New(NULL, 64);
|
||||
wts_write_drdynvc_create_request(s, channel->channel_id, pVirtualName);
|
||||
WTSVirtualChannelWrite(vcm->drdynvc_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), NULL);
|
||||
Stream_Free(s, TRUE);
|
||||
|
||||
DEBUG_DVC("ChannelId %d.%s (total %d)", channel->channel_id, pVirtualName, list_size(vcm->dvc_channel_list));
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlen(pVirtualName);
|
||||
|
||||
if (len > 8)
|
||||
{
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < client->settings->ChannelCount; i++)
|
||||
{
|
||||
if (client->settings->ChannelDefArray[i].joined &&
|
||||
strncmp(client->settings->ChannelDefArray[i].Name, pVirtualName, len) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= client->settings->ChannelCount)
|
||||
{
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
channel = (rdpPeerChannel*) client->settings->ChannelDefArray[i].handle;
|
||||
|
||||
if (channel == NULL)
|
||||
{
|
||||
channel = (rdpPeerChannel*) malloc(sizeof(rdpPeerChannel));
|
||||
ZeroMemory(channel, sizeof(rdpPeerChannel));
|
||||
|
||||
channel->vcm = vcm;
|
||||
channel->client = client;
|
||||
channel->channel_id = client->settings->ChannelDefArray[i].ChannelId;
|
||||
channel->index = i;
|
||||
channel->channel_type = RDP_PEER_CHANNEL_TYPE_SVC;
|
||||
channel->receive_data = Stream_New(NULL, client->settings->VirtualChannelChunkSize);
|
||||
channel->receive_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
channel->receive_queue = list_new();
|
||||
channel->mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
|
||||
client->settings->ChannelDefArray[i].handle = channel;
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
BOOL WTSVirtualChannelQuery(HANDLE hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, PVOID* ppBuffer, DWORD* pBytesReturned)
|
||||
{
|
||||
void* pfd;
|
||||
BOOL bval;
|
||||
void* fds[10];
|
||||
int fds_count = 0;
|
||||
BOOL result = FALSE;
|
||||
rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle;
|
||||
ZeroMemory(fds, sizeof(fds));
|
||||
|
||||
switch (WtsVirtualClass)
|
||||
{
|
||||
case WTSVirtualFileHandle:
|
||||
|
||||
pfd = GetEventWaitObject(channel->receive_event);
|
||||
|
||||
if (pfd)
|
||||
{
|
||||
fds[fds_count] = pfd;
|
||||
(fds_count)++;
|
||||
}
|
||||
|
||||
*ppBuffer = malloc(sizeof(void*));
|
||||
CopyMemory(*ppBuffer, &fds[0], sizeof(void*));
|
||||
*pBytesReturned = sizeof(void*);
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case WTSVirtualEventHandle:
|
||||
*ppBuffer = malloc(sizeof(HANDLE));
|
||||
CopyMemory(*ppBuffer, &(channel->receive_event), sizeof(HANDLE));
|
||||
*pBytesReturned = sizeof(void*);
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case WTSVirtualChannelReady:
|
||||
if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC)
|
||||
{
|
||||
bval = TRUE;
|
||||
result = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (channel->dvc_open_state)
|
||||
{
|
||||
case DVC_OPEN_STATE_NONE:
|
||||
bval = FALSE;
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case DVC_OPEN_STATE_SUCCEEDED:
|
||||
bval = TRUE;
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
bval = FALSE;
|
||||
result = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*ppBuffer = malloc(sizeof(BOOL));
|
||||
CopyMemory(*ppBuffer, &bval, sizeof(BOOL));
|
||||
*pBytesReturned = sizeof(BOOL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VOID WTSFreeMemory(PVOID pMemory)
|
||||
{
|
||||
free(pMemory);
|
||||
}
|
||||
|
||||
BOOL WTSVirtualChannelRead(HANDLE hChannelHandle, ULONG TimeOut, PCHAR Buffer, ULONG BufferSize, PULONG pBytesRead)
|
||||
{
|
||||
wts_data_item* item;
|
||||
rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle;
|
||||
|
||||
item = (wts_data_item*) list_peek(channel->receive_queue);
|
||||
|
||||
if (!item)
|
||||
{
|
||||
ResetEvent(channel->receive_event);
|
||||
*pBytesRead = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*pBytesRead = item->length;
|
||||
|
||||
if (item->length > BufferSize)
|
||||
return FALSE;
|
||||
|
||||
/* remove the first element (same as what we just peek) */
|
||||
WaitForSingleObject(channel->mutex, INFINITE);
|
||||
list_dequeue(channel->receive_queue);
|
||||
|
||||
if (list_size(channel->receive_queue) == 0)
|
||||
ResetEvent(channel->receive_event);
|
||||
|
||||
ReleaseMutex(channel->mutex);
|
||||
|
||||
CopyMemory(Buffer, item->buffer, item->length);
|
||||
wts_data_item_free(item);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, ULONG Length, PULONG pBytesWritten)
|
||||
{
|
||||
rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle;
|
||||
wts_data_item* item;
|
||||
wStream* s;
|
||||
int cbLen;
|
||||
int cbChId;
|
||||
int first;
|
||||
UINT32 written;
|
||||
|
||||
if (!channel)
|
||||
return FALSE;
|
||||
|
||||
if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC)
|
||||
{
|
||||
item = (wts_data_item*) malloc(sizeof(wts_data_item));
|
||||
ZeroMemory(item, sizeof(wts_data_item));
|
||||
|
||||
item->buffer = malloc(Length);
|
||||
item->length = Length;
|
||||
CopyMemory(item->buffer, Buffer, Length);
|
||||
|
||||
wts_queue_send_item(channel, item);
|
||||
}
|
||||
else if (channel->vcm->drdynvc_channel == NULL || channel->vcm->drdynvc_state != DRDYNVC_STATE_READY)
|
||||
{
|
||||
DEBUG_DVC("drdynvc not ready");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = TRUE;
|
||||
|
||||
while (Length > 0)
|
||||
{
|
||||
item = (wts_data_item*) malloc(sizeof(wts_data_item));
|
||||
ZeroMemory(item, sizeof(wts_data_item));
|
||||
|
||||
s = Stream_New(NULL, channel->client->settings->VirtualChannelChunkSize);
|
||||
item->buffer = Stream_Buffer(s);
|
||||
|
||||
Stream_Seek_UINT8(s);
|
||||
cbChId = wts_write_variable_uint(s, channel->channel_id);
|
||||
|
||||
if (first && (Length > (UINT32) Stream_GetRemainingLength(s)))
|
||||
{
|
||||
cbLen = wts_write_variable_uint(s, Length);
|
||||
item->buffer[0] = (DATA_FIRST_PDU << 4) | (cbLen << 2) | cbChId;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->buffer[0] = (DATA_PDU << 4) | cbChId;
|
||||
}
|
||||
|
||||
first = FALSE;
|
||||
written = Stream_GetRemainingLength(s);
|
||||
|
||||
if (written > Length)
|
||||
written = Length;
|
||||
|
||||
Stream_Write(s, Buffer, written);
|
||||
item->length = Stream_GetPosition(s);
|
||||
Stream_Free(s, FALSE);
|
||||
|
||||
Length -= written;
|
||||
Buffer += written;
|
||||
|
||||
wts_queue_send_item(channel->vcm->drdynvc_channel, item);
|
||||
}
|
||||
}
|
||||
|
||||
if (pBytesWritten != NULL)
|
||||
*pBytesWritten = Length;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WTSVirtualChannelClose(HANDLE hChannelHandle)
|
||||
{
|
||||
wStream* s;
|
||||
wts_data_item* item;
|
||||
WTSVirtualChannelManager* vcm;
|
||||
rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle;
|
||||
|
||||
if (channel)
|
||||
{
|
||||
vcm = channel->vcm;
|
||||
|
||||
if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC)
|
||||
{
|
||||
if (channel->index < channel->client->settings->ChannelCount)
|
||||
channel->client->settings->ChannelDefArray[channel->index].handle = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForSingleObject(vcm->mutex, INFINITE);
|
||||
list_remove(vcm->dvc_channel_list, channel);
|
||||
ReleaseMutex(vcm->mutex);
|
||||
|
||||
if (channel->dvc_open_state == DVC_OPEN_STATE_SUCCEEDED)
|
||||
{
|
||||
s = Stream_New(NULL, 8);
|
||||
wts_write_drdynvc_header(s, CLOSE_REQUEST_PDU, channel->channel_id);
|
||||
WTSVirtualChannelWrite(vcm->drdynvc_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), NULL);
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (channel->receive_data)
|
||||
Stream_Free(channel->receive_data, TRUE);
|
||||
|
||||
if (channel->receive_event)
|
||||
CloseHandle(channel->receive_event);
|
||||
|
||||
if (channel->receive_queue)
|
||||
{
|
||||
while ((item = (wts_data_item*) list_dequeue(channel->receive_queue)) != NULL)
|
||||
{
|
||||
wts_data_item_free(item);
|
||||
}
|
||||
|
||||
list_free(channel->receive_queue);
|
||||
}
|
||||
|
||||
if (channel->mutex)
|
||||
CloseHandle(channel->mutex);
|
||||
|
||||
free(channel);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -21,71 +21,6 @@
|
||||
#ifndef __WTSVC_H
|
||||
#define __WTSVC_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/utils/list.h>
|
||||
#include <freerdp/utils/debug.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#ifdef WITH_DEBUG_DVC
|
||||
#define DEBUG_DVC(fmt, ...) DEBUG_CLASS(DVC, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_DVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
RDP_PEER_CHANNEL_TYPE_SVC = 0,
|
||||
RDP_PEER_CHANNEL_TYPE_DVC = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DRDYNVC_STATE_NONE = 0,
|
||||
DRDYNVC_STATE_INITIALIZED = 1,
|
||||
DRDYNVC_STATE_READY = 2
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DVC_OPEN_STATE_NONE = 0,
|
||||
DVC_OPEN_STATE_SUCCEEDED = 1,
|
||||
DVC_OPEN_STATE_FAILED = 2,
|
||||
DVC_OPEN_STATE_CLOSED = 3
|
||||
};
|
||||
|
||||
typedef struct rdp_peer_channel rdpPeerChannel;
|
||||
|
||||
struct rdp_peer_channel
|
||||
{
|
||||
WTSVirtualChannelManager* vcm;
|
||||
freerdp_peer* client;
|
||||
UINT32 channel_id;
|
||||
UINT16 channel_type;
|
||||
UINT16 index;
|
||||
|
||||
wStream* receive_data;
|
||||
HANDLE receive_event;
|
||||
LIST* receive_queue;
|
||||
HANDLE mutex;
|
||||
|
||||
BYTE dvc_open_state;
|
||||
UINT32 dvc_total_length;
|
||||
};
|
||||
|
||||
struct WTSVirtualChannelManager
|
||||
{
|
||||
freerdp_peer* client;
|
||||
HANDLE send_event;
|
||||
LIST* send_queue;
|
||||
HANDLE mutex;
|
||||
|
||||
rdpPeerChannel* drdynvc_channel;
|
||||
BYTE drdynvc_state;
|
||||
UINT32 dvc_channel_id_seq;
|
||||
LIST* dvc_channel_list;
|
||||
};
|
||||
|
||||
#endif /* __WTSVC_H */
|
||||
|
@ -15,4 +15,3 @@ define_channel_options(NAME "smartcard" TYPE "device"
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
|
||||
|
@ -40,6 +40,10 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${PCSC_LIBRARIES})
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS winscard)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -40,8 +40,6 @@ static void smartcard_free(DEVICE* device)
|
||||
{
|
||||
SMARTCARD_DEVICE* smartcard = (SMARTCARD_DEVICE*) device;
|
||||
|
||||
printf("smartcard_free\n");
|
||||
|
||||
MessageQueue_PostQuit(smartcard->IrpQueue, 0);
|
||||
WaitForSingleObject(smartcard->thread, INFINITE);
|
||||
|
||||
@ -119,8 +117,10 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
|
||||
path = device->Path;
|
||||
|
||||
/* TODO: check if server supports sc redirect (version 5.1) */
|
||||
smartcard = (SMARTCARD_DEVICE*) malloc(sizeof(SMARTCARD_DEVICE));
|
||||
ZeroMemory(smartcard, sizeof(SMARTCARD_DEVICE));
|
||||
smartcard = (SMARTCARD_DEVICE*) calloc(1, sizeof(SMARTCARD_DEVICE));
|
||||
|
||||
if (!smartcard)
|
||||
return -1;
|
||||
|
||||
smartcard->device.type = RDPDR_DTYP_SMARTCARD;
|
||||
smartcard->device.name = "SCARD";
|
||||
|
@ -21,8 +21,6 @@
|
||||
#ifndef FREERDP_CHANNEL_SMARTCARD_CLIENT_MAIN_H
|
||||
#define FREERDP_CHANNEL_SMARTCARD_CLIENT_MAIN_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <freerdp/utils/list.h>
|
||||
#include <freerdp/utils/debug.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
@ -23,17 +23,37 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winscard.h>
|
||||
#else
|
||||
#include <strings.h>
|
||||
#define BOOL PCSC_BOOL
|
||||
#include <PCSC/pcsclite.h>
|
||||
#include <PCSC/reader.h>
|
||||
#include <PCSC/winscard.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <PCSC/reader.h>
|
||||
#else
|
||||
/* On OS X reader.h isn't available so define it here */
|
||||
#endif
|
||||
#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag)))
|
||||
#define SCARD_CLASS_SYSTEM 0x7fff /**< System-specific definitions */
|
||||
#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003)
|
||||
#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0005)
|
||||
#ifdef UNICODE
|
||||
#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_W /**< Reader's display name. */
|
||||
#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_W /**< Reader's system name. */
|
||||
#else
|
||||
#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_A /**< Reader's display name. */
|
||||
#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_A /**< Reader's system name. */
|
||||
#define SCARD_CTL_CODE(code) (0x42000000 + (code))
|
||||
#endif
|
||||
#undef BOOL
|
||||
#endif
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
|
@ -43,12 +43,22 @@
|
||||
#define AVMEDIA_TYPE_AUDIO 1
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 54
|
||||
#define MAX_AUDIO_FRAME_SIZE AVCODEC_MAX_AUDIO_FRAME_SIZE
|
||||
#else
|
||||
#define MAX_AUDIO_FRAME_SIZE 192000
|
||||
#endif
|
||||
|
||||
typedef struct _TSMFFFmpegDecoder
|
||||
{
|
||||
ITSMFDecoder iface;
|
||||
|
||||
int media_type;
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
enum CodecID codec_id;
|
||||
#else
|
||||
enum AVCodecID codec_id;
|
||||
#endif
|
||||
AVCodecContext* codec_context;
|
||||
AVCodec* codec;
|
||||
AVFrame* frame;
|
||||
@ -98,6 +108,7 @@ static BOOL tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MED
|
||||
mdecoder->codec_context->channels = media_type->Channels;
|
||||
mdecoder->codec_context->block_align = media_type->BlockAlign;
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 55
|
||||
#ifdef AV_CPU_FLAG_SSE2
|
||||
mdecoder->codec_context->dsp_mask = AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2;
|
||||
#else
|
||||
@ -107,6 +118,13 @@ static BOOL tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MED
|
||||
mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMX2;
|
||||
#endif
|
||||
#endif
|
||||
#else /* LIBAVCODEC_VERSION_MAJOR < 55 */
|
||||
#ifdef AV_CPU_FLAG_SSE2
|
||||
av_set_cpu_flags_mask(AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2);
|
||||
#else
|
||||
av_set_cpu_flags_mask(FF_MM_SSE2 | FF_MM_MMX2);
|
||||
#endif
|
||||
#endif /* LIBAVCODEC_VERSION_MAJOR < 55 */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -351,7 +369,7 @@ static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UI
|
||||
#endif
|
||||
|
||||
if (mdecoder->decoded_size_max == 0)
|
||||
mdecoder->decoded_size_max = AVCODEC_MAX_AUDIO_FRAME_SIZE + 16;
|
||||
mdecoder->decoded_size_max = MAX_AUDIO_FRAME_SIZE + 16;
|
||||
mdecoder->decoded_data = malloc(mdecoder->decoded_size_max);
|
||||
ZeroMemory(mdecoder->decoded_data, mdecoder->decoded_size_max);
|
||||
/* align the memory for SSE2 needs */
|
||||
@ -363,7 +381,7 @@ static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UI
|
||||
while (src_size > 0)
|
||||
{
|
||||
/* Ensure enough space for decoding */
|
||||
if (mdecoder->decoded_size_max - mdecoder->decoded_size < AVCODEC_MAX_AUDIO_FRAME_SIZE)
|
||||
if (mdecoder->decoded_size_max - mdecoder->decoded_size < MAX_AUDIO_FRAME_SIZE)
|
||||
{
|
||||
mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2 + 16;
|
||||
mdecoder->decoded_data = realloc(mdecoder->decoded_data, mdecoder->decoded_size_max);
|
||||
|
6
ci/cmake-preloads/config-android.txt
Normal file
6
ci/cmake-preloads/config-android.txt
Normal file
@ -0,0 +1,6 @@
|
||||
message("PRELOADING android cache")
|
||||
set(CMAKE_TOOLCHAIN_FILE "cmake/AndroidToolchain.cmake" CACHE PATH "ToolChain file")
|
||||
set(FREERDP_ANDROID_EXTERNAL_SSL_PATH $ENV{ANDROID_SSL_PATH} CACHE PATH "android ssl")
|
||||
# ANDROID_NDK and ANDROID_SDK must be set as environment variable
|
||||
#set(ANDROID_NDK $ENV{ANDROID_SDK} CACHE PATH "Android NDK")
|
||||
#set(ANDROID_SDK "${ANDROID_NDK}" CACHE PATH "android SDK")
|
9
ci/cmake-preloads/config-debian-squeeze.txt
Normal file
9
ci/cmake-preloads/config-debian-squeeze.txt
Normal file
@ -0,0 +1,9 @@
|
||||
message("PRELOADING cache")
|
||||
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
|
||||
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
|
||||
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
|
||||
set (WITH_XV OFF CACHE BOOL "xvideo support")
|
||||
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
|
5
ci/cmake-preloads/config-ios.txt
Normal file
5
ci/cmake-preloads/config-ios.txt
Normal file
@ -0,0 +1,5 @@
|
||||
message("PRELOADING android cache")
|
||||
set(CMAKE_TOOLCHAIN_FILE "cmake/iOSToolchain.cmake" CACHE PATH "ToolChain file")
|
||||
set(FREERDP_IOS_EXTERNAL_SSL_PATH $ENV{FREERDP_IOS_EXTERNAL_SSL_PATH} CACHE PATH "android ssl")
|
||||
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||
set(IOS_PLATFORM "SIMULATOR" CACHE STRING "iso platfrorm to build")
|
5
ci/cmake-preloads/config-macosx.txt
Normal file
5
ci/cmake-preloads/config-macosx.txt
Normal file
@ -0,0 +1,5 @@
|
||||
message("PRELOADING mac cache")
|
||||
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
|
||||
set (BUILD_TESTING OFF CACHE BOOL "build testing")
|
9
ci/cmake-preloads/config-ubuntu-1204.txt
Normal file
9
ci/cmake-preloads/config-ubuntu-1204.txt
Normal file
@ -0,0 +1,9 @@
|
||||
message("PRELOADING cache")
|
||||
set (WITH_MANPAGES OFF CACHE BOOL "man pages")
|
||||
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||
set (WITH_CUPS OFF CACHE BOOL "CUPS printing")
|
||||
set (WITH_ALSA OFF CACHE BOOL "alsa audio")
|
||||
set (WITH_FFMPEG OFF CACHE BOOL "ffmepg support")
|
||||
set (WITH_XV OFF CACHE BOOL "xvideo support")
|
||||
set (BUILD_TESTING ON CACHE BOOL "build testing")
|
||||
set (WITH_XSHM OFF CACHE BOOL "build with xshm support")
|
3
ci/cmake-preloads/config-windows.txt
Normal file
3
ci/cmake-preloads/config-windows.txt
Normal file
@ -0,0 +1,3 @@
|
||||
message("PRELOADING windows cache")
|
||||
set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type")
|
||||
#set (BUILD_TESTING ON CACHE BOOL "build testing")
|
4
client/Android/.gitignore
vendored
4
client/Android/.gitignore
vendored
@ -8,3 +8,7 @@ libs/armeabi*
|
||||
AndroidManifest.xml
|
||||
local.properties
|
||||
!.project
|
||||
|
||||
FreeRDPCore/project.properties
|
||||
FreeRDPCore/src/com/freerdp/freerdpcore/utils/BuildConfiguration.java
|
||||
aFreeRDP/project.properties
|
||||
|
@ -1,7 +1,7 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2013 Bernhard Miklautz <bmiklautz@thinstuff.at>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -15,33 +15,45 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(ANDROID_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/aFreeRDP")
|
||||
if (NOT ANDROID_NDK)
|
||||
message(FATAL_ERROR "ANDROID_NDK not set but required for building android native library.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_PROGRAM_PATH ${ANDROID_NDK})
|
||||
find_program(NDK_COMMAND ndk-build CMAKE_FIND_ROOT_PATH_BOTH)
|
||||
|
||||
if(NDK_COMMAND STREQUAL "NDK_COMMAND-NOTFOUND")
|
||||
message(FATAL_ERROR "ndk-build not found but required to build native lib")
|
||||
endif()
|
||||
|
||||
set(NDK_LIB_CFG "${CMAKE_CURRENT_BINARY_DIR}/FreeRDPCore/jni/Android.mk")
|
||||
if(ANDROID_BUILD_JAVA)
|
||||
if (NOT ANDROID_SDK)
|
||||
message(FATAL_ERROR "ANDROID_SDK not set but required for building the java gui (ANDROID_BUILD_JAVA)")
|
||||
endif()
|
||||
# And isn't shiped with the android ndk/sdk so
|
||||
# we need to find it on the local machine
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER, BOTH)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
find_program(ANT_COMMAND ant)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER, ONLY)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
|
||||
if(ANT_COMMAND STREQUAL "ANT_COMMAND-NOTFOUND")
|
||||
message(FATAL_ERROR "ant not found but required to build android java")
|
||||
endif()
|
||||
|
||||
if(ANDROID_BUILD_JAVA_DEBUG)
|
||||
set(ANDROID_BUILD_TYPE "debug")
|
||||
else()
|
||||
set(ANDROID_BUILD_TYPE "release")
|
||||
endif()
|
||||
endif(ANDROID_BUILD_JAVA)
|
||||
|
||||
if(ANDROID_BUILD_JAVA_DEBUG)
|
||||
set(ANDROID_BUILD_TYPE "debug")
|
||||
else()
|
||||
set(ANDROID_BUILD_TYPE "release")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
set(ANDROID_DEBUG_ENABLE "true")
|
||||
set(NDK_DEBUG "1")
|
||||
else()
|
||||
set(ANDROID_DEBUG_ENABLE "false")
|
||||
set(NDK_DEBUG "0")
|
||||
endif()
|
||||
|
||||
add_subdirectory(FreeRDPCore)
|
||||
|
4
client/Android/FreeRDPCore/.gitignore
vendored
Normal file
4
client/Android/FreeRDPCore/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
ant.properties
|
||||
build.xml
|
||||
jni/Android.mk
|
||||
jni/Application.mk
|
@ -5,6 +5,16 @@
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
@ -25,9 +35,19 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
@ -3,19 +3,29 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto"
|
||||
package="com.freerdp.freerdpcore"
|
||||
android:versionCode="2"
|
||||
android:versionCode="@ANDROID_APP_VERSION@"
|
||||
android:versionName="@GIT_REVISION@" >
|
||||
|
||||
<uses-sdk android:targetSdkVersion="@ANDROID_APP_TARGET_SDK@" android:minSdkVersion="@ANDROID_APP_MIN_SDK@"/>
|
||||
<uses-sdk
|
||||
android:targetSdkVersion="@ANDROID_APP_TARGET_SDK@"
|
||||
android:minSdkVersion="@ANDROID_APP_MIN_SDK@"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<supports-screens android:anyDensity="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<application>
|
||||
<application
|
||||
android:debuggable="@ANDROID_DEBUG_ENABLE@">
|
||||
|
||||
<!-- Activity to create shortcuts -->
|
||||
<activity android:name=".presentation.ShortcutsActivity"
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Bernhard Miklautz <bmiklautz@thinstuff.at>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -17,7 +17,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
set(ANDROID_PACKAGE_NAME "aFreeRDPCore")
|
||||
file(MAKE_DIRECTORY ${ANDROID_BINARY_DIR})
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml @ONLY)
|
||||
@ -45,20 +44,5 @@ endif()
|
||||
|
||||
add_subdirectory(jni)
|
||||
|
||||
SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "gen;bin;obj;libs")
|
||||
|
||||
if(ANDROID_BUILD_JAVA)
|
||||
file(MAKE_DIRECTORY "${ANDROID_BINARY_DIR}/bin")
|
||||
set(ANDROIDLIB "${ANDROID_BINARY_DIR}/bin/classes.jar")
|
||||
|
||||
# command to create the android package
|
||||
add_custom_command(
|
||||
OUTPUT "${ANDROIDLIB}"
|
||||
COMMAND ${ANT_COMMAND} ${ANDROID_BUILD_TYPE}
|
||||
WORKING_DIRECTORY "${ANDROID_BINARY_DIR}"
|
||||
MAIN_DEPENDENCY AndroidManifest.xml
|
||||
DEPENDS freerdp-android
|
||||
${CMAKE_CURRENT_BINARY_DIR}/local.properties
|
||||
)
|
||||
add_custom_target(android-lib ALL SOURCES "${ANDROIDLIB}")
|
||||
SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "gen;bin")
|
||||
endif()
|
||||
|
@ -15,6 +15,6 @@
|
||||
# 'key.alias' for the name of the key to use.
|
||||
# The password will be asked during the build when you use the 'release' target.
|
||||
build.dir=@CMAKE_CURRENT_BINARY_DIR@
|
||||
source.dir=@CMAKE_CURRENT_SOURCE_DIR@/src
|
||||
source.path=@CMAKE_CURRENT_SOURCE_DIR@/src:@CMAKE_CURRENT_BINARY_DIR@/src
|
||||
out.dir=@CMAKE_CURRENT_BINARY_DIR@/bin
|
||||
|
||||
|
8
client/Android/FreeRDPCore/jni/Android.mk.cmake
Normal file
8
client/Android/FreeRDPCore/jni/Android.mk.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := freerdp-android
|
||||
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfreerdp-android.so
|
||||
LOCAL_EXPORT_C_INCLUDES := ../../../../include
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
1
client/Android/FreeRDPCore/jni/Application.mk.cmake
Normal file
1
client/Android/FreeRDPCore/jni/Application.mk.cmake
Normal file
@ -0,0 +1 @@
|
||||
APP_ABI := @ANDROID_ABI@
|
@ -2,7 +2,7 @@
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Bernhard Miklautz <bmiklautz@thinstuff.at>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -19,10 +19,14 @@
|
||||
set(MODULE_NAME "freerdp-android")
|
||||
set(MODULE_PREFIX "FREERDP_CLIENT_ANDROID")
|
||||
|
||||
|
||||
include_directories(.)
|
||||
include_directories(generated)
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Application.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Application.mk @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Android.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Android.mk @ONLY)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-pointer-sign")
|
||||
endif()
|
||||
@ -68,16 +72,11 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} jnigraphics)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_ABI}")
|
||||
set_target_properties(${MODULE_NAME}
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${ANDROID_BINARY_DIR}/libs/${ANDROID_ABI}")
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_ABI}")
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Android")
|
||||
|
||||
get_property(LIB_ABSNAME TARGET ${MODULE_NAME} PROPERTY LOCATION)
|
||||
file(MAKE_DIRECTORY ${ANDROID_BINARY_DIR}/obj/local/${ANDROID_NDK_ABI_NAME})
|
||||
|
||||
add_custom_command(TARGET ${MODULE_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${LIB_ABSNAME}
|
||||
${ANDROID_BINARY_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/)
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Android Debug Interface
|
||||
*
|
||||
* Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -16,19 +16,15 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#define TAG "LibFreeRDP"
|
||||
|
||||
#define DEBUG_ANDROID_NULL(fmt, ...) do { } while (0)
|
||||
#define DEBUG_ANDROID_PRINT(_dbg_str, fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, _dbg_str fmt "\n" , __FUNCTION__, __LINE__, ## __VA_ARGS__)
|
||||
#define DEBUG_ANDROID_CLASS(_dbg_class, fmt, ...) DEBUG_ANDROID_PRINT("DBG_" #_dbg_class " %s (%d): ", fmt, ## __VA_ARGS__)
|
||||
#include <freerdp/utils/debug.h>
|
||||
|
||||
#ifdef WITH_DEBUG_ANDROID_JNI
|
||||
#define DEBUG_ANDROID(fmt, ...) DEBUG_ANDROID_PRINT("DBG %s (%d): ", fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_ANDROID(fmt, ...) DEBUG_CLASS(JNI, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_ANDROID(fmt, ...) DEBUG_ANDROID_NULL(fmt, ## __VA_ARGS__)
|
||||
#define DEBUG_ANDROID(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* FREERDP_ANDROID_DEBUG_H */
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Android Event System
|
||||
*
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -2,8 +2,8 @@
|
||||
Android JNI Client Layer
|
||||
|
||||
Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Armin Novak
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Armin Novak
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
@ -28,6 +28,8 @@
|
||||
#include <freerdp/utils/event.h>
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/locale/keyboard.h>
|
||||
#include <freerdp/primitives.h>
|
||||
#include <freerdp/version.h>
|
||||
|
||||
#include <android/bitmap.h>
|
||||
|
||||
@ -42,12 +44,6 @@
|
||||
#include "jni/prof.h"
|
||||
#endif
|
||||
|
||||
struct thread_data
|
||||
{
|
||||
freerdp* instance;
|
||||
};
|
||||
|
||||
|
||||
int android_context_new(freerdp* instance, rdpContext* context)
|
||||
{
|
||||
context->channels = freerdp_channels_new();
|
||||
@ -72,28 +68,33 @@ void android_begin_paint(rdpContext* context)
|
||||
|
||||
void android_end_paint(rdpContext* context)
|
||||
{
|
||||
androidContext *ctx = (androidContext*)context;
|
||||
rdpSettings* settings = context->instance->settings;
|
||||
|
||||
DEBUG_ANDROID("ui_update");
|
||||
|
||||
rdpGdi *gdi = context->gdi;
|
||||
if (gdi->primary->hdc->hwnd->invalid->null)
|
||||
return;
|
||||
assert(ctx);
|
||||
assert(settings);
|
||||
assert(context->instance);
|
||||
|
||||
int x = gdi->primary->hdc->hwnd->invalid->x;
|
||||
int y = gdi->primary->hdc->hwnd->invalid->y;
|
||||
int w = gdi->primary->hdc->hwnd->invalid->w;
|
||||
int h = gdi->primary->hdc->hwnd->invalid->h;
|
||||
|
||||
DEBUG_ANDROID("ui_update: x:%d y:%d w:%d h:%d", x, y, w, h);
|
||||
|
||||
freerdp_callback("OnGraphicsUpdate", "(IIIII)V", context->instance, x, y, w, h);
|
||||
DEBUG_ANDROID("width=%d, height=%d, bpp=%d", settings->DesktopWidth,
|
||||
settings->DesktopHeight, settings->ColorDepth);
|
||||
|
||||
freerdp_callback("OnGraphicsUpdate", "(IIIII)V", context->instance,
|
||||
0, 0, settings->DesktopWidth, settings->DesktopHeight);
|
||||
}
|
||||
|
||||
void android_desktop_resize(rdpContext* context)
|
||||
{
|
||||
DEBUG_ANDROID("ui_desktop_resize");
|
||||
|
||||
rdpGdi *gdi = context->gdi;
|
||||
freerdp_callback("OnGraphicsResize", "(IIII)V", context->instance, gdi->width, gdi->height, gdi->dstBpp);
|
||||
assert(context);
|
||||
assert(context->settings);
|
||||
assert(context->instance);
|
||||
|
||||
freerdp_callback("OnGraphicsResize", "(IIII)V",
|
||||
context->instance, context->settings->DesktopWidth,
|
||||
context->settings->DesktopHeight, context->settings->ColorDepth);
|
||||
}
|
||||
|
||||
|
||||
@ -138,17 +139,24 @@ BOOL android_pre_connect(freerdp* instance)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL android_post_connect(freerdp* instance)
|
||||
static BOOL android_post_connect(freerdp* instance)
|
||||
{
|
||||
rdpSettings *settings = instance->settings;
|
||||
|
||||
DEBUG_ANDROID("android_post_connect");
|
||||
|
||||
assert(instance);
|
||||
assert(settings);
|
||||
|
||||
freerdp_callback("OnSettingsChanged", "(IIII)V", instance,
|
||||
instance->settings->DesktopWidth, instance->settings->DesktopHeight,
|
||||
instance->settings->ColorDepth);
|
||||
settings->DesktopWidth, settings->DesktopHeight,
|
||||
settings->ColorDepth);
|
||||
|
||||
instance->context->cache = cache_new(instance->settings);
|
||||
instance->context->cache = cache_new(settings);
|
||||
|
||||
gdi_init(instance, CLRCONV_ALPHA | ((instance->settings->ColorDepth > 16) ? CLRBUF_32BPP : CLRBUF_16BPP), NULL);
|
||||
gdi_init(instance, CLRCONV_ALPHA | CLRCONV_INVERT |
|
||||
((instance->settings->ColorDepth > 16) ? CLRBUF_32BPP : CLRBUF_16BPP),
|
||||
NULL);
|
||||
|
||||
instance->update->BeginPaint = android_begin_paint;
|
||||
instance->update->EndPaint = android_end_paint;
|
||||
@ -164,6 +172,13 @@ BOOL android_post_connect(freerdp* instance)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void android_post_disconnect(freerdp* instance)
|
||||
{
|
||||
gdi_free(instance);
|
||||
cache_free(instance->context->cache);
|
||||
android_cliprdr_uninit(instance);
|
||||
}
|
||||
|
||||
BOOL android_authenticate(freerdp* instance, char** username, char** password, char** domain)
|
||||
{
|
||||
DEBUG_ANDROID("Authenticate user:");
|
||||
@ -231,11 +246,6 @@ BOOL android_verify_changed_certificate(freerdp* instance, char* subject, char*
|
||||
return android_verify_certificate(instance, subject, issuer, new_fingerprint);
|
||||
}
|
||||
|
||||
int android_receive_channel_data(freerdp* instance, int channelId, UINT8* data, int size, int flags, int total_size)
|
||||
{
|
||||
return freerdp_channels_data(instance, channelId, data, size, flags, total_size);
|
||||
}
|
||||
|
||||
static void android_process_channel_event(rdpChannels* channels, freerdp* instance)
|
||||
{
|
||||
wMessage* event;
|
||||
@ -314,6 +324,9 @@ static void* jni_input_thread(void* arg)
|
||||
do
|
||||
{
|
||||
DWORD rc = WaitForMultipleObjects(3, event, FALSE, INFINITE);
|
||||
if ((rc < WAIT_OBJECT_0) || (rc > WAIT_OBJECT_0 + 2))
|
||||
continue;
|
||||
|
||||
if (rc == WAIT_OBJECT_0 + 2)
|
||||
{
|
||||
wMessage msg;
|
||||
@ -322,9 +335,6 @@ static void* jni_input_thread(void* arg)
|
||||
if (msg.id == WMQ_QUIT)
|
||||
break;
|
||||
}
|
||||
if ((rc < WAIT_OBJECT_0) && (rc > WAIT_OBJECT_0 + 1))
|
||||
break;
|
||||
|
||||
if (android_check_fds(instance) != TRUE)
|
||||
break;
|
||||
}
|
||||
@ -374,7 +384,7 @@ static int android_freerdp_run(freerdp* instance)
|
||||
int rcount;
|
||||
int wcount;
|
||||
int fd_input_event;
|
||||
HANDLE input_event;
|
||||
HANDLE input_event = NULL;
|
||||
void* rfds[32];
|
||||
void* wfds[32];
|
||||
fd_set rfds_set;
|
||||
@ -413,7 +423,7 @@ static int android_freerdp_run(freerdp* instance)
|
||||
(LPTHREAD_START_ROUTINE) jni_update_thread, instance, 0, NULL);
|
||||
}
|
||||
|
||||
if (async_input)
|
||||
if (async_input)
|
||||
{
|
||||
input_thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) jni_input_thread, instance, 0, NULL);
|
||||
@ -521,7 +531,7 @@ static int android_freerdp_run(freerdp* instance)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (input_event)
|
||||
{
|
||||
if (WaitForSingleObject(input_event, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
@ -555,6 +565,13 @@ static int android_freerdp_run(freerdp* instance)
|
||||
freerdp_channels_close(instance->context->channels, instance);
|
||||
|
||||
DEBUG_ANDROID("Cleanup threads...");
|
||||
|
||||
if (async_channels)
|
||||
{
|
||||
WaitForSingleObject(channels_thread, INFINITE);
|
||||
CloseHandle(channels_thread);
|
||||
}
|
||||
|
||||
if (async_update)
|
||||
{
|
||||
wMessageQueue* update_queue = freerdp_get_message_queue(instance, FREERDP_UPDATE_MESSAGE_QUEUE);
|
||||
@ -570,19 +587,9 @@ static int android_freerdp_run(freerdp* instance)
|
||||
WaitForSingleObject(input_thread, INFINITE);
|
||||
CloseHandle(input_thread);
|
||||
}
|
||||
|
||||
if (async_channels)
|
||||
{
|
||||
WaitForSingleObject(channels_thread, INFINITE);
|
||||
CloseHandle(channels_thread);
|
||||
}
|
||||
|
||||
DEBUG_ANDROID("Disconnecting...");
|
||||
freerdp_channels_free(instance->context->channels);
|
||||
freerdp_disconnect(instance);
|
||||
gdi_free(instance);
|
||||
cache_free(instance->context->cache);
|
||||
android_cliprdr_uninit(instance);
|
||||
freerdp_callback("OnDisconnected", "(I)V", instance);
|
||||
|
||||
DEBUG_ANDROID("Quit.");
|
||||
@ -590,19 +597,15 @@ static int android_freerdp_run(freerdp* instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* android_thread_func(void* param)
|
||||
static void* android_thread_func(void* param)
|
||||
{
|
||||
struct thread_data* data;
|
||||
data = (struct thread_data*) param;
|
||||
|
||||
assert(data);
|
||||
assert(data->instance);
|
||||
freerdp* instance = param;
|
||||
|
||||
DEBUG_ANDROID("Start.");
|
||||
|
||||
freerdp* instance = data->instance;
|
||||
assert(instance);
|
||||
|
||||
android_freerdp_run(instance);
|
||||
free(data);
|
||||
|
||||
DEBUG_ANDROID("Quit.");
|
||||
|
||||
@ -615,6 +618,7 @@ JNIEXPORT jint JNICALL jni_freerdp_new(JNIEnv *env, jclass cls)
|
||||
freerdp* instance;
|
||||
|
||||
#if defined(WITH_GPROF)
|
||||
setenv("CPUPROFILE_FREQUENCY", "200", 1);
|
||||
monstartup("libfreerdp-android.so");
|
||||
#endif
|
||||
|
||||
@ -622,11 +626,10 @@ JNIEXPORT jint JNICALL jni_freerdp_new(JNIEnv *env, jclass cls)
|
||||
instance = freerdp_new();
|
||||
instance->PreConnect = android_pre_connect;
|
||||
instance->PostConnect = android_post_connect;
|
||||
instance->PostDisconnect = android_post_disconnect;
|
||||
instance->Authenticate = android_authenticate;
|
||||
instance->VerifyCertificate = android_verify_certificate;
|
||||
instance->VerifyChangedCertificate = android_verify_changed_certificate;
|
||||
instance->ReceiveChannelData = android_receive_channel_data;
|
||||
|
||||
|
||||
// create context
|
||||
instance->ContextSize = sizeof(androidContext);
|
||||
@ -640,6 +643,8 @@ JNIEXPORT jint JNICALL jni_freerdp_new(JNIEnv *env, jclass cls)
|
||||
JNIEXPORT void JNICALL jni_freerdp_free(JNIEnv *env, jclass cls, jint instance)
|
||||
{
|
||||
freerdp* inst = (freerdp*)instance;
|
||||
|
||||
freerdp_context_free(inst);
|
||||
freerdp_free(inst);
|
||||
|
||||
#if defined(WITH_GPROF)
|
||||
@ -650,15 +655,13 @@ JNIEXPORT void JNICALL jni_freerdp_free(JNIEnv *env, jclass cls, jint instance)
|
||||
JNIEXPORT jboolean JNICALL jni_freerdp_connect(JNIEnv *env, jclass cls, jint instance)
|
||||
{
|
||||
freerdp* inst = (freerdp*)instance;
|
||||
struct thread_data* data = (struct thread_data*) malloc(sizeof(struct thread_data));
|
||||
data->instance = inst;
|
||||
androidContext* ctx = (androidContext*)inst->context;
|
||||
|
||||
assert(inst);
|
||||
assert(data);
|
||||
assert(inst->context);
|
||||
assert(ctx);
|
||||
|
||||
androidContext* ctx = (androidContext*)inst->context;
|
||||
pthread_create(&ctx->thread, 0, android_thread_func, data);
|
||||
ctx->thread = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)android_thread_func, inst, 0, NULL);
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
@ -666,19 +669,27 @@ JNIEXPORT jboolean JNICALL jni_freerdp_connect(JNIEnv *env, jclass cls, jint ins
|
||||
JNIEXPORT jboolean JNICALL jni_freerdp_disconnect(JNIEnv *env, jclass cls, jint instance)
|
||||
{
|
||||
freerdp* inst = (freerdp*)instance;
|
||||
androidContext* ctx = (androidContext*)inst->context;
|
||||
ANDROID_EVENT* event = (ANDROID_EVENT*)android_event_disconnect_new();
|
||||
|
||||
assert(inst);
|
||||
assert(ctx);
|
||||
assert(event);
|
||||
|
||||
android_push_event(inst, event);
|
||||
|
||||
WaitForSingleObject(ctx->thread, INFINITE);
|
||||
CloseHandle(ctx->thread);
|
||||
ctx->thread = NULL;
|
||||
|
||||
freerdp_callback("OnDisconnecting", "(I)V", instance);
|
||||
|
||||
return (jboolean) JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL jni_freerdp_cancel_connection(JNIEnv *env, jclass cls, jint instance)
|
||||
{
|
||||
DEBUG_ANDROID("Cancelling connection ...");
|
||||
freerdp* inst = (freerdp*)instance;
|
||||
ANDROID_EVENT* event = (ANDROID_EVENT*)android_event_disconnect_new();
|
||||
android_push_event(inst, event);
|
||||
freerdp_callback("OnDisconnecting", "(I)V", instance);
|
||||
jni_freerdp_disconnect(env, cls, instance);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL jni_freerdp_set_data_directory(JNIEnv *env, jclass cls, jint instance, jstring jdirectory)
|
||||
@ -1001,7 +1012,7 @@ JNIEXPORT void JNICALL jni_freerdp_set_gateway_info(JNIEnv *env, jclass cls, jin
|
||||
(*env)->ReleaseStringUTFChars(env, jgatewaydomain, gatewaydomain);
|
||||
}
|
||||
|
||||
void copy_pixel_buffer(UINT8* dstBuf, UINT8* srcBuf, int x, int y, int width, int height, int wBuf, int hBuf, int bpp)
|
||||
static void copy_pixel_buffer(UINT8* dstBuf, UINT8* srcBuf, int x, int y, int width, int height, int wBuf, int hBuf, int bpp)
|
||||
{
|
||||
int i, j;
|
||||
int length;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
Android JNI Client Layer
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
@ -11,7 +11,6 @@
|
||||
#define __ANDROID_FREERDP_H
|
||||
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include "android_event.h"
|
||||
@ -21,18 +20,14 @@ struct android_context
|
||||
rdpContext rdpCtx;
|
||||
|
||||
ANDROID_EVENT_QUEUE* event_queue;
|
||||
pthread_t thread;
|
||||
HANDLE thread;
|
||||
|
||||
BOOL is_connected;
|
||||
|
||||
void* clipboard_context;
|
||||
};
|
||||
typedef struct android_context androidContext;
|
||||
|
||||
|
||||
|
||||
void copy_remotefx_tile(UINT8* dstBuf, UINT8* srcBuf, int x, int y, int width, int height, int bpp);
|
||||
void copy_pixel_buffer(UINT8* dstBuf, UINT8* srcBuf, int x, int y, int width, int height, int wBuf, int hBuf, int bpp);
|
||||
|
||||
JNIEXPORT jint JNICALL jni_freerdp_new(JNIEnv *env, jclass cls);
|
||||
JNIEXPORT void JNICALL jni_freerdp_free(JNIEnv *env, jclass cls, jint instance);
|
||||
JNIEXPORT jboolean JNICALL jni_freerdp_connect(JNIEnv *env, jclass cls, jint instance);
|
||||
|
@ -2,7 +2,7 @@
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Android JNI Callback Helpers
|
||||
*
|
||||
* Copyright 2011-2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2011-2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -13,7 +13,6 @@
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include "android_jni_callback.h"
|
||||
#include "android_debug.h"
|
||||
@ -36,24 +35,30 @@ void jni_load_class(JNIEnv *env, const char *path, jobject *objptr)
|
||||
|
||||
if (!class)
|
||||
{
|
||||
DEBUG_ANDROID("jni_load_class: failed to find class %s", path);
|
||||
DEBUG_WARN("jni_load_class: failed to find class %s", path);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
method = (*env)->GetMethodID(env, class, "<init>", "()V");
|
||||
|
||||
if (!method)
|
||||
{
|
||||
DEBUG_ANDROID("jni_load_class: failed to find class constructor of %s", path);
|
||||
DEBUG_WARN("jni_load_class: failed to find class constructor of %s", path);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
object = (*env)->NewObject(env, class, method);
|
||||
|
||||
if (!object)
|
||||
{
|
||||
DEBUG_ANDROID("jni_load_class: failed create new object of %s", path);
|
||||
DEBUG_WARN("jni_load_class: failed create new object of %s", path);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
(*objptr) = (*env)->NewGlobalRef(env, object);
|
||||
|
||||
finish:
|
||||
while(0);
|
||||
}
|
||||
|
||||
jint init_callback_environment(JavaVM* vm)
|
||||
@ -61,7 +66,7 @@ jint init_callback_environment(JavaVM* vm)
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
|
||||
{
|
||||
DEBUG_ANDROID("JNI_OnLoad: failed to obtain current JNI environment");
|
||||
DEBUG_WARN("JNI_OnLoad: failed to obtain current JNI environment");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -83,7 +88,7 @@ jboolean jni_attach_thread(JNIEnv** env)
|
||||
|
||||
if ((*jVM)->GetEnv(jVM, (void**) env, JNI_VERSION_1_4) != JNI_OK)
|
||||
{
|
||||
DEBUG_ANDROID("android_java_callback: failed to obtain current JNI environment");
|
||||
DEBUG_WARN("android_java_callback: failed to obtain current JNI environment");
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
@ -113,17 +118,20 @@ void java_callback_void(jobject obj, const char * callback, const char* signatur
|
||||
jObjClass = (*env)->GetObjectClass(env, obj);
|
||||
|
||||
if (!jObjClass) {
|
||||
DEBUG_ANDROID("android_java_callback: failed to get class reference");
|
||||
DEBUG_WARN("android_java_callback: failed to get class reference");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
jCallback = (*env)->GetStaticMethodID(env, jObjClass, callback, signature);
|
||||
|
||||
if (!jCallback) {
|
||||
DEBUG_ANDROID("android_java_callback: failed to get method id");
|
||||
DEBUG_WARN("android_java_callback: failed to get method id");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethodV(env, jObjClass, jCallback, args);
|
||||
|
||||
finish:
|
||||
if(attached == JNI_TRUE)
|
||||
jni_detach_thread();
|
||||
}
|
||||
@ -134,6 +142,7 @@ jboolean java_callback_bool(jobject obj, const char * callback, const char* sign
|
||||
jclass jObjClass;
|
||||
jmethodID jCallback;
|
||||
jboolean attached;
|
||||
jboolean res = JNI_FALSE;
|
||||
JNIEnv *env;
|
||||
|
||||
DEBUG_ANDROID("java_callback: %s (%s)", callback, signature);
|
||||
@ -143,17 +152,20 @@ jboolean java_callback_bool(jobject obj, const char * callback, const char* sign
|
||||
jObjClass = (*env)->GetObjectClass(env, obj);
|
||||
|
||||
if (!jObjClass) {
|
||||
DEBUG_ANDROID("android_java_callback: failed to get class reference");
|
||||
DEBUG_WARN("android_java_callback: failed to get class reference");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
jCallback = (*env)->GetStaticMethodID(env, jObjClass, callback, signature);
|
||||
|
||||
if (!jCallback) {
|
||||
DEBUG_ANDROID("android_java_callback: failed to get method id");
|
||||
DEBUG_WARN("android_java_callback: failed to get method id");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
jboolean res = (*env)->CallStaticBooleanMethodV(env, jObjClass, jCallback, args);
|
||||
res = (*env)->CallStaticBooleanMethodV(env, jObjClass, jCallback, args);
|
||||
|
||||
finish:
|
||||
if(attached == JNI_TRUE)
|
||||
jni_detach_thread();
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Android JNI Callback Helpers
|
||||
*
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2011-2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2011-2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -21,7 +21,6 @@ jboolean jni_attach_thread(JNIEnv** env);
|
||||
void jni_detach_thread(void);
|
||||
void freerdp_callback(const char * callback, const char * signature, ...);
|
||||
jboolean freerdp_callback_bool_result(const char * callback, const char * signature, ...);
|
||||
void tsxconnect_callback(const char * callback, const char * signature, ...);
|
||||
|
||||
#endif /* FREERDP_ANDROID_JNI_CALLBACK_H */
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Android Event System
|
||||
*
|
||||
* Copyright 2013 Felix Long
|
||||
* Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Android Event System
|
||||
*
|
||||
* Copyright 2013 Felix Long
|
||||
* Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Layout for custom preference item showing a button on the right side
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Layout for user credentials input
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Layout for dialogs with a "Don't show again" checkbox
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Main screen layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
List header layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Session screen layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Super bar/Quick connect bar layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -188,7 +188,6 @@
|
||||
<string name="dlg_msg_connecting">Conectando ...</string>
|
||||
<string name="dlg_msg_logging_in">Ingresando a ...</string>
|
||||
<string name="dlg_title_about">Sobre aFreeRDP</string>
|
||||
<string name="dlg_msg_about">Version: %1$s\n\u00A9 2012 Thinstuff Technologies GmbH</string>
|
||||
<string name="dlg_title_create_bookmark_after_qc">Guardar configuración de conexión?</string>
|
||||
<string name="dlg_msg_create_bookmark_after_qc">La configuración de conexión no se han guardado! ¿Quieres guardarlos?</string>
|
||||
<string name="dlg_title_save_bookmark">Guardar Conexión</string>
|
||||
|
@ -187,7 +187,6 @@
|
||||
<string name="dlg_msg_connecting">"Connexion..."</string>
|
||||
<string name="dlg_msg_logging_in">"Connexion..."</string>
|
||||
<string name="dlg_title_about">"À propos de aFreeRDP"</string>
|
||||
<string name="dlg_msg_about">"Version: %1$s \ n \ u00A9 2012 Technologies GmbH Thinstuff"</string>
|
||||
<string name="dlg_title_create_bookmark_after_qc">"Enregistrer les paramètres de connexion?"</string>
|
||||
<string name="dlg_msg_create_bookmark_after_qc">"Vos paramètres de connexion n'ont pas été sauvegardés! Voulez-vous les enregistrer?"</string>
|
||||
<string name="dlg_title_save_bookmark">"Enregistrer la connexion?"</string>
|
||||
|
@ -188,7 +188,6 @@
|
||||
<string name="dlg_msg_connecting">Verbinden ...</string>
|
||||
<string name="dlg_msg_logging_in">Aanmelden ...</string>
|
||||
<string name="dlg_title_about">Over aFreeRDP</string>
|
||||
<string name="dlg_msg_about">Versie: %1$s\n\u00A9 2012 Thinstuff Technologies GmbH</string>
|
||||
<string name="dlg_title_create_bookmark_after_qc">Connectie instellingen opslaan?</string>
|
||||
<string name="dlg_msg_create_bookmark_after_qc">Uw connectie instellingen zijn niet opgeslagen! Wilt u deze opslaan?</string>
|
||||
<string name="dlg_title_save_bookmark">Verbinding opslaan?</string>
|
||||
|
@ -185,7 +185,6 @@
|
||||
<string name="dlg_msg_connecting">Connecting …</string>
|
||||
<string name="dlg_msg_logging_in">Logging in …</string>
|
||||
<string name="dlg_title_about">About aFreeRDP</string>
|
||||
<string name="dlg_msg_about">Version: %1$s\n\u00A9 2012 Thinstuff Technologies GmbH</string>
|
||||
<string name="dlg_title_create_bookmark_after_qc">Save Connection Settings?</string>
|
||||
<string name="dlg_msg_create_bookmark_after_qc">Your connection settings have not been saved! Do you want to save them?</string>
|
||||
<string name="dlg_title_save_bookmark">Save Connection?</string>
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Advanced Bookmark Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Application Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Bookmark Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Credential Settings Layout
|
||||
|
||||
Copyright 2012 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2012 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Cursor Keyboard Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,8 +3,8 @@
|
||||
/*
|
||||
Debug Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH
|
||||
Copyright 2013 Armin Novak <anovak@thinstuff.at>
|
||||
Copyright 2013 Thincast Technologies GmbH
|
||||
Copyright 2013 Armin Novak <armin.novak@thincast.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Modifier Keys Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Numpad keyboard layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Bookmark Performance Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Bookmark 3G Performance Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Bookmark Screen Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
Bookmark 3G Screen Settings Layout
|
||||
|
||||
Copyright 2013 Thinstuff Technologies GmbH, Author: Martin Fleisz
|
||||
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user