New upstream version 2.8.0+dfsg1
This commit is contained in:
parent
f26a873894
commit
cfbb8ebcbd
@ -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")
|
||||
|
||||
36
ChangeLog
36
ChangeLog
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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!");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) ||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
||||
22
channels/rdpecam/CMakeLists.txt
Normal file
22
channels/rdpecam/CMakeLists.txt
Normal 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()
|
||||
12
channels/rdpecam/ChannelOptions.cmake
Normal file
12
channels/rdpecam/ChannelOptions.cmake
Normal 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})
|
||||
|
||||
27
channels/rdpecam/server/CMakeLists.txt
Normal file
27
channels/rdpecam/server/CMakeLists.txt
Normal 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")
|
||||
612
channels/rdpecam/server/camera_device_enumerator_main.c
Normal file
612
channels/rdpecam/server/camera_device_enumerator_main.c
Normal 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);
|
||||
}
|
||||
971
channels/rdpecam/server/camera_device_main.c
Normal file
971
channels/rdpecam/server/camera_device_main.c
Normal 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);
|
||||
}
|
||||
@ -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)))
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) ||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
22
channels/telemetry/CMakeLists.txt
Normal file
22
channels/telemetry/CMakeLists.txt
Normal 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()
|
||||
12
channels/telemetry/ChannelOptions.cmake
Normal file
12
channels/telemetry/ChannelOptions.cmake
Normal 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})
|
||||
|
||||
26
channels/telemetry/server/CMakeLists.txt
Normal file
26
channels/telemetry/server/CMakeLists.txt
Normal 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")
|
||||
443
channels/telemetry/server/telemetry_main.c
Normal file
443
channels/telemetry/server/telemetry_main.c
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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, ¤t);
|
||||
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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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++)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
336
include/freerdp/channels/rdpecam.h
Normal file
336
include/freerdp/channels/rdpecam.h
Normal 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 */
|
||||
@ -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
|
||||
{
|
||||
|
||||
38
include/freerdp/channels/telemetry.h
Normal file
38
include/freerdp/channels/telemetry.h
Normal 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 */
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
134
include/freerdp/server/rdpecam-enumerator.h
Normal file
134
include/freerdp/server/rdpecam-enumerator.h
Normal 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 */
|
||||
278
include/freerdp/server/rdpecam.h
Normal file
278
include/freerdp/server/rdpecam.h
Normal 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 */
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
108
include/freerdp/server/telemetry.h
Normal file
108
include/freerdp/server/telemetry.h
Normal 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 */
|
||||
@ -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
|
||||
|
||||
48
include/freerdp/utils/cliprdr_utils.h
Normal file
48
include/freerdp/utils/cliprdr_utils.h
Normal 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
|
||||
@ -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()
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) && \
|
||||
|
||||
@ -20,6 +20,7 @@ set(MODULE_PREFIX "FREERDP_UTILS")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
passphrase.c
|
||||
cliprdr_utils.c
|
||||
pcap.c
|
||||
profiler.c
|
||||
ringbuffer.c
|
||||
|
||||
235
libfreerdp/utils/cliprdr_utils.c
Normal file
235
libfreerdp/utils/cliprdr_utils.c
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user