Add support for graphics redirection protocol

Add support for new graphics redirection protocol (aka VAIL) used by
WSLg. This protocol is currently being documented and the official
documentation will be posted in the near future.
This commit is contained in:
Steve Pronovost 2021-10-16 11:59:45 -07:00 committed by akallabeth
parent f6f4acc921
commit bb7e4f220b
12 changed files with 1312 additions and 0 deletions

View File

@ -0,0 +1,23 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2020 Microsoft
#
# 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.
if(WITH_CHANNEL_GFXREDIR)
define_channel("gfxredir")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
endif()

View File

@ -0,0 +1,12 @@
if(WITH_CHANNEL_GFXREDIR)
set(OPTION_DEFAULT OFF)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "gfxredir" TYPE "dynamic"
DESCRIPTION "Graphics Redirection Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPXXXX]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})
endif()

View File

@ -0,0 +1,60 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Microsoft
*
* 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 <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/log.h>
#define TAG CHANNELS_TAG("gfxredir.common")
#include "gfxredir_common.h"
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header)
{
if (Stream_GetRemainingLength(s) < 8)
{
WLog_ERR(TAG, "header parsing failed: not enough data!");
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, header->cmdId);
Stream_Read_UINT32(s, header->length);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header)
{
Stream_Write_UINT32(s, header->cmdId);
Stream_Write_UINT32(s, header->length);
return CHANNEL_RC_OK;
}

View File

@ -0,0 +1,32 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Microsoft
*
* 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_GFXREDIR_COMMON_H
#define FREERDP_CHANNEL_GFXREDIR_COMMON_H
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/gfxredir.h>
#include <freerdp/api.h>
FREERDP_LOCAL UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header);
FREERDP_LOCAL UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header);
#endif /* FREERDP_CHANNEL_GFXREDIR_COMMON_H */

View File

@ -0,0 +1,32 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2020 Microsoft
#
# 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("gfxredir")
set(${MODULE_PREFIX}_SRCS
gfxredir_main.c
gfxredir_main.h
../gfxredir_common.c
../gfxredir_common.h
)
include_directories(..)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
target_link_libraries(${MODULE_NAME} freerdp)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -0,0 +1,805 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Microsoft
*
* 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 "gfxredir_main.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/log.h>
#include <freerdp/server/gfxredir.h>
#include "../gfxredir_common.h"
#define TAG CHANNELS_TAG("gfxredir.server")
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_recv_legacy_caps_pdu(wStream* s, GfxRedirServerContext* context)
{
UINT32 error = CHANNEL_RC_OK;
GFXREDIR_LEGACY_CAPS_PDU pdu;
if (Stream_GetRemainingLength(s) < 2)
{
WLog_ERR(TAG, "not enough data!");
return ERROR_INVALID_DATA;
}
Stream_Read_UINT16(s, pdu.version); /* version (2 bytes) */
if (context)
IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_recv_caps_advertise_pdu(wStream* s, UINT32 length,
GfxRedirServerContext* context)
{
UINT32 error = CHANNEL_RC_OK;
GFXREDIR_CAPS_ADVERTISE_PDU pdu;
if (Stream_GetRemainingLength(s) < length)
{
WLog_ERR(TAG, "not enough data!");
return ERROR_INVALID_DATA;
}
pdu.length = length;
Stream_GetPointer(s, pdu.caps);
Stream_Seek(s, length);
if (!context->GraphicsRedirectionCapsAdvertise)
{
WLog_ERR(TAG, "server does not support CapsAdvertise PDU!");
return ERROR_NOT_SUPPORTED;
}
else if (context)
{
IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu);
}
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_recv_present_buffer_ack_pdu(wStream* s, GfxRedirServerContext* context)
{
UINT32 error = CHANNEL_RC_OK;
GFXREDIR_PRESENT_BUFFER_ACK_PDU pdu;
if (Stream_GetRemainingLength(s) < 16)
{
WLog_ERR(TAG, "not enough data!");
return ERROR_INVALID_DATA;
}
Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
Stream_Read_UINT64(s, pdu.presentId); /* presentId (8 bytes) */
if (context)
{
IFCALLRET(context->PresentBufferAck, error, context, &pdu);
}
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context, wStream* s)
{
UINT error = CHANNEL_RC_OK;
size_t beg, end;
GFXREDIR_HEADER header;
beg = Stream_GetPosition(s);
if ((error = gfxredir_read_header(s, &header)))
{
WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error);
return error;
}
switch (header.cmdId)
{
case GFXREDIR_CMDID_LEGACY_CAPS:
if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
WLog_ERR(TAG,
"gfxredir_recv_legacy_caps_pdu "
"failed with error %" PRIu32 "!",
error);
break;
case GFXREDIR_CMDID_CAPS_ADVERTISE:
if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
WLog_ERR(TAG,
"gfxredir_recv_caps_advertise "
"failed with error %" PRIu32 "!",
error);
break;
case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
WLog_ERR(TAG,
"gfxredir_recv_present_buffer_ask_pdu "
"failed with error %" PRIu32 "!",
error);
break;
default:
error = CHANNEL_RC_BAD_PROC;
WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId);
break;
}
end = Stream_GetPosition(s);
if (end != (beg + header.length))
{
WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32 "", end,
(beg + header.length));
Stream_SetPosition(s, (beg + header.length));
}
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
{
DWORD BytesReturned;
void* buffer;
UINT ret = CHANNEL_RC_OK;
GfxRedirServerPrivate* priv = context->priv;
wStream* s = priv->input_stream;
/* Check whether the dynamic channel is ready */
if (!priv->isReady)
{
if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
&BytesReturned) == FALSE)
{
if (GetLastError() == ERROR_NO_DATA)
return ERROR_NO_DATA;
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
return ERROR_INTERNAL_ERROR;
}
priv->isReady = *((BOOL*)buffer);
WTSFreeMemory(buffer);
}
/* Consume channel event only after the dynamic channel is ready */
if (priv->isReady)
{
Stream_SetPosition(s, 0);
if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned))
{
if (GetLastError() == ERROR_NO_DATA)
return ERROR_NO_DATA;
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
return ERROR_INTERNAL_ERROR;
}
if (BytesReturned < 1)
return CHANNEL_RC_OK;
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
return CHANNEL_RC_NO_MEMORY;
}
if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s),
Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
return ERROR_INTERNAL_ERROR;
}
Stream_SetLength(s, BytesReturned);
Stream_SetPosition(s, 0);
while (Stream_GetPosition(s) < Stream_Length(s))
{
if ((ret = gfxredir_server_receive_pdu(context, s)))
{
WLog_ERR(TAG,
"gfxredir_server_receive_pdu "
"failed with error %" PRIu32 "!",
ret);
return ret;
}
}
}
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
{
GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
GfxRedirServerPrivate* priv = context->priv;
DWORD status;
DWORD nCount;
HANDLE events[8];
UINT error = CHANNEL_RC_OK;
nCount = 0;
events[nCount++] = priv->stopEvent;
events[nCount++] = priv->channelEvent;
while (TRUE)
{
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
break;
}
/* Stop Event */
if (status == WAIT_OBJECT_0)
break;
if ((error = gfxredir_server_handle_messages(context)))
{
WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error);
break;
}
}
ExitThread(error);
return error;
}
/**
* Function description
* Create new stream for single gfxredir packet. The new stream length
* would be required data length + header. The header will be written
* to the stream before return.
*
* @param cmdId
* @param length - data length without header
*
* @return new stream
*/
static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, UINT32 length)
{
UINT error;
GFXREDIR_HEADER header;
wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
goto error;
}
header.cmdId = cmdId;
header.length = GFXREDIR_HEADER_SIZE + length;
if ((error = gfxredir_write_header(s, &header)))
{
WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
goto error;
}
return s;
error:
Stream_Free(s, TRUE);
return NULL;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s)
{
UINT ret;
ULONG written;
if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
ret = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
ret = CHANNEL_RC_OK;
out:
Stream_Free(s, TRUE);
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error)
{
wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT32(s, error->errorCode);
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm)
{
UINT ret;
wStream* s;
if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
{
WLog_ERR(TAG, "length must be greater than header size, failed!");
return ERROR_INVALID_DATA;
}
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
Stream_Write_UINT32(s, graphicsCapsConfirm->version);
Stream_Write_UINT32(s, graphicsCapsConfirm->length);
if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
Stream_Write(s, graphicsCapsConfirm->capsData,
graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
ret = gfxredir_server_packet_send(context, s);
if (ret == CHANNEL_RC_OK)
context->confirmedCapsVersion = graphicsCapsConfirm->version;
return ret;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
const GFXREDIR_OPEN_POOL_PDU* openPool)
{
wStream* s;
if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
{
WLog_ERR(TAG, "open_pool is called with invalid version!");
return ERROR_INTERNAL_ERROR;
}
if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL)
{
WLog_ERR(TAG, "section name must be provided!");
return ERROR_INVALID_DATA;
}
/* make sure null-terminate */
if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
{
WLog_ERR(TAG, "section name must be terminated with NULL!");
return ERROR_INVALID_DATA;
}
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
20 + (openPool->sectionNameLength * 2));
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT64(s, openPool->poolId);
Stream_Write_UINT64(s, openPool->poolSize);
Stream_Write_UINT32(s, openPool->sectionNameLength);
Stream_Write(s, openPool->sectionName, (openPool->sectionNameLength * 2));
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
const GFXREDIR_CLOSE_POOL_PDU* closePool)
{
wStream* s;
if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
{
WLog_ERR(TAG, "close_pool is called with invalid version!");
return ERROR_INTERNAL_ERROR;
}
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT64(s, closePool->poolId);
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
const GFXREDIR_CREATE_BUFFER_PDU* createBuffer)
{
wStream* s;
if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
{
WLog_ERR(TAG, "create_buffer is called with invalid version!");
return ERROR_INTERNAL_ERROR;
}
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT64(s, createBuffer->poolId);
Stream_Write_UINT64(s, createBuffer->bufferId);
Stream_Write_UINT64(s, createBuffer->offset);
Stream_Write_UINT32(s, createBuffer->stride);
Stream_Write_UINT32(s, createBuffer->width);
Stream_Write_UINT32(s, createBuffer->height);
Stream_Write_UINT32(s, createBuffer->format);
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer)
{
wStream* s;
if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
{
WLog_ERR(TAG, "destroy_buffer is called with invalid version!");
return ERROR_INTERNAL_ERROR;
}
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT64(s, destroyBuffer->bufferId);
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer)
{
UINT32 length;
wStream* s;
RECTANGLE_32 dummyRect = { 0, 0, 0, 0 };
if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
{
WLog_ERR(TAG, "present_buffer is called with invalid version!");
return ERROR_INTERNAL_ERROR;
}
if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
{
WLog_ERR(TAG, "numOpaqueRects is larger than its limit!");
return ERROR_INVALID_DATA;
}
length = 64 + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
sizeof(RECTANGLE_32));
s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
if (!s)
{
WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT64(s, presentBuffer->timestamp);
Stream_Write_UINT64(s, presentBuffer->presentId);
Stream_Write_UINT64(s, presentBuffer->windowId);
Stream_Write_UINT64(s, presentBuffer->bufferId);
Stream_Write_UINT32(s, presentBuffer->orientation);
Stream_Write_UINT32(s, presentBuffer->targetWidth);
Stream_Write_UINT32(s, presentBuffer->targetHeight);
Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
if (presentBuffer->numOpaqueRects)
Stream_Write(s, presentBuffer->opaqueRects,
(presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32)));
else
Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32));
return gfxredir_server_packet_send(context, s);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_server_open(GfxRedirServerContext* context)
{
UINT rc = ERROR_INTERNAL_ERROR;
GfxRedirServerPrivate* priv = context->priv;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
void* buffer;
buffer = NULL;
priv->SessionId = WTS_CURRENT_SESSION;
if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
priv->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
priv->gfxredir_channel = (HANDLE)WTSVirtualChannelOpenEx(
priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!priv->gfxredir_channel)
{
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
rc = GetLastError();
goto out_close;
}
/* Query for channel event handle */
if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) ||
(BytesReturned != sizeof(HANDLE)))
{
WLog_ERR(TAG,
"WTSVirtualChannelQuery failed "
"or invalid returned size(%" PRIu32 ")",
BytesReturned);
if (buffer)
WTSFreeMemory(buffer);
rc = ERROR_INTERNAL_ERROR;
goto out_close;
}
CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
if (priv->thread == NULL)
{
if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
rc = ERROR_INTERNAL_ERROR;
}
if (!(priv->thread =
CreateThread(NULL, 0, gfxredir_server_thread_func, (void*)context, 0, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
CloseHandle(priv->stopEvent);
priv->stopEvent = NULL;
rc = ERROR_INTERNAL_ERROR;
}
}
return CHANNEL_RC_OK;
out_close:
WTSVirtualChannelClose(priv->gfxredir_channel);
priv->gfxredir_channel = NULL;
priv->channelEvent = NULL;
return rc;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT gfxredir_server_close(GfxRedirServerContext* context)
{
UINT error = CHANNEL_RC_OK;
GfxRedirServerPrivate* priv = context->priv;
if (priv->thread)
{
SetEvent(priv->stopEvent);
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(priv->thread);
CloseHandle(priv->stopEvent);
priv->thread = NULL;
priv->stopEvent = NULL;
}
if (priv->gfxredir_channel)
{
WTSVirtualChannelClose(priv->gfxredir_channel);
priv->gfxredir_channel = NULL;
}
return error;
}
GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
{
GfxRedirServerContext* context;
GfxRedirServerPrivate* priv;
context = (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext));
if (!context)
{
WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
return NULL;
}
priv = context->priv = (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate));
if (!context->priv)
{
WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
goto out_free;
}
priv->input_stream = Stream_New(NULL, 4);
if (!priv->input_stream)
{
WLog_ERR(TAG, "Stream_New failed!");
goto out_free_priv;
}
context->vcm = vcm;
context->Open = gfxredir_server_open;
context->Close = gfxredir_server_close;
context->Error = gfxredir_send_error;
context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
context->OpenPool = gfxredir_send_open_pool;
context->ClosePool = gfxredir_send_close_pool;
context->CreateBuffer = gfxredir_send_create_buffer;
context->DestroyBuffer = gfxredir_send_destroy_buffer;
context->PresentBuffer = gfxredir_send_present_buffer;
context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
priv->isReady = FALSE;
return context;
out_free_priv:
free(context->priv);
out_free:
free(context);
return NULL;
}
void gfxredir_server_context_free(GfxRedirServerContext* context)
{
if (!context)
return;
gfxredir_server_close(context);
if (context->priv)
{
Stream_Free(context->priv->input_stream, TRUE);
free(context->priv);
}
free(context);
}

View File

@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Microsoft
*
* 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_GFXREDIR_SERVER_MAIN_H
#define FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H
#include <freerdp/server/gfxredir.h>
struct _gfxredir_server_private
{
BOOL isReady;
wStream* input_stream;
HANDLE channelEvent;
HANDLE thread;
HANDLE stopEvent;
DWORD SessionId;
void* gfxredir_channel;
};
#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H */

View File

@ -52,6 +52,7 @@
#include <freerdp/server/rail.h>
#include <freerdp/server/rdpgfx.h>
#include <freerdp/server/disp.h>
#include <freerdp/server/gfxredir.h>
extern void freerdp_channels_dummy(void);
@ -69,6 +70,9 @@ void freerdp_channels_dummy(void)
RailServerContext* rail;
RdpgfxServerContext* rdpgfx;
DispServerContext* disp;
#ifdef WITH_CHANNEL_GFXREDIR
GfxRedirServerContext* gfxredir;
#endif // WITH_CHANNEL_GFXREDIR
audin = audin_server_context_new(NULL);
audin_server_context_free(audin);
rdpsnd = rdpsnd_server_context_new(NULL);
@ -93,6 +97,10 @@ void freerdp_channels_dummy(void)
rdpgfx_server_context_free(rdpgfx);
disp = disp_server_context_new(NULL);
disp_server_context_free(disp);
#ifdef WITH_CHANNEL_GFXREDIR
gfxredir = gfxredir_server_context_new(NULL);
gfxredir_server_context_free(gfxredir);
#endif // WITH_CHANNEL_GFXREDIR
}
/**

View File

@ -58,6 +58,9 @@
#cmakedefine WITH_VAAPI
#cmakedefine WITH_CHANNEL_GFXREDIR
#cmakedefine WITH_CHANNEL_RDPAPPLIST
/* Plugins */
#cmakedefine BUILTIN_CHANNELS
#cmakedefine WITH_RDPDR
@ -84,6 +87,9 @@
#cmakedefine CHANNEL_ENCOMSP
#cmakedefine CHANNEL_ENCOMSP_CLIENT
#cmakedefine CHANNEL_ENCOMSP_SERVER
#cmakedefine CHANNEL_GFXREDIR
#cmakedefine CHANNEL_GFXREDIR_CLIENT
#cmakedefine CHANNEL_GFXREDIR_SERVER
#cmakedefine CHANNEL_PARALLEL
#cmakedefine CHANNEL_PARALLEL_CLIENT
#cmakedefine CHANNEL_PARALLEL_SERVER
@ -93,6 +99,9 @@
#cmakedefine CHANNEL_RAIL
#cmakedefine CHANNEL_RAIL_CLIENT
#cmakedefine CHANNEL_RAIL_SERVER
#cmakedefine CHANNEL_RDPAPPLIST
#cmakedefine CHANNEL_RDPAPPLIST_CLIENT
#cmakedefine CHANNEL_RDPAPPLIST_SERVER
#cmakedefine CHANNEL_RDPDR
#cmakedefine CHANNEL_RDPDR_CLIENT
#cmakedefine CHANNEL_RDPDR_SERVER

View File

@ -0,0 +1,183 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Hideyuki Nagase <hideyukn@microsoft.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_GFXREDIR_H
#define FREERDP_CHANNEL_GFXREDIR_H
#include <freerdp/api.h>
#include <freerdp/types.h>
#define GFXREDIR_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::RemoteAppGraphicsRedirection"
/* GFXREDIR_LEGACY_CAPS_PDU.version */
#define GFXREDIR_CHANNEL_VERSION_LEGACY 1
#define GFXREDIR_CHANNEL_VERSION_MAJOR 2
#define GFXREDIR_CHANNEL_VERSION_MINOR 0
/* GFXREDIR_CAPS_VERSION1 */
#define GFXREDIR_CMDID_LEGACY_CAPS 0x00000001
#define GFXREDIR_CMDID_ERROR 0x00000006
#define GFXREDIR_CMDID_CAPS_ADVERTISE 0x00000008
#define GFXREDIR_CMDID_CAPS_CONFIRM 0x00000009
/* GFXREDIR_CAPS_VERSION2_0 */
#define GFXREDIR_CMDID_OPEN_POOL 0x0000000a
#define GFXREDIR_CMDID_CLOSE_POOL 0x0000000b
#define GFXREDIR_CMDID_CREATE_BUFFER 0x0000000c
#define GFXREDIR_CMDID_DESTROY_BUFFER 0x0000000d
#define GFXREDIR_CMDID_PRESENT_BUFFER 0x0000000e
#define GFXREDIR_CMDID_PRESENT_BUFFER_ACK 0x0000000f
/* GFXREDIR_HEADER */
#define GFXREDIR_HEADER_SIZE 8
/* GFXREDIR_CAPS_HEADER */
#define GFXREDIR_CAPS_HEADER_SIZE 12
/* GFXREDIR_CAPS_HEADER.signature */
#define GFXREDIR_CAPS_SIGNATURE 0x53504143 /* = 'SPAC' */
/* GFXREDIR_CAPS_HEADER.version */
#define GFXREDIR_CAPS_VERSION1 0x1
#define GFXREDIR_CAPS_VERSION2_0 0x2000
/* GFXREDIR_CREATE_BUFFER_PDU.format */
#define GFXREDIR_BUFFER_PIXEL_FORMAT_XRGB_8888 1
#define GFXREDIR_BUFFER_PIXEL_FORMAT_ARGB_8888 2
/* GFXREDIR_PRESENT_BUFFER_PDU.numOpaqueRects */
#define GFXREDIR_MAX_OPAQUE_RECTS 0x10
struct _GFXREDIR_HEADER
{
UINT32 cmdId;
UINT32 length;
};
typedef struct _GFXREDIR_HEADER GFXREDIR_HEADER;
struct _GFXREDIR_LEGACY_CAPS_PDU
{
UINT16 version; // GFXREDIR_CHANNEL_VERSION_LEGACY
};
typedef struct _GFXREDIR_LEGACY_CAPS_PDU GFXREDIR_LEGACY_CAPS_PDU;
struct _GFXREDIR_CAPS_HEADER
{
UINT32 signature; // GFXREDIR_CAPS_SIGNATURE
UINT32 version; // GFXREDIR_CAPS_VERSION
UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData
const BYTE capsData[0]; // GFXREDIR_CAPS_HEADER.length - GFXREDIR_CAPS_HEADER_SIZE
};
typedef struct _GFXREDIR_CAPS_HEADER GFXREDIR_CAPS_HEADER;
struct _GFXREDIR_CAPS_V2_0_PDU
{
GFXREDIR_CAPS_HEADER header;
UINT32 supportedFeatures; /* Reserved for future extensions */
};
typedef struct _GFXREDIR_CAPS_V2_0_PDU GFXREDIR_CAPS_V2_0_PDU;
struct _GFXREDIR_ERROR_PDU
{
UINT32 errorCode;
};
typedef struct _GFXREDIR_ERROR_PDU GFXREDIR_ERROR_PDU;
struct _GFXREDIR_CAPS_ADVERTISE_PDU
{
UINT32 length; // length of caps;
const BYTE* caps; // points variable length array of GFXREDIR_CAPS_HEADER.
};
typedef struct _GFXREDIR_CAPS_ADVERTISE_PDU GFXREDIR_CAPS_ADVERTISE_PDU;
struct _GFXREDIR_CAPS_CONFIRM_PDU
{
UINT32 version; // confirmed version, must be one of advertised by client.
UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData.
const BYTE* capsData; // confirmed capsData from selected GFXREDIR_CAPS_HEADER.capsData.
};
typedef struct _GFXREDIR_CAPS_CONFIRM_PDU GFXREDIR_CAPS_CONFIRM_PDU;
struct _GFXREDIR_OPEN_POOL_PDU
{
UINT64 poolId;
UINT64 poolSize;
UINT32 sectionNameLength; // number of charactor, must include null terminated char.
const unsigned short* sectionName; // Windows-style 2 bytes wchar_t with null-terminated.
};
typedef struct _GFXREDIR_OPEN_POOL_PDU GFXREDIR_OPEN_POOL_PDU;
struct _GFXREDIR_CLOSE_POOL_PDU
{
UINT64 poolId;
};
typedef struct _GFXREDIR_CLOSE_POOL_PDU GFXREDIR_CLOSE_POOL_PDU;
struct _GFXREDIR_CREATE_BUFFER_PDU
{
UINT64 poolId;
UINT64 bufferId;
UINT64 offset;
UINT32 stride;
UINT32 width;
UINT32 height;
UINT32 format; // GFXREDIR_BUFFER_PIXEL_FORMAT_
};
typedef struct _GFXREDIR_CREATE_BUFFER_PDU GFXREDIR_CREATE_BUFFER_PDU;
struct _GFXREDIR_DESTROY_BUFFER_PDU
{
UINT64 bufferId;
};
typedef struct _GFXREDIR_DESTROY_BUFFER_PDU GFXREDIR_DESTROY_BUFFER_PDU;
struct _GFXREDIR_PRESENT_BUFFER_PDU
{
UINT64 timestamp;
UINT64 presentId;
UINT64 windowId;
UINT64 bufferId;
UINT32 orientation; // 0, 90, 180 or 270.
UINT32 targetWidth;
UINT32 targetHeight;
RECTANGLE_32 dirtyRect;
UINT32 numOpaqueRects;
RECTANGLE_32* opaqueRects;
};
typedef struct _GFXREDIR_PRESENT_BUFFER_PDU GFXREDIR_PRESENT_BUFFER_PDU;
struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU
{
UINT64 windowId;
UINT64 presentId;
};
typedef struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU GFXREDIR_PRESENT_BUFFER_ACK_PDU;
#endif /* FREERDP_CHANNEL_GFXREDIR_H */

View File

@ -0,0 +1,102 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPXXXX Remote App Graphics Redirection Virtual Channel Extension
*
* Copyright 2020 Microsoft
*
* 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_GFXREDIR_SERVER_GFXREDIR_H
#define FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H
#include <freerdp/channels/gfxredir.h>
#include <freerdp/freerdp.h>
#include <freerdp/api.h>
#include <freerdp/types.h>
typedef struct _gfxredir_server_private GfxRedirServerPrivate;
typedef struct _gfxredir_server_context GfxRedirServerContext;
typedef UINT (*psGfxRedirOpen)(GfxRedirServerContext* context);
typedef UINT (*psGfxRedirClose)(GfxRedirServerContext* context);
typedef UINT (*psGfxRedirError)(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error);
typedef UINT (*psGfxRedirGraphicsRedirectionLegacyCaps)(
GfxRedirServerContext* context, const GFXREDIR_LEGACY_CAPS_PDU* graphicsCaps);
typedef UINT (*psGfxRedirGraphicsRedirectionCapsAdvertise)(
GfxRedirServerContext* context, const GFXREDIR_CAPS_ADVERTISE_PDU* graphicsCapsAdvertise);
typedef UINT (*psGfxRedirGraphicsRedirectionCapsConfirm)(
GfxRedirServerContext* context, const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm);
typedef UINT (*psGfxRedirOpenPool)(GfxRedirServerContext* context,
const GFXREDIR_OPEN_POOL_PDU* openPool);
typedef UINT (*psGfxRedirClosePool)(GfxRedirServerContext* context,
const GFXREDIR_CLOSE_POOL_PDU* closePool);
typedef UINT (*psGfxRedirCreateBuffer)(GfxRedirServerContext* context,
const GFXREDIR_CREATE_BUFFER_PDU* createBuffer);
typedef UINT (*psGfxRedirDestroyBuffer)(GfxRedirServerContext* context,
const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer);
typedef UINT (*psGfxRedirPresentBuffer)(GfxRedirServerContext* context,
const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer);
typedef UINT (*psGfxRedirPresentBufferAck)(GfxRedirServerContext* context,
const GFXREDIR_PRESENT_BUFFER_ACK_PDU* presentBufferAck);
struct _gfxredir_server_context
{
void* custom;
HANDLE vcm;
psGfxRedirOpen Open;
psGfxRedirClose Close;
psGfxRedirError Error;
psGfxRedirGraphicsRedirectionLegacyCaps GraphicsRedirectionLegacyCaps;
psGfxRedirGraphicsRedirectionCapsAdvertise GraphicsRedirectionCapsAdvertise;
psGfxRedirGraphicsRedirectionCapsConfirm GraphicsRedirectionCapsConfirm;
psGfxRedirOpenPool OpenPool;
psGfxRedirClosePool ClosePool;
psGfxRedirCreateBuffer CreateBuffer;
psGfxRedirDestroyBuffer DestroyBuffer;
psGfxRedirPresentBuffer PresentBuffer;
psGfxRedirPresentBufferAck PresentBufferAck;
GfxRedirServerPrivate* priv;
rdpContext* rdpcontext;
UINT32 confirmedCapsVersion;
};
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_API GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm);
FREERDP_API void gfxredir_server_context_free(GfxRedirServerContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H */

View File

@ -74,6 +74,15 @@ struct _RECTANGLE_16
};
typedef struct _RECTANGLE_16 RECTANGLE_16;
struct _RECTANGLE_32
{
UINT32 left;
UINT32 top;
UINT32 width;
UINT32 height;
};
typedef struct _RECTANGLE_32 RECTANGLE_32;
/* Plugin events */
#include <freerdp/message.h>