New upstream version 2.8.0+dfsg1

This commit is contained in:
Mike Gabriel 2022-08-16 23:18:01 +02:00
parent f26a873894
commit cfbb8ebcbd
129 changed files with 6897 additions and 1612 deletions

View File

@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER})
endif()
set(WITH_LIBRARY_VERSIONING "ON")
set(RAW_VERSION_STRING "2.7.0")
set(RAW_VERSION_STRING "2.8.0")
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
elseif(USE_VERSION_FROM_GIT_TAG)
@ -355,7 +355,7 @@ if(NOT IOS)
find_package(Threads REQUIRED)
endif()
if(NOT WIN32)
if(NOT WIN32 AND NOT IOS)
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)
@ -598,6 +598,11 @@ if(ANDROID)
set (WITH_NEON OFF)
endif()
if(ANDROID_ABI STREQUAL arm64-v8a)
# https://github.com/android/ndk/issues/910
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DNDK_DEBUG=1)
@ -1032,15 +1037,6 @@ add_subdirectory(include)
add_subdirectory(libfreerdp)
if (IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0")
if (IOS_PLATFORM MATCHES "SIMULATOR")
set(CMAKE_OSX_SYSROOT "iphonesimulator")
else()
set(CMAKE_OSX_SYSROOT "iphoneos")
endif()
endif()
# RdTk
include_directories("${CMAKE_SOURCE_DIR}/rdtk/include")
include_directories("${CMAKE_BINARY_DIR}/rdtk/include")

View File

@ -1,3 +1,38 @@
# 2022-07-28 Version 2.8.0
Noteworthy changes:
* Backported API to get peer accepted channel option flags
* Backported API to get peer accepted channel names
* Backported Stream_CheckAndLogRequiredLength
* Backported #7954: Add server side handling for [MS-RDPET]
* Backported #8010: Add server side handling for [MS-RDPECAM]
* Backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
* Backported #8051: Relieve CLIPRDR filename restriction when connecting to non-MS Windows servers
* Backported #8048: TLS version control
* Backported #7987: Add a new command line arg to enforce tls1.2
Fixed issues:
* Fixed #7837: Prevent out of bound reads for FFMPEG
* Backported #7859 and #7861: Unwind support for backtrace generation
* Backported #7440: wlfreerdp appid
* Backported #7832: RAIL window restore
* Backported #7833: Refactored WinPR thread locking
* Backported #7893: Mac rdpsnd memory leak fixes
* Backported #7895: Mac audin memory leak fixes
* Backported #7898: Automatic android versioning
* Backported #7916: GFX 10.7 capability support
* Backported #7949: Server RDPSND API improvements
* Backported #7957: Server DVC API improvements
* Backported #7760: Fixed osMinorType values
* Backported #8013: Add missing osMajorType values
* Backported #8076: Fix wrong usage of subband diffing flag (tile artifact fix)
For a complete and detailed change log since the last release run:
git log 2.7.0..2.8.0
# 2022-04-25 Version 2.7.0
Noteworthy changes:
@ -583,4 +618,3 @@ Virtual Channels:
* rdpsnd (Sound Redirection)
* alsa support
* pulse support

View File

@ -21,59 +21,37 @@
#ifndef FREERDP_INT_AINPUT_COMMON_H
#define FREERDP_INT_AINPUT_COMMON_H
#include <assert.h>
#include <winpr/string.h>
#include <freerdp/channels/ainput.h>
static INLINE void ainput_append(char* buffer, size_t size, const char* what, BOOL separator)
{
size_t have;
size_t toadd;
assert(buffer || (size == 0));
assert(what);
have = strnlen(buffer, size);
toadd = strlen(what);
if (have > 0)
toadd += 1;
if (size - have < toadd + 1)
return;
if (have > 0)
strcat(buffer, separator ? "|" : " ");
strcat(buffer, what);
}
static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size)
{
char number[32] = { 0 };
if (flags & AINPUT_FLAGS_HAVE_REL)
ainput_append(buffer, size, "AINPUT_FLAGS_HAVE_REL", TRUE);
winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|");
if (flags & AINPUT_FLAGS_WHEEL)
ainput_append(buffer, size, "AINPUT_FLAGS_WHEEL", TRUE);
winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|");
if (flags & AINPUT_FLAGS_MOVE)
ainput_append(buffer, size, "AINPUT_FLAGS_MOVE", TRUE);
winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|");
if (flags & AINPUT_FLAGS_DOWN)
ainput_append(buffer, size, "AINPUT_FLAGS_DOWN", TRUE);
winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|");
if (flags & AINPUT_FLAGS_REL)
ainput_append(buffer, size, "AINPUT_FLAGS_REL", TRUE);
winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON1)
ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON1", TRUE);
winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON2)
ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON2", TRUE);
winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|");
if (flags & AINPUT_FLAGS_BUTTON3)
ainput_append(buffer, size, "AINPUT_FLAGS_BUTTON3", TRUE);
winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|");
if (flags & AINPUT_XFLAGS_BUTTON1)
ainput_append(buffer, size, "AINPUT_XFLAGS_BUTTON1", TRUE);
winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|");
if (flags & AINPUT_XFLAGS_BUTTON2)
ainput_append(buffer, size, "AINPUT_XFLAGS_BUTTON2", TRUE);
winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|");
_snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags);
ainput_append(buffer, size, number, FALSE);
winpr_str_append(number, buffer, size, " ");
return buffer;
}

View File

@ -32,6 +32,7 @@
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <winpr/stream.h>
#include <freerdp/server/ainput.h>
#include <freerdp/channels/ainput.h>
@ -121,16 +122,36 @@ static UINT ainput_server_open_channel(ainput_server* ainput)
ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
WTS_CHANNEL_OPTION_DYNAMIC);
if (ainput->ainput_channel)
break;
Error = GetLastError();
if (Error == ERROR_NOT_FOUND)
{
WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
break;
}
if (ainput->ainput_channel)
{
UINT32 channelId;
BOOL status = TRUE;
channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
break;
}
if (GetTickCount() - StartTick > 5000)
{
WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
break;
}
}
return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
@ -148,7 +169,10 @@ static UINT ainput_server_send_version(ainput_server* ainput)
Stream_SetPosition(s, 0);
if (!Stream_EnsureCapacity(s, 10))
{
WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
return ERROR_OUTOFMEMORY;
}
Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
@ -175,7 +199,7 @@ static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
WINPR_ASSERT(ainput);
WINPR_ASSERT(s);
if (Stream_GetRemainingLength(s) < 24)
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
return ERROR_NO_DATA;
Stream_Read_UINT64(s, time);
@ -237,9 +261,11 @@ static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
case WAIT_OBJECT_0 + 1:
case WAIT_OBJECT_0:
error = ainput_server_context_poll_int(&ainput->context);
break;
case WAIT_FAILED:
default:
WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
error = ERROR_INTERNAL_ERROR;
break;
}
@ -252,9 +278,10 @@ static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
case WAIT_OBJECT_0 + 1:
case WAIT_OBJECT_0:
error = ainput_server_context_poll_int(&ainput->context);
break;
case WAIT_FAILED:
default:
WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
error = ERROR_INTERNAL_ERROR;
break;
}
@ -408,7 +435,7 @@ static UINT ainput_process_message(ainput_server* ainput)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
ULONG BytesReturned, ActualBytesReturned;
UINT16 MessageId;
wStream* s;
@ -437,13 +464,20 @@ static UINT ainput_process_message(ainput_server* ainput)
}
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (BytesReturned != ActualBytesReturned)
{
WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
ActualBytesReturned, BytesReturned);
goto out;
}
Stream_SetLength(s, ActualBytesReturned);
Stream_Read_UINT16(s, MessageId);
switch (MessageId)
@ -471,9 +505,15 @@ BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle
WINPR_ASSERT(handle);
if (!ainput->externalThread)
{
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
return FALSE;
}
if (ainput->state == AINPUT_INITIAL)
{
WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
return FALSE;
}
*handle = ainput_server_get_channel_handle(ainput);
return TRUE;
}
@ -539,6 +579,9 @@ UINT ainput_server_context_poll(ainput_server_context* context)
WINPR_ASSERT(ainput);
if (!ainput->externalThread)
{
WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
return ERROR_INTERNAL_ERROR;
}
return ainput_server_context_poll_int(context);
}

View File

@ -423,32 +423,34 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
#if defined(MAC_OS_X_VERSION_10_14)
if (@available(macOS 10.14, *))
{
AVAuthorizationStatus status =
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status)
{
case AVAuthorizationStatusAuthorized:
mac->isAuthorized = TRUE;
break;
case AVAuthorizationStatusNotDetermined:
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted == YES)
{
mac->isAuthorized = TRUE;
}
else
WLog_WARN(TAG, "Microphone access denied by user");
}];
break;
case AVAuthorizationStatusRestricted:
WLog_WARN(TAG, "Microphone access restricted by policy");
break;
case AVAuthorizationStatusDenied:
WLog_WARN(TAG, "Microphone access denied by policy");
break;
default:
break;
@autoreleasepool {
AVAuthorizationStatus status =
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status)
{
case AVAuthorizationStatusAuthorized:
mac->isAuthorized = TRUE;
break;
case AVAuthorizationStatusNotDetermined:
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted == YES)
{
mac->isAuthorized = TRUE;
}
else
WLog_WARN(TAG, "Microphone access denied by user");
}];
break;
case AVAuthorizationStatusRestricted:
WLog_WARN(TAG, "Microphone access restricted by policy");
break;
case AVAuthorizationStatusDenied:
WLog_WARN(TAG, "Microphone access denied by policy");
break;
default:
break;
}
}
}
#endif

View File

@ -215,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void
opensles->user_data = user_data;
return CHANNEL_RC_OK;
error_out:
audin_opensles_close(opensles);
audin_opensles_close(device);
return ERROR_INTERNAL_ERROR;
}

View File

@ -162,9 +162,6 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s)
for (i = 0; i < audin->context.num_server_formats; i++)
{
AUDIO_FORMAT format = audin->context.server_formats[i];
// TODO: Eliminate this
format.nAvgBytesPerSec =
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
if (!audio_format_write(s, &format))
{
@ -560,6 +557,8 @@ static BOOL audin_server_open(audin_server_context* context)
PULONG pSessionId = NULL;
DWORD BytesReturned = 0;
audin->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned))
@ -577,6 +576,15 @@ static BOOL audin_server_open(audin_server_context* context)
return FALSE;
}
channelId = WTSChannelGetIdByHandle(audin->audin_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");

View File

@ -173,198 +173,3 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
return error;
}
static UINT64 filetime_to_uint64(FILETIME value)
{
UINT64 converted = 0;
converted |= (UINT32)value.dwHighDateTime;
converted <<= 32;
converted |= (UINT32)value.dwLowDateTime;
return converted;
}
static FILETIME uint64_to_filetime(UINT64 value)
{
FILETIME converted;
converted.dwLowDateTime = (UINT32)(value >> 0);
converted.dwHighDateTime = (UINT32)(value >> 32);
return converted;
}
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
/**
* Parse a packed file list.
*
* The resulting array must be freed with the `free()` function.
*
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
* @param [in] format_data_length length of `format_data` in bytes.
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
{
UINT result = NO_ERROR;
UINT32 i;
UINT32 count = 0;
wStream* s = NULL;
if (!format_data || !file_descriptor_array || !file_descriptor_count)
return ERROR_BAD_ARGUMENTS;
s = Stream_New((BYTE*)format_data, format_data_length);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
if (Stream_GetRemainingLength(s) < 4)
{
WLog_ERR(TAG, "invalid packed file list");
result = ERROR_INCORRECT_SIZE;
goto out;
}
Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
{
WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
result = ERROR_INCORRECT_SIZE;
goto out;
}
*file_descriptor_count = count;
*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
if (!*file_descriptor_array)
{
result = ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
for (i = 0; i < count; i++)
{
int c;
UINT64 lastWriteTime;
FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Seek(s, 32); /* reserved1 (32 bytes) */
Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Seek(s, 16); /* reserved2 (16 bytes) */
Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Stream_Read_UINT16(s, file->cFileName[c]);
}
if (Stream_GetRemainingLength(s) > 0)
WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
Stream_GetRemainingLength(s));
out:
Stream_Free(s, FALSE);
return result;
}
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
/**
* Serialize a packed file list.
*
* The resulting format data must be freed with the `free()` function.
*
* @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
* @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
* @param [out] format_data serialized CLIPRDR_FILELIST.
* @param [out] format_data_length length of `format_data` in bytes.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
file_descriptor_count, format_data, format_data_length);
}
UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
UINT result = NO_ERROR;
UINT32 i;
wStream* s = NULL;
if (!file_descriptor_array || !format_data || !format_data_length)
return ERROR_BAD_ARGUMENTS;
if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
{
WLog_WARN(TAG, "No file clipboard support annouonced!");
return ERROR_BAD_ARGUMENTS;
}
s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
for (i = 0; i < file_descriptor_count; i++)
{
int c;
UINT64 lastWriteTime;
const FILEDESCRIPTORW* file = &file_descriptor_array[i];
/*
* There is a known issue with Windows server getting stuck in
* an infinite loop when downloading files that are larger than
* 2 gigabytes. Do not allow clients to send such file lists.
*
* https://support.microsoft.com/en-us/help/2258090
*/
if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
{
if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
{
WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
result = ERROR_FILE_TOO_LARGE;
goto error;
}
}
Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Zero(s, 32); /* reserved1 (32 bytes) */
Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Zero(s, 16); /* reserved2 (16 bytes) */
lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Stream_Write_UINT16(s, file->cFileName[c]);
}
Stream_SealLength(s);
Stream_GetBuffer(s, *format_data);
Stream_GetLength(s, *format_data_length);
Stream_Free(s, FALSE);
return result;
error:
Stream_Free(s, TRUE);
return result;
}

View File

@ -378,6 +378,8 @@ static UINT disp_server_open(DispServerContext* context)
void* buffer;
buffer = NULL;
priv->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
@ -399,6 +401,16 @@ static UINT disp_server_open(DispServerContext* context)
goto out_close;
}
channelId = WTSChannelGetIdByHandle(priv->disp_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
/* Query for channel event handle */
if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) ||

View File

@ -91,7 +91,21 @@ static UINT echo_server_open_channel(echo_server* echo)
WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
if (echo->echo_channel)
{
UINT32 channelId;
BOOL status = TRUE;
channelId = WTSChannelGetIdByHandle(echo->echo_channel);
IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
break;
}
Error = GetLastError();

View File

@ -32,6 +32,8 @@
#include "rail_orders.h"
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask);
/**
* Function description
*
@ -39,6 +41,7 @@
*/
UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
{
char buffer[128] = { 0 };
UINT16 orderLength;
if (!rail || !s)
@ -49,7 +52,7 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
rail_write_pdu_header(s, orderType, orderLength);
Stream_SetPosition(s, orderLength);
WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "",
rail_get_order_type_string(orderType), orderLength);
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
return rail_send_channel_data(rail, s);
}
@ -388,7 +391,46 @@ static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s)
return error;
}
static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
static UINT rail_read_compartment_info_order(wStream* s,
RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */
Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */
return CHANNEL_RC_OK;
}
static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 };
UINT error;
if (!context || !s)
return ERROR_INVALID_PARAMETER;
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
return ERROR_BAD_CONFIGURATION;
if ((error = rail_read_compartment_info_order(s, &pdu)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error);
}
return error;
}
BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask)
{
UINT32 supported, masked;
@ -400,7 +442,15 @@ static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureM
masked = (supported & featureMask);
if (masked != featureMask)
{
char mask[256] = { 0 };
char actual[256] = { 0 };
WLog_WARN(TAG, "[%s] have %s, require %s", __func__,
freerdp_rail_support_flags_to_string(supported, actual, sizeof(actual)),
freerdp_rail_support_flags_to_string(featureMask, mask, sizeof(mask)));
return FALSE;
}
return TRUE;
}
@ -904,6 +954,73 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai
return error;
}
static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor)
{
WINPR_ASSERT(pTextScaleFactor);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, *pTextScaleFactor);
return CHANNEL_RC_OK;
}
static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
UINT32 TextScaleFactor = 0;
UINT error;
if (!context)
return ERROR_INVALID_PARAMETER;
if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor);
if (error)
WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error);
}
return error;
}
static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate)
{
WINPR_ASSERT(pCaretBlinkRate);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, *pCaretBlinkRate);
return CHANNEL_RC_OK;
}
static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s)
{
RailClientContext* context = rail_get_client_interface(rail);
UINT32 CaretBlinkRate = 0;
UINT error;
if (!context)
return ERROR_INVALID_PARAMETER;
if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate)))
return error;
if (context->custom)
{
IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate);
if (error)
WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error);
}
return error;
}
/**
* Function description
*
@ -911,9 +1028,10 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai
*/
UINT rail_order_recv(railPlugin* rail, wStream* s)
{
char buffer[128] = { 0 };
UINT16 orderType;
UINT16 orderLength;
UINT error;
UINT error = CHANNEL_RC_OK;
if (!rail || !s)
return ERROR_INVALID_PARAMETER;
@ -925,55 +1043,88 @@ UINT rail_order_recv(railPlugin* rail, wStream* s)
}
WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "",
rail_get_order_type_string(orderType), orderLength);
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
switch (orderType)
{
case TS_RAIL_ORDER_HANDSHAKE:
return rail_recv_handshake_order(rail, s);
error = rail_recv_handshake_order(rail, s);
break;
case TS_RAIL_ORDER_COMPARTMENTINFO:
error = rail_recv_compartmentinfo_order(rail, s);
break;
case TS_RAIL_ORDER_HANDSHAKE_EX:
return rail_recv_handshake_ex_order(rail, s);
error = rail_recv_handshake_ex_order(rail, s);
break;
case TS_RAIL_ORDER_EXEC_RESULT:
return rail_recv_exec_result_order(rail, s);
error = rail_recv_exec_result_order(rail, s);
break;
case TS_RAIL_ORDER_SYSPARAM:
return rail_recv_server_sysparam_order(rail, s);
error = rail_recv_server_sysparam_order(rail, s);
break;
case TS_RAIL_ORDER_MINMAXINFO:
return rail_recv_server_minmaxinfo_order(rail, s);
error = rail_recv_server_minmaxinfo_order(rail, s);
break;
case TS_RAIL_ORDER_LOCALMOVESIZE:
return rail_recv_server_localmovesize_order(rail, s);
error = rail_recv_server_localmovesize_order(rail, s);
break;
case TS_RAIL_ORDER_GET_APPID_RESP:
return rail_recv_server_get_appid_resp_order(rail, s);
error = rail_recv_server_get_appid_resp_order(rail, s);
break;
case TS_RAIL_ORDER_LANGBARINFO:
return rail_recv_langbar_info_order(rail, s);
error = rail_recv_langbar_info_order(rail, s);
break;
case TS_RAIL_ORDER_TASKBARINFO:
return rail_recv_taskbar_info_order(rail, s);
error = rail_recv_taskbar_info_order(rail, s);
break;
case TS_RAIL_ORDER_ZORDER_SYNC:
return rail_recv_zorder_sync_order(rail, s);
error = rail_recv_zorder_sync_order(rail, s);
break;
case TS_RAIL_ORDER_CLOAK:
return rail_recv_cloak_order(rail, s);
error = rail_recv_cloak_order(rail, s);
break;
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
return rail_recv_power_display_request_order(rail, s);
error = rail_recv_power_display_request_order(rail, s);
break;
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
return rail_recv_get_application_id_extended_response_order(rail, s);
error = rail_recv_get_application_id_extended_response_order(rail, s);
break;
case TS_RAIL_ORDER_TEXTSCALEINFO:
error = rail_recv_textscaleinfo_order(rail, s);
break;
case TS_RAIL_ORDER_CARETBLINKINFO:
error = rail_recv_caretblinkinfo_order(rail, s);
break;
default:
WLog_ERR(TAG, "Unknown RAIL PDU order reveived.");
WLog_ERR(TAG, "Unknown RAIL PDU %s received.",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)));
return ERROR_INVALID_DATA;
}
return CHANNEL_RC_OK;
if (error != CHANNEL_RC_OK)
{
char buffer[128] = { 0 };
WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
}
return error;
}
/**
@ -1358,6 +1509,9 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail,
if (!rail || !compartmentInfo)
return ERROR_INVALID_PARAMETER;
if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED))
return ERROR_BAD_CONFIGURATION;
s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH);
if (!s)

View File

@ -27,37 +27,72 @@
#define TAG CHANNELS_TAG("rail.common")
static const char* const RAIL_ORDER_TYPE_STRINGS[] = { "",
"Execute",
"Activate",
"System Parameters Update",
"System Command",
"Handshake",
"Notify Event",
"",
"Window Move",
"Local Move/Size",
"Min Max Info",
"Client Status",
"System Menu",
"Language Bar Info",
"Get Application ID Request",
"Get Application ID Response",
"Execute Result",
"",
"",
"",
"",
"",
"" };
const char* rail_get_order_type_string(UINT16 orderType)
{
UINT32 index = ((orderType & 0xF0) >> 3) + (orderType & 0x0F);
if (index >= ARRAYSIZE(RAIL_ORDER_TYPE_STRINGS))
return "UNKNOWN";
switch (orderType)
{
case TS_RAIL_ORDER_EXEC:
return "TS_RAIL_ORDER_EXEC";
case TS_RAIL_ORDER_ACTIVATE:
return "TS_RAIL_ORDER_ACTIVATE";
case TS_RAIL_ORDER_SYSPARAM:
return "TS_RAIL_ORDER_SYSPARAM";
case TS_RAIL_ORDER_SYSCOMMAND:
return "TS_RAIL_ORDER_SYSCOMMAND";
case TS_RAIL_ORDER_HANDSHAKE:
return "TS_RAIL_ORDER_HANDSHAKE";
case TS_RAIL_ORDER_NOTIFY_EVENT:
return "TS_RAIL_ORDER_NOTIFY_EVENT";
case TS_RAIL_ORDER_WINDOWMOVE:
return "TS_RAIL_ORDER_WINDOWMOVE";
case TS_RAIL_ORDER_LOCALMOVESIZE:
return "TS_RAIL_ORDER_LOCALMOVESIZE";
case TS_RAIL_ORDER_MINMAXINFO:
return "TS_RAIL_ORDER_MINMAXINFO";
case TS_RAIL_ORDER_CLIENTSTATUS:
return "TS_RAIL_ORDER_CLIENTSTATUS";
case TS_RAIL_ORDER_SYSMENU:
return "TS_RAIL_ORDER_SYSMENU";
case TS_RAIL_ORDER_LANGBARINFO:
return "TS_RAIL_ORDER_LANGBARINFO";
case TS_RAIL_ORDER_GET_APPID_REQ:
return "TS_RAIL_ORDER_GET_APPID_REQ";
case TS_RAIL_ORDER_GET_APPID_RESP:
return "TS_RAIL_ORDER_GET_APPID_RESP";
case TS_RAIL_ORDER_TASKBARINFO:
return "TS_RAIL_ORDER_TASKBARINFO";
case TS_RAIL_ORDER_LANGUAGEIMEINFO:
return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
case TS_RAIL_ORDER_COMPARTMENTINFO:
return "TS_RAIL_ORDER_COMPARTMENTINFO";
case TS_RAIL_ORDER_HANDSHAKE_EX:
return "TS_RAIL_ORDER_HANDSHAKE_EX";
case TS_RAIL_ORDER_ZORDER_SYNC:
return "TS_RAIL_ORDER_ZORDER_SYNC";
case TS_RAIL_ORDER_CLOAK:
return "TS_RAIL_ORDER_CLOAK";
case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
case TS_RAIL_ORDER_SNAP_ARRANGE:
return "TS_RAIL_ORDER_SNAP_ARRANGE";
case TS_RAIL_ORDER_GET_APPID_RESP_EX:
return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
case TS_RAIL_ORDER_EXEC_RESULT:
return "TS_RAIL_ORDER_EXEC_RESULT";
case TS_RAIL_ORDER_TEXTSCALEINFO:
return "TS_RAIL_ORDER_TEXTSCALEINFO";
case TS_RAIL_ORDER_CARETBLINKINFO:
return "TS_RAIL_ORDER_CARETBLINKINFO";
default:
return "TS_RAIL_ORDER_UNKNOWN";
}
}
return RAIL_ORDER_TYPE_STRINGS[index];
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
{
_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
orderType);
return buffer;
}
/**

View File

@ -71,5 +71,6 @@ UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
BOOL extendedSpiSupported);
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
const char* rail_get_order_type_string(UINT16 orderType);
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
#endif /* FREERDP_CHANNEL_RAIL_COMMON_H */

View File

@ -62,6 +62,7 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length)
*/
static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType)
{
char buffer[128] = { 0 };
UINT16 orderLength;
if (!context || !s)
@ -71,8 +72,8 @@ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16
Stream_SetPosition(s, 0);
rail_write_pdu_header(s, orderType, orderLength);
Stream_SetPosition(s, orderLength);
WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", rail_get_order_type_string(orderType),
orderLength);
WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
return rail_send(context, s, orderLength);
}
@ -1535,6 +1536,7 @@ void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags)
UINT rail_server_handle_messages(RailServerContext* context)
{
char buffer[128] = { 0 };
UINT status = CHANNEL_RC_OK;
DWORD bytesReturned;
UINT16 orderType;
@ -1584,8 +1586,8 @@ UINT rail_server_handle_messages(RailServerContext* context)
return ERROR_INTERNAL_ERROR;
}
WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", rail_get_order_type_string(orderType),
orderLength);
WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "",
rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength);
switch (orderType)
{

View File

@ -1952,7 +1952,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con
irp->Callback = rdpdr_server_drive_query_directory_callback2;
irp->DeviceId = deviceId;
irp->FileId = fileId;
strcat(irp->PathName, "\\*.*");
winpr_str_append("\\*.*", irp->PathName, ARRAYSIZE(irp->PathName), NULL);
if (!rdpdr_server_enqueue_irp(context, irp))
{

View File

@ -0,0 +1,22 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("rdpecam")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,12 @@
set(OPTION_DEFAULT ON)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "rdpecam" TYPE "dynamic"
DESCRIPTION "Video Capture Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPECAM]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,27 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("rdpecam")
set(${MODULE_PREFIX}_SRCS
camera_device_enumerator_main.c
camera_device_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,612 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/rdpecam-enumerator.h>
#define TAG CHANNELS_TAG("rdpecam-enumerator.server")
typedef enum
{
ENUMERATOR_INITIAL,
ENUMERATOR_OPENED,
} eEnumeratorChannelState;
typedef struct
{
CamDevEnumServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* enumerator_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eEnumeratorChannelState state;
wStream* buffer;
} enumerator_server;
static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (enumerator->isOpened)
{
WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
enumerator->externalThread = externalThread;
return error;
}
static UINT enumerator_server_open_channel(enumerator_server* enumerator)
{
CamDevEnumServerContext* context = &enumerator->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(enumerator);
if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
enumerator->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!enumerator->enumerator_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SELECT_VERSION_REQUEST pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_DEVICE_ADDED_NOTIFICATION pdu;
UINT error = CHANNEL_RC_OK;
size_t remaining_length;
WCHAR* channel_name_start;
char* tmp;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
/*
* RequiredLength 4:
*
* Nullterminator DeviceName (2),
* VirtualChannelName (>= 1),
* Nullterminator VirtualChannelName (1)
*/
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_NO_DATA;
pdu.DeviceName = (WCHAR*)Stream_Pointer(s);
remaining_length = Stream_GetRemainingLength(s);
channel_name_start = (WCHAR*)Stream_Pointer(s);
/* Search for null terminator of DeviceName */
for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
{
if (*channel_name_start == L'\0')
break;
}
if (*channel_name_start != L'\0')
{
WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
return ERROR_INVALID_DATA;
}
pdu.VirtualChannelName = (char*)++channel_name_start;
++i;
if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
tmp = pdu.VirtualChannelName;
for (; i < remaining_length; ++i, ++tmp)
{
if (*tmp == '\0')
break;
}
if (*tmp != '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_DEVICE_REMOVED_NOTIFICATION pdu;
UINT error = CHANNEL_RC_OK;
size_t remaining_length;
char* tmp;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return ERROR_NO_DATA;
pdu.VirtualChannelName = (char*)Stream_Pointer(s);
remaining_length = Stream_GetRemainingLength(s);
tmp = (char*)(Stream_Pointer(s) + 1);
for (i = 1; i < remaining_length; ++i, ++tmp)
{
if (*tmp == '\0')
break;
}
if (*tmp != '\0')
{
WLog_ERR(TAG,
"enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
return ERROR_INVALID_DATA;
}
IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
return error;
}
static UINT enumerator_process_message(enumerator_server* enumerator)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
CAM_SHARED_MSG_HEADER header = { 0 };
wStream* s;
WINPR_ASSERT(enumerator);
WINPR_ASSERT(enumerator->enumerator_channel);
s = enumerator->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, header.Version);
Stream_Read_UINT8(s, header.MessageId);
switch (header.MessageId)
{
case CAM_MSG_ID_SelectVersionRequest:
error =
enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
break;
case CAM_MSG_ID_DeviceAddedNotification:
error =
enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
break;
case CAM_MSG_ID_DeviceRemovedNotification:
error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
&header);
break;
default:
WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
header.MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(enumerator);
switch (enumerator->state)
{
case ENUMERATOR_INITIAL:
error = enumerator_server_open_channel(enumerator);
if (error)
WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
error);
else
enumerator->state = ENUMERATOR_OPENED;
break;
case ENUMERATOR_OPENED:
error = enumerator_process_message(enumerator);
break;
}
return error;
}
static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(enumerator);
if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
enumerator_server* enumerator = (enumerator_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(enumerator);
nCount = 0;
events[nCount++] = enumerator->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (enumerator->state)
{
case ENUMERATOR_INITIAL:
error = enumerator_server_context_poll_int(&enumerator->context);
if (error == CHANNEL_RC_OK)
{
events[1] = enumerator_server_get_channel_handle(enumerator);
nCount = 2;
}
break;
case ENUMERATOR_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = enumerator_server_context_poll_int(&enumerator->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(enumerator->enumerator_channel);
enumerator->enumerator_channel = NULL;
if (error && enumerator->context.rdpcontext)
setChannelError(enumerator->context.rdpcontext, error,
"enumerator_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT enumerator_server_open(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread && (enumerator->thread == NULL))
{
enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!enumerator->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
enumerator->thread =
CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
if (!enumerator->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(enumerator->stopEvent);
enumerator->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
enumerator->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT enumerator_server_close(CamDevEnumServerContext* context)
{
UINT error = CHANNEL_RC_OK;
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread && enumerator->thread)
{
SetEvent(enumerator->stopEvent);
if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(enumerator->thread);
CloseHandle(enumerator->stopEvent);
enumerator->thread = NULL;
enumerator->stopEvent = NULL;
}
if (enumerator->externalThread)
{
if (enumerator->state != ENUMERATOR_INITIAL)
{
WTSVirtualChannelClose(enumerator->enumerator_channel);
enumerator->enumerator_channel = NULL;
enumerator->state = ENUMERATOR_INITIAL;
}
}
enumerator->isOpened = FALSE;
return error;
}
static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
if (!enumerator->externalThread)
return ERROR_INTERNAL_ERROR;
return enumerator_server_context_poll_int(context);
}
static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
{
enumerator_server* enumerator = (enumerator_server*)context;
WINPR_ASSERT(enumerator);
WINPR_ASSERT(handle);
if (!enumerator->externalThread)
return FALSE;
if (enumerator->state == ENUMERATOR_INITIAL)
return FALSE;
*handle = enumerator_server_get_channel_handle(enumerator);
return TRUE;
}
static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
{
enumerator_server* enumerator = (enumerator_server*)context;
UINT error = CHANNEL_RC_OK;
ULONG written;
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
out:
Stream_Free(s, TRUE);
return error;
}
static UINT enumerator_send_select_version_response_pdu(
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
{
wStream* s;
s = Stream_New(NULL, CAM_HEADER_SIZE);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId);
return enumerator_server_packet_send(context, s);
}
CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
{
enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
if (!enumerator)
return NULL;
enumerator->context.vcm = vcm;
enumerator->context.Initialize = enumerator_server_initialize;
enumerator->context.Open = enumerator_server_open;
enumerator->context.Close = enumerator_server_close;
enumerator->context.Poll = enumerator_server_context_poll;
enumerator->context.ChannelHandle = enumerator_server_context_handle;
enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
enumerator->buffer = Stream_New(NULL, 4096);
if (!enumerator->buffer)
goto fail;
return &enumerator->context;
fail:
cam_dev_enum_server_context_free(&enumerator->context);
return NULL;
}
void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
{
enumerator_server* enumerator = (enumerator_server*)context;
if (enumerator)
{
enumerator_server_close(context);
Stream_Free(enumerator->buffer, TRUE);
}
free(enumerator);
}

View File

@ -0,0 +1,971 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/rdpecam.h>
#define TAG CHANNELS_TAG("rdpecam.server")
typedef enum
{
CAMERA_DEVICE_INITIAL,
CAMERA_DEVICE_OPENED,
} eCameraDeviceChannelState;
typedef struct
{
CameraDeviceServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* device_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eCameraDeviceChannelState state;
wStream* buffer;
} device_server;
static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (device->isOpened)
{
WLog_WARN(TAG, "Application error: Camera channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
device->externalThread = externalThread;
return error;
}
static UINT device_server_open_channel(device_server* device)
{
CameraDeviceServerContext* context = &device->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(device);
if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
device->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName,
WTS_CHANNEL_OPTION_DYNAMIC);
if (!device->device_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(device->device_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SUCCESS_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
IFCALLRET(context->SuccessResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_ERROR_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_NO_DATA;
Stream_Read_UINT32(s, pdu.ErrorCode);
IFCALLRET(context->ErrorResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_STREAM_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
BYTE i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255);
for (i = 0; i < pdu.N_Descriptions; ++i)
{
CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i];
Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes);
Stream_Read_UINT8(s, StreamDescription->StreamCategory);
Stream_Read_UINT8(s, StreamDescription->Selected);
Stream_Read_UINT8(s, StreamDescription->CanBeShared);
}
IFCALLRET(context->StreamListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_MEDIA_TYPE_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
BYTE i;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
return ERROR_NO_DATA;
pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26;
pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
if (!pdu.MediaTypeDescriptions)
{
WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs",
pdu.N_Descriptions);
return ERROR_NOT_ENOUGH_MEMORY;
}
for (i = 0; i < pdu.N_Descriptions; ++i)
{
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i];
Stream_Read_UINT8(s, MediaTypeDescriptions->Format);
Stream_Read_UINT32(s, MediaTypeDescriptions->Width);
Stream_Read_UINT32(s, MediaTypeDescriptions->Height);
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator);
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator);
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator);
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator);
Stream_Read_UINT8(s, MediaTypeDescriptions->Flags);
}
IFCALLRET(context->MediaTypeListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error);
free(pdu.MediaTypeDescriptions);
return error;
}
static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator);
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator);
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags);
IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SAMPLE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.StreamIndex);
pdu.SampleSize = Stream_GetRemainingLength(s);
pdu.Sample = Stream_Pointer(s);
IFCALLRET(context->SampleResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_SAMPLE_ERROR_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.StreamIndex);
Stream_Read_UINT32(s, pdu.ErrorCode);
IFCALLRET(context->SampleErrorResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_PROPERTY_LIST_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
pdu.N_Properties = Stream_GetRemainingLength(s) / 19;
if (pdu.N_Properties > 0)
{
size_t i;
pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION));
if (!pdu.Properties)
{
WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs",
pdu.N_Properties);
return ERROR_NOT_ENOUGH_MEMORY;
}
for (i = 0; i < pdu.N_Properties; ++i)
{
Stream_Read_UINT8(s, pdu.Properties[i].PropertySet);
Stream_Read_UINT8(s, pdu.Properties[i].PropertyId);
Stream_Read_UINT8(s, pdu.Properties[i].Capabilities);
Stream_Read_INT32(s, pdu.Properties[i].MinValue);
Stream_Read_INT32(s, pdu.Properties[i].MaxValue);
Stream_Read_INT32(s, pdu.Properties[i].Step);
Stream_Read_INT32(s, pdu.Properties[i].DefaultValue);
}
}
IFCALLRET(context->PropertyListResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error);
free(pdu.Properties);
return error;
}
static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context,
wStream* s,
const CAM_SHARED_MSG_HEADER* header)
{
CAM_PROPERTY_VALUE_RESPONSE pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(header);
pdu.Header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, pdu.PropertyValue.Mode);
Stream_Read_INT32(s, pdu.PropertyValue.Value);
IFCALLRET(context->PropertyValueResponse, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error);
return error;
}
static UINT device_process_message(device_server* device)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
CAM_SHARED_MSG_HEADER header = { 0 };
wStream* s;
WINPR_ASSERT(device);
WINPR_ASSERT(device->device_channel);
s = device->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, header.Version);
Stream_Read_UINT8(s, header.MessageId);
switch (header.MessageId)
{
case CAM_MSG_ID_SuccessResponse:
error = device_server_handle_success_response(&device->context, s, &header);
break;
case CAM_MSG_ID_ErrorResponse:
error = device_server_recv_error_response(&device->context, s, &header);
break;
case CAM_MSG_ID_StreamListResponse:
error = device_server_recv_stream_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_MediaTypeListResponse:
error = device_server_recv_media_type_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_CurrentMediaTypeResponse:
error = device_server_recv_current_media_type_response(&device->context, s, &header);
break;
case CAM_MSG_ID_SampleResponse:
error = device_server_recv_sample_response(&device->context, s, &header);
break;
case CAM_MSG_ID_SampleErrorResponse:
error = device_server_recv_sample_error_response(&device->context, s, &header);
break;
case CAM_MSG_ID_PropertyListResponse:
error = device_server_recv_property_list_response(&device->context, s, &header);
break;
case CAM_MSG_ID_PropertyValueResponse:
error = device_server_recv_property_value_response(&device->context, s, &header);
break;
default:
WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "",
header.MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT device_server_context_poll_int(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(device);
switch (device->state)
{
case CAMERA_DEVICE_INITIAL:
error = device_server_open_channel(device);
if (error)
WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error);
else
device->state = CAMERA_DEVICE_OPENED;
break;
case CAMERA_DEVICE_OPENED:
error = device_process_message(device);
break;
}
return error;
}
static HANDLE device_server_get_channel_handle(device_server* device)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(device);
if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI device_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
device_server* device = (device_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(device);
nCount = 0;
events[nCount++] = device->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (device->state)
{
case CAMERA_DEVICE_INITIAL:
error = device_server_context_poll_int(&device->context);
if (error == CHANNEL_RC_OK)
{
events[1] = device_server_get_channel_handle(device);
nCount = 2;
}
break;
case CAMERA_DEVICE_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = device_server_context_poll_int(&device->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(device->device_channel);
device->device_channel = NULL;
if (error && device->context.rdpcontext)
setChannelError(device->context.rdpcontext, error,
"device_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT device_server_open(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread && (device->thread == NULL))
{
device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!device->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL);
if (!device->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(device->stopEvent);
device->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
device->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT device_server_close(CameraDeviceServerContext* context)
{
UINT error = CHANNEL_RC_OK;
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread && device->thread)
{
SetEvent(device->stopEvent);
if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(device->thread);
CloseHandle(device->stopEvent);
device->thread = NULL;
device->stopEvent = NULL;
}
if (device->externalThread)
{
if (device->state != CAMERA_DEVICE_INITIAL)
{
WTSVirtualChannelClose(device->device_channel);
device->device_channel = NULL;
device->state = CAMERA_DEVICE_INITIAL;
}
}
device->isOpened = FALSE;
return error;
}
static UINT device_server_context_poll(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
if (!device->externalThread)
return ERROR_INTERNAL_ERROR;
return device_server_context_poll_int(context);
}
static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle)
{
device_server* device = (device_server*)context;
WINPR_ASSERT(device);
WINPR_ASSERT(handle);
if (!device->externalThread)
return FALSE;
if (device->state == CAMERA_DEVICE_INITIAL)
return FALSE;
*handle = device_server_get_channel_handle(device);
return TRUE;
}
static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId)
{
wStream* s;
WINPR_ASSERT(size > 0);
/* Allocate what we need plus header bytes */
s = Stream_New(NULL, size + CAM_HEADER_SIZE);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return NULL;
}
Stream_Write_UINT8(s, version);
Stream_Write_UINT8(s, messageId);
return s;
}
static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s)
{
device_server* device = (device_server*)context;
UINT error = CHANNEL_RC_OK;
ULONG written;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
out:
Stream_Free(s, TRUE);
return error;
}
static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId)
{
wStream* s;
WINPR_ASSERT(context);
s = device_server_packet_new(0, context->protocolVersion, messageId);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
return device_server_packet_send(context, s);
}
static UINT
device_send_activate_device_request_pdu(CameraDeviceServerContext* context,
const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest);
}
static UINT device_send_deactivate_device_request_pdu(
CameraDeviceServerContext* context,
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest);
}
static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context,
const CAM_STREAM_LIST_REQUEST* streamListRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest);
}
static UINT
device_send_media_type_list_request_pdu(CameraDeviceServerContext* context,
const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(mediaTypeListRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_MediaTypeListRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT device_send_current_media_type_request_pdu(
CameraDeviceServerContext* context,
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(currentMediaTypeRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_CurrentMediaTypeRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT
device_send_start_streams_request_pdu(CameraDeviceServerContext* context,
const CAM_START_STREAMS_REQUEST* startStreamsRequest)
{
wStream* s;
size_t i;
WINPR_ASSERT(context);
WINPR_ASSERT(startStreamsRequest);
s = device_server_packet_new(startStreamsRequest->N_Infos * 27ul, context->protocolVersion,
CAM_MSG_ID_StartStreamsRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
for (i = 0; i < startStreamsRequest->N_Infos; ++i)
{
const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i];
const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription;
Stream_Write_UINT8(s, info->StreamIndex);
Stream_Write_UINT8(s, description->Format);
Stream_Write_UINT32(s, description->Width);
Stream_Write_UINT32(s, description->Height);
Stream_Write_UINT32(s, description->FrameRateNumerator);
Stream_Write_UINT32(s, description->FrameRateDenominator);
Stream_Write_UINT32(s, description->PixelAspectRatioNumerator);
Stream_Write_UINT32(s, description->PixelAspectRatioDenominator);
Stream_Write_UINT8(s, description->Flags);
}
return device_server_packet_send(context, s);
}
static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context,
const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest);
}
static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context,
const CAM_SAMPLE_REQUEST* sampleRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(sampleRequest);
s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_SampleRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, sampleRequest->StreamIndex);
return device_server_packet_send(context, s);
}
static UINT
device_send_property_list_request_pdu(CameraDeviceServerContext* context,
const CAM_PROPERTY_LIST_REQUEST* propertyListRequest)
{
WINPR_ASSERT(context);
return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest);
}
static UINT
device_send_property_value_request_pdu(CameraDeviceServerContext* context,
const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(propertyValueRequest);
s = device_server_packet_new(2, context->protocolVersion, CAM_MSG_ID_PropertyValueRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, propertyValueRequest->PropertySet);
Stream_Write_UINT8(s, propertyValueRequest->PropertyId);
return device_server_packet_send(context, s);
}
static UINT device_send_set_property_value_request_pdu(
CameraDeviceServerContext* context,
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(setPropertyValueRequest);
s = device_server_packet_new(2 + 5, context->protocolVersion,
CAM_MSG_ID_SetPropertyValueRequest);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet);
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId);
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode);
Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value);
return device_server_packet_send(context, s);
}
CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm)
{
device_server* device = (device_server*)calloc(1, sizeof(device_server));
if (!device)
return NULL;
device->context.vcm = vcm;
device->context.Initialize = device_server_initialize;
device->context.Open = device_server_open;
device->context.Close = device_server_close;
device->context.Poll = device_server_context_poll;
device->context.ChannelHandle = device_server_context_handle;
device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu;
device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu;
device->context.StreamListRequest = device_send_stream_list_request_pdu;
device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu;
device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu;
device->context.StartStreamsRequest = device_send_start_streams_request_pdu;
device->context.StopStreamsRequest = device_send_stop_streams_request_pdu;
device->context.SampleRequest = device_send_sample_request_pdu;
device->context.PropertyListRequest = device_send_property_list_request_pdu;
device->context.PropertyValueRequest = device_send_property_value_request_pdu;
device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu;
device->buffer = Stream_New(NULL, 4096);
if (!device->buffer)
goto fail;
return &device->context;
fail:
camera_device_server_context_free(&device->context);
return NULL;
}
void camera_device_server_context_free(CameraDeviceServerContext* context)
{
device_server* device = (device_server*)context;
if (device)
{
device_server_close(context);
Stream_Free(device->buffer, TRUE);
}
free(context->virtualChannelName);
free(device);
}

View File

@ -99,6 +99,8 @@ UINT rdpei_server_init(RdpeiServerContext* context)
void* buffer = NULL;
DWORD bytesReturned;
RdpeiServerPrivate* priv = context->priv;
UINT32 channelId;
BOOL status = TRUE;
priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME,
WTS_CHANNEL_OPTION_DYNAMIC);
@ -108,6 +110,15 @@ UINT rdpei_server_init(RdpeiServerContext* context)
return CHANNEL_RC_INITIALIZATION_ERROR;
}
channelId = WTSChannelGetIdByHandle(priv->channelHandle);
IFCALLRET(context->onChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->onChannelIdAssigned failed!");
goto out_close;
}
if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer,
&bytesReturned) ||
(bytesReturned != sizeof(HANDLE)))

View File

@ -163,11 +163,12 @@ fail:
static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps)
{
const UINT32 filter = gfx->capsFilter;
const UINT32 capList[] = {
RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10,
RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106
};
const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81,
RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101,
RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
RDPGFX_CAPVERSION_107 };
UINT32 x;
for (x = 0; x < ARRAYSIZE(capList); x++)
@ -190,7 +191,7 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
RdpgfxClientContext* context;
RDPGFX_CAPSET* capsSet;
RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 };
RDPGFX_CAPS_ADVERTISE_PDU pdu;
RDPGFX_CAPS_ADVERTISE_PDU pdu = { 0 };
if (!callback)
return ERROR_BAD_ARGUMENTS;
@ -323,6 +324,25 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback)
capsSet->length = 0x4;
capsSet->flags = caps10Flags;
}
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR))
{
capsSet = &capsSets[pdu.capsSetCount++];
capsSet->version = RDPGFX_CAPVERSION_106_ERR;
capsSet->length = 0x4;
capsSet->flags = caps10Flags;
}
if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107))
{
capsSet = &capsSets[pdu.capsSetCount++];
capsSet->version = RDPGFX_CAPVERSION_107;
capsSet->length = 0x4;
capsSet->flags = caps10Flags;
#if !defined(CAIRO_FOUND) && !defined(SWSCALE_FOUND)
capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE;
#endif
}
}
return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu);
@ -895,6 +915,8 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream
case RDPGFX_CAPVERSION_104:
case RDPGFX_CAPVERSION_105:
case RDPGFX_CAPVERSION_106:
case RDPGFX_CAPVERSION_106_ERR:
case RDPGFX_CAPVERSION_107:
if (gfx->SendQoeAck)
{
RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe;

View File

@ -1428,6 +1428,8 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
PULONG pSessionId = NULL;
DWORD BytesReturned = 0;
priv->SessionId = WTS_CURRENT_SESSION;
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
@ -1447,6 +1449,15 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
return FALSE;
}
channelId = WTSChannelGetIdByHandle(priv->rdpgfx_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
goto out_close;
}
/* Query for channel event handle */
if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) ||

View File

@ -213,10 +213,6 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMA
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
break;
default:
return FALSE;
}

View File

@ -139,86 +139,99 @@ static void rdpsnd_mac_release(rdpsndMacPlugin *mac)
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency)
{
AudioDeviceID outputDeviceID;
UInt32 propertySize;
OSStatus err;
NSError *error;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
@autoreleasepool
{
AudioDeviceID outputDeviceID;
UInt32 propertySize;
OSStatus err;
NSError *error;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
#if defined(MAC_OS_VERSION_12_0)
kAudioObjectPropertyElementMain
#else
kAudioObjectPropertyElementMaster
#endif
};
if (mac->isOpen)
if (mac->isOpen)
return TRUE;
if (!rdpsnd_mac_set_format(device, format, latency))
return FALSE;
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
&propertySize, &outputDeviceID);
if (err)
{
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
return FALSE;
}
mac->engine = [[AVAudioEngine alloc] init];
if (!mac->engine)
return FALSE;
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
0, &outputDeviceID, sizeof(outputDeviceID));
if (err)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
return FALSE;
}
mac->player = [[AVAudioPlayerNode alloc] init];
if (!mac->player)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
return FALSE;
}
[mac->engine attachNode:mac->player];
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
[mac->engine prepare];
if (![mac->engine startAndReturnError:&error])
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]);
return FALSE;
}
mac->isOpen = TRUE;
return TRUE;
if (!rdpsnd_mac_set_format(device, format, latency))
return FALSE;
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
&propertySize, &outputDeviceID);
if (err)
{
WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err));
return FALSE;
}
mac->engine = [[AVAudioEngine alloc] init];
if (!mac->engine)
return FALSE;
err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
&outputDeviceID, sizeof(outputDeviceID));
if (err)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err));
return FALSE;
}
mac->player = [[AVAudioPlayerNode alloc] init];
if (!mac->player)
{
rdpsnd_mac_release(mac);
WLog_ERR(TAG, "AVAudioPlayerNode::init() failed");
return FALSE;
}
[mac->engine attachNode:mac->player];
[mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
[mac->engine prepare];
if (![mac->engine startAndReturnError:&error])
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s", [error.localizedDescription UTF8String]);
return FALSE;
}
mac->isOpen = TRUE;
return TRUE;
}
static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
{
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (mac->isPlaying)
@autoreleasepool
{
[mac->player stop];
mac->isPlaying = FALSE;
}
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (mac->isOpen)
{
[mac->engine stop];
mac->isOpen = FALSE;
}
if (mac->isPlaying)
{
[mac->player stop];
mac->isPlaying = FALSE;
}
rdpsnd_mac_release(mac);
if (mac->isOpen)
{
[mac->engine stop];
mac->isOpen = FALSE;
}
rdpsnd_mac_release(mac);
}
}
static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
@ -246,108 +259,118 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device, const AUDIO_
static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
{
Float32 fVolume;
UINT16 volumeLeft;
UINT16 volumeRight;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
@autoreleasepool
{
Float32 fVolume;
UINT16 volumeLeft;
UINT16 volumeRight;
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (!mac->player)
return FALSE;
if (!mac->player)
return FALSE;
volumeLeft = (value & 0xFFFF);
volumeRight = ((value >> 16) & 0xFFFF);
fVolume = ((float)volumeLeft) / 65535.0f;
volumeLeft = (value & 0xFFFF);
volumeRight = ((value >> 16) & 0xFFFF);
fVolume = ((float)volumeLeft) / 65535.0f;
mac->player.volume = fVolume;
mac->player.volume = fVolume;
return TRUE;
return TRUE;
}
}
static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
{
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (!mac->isPlaying)
@autoreleasepool
{
if (!mac->engine.isRunning)
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
if (!mac->isPlaying)
{
NSError *error;
if (![mac->engine startAndReturnError:&error])
if (!mac->engine.isRunning)
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]);
return;
NSError *error;
if (![mac->engine startAndReturnError:&error])
{
device->Close(device);
WLog_ERR(TAG, "Failed to start audio player %s",
[error.localizedDescription UTF8String]);
return;
}
}
[mac->player play];
mac->isPlaying = TRUE;
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
}
[mac->player play];
mac->isPlaying = TRUE;
mac->diff = 100; /* Initial latency, corrected after first sample is played. */
}
}
static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size)
{
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
AVAudioPCMBuffer *buffer;
AVAudioFormat *format;
float *const *db;
size_t pos, step, x;
AVAudioFrameCount count;
UINT64 start = GetTickCount64();
if (!mac->isOpen)
return 0;
step = 2 * mac->format.nChannels;
count = size / step;
format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:mac->format.nSamplesPerSec
channels:mac->format.nChannels
interleaved:NO];
if (!format)
@autoreleasepool
{
WLog_WARN(TAG, "AVAudioFormat::init() failed");
return 0;
}
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
if (!buffer)
{
[format release];
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
return 0;
}
rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
AVAudioPCMBuffer *buffer;
AVAudioFormat *format;
float *const *db;
size_t pos, step, x;
AVAudioFrameCount count;
UINT64 start = GetTickCount64();
buffer.frameLength = buffer.frameCapacity;
db = buffer.floatChannelData;
if (!mac->isOpen)
return 0;
for (pos = 0; pos < count; pos++)
{
const BYTE *d = &data[pos * step];
for (x = 0; x < mac->format.nChannels; x++)
step = 2 * mac->format.nChannels;
count = size / step;
format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:mac->format.nSamplesPerSec
channels:mac->format.nChannels
interleaved:NO];
if (!format)
{
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
db[x][pos] = val;
d += sizeof(int16_t);
WLog_WARN(TAG, "AVAudioFormat::init() failed");
return 0;
}
buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
[format release];
if (!buffer)
{
WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed");
return 0;
}
buffer.frameLength = buffer.frameCapacity;
db = buffer.floatChannelData;
for (pos = 0; pos < count; pos++)
{
const BYTE *d = &data[pos * step];
for (x = 0; x < mac->format.nChannels; x++)
{
const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
db[x][pos] = val;
d += sizeof(int16_t);
}
}
rdpsnd_mac_start(device);
[mac->player scheduleBuffer:buffer
completionHandler:^{
UINT64 stop = GetTickCount64();
if (start > stop)
mac->diff = 0;
else
mac->diff = stop - start;
}];
[buffer release];
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
}
rdpsnd_mac_start(device);
[mac->player scheduleBuffer:buffer
completionHandler:^{
UINT64 stop = GetTickCount64();
if (start > stop)
mac->diff = 0;
else
mac->diff = stop - start;
}];
return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
}
#ifdef BUILTIN_CHANNELS

View File

@ -117,10 +117,6 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_
break;
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_ALAW:
break;
default:
return FALSE;
}

View File

@ -427,15 +427,7 @@ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMA
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
default:
break;
}

View File

@ -582,6 +582,7 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
UINT64 end;
UINT64 diffMS, ts;
UINT latency = 0;
UINT error;
if (Stream_GetRemainingLength(s) < size)
return ERROR_BAD_LENGTH;
@ -589,6 +590,15 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats)
return ERROR_INTERNAL_ERROR;
/*
* Send the first WaveConfirm PDU. The server side uses this to determine the
* network latency.
* See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU
*/
error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo);
if (error)
return error;
data = Stream_Pointer(s);
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
WLog_Print(rdpsnd->log, WLOG_DEBUG,
@ -621,10 +631,11 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
diffMS = end - rdpsnd->wArrivalTime + latency;
ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX;
/* Don't send wave confirm PDU if on dynamic channel */
if (rdpsnd->dynamic)
return CHANNEL_RC_OK;
/*
* Send the second WaveConfirm PDU. With the first WaveConfirm PDU,
* the server side uses this second WaveConfirm PDU to determine the actual
* render latency.
*/
return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo);
}

View File

@ -28,6 +28,7 @@
#include <string.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/print.h>
#include <winpr/stream.h>
@ -36,17 +37,33 @@
#include "rdpsnd_common.h"
#include "rdpsnd_main.h"
static wStream* rdpsnd_server_get_buffer(RdpsndServerContext* context)
{
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
s = context->priv->rdpsnd_pdu;
Stream_SetPosition(s, 0);
return s;
}
/**
* Function description
* Send Server Audio Formats and Version PDU (2.2.2.1)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
{
wStream* s = rdpsnd_server_get_buffer(context);
size_t pos;
UINT16 i;
BOOL status = FALSE;
ULONG written;
if (!Stream_EnsureRemainingCapacity(s, 24))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_FORMATS);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
@ -61,12 +78,9 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
for (i = 0; i < context->num_server_formats; i++)
{
AUDIO_FORMAT format = context->server_formats[i];
// TODO: Eliminate this!!!
format.nAvgBytesPerSec =
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
const AUDIO_FORMAT* format = &context->server_formats[i];
if (!audio_format_write(s, &format))
if (!audio_format_write(s, format))
goto fail;
}
@ -74,6 +88,8 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, pos - 4);
Stream_SetPosition(s, pos);
WINPR_ASSERT(context->priv);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written);
Stream_SetPosition(s, 0);
@ -82,7 +98,7 @@ fail:
}
/**
* Function description
* Read Wave Confirm PDU (2.2.3.8) and handle callback
*
* @return 0 on success, otherwise a Win32 error code
*/
@ -92,11 +108,10 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
BYTE confirmBlockNum;
UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 4)
{
WLog_ERR(TAG, "not enough data in stream!");
WINPR_ASSERT(context);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
}
Stream_Read_UINT16(s, timestamp);
Stream_Read_UINT8(s, confirmBlockNum);
@ -110,13 +125,39 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
}
/**
* Function description
* Read Training Confirm PDU (2.2.3.2) and handle callback
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s)
{
UINT16 timestamp;
UINT16 packsize;
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_INVALID_DATA;
Stream_Read_UINT16(s, timestamp);
Stream_Read_UINT16(s, packsize);
IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize);
if (error)
WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error);
return error;
}
/**
* Read Quality Mode PDU (2.2.2.3)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s)
{
UINT16 quality;
WINPR_ASSERT(context);
if (Stream_GetRemainingLength(s) < 4)
{
@ -124,34 +165,34 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStrea
return ERROR_INVALID_DATA;
}
Stream_Read_UINT16(s, quality);
Stream_Seek_UINT16(s); // reserved
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality);
Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */
Stream_Seek_UINT16(s); /* Reserved */
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode);
return CHANNEL_RC_OK;
}
/**
* Function description
* Read Client Audio Formats and Version PDU (2.2.2.2)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
{
UINT16 i, num_known_format = 0;
UINT32 flags, vol, pitch;
UINT16 udpPort;
BYTE lastblock;
UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 20)
{
WLog_ERR(TAG, "not enough data in stream!");
return ERROR_INVALID_DATA;
}
WINPR_ASSERT(context);
Stream_Read_UINT32(s, flags); /* dwFlags */
Stream_Read_UINT32(s, vol); /* dwVolume */
Stream_Read_UINT32(s, pitch); /* dwPitch */
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */
Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */
Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */
Stream_Read_UINT16(s, udpPort); /* wDGramPort */
Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */
Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */
@ -159,11 +200,8 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
Stream_Seek_UINT8(s); /* bPad */
/* this check is only a guess as cbSize can influence the size of a format record */
if (Stream_GetRemainingLength(s) < context->num_client_formats * 18)
{
WLog_ERR(TAG, "not enough data in stream!");
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18ull * context->num_client_formats))
return ERROR_INVALID_DATA;
}
if (!context->num_client_formats)
{
@ -181,24 +219,26 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
for (i = 0; i < context->num_client_formats; i++)
{
if (Stream_GetRemainingLength(s) < 18)
AUDIO_FORMAT* format = &context->client_formats[i];
if (!Stream_CheckAndLogRequiredLength(TAG, s, 18))
{
WLog_ERR(TAG, "not enough data in stream!");
error = ERROR_INVALID_DATA;
goto out_free;
}
Stream_Read_UINT16(s, context->client_formats[i].wFormatTag);
Stream_Read_UINT16(s, context->client_formats[i].nChannels);
Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec);
Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec);
Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign);
Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample);
Stream_Read_UINT16(s, context->client_formats[i].cbSize);
Stream_Read_UINT16(s, format->wFormatTag);
Stream_Read_UINT16(s, format->nChannels);
Stream_Read_UINT32(s, format->nSamplesPerSec);
Stream_Read_UINT32(s, format->nAvgBytesPerSec);
Stream_Read_UINT16(s, format->nBlockAlign);
Stream_Read_UINT16(s, format->wBitsPerSample);
Stream_Read_UINT16(s, format->cbSize);
if (context->client_formats[i].cbSize > 0)
if (format->cbSize > 0)
{
if (!Stream_SafeSeek(s, context->client_formats[i].cbSize))
if (!Stream_SafeSeek(s, format->cbSize))
{
WLog_ERR(TAG, "Stream_SafeSeek failed!");
error = ERROR_INTERNAL_ERROR;
@ -206,7 +246,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
}
}
if (context->client_formats[i].wFormatTag != 0)
if (format->wFormatTag != 0)
{
// lets call this a known format
// TODO: actually look through our own list of known formats
@ -228,15 +268,19 @@ out_free:
static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
{
DWORD nCount, status;
HANDLE events[8];
RdpsndServerContext* context;
DWORD nCount = 0, status;
HANDLE events[2] = { 0 };
RdpsndServerContext* context = (RdpsndServerContext*)arg;
UINT error = CHANNEL_RC_OK;
context = (RdpsndServerContext*)arg;
nCount = 0;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
events[nCount++] = context->priv->channelEvent;
events[nCount++] = context->priv->StopEvent;
WINPR_ASSERT(nCount <= ARRAYSIZE(events));
while (TRUE)
{
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
@ -281,6 +325,9 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
*/
static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread)
{
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
context->priv->ownThread = ownThread;
return context->Start(context);
}
@ -297,6 +344,9 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli
AUDIO_FORMAT* format;
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if ((client_format_index >= context->num_client_formats) || (!context->src_format))
{
WLog_ERR(TAG, "index %d is not correct.", client_format_index);
@ -371,6 +421,51 @@ out:
return error;
}
/**
* Send Training PDU (2.2.3.1)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize,
BYTE* data)
{
size_t end = 0;
ULONG written;
BOOL status;
wStream* s = rdpsnd_server_get_buffer(context);
if (!Stream_EnsureRemainingCapacity(s, 8))
return ERROR_INTERNAL_ERROR;
Stream_Write_UINT8(s, SNDC_TRAINING);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
Stream_Write_UINT16(s, timestamp);
Stream_Write_UINT16(s, packsize);
if (packsize > 0)
{
if (!Stream_EnsureRemainingCapacity(s, packsize))
{
Stream_SetPosition(s, 0);
return ERROR_INTERNAL_ERROR;
}
Stream_Write(s, data, packsize);
}
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
Stream_SetPosition(s, 0);
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
{
size_t size;
@ -404,15 +499,21 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
const BYTE* src;
AUDIO_FORMAT* format;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK;
wStream* s = rdpsnd_server_get_buffer(context);
if (context->selected_client_format >= context->num_client_formats)
if (context->selected_client_format > context->num_client_formats)
return ERROR_INTERNAL_ERROR;
WINPR_ASSERT(context->client_formats);
format = &context->client_formats[context->selected_client_format];
/* WaveInfo PDU */
Stream_SetPosition(s, 0);
if (!Stream_EnsureRemainingCapacity(s, 16))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */
@ -436,7 +537,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - start + 8);
Stream_SetPosition(s, end);
context->block_no = (context->block_no + 1) % 256;
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
start + 4, &written))
@ -464,6 +564,8 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
error = ERROR_INTERNAL_ERROR;
}
context->block_no = (context->block_no + 1) % 256;
out:
Stream_SetPosition(s, 0);
context->priv->out_pending_frames = 0;
@ -476,59 +578,79 @@ out:
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo,
const BYTE* data, size_t size, BOOL encoded,
UINT16 timestamp, UINT32 audioTimeStamp)
{
size_t length;
size_t end = 0;
const BYTE* src;
AUDIO_FORMAT* format;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK;
BOOL status;
wStream* s = rdpsnd_server_get_buffer(context);
if (context->selected_client_format >= context->num_client_formats)
return ERROR_INTERNAL_ERROR;
format = &context->client_formats[context->selected_client_format];
/* WaveInfo PDU */
Stream_SetPosition(s, 0);
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */
Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */
Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
Stream_Seek(s, 3); /* bPad */
Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */
src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
if (!Stream_EnsureRemainingCapacity(s, 16))
{
error = ERROR_INTERNAL_ERROR;
goto out;
}
/* Wave2 PDU */
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, 0); /* BodySize */
Stream_Write_UINT16(s, timestamp); /* wTimeStamp */
Stream_Write_UINT16(s, formatNo); /* wFormatNo */
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */
if (encoded)
{
if (!Stream_EnsureRemainingCapacity(s, size))
{
error = ERROR_INTERNAL_ERROR;
goto out;
}
Stream_Write(s, data, size);
}
else
{
BOOL rc;
AUDIO_FORMAT* format;
/* Set stream size */
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
return ERROR_INTERNAL_ERROR;
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
context->block_no = (context->block_no + 1) % 256;
rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
if (!rc || (end != written))
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s))
{
WLog_ERR(TAG,
"WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
end, written);
error = ERROR_INTERNAL_ERROR;
goto out;
}
format = &context->client_formats[formatNo];
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
{
error = ERROR_INTERNAL_ERROR;
goto out;
}
}
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - 4);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
&written);
if (!status || (end != written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
end, written);
error = ERROR_INTERNAL_ERROR;
}
context->block_no = (context->block_no + 1) % 256;
out:
Stream_SetPosition(s, 0);
context->priv->out_pending_frames = 0;
return error;
@ -537,8 +659,21 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wT
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
{
const BYTE* src;
size_t length;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (context->selected_client_format >= context->num_client_formats)
return ERROR_INTERNAL_ERROR;
src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
return rdpsnd_server_send_wave2_pdu(context, wTimestamp);
return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length,
FALSE, wTimestamp, wTimestamp);
else
return rdpsnd_server_send_wave_pdu(context, wTimestamp);
}
@ -551,9 +686,11 @@ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wT
static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes,
UINT16 wTimestamp)
{
int cframes;
int cframesize;
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
EnterCriticalSection(&context->priv->lock);
if (context->selected_client_format >= context->num_client_formats)
@ -566,12 +703,13 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void*
while (nframes > 0)
{
cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames);
cframesize = cframes * context->priv->src_bytes_per_frame;
const size_t cframes =
MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames);
size_t cframesize = cframes * context->priv->src_bytes_per_frame;
CopyMemory(context->priv->out_buffer +
(context->priv->out_pending_frames * context->priv->src_bytes_per_frame),
buf, cframesize);
buf = (BYTE*)buf + cframesize;
buf = (const BYTE*)buf + cframesize;
nframes -= cframes;
context->priv->out_pending_frames += cframes;
@ -590,6 +728,33 @@ out:
return error;
}
/**
* Send encoded audio samples using a Wave2 PDU.
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo,
const void* buf, size_t size, UINT16 timestamp,
UINT32 audioTimeStamp)
{
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (context->clientVersion < CHANNEL_VERSION_WIN_8)
return ERROR_INTERNAL_ERROR;
EnterCriticalSection(&context->priv->lock);
error =
rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp);
LeaveCriticalSection(&context->priv->lock);
return error;
}
/**
* Function description
*
@ -597,21 +762,23 @@ out:
*/
static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right)
{
size_t pos;
size_t len;
BOOL status;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
wStream* s = rdpsnd_server_get_buffer(context);
if (!Stream_EnsureRemainingCapacity(s, 8))
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT8(s, SNDC_SETVOLUME);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
Stream_Write_UINT16(s, 4); /* Payload length */
Stream_Write_UINT16(s, left);
Stream_Write_UINT16(s, right);
pos = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, pos - 4);
Stream_SetPosition(s, pos);
len = Stream_GetPosition(s);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written);
(ULONG)len, &written);
Stream_SetPosition(s, 0);
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
@ -626,8 +793,9 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
size_t pos;
BOOL status;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK;
wStream* s = rdpsnd_server_get_buffer(context);
EnterCriticalSection(&context->priv->lock);
if (context->priv->out_pending_frames > 0)
@ -649,6 +817,10 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context)
return error;
context->selected_client_format = 0xFFFF;
if (!Stream_EnsureRemainingCapacity(s, 4))
return ERROR_OUTOFMEMORY;
Stream_Write_UINT8(s, SNDC_CLOSE);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
@ -671,14 +843,21 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
{
void* buffer = NULL;
DWORD bytesReturned;
RdpsndServerPrivate* priv = context->priv;
RdpsndServerPrivate* priv;
UINT error = ERROR_INTERNAL_ERROR;
PULONG pSessionId = NULL;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
priv = context->priv;
priv->SessionId = WTS_CURRENT_SESSION;
if (context->use_dynamic_virtual_channel)
{
UINT32 channelId;
BOOL status = TRUE;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &bytesReturned))
{
@ -691,6 +870,15 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
WLog_ERR(TAG, "Open audio dynamic virtual channel (AUDIO_PLAYBACK_DVC) failed!");
return ERROR_INTERNAL_ERROR;
}
channelId = WTSChannelGetIdByHandle(priv->ChannelHandle);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
goto out_close;
}
}
else
{
@ -741,7 +929,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
goto out_pdu;
}
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu)))
if ((error = rdpsnd_server_send_formats(context)))
{
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error);
goto out_lock;
@ -791,6 +979,12 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
{
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
if (!context->priv->StopEvent)
return error;
if (context->priv->ownThread)
{
if (context->priv->StopEvent)
@ -806,36 +1000,46 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context)
CloseHandle(context->priv->Thread);
CloseHandle(context->priv->StopEvent);
context->priv->Thread = NULL;
context->priv->StopEvent = NULL;
}
}
DeleteCriticalSection(&context->priv->lock);
if (context->priv->rdpsnd_pdu)
{
Stream_Free(context->priv->rdpsnd_pdu, TRUE);
context->priv->rdpsnd_pdu = NULL;
}
if (context->priv->ChannelHandle)
{
WTSVirtualChannelClose(context->priv->ChannelHandle);
context->priv->ChannelHandle = NULL;
}
return error;
}
RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
{
RdpsndServerContext* context;
RdpsndServerPrivate* priv;
context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext));
RdpsndServerContext* context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext));
if (!context)
{
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
goto fail;
context->vcm = vcm;
context->Start = rdpsnd_server_start;
context->Stop = rdpsnd_server_stop;
context->selected_client_format = 0xFFFF;
context->Initialize = rdpsnd_server_initialize;
context->SendFormats = rdpsnd_server_send_formats;
context->SelectFormat = rdpsnd_server_select_format;
context->Training = rdpsnd_server_training;
context->SendSamples = rdpsnd_server_send_samples;
context->SendSamples2 = rdpsnd_server_send_samples2;
context->SetVolume = rdpsnd_server_set_volume;
context->Close = rdpsnd_server_close;
context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate));
@ -843,7 +1047,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv)
{
WLog_ERR(TAG, "calloc failed!");
goto out_free;
goto fail;
}
priv->dsp_context = freerdp_dsp_context_new(TRUE);
@ -851,7 +1055,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
goto out_free_priv;
goto fail;
}
priv->input_stream = Stream_New(NULL, 4);
@ -859,24 +1063,23 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
if (!priv->input_stream)
{
WLog_ERR(TAG, "Stream_New failed!");
goto out_free_dsp;
goto fail;
}
priv->expectedBytes = 4;
priv->waitingHeader = TRUE;
priv->ownThread = TRUE;
return context;
out_free_dsp:
freerdp_dsp_context_free(priv->dsp_context);
out_free_priv:
free(context->priv);
out_free:
free(context);
fail:
rdpsnd_server_context_free(context);
return NULL;
}
void rdpsnd_server_context_reset(RdpsndServerContext* context)
{
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
context->priv->expectedBytes = 4;
context->priv->waitingHeader = TRUE;
Stream_SetPosition(context->priv->input_stream, 0);
@ -884,16 +1087,21 @@ void rdpsnd_server_context_reset(RdpsndServerContext* context)
void rdpsnd_server_context_free(RdpsndServerContext* context)
{
if (context->priv->ChannelHandle)
WTSVirtualChannelClose(context->priv->ChannelHandle);
if (!context)
return;
free(context->priv->out_buffer);
if (context->priv)
{
rdpsnd_server_stop(context);
if (context->priv->dsp_context)
freerdp_dsp_context_free(context->priv->dsp_context);
free(context->priv->out_buffer);
if (context->priv->input_stream)
Stream_Free(context->priv->input_stream, TRUE);
if (context->priv->dsp_context)
freerdp_dsp_context_free(context->priv->dsp_context);
if (context->priv->input_stream)
Stream_Free(context->priv->input_stream, TRUE);
}
free(context->server_formats);
free(context->client_formats);
@ -903,6 +1111,9 @@ void rdpsnd_server_context_free(RdpsndServerContext* context)
HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context)
{
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
return context->priv->channelEvent;
}
@ -924,8 +1135,14 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
{
DWORD bytesReturned;
UINT ret = CHANNEL_RC_OK;
RdpsndServerPrivate* priv = context->priv;
wStream* s = priv->input_stream;
RdpsndServerPrivate* priv;
wStream* s;
WINPR_ASSERT(context);
WINPR_ASSERT(context->priv);
priv = context->priv;
s = priv->input_stream;
if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s),
priv->expectedBytes, &bytesReturned))
@ -980,6 +1197,10 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
ret = rdpsnd_server_recv_waveconfirm(context, s);
break;
case SNDC_TRAINING:
ret = rdpsnd_server_recv_trainingconfirm(context, s);
break;
case SNDC_FORMATS:
ret = rdpsnd_server_recv_formats(context, s);
@ -990,8 +1211,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
case SNDC_QUALITYMODE:
ret = rdpsnd_server_recv_quality_mode(context, s);
Stream_SetPosition(s,
0); /* in case the Activated callback tries to treat some messages */
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
IFCALL(context->Activated, context);

View File

@ -50,9 +50,15 @@
#include <freerdp/server/remdesk.h>
#include <freerdp/server/encomsp.h>
#include <freerdp/server/rail.h>
#include <freerdp/server/telemetry.h>
#include <freerdp/server/rdpgfx.h>
#include <freerdp/server/disp.h>
#if defined(CHANNEL_RDPECAM_SERVER)
#include <freerdp/server/rdpecam-enumerator.h>
#include <freerdp/server/rdpecam.h>
#endif
#if defined(CHANNEL_AINPUT_SERVER)
#include <freerdp/server/ainput.h>
#endif
@ -71,8 +77,13 @@ void freerdp_channels_dummy(void)
RemdeskServerContext* remdesk;
EncomspServerContext* encomsp;
RailServerContext* rail;
TelemetryServerContext* telemetry;
RdpgfxServerContext* rdpgfx;
DispServerContext* disp;
#if defined (CHANNEL_RDPECAM_SERVER)
CamDevEnumServerContext* camera_enumerator;
CameraDeviceServerContext* camera_device;
#endif
audin = audin_server_context_new(NULL);
audin_server_context_free(audin);
rdpsnd = rdpsnd_server_context_new(NULL);
@ -93,10 +104,20 @@ void freerdp_channels_dummy(void)
encomsp_server_context_free(encomsp);
rail = rail_server_context_new(NULL);
rail_server_context_free(rail);
telemetry = telemetry_server_context_new(NULL);
telemetry_server_context_free(telemetry);
rdpgfx = rdpgfx_server_context_new(NULL);
rdpgfx_server_context_free(rdpgfx);
disp = disp_server_context_new(NULL);
disp_server_context_free(disp);
#if defined (CHANNEL_RDPECAM_SERVER)
camera_enumerator = cam_dev_enum_server_context_new(NULL);
cam_dev_enum_server_context_free(camera_enumerator);
camera_device = camera_device_server_context_new(NULL);
camera_device_server_context_free(camera_device);
#endif
#if defined(CHANNEL_AINPUT_SERVER)
{
ainput_server_context* ainput = ainput_server_context_new(NULL);

View File

@ -0,0 +1,22 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("telemetry")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,12 @@
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "telemetry" TYPE "dynamic"
DESCRIPTION "Telemetry Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPET]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,26 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("telemetry")
set(${MODULE_PREFIX}_SRCS
telemetry_main.c)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,443 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Telemetry Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/channels/log.h>
#include <freerdp/server/telemetry.h>
#define TAG CHANNELS_TAG("telemetry.server")
typedef enum
{
TELEMETRY_INITIAL,
TELEMETRY_OPENED,
} eTelemetryChannelState;
typedef struct
{
TelemetryServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* telemetry_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eTelemetryChannelState state;
wStream* buffer;
} telemetry_server;
static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (telemetry->isOpened)
{
WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
telemetry->externalThread = externalThread;
return error;
}
static UINT telemetry_server_open_channel(telemetry_server* telemetry)
{
TelemetryServerContext* context = &telemetry->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(telemetry);
if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
telemetry->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
telemetry->telemetry_channel = WTSVirtualChannelOpenEx(
telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!telemetry->telemetry_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s)
{
TELEMETRY_RDP_TELEMETRY_PDU pdu;
UINT error = CHANNEL_RC_OK;
if (Stream_GetRemainingLength(s) < 16)
{
WLog_ERR(TAG, "telemetry_server_recv_rdp_telemetry_pdu: Not enough data!");
return ERROR_NO_DATA;
}
Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis);
Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis);
Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis);
Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis);
IFCALLRET(context->RdpTelemetry, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error);
return error;
}
static UINT telemetry_process_message(telemetry_server* telemetry)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
BYTE MessageId;
BYTE Length;
wStream* s;
WINPR_ASSERT(telemetry);
WINPR_ASSERT(telemetry->telemetry_channel);
s = telemetry->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return ERROR_NO_DATA;
Stream_Read_UINT8(s, MessageId);
Stream_Read_UINT8(s, Length);
switch (MessageId)
{
case 0x01:
error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s);
break;
default:
WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT telemetry_server_context_poll_int(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(telemetry);
switch (telemetry->state)
{
case TELEMETRY_INITIAL:
error = telemetry_server_open_channel(telemetry);
if (error)
WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!",
error);
else
telemetry->state = TELEMETRY_OPENED;
break;
case TELEMETRY_OPENED:
error = telemetry_process_message(telemetry);
break;
}
return error;
}
static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(telemetry);
if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI telemetry_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
telemetry_server* telemetry = (telemetry_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(telemetry);
nCount = 0;
events[nCount++] = telemetry->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (telemetry->state)
{
case TELEMETRY_INITIAL:
error = telemetry_server_context_poll_int(&telemetry->context);
if (error == CHANNEL_RC_OK)
{
events[1] = telemetry_server_get_channel_handle(telemetry);
nCount = 2;
}
break;
case TELEMETRY_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = telemetry_server_context_poll_int(&telemetry->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(telemetry->telemetry_channel);
telemetry->telemetry_channel = NULL;
if (error && telemetry->context.rdpcontext)
setChannelError(telemetry->context.rdpcontext, error,
"telemetry_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT telemetry_server_open(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread && (telemetry->thread == NULL))
{
telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!telemetry->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL);
if (!telemetry->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(telemetry->stopEvent);
telemetry->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
telemetry->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT telemetry_server_close(TelemetryServerContext* context)
{
UINT error = CHANNEL_RC_OK;
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread && telemetry->thread)
{
SetEvent(telemetry->stopEvent);
if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(telemetry->thread);
CloseHandle(telemetry->stopEvent);
telemetry->thread = NULL;
telemetry->stopEvent = NULL;
}
if (telemetry->externalThread)
{
if (telemetry->state != TELEMETRY_INITIAL)
{
WTSVirtualChannelClose(telemetry->telemetry_channel);
telemetry->telemetry_channel = NULL;
telemetry->state = TELEMETRY_INITIAL;
}
}
telemetry->isOpened = FALSE;
return error;
}
static UINT telemetry_server_context_poll(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
if (!telemetry->externalThread)
return ERROR_INTERNAL_ERROR;
return telemetry_server_context_poll_int(context);
}
static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle)
{
telemetry_server* telemetry = (telemetry_server*)context;
WINPR_ASSERT(telemetry);
WINPR_ASSERT(handle);
if (!telemetry->externalThread)
return FALSE;
if (telemetry->state == TELEMETRY_INITIAL)
return FALSE;
*handle = telemetry_server_get_channel_handle(telemetry);
return TRUE;
}
TelemetryServerContext* telemetry_server_context_new(HANDLE vcm)
{
telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server));
if (!telemetry)
return NULL;
telemetry->context.vcm = vcm;
telemetry->context.Initialize = telemetry_server_initialize;
telemetry->context.Open = telemetry_server_open;
telemetry->context.Close = telemetry_server_close;
telemetry->context.Poll = telemetry_server_context_poll;
telemetry->context.ChannelHandle = telemetry_server_context_handle;
telemetry->buffer = Stream_New(NULL, 4096);
if (!telemetry->buffer)
goto fail;
return &telemetry->context;
fail:
telemetry_server_context_free(&telemetry->context);
return NULL;
}
void telemetry_server_context_free(TelemetryServerContext* context)
{
telemetry_server* telemetry = (telemetry_server*)context;
if (telemetry)
{
telemetry_server_close(context);
Stream_Free(telemetry->buffer, TRUE);
}
free(telemetry);
}

View File

@ -237,7 +237,7 @@ static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data)
}
}
static void func_iso_callback(struct libusb_transfer* transfer)
static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer)
{
ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data;
const UINT32 streamID = stream_id_from_buffer(transfer);
@ -336,7 +336,7 @@ static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPT
return NULL;
}
static void func_bulk_transfer_cb(struct libusb_transfer* transfer)
static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer)
{
ASYNC_TRANSFER_USER_DATA* user_data;
uint32_t streamID;

View File

@ -511,12 +511,7 @@ static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass)
static BOOL append(char* dst, size_t length, const char* src)
{
size_t slen = strlen(src);
size_t dlen = strnlen(dst, length);
if (dlen + slen >= length)
return FALSE;
strcat(dst, src);
return TRUE;
return winpr_str_append(src, dst, length, NULL);
}
static BOOL device_is_filtered(struct libusb_device* dev,
@ -865,7 +860,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman)
return rc > 0;
}
static DWORD poll_thread(LPVOID lpThreadParameter)
static DWORD WINAPI poll_thread(LPVOID lpThreadParameter)
{
libusb_hotplug_callback_handle handle;
UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter;

View File

@ -225,6 +225,7 @@ static BOOL wl_post_connect(freerdp* instance)
wlfContext* context;
rdpSettings* settings;
char* title = "FreeRDP";
char* app_id = "wlfreerdp";
UINT32 w, h;
if (!instance || !instance->context)
@ -266,6 +267,7 @@ static BOOL wl_post_connect(freerdp* instance)
UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen);
UwacWindowSetTitle(window, title);
UwacWindowSetAppId(window, app_id);
UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h);
instance->update->BeginPaint = wl_begin_paint;
instance->update->EndPaint = wl_end_paint;

View File

@ -815,8 +815,6 @@ void xf_lock_x11_(xfContext* xfc, const char* fkt)
else
XLockDisplay(xfc->display);
if (xfc->locked)
WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt);
xfc->locked++;
WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt);
}
@ -1256,6 +1254,7 @@ static BOOL xf_post_connect(freerdp* instance)
context = instance->context;
settings = instance->settings;
update = context->update;
BOOL serverIsWindowsPlatform;
if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
return FALSE;
@ -1325,7 +1324,8 @@ static BOOL xf_post_connect(freerdp* instance)
update->SetKeyboardIndicators = xf_keyboard_set_indicators;
update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
if (!(xfc->clipboard = xf_clipboard_new(xfc)))
serverIsWindowsPlatform = (settings->OsMajorType == OSMAJORTYPE_WINDOWS);
if (!(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform)))
return FALSE;
if (!(xfc->xfDisp = xf_disp_new(xfc)))

View File

@ -1756,7 +1756,27 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate
return clipboard->context->ClientFileContentsResponse(clipboard->context, &response);
}
xfClipboard* xf_clipboard_new(xfContext* xfc)
static BOOL xf_cliprdr_clipboard_is_valid_unix_filename(LPCWSTR filename)
{
LPCWSTR c;
if (!filename)
return FALSE;
if (filename[0] == L'\0')
return FALSE;
/* Reserved characters */
for (c = filename; *c; ++c)
{
if (*c == L'/')
return FALSE;
}
return TRUE;
}
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction)
{
int i, n = 0;
rdpChannels* channels;
@ -1885,6 +1905,13 @@ xfClipboard* xf_clipboard_new(xfContext* xfc)
clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure;
clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success;
clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure;
if (relieveFilenameRestriction)
{
WLog_DBG(TAG, "Relieving CLIPRDR filename restriction");
clipboard->delegate->IsFileNameComponentValid = xf_cliprdr_clipboard_is_valid_unix_filename;
}
return clipboard;
error:

View File

@ -25,7 +25,7 @@
#include <freerdp/client/cliprdr.h>
xfClipboard* xf_clipboard_new(xfContext* xfc);
xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction);
void xf_clipboard_free(xfClipboard* clipboard);
void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr);

View File

@ -24,6 +24,8 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <winpr/assert.h>
#include <freerdp/log.h>
#include <freerdp/locale/keyboard.h>
@ -33,6 +35,7 @@
#include "xf_disp.h"
#include "xf_input.h"
#include "xf_gfx.h"
#include "xf_graphics.h"
#include "xf_event.h"
#include "xf_input.h"
@ -537,6 +540,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap
/* Release all keys, should already be done at FocusOut but might be missed
* if the WM decided to use an alternate event order */
xf_keyboard_release_all_keypress(xfc);
xf_pointer_update_scale(xfc);
if (app)
{
@ -668,6 +672,9 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
rdpSettings* settings;
settings = xfc->context.settings;
WLog_DBG(TAG, "%s: x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32, __func__, event->x,
event->y, event->width, event->height);
if (!app)
{
if (!xfc->window)
@ -710,43 +717,42 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
/* ask the server to resize using the display channel */
xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
}
return TRUE;
}
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
else
{
/*
* ConfigureNotify coordinates are expressed relative to the window parent.
* Translate these to root window coordinates.
*/
XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), 0,
0, &appWindow->x, &appWindow->y, &childWindow);
appWindow->width = event->width;
appWindow->height = event->height;
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
/*
* Additional checks for not in a local move and not ignoring configure to send
* position update to server, also should the window not be focused then do not
* send to server yet (i.e. resizing using window decoration).
* The server will be updated when the window gets refocused.
*/
if (appWindow->decorations)
if (appWindow)
{
/* moving resizing using window decoration */
xf_rail_adjust_position(xfc, appWindow);
}
else
{
if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
!appWindow->rail_ignore_configure && xfc->focused)
/*
* ConfigureNotify coordinates are expressed relative to the window parent.
* Translate these to root window coordinates.
*/
XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
0, 0, &appWindow->x, &appWindow->y, &childWindow);
appWindow->width = event->width;
appWindow->height = event->height;
/*
* Additional checks for not in a local move and not ignoring configure to send
* position update to server, also should the window not be focused then do not
* send to server yet (i.e. resizing using window decoration).
* The server will be updated when the window gets refocused.
*/
if (appWindow->decorations)
{
/* moving resizing using window decoration */
xf_rail_adjust_position(xfc, appWindow);
}
else
{
if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
!appWindow->rail_ignore_configure && xfc->focused)
xf_rail_adjust_position(xfc, appWindow);
}
}
}
return TRUE;
return xf_pointer_update_scale(xfc);
}
static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
@ -759,14 +765,14 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
{
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
if (appWindow && (appWindow->rail_state == WINDOW_SHOW))
{
/* local restore event */
/* This is now handled as part of the PropertyNotify
* Doing this here would inhibit the ability to restore a maximized window
* that is minimized back to the maximized state
*/
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
appWindow->is_mapped = TRUE;
}
}
@ -777,6 +783,10 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
{
xfAppWindow* appWindow;
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
xf_keyboard_release_all_keypress(xfc);
if (!app)
@ -796,6 +806,9 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
/*
* This section handles sending the appropriate commands to the rail server
* when the window has been minimized, maximized, restored locally
@ -830,18 +843,27 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
if (status)
{
if (appWindow)
{
appWindow->maxVert = FALSE;
appWindow->maxHorz = FALSE;
}
for (i = 0; i < nitems; i++)
{
if ((Atom)((UINT16**)prop)[i] ==
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False))
{
maxVert = TRUE;
if (appWindow)
appWindow->maxVert = TRUE;
}
if ((Atom)((UINT16**)prop)[i] ==
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False))
{
maxHorz = TRUE;
if (appWindow)
appWindow->maxHorz = TRUE;
}
}
@ -858,9 +880,17 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
{
/* If the window is in the iconic state */
if (((UINT32)*prop == 3))
{
minimized = TRUE;
if (appWindow)
appWindow->minimized = TRUE;
}
else
{
minimized = FALSE;
if (appWindow)
appWindow->minimized = FALSE;
}
minimizedChanged = TRUE;
XFree(prop);
@ -869,22 +899,30 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
if (app)
{
if (maxVert && maxHorz && !minimized &&
(appWindow->rail_state != WINDOW_SHOW_MAXIMIZED))
WINPR_ASSERT(appWindow);
if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized)
{
appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)
{
appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
}
}
else if (minimized && (appWindow->rail_state != WINDOW_SHOW_MINIMIZED))
else if (appWindow->minimized)
{
appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
{
appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
}
}
else if (!minimized && !maxVert && !maxHorz && (appWindow->rail_state != WINDOW_SHOW) &&
(appWindow->rail_state != WINDOW_HIDE))
else
{
appWindow->rail_state = WINDOW_SHOW;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE)
{
appWindow->rail_state = WINDOW_SHOW;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
}
}
}
else if (minimizedChanged)

View File

@ -30,6 +30,9 @@
#include <X11/Xcursor/Xcursor.h>
#endif
#include <float.h>
#include <math.h>
#include <winpr/crt.h>
#include <freerdp/codec/bitmap.h>
@ -42,6 +45,8 @@
#include <freerdp/log.h>
#define TAG CLIENT_TAG("x11")
static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer);
BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color)
{
rdpGdi* gdi;
@ -227,14 +232,14 @@ static BOOL xf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL pr
return TRUE;
}
static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer,
Cursor* cursor)
static BOOL xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer,
Cursor* cursor)
{
#ifdef WITH_XCURSOR
UINT32 CursorFormat;
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
XcursorImage ci;
XcursorImage ci = { 0 };
rdpSettings* settings;
UINT32 xTargetSize;
UINT32 yTargetSize;
@ -256,6 +261,9 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP
xTargetSize = pointer->width * xscale;
yTargetSize = pointer->height * yscale;
WLog_DBG(TAG, "%s: scaled: %" PRIu32 "x%" PRIu32 ", desktop: %" PRIu32 "x%" PRIu32, __func__,
xfc->scaledWidth, xfc->savedHeight, settings->DesktopWidth, settings->DesktopHeight);
for (int i = 0; i < xpointer->nCursors; i++)
{
if (xpointer->cursorWidths[i] == xTargetSize && xpointer->cursorHeights[i] == yTargetSize)
@ -297,7 +305,6 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP
}
}
ZeroMemory(&ci, sizeof(ci));
ci.version = XCURSOR_IMAGE_VERSION;
ci.size = sizeof(ci);
ci.width = xTargetSize;
@ -306,14 +313,21 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP
ci.yhot = pointer->yPos * yscale;
size = ci.height * ci.width * GetBytesPerPixel(CursorFormat) * 1ULL;
if (xscale != 1 || yscale != 1)
if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16)))
{
if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16)))
{
xf_unlock_x11(xfc);
return FALSE;
}
xf_unlock_x11(xfc);
return FALSE;
}
const double xs = fabs(fabs(xscale) - 1.0);
const double ys = fabs(fabs(yscale) - 1.0);
WLog_DBG(TAG,
"%s: cursorIndex %" PRId32 " scaling pointer %" PRIu32 "x%" PRIu32 " --> %" PRIu32
"x%" PRIu32 " [%lfx%lf]",
__func__, cursorIndex, pointer->width, pointer->height, ci.width, ci.height,
xscale, yscale);
if ((xs > DBL_EPSILON) || (ys > DBL_EPSILON))
{
if (!freerdp_image_scale((BYTE*)ci.pixels, CursorFormat, 0, 0, 0, ci.width, ci.height,
(BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0,
pointer->width, pointer->height))
@ -325,7 +339,7 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP
}
else
{
ci.pixels = xpointer->cursorPixels;
memcpy(ci.pixels, xpointer->cursorPixels, size);
}
cursorIndex = xpointer->nCursors;
@ -333,11 +347,14 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP
xpointer->cursorHeights[cursorIndex] = ci.height;
xpointer->cursors[cursorIndex] = XcursorImageLoadCursor(xfc->display, &ci);
xpointer->nCursors += 1;
if (xscale != 1 || yscale != 1)
_aligned_free(ci.pixels);
_aligned_free(ci.pixels);
xf_unlock_x11(xfc);
}
else
{
WLog_DBG(TAG, "%s: using cached cursor %" PRId32, __func__, cursorIndex);
}
cursor[0] = xpointer->cursors[cursorIndex];
#endif
@ -372,8 +389,21 @@ static Window xf_Pointer_get_window(xfContext* xfc)
}
}
BOOL xf_pointer_update_scale(xfContext* xfc)
{
xfPointer* pointer;
WINPR_ASSERT(xfc);
pointer = xfc->pointer;
if (!pointer)
return TRUE;
return xf_Pointer_Set(&xfc->context, &xfc->pointer->pointer);
}
static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
{
BOOL rc = FALSE;
#ifdef WITH_XCURSOR
UINT32 CursorFormat;
size_t size;
@ -381,7 +411,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
xfPointer* xpointer = (xfPointer*)pointer;
if (!context || !pointer || !context->gdi)
return FALSE;
goto fail;
if (!xfc->invert)
CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32;
@ -394,7 +424,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
size = pointer->height * pointer->width * GetBytesPerPixel(CursorFormat) * 1ULL;
if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16)))
return FALSE;
goto fail;
if (!freerdp_image_copy_from_pointer_data(
(BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height,
@ -404,15 +434,18 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
_aligned_free(xpointer->cursorPixels);
return FALSE;
}
rc = TRUE;
if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xpointer->cursor)))
return FALSE;
#endif
return TRUE;
fail:
WLog_DBG(TAG, "%s: %ld", __func__, rc ? pointer : -1);
return rc;
}
static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer)
{
WLog_DBG(TAG, "%s: %p", __func__, pointer);
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
@ -438,6 +471,8 @@ static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer)
static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
{
WLog_DBG(TAG, "%s: %p", __func__, pointer);
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
Window handle = xf_Pointer_get_window(xfc);
@ -447,18 +482,23 @@ static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
if (handle)
{
if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor)))
if (!xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor)))
return FALSE;
xf_lock_x11(xfc);
XDefineCursor(xfc->display, handle, xfc->pointer->cursor);
xf_unlock_x11(xfc);
}
else
{
WLog_WARN(TAG, "%s: handle=%ld", __func__, handle);
}
#endif
return TRUE;
}
static BOOL xf_Pointer_SetNull(rdpContext* context)
{
WLog_DBG(TAG, "%s", __func__);
#ifdef WITH_XCURSOR
xfContext* xfc = (xfContext*)context;
static Cursor nullcursor = None;
@ -515,10 +555,11 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
if (!handle)
{
WLog_WARN(TAG, "xf_Pointer_SetPosition: focus %d, handle%lu", xfc->focused, handle);
WLog_WARN(TAG, "%s: focus %d, handle%lu", __func__, xfc->focused, handle);
return TRUE;
}
WLog_DBG(TAG, "%s: %" PRIu32 "x%" PRIu32, __func__, x, y);
if (xfc->remote_app && !xfc->focused)
return TRUE;
@ -529,7 +570,7 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
rc = XGetWindowAttributes(xfc->display, handle, &current);
if (rc == 0)
{
WLog_WARN(TAG, "xf_Pointer_SetPosition: XGetWindowAttributes==%d", rc);
WLog_WARN(TAG, "%s: XGetWindowAttributes==%d", __func__, rc);
goto out;
}
@ -538,17 +579,17 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp);
if (rc == 0)
{
WLog_WARN(TAG, "xf_Pointer_SetPosition: XChangeWindowAttributes==%d", rc);
WLog_WARN(TAG, "%s: XChangeWindowAttributes==%d", __func__, rc);
goto out;
}
rc = XWarpPointer(xfc->display, None, handle, 0, 0, 0, 0, x, y);
if (rc == 0)
WLog_WARN(TAG, "xf_Pointer_SetPosition: XWarpPointer==%d", rc);
WLog_WARN(TAG, "%s: XWarpPointer==%d", __func__, rc);
tmp.event_mask = current.your_event_mask;
rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp);
if (rc == 0)
WLog_WARN(TAG, "xf_Pointer_SetPosition: 2.try XChangeWindowAttributes==%d", rc);
WLog_WARN(TAG, "%s: 2.try XChangeWindowAttributes==%d", __func__, rc);
ret = TRUE;
out:
xf_unlock_x11(xfc);

View File

@ -29,4 +29,6 @@ BOOL xf_register_graphics(rdpGraphics* graphics);
BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color);
UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned);
BOOL xf_pointer_update_scale(xfContext* xfc);
#endif /* FREERDP_CLIENT_X11_GRAPHICS_H */

View File

@ -400,18 +400,19 @@ static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* m
}
if (mod->Shift)
strcat(combination, "Shift+");
winpr_str_append("Shift", combination, sizeof(combination), "+");
if (mod->Ctrl)
strcat(combination, "Ctrl+");
winpr_str_append("Ctrl", combination, sizeof(combination), "+");
if (mod->Alt)
strcat(combination, "Alt+");
winpr_str_append("Alt", combination, sizeof(combination), "+");
if (mod->Super)
strcat(combination, "Super+");
winpr_str_append("Super", combination, sizeof(combination), "+");
winpr_str_append(keyStr, combination, sizeof(combination), NULL);
strcat(combination, keyStr);
count = ArrayList_Count(xfc->keyCombinations);
for (index = 0; index < count; index++)

View File

@ -136,10 +136,10 @@ void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
* Calculate new size/position for the rail window(new values for
* windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
*/
windowMove.left = appWindow->x;
windowMove.top = appWindow->y;
windowMove.right = windowMove.left + appWindow->width;
windowMove.bottom = windowMove.top + appWindow->height;
windowMove.left = appWindow->x - appWindow->resizeMarginLeft;
windowMove.top = appWindow->y - appWindow->resizeMarginTop;
windowMove.right = appWindow->x + appWindow->width + appWindow->resizeMarginRight;
windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom;
xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
}
}
@ -163,12 +163,12 @@ void xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow)
* windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
*
*/
windowMove.left = appWindow->x;
windowMove.top = appWindow->y;
windowMove.left = appWindow->x - appWindow->resizeMarginLeft;
windowMove.top = appWindow->y - appWindow->resizeMarginTop;
windowMove.right =
windowMove.left +
appWindow->width; /* In the update to RDP the position is one past the window */
windowMove.bottom = windowMove.top + appWindow->height;
appWindow->x +
appWindow->width + appWindow->resizeMarginRight; /* In the update to RDP the position is one past the window */
windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom;
xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
/*
* Simulate button up at new position to end the local move (per RDP spec)
@ -344,6 +344,18 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
appWindow->windowWidth = windowState->windowWidth;
appWindow->windowHeight = windowState->windowHeight;
}
if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X)
{
appWindow->resizeMarginLeft = windowState->resizeMarginLeft;
appWindow->resizeMarginRight = windowState->resizeMarginRight;
}
if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y)
{
appWindow->resizeMarginTop = windowState->resizeMarginTop;
appWindow->resizeMarginBottom = windowState->resizeMarginBottom;
}
if (fieldFlags & WINDOW_ORDER_FIELD_OWNER)
{

View File

@ -36,6 +36,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <winpr/assert.h>
#include <winpr/thread.h>
#include <winpr/crt.h>
#include <winpr/string.h>
@ -775,6 +776,9 @@ int xf_AppWindowCreate(xfContext* xfc, xfAppWindow* appWindow)
appWindow->is_mapped = FALSE;
appWindow->is_transient = FALSE;
appWindow->rail_state = 0;
appWindow->maxVert = FALSE;
appWindow->maxHorz = FALSE;
appWindow->minimized = FALSE;
appWindow->rail_ignore_configure = FALSE;
appWindow->handle = XCreateWindow(xfc->display, RootWindowOfScreen(xfc->screen), appWindow->x,
appWindow->y, appWindow->width, appWindow->height, 0,
@ -930,6 +934,9 @@ void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int wid
void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(appWindow);
switch (state)
{
case WINDOW_HIDE:
@ -937,11 +944,14 @@ void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state)
break;
case WINDOW_SHOW_MINIMIZED:
appWindow->minimized = TRUE;
XIconifyWindow(xfc->display, appWindow->handle, xfc->screen_number);
break;
case WINDOW_SHOW_MAXIMIZED:
/* Set the window as maximized */
appWindow->maxHorz = TRUE;
appWindow->maxVert = TRUE;
xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, _NET_WM_STATE_ADD,
xfc->_NET_WM_STATE_MAXIMIZED_VERT, xfc->_NET_WM_STATE_MAXIMIZED_HORZ,
0);

View File

@ -126,6 +126,11 @@ struct xf_app_window
UINT32 localWindowOffsetCorrX;
UINT32 localWindowOffsetCorrY;
UINT32 resizeMarginLeft;
UINT32 resizeMarginTop;
UINT32 resizeMarginRight;
UINT32 resizeMarginBottom;
GC gc;
int shmid;
@ -137,6 +142,9 @@ struct xf_app_window
BOOL is_transient;
xfLocalMove local_move;
BYTE rail_state;
BOOL maxVert;
BOOL maxHorz;
BOOL minimized;
BOOL rail_ignore_configure;
};

View File

@ -782,7 +782,7 @@ BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp*
if (freerdp_reconnect(instance))
return TRUE;
switch (freerdp_get_last_error(instance))
switch (freerdp_get_last_error(instance->context))
{
case FREERDP_ERROR_CONNECT_CANCELLED:
WLog_WARN(TAG, "Autoreconnect aborted by user");

View File

@ -211,6 +211,7 @@ static BOOL copy_value(const char* value, char** dst)
static BOOL append_value(const char* value, char** dst)
{
size_t x = 0, y;
size_t size;
char* tmp;
if (!dst || !value)
return FALSE;
@ -218,14 +219,16 @@ static BOOL append_value(const char* value, char** dst)
if (*dst)
x = strlen(*dst);
y = strlen(value);
tmp = realloc(*dst, x + y + 2);
size = x + y + 2;
tmp = realloc(*dst, size);
if (!tmp)
return FALSE;
if (x == 0)
tmp[0] = '\0';
else
strcat(tmp, ",");
strcat(tmp, value);
winpr_str_append(",", tmp, size, NULL);
winpr_str_append(value, tmp, size, NULL);
*dst = tmp;
return TRUE;
}
@ -2915,6 +2918,12 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
settings->TlsSecLevel = (UINT32)val;
}
CommandLineSwitchCase(arg, "enforce-tlsv1_2")
{
if (!(freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_2_VERSION) &&
freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, TLS1_2_VERSION)))
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
CommandLineSwitchCase(arg, "cert")
{
int rc = 0;

View File

@ -353,6 +353,9 @@ static const COMMAND_LINE_ARGUMENT_A args[] = {
"Allowed TLS ciphers" },
{ "tls-seclevel", COMMAND_LINE_VALUE_REQUIRED, "<level>", "1", NULL, -1, NULL,
"TLS security level - defaults to 1" },
{ "enforce-tlsv1_2", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
"Force use of TLS1.2 for connection. Some servers have a buggy TLS version negotiation and "
"might fail without this" },
{ "toggle-fullscreen", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
"Alt+Ctrl+Enter to toggle fullscreen" },
{ "tune", COMMAND_LINE_VALUE_REQUIRED, "<setting:value>,<setting:value>", "", NULL, -1, NULL,

View File

@ -111,6 +111,9 @@
#cmakedefine CHANNEL_RDPDR
#cmakedefine CHANNEL_RDPDR_CLIENT
#cmakedefine CHANNEL_RDPDR_SERVER
#cmakedefine CHANNEL_RDPECAM
#cmakedefine CHANNEL_RDPECAM_CLIENT
#cmakedefine CHANNEL_RDPECAM_SERVER
#cmakedefine CHANNEL_RDPEI
#cmakedefine CHANNEL_RDPEI_CLIENT
#cmakedefine CHANNEL_RDPEI_SERVER
@ -132,6 +135,9 @@
#cmakedefine CHANNEL_SSHAGENT
#cmakedefine CHANNEL_SSHAGENT_CLIENT
#cmakedefine CHANNEL_SSHAGENT_SERVER
#cmakedefine CHANNEL_TELEMETRY
#cmakedefine CHANNEL_TELEMETRY_CLIENT
#cmakedefine CHANNEL_TELEMETRY_SERVER
#cmakedefine CHANNEL_TSMF
#cmakedefine CHANNEL_TSMF_CLIENT
#cmakedefine CHANNEL_TSMF_SERVER

View File

@ -22,6 +22,7 @@
#include <freerdp/api.h>
#include <freerdp/types.h>
#include <freerdp/utils/cliprdr_utils.h>
#include <winpr/shell.h>
@ -93,17 +94,6 @@ extern "C"
{
#endif
FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
FILEDESCRIPTORW** file_descriptor_array,
UINT32* file_descriptor_count);
FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length);
FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count,
BYTE** format_data, UINT32* format_data_length);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,336 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_RDPECAM_H
#define FREERDP_CHANNEL_RDPECAM_H
#include <freerdp/api.h>
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#define RDPECAM_DVC_CHANNEL_NAME "rdpecam"
#define RDPECAM_CONTROL_DVC_CHANNEL_NAME "RDCamera_Device_Enumerator"
typedef enum
{
CAM_MSG_ID_SuccessResponse = 0x01,
CAM_MSG_ID_ErrorResponse = 0x02,
CAM_MSG_ID_SelectVersionRequest = 0x03,
CAM_MSG_ID_SelectVersionResponse = 0x04,
CAM_MSG_ID_DeviceAddedNotification = 0x05,
CAM_MSG_ID_DeviceRemovedNotification = 0x06,
CAM_MSG_ID_ActivateDeviceRequest = 0x07,
CAM_MSG_ID_DeactivateDeviceRequest = 0x08,
CAM_MSG_ID_StreamListRequest = 0x09,
CAM_MSG_ID_StreamListResponse = 0x0A,
CAM_MSG_ID_MediaTypeListRequest = 0x0B,
CAM_MSG_ID_MediaTypeListResponse = 0x0C,
CAM_MSG_ID_CurrentMediaTypeRequest = 0x0D,
CAM_MSG_ID_CurrentMediaTypeResponse = 0x0E,
CAM_MSG_ID_StartStreamsRequest = 0x0F,
CAM_MSG_ID_StopStreamsRequest = 0x10,
CAM_MSG_ID_SampleRequest = 0x11,
CAM_MSG_ID_SampleResponse = 0x12,
CAM_MSG_ID_SampleErrorResponse = 0x13,
CAM_MSG_ID_PropertyListRequest = 0x14,
CAM_MSG_ID_PropertyListResponse = 0x15,
CAM_MSG_ID_PropertyValueRequest = 0x16,
CAM_MSG_ID_PropertyValueResponse = 0x17,
CAM_MSG_ID_SetPropertyValueRequest = 0x18,
} CAM_MSG_ID;
#define CAM_HEADER_SIZE 2
typedef struct
{
BYTE Version;
CAM_MSG_ID MessageId;
} CAM_SHARED_MSG_HEADER;
/* Messages Exchanged on the Device Enumeration Channel (2.2.2) */
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_SELECT_VERSION_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_SELECT_VERSION_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
WCHAR* DeviceName;
char* VirtualChannelName;
} CAM_DEVICE_ADDED_NOTIFICATION;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
char* VirtualChannelName;
} CAM_DEVICE_REMOVED_NOTIFICATION;
/* Messages Exchanged on Device Channels (2.2.3) */
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_SUCCESS_RESPONSE;
typedef enum
{
CAM_ERROR_CODE_UnexpectedError = 0x00000001,
CAM_ERROR_CODE_InvalidMessage = 0x00000002,
CAM_ERROR_CODE_NotInitialized = 0x00000003,
CAM_ERROR_CODE_InvalidRequest = 0x00000004,
CAM_ERROR_CODE_InvalidStreamNumber = 0x00000005,
CAM_ERROR_CODE_InvalidMediaType = 0x00000006,
CAM_ERROR_CODE_OutOfMemory = 0x00000007,
CAM_ERROR_CODE_ItemNotFound = 0x00000008,
CAM_ERROR_CODE_SetNotFound = 0x00000009,
CAM_ERROR_CODE_OperationNotSupported = 0x0000000A,
} CAM_ERROR_CODE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
CAM_ERROR_CODE ErrorCode;
} CAM_ERROR_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_ACTIVATE_DEVICE_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_DEACTIVATE_DEVICE_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_STREAM_LIST_REQUEST;
typedef enum
{
CAM_STREAM_FRAME_SOURCE_TYPE_Color = 0x0001,
CAM_STREAM_FRAME_SOURCE_TYPE_Infrared = 0x0002,
CAM_STREAM_FRAME_SOURCE_TYPE_Custom = 0x0008,
} CAM_STREAM_FRAME_SOURCE_TYPES;
typedef enum
{
CAM_STREAM_CATEGORY_Capture = 0x01,
} CAM_STREAM_CATEGORY;
typedef struct
{
CAM_STREAM_FRAME_SOURCE_TYPES FrameSourceTypes;
CAM_STREAM_CATEGORY StreamCategory;
BYTE Selected;
BYTE CanBeShared;
} CAM_STREAM_DESCRIPTION;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE N_Descriptions;
CAM_STREAM_DESCRIPTION StreamDescriptions[255];
} CAM_STREAM_LIST_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE StreamIndex;
} CAM_MEDIA_TYPE_LIST_REQUEST;
typedef enum
{
CAM_MEDIA_FORMAT_H264 = 0x01,
CAM_MEDIA_FORMAT_MJPG = 0x02,
CAM_MEDIA_FORMAT_YUY2 = 0x03,
CAM_MEDIA_FORMAT_NV12 = 0x04,
CAM_MEDIA_FORMAT_I420 = 0x05,
CAM_MEDIA_FORMAT_RGB24 = 0x06,
CAM_MEDIA_FORMAT_RGB32 = 0x07,
} CAM_MEDIA_FORMAT;
typedef enum
{
CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired = 0x01,
CAM_MEDIA_TYPE_DESCRIPTION_FLAG_BottomUpImage = 0x02,
} CAM_MEDIA_TYPE_DESCRIPTION_FLAGS;
typedef struct
{
CAM_MEDIA_FORMAT Format;
UINT32 Width;
UINT32 Height;
UINT32 FrameRateNumerator;
UINT32 FrameRateDenominator;
UINT32 PixelAspectRatioNumerator;
UINT32 PixelAspectRatioDenominator;
CAM_MEDIA_TYPE_DESCRIPTION_FLAGS Flags;
} CAM_MEDIA_TYPE_DESCRIPTION;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
size_t N_Descriptions;
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions;
} CAM_MEDIA_TYPE_LIST_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE StreamIndex;
} CAM_CURRENT_MEDIA_TYPE_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription;
} CAM_CURRENT_MEDIA_TYPE_RESPONSE;
typedef struct
{
BYTE StreamIndex;
CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription;
} CAM_START_STREAM_INFO;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE N_Infos;
CAM_START_STREAM_INFO StartStreamsInfo[255];
} CAM_START_STREAMS_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_STOP_STREAMS_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE StreamIndex;
} CAM_SAMPLE_REQUEST;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE StreamIndex;
size_t SampleSize;
BYTE* Sample;
} CAM_SAMPLE_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
BYTE StreamIndex;
CAM_ERROR_CODE ErrorCode;
} CAM_SAMPLE_ERROR_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
} CAM_PROPERTY_LIST_REQUEST;
typedef enum
{
CAM_PROPERTY_SET_CameraControl = 0x01,
CAM_PROPERTY_SET_VideoProcAmp = 0x02,
} CAM_PROPERTY_SET;
/* CameraControl properties */
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Exposure 0x01
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Focus 0x02
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Pan 0x03
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Roll 0x04
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Tilt 0x05
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Zoom 0x06
/* VideoProcAmp properties */
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_BacklightCompensation 0x01
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Brightness 0x02
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Contrast 0x03
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Hue 0x04
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_WhiteBalance 0x05
typedef enum
{
CAM_PROPERTY_CAPABILITY_Manual = 0x01,
CAM_PROPERTY_CAPABILITY_Auto = 0x02,
} CAM_PROPERTY_CAPABILITIES;
typedef struct
{
CAM_PROPERTY_SET PropertySet;
BYTE PropertyId;
CAM_PROPERTY_CAPABILITIES Capabilities;
INT32 MinValue;
INT32 MaxValue;
INT32 Step;
INT32 DefaultValue;
} CAM_PROPERTY_DESCRIPTION;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
size_t N_Properties;
CAM_PROPERTY_DESCRIPTION* Properties;
} CAM_PROPERTY_LIST_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
CAM_PROPERTY_SET PropertySet;
BYTE PropertyId;
} CAM_PROPERTY_VALUE_REQUEST;
typedef enum
{
CAM_PROPERTY_MODE_Manual = 0x01,
CAM_PROPERTY_MODE_Auto = 0x02,
} CAM_PROPERTY_MODE;
typedef struct
{
CAM_PROPERTY_MODE Mode;
INT32 Value;
} CAM_PROPERTY_VALUE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
CAM_PROPERTY_VALUE PropertyValue;
} CAM_PROPERTY_VALUE_RESPONSE;
typedef struct
{
CAM_SHARED_MSG_HEADER Header;
CAM_PROPERTY_SET PropertySet;
BYTE PropertyId;
CAM_PROPERTY_VALUE PropertyValue;
} CAM_SET_PROPERTY_VALUE_REQUEST;
#endif /* FREERDP_CHANNEL_RDPECAM_H */

View File

@ -99,11 +99,16 @@ typedef struct _RDPGFX_HEADER RDPGFX_HEADER;
#define RDPGFX_CAPVERSION_103 0x000A0301 /** [MS-RDPEGFX] 2.2.3.6 */
#define RDPGFX_CAPVERSION_104 0x000A0400 /** [MS-RDPEGFX] 2.2.3.7 */
#define RDPGFX_CAPVERSION_105 0x000A0502 /** [MS-RDPEGFX] 2.2.3.8 */
#define RDPGFX_CAPVERSION_106 \
0x000A0600 /** [MS-RDPEGFX] 2.2.3.9, [MS-RDPEGFX-errata] 2018-12-10 \
#define RDPGFX_CAPVERSION_106 \
0x000A0600 /** [MS-RDPEGFX] 2.2.3.9 (the value in the doc is wrong, see \
* [MS-RDPEGFX]-180912-errata] \
* Since this is/was documented for a long time, also define \
* the incorrect value in case some server actually uses it. \
*/
#define RDPGFX_CAPVERSION_106_ERR 0x000A0601
#define RDPGFX_CAPVERSION_107 0x000A0701 /** [MS-RDPEGFX] 2.2.3.10 */
#define RDPGFX_NUMBER_CAPSETS 9
#define RDPGFX_NUMBER_CAPSETS 11
#define RDPGFX_CAPSET_BASE_SIZE 8
struct _RDPGFX_CAPSET
@ -119,6 +124,7 @@ typedef struct _RDPGFX_CAPSET RDPGFX_CAPSET;
#define RDPGFX_CAPS_FLAG_AVC420_ENABLED 0x00000010U /* 8.1+ */
#define RDPGFX_CAPS_FLAG_AVC_DISABLED 0x00000020U /* 10.0+ */
#define RDPGFX_CAPS_FLAG_AVC_THINCLIENT 0x00000040U /* 10.3+ */
#define RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE 0x00000080U /* 10.7+ */
struct _RDPGFX_CAPSET_VERSION8
{

View File

@ -0,0 +1,38 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Telemetry Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_TELEMETRY_H
#define FREERDP_CHANNEL_TELEMETRY_H
#include <freerdp/api.h>
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#define TELEMETRY_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::Telemetry"
struct _TELEMETRY_RDP_TELEMETRY_PDU
{
UINT32 PromptForCredentialsMillis;
UINT32 PromptForCredentialsDoneMillis;
UINT32 GraphicsChannelOpenedMillis;
UINT32 FirstGraphicsReceivedMillis;
};
typedef struct _TELEMETRY_RDP_TELEMETRY_PDU TELEMETRY_RDP_TELEMETRY_PDU;
#endif /* FREERDP_CHANNEL_TELEMETRY_H */

View File

@ -52,6 +52,9 @@ extern "C"
DRDYNVC_STATE_FAILED = 3
};
typedef BOOL (*psDVCCreationStatusCallback)(void* userdata, UINT32 channelId,
INT32 creationStatus);
/**
* WTSVirtualChannelManager functions are FreeRDP extensions to the API.
*/
@ -62,6 +65,9 @@ extern "C"
FREERDP_API HANDLE WTSVirtualChannelManagerGetEventHandle(HANDLE hServer);
FREERDP_API BOOL WTSVirtualChannelManagerIsChannelJoined(HANDLE hServer, const char* name);
FREERDP_API BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer);
FREERDP_API void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer,
psDVCCreationStatusCallback cb,
void* userdata);
/**
* Extended FreeRDP WTS functions for channel handling
@ -76,6 +82,10 @@ extern "C"
FREERDP_API void* WTSChannelGetHandleByName(freerdp_peer* client, const char* channel_name);
FREERDP_API void* WTSChannelGetHandleById(freerdp_peer* client, const UINT16 channel_id);
FREERDP_API const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id);
FREERDP_API INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id);
FREERDP_API char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count);
FREERDP_API UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle);
#ifdef __cplusplus
}

View File

@ -86,6 +86,8 @@ typedef UINT (*pcRailServerGetAppidResponseExtended)(RailClientContext* context,
const RAIL_GET_APPID_RESP_EX* id);
typedef UINT (*pcRailClientCompartmentInfo)(RailClientContext* context,
const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo);
typedef UINT (*pcRailClientTextScale)(RailClientContext* context, UINT32 TextScale);
typedef UINT (*pcRailClientCaretBlinkRate)(RailClientContext* context, UINT32 CaretBlinkRate);
struct _rail_client_context
{
@ -121,6 +123,8 @@ struct _rail_client_context
pcRailServerGetAppidResponseExtended ServerGetAppidResponseExtended;
pcRailClientCompartmentInfo ClientCompartmentInfo;
pcRailOnOpen OnOpen;
pcRailClientTextScale ClientTextScale;
pcRailClientCaretBlinkRate ClientCaretBlinkRate;
};
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_RAIL_H */

View File

@ -45,6 +45,10 @@ enum RDP_CODEC_ID
#define OSMAJORTYPE_OS2 0x0002
#define OSMAJORTYPE_MACINTOSH 0x0003
#define OSMAJORTYPE_UNIX 0x0004
#define OSMAJORTYPE_IOS 0x0005
#define OSMAJORTYPE_OSX 0x0006
#define OSMAJORTYPE_ANDROID 0x0007
#define OSMAJORTYPE_CHROME_OS 0x0008
/**
* OSMinorType
@ -58,6 +62,8 @@ enum RDP_CODEC_ID
#define OSMINORTYPE_MACINTOSH 0x0006
#define OSMINORTYPE_NATIVE_XSERVER 0x0007
#define OSMINORTYPE_PSEUDO_XSERVER 0x0008
#define OSMINORTYPE_NATIVE_WAYLAND 0x0009
#define OSMINORTYPE_WINDOWS_RT 0x0009
/* As of 2022-03-29 the following does not exist officially in [MS-RDPBCGR] */
#define OSMINORTYPE_NATIVE_WAYLAND (0xFFFF - 1)
#endif /* FREERDP_CONSTANTS_H */

View File

@ -143,6 +143,9 @@ extern "C"
FREERDP_API BOOL freerdp_peer_context_new(freerdp_peer* client);
FREERDP_API void freerdp_peer_context_free(freerdp_peer* client);
FREERDP_API const char* freerdp_peer_os_major_type_string(freerdp_peer* client);
FREERDP_API const char* freerdp_peer_os_minor_type_string(freerdp_peer* client);
FREERDP_API freerdp_peer* freerdp_peer_new(int sockfd);
FREERDP_API void freerdp_peer_free(freerdp_peer* client);

View File

@ -135,15 +135,19 @@ enum SPI_MASK
#endif
/* Client Information PDU */
#define TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE 0x00000001
#define TS_RAIL_CLIENTSTATUS_AUTORECONNECT 0x00000002
#define TS_RAIL_CLIENTSTATUS_ZORDER_SYNC 0x00000004
#define TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED 0x00000010
#define TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED 0x00000020
#define TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED 0x00000040
#define TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED 0x00000080
#define TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED 0x00000100
#define TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED 0x00000200
typedef enum
{
TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE = 0x00000001,
TS_RAIL_CLIENTSTATUS_AUTORECONNECT = 0x00000002,
TS_RAIL_CLIENTSTATUS_ZORDER_SYNC = 0x00000004,
TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED = 0x00000010,
TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED = 0x00000020,
TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED = 0x00000040,
TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED = 0x00000080,
TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED = 0x00000100,
TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED = 0x00000200,
TS_RAIL_CLIENTSTATUS_SUPPRESS_ICON_ORDERS = 0x00000400
} CLIENT_INFO_PDU;
/* Server Move/Size Start PDU */
#define RAIL_WMSZ_LEFT 0x0001
@ -182,10 +186,15 @@ enum SPI_MASK
#endif
/* Extended Handshake Flags */
#define TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF 0x00000001
#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED 0x00000002
#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED 0x00000004
typedef enum
{
TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF = 0x00000001,
TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED = 0x00000002,
TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED = 0x00000004,
TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_TEXT_SCALE_SUPPORTED = 0x00000008,
TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_CARET_BLINK_SUPPORTED = 0x00000010,
TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_2_SUPPORTED = 0x00000020
} EXTENDED_HANDSHAKE_FLAGS;
/* Language Profile Information Flags */
#define TF_PROFILETYPE_INPUTPROCESSOR 0x00000001
#define TF_PROFILETYPE_KEYBOARDLAYOUT 0x00000002
@ -560,30 +569,35 @@ typedef struct _RAIL_GET_APPID_RESP_EX RAIL_GET_APPID_RESP_EX;
/* RAIL Constants */
#define TS_RAIL_ORDER_EXEC 0x0001
#define TS_RAIL_ORDER_ACTIVATE 0x0002
#define TS_RAIL_ORDER_SYSPARAM 0x0003
#define TS_RAIL_ORDER_SYSCOMMAND 0x0004
#define TS_RAIL_ORDER_HANDSHAKE 0x0005
#define TS_RAIL_ORDER_NOTIFY_EVENT 0x0006
#define TS_RAIL_ORDER_WINDOWMOVE 0x0008
#define TS_RAIL_ORDER_LOCALMOVESIZE 0x0009
#define TS_RAIL_ORDER_MINMAXINFO 0x000A
#define TS_RAIL_ORDER_CLIENTSTATUS 0x000B
#define TS_RAIL_ORDER_SYSMENU 0x000C
#define TS_RAIL_ORDER_LANGBARINFO 0x000D
#define TS_RAIL_ORDER_GET_APPID_REQ 0x000E
#define TS_RAIL_ORDER_GET_APPID_RESP 0x000F
#define TS_RAIL_ORDER_TASKBARINFO 0x0010
#define TS_RAIL_ORDER_LANGUAGEIMEINFO 0x0011
#define TS_RAIL_ORDER_COMPARTMENTINFO 0x0012
#define TS_RAIL_ORDER_HANDSHAKE_EX 0x0013
#define TS_RAIL_ORDER_ZORDER_SYNC 0x0014
#define TS_RAIL_ORDER_CLOAK 0x0015
#define TS_RAIL_ORDER_POWER_DISPLAY_REQUEST 0x0016
#define TS_RAIL_ORDER_SNAP_ARRANGE 0x0017
#define TS_RAIL_ORDER_GET_APPID_RESP_EX 0x0018
#define TS_RAIL_ORDER_EXEC_RESULT 0x0080
typedef enum
{
TS_RAIL_ORDER_EXEC = 0x0001,
TS_RAIL_ORDER_ACTIVATE = 0x0002,
TS_RAIL_ORDER_SYSPARAM = 0x0003,
TS_RAIL_ORDER_SYSCOMMAND = 0x0004,
TS_RAIL_ORDER_HANDSHAKE = 0x0005,
TS_RAIL_ORDER_NOTIFY_EVENT = 0x0006,
TS_RAIL_ORDER_WINDOWMOVE = 0x0008,
TS_RAIL_ORDER_LOCALMOVESIZE = 0x0009,
TS_RAIL_ORDER_MINMAXINFO = 0x000A,
TS_RAIL_ORDER_CLIENTSTATUS = 0x000B,
TS_RAIL_ORDER_SYSMENU = 0x000C,
TS_RAIL_ORDER_LANGBARINFO = 0x000D,
TS_RAIL_ORDER_GET_APPID_REQ = 0x000E,
TS_RAIL_ORDER_GET_APPID_RESP = 0x000F,
TS_RAIL_ORDER_TASKBARINFO = 0x0010,
TS_RAIL_ORDER_LANGUAGEIMEINFO = 0x0011,
TS_RAIL_ORDER_COMPARTMENTINFO = 0x0012,
TS_RAIL_ORDER_HANDSHAKE_EX = 0x0013,
TS_RAIL_ORDER_ZORDER_SYNC = 0x0014,
TS_RAIL_ORDER_CLOAK = 0x0015,
TS_RAIL_ORDER_POWER_DISPLAY_REQUEST = 0x0016,
TS_RAIL_ORDER_SNAP_ARRANGE = 0x0017,
TS_RAIL_ORDER_GET_APPID_RESP_EX = 0x0018,
TS_RAIL_ORDER_TEXTSCALEINFO = 0x0019,
TS_RAIL_ORDER_CARETBLINKINFO = 0x001A,
TS_RAIL_ORDER_EXEC_RESULT = 0x0080
} ORDER_TYPE;
#ifdef __cplusplus
extern "C"

View File

@ -22,6 +22,7 @@
#define FREERDP_CHANNEL_AINPUT_SERVER_H
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/ainput.h>
typedef enum AINPUT_SERVER_OPEN_RESULT
{
@ -33,6 +34,8 @@ typedef enum AINPUT_SERVER_OPEN_RESULT
typedef struct _ainput_server_context ainput_server_context;
typedef BOOL (*psAInputChannelIdAssigned)(ainput_server_context* context, UINT32 channelId);
typedef UINT (*psAInputServerInitialize)(ainput_server_context* context, BOOL externalThread);
typedef UINT (*psAInputServerPoll)(ainput_server_context* context);
typedef BOOL (*psAInputServerChannelHandle)(ainput_server_context* context, HANDLE* handle);
@ -97,6 +100,11 @@ struct _ainput_server_context
psAInputServerMouseEvent MouseEvent;
rdpContext* rdpcontext;
/**
* Callback, when the channel got its id assigned.
*/
psAInputChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -28,6 +28,8 @@
typedef struct _audin_server_context audin_server_context;
typedef BOOL (*psAudinServerChannelIdAssigned)(audin_server_context* context, UINT32 channelId);
typedef UINT (*psAudinServerSelectFormat)(audin_server_context* context,
size_t client_format_index);
typedef BOOL (*psAudinServerOpen)(audin_server_context* context);
@ -99,6 +101,11 @@ struct _audin_server_context
psAudinServerReceiveSamples ReceiveSamples;
rdpContext* rdpcontext;
/**
* Callback, when the channel got its id assigned.
*/
psAudinServerChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -29,6 +29,8 @@
typedef struct _disp_server_private DispServerPrivate;
typedef struct _disp_server_context DispServerContext;
typedef BOOL (*psDispChannelIdAssigned)(DispServerContext* context, UINT32 channelId);
typedef UINT (*psDispMonitorLayout)(DispServerContext* context,
const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU* pdu);
typedef UINT (*psDispCaps)(DispServerContext* context);
@ -53,6 +55,11 @@ struct _disp_server_context
DispServerPrivate* priv;
rdpContext* rdpcontext;
/**
* Callback, when the channel got its id assigned.
*/
psDispChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -34,6 +34,8 @@ typedef enum ECHO_SERVER_OPEN_RESULT
typedef struct _echo_server_context echo_server_context;
typedef BOOL (*psEchoServerChannelIdAssigned)(echo_server_context* context, UINT32 channelId);
typedef UINT (*psEchoServerOpen)(echo_server_context* context);
typedef UINT (*psEchoServerClose)(echo_server_context* context);
typedef BOOL (*psEchoServerRequest)(echo_server_context* context, const BYTE* buffer,
@ -76,6 +78,11 @@ struct _echo_server_context
psEchoServerResponse Response;
rdpContext* rdpcontext;
/**
* Callback, when the channel got its id assigned.
*/
psEchoServerChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -0,0 +1,134 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H
#define FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H
#include <freerdp/channels/rdpecam.h>
#include <freerdp/channels/wtsvc.h>
typedef struct _cam_dev_enum_server_context CamDevEnumServerContext;
typedef UINT (*psCamDevEnumServerServerOpen)(CamDevEnumServerContext* context);
typedef UINT (*psCamDevEnumServerServerClose)(CamDevEnumServerContext* context);
typedef BOOL (*psCamDevEnumServerServerChannelIdAssigned)(CamDevEnumServerContext* context,
UINT32 channelId);
typedef UINT (*psCamDevEnumServerServerInitialize)(CamDevEnumServerContext* context,
BOOL externalThread);
typedef UINT (*psCamDevEnumServerServerPoll)(CamDevEnumServerContext* context);
typedef BOOL (*psCamDevEnumServerServerChannelHandle)(CamDevEnumServerContext* context,
HANDLE* handle);
typedef UINT (*psCamDevEnumServerServerSelectVersionRequest)(
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_REQUEST* selectVersionRequest);
typedef UINT (*psCamDevEnumServerServerSelectVersionResponse)(
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse);
typedef UINT (*psCamDevEnumServerServerDeviceAddedNotification)(
CamDevEnumServerContext* context, const CAM_DEVICE_ADDED_NOTIFICATION* deviceAddedNotification);
typedef UINT (*psCamDevEnumServerServerDeviceRemovedNotification)(
CamDevEnumServerContext* context,
const CAM_DEVICE_REMOVED_NOTIFICATION* deviceRemovedNotification);
struct _cam_dev_enum_server_context
{
HANDLE vcm;
/* Server self-defined pointer. */
void* userdata;
/*** APIs called by the server. ***/
/**
* Optional: Set thread handling.
* When externalThread=TRUE, the application is responsible to call
* Poll() periodically to process channel events.
*
* Defaults to externalThread=FALSE
*/
psCamDevEnumServerServerInitialize Initialize;
/**
* Open the camera device enumerator channel.
*/
psCamDevEnumServerServerOpen Open;
/**
* Close the camera device enumerator channel.
*/
psCamDevEnumServerServerClose Close;
/**
* Poll
* When externalThread=TRUE, call Poll() periodically from your main loop.
* If externalThread=FALSE do not call.
*/
psCamDevEnumServerServerPoll Poll;
/**
* Retrieve the channel handle for use in conjunction with Poll().
* If externalThread=FALSE do not call.
*/
psCamDevEnumServerServerChannelHandle ChannelHandle;
/*
* Send a Select Version Response PDU.
*/
psCamDevEnumServerServerSelectVersionResponse SelectVersionResponse;
/*** Callbacks registered by the server. ***/
/**
* Callback, when the channel got its id assigned.
*/
psCamDevEnumServerServerChannelIdAssigned ChannelIdAssigned;
/**
* Callback for the Select Version Request PDU.
*/
psCamDevEnumServerServerSelectVersionRequest SelectVersionRequest;
/**
* Callback for the Device Added Notification PDU.
*/
psCamDevEnumServerServerDeviceAddedNotification DeviceAddedNotification;
/**
* Callback for the Device Removed Notification PDU.
*/
psCamDevEnumServerServerDeviceRemovedNotification DeviceRemovedNotification;
rdpContext* rdpcontext;
};
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm);
FREERDP_API void cam_dev_enum_server_context_free(CamDevEnumServerContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H */

View File

@ -0,0 +1,278 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Video Capture Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H
#define FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H
#include <freerdp/channels/rdpecam.h>
#include <freerdp/channels/wtsvc.h>
typedef struct camera_device_server_context CameraDeviceServerContext;
typedef UINT (*psCameraDeviceServerOpen)(CameraDeviceServerContext* context);
typedef UINT (*psCameraDeviceServerClose)(CameraDeviceServerContext* context);
typedef BOOL (*psCameraDeviceServerChannelIdAssigned)(CameraDeviceServerContext* context,
UINT32 channelId);
typedef UINT (*psCameraDeviceServerInitialize)(CameraDeviceServerContext* context,
BOOL externalThread);
typedef UINT (*psCameraDeviceServerPoll)(CameraDeviceServerContext* context);
typedef BOOL (*psCameraDeviceServerChannelHandle)(CameraDeviceServerContext* context,
HANDLE* handle);
typedef UINT (*psCameraDeviceServerSuccessResponse)(CameraDeviceServerContext* context,
const CAM_SUCCESS_RESPONSE* successResponse);
typedef UINT (*psCameraDeviceServerErrorResponse)(CameraDeviceServerContext* context,
const CAM_ERROR_RESPONSE* errorResponse);
typedef UINT (*psCameraDeviceServerActivateDeviceRequest)(
CameraDeviceServerContext* context, const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest);
typedef UINT (*psCameraDeviceServerDeactivateDeviceRequest)(
CameraDeviceServerContext* context,
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest);
typedef UINT (*psCameraDeviceServerStreamListRequest)(
CameraDeviceServerContext* context, const CAM_STREAM_LIST_REQUEST* streamListRequest);
typedef UINT (*psCameraDeviceServerStreamListResponse)(
CameraDeviceServerContext* context, const CAM_STREAM_LIST_RESPONSE* streamListResponse);
typedef UINT (*psCameraDeviceServerMediaTypeListRequest)(
CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest);
typedef UINT (*psCameraDeviceServerMediaTypeListResponse)(
CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_RESPONSE* mediaTypeListResponse);
typedef UINT (*psCameraDeviceServerCurrentMediaTypeRequest)(
CameraDeviceServerContext* context,
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest);
typedef UINT (*psCameraDeviceServerCurrentMediaTypeResponse)(
CameraDeviceServerContext* context,
const CAM_CURRENT_MEDIA_TYPE_RESPONSE* currentMediaTypeResponse);
typedef UINT (*psCameraDeviceServerStartStreamsRequest)(
CameraDeviceServerContext* context, const CAM_START_STREAMS_REQUEST* startStreamsRequest);
typedef UINT (*psCameraDeviceServerStopStreamsRequest)(
CameraDeviceServerContext* context, const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest);
typedef UINT (*psCameraDeviceServerSampleRequest)(CameraDeviceServerContext* context,
const CAM_SAMPLE_REQUEST* sampleRequest);
typedef UINT (*psCameraDeviceServerSampleResponse)(CameraDeviceServerContext* context,
const CAM_SAMPLE_RESPONSE* sampleResponse);
typedef UINT (*psCameraDeviceServerSampleErrorResponse)(
CameraDeviceServerContext* context, const CAM_SAMPLE_ERROR_RESPONSE* sampleErrorResponse);
typedef UINT (*psCameraDeviceServerPropertyListRequest)(
CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_REQUEST* propertyListRequest);
typedef UINT (*psCameraDeviceServerPropertyListResponse)(
CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_RESPONSE* propertyListResponse);
typedef UINT (*psCameraDeviceServerPropertyValueRequest)(
CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest);
typedef UINT (*psCameraDeviceServerPropertyValueResponse)(
CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_RESPONSE* propertyValueResponse);
typedef UINT (*psCameraDeviceServerSetPropertyValueRequest)(
CameraDeviceServerContext* context,
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest);
struct camera_device_server_context
{
HANDLE vcm;
/* Server self-defined pointer. */
void* userdata;
/**
* Name of the virtual channel. Pointer owned by the CameraDeviceServerContext,
* meaning camera_device_server_context_free() takes care of freeing the pointer.
*
* Server implementations should sanitize the virtual channel name for invalid
* names, like names for other known channels
* ("ECHO", "AUDIO_PLAYBACK_DVC", etc.)
*/
char* virtualChannelName;
/**
* Protocol version to be used. Every sent server to client PDU has the
* version value in the Header set to the following value.
*/
BYTE protocolVersion;
/*** APIs called by the server. ***/
/**
* Optional: Set thread handling.
* When externalThread=TRUE, the application is responsible to call
* Poll() periodically to process channel events.
*
* Defaults to externalThread=FALSE
*/
psCameraDeviceServerInitialize Initialize;
/**
* Open the camera device channel.
*/
psCameraDeviceServerOpen Open;
/**
* Close the camera device channel.
*/
psCameraDeviceServerClose Close;
/**
* Poll
* When externalThread=TRUE, call Poll() periodically from your main loop.
* If externalThread=FALSE do not call.
*/
psCameraDeviceServerPoll Poll;
/**
* Retrieve the channel handle for use in conjunction with Poll().
* If externalThread=FALSE do not call.
*/
psCameraDeviceServerChannelHandle ChannelHandle;
/**
* For the following server to client PDUs,
* the message header does not have to be set.
*/
/**
* Send a Activate Device Request PDU.
*/
psCameraDeviceServerActivateDeviceRequest ActivateDeviceRequest;
/**
* Send a Deactivate Device Request PDU.
*/
psCameraDeviceServerDeactivateDeviceRequest DeactivateDeviceRequest;
/**
* Send a Stream List Request PDU.
*/
psCameraDeviceServerStreamListRequest StreamListRequest;
/**
* Send a Media Type List Request PDU.
*/
psCameraDeviceServerMediaTypeListRequest MediaTypeListRequest;
/**
* Send a Current Media Type Request PDU.
*/
psCameraDeviceServerCurrentMediaTypeRequest CurrentMediaTypeRequest;
/**
* Send a Start Streams Request PDU.
*/
psCameraDeviceServerStartStreamsRequest StartStreamsRequest;
/**
* Send a Stop Streams Request PDU.
*/
psCameraDeviceServerStopStreamsRequest StopStreamsRequest;
/**
* Send a Sample Request PDU.
*/
psCameraDeviceServerSampleRequest SampleRequest;
/**
* Send a Property List Request PDU.
*/
psCameraDeviceServerPropertyListRequest PropertyListRequest;
/**
* Send a Property Value Request PDU.
*/
psCameraDeviceServerPropertyValueRequest PropertyValueRequest;
/**
* Send a Set Property Value Request PDU.
*/
psCameraDeviceServerSetPropertyValueRequest SetPropertyValueRequest;
/*** Callbacks registered by the server. ***/
/**
* Callback, when the channel got its id assigned.
*/
psCameraDeviceServerChannelIdAssigned ChannelIdAssigned;
/**
* Callback for the Success Response PDU.
*/
psCameraDeviceServerSuccessResponse SuccessResponse;
/**
* Callback for the Error Response PDU.
*/
psCameraDeviceServerErrorResponse ErrorResponse;
/**
* Callback for the Stream List Response PDU.
*/
psCameraDeviceServerStreamListResponse StreamListResponse;
/**
* Callback for the Media Type List Response PDU.
*/
psCameraDeviceServerMediaTypeListResponse MediaTypeListResponse;
/**
* Callback for the Current Media Type Response PDU.
*/
psCameraDeviceServerCurrentMediaTypeResponse CurrentMediaTypeResponse;
/**
* Callback for the Sample Response PDU.
*/
psCameraDeviceServerSampleResponse SampleResponse;
/**
* Callback for the Sample Error Response PDU.
*/
psCameraDeviceServerSampleErrorResponse SampleErrorResponse;
/**
* Callback for the Property List Response PDU.
*/
psCameraDeviceServerPropertyListResponse PropertyListResponse;
/**
* Callback for the Property Value Response PDU.
*/
psCameraDeviceServerPropertyValueResponse PropertyValueResponse;
rdpContext* rdpcontext;
};
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm);
FREERDP_API void camera_device_server_context_free(CameraDeviceServerContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H */

View File

@ -47,6 +47,11 @@ struct _rdpei_server_context
void* user_data; /* user data, useful for callbacks */
UINT (*onPenEvent)(RdpeiServerContext* context, const RDPINPUT_PEN_EVENT* penEvent);
/**
* Callback, when the channel got its id assigned.
*/
BOOL (*onChannelIdAssigned)(RdpeiServerContext* context, UINT32 channelId);
};
#ifdef __cplusplus

View File

@ -29,6 +29,8 @@ typedef struct _rdpgfx_server_private RdpgfxServerPrivate;
typedef BOOL (*psRdpgfxServerOpen)(RdpgfxServerContext* context);
typedef BOOL (*psRdpgfxServerClose)(RdpgfxServerContext* context);
typedef BOOL (*psRdpgfxServerChannelIdAssigned)(RdpgfxServerContext* context, UINT32 channelId);
typedef UINT (*psRdpgfxResetGraphics)(RdpgfxServerContext* context,
const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics);
typedef UINT (*psRdpgfxStartFrame)(RdpgfxServerContext* context,
@ -112,6 +114,11 @@ struct _rdpgfx_server_context
RdpgfxServerPrivate* priv;
rdpContext* rdpcontext;
/**
* Callback, when the channel got its id assigned.
*/
psRdpgfxServerChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -32,11 +32,21 @@ typedef struct _rdpsnd_server_private RdpsndServerPrivate;
typedef UINT (*psRdpsndStart)(RdpsndServerContext* context);
typedef UINT (*psRdpsndStop)(RdpsndServerContext* context);
typedef BOOL (*psRdpsndChannelIdAssigned)(RdpsndServerContext* context, UINT32 channelId);
typedef UINT (*psRdpsndServerInitialize)(RdpsndServerContext* context, BOOL ownThread);
typedef UINT (*psRdpsndServerSendFormats)(RdpsndServerContext* context);
typedef UINT (*psRdpsndServerSelectFormat)(RdpsndServerContext* context,
UINT16 client_format_index);
typedef UINT (*psRdpsndServerTraining)(RdpsndServerContext* context, UINT16 timestamp,
UINT16 packsize, BYTE* data);
typedef UINT (*psRdpsndServerTrainingConfirm)(RdpsndServerContext* context, UINT16 timestamp,
UINT16 packsize);
typedef UINT (*psRdpsndServerSendSamples)(RdpsndServerContext* context, const void* buf,
int nframes, UINT16 wTimestamp);
typedef UINT (*psRdpsndServerSendSamples2)(RdpsndServerContext* context, UINT16 formatNo,
const void* buf, size_t size, UINT16 timestamp,
UINT32 audioTimeStamp);
typedef UINT (*psRdpsndServerConfirmBlock)(RdpsndServerContext* context, BYTE confirmBlockNum,
UINT16 wtimestamp);
typedef UINT (*psRdpsndServerSetVolume)(RdpsndServerContext* context, int left, int right);
@ -122,6 +132,43 @@ struct _rdpsnd_server_context
/* Server to request to use dynamic virtual channel. */
BOOL use_dynamic_virtual_channel;
/* dwFlags in CLIENT_AUDIO_VERSION_AND_FORMATS */
UINT32 capsFlags;
/* dwVolume in CLIENT_AUDIO_VERSION_AND_FORMATS */
UINT32 initialVolume;
/* dwPitch in CLIENT_AUDIO_VERSION_AND_FORMATS */
UINT32 initialPitch;
UINT16 qualityMode;
/**
* Send server formats and version to the client. Automatically sent, when
* opening the channel.
* Also used to restart the protocol after sending the Close PDU.
*/
psRdpsndServerSendFormats SendFormats;
/**
* Send Training PDU.
*/
psRdpsndServerTraining Training;
/**
* Send encoded audio samples using a Wave2 PDU.
* When successful, the block_no member is incremented.
*/
psRdpsndServerSendSamples2 SendSamples2;
/**
* Called when a TrainingConfirm PDU is received from the client.
*/
psRdpsndServerTrainingConfirm TrainingConfirm;
/**
* Callback, when the channel got its id assigned.
* Only called, when use_dynamic_virtual_channel=TRUE.
*/
psRdpsndChannelIdAssigned ChannelIdAssigned;
};
#ifdef __cplusplus

View File

@ -0,0 +1,108 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Telemetry Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H
#define FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H
#include <freerdp/channels/telemetry.h>
#include <freerdp/channels/wtsvc.h>
typedef struct _telemetry_server_context TelemetryServerContext;
typedef UINT (*psTelemetryServerOpen)(TelemetryServerContext* context);
typedef UINT (*psTelemetryServerClose)(TelemetryServerContext* context);
typedef BOOL (*psTelemetryServerChannelIdAssigned)(TelemetryServerContext* context,
UINT32 channelId);
typedef UINT (*psTelemetryServerInitialize)(TelemetryServerContext* context, BOOL externalThread);
typedef UINT (*psTelemetryServerPoll)(TelemetryServerContext* context);
typedef BOOL (*psTelemetryServerChannelHandle)(TelemetryServerContext* context, HANDLE* handle);
typedef UINT (*psTelemetryServerRdpTelemetry)(TelemetryServerContext* context,
const TELEMETRY_RDP_TELEMETRY_PDU* rdpTelemetry);
struct _telemetry_server_context
{
HANDLE vcm;
/* Server self-defined pointer. */
void* userdata;
/*** APIs called by the server. ***/
/**
* Optional: Set thread handling.
* When externalThread=TRUE, the application is responsible to call
* Poll() periodically to process channel events.
*
* Defaults to externalThread=FALSE
*/
psTelemetryServerInitialize Initialize;
/**
* Open the telemetry channel.
*/
psTelemetryServerOpen Open;
/**
* Close the telemetry channel.
*/
psTelemetryServerClose Close;
/**
* Poll
* When externalThread=TRUE, call Poll() periodically from your main loop.
* If externalThread=FALSE do not call.
*/
psTelemetryServerPoll Poll;
/**
* Retrieve the channel handle for use in conjunction with Poll().
* If externalThread=FALSE do not call.
*/
psTelemetryServerChannelHandle ChannelHandle;
/*** Callbacks registered by the server. ***/
/**
* Callback, when the channel got its id assigned
*/
psTelemetryServerChannelIdAssigned ChannelIdAssigned;
/**
* Callback for the RDP Telemetry PDU.
*/
psTelemetryServerRdpTelemetry RdpTelemetry;
rdpContext* rdpcontext;
};
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API TelemetryServerContext* telemetry_server_context_new(HANDLE vcm);
FREERDP_API void telemetry_server_context_free(TelemetryServerContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H */

View File

@ -656,6 +656,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
#define FreeRDP_NtlmSamFile (1103)
#define FreeRDP_FIPSMode (1104)
#define FreeRDP_TlsSecLevel (1105)
#define FreeRDP_TLSMinVersion (1106)
#define FreeRDP_TLSMaxVersion (1107)
#define FreeRDP_MstscCookieMode (1152)
#define FreeRDP_CookieMaxLength (1153)
#define FreeRDP_PreconnectionId (1154)
@ -1108,7 +1110,9 @@ struct rdp_settings
ALIGN64 char* NtlmSamFile; /* 1103 */
ALIGN64 BOOL FIPSMode; /* 1104 */
ALIGN64 UINT32 TlsSecLevel; /* 1105 */
UINT64 padding1152[1152 - 1106]; /* 1106 */
ALIGN64 UINT16 TLSMinVersion; /* 1106 */
ALIGN64 UINT16 TLSMaxVersion; /* 1107 */
UINT64 padding1152[1152 - 1108]; /* 1108 */
/* Connection Cookie */
ALIGN64 BOOL MstscCookieMode; /* 1152 */
@ -1705,6 +1709,9 @@ extern "C"
FREERDP_API SSIZE_T freerdp_settings_get_type_for_key(size_t key);
FREERDP_API const char* freerdp_settings_get_name_for_key(size_t key);
FREERDP_API char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer,
size_t length);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,48 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPDR utility functions
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_UTILS_CLIPRDR_H
#define FREERDP_UTILS_CLIPRDR_H
#include <winpr/wtypes.h>
#include <winpr/shell.h>
#include <freerdp/api.h>
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
FILEDESCRIPTORW** file_descriptor_array,
UINT32* file_descriptor_count);
FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length);
FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count,
BYTE** format_data, UINT32* format_data_length);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -211,7 +211,12 @@ if(FAAC_FOUND)
endif()
if(WITH_NEON)
set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon -Wno-unused-variable" )
check_symbol_exists("_M_AMD64" "" MSVC_ARM64)
check_symbol_exists("__aarch64__" "" ARCH_ARM64)
if (NOT MSVC_ARM64 AND NOT ARCH_ARM64)
set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon" )
endif()
set(CODEC_SRCS ${CODEC_SRCS} ${CODEC_NEON_SRCS})
endif()
@ -308,7 +313,7 @@ freerdp_definition_add(-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
### IPP Variable debugging
if(WITH_IPP)
if(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
foreach(INCLDIR ${IPP_INCLUDE_DIRS})
set(OPTIMIZATION "${OPTIMIZATION} -I${INCLDIR}")
endforeach(INCLDIR)
@ -330,9 +335,11 @@ if(WITH_SSE2)
PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} /arch:SSE2")
endif()
elseif(WITH_NEON)
if(CMAKE_COMPILER_IS_GNUCC)
set_source_files_properties(${PRIMITIVES_OPT_SRCS}
PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon")
if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
if (NOT MSVC_ARM64 AND NOT ARCH_ARM64)
set_source_files_properties(${PRIMITIVES_OPT_SRCS}
PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon")
endif()
endif()
# TODO: Add MSVC equivalent
endif()

View File

@ -896,7 +896,7 @@ static INLINE int progressive_rfx_decode_component(PROGRESSIVE_CONTEXT* progress
return status;
CopyMemory(sign, buffer, 4096 * 2);
if (!subbandDiff)
if (!extrapolate)
{
rfx_differential_decode(buffer + 4032, 64);
progressive_rfx_decode_block(prims, &buffer[0], 1024, shift->HL1); /* HL1 */

View File

@ -21,7 +21,7 @@
#include "config.h"
#endif
#if defined(__ARM_NEON__)
#if defined(WITH_NEON)
#include <stdio.h>
#include <stdlib.h>
@ -241,4 +241,4 @@ void rfx_init_neon(RFX_CONTEXT* context)
}
}
#endif // __ARM_NEON__
#endif // WITH_NEON

View File

@ -363,6 +363,17 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
return TRUE;
}
/* Allocate the buffers a bit larger.
*
* Due to optimizations some h264 decoders will read data beyond
* the actual available data, so ensure that it will never be a
* out of bounds read.
*/
static BYTE* aligned_zgfx_malloc(size_t size)
{
return malloc(size + 64);
}
int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData,
UINT32* pDstSize, UINT32 flags)
{
@ -386,7 +397,7 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
*ppDstData = NULL;
if (zgfx->OutputCount > 0)
*ppDstData = (BYTE*)malloc(zgfx->OutputCount);
*ppDstData = aligned_zgfx_malloc(zgfx->OutputCount);
if (!*ppDstData)
goto fail;
@ -412,7 +423,7 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
if (Stream_GetRemainingLength(stream) < segmentCount * sizeof(UINT32))
goto fail;
pConcatenated = (BYTE*)malloc(uncompressedSize);
pConcatenated = aligned_zgfx_malloc(uncompressedSize);
if (!pConcatenated)
goto fail;

View File

@ -925,3 +925,26 @@ BOOL freerdp_settings_set_value_for_name(rdpSettings* settings, const char* name
}
return FALSE;
}
char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer, size_t length)
{
if (flags & RAIL_LEVEL_SUPPORTED)
winpr_str_append("RAIL_LEVEL_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED)
winpr_str_append("RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED)
winpr_str_append("RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)
winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED)
winpr_str_append("RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED)
winpr_str_append("RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED)
winpr_str_append("RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED)
winpr_str_append("RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED", buffer, length, "|");
if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)
winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|");
return buffer;
}

View File

@ -1193,6 +1193,12 @@ UINT16 freerdp_settings_get_uint16(const rdpSettings* settings, size_t id)
case FreeRDP_ProxyPort:
return settings->ProxyPort;
case FreeRDP_TLSMaxVersion:
return settings->TLSMaxVersion;
case FreeRDP_TLSMinVersion:
return settings->TLSMinVersion;
default:
WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id);
return FALSE;
@ -1214,6 +1220,14 @@ BOOL freerdp_settings_set_uint16(rdpSettings* settings, size_t id, UINT16 val)
settings->ProxyPort = val;
break;
case FreeRDP_TLSMaxVersion:
settings->TLSMaxVersion = val;
break;
case FreeRDP_TLSMinVersion:
settings->TLSMinVersion = val;
break;
default:
WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id);
return FALSE;

View File

@ -180,6 +180,8 @@ static const struct settings_str_entry settings_map[] = {
{ FreeRDP_Workarea, 0, "FreeRDP_Workarea" },
{ FreeRDP_DesktopOrientation, 1, "FreeRDP_DesktopOrientation" },
{ FreeRDP_ProxyPort, 1, "FreeRDP_ProxyPort" },
{ FreeRDP_TLSMaxVersion, 1, "FreeRDP_TLSMaxVersion" },
{ FreeRDP_TLSMinVersion, 1, "FreeRDP_TLSMinVersion" },
{ FreeRDP_AcceptedCertLength, 3, "FreeRDP_AcceptedCertLength" },
{ FreeRDP_AuthenticationLevel, 3, "FreeRDP_AuthenticationLevel" },
{ FreeRDP_AutoReconnectMaxRetries, 3, "FreeRDP_AutoReconnectMaxRetries" },
@ -393,6 +395,10 @@ static const struct settings_str_entry settings_map[] = {
BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src)
{
size_t x;
WINPR_ASSERT(dst);
WINPR_ASSERT(src);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
@ -466,9 +472,214 @@ BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src)
return TRUE;
}
BOOL freerdp_settings_print_diff(wLog* log, DWORD level, const rdpSettings* settings,
const rdpSettings* other)
{
BOOL rc = FALSE;
size_t x;
WINPR_ASSERT(log);
WINPR_ASSERT(settings);
WINPR_ASSERT(other);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
switch (cur->type)
{
case 0: /* bool */
{
BOOL sval = freerdp_settings_get_bool(settings, cur->id);
BOOL cval = freerdp_settings_get_bool(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [BOOL]: %s -> %s", cur->str, sval ? "TRUE" : "FALSE",
cval ? "TRUE" : "FALSE");
}
}
break;
case 1: /* UINT16 */
{
UINT16 sval = freerdp_settings_get_uint16(settings, cur->id);
UINT16 cval = freerdp_settings_get_uint16(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [UINT16]: %" PRIu16 " -> %" PRIu16, cur->str, sval,
cval);
}
}
break;
case 2: /* INT16 */
{
INT16 sval = freerdp_settings_get_int16(settings, cur->id);
INT16 cval = freerdp_settings_get_int16(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [INT16]: %" PRId16 " -> %" PRId16, cur->str, sval,
cval);
}
}
break;
case 3: /* UINT32 */
{
UINT32 sval = freerdp_settings_get_uint32(settings, cur->id);
UINT32 cval = freerdp_settings_get_uint32(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [UINT32]: %" PRIu32 " -> %" PRIu32, cur->str, sval,
cval);
}
}
break;
case 4: /* INT32 */
{
INT32 sval = freerdp_settings_get_int32(settings, cur->id);
INT32 cval = freerdp_settings_get_int32(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [INT32]: %" PRId32 " -> %" PRId32, cur->str, sval,
cval);
}
}
break;
case 5: /* UINT64 */
{
UINT64 sval = freerdp_settings_get_uint64(settings, cur->id);
UINT64 cval = freerdp_settings_get_uint64(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [UINT64]: %" PRIu64 " -> %" PRIu64, cur->str, sval,
cval);
}
}
break;
case 6: /* INT64 */
{
INT64 sval = freerdp_settings_get_int64(settings, cur->id);
INT64 cval = freerdp_settings_get_int64(other, cur->id);
if (sval != cval)
{
rc = TRUE;
WLog_Print(log, level, "%s [INT64]: %" PRId64 " -> %" PRId64, cur->str, sval,
cval);
}
}
break;
case 7: /* strings */
{
const char* sval = freerdp_settings_get_string(settings, cur->id);
const char* cval = freerdp_settings_get_string(other, cur->id);
if (sval != cval)
{
if (!sval || !cval || (strcmp(sval, cval) != 0))
{
rc = TRUE;
WLog_Print(log, level, "%s [STRING]: '%s' -> '%s'", cur->str, sval, cval);
}
}
}
break;
case 8: /* pointer */
{
const void* sval = freerdp_settings_get_pointer(settings, cur->id);
const void* cval = freerdp_settings_get_pointer(other, cur->id);
if (sval != cval)
{
if ((sval && !cval) || (!sval && cval))
{
rc = TRUE;
WLog_Print(log, level, "%s [POINTER]: '%p' -> '%p'", cur->str, sval, cval);
}
}
}
break;
}
}
return rc;
}
void freerdp_settings_dump(wLog* log, DWORD level, const rdpSettings* settings)
{
size_t x;
WINPR_ASSERT(log);
WINPR_ASSERT(settings);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
switch (cur->type)
{
case 0: /* bool */
{
BOOL sval = freerdp_settings_get_bool(settings, cur->id);
WLog_Print(log, level, "%s [BOOL]: %s", cur->str, sval ? "TRUE" : "FALSE");
}
break;
case 1: /* UINT16 */
{
UINT16 sval = freerdp_settings_get_uint16(settings, cur->id);
WLog_Print(log, level, "%s [UINT16]: %" PRIu16, cur->str, sval);
}
break;
case 2: /* INT16 */
{
INT16 sval = freerdp_settings_get_int16(settings, cur->id);
WLog_Print(log, level, "%s [INT16]: %" PRId16, cur->str, sval);
}
break;
case 3: /* UINT32 */
{
UINT32 sval = freerdp_settings_get_uint32(settings, cur->id);
WLog_Print(log, level, "%s [UINT32]: %" PRIu32, cur->str, sval);
}
break;
case 4: /* INT32 */
{
INT32 sval = freerdp_settings_get_int32(settings, cur->id);
WLog_Print(log, level, "%s [INT32]: %" PRId32, cur->str, sval);
}
break;
case 5: /* UINT64 */
{
UINT64 sval = freerdp_settings_get_uint64(settings, cur->id);
WLog_Print(log, level, "%s [UINT64]: %" PRIu64, cur->str, sval);
}
break;
case 6: /* INT64 */
{
INT64 sval = freerdp_settings_get_int64(settings, cur->id);
WLog_Print(log, level, "%s [INT64]: %" PRId64, cur->str, sval);
}
break;
case 7: /* strings */
{
const char* sval = freerdp_settings_get_string(settings, cur->id);
WLog_Print(log, level, "%s [STRING]: '%s'", cur->str, sval);
}
break;
case 8: /* pointer */
{
const void* sval = freerdp_settings_get_pointer(settings, cur->id);
WLog_Print(log, level, "%s [POINTER]: '%p'", cur->str, sval);
}
break;
}
}
}
void freerdp_settings_free_keys(rdpSettings* dst, BOOL cleanup)
{
size_t x;
WINPR_ASSERT(dst);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
@ -487,6 +698,9 @@ void freerdp_settings_free_keys(rdpSettings* dst, BOOL cleanup)
SSIZE_T freerdp_settings_get_key_for_name(const char* value)
{
size_t x;
WINPR_ASSERT(value);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
@ -499,6 +713,9 @@ SSIZE_T freerdp_settings_get_key_for_name(const char* value)
SSIZE_T freerdp_settings_get_type_for_name(const char* value)
{
size_t x;
WINPR_ASSERT(value);
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
@ -511,6 +728,7 @@ SSIZE_T freerdp_settings_get_type_for_name(const char* value)
SSIZE_T freerdp_settings_get_type_for_key(size_t key)
{
size_t x;
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];
@ -523,6 +741,7 @@ SSIZE_T freerdp_settings_get_type_for_key(size_t key)
const char* freerdp_settings_get_name_for_key(size_t key)
{
size_t x;
for (x = 0; x < ARRAYSIZE(settings_map); x++)
{
const struct settings_str_entry* cur = &settings_map[x];

View File

@ -162,26 +162,17 @@ static BOOL rdp_read_general_capability_set(wStream* s, rdpSettings* settings)
if (Stream_GetRemainingLength(s) < 20)
return FALSE;
if (settings->ServerMode)
{
Stream_Read_UINT16(s, settings->OsMajorType); /* osMajorType (2 bytes) */
Stream_Read_UINT16(s, settings->OsMinorType); /* osMinorType (2 bytes) */
}
else
{
Stream_Seek_UINT16(s); /* osMajorType (2 bytes) */
Stream_Seek_UINT16(s); /* osMinorType (2 bytes) */
}
Stream_Seek_UINT16(s); /* protocolVersion (2 bytes) */
Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
Stream_Seek_UINT16(s); /* generalCompressionTypes (2 bytes) */
Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
Stream_Seek_UINT16(s); /* updateCapabilityFlag (2 bytes) */
Stream_Seek_UINT16(s); /* remoteUnshareFlag (2 bytes) */
Stream_Seek_UINT16(s); /* generalCompressionLevel (2 bytes) */
Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */
Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */
Stream_Read_UINT16(s, settings->OsMajorType); /* osMajorType (2 bytes) */
Stream_Read_UINT16(s, settings->OsMinorType); /* osMinorType (2 bytes) */
Stream_Seek_UINT16(s); /* protocolVersion (2 bytes) */
Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
Stream_Seek_UINT16(s); /* generalCompressionTypes (2 bytes) */
Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
Stream_Seek_UINT16(s); /* updateCapabilityFlag (2 bytes) */
Stream_Seek_UINT16(s); /* remoteUnshareFlag (2 bytes) */
Stream_Seek_UINT16(s); /* generalCompressionLevel (2 bytes) */
Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */
Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */
settings->NoBitmapCompressionHeader = (extraFlags & NO_BITMAP_COMPRESSION_HDR) ? TRUE : FALSE;
settings->LongCredentialsSupported = (extraFlags & LONG_CREDENTIALS_SUPPORTED) ? TRUE : FALSE;

View File

@ -257,20 +257,18 @@ static const char* flags_to_string(UINT32 flags, const t_flag_mapping* map, size
{
size_t x = 0;
static char buffer[1024] = { 0 };
char fields[12];
memset(buffer, 0, sizeof(buffer));
char fields[12] = { 0 };
for (x = 0; x < elements; x++)
{
if (buffer[0] != '\0')
strcat(buffer, "|");
const t_flag_mapping* cur = &map[x];
if ((map[x].code & flags) != 0)
strcat(buffer, map[x].name);
if ((cur->code & flags) != 0)
winpr_str_append(cur->name, buffer, sizeof(buffer), "|");
}
sprintf_s(fields, ARRAYSIZE(fields), " [%04" PRIx32 "]", flags);
strcat(buffer, fields);
winpr_str_append(fields, buffer, sizeof(buffer), NULL);
return buffer;
}
@ -314,7 +312,8 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1
/* Read length of the string */
if (rem < 4)
{
WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes", rem);
WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes",
__FUNCTION__, rem);
return FALSE;
}
Stream_Read_UINT16(s, strLenBytes);
@ -327,7 +326,7 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1
{
WLog_ERR(TAG,
"[%s]: Could not read stream data, only have % " PRIuz " bytes, expected %" PRIu16,
rem - 4, strLenBytes);
__FUNCTION__, rem - 4, strLenBytes);
return FALSE;
}

View File

@ -41,11 +41,13 @@ static const char* const INFO_TYPE_LOGON_STRINGS[4] = { "Logon Info V1", "Logon
/* This define limits the length of the strings in the label field. */
#define MAX_LABEL_LENGTH 40
static struct
struct info_flags_t
{
UINT32 flag;
const char* label;
} const info_flags[] = {
};
static const struct info_flags_t info_flags[] = {
{ INFO_MOUSE, "INFO_MOUSE" },
{ INFO_DISABLECTRLALTDEL, "INFO_DISABLECTRLALTDEL" },
{ INFO_AUTOLOGON, "INFO_AUTOLOGON" },
@ -118,12 +120,8 @@ static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CH
static char* rdp_info_package_flags_description(UINT32 flags)
{
char* result;
size_t maximum_size = 1; /* Reserve space for the terminating '\0' by strcat if all flags set */
size_t maximum_size = 1 + MAX_LABEL_LENGTH * ARRAYSIZE(info_flags);
size_t i;
size_t size;
for (i = 0; i < ARRAYSIZE(info_flags); i++)
maximum_size += strnlen(info_flags[i].label, MAX_LABEL_LENGTH) + 1;
result = calloc(maximum_size, sizeof(char));
@ -132,18 +130,13 @@ static char* rdp_info_package_flags_description(UINT32 flags)
for (i = 0; i < ARRAYSIZE(info_flags); i++)
{
if (info_flags[i].flag & flags)
const struct info_flags_t* cur = &info_flags[i];
if (cur->flag & flags)
{
strcat(result, info_flags[i].label);
strcat(result, "|");
winpr_str_append(cur->label, result, maximum_size, "|");
}
}
size = strnlen(result, maximum_size);
if (size > 0)
result[size - 1] = '\0'; /* remove last "|" */
return result;
}

View File

@ -945,6 +945,28 @@ BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s)
* @param nego
* @param s
*/
static const char* nego_rdp_neg_rsp_flags_str(UINT32 flags)
{
static char buffer[1024] = { 0 };
_snprintf(buffer, ARRAYSIZE(buffer), "[0x%02" PRIx32 "] ", flags);
if (flags & EXTENDED_CLIENT_DATA_SUPPORTED)
winpr_str_append("EXTENDED_CLIENT_DATA_SUPPORTED", buffer, sizeof(buffer), "|");
if (flags & DYNVC_GFX_PROTOCOL_SUPPORTED)
winpr_str_append("DYNVC_GFX_PROTOCOL_SUPPORTED", buffer, sizeof(buffer), "|");
if (flags & RDP_NEGRSP_RESERVED)
winpr_str_append("RDP_NEGRSP_RESERVED", buffer, sizeof(buffer), "|");
if (flags & RESTRICTED_ADMIN_MODE_SUPPORTED)
winpr_str_append("RESTRICTED_ADMIN_MODE_SUPPORTED", buffer, sizeof(buffer), "|");
if (flags & REDIRECTED_AUTHENTICATION_MODE_SUPPORTED)
winpr_str_append("REDIRECTED_AUTHENTICATION_MODE_SUPPORTED", buffer, sizeof(buffer), "|");
if ((flags &
~(EXTENDED_CLIENT_DATA_SUPPORTED | DYNVC_GFX_PROTOCOL_SUPPORTED | RDP_NEGRSP_RESERVED |
RESTRICTED_ADMIN_MODE_SUPPORTED | REDIRECTED_AUTHENTICATION_MODE_SUPPORTED)))
winpr_str_append("UNKNOWN", buffer, sizeof(buffer), "|");
return buffer;
}
BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s)
{
@ -959,6 +981,7 @@ BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s)
}
Stream_Read_UINT8(s, nego->flags);
WLog_DBG(TAG, "RDP_NEG_RSP::flags = { %s }", nego_rdp_neg_rsp_flags_str(nego->flags));
Stream_Read_UINT16(s, length);
Stream_Read_UINT32(s, nego->SelectedProtocol);
nego->state = NEGO_STATE_FINAL;

View File

@ -81,6 +81,7 @@ enum RDP_NEG_MSG
#define DYNVC_GFX_PROTOCOL_SUPPORTED 0x02
#define RDP_NEGRSP_RESERVED 0x04
#define RESTRICTED_ADMIN_MODE_SUPPORTED 0x08
#define REDIRECTED_AUTHENTICATION_MODE_SUPPORTED 0x10
#define PRECONNECTION_PDU_V1_SIZE 16
#define PRECONNECTION_PDU_V2_MIN_SIZE (PRECONNECTION_PDU_V1_SIZE + 2)

View File

@ -866,6 +866,94 @@ void freerdp_peer_context_free(freerdp_peer* client)
}
}
static const char* os_major_type_to_string(UINT16 osMajorType)
{
switch (osMajorType)
{
case OSMAJORTYPE_UNSPECIFIED:
return "Unspecified platform";
case OSMAJORTYPE_WINDOWS:
return "Windows platform";
case OSMAJORTYPE_OS2:
return "OS/2 platform";
case OSMAJORTYPE_MACINTOSH:
return "Macintosh platform";
case OSMAJORTYPE_UNIX:
return "UNIX platform";
case OSMAJORTYPE_IOS:
return "iOS platform";
case OSMAJORTYPE_OSX:
return "OS X platform";
case OSMAJORTYPE_ANDROID:
return "Android platform";
case OSMAJORTYPE_CHROME_OS:
return "Chrome OS platform";
}
return "Unknown platform";
}
const char* freerdp_peer_os_major_type_string(freerdp_peer* client)
{
rdpContext* context;
UINT16 osMajorType;
WINPR_ASSERT(client);
context = client->context;
WINPR_ASSERT(context);
WINPR_ASSERT(context->settings);
osMajorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMajorType);
return os_major_type_to_string(osMajorType);
}
static const char* os_minor_type_to_string(UINT16 osMinorType)
{
switch (osMinorType)
{
case OSMINORTYPE_UNSPECIFIED:
return "Unspecified version";
case OSMINORTYPE_WINDOWS_31X:
return "Windows 3.1x";
case OSMINORTYPE_WINDOWS_95:
return "Windows 95";
case OSMINORTYPE_WINDOWS_NT:
return "Windows NT";
case OSMINORTYPE_OS2_V21:
return "OS/2 2.1";
case OSMINORTYPE_POWER_PC:
return "PowerPC";
case OSMINORTYPE_MACINTOSH:
return "Macintosh";
case OSMINORTYPE_NATIVE_XSERVER:
return "Native X Server";
case OSMINORTYPE_PSEUDO_XSERVER:
return "Pseudo X Server";
case OSMINORTYPE_WINDOWS_RT:
return "Windows RT";
}
return "Unknown version";
}
const char* freerdp_peer_os_minor_type_string(freerdp_peer* client)
{
rdpContext* context;
UINT16 osMinorType;
WINPR_ASSERT(client);
context = client->context;
WINPR_ASSERT(context);
WINPR_ASSERT(context->settings);
osMinorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMinorType);
return os_minor_type_to_string(osMinorType);
}
freerdp_peer* freerdp_peer_new(int sockfd)
{
UINT32 option_value;

View File

@ -158,6 +158,7 @@ static BOOL wts_read_drdynvc_capabilities_response(rdpPeerChannel* channel, UINT
static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s, UINT32 length)
{
UINT32 CreationStatus;
BOOL status = TRUE;
if (length < 4)
return FALSE;
@ -176,7 +177,12 @@ static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s
channel->dvc_open_state = DVC_OPEN_STATE_SUCCEEDED;
}
return TRUE;
IFCALLRET(channel->vcm->dvc_creation_status, status, channel->vcm->dvc_creation_status_userdata,
channel->channelId, (INT32)CreationStatus);
if (!status)
WLog_ERR(TAG, "vcm->dvc_creation_status failed!");
return status;
}
static BOOL wts_read_drdynvc_data_first(rdpPeerChannel* channel, wStream* s, int cbLen,
@ -578,6 +584,17 @@ BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer)
return vcm->drdynvc_state;
}
void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer, psDVCCreationStatusCallback cb,
void* userdata)
{
WTSVirtualChannelManager* vcm = hServer;
WINPR_ASSERT(vcm);
vcm->dvc_creation_status = cb;
vcm->dvc_creation_status_userdata = userdata;
}
UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name)
{
rdpMcsChannel* channel;
@ -593,6 +610,15 @@ UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name)
return channel->ChannelId;
}
UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle)
{
rdpPeerChannel* channel = hChannelHandle;
WINPR_ASSERT(channel);
return channel->channelId;
}
BOOL WTSChannelSetHandleByName(freerdp_peer* client, const char* channel_name, void* handle)
{
rdpMcsChannel* channel;
@ -670,6 +696,43 @@ const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id)
return (const char*)channel->Name;
}
INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id)
{
rdpMcsChannel* channel;
if (!client || !client->context || !client->context->rdp)
return -1;
channel = wts_get_joined_channel_by_id(client->context->rdp->mcs, channel_id);
if (!channel)
return -1;
return (INT64)channel->options;
}
char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count)
{
rdpMcs* mcs;
char** names;
UINT32 index;
if (!client || !client->context || !count)
return NULL;
mcs = client->context->rdp->mcs;
*count = mcs->channelCount;
names = (char**)calloc(mcs->channelCount, sizeof(char*));
if (!names)
return NULL;
for (index = 0; index < mcs->channelCount; index++)
names[index] = mcs->channels[index].Name;
return names;
}
BOOL WINAPI FreeRDP_WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, ULONG TargetLogonId,
BYTE HotkeyVk, USHORT HotkeyModifiers)
{

View File

@ -86,6 +86,9 @@ struct WTSVirtualChannelManager
BYTE drdynvc_state;
LONG dvc_channel_id_seq;
psDVCCreationStatusCallback dvc_creation_status;
void* dvc_creation_status_userdata;
wArrayList* dynamicVirtualChannels;
};

View File

@ -599,6 +599,9 @@ rdpSettings* freerdp_settings_new(DWORD flags)
settings->TlsSecLevel = 1;
settings->OrderSupport = calloc(1, 32);
freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_VERSION);
freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, 0);
if (!settings->OrderSupport)
goto out_fail;

View File

@ -173,6 +173,8 @@ static const size_t bool_list_indices[] = {
static const size_t uint16_list_indices[] = {
FreeRDP_DesktopOrientation,
FreeRDP_ProxyPort,
FreeRDP_TLSMaxVersion,
FreeRDP_TLSMinVersion,
};
#define have_uint32_list_indices

View File

@ -19,7 +19,8 @@
#include "opensslcompat.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
BIO_METHOD* BIO_meth_new(int type, const char* name)
{

View File

@ -30,7 +30,8 @@
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
#include <openssl/bio.h>
#include <openssl/rsa.h>

View File

@ -77,6 +77,7 @@ struct _BIO_RDP_TLS
};
typedef struct _BIO_RDP_TLS BIO_RDP_TLS;
static BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode);
static int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port);
static void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port,
const char* common_name, char** alt_names,
@ -667,8 +668,18 @@ static BOOL tls_prepare(rdpTls* tls, BIO* underlying, SSL_METHOD* method, int op
SSL_CTX_set_options(tls->ctx, options);
SSL_CTX_set_read_ahead(tls->ctx, 1);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_set_min_proto_version(tls->ctx, TLS1_VERSION); /* min version */
SSL_CTX_set_max_proto_version(tls->ctx, 0); /* highest supported version by library */
UINT16 version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMinVersion);
if (!SSL_CTX_set_min_proto_version(tls->ctx, version))
{
WLog_ERR(TAG, "SSL_CTX_set_min_proto_version %s failed", version);
return FALSE;
}
version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMaxVersion);
if (!SSL_CTX_set_max_proto_version(tls->ctx, version))
{
WLog_ERR(TAG, "SSL_CTX_set_max_proto_version %s failed", version);
return FALSE;
}
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_set_security_level(tls->ctx, settings->TlsSecLevel);
@ -855,6 +866,18 @@ int tls_connect(rdpTls* tls, BIO* underlying)
* support empty fragments. This needs to be disabled.
*/
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
if (!tls_prep(tls, underlying, options, TRUE))
return 0;
#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER)
SSL_set_tlsext_host_name(tls->ssl, tls->hostname);
#endif
return tls_do_handshake(tls, TRUE);
}
BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
/**
* disable SSLv2 and SSLv3
@ -862,16 +885,10 @@ int tls_connect(rdpTls* tls, BIO* underlying)
options |= SSL_OP_NO_SSLv2;
options |= SSL_OP_NO_SSLv3;
if (!tls_prepare(tls, underlying, SSLv23_client_method(), options, TRUE))
return tls_prepare(tls, underlying, SSLv23_client_method(), options, clientMode);
#else
if (!tls_prepare(tls, underlying, TLS_client_method(), options, TRUE))
return tls_prepare(tls, underlying, TLS_client_method(), options, clientMode);
#endif
return 0;
#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER)
SSL_set_tlsext_host_name(tls->ssl, tls->hostname);
#endif
return tls_do_handshake(tls, TRUE);
}
#if defined(MICROSOFT_IOS_SNI_BUG) && !defined(OPENSSL_NO_TLSEXT) && \

View File

@ -20,6 +20,7 @@ set(MODULE_PREFIX "FREERDP_UTILS")
set(${MODULE_PREFIX}_SRCS
passphrase.c
cliprdr_utils.c
pcap.c
profiler.c
ringbuffer.c

View File

@ -0,0 +1,235 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Clipboard Virtual Channel Extension
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2022 Armin Novak <anovak@thincast.com
* Copyright 2022 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/stream.h>
#include <freerdp/utils/cliprdr_utils.h>
#include <freerdp/channels/cliprdr.h>
#include <freerdp/log.h>
#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
static UINT64 filetime_to_uint64(FILETIME value)
{
UINT64 converted = 0;
converted |= (UINT32)value.dwHighDateTime;
converted <<= 32;
converted |= (UINT32)value.dwLowDateTime;
return converted;
}
static FILETIME uint64_to_filetime(UINT64 value)
{
FILETIME converted;
converted.dwLowDateTime = (UINT32)(value >> 0);
converted.dwHighDateTime = (UINT32)(value >> 32);
return converted;
}
/**
* Parse a packed file list.
*
* The resulting array must be freed with the `free()` function.
*
* @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
* @param [in] format_data_length length of `format_data` in bytes.
* @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
* @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
{
UINT result = NO_ERROR;
UINT32 i;
UINT32 count = 0;
wStream sbuffer;
wStream* s = &sbuffer;
if (!format_data || !file_descriptor_array || !file_descriptor_count)
return ERROR_BAD_ARGUMENTS;
Stream_StaticInit(&sbuffer, format_data, format_data_length);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
if (Stream_GetRemainingLength(s) < 4)
{
WLog_ERR(TAG, "invalid packed file list");
result = ERROR_INCORRECT_SIZE;
goto out;
}
Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
{
WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
result = ERROR_INCORRECT_SIZE;
goto out;
}
*file_descriptor_count = count;
*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
if (!*file_descriptor_array)
{
result = ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
for (i = 0; i < count; i++)
{
UINT64 tmp;
FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Read_UINT32(s, file->clsid.Data1);
Stream_Read_UINT16(s, file->clsid.Data2);
Stream_Read_UINT16(s, file->clsid.Data3);
Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
Stream_Read_INT32(s, file->sizel.cx);
Stream_Read_INT32(s, file->sizel.cy);
Stream_Read_INT32(s, file->pointl.x);
Stream_Read_INT32(s, file->pointl.y);
Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
file->ftCreationTime = uint64_to_filetime(tmp);
Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
file->ftLastAccessTime = uint64_to_filetime(tmp);
Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
file->ftLastWriteTime = uint64_to_filetime(tmp);
Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
Stream_Read_UTF16_String(s, file->cFileName,
ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
}
if (Stream_GetRemainingLength(s) > 0)
WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
Stream_GetRemainingLength(s));
out:
return result;
}
/**
* Serialize a packed file list.
*
* The resulting format data must be freed with the `free()` function.
*
* @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
* @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
* @param [out] format_data serialized CLIPRDR_FILELIST.
* @param [out] format_data_length length of `format_data` in bytes.
*
* @returns 0 on success, otherwise a Win32 error code.
*/
UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
file_descriptor_count, format_data, format_data_length);
}
UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
UINT32 file_descriptor_count, BYTE** format_data,
UINT32* format_data_length)
{
UINT result = NO_ERROR;
UINT32 i;
size_t len;
wStream* s = NULL;
if (!file_descriptor_array || !format_data || !format_data_length)
return ERROR_BAD_ARGUMENTS;
if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
{
WLog_WARN(TAG, "No file clipboard support annouonced!");
return ERROR_BAD_ARGUMENTS;
}
s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
if (!s)
return ERROR_NOT_ENOUGH_MEMORY;
Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
for (i = 0; i < file_descriptor_count; i++)
{
int c;
UINT64 lastWriteTime;
const FILEDESCRIPTORW* file = &file_descriptor_array[i];
/*
* There is a known issue with Windows server getting stuck in
* an infinite loop when downloading files that are larger than
* 2 gigabytes. Do not allow clients to send such file lists.
*
* https://support.microsoft.com/en-us/help/2258090
*/
if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
{
if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
{
WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
result = ERROR_FILE_TOO_LARGE;
goto error;
}
}
Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Stream_Zero(s, 32); /* reserved1 (32 bytes) */
Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Stream_Zero(s, 16); /* reserved2 (16 bytes) */
lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Stream_Write_UINT16(s, file->cFileName[c]);
}
Stream_SealLength(s);
Stream_GetBuffer(s, *format_data);
Stream_GetLength(s, len);
if (len > UINT32_MAX)
goto error;
*format_data_length = (UINT32)len;
Stream_Free(s, FALSE);
return result;
error:
Stream_Free(s, TRUE);
return result;
}

View File

@ -596,11 +596,12 @@ shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context,
static BOOL shadow_are_caps_filtered(const rdpSettings* settings, UINT32 caps)
{
const UINT32 filter = settings->GfxCapsFilter;
const UINT32 capList[] = {
RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10,
RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106
};
const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81,
RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101,
RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
RDPGFX_CAPVERSION_107 };
UINT32 x;
for (x = 0; x < ARRAYSIZE(capList); x++)
@ -682,10 +683,19 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context,
if (!shadow_client_refresh_rect(&client->context, 0, NULL))
return rc;
if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets,
capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_107, &rc))
return rc;
if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets,
capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106, &rc))
return rc;
if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets,
capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106_ERR,
&rc))
return rc;
if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets,
capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_105, &rc))
return rc;

View File

@ -551,10 +551,18 @@ extern "C"
UWAC_API void UwacWindowSetTitle(UwacWindow* window, const char* name);
/**
* Sets the app id of the UwacWindow
*
* @param display
* @param timeout
* @return
* @param window the UwacWindow
* @param app_id app id
*/
UWAC_API void UwacWindowSetAppId(UwacWindow* window, const char* app_id);
/** Dispatch the display
*
* @param display The display to dispatch
* @param timeout The maximum time to wait in milliseconds (-1 == infinite).
* @return 1 for success, 0 if display not running, -1 on failure
*/
UWAC_API int UwacDisplayDispatch(UwacDisplay* display, int timeout);

View File

@ -817,3 +817,9 @@ void UwacWindowSetTitle(UwacWindow* window, const char* name)
else if (window->shell_surface)
wl_shell_surface_set_title(window->shell_surface, name);
}
void UwacWindowSetAppId(UwacWindow* window, const char* app_id)
{
if (window->xdg_toplevel)
xdg_toplevel_set_app_id(window->xdg_toplevel, app_id);
}

View File

@ -57,7 +57,7 @@ if (NOT WIN32)
endif()
# Soname versioning
set(RAW_VERSION_STRING "2.7.0")
set(RAW_VERSION_STRING "2.8.0")
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
elseif(USE_VERSION_FROM_GIT_TAG)
@ -114,6 +114,10 @@ else()
check_include_files(sys/sockio.h HAVE_SYS_SOCKIO_H)
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
check_include_files(unwind.h HAVE_UNWIND_H)
if (HAVE_UNWIND_H)
add_definitions(-DHAVE_UNWIND_H)
endif()
else()
set(HAVE_FCNTL_H 1)
set(HAVE_UNISTD_H 1)
@ -127,7 +131,7 @@ else()
set(HAVE_TM_GMTOFF 1)
endif()
if(NOT WIN32)
if(NOT WIN32 AND NOT IOS)
CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL)
CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB)

View File

@ -64,6 +64,8 @@ struct _wClipboardDelegate
(wClipboardDelegate*, const wClipboardFileRangeRequest*, const BYTE* data, UINT32 size);
UINT(*ClipboardFileRangeFailure)
(wClipboardDelegate*, const wClipboardFileRangeRequest*, UINT errorCode);
BOOL (*IsFileNameComponentValid)(LPCWSTR lpFileName);
};
#ifdef __cplusplus

View File

@ -32,6 +32,12 @@
#ifndef _WIN32
#include <unistd.h>
#ifndef _write
#define _write write
#endif
#ifndef _strtoui64
#define _strtoui64 strtoull
#endif

Some files were not shown because too many files have changed in this diff Show More