Merge branch 'master' of github.com:awakecoding/FreeRDP into smartcard

Conflicts:
	channels/smartcard/client/smartcard_main.c
This commit is contained in:
Marc-André Moreau 2014-04-02 18:27:31 -04:00
commit 8cdb3576c2
607 changed files with 38640 additions and 11507 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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.")

View File

@ -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()

View File

@ -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()

View File

@ -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.");

View 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")

View 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;
}

View File

@ -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")

View File

@ -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;

View File

@ -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()

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -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
{

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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++)

View File

@ -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;

View File

@ -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;
}

View File

@ -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})

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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})

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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})

View File

@ -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)

View File

@ -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";

View File

@ -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>

View File

@ -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>

View File

@ -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);

View 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")

View 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")

View 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")

View 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")

View 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")

View 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")

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,4 @@
ant.properties
build.xml
jni/Android.mk
jni/Application.mk

View File

@ -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>&lt;project&gt;/.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>

View File

@ -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"

View File

@ -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()

View File

@ -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

View 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)

View File

@ -0,0 +1 @@
APP_ABI := @ANDROID_ABI@

View File

@ -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}/)

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -185,7 +185,6 @@
<string name="dlg_msg_connecting">Connecting &#8230;</string>
<string name="dlg_msg_logging_in">Logging in &#8230;</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>

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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/.

View File

@ -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