add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()
+if(WITH_SERVER_CHANNELS)
+ add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
rdpgfx_main.h
rdpgfx_codec.c
rdpgfx_codec.h
- rdpgfx_common.c
- rdpgfx_common.h)
+ ../rdpgfx_common.c
+ ../rdpgfx_common.h)
include_directories(..)
"RDPGFX_CMDID_CAPSADVERTISE",
"RDPGFX_CMDID_CAPSCONFIRM",
"RDPGFX_CMDID_UNUSED_0014",
- "RDPGFX_CMDID_MAPSURFACETOWINDOW"
+ "RDPGFX_CMDID_MAPSURFACETOWINDOW",
+ "RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE"
};
const char* rdpgfx_get_cmd_id_string(UINT16 cmdId)
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+define_channel_server("rdpgfx")
+
+set(${MODULE_PREFIX}_SRCS
+ rdpgfx_main.c
+ rdpgfx_main.h
+ ../rdpgfx_common.c
+ ../rdpgfx_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")
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Graphics Pipeline Extension
+ *
+ * Copyright 2016 Jiang Zihao <zihao.jiang@yahoo.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.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 <freerdp/channels/wtsvc.h>
+#include <freerdp/channels/log.h>
+
+#include "rdpgfx_common.h"
+#include "rdpgfx_main.h"
+
+#define TAG CHANNELS_TAG("rdpgfx.server")
+#define RDPGFX_RESET_GRAPHICS_PDU_SIZE 340
+
+/**
+ * Function description
+ * Create new stream for rdpgfx packet. The new stream length
+ * would be required data length + header. The header will be written
+ * to the stream before return, but the pduLength field might be
+ * changed in rdpgfx_server_packet_send.
+ *
+ * @return new stream
+ */
+static wStream* rdpgfx_server_packet_new(UINT16 cmdId, UINT32 dataLen)
+{
+ UINT error;
+ wStream* s;
+ RDPGFX_HEADER header;
+
+ header.flags = 0;
+ header.cmdId = cmdId;
+ header.pduLength = RDPGFX_HEADER_SIZE + dataLen;
+
+ s = Stream_New(NULL, header.pduLength);
+
+ if(!s)
+ {
+ WLog_ERR(TAG, "Stream_New failed!");
+ return NULL;
+ }
+
+ /* Write header. Note that actual length will be filled
+ * after the entire packet has been constructed. */
+ if ((error = rdpgfx_write_header(s, &header)))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_header failed with error %lu!", error);
+ return NULL;
+ }
+
+ return s;
+}
+
+/**
+ * Function description
+ * Send the stream for rdpgfx packet. The packet would be compressed
+ * according to [MS-RDPEGFX].
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_server_packet_send(RdpgfxServerContext* context, wStream* s)
+{
+ UINT error;
+ UINT32 flags = 0;
+ ULONG written;
+ BYTE* pSrcData = Stream_Buffer(s);
+ UINT32 SrcSize = Stream_GetPosition(s);
+ wStream* fs;
+
+ /* Fill actual length */
+ Stream_SetPosition(s, sizeof(RDPGFX_HEADER) - sizeof(UINT32));
+ Stream_Write_UINT32(s, SrcSize); /* pduLength (4 bytes) */
+
+ /* Allocate new stream with enough capacity. Additional overhead is
+ * descriptor (1 bytes) + segmentCount (2 bytes) + uncompressedSize (4 bytes)
+ * + segmentCount * size (4 bytes) */
+ fs = Stream_New(NULL, SrcSize + 7
+ + (SrcSize/ZGFX_SEGMENTED_MAXSIZE + 1) * 4);
+
+ if (zgfx_compress_to_stream(context->priv->zgfx, fs, pSrcData, SrcSize, &flags) < 0)
+ {
+ WLog_ERR(TAG, "zgfx_compress_to_stream failed!");
+ error = ERROR_INTERNAL_ERROR;
+ goto out;
+ }
+
+ error = WTSVirtualChannelWrite(context->priv->rdpgfx_channel, (PCHAR) Stream_Buffer(fs),
+ Stream_GetPosition(fs), &written)
+ ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
+
+out:
+ Stream_Free(fs, TRUE);
+ Stream_Free(s, TRUE);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_caps_confirm_pdu(RdpgfxServerContext* context, RDPGFX_CAPS_CONFIRM_PDU* capsConfirm)
+{
+ RDPGFX_CAPSET* capsSet = capsConfirm->capsSet;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_CAPSCONFIRM,
+ sizeof(RDPGFX_CAPSET) + sizeof(capsSet->flags));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, capsSet->version); /* version (4 bytes) */
+ Stream_Write_UINT32(s, sizeof(capsSet->flags)); /* capsDataLength (4 bytes) */
+ Stream_Write_UINT32(s, capsSet->flags); /* capsData (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_reset_graphics_pdu(RdpgfxServerContext* context, RDPGFX_RESET_GRAPHICS_PDU* pdu)
+{
+ UINT32 index;
+ MONITOR_DEF* monitor;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_RESETGRAPHICS,
+ RDPGFX_RESET_GRAPHICS_PDU_SIZE - RDPGFX_HEADER_SIZE);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, pdu->width); /* width (4 bytes) */
+ Stream_Write_UINT32(s, pdu->height); /* height (4 bytes) */
+ Stream_Write_UINT32(s, pdu->monitorCount); /* monitorCount (4 bytes) */
+
+ for (index = 0; index < pdu->monitorCount; index++)
+ {
+ monitor = &(pdu->monitorDefArray[index]);
+ Stream_Write_UINT32(s, monitor->left); /* left (4 bytes) */
+ Stream_Write_UINT32(s, monitor->top); /* top (4 bytes) */
+ Stream_Write_UINT32(s, monitor->right); /* right (4 bytes) */
+ Stream_Write_UINT32(s, monitor->bottom); /* bottom (4 bytes) */
+ Stream_Write_UINT32(s, monitor->flags); /* flags (4 bytes) */
+ }
+
+ /* pad (total size must be 340 bytes) */
+ if (Stream_GetPosition(s) > RDPGFX_RESET_GRAPHICS_PDU_SIZE)
+ {
+ WLog_ERR(TAG, "Invalid RDPGFX_RESET_GRAPHICS_PDU data!");
+ return CHANNEL_RC_NO_BUFFER;
+ }
+ Stream_SetPosition(s, RDPGFX_RESET_GRAPHICS_PDU_SIZE);
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_evict_cache_entry_pdu(RdpgfxServerContext* context, RDPGFX_EVICT_CACHE_ENTRY_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_EVICTCACHEENTRY,
+ sizeof(RDPGFX_EVICT_CACHE_ENTRY_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_cache_import_reply_pdu(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_REPLY_PDU* pdu)
+{
+ UINT16 index;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_CACHEIMPORTREPLY,
+ sizeof(RDPGFX_CACHE_IMPORT_REPLY_PDU) + sizeof(UINT16) * pdu->importedEntriesCount);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->importedEntriesCount); /* importedEntriesCount (2 bytes) */
+ for (index = 0; index < pdu->importedEntriesCount; index++)
+ {
+ Stream_Write_UINT16(s, pdu->cacheSlots[index]); /* cacheSlot (2 bytes) */
+ }
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_create_surface_pdu(RdpgfxServerContext* context, RDPGFX_CREATE_SURFACE_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_CREATESURFACE,
+ sizeof(RDPGFX_CREATE_SURFACE_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT16(s, pdu->width); /* width (2 bytes) */
+ Stream_Write_UINT16(s, pdu->height); /* height (2 bytes) */
+ Stream_Write_UINT8(s, pdu->pixelFormat); /* RDPGFX_PIXELFORMAT (1 byte) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rdpgfx_send_delete_surface_pdu(RdpgfxServerContext* context, RDPGFX_DELETE_SURFACE_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_DELETESURFACE,
+ sizeof(RDPGFX_DELETE_SURFACE_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_start_frame_pdu(RdpgfxServerContext* context, RDPGFX_START_FRAME_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_STARTFRAME,
+ sizeof(RDPGFX_START_FRAME_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, pdu->timestamp); /* timestamp (4 bytes) */
+ Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_end_frame_pdu(RdpgfxServerContext* context, RDPGFX_END_FRAME_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_ENDFRAME,
+ sizeof(RDPGFX_END_FRAME_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_write_h264_metablock(RdpgfxServerContext* context, wStream* s,
+ RDPGFX_H264_METABLOCK* meta)
+{
+ UINT32 index;
+ RECTANGLE_16* regionRect;
+ RDPGFX_H264_QUANT_QUALITY* quantQualityVal;
+ UINT error = CHANNEL_RC_OK;
+
+ Stream_Write_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */
+
+ for (index = 0; index < meta->numRegionRects; index++)
+ {
+ regionRect = &(meta->regionRects[index]);
+ if ((error = rdpgfx_write_rect16(s, regionRect)))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error);
+ return error;
+ }
+ }
+
+ for (index = 0; index < meta->numRegionRects; index++)
+ {
+ quantQualityVal = &(meta->quantQualityVals[index]);
+
+ Stream_Write_UINT8(s, quantQualityVal->qp
+ | (quantQualityVal->r << 6)
+ | (quantQualityVal->p << 7)); /* qpVal (1 byte) */
+ Stream_Write_UINT8(s, quantQualityVal->qualityVal); /* qualityVal (1 byte) */
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ * Write RFX_AVC420_BITMAP_STREAM structure to stream
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static INLINE UINT rdpgfx_write_h264_avc420(RdpgfxServerContext* context, wStream* s,
+ RDPGFX_AVC420_BITMAP_STREAM* havc420)
+{
+ UINT error = CHANNEL_RC_OK;
+ if ((error = rdpgfx_write_h264_metablock(context, s, &(havc420->meta))))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_h264_metablock failed with error %lu!", error);
+ return error;
+ }
+
+ Stream_Write(s, havc420->data, havc420->length);
+ return error;
+}
+
+/**
+ * Function description
+ * Estimate RFX_AVC420_BITMAP_STREAM structure size in stream
+ *
+ * @return estimated size
+ */
+static INLINE UINT32 rdpgfx_estimate_h264_avc420(RDPGFX_AVC420_BITMAP_STREAM* havc420)
+{
+ return sizeof(UINT32) /* numRegionRects */
+ + (sizeof(RECTANGLE_16) + 2) /* regionRects + quantQualityVals */
+ * havc420->meta.numRegionRects
+ + havc420->length;
+}
+/**
+ * Function description
+ * Send RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2
+ * message according to codecId
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_surface_command(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd)
+{
+ wStream* s;
+ RDPGFX_AVC420_BITMAP_STREAM *havc420 = NULL;
+ RDPGFX_AVC444_BITMAP_STREAM *havc444 = NULL;
+ UINT32 h264Size = 0;
+ UINT32 bitmapDataStart = 0;
+ UINT32 bitmapDataLength = 0;
+
+ /* Create new stream according to codec. */
+ if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE ||
+ cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2)
+ {
+ s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_WIRETOSURFACE_2,
+ sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_2) + cmd->length);
+ }
+ else if (cmd->codecId == RDPGFX_CODECID_AVC420)
+ {
+ havc420 = (RDPGFX_AVC420_BITMAP_STREAM *)cmd->extra;
+ h264Size = rdpgfx_estimate_h264_avc420(havc420);
+
+ s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_WIRETOSURFACE_1,
+ sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + h264Size);
+ }
+ else if (cmd->codecId == RDPGFX_CODECID_AVC444)
+ {
+ havc444 = (RDPGFX_AVC444_BITMAP_STREAM *)cmd->extra;
+ h264Size = sizeof(UINT32); /* cbAvc420EncodedBitstream1 */
+
+ /* avc420EncodedBitstream1 */
+ havc420 = &(havc444->bitstream[0]);
+ h264Size += rdpgfx_estimate_h264_avc420(havc420);
+
+ /* avc420EncodedBitstream2 */
+ if (havc444->LC == 0)
+ {
+ havc420 = &(havc444->bitstream[1]);
+ h264Size += rdpgfx_estimate_h264_avc420(havc420);
+ }
+
+ s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_WIRETOSURFACE_1,
+ sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + h264Size);
+ }
+ else
+ {
+ s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_WIRETOSURFACE_1,
+ sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + sizeof(cmd->length));
+ }
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ /* Reformat and send */
+ if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE ||
+ cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2)
+ {
+ Stream_Write_UINT16(s, cmd->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT16(s, cmd->codecId); /* codecId (2 bytes) */
+ Stream_Write_UINT32(s, cmd->contextId); /* codecContextId (4 bytes) */
+ Stream_Write_UINT8(s, cmd->format); /* pixelFormat (1 byte) */
+
+ Stream_Write_UINT32(s, cmd->length); /* bitmapDataLength (4 bytes) */
+ Stream_Write(s, cmd->data, cmd->length);
+
+ return rdpgfx_server_packet_send(context, s);
+ }
+ else
+ {
+ Stream_Write_UINT16(s, cmd->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT16(s, cmd->codecId); /* codecId (2 bytes) */
+ Stream_Write_UINT8(s, cmd->format); /* pixelFormat (1 byte) */
+
+ Stream_Write_UINT16(s, cmd->left); /* left (2 bytes) */
+ Stream_Write_UINT16(s, cmd->top); /* top (2 bytes) */
+ Stream_Write_UINT16(s, cmd->right); /* right (2 bytes) */
+ Stream_Write_UINT16(s, cmd->bottom); /* bottom (2 bytes) */
+
+ Stream_Write_UINT32(s, cmd->length); /* bitmapDataLength (4 bytes) */
+ bitmapDataStart = Stream_GetPosition(s);
+
+ if (cmd->codecId == RDPGFX_CODECID_AVC420)
+ {
+ havc420 = (RDPGFX_AVC420_BITMAP_STREAM *)cmd->extra;
+ rdpgfx_write_h264_avc420(context, s, havc420);
+ }
+ else if (cmd->codecId == RDPGFX_CODECID_AVC444)
+ {
+ havc444 = (RDPGFX_AVC444_BITMAP_STREAM *)cmd->extra;
+ havc420 = &(havc444->bitstream[0]);
+
+ /* avc420EncodedBitstreamInfo (4 bytes) */
+ Stream_Write_UINT32(s, havc420->length | (havc444->LC << 30UL));
+
+ /* avc420EncodedBitstream1 */
+ rdpgfx_write_h264_avc420(context, s, havc420);
+
+ /* avc420EncodedBitstream2 */
+ if (havc444->LC == 0)
+ {
+ havc420 = &(havc444->bitstream[0]);
+ rdpgfx_write_h264_avc420(context, s, havc420);
+ }
+ }
+ else
+ {
+ Stream_Write(s, cmd->data, cmd->length);
+ }
+
+ assert(Stream_GetPosition(s) <= Stream_Capacity(s));
+
+ /* Fill actual bitmap data length */
+ bitmapDataLength = Stream_GetPosition(s) - bitmapDataStart;
+ Stream_SetPosition(s, bitmapDataStart - sizeof(UINT32));
+ Stream_Write_UINT32(s, bitmapDataLength); /* bitmapDataLength (4 bytes) */
+ Stream_Seek(s, bitmapDataLength);
+
+ return rdpgfx_server_packet_send(context, s);
+ }
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_delete_encoding_context_pdu(RdpgfxServerContext* context, RDPGFX_DELETE_ENCODING_CONTEXT_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_DELETEENCODINGCONTEXT,
+ sizeof(RDPGFX_DELETE_ENCODING_CONTEXT_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT32(s, pdu->codecContextId); /* codecContextId (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT rdpgfx_send_solid_fill_pdu(RdpgfxServerContext* context, RDPGFX_SOLID_FILL_PDU* pdu)
+{
+ UINT16 index;
+ RECTANGLE_16* fillRect;
+ UINT error;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_SOLIDFILL,
+ sizeof(RDPGFX_SOLID_FILL_PDU) + sizeof(RECTANGLE_16) * pdu->fillRectCount);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ if ((error = rdpgfx_write_color32(s, &(pdu->fillPixel)))) /* fillPixel (4 bytes) */
+ {
+ WLog_ERR(TAG, "rdpgfx_write_color32 failed with error %lu!", error);
+ return error;
+ }
+
+ Stream_Write_UINT16(s, pdu->fillRectCount); /* fillRectCount (2 bytes) */
+ for (index = 0; index < pdu->fillRectCount; index++)
+ {
+ fillRect = &(pdu->fillRects[index]);
+ if ((error = rdpgfx_write_rect16(s, fillRect)))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error);
+ return error;
+ }
+ }
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_surface_to_surface_pdu(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_SURFACE_PDU* pdu)
+{
+ UINT16 index;
+ UINT error;
+ RDPGFX_POINT16* destPt;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_SURFACETOSURFACE,
+ sizeof(RDPGFX_SURFACE_TO_SURFACE_PDU) + sizeof(RDPGFX_POINT16) * pdu->destPtsCount);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceIdSrc); /* surfaceIdSrc (2 bytes) */
+ Stream_Write_UINT16(s, pdu->surfaceIdDest); /* surfaceIdDest (2 bytes) */
+
+ if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) /* rectSrc (8 bytes ) */
+ {
+ WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error);
+ return error;
+ }
+
+ Stream_Write_UINT16(s, pdu->destPtsCount); /* destPtsCount (2 bytes) */
+ for (index = 0; index < pdu->destPtsCount; index++)
+ {
+ destPt = &(pdu->destPts[index]);
+ if ((error = rdpgfx_write_point16(s, destPt)))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_point16 failed with error %lu!", error);
+ return error;
+ }
+ }
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_surface_to_cache_pdu(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_CACHE_PDU* pdu)
+{
+ UINT error;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_SURFACETOCACHE,
+ sizeof(RDPGFX_SURFACE_TO_CACHE_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT64(s, pdu->cacheKey); /* cacheKey (8 bytes) */
+ Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */
+ if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) /* rectSrc (8 bytes ) */
+ {
+ WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error);
+ return error;
+ }
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_cache_to_surface_pdu(RdpgfxServerContext* context, RDPGFX_CACHE_TO_SURFACE_PDU* pdu)
+{
+ UINT16 index;
+ UINT error;
+ RDPGFX_POINT16* destPt;
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_CACHETOSURFACE,
+ sizeof(RDPGFX_CACHE_TO_SURFACE_PDU) + sizeof(RDPGFX_POINT16) * pdu->destPtsCount);
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT16(s, pdu->destPtsCount); /* destPtsCount (2 bytes) */
+
+ for (index = 0; index < pdu->destPtsCount; index++)
+ {
+ destPt = &(pdu->destPts[index]);
+ if ((error = rdpgfx_write_point16(s, destPt)))
+ {
+ WLog_ERR(TAG, "rdpgfx_write_point16 failed with error %lu", error);
+ return error;
+ }
+ }
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_map_surface_to_output_pdu(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_MAPSURFACETOOUTPUT,
+ sizeof(RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* reserved (2 bytes). Must be 0 */
+ Stream_Write_UINT32(s, pdu->outputOriginX); /* outputOriginX (4 bytes) */
+ Stream_Write_UINT32(s, pdu->outputOriginY); /* outputOriginY (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_send_map_surface_to_window_pdu(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* pdu)
+{
+ wStream* s = rdpgfx_server_packet_new(
+ RDPGFX_CMDID_MAPSURFACETOWINDOW,
+ sizeof(RDPGFX_MAP_SURFACE_TO_WINDOW_PDU));
+
+ if (!s)
+ {
+ WLog_ERR(TAG, "rdpgfx_server_packet_new failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */
+ Stream_Write_UINT64(s, pdu->windowId); /* windowId (8 bytes) */
+ Stream_Write_UINT32(s, pdu->mappedWidth); /* mappedWidth (4 bytes) */
+ Stream_Write_UINT32(s, pdu->mappedHeight); /* mappedHeight (4 bytes) */
+
+ return rdpgfx_server_packet_send(context, s);
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_recv_frame_acknowledge_pdu(RdpgfxServerContext* context, wStream* s)
+{
+ RDPGFX_FRAME_ACKNOWLEDGE_PDU pdu;
+ UINT error = CHANNEL_RC_OK;
+
+ Stream_Read_UINT32(s, pdu.queueDepth); /* queueDepth (4 bytes) */
+ Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
+ Stream_Read_UINT32(s, pdu.totalFramesDecoded); /* totalFramesDecoded (4 bytes) */
+
+ if (context)
+ {
+ IFCALLRET(context->FrameAcknowledge, error, context, &pdu);
+ if (error)
+ WLog_ERR(TAG, "context->FrameAcknowledge failed with error %lu", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_recv_cache_import_offer_pdu(RdpgfxServerContext* context, wStream* s)
+{
+ UINT16 index;
+ RDPGFX_CACHE_IMPORT_OFFER_PDU pdu;
+ RDPGFX_CACHE_ENTRY_METADATA* cacheEntries;
+ UINT error = CHANNEL_RC_OK;
+
+ Stream_Read_UINT16(s, pdu.cacheEntriesCount); /* cacheEntriesCount (2 bytes) */
+ pdu.cacheEntries = (RDPGFX_CACHE_ENTRY_METADATA*)
+ calloc(pdu.cacheEntriesCount, sizeof(RDPGFX_CACHE_ENTRY_METADATA));
+
+ if (!pdu.cacheEntries)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ for (index = 0; index < pdu.cacheEntriesCount; index++)
+ {
+ cacheEntries = &(pdu.cacheEntries[index]);
+ Stream_Read_UINT64(s, cacheEntries->cacheKey); /* cacheKey (8 bytes) */
+ Stream_Read_UINT32(s, cacheEntries->bitmapLength); /* bitmapLength (4 bytes) */
+ }
+
+ if (context)
+ {
+ IFCALLRET(context->CacheImportOffer, error, context, &pdu);
+ if (error)
+ WLog_ERR(TAG, "context->CacheImportOffer failed with error %lu", error);
+ }
+
+ free(pdu.cacheEntries);
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_recv_caps_advertise_pdu(RdpgfxServerContext* context, wStream* s)
+{
+ UINT16 index;
+ RDPGFX_CAPSET* capsSet;
+ RDPGFX_CAPSET capsSets[3];
+ RDPGFX_CAPS_ADVERTISE_PDU pdu;
+ UINT error = CHANNEL_RC_OK;
+
+ Stream_Read_UINT16(s, pdu.capsSetCount); /* capsSetCount (2 bytes) */
+ pdu.capsSets = (RDPGFX_CAPSET*) capsSets;
+
+ for (index = 0; index < pdu.capsSetCount; index++)
+ {
+ capsSet = &(pdu.capsSets[index]);
+ Stream_Read_UINT32(s, capsSet->version); /* version (4 bytes) */
+ Stream_Seek(s, 4); /* capsDataLength (4 bytes) */
+ Stream_Read_UINT32(s, capsSet->flags); /* capsData (4 bytes) */
+ }
+
+ if (context)
+ {
+ IFCALLRET(context->CapsAdvertise, error, context, &pdu);
+ if (error)
+ WLog_ERR(TAG, "context->CapsAdvertise failed with error %lu", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_recv_qoe_frame_acknowledge_pdu(RdpgfxServerContext* context, wStream* s)
+{
+ RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU pdu;
+ UINT error = CHANNEL_RC_OK;
+
+ Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */
+ Stream_Read_UINT32(s, pdu.timestamp); /* timestamp (4 bytes) */
+ Stream_Read_UINT16(s, pdu.timeDiffSE); /* timeDiffSE (2 bytes) */
+ Stream_Read_UINT16(s, pdu.timeDiffEDR); /* timeDiffEDR (2 bytes) */
+
+ if (context)
+ {
+ IFCALLRET(context->QoeFrameAcknowledge, error, context, &pdu);
+ if (error)
+ WLog_ERR(TAG, "context->QoeFrameAcknowledge failed with error %lu", error);
+ }
+
+ return error;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_server_receive_pdu(RdpgfxServerContext* context, wStream* s)
+{
+ int beg, end;
+ RDPGFX_HEADER header;
+ UINT error = CHANNEL_RC_OK;
+
+ beg = Stream_GetPosition(s);
+
+ if ((error = rdpgfx_read_header(s, &header)))
+ {
+ WLog_ERR(TAG, "rdpgfx_read_header failed with error %lu!", error);
+ return error;
+ }
+
+ WLog_DBG(TAG, "cmdId: %s (0x%04X) flags: 0x%04X pduLength: %d",
+ rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId, header.flags, header.pduLength);
+
+ switch (header.cmdId)
+ {
+ case RDPGFX_CMDID_FRAMEACKNOWLEDGE:
+ if ((error = rdpgfx_recv_frame_acknowledge_pdu(context, s)))
+ WLog_ERR(TAG, "rdpgfx_recv_frame_acknowledge_pdu failed with error %lu!", error);
+ break;
+
+ case RDPGFX_CMDID_CACHEIMPORTOFFER:
+ if ((error = rdpgfx_recv_cache_import_offer_pdu(context, s)))
+ WLog_ERR(TAG, "rdpgfx_recv_cache_import_offer_pdu failed with error %lu!", error);
+ break;
+
+ case RDPGFX_CMDID_CAPSADVERTISE:
+ if ((error = rdpgfx_recv_caps_advertise_pdu(context, s)))
+ WLog_ERR(TAG, "rdpgfx_recv_caps_advertise_pdu failed with error %lu!", error);
+ break;
+
+ case RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE:
+ if ((error = rdpgfx_recv_qoe_frame_acknowledge_pdu(context, s)))
+ WLog_ERR(TAG, "rdpgfx_recv_qoe_frame_acknowledge_pdu failed with error %lu!", error);
+ break;
+
+ default:
+ error = CHANNEL_RC_BAD_PROC;
+ break;
+ }
+
+ if (error)
+ {
+ WLog_ERR(TAG, "Error while parsing GFX cmdId: %s (0x%04X)",
+ rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId);
+ return error;
+ }
+
+ end = Stream_GetPosition(s);
+
+ if (end != (beg + header.pduLength))
+ {
+ WLog_ERR(TAG, "Unexpected gfx pdu end: Actual: %d, Expected: %d",
+ end, (beg + header.pduLength));
+ Stream_SetPosition(s, (beg + header.pduLength));
+ }
+
+ return error;
+
+}
+
+static void* rdpgfx_server_thread_func(void* arg)
+{
+ RdpgfxServerContext* context = (RdpgfxServerContext*) arg;
+ RdpgfxServerPrivate* priv = context->priv;
+ wStream* s;
+ DWORD status;
+ DWORD nCount;
+ void* buffer;
+ HANDLE events[8];
+ HANDLE ChannelEvent;
+ DWORD BytesReturned = 0;
+ UINT error = CHANNEL_RC_OK;
+ BOOL ready = FALSE;
+
+ buffer = NULL;
+ BytesReturned = 0;
+ ChannelEvent = NULL;
+
+ /* Query for channel event handle */
+ if (WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
+ {
+ if (BytesReturned == sizeof(HANDLE))
+ CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
+
+ WTSFreeMemory(buffer);
+ }
+ else
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
+ error = ERROR_INTERNAL_ERROR;
+ goto out;
+ }
+
+ nCount = 0;
+ events[nCount++] = priv->stopEvent;
+ events[nCount++] = ChannelEvent;
+
+ /* Wait for the client to confirm that the dynamic channel is ready */
+ while (1)
+ {
+ if ((status = WaitForMultipleObjects(nCount, events, FALSE, 100)) == WAIT_OBJECT_0)
+ goto out;
+
+ if (status == WAIT_FAILED)
+ {
+ error = GetLastError();
+ WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
+ goto out;
+ }
+
+ if (WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE)
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
+ error = ERROR_INTERNAL_ERROR;
+ goto out;
+ }
+
+ ready = *((BOOL*) buffer);
+
+ WTSFreeMemory(buffer);
+
+ if (ready)
+ break;
+ }
+
+ /* Create shared stream */
+ s = Stream_New(NULL, 4096);
+ if (!s)
+ {
+ WLog_ERR(TAG, "Stream_New failed!");
+ error = CHANNEL_RC_NO_MEMORY;
+ goto out;
+ }
+
+ /* Main virtual channel loop. RDPGFX do not need version negotiation */
+ while (ready)
+ {
+ status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
+
+ if (status == WAIT_FAILED)
+ {
+ error = GetLastError();
+ WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error);
+ goto out;
+ }
+
+ /* Stop Event */
+ if (status == WAIT_OBJECT_0)
+ break;
+
+ /* Channel Event */
+ Stream_SetPosition(s, 0);
+
+ if (!WTSVirtualChannelRead(priv->rdpgfx_channel, 0, NULL, 0, &BytesReturned))
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
+ error = ERROR_INTERNAL_ERROR;
+ break;
+ }
+
+ if (BytesReturned < 1)
+ continue;
+
+ if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ error = CHANNEL_RC_NO_MEMORY;
+ break;
+ }
+
+ if (WTSVirtualChannelRead(priv->rdpgfx_channel, 0, (PCHAR) Stream_Buffer(s),
+ Stream_Capacity(s), &BytesReturned) == FALSE)
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
+ error = ERROR_INTERNAL_ERROR;
+ break;
+ }
+
+ Stream_SetLength(s, BytesReturned);
+ Stream_SetPosition(s, 0);
+ if ((error = rdpgfx_server_receive_pdu(context, s)))
+ {
+ WLog_ERR(TAG, "rdpgfx_server_receive_pdu failed with error %lu!", error);
+ break;
+ }
+ Stream_SetPosition(s, 0);
+ }
+
+ Stream_Free(s, TRUE);
+out:
+ WTSVirtualChannelClose(priv->rdpgfx_channel);
+ priv->rdpgfx_channel = NULL;
+ if (error && context->rdpcontext)
+ setChannelError(context->rdpcontext, error, "rdpgfx_server_thread_func reported an error");
+
+ ExitThread((DWORD)error);
+ return NULL;
+}
+
+static BOOL rdpgfx_server_open(RdpgfxServerContext* context)
+{
+ RdpgfxServerPrivate* priv = (RdpgfxServerPrivate *) context->priv;
+
+ if (!priv->thread)
+ {
+ PULONG pSessionId = NULL;
+ DWORD BytesReturned = 0;
+
+ priv->SessionId = WTS_CURRENT_SESSION;
+
+ if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION,
+ WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned))
+ {
+ priv->SessionId = (DWORD) *pSessionId;
+ WTSFreeMemory(pSessionId);
+ }
+
+ priv->rdpgfx_channel = WTSVirtualChannelOpenEx(priv->SessionId,
+ RDPGFX_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
+
+ if (!priv->rdpgfx_channel)
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
+ return FALSE;
+ }
+
+ if (!(priv->zgfx = zgfx_context_new(TRUE)))
+ {
+ WLog_ERR(TAG, "Create zgfx context failed!");
+ return FALSE;
+ }
+
+ if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
+ {
+ WLog_ERR(TAG, "CreateEvent failed!");
+ return FALSE;
+ }
+
+ if (!(priv->thread = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) rdpgfx_server_thread_func, (void*) context, 0, NULL)))
+ {
+ WLog_ERR(TAG, "CreateThread failed!");
+ CloseHandle(priv->stopEvent);
+ priv->stopEvent = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ WLog_ERR(TAG, "thread already running!");
+ return FALSE;
+}
+
+static BOOL rdpgfx_server_close(RdpgfxServerContext* context)
+{
+ RdpgfxServerPrivate* priv = (RdpgfxServerPrivate *) context->priv;
+
+ if (priv->thread)
+ {
+ SetEvent(priv->stopEvent);
+ if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
+ {
+ WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", GetLastError());
+ return FALSE;
+ }
+
+ CloseHandle(priv->thread);
+ CloseHandle(priv->stopEvent);
+ priv->thread = NULL;
+ priv->stopEvent = NULL;
+ }
+
+ zgfx_context_free(priv->zgfx);
+ priv->zgfx = NULL;
+
+ if (priv->rdpgfx_channel)
+ {
+ WTSVirtualChannelClose(priv->rdpgfx_channel);
+ priv->rdpgfx_channel = NULL;
+ }
+
+ return TRUE;
+}
+
+RdpgfxServerContext* rdpgfx_server_context_new(HANDLE vcm)
+{
+ RdpgfxServerContext* context;
+ RdpgfxServerPrivate *priv;
+
+ context = (RdpgfxServerContext *)calloc(1, sizeof(RdpgfxServerContext));
+ if (!context)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return NULL;
+ }
+
+ context->vcm = vcm;
+ context->Open = rdpgfx_server_open;
+ context->Close = rdpgfx_server_close;
+
+ context->ResetGraphics = rdpgfx_send_reset_graphics_pdu;
+ context->StartFrame = rdpgfx_send_start_frame_pdu;
+ context->EndFrame = rdpgfx_send_end_frame_pdu;
+ context->SurfaceCommand = rdpgfx_send_surface_command;
+ context->DeleteEncodingContext = rdpgfx_send_delete_encoding_context_pdu;
+ context->CreateSurface = rdpgfx_send_create_surface_pdu;
+ context->DeleteSurface = rdpgfx_send_delete_surface_pdu;
+ context->SolidFill = rdpgfx_send_solid_fill_pdu;
+ context->SurfaceToSurface = rdpgfx_send_surface_to_surface_pdu;
+ context->SurfaceToCache = rdpgfx_send_surface_to_cache_pdu;
+ context->CacheToSurface = rdpgfx_send_cache_to_surface_pdu;
+ context->CacheImportOffer = NULL;
+ context->CacheImportReply = rdpgfx_send_cache_import_reply_pdu;
+ context->EvictCacheEntry = rdpgfx_send_evict_cache_entry_pdu;
+ context->MapSurfaceToOutput = rdpgfx_send_map_surface_to_output_pdu;
+ context->MapSurfaceToWindow = rdpgfx_send_map_surface_to_window_pdu;
+ context->CapsAdvertise = NULL;
+ context->CapsConfirm = rdpgfx_send_caps_confirm_pdu;
+ context->FrameAcknowledge = NULL;
+ context->QoeFrameAcknowledge = NULL;
+
+ context->priv = priv = (RdpgfxServerPrivate *)calloc(1, sizeof(RdpgfxServerPrivate));
+ if (!priv)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ goto out_free;
+ }
+
+ return (RdpgfxServerContext*) context;
+
+out_free:
+ free(context);
+ return NULL;
+}
+
+void rdpgfx_server_context_free(RdpgfxServerContext* context)
+{
+ rdpgfx_server_close(context);
+
+ free(context->priv);
+
+ free(context);
+}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Graphics Pipeline Extension
+ *
+ * Copyright 2016 Jiang Zihao <zihao.jiang@yahoo.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_RDPGFX_SERVER_MAIN_H
+#define FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H
+
+#include <freerdp/server/rdpgfx.h>
+#include <freerdp/codec/zgfx.h>
+
+struct _rdpgfx_server_private
+{
+ ZGFX_CONTEXT* zgfx;
+ HANDLE thread;
+ HANDLE stopEvent;
+ void* rdpgfx_channel;
+ DWORD SessionId;
+ BOOL opened;
+};
+
+#endif /* FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H */
#define RDPGFX_CMDID_CAPSCONFIRM 0x0013
#define RDPGFX_CMDID_UNUSED_0014 0x0014
#define RDPGFX_CMDID_MAPSURFACETOWINDOW 0x0015
+#define RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE 0x0016
#define RDPGFX_HEADER_SIZE 8
};
typedef struct _RDPGFX_AVC444_BITMAP_STREAM RDPGFX_AVC444_BITMAP_STREAM;
+struct _RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU
+{
+ UINT32 frameId;
+ UINT32 timestamp;
+ UINT16 timeDiffSE;
+ UINT16 timeDiffEDR;
+};
+typedef struct _RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU;
#endif /* FREERDP_CHANNEL_RDPGFX_H */
#define ZGFX_SEGMENTED_SINGLE 0xE0
#define ZGFX_SEGMENTED_MULTIPART 0xE1
+#define ZGFX_PACKET_COMPR_TYPE_RDP8 0x04
+
+#define ZGFX_SEGMENTED_MAXSIZE 65535
+
struct _ZGFX_CONTEXT
{
BOOL Compressor;
extern "C" {
#endif
-FREERDP_API int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags);
FREERDP_API int zgfx_decompress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags);
+FREERDP_API int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags);
+FREERDP_API int zgfx_compress_to_stream(ZGFX_CONTEXT* zgfx, wStream* sDst, BYTE* pUncompressed, UINT32 uncompressedSize, UINT32* pFlags);
FREERDP_API void zgfx_context_reset(ZGFX_CONTEXT* zgfx, BOOL flush);
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Graphics Pipeline Extension
+ *
+ * Copyright 2016 Jiang Zihao <zihao.jiang@yahoo.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_SERVER_RDPGFX_H
+#define FREERDP_CHANNEL_SERVER_RDPGFX_H
+
+#include <freerdp/channels/rdpgfx.h>
+
+typedef struct _rdpgfx_server_context RdpgfxServerContext;
+typedef struct _rdpgfx_server_private RdpgfxServerPrivate;
+
+typedef BOOL (*psRdpgfxServerOpen)(RdpgfxServerContext* context);
+typedef BOOL (*psRdpgfxServerClose)(RdpgfxServerContext* context);
+
+typedef UINT (*pcRdpgfxResetGraphics)(RdpgfxServerContext* context, RDPGFX_RESET_GRAPHICS_PDU* resetGraphics);
+typedef UINT (*pcRdpgfxStartFrame)(RdpgfxServerContext* context, RDPGFX_START_FRAME_PDU* startFrame);
+typedef UINT (*pcRdpgfxEndFrame)(RdpgfxServerContext* context, RDPGFX_END_FRAME_PDU* endFrame);
+typedef UINT (*pcRdpgfxSurfaceCommand)(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd);
+typedef UINT (*pcRdpgfxDeleteEncodingContext)(RdpgfxServerContext* context, RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext);
+typedef UINT (*pcRdpgfxCreateSurface)(RdpgfxServerContext* context, RDPGFX_CREATE_SURFACE_PDU* createSurface);
+typedef UINT (*pcRdpgfxDeleteSurface)(RdpgfxServerContext* context, RDPGFX_DELETE_SURFACE_PDU* deleteSurface);
+typedef UINT (*pcRdpgfxSolidFill)(RdpgfxServerContext* context, RDPGFX_SOLID_FILL_PDU* solidFill);
+typedef UINT (*pcRdpgfxSurfaceToSurface)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface);
+typedef UINT (*pcRdpgfxSurfaceToCache)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache);
+typedef UINT (*pcRdpgfxCacheToSurface)(RdpgfxServerContext* context, RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface);
+typedef UINT (*pcRdpgfxCacheImportOffer)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_OFFER_PDU* cacheImportOffer);
+typedef UINT (*pcRdpgfxCacheImportReply)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply);
+typedef UINT (*pcRdpgfxEvictCacheEntry)(RdpgfxServerContext* context, RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry);
+typedef UINT (*pcRdpgfxMapSurfaceToOutput)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput);
+typedef UINT (*pcRdpgfxMapSurfaceToWindow)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow);
+typedef UINT (*pcRdpgfxCapsAdvertise)(RdpgfxServerContext* context, RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise);
+typedef UINT (*pcRdpgfxCapsConfirm)(RdpgfxServerContext* context, RDPGFX_CAPS_CONFIRM_PDU* capsConfirm);
+typedef UINT (*pcRdpgfxFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge);
+typedef UINT (*pcRdpgfxQoeFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU* qoeFrameAcknowledge);
+
+struct _rdpgfx_server_context
+{
+ HANDLE vcm;
+ void* custom;
+
+ psRdpgfxServerOpen Open;
+ psRdpgfxServerClose Close;
+
+ pcRdpgfxResetGraphics ResetGraphics;
+ pcRdpgfxStartFrame StartFrame;
+ pcRdpgfxEndFrame EndFrame;
+ pcRdpgfxSurfaceCommand SurfaceCommand;
+ pcRdpgfxDeleteEncodingContext DeleteEncodingContext;
+ pcRdpgfxCreateSurface CreateSurface;
+ pcRdpgfxDeleteSurface DeleteSurface;
+ pcRdpgfxSolidFill SolidFill;
+ pcRdpgfxSurfaceToSurface SurfaceToSurface;
+ pcRdpgfxSurfaceToCache SurfaceToCache;
+ pcRdpgfxCacheToSurface CacheToSurface;
+ pcRdpgfxCacheImportOffer CacheImportOffer;
+ pcRdpgfxCacheImportReply CacheImportReply;
+ pcRdpgfxEvictCacheEntry EvictCacheEntry;
+ pcRdpgfxMapSurfaceToOutput MapSurfaceToOutput;
+ pcRdpgfxMapSurfaceToWindow MapSurfaceToWindow;
+ pcRdpgfxCapsAdvertise CapsAdvertise;
+ pcRdpgfxCapsConfirm CapsConfirm;
+ pcRdpgfxFrameAcknowledge FrameAcknowledge;
+ pcRdpgfxQoeFrameAcknowledge QoeFrameAcknowledge;
+
+ RdpgfxServerPrivate* priv;
+ rdpContext* rdpcontext;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+FREERDP_API RdpgfxServerContext* rdpgfx_server_context_new(HANDLE vcm);
+FREERDP_API void rdpgfx_server_context_free(RdpgfxServerContext* context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_CHANNEL_SERVER_RDPGFX_H */
#include <freerdp/server/remdesk.h>
#include <freerdp/server/rdpsnd.h>
#include <freerdp/server/audin.h>
+#include <freerdp/server/rdpgfx.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/region.h>
BOOL inLobby;
BOOL mayView;
BOOL mayInteract;
+ BOOL suppressOutput;
wMessageQueue* MsgQueue;
CRITICAL_SECTION lock;
REGION16 invalidRegion;
RemdeskServerContext* remdesk;
RdpsndServerContext* rdpsnd;
audin_server_context* audin;
+ RdpgfxServerContext* rdpgfx;
};
struct rdp_shadow_server
int selectedMonitor; \
MONITOR_DEF monitors[16]; \
MONITOR_DEF virtualScreen; \
+ \
+ /* This event indicates that we have graphic change */ \
+ /* such as screen update and resize. It should not be */ \
+ /* used by subsystem implementation directly */ \
rdpShadowMultiClientEvent* updateEvent; \
- BOOL suppressOutput; \
- REGION16 invalidRegion; \
+ \
wMessagePipe* MsgPipe; \
UINT32 pointerX; \
UINT32 pointerY; \
};
/* Definition of message between subsystem and clients */
-#define SHADOW_MSG_IN_REFRESH_OUTPUT_ID 1001
-#define SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID 1002
-
-struct _SHADOW_MSG_IN_REFRESH_OUTPUT
-{
- UINT32 numRects;
- RECTANGLE_16* rects;
-};
-typedef struct _SHADOW_MSG_IN_REFRESH_OUTPUT SHADOW_MSG_IN_REFRESH_OUTPUT;
-
-struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT
-{
- BOOL allow;
- RECTANGLE_16 rect;
-};
-typedef struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT SHADOW_MSG_IN_SUPPRESS_OUTPUT;
+#define SHADOW_MSG_IN_REFRESH_REQUEST_ID 1001
typedef struct _SHADOW_MSG_OUT SHADOW_MSG_OUT;
typedef void (*MSG_OUT_FREE_FN)(UINT32 id, SHADOW_MSG_OUT* msg); /* function to free SHADOW_MSG_OUT */
#include <winpr/crt.h>
#include <winpr/print.h>
+#include <winpr/bitstream.h>
+#include <freerdp/freerdp.h>
#include <freerdp/codec/zgfx.h>
+#include <freerdp/log.h>
+
+/* Sample from [MS-RDPEGFX] */
+static const BYTE TEST_FOX_DATA[] =
+ "The quick brown "
+ "fox jumps over t"
+ "he lazy dog";
+
+static const BYTE TEST_FOX_DATA_SINGLE[] =
+ "\xE0\x04\x54\x68\x65\x20\x71\x75\x69\x63\x6B\x20\x62\x72\x6F\x77"
+ "\x6E\x20\x66\x6F\x78\x20\x6A\x75\x6D\x70\x73\x20\x6F\x76\x65\x72"
+ "\x20\x74\x68\x65\x20\x6C\x61\x7A\x79\x20\x64\x6F\x67";
+
+static const BYTE TEST_FOX_DATA_MULTIPART[] =
+ "\xE1\x03\x00\x2B\x00\x00\x00\x11\x00\x00\x00\x04\x54\x68\x65\x20"
+ "\x71\x75\x69\x63\x6B\x20\x62\x72\x6F\x77\x6E\x20\x0E\x00\x00\x00"
+ "\x04\x66\x6F\x78\x20\x6A\x75\x6D\x70\x73\x20\x6F\x76\x65\x10\x00"
+ "\x00\x00\x24\x39\x08\x0E\x91\xF8\xD8\x61\x3D\x1E\x44\x06\x43\x79"
+ "\x9C\x02";
+
+int test_ZGfxCompressFox()
+{
+ int status;
+ UINT32 Flags;
+ BYTE* pSrcData;
+ UINT32 SrcSize;
+ UINT32 DstSize;
+ BYTE* pDstData;
+ ZGFX_CONTEXT* zgfx;
+ UINT32 expectedSize;
+
+ zgfx = zgfx_context_new(TRUE);
+ SrcSize = sizeof(TEST_FOX_DATA) - 1;
+ pSrcData = (BYTE*) TEST_FOX_DATA;
+ Flags = 0;
+ expectedSize = sizeof(TEST_FOX_DATA_SINGLE) - 1;
+ status = zgfx_compress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, &Flags);
+ printf("flags: 0x%04X size: %d\n", Flags, DstSize);
+
+ if (DstSize != expectedSize)
+ {
+ printf("test_ZGfxCompressFox: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize);
+ return -1;
+ }
+
+ if (memcmp(pDstData, TEST_FOX_DATA_SINGLE, DstSize) != 0)
+ {
+ printf("test_ZGfxCompressFox: output mismatch\n");
+ printf("Actual\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0);
+ printf("Expected\n");
+ BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA_SINGLE, DstSize * 8, 0);
+ return -1;
+ }
+
+ zgfx_context_free(zgfx);
+ return 0;
+}
+
+int test_ZGfxDecompressFoxSingle()
+{
+ int status;
+ UINT32 Flags;
+ BYTE* pSrcData;
+ UINT32 SrcSize;
+ UINT32 DstSize;
+ BYTE* pDstData;
+ ZGFX_CONTEXT* zgfx;
+ UINT32 expectedSize;
+
+ zgfx = zgfx_context_new(TRUE);
+ SrcSize = sizeof(TEST_FOX_DATA_SINGLE) - 1;
+ pSrcData = (BYTE*) TEST_FOX_DATA_SINGLE;
+ Flags = 0;
+ expectedSize = sizeof(TEST_FOX_DATA) - 1;
+ status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+ printf("flags: 0x%04X size: %d\n", Flags, DstSize);
+
+ if (DstSize != expectedSize)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize);
+ return -1;
+ }
+
+ if (memcmp(pDstData, TEST_FOX_DATA, DstSize) != 0)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output mismatch\n");
+ printf("Actual\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0);
+ printf("Expected\n");
+ BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA, DstSize * 8, 0);
+ return -1;
+ }
+
+ zgfx_context_free(zgfx);
+ return 0;
+}
+
+int test_ZGfxDecompressFoxMultipart()
+{
+ int status;
+ UINT32 Flags;
+ BYTE* pSrcData;
+ UINT32 SrcSize;
+ UINT32 DstSize;
+ BYTE* pDstData;
+ ZGFX_CONTEXT* zgfx;
+ UINT32 expectedSize;
+
+ zgfx = zgfx_context_new(TRUE);
+ SrcSize = sizeof(TEST_FOX_DATA_MULTIPART) - 1;
+ pSrcData = (BYTE*) TEST_FOX_DATA_MULTIPART;
+ Flags = 0;
+ expectedSize = sizeof(TEST_FOX_DATA) - 1;
+ status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+ printf("flags: 0x%04X size: %d\n", Flags, DstSize);
+
+ if (DstSize != expectedSize)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize);
+ return -1;
+ }
+
+ if (memcmp(pDstData, TEST_FOX_DATA, DstSize) != 0)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output mismatch\n");
+ printf("Actual\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0);
+ printf("Expected\n");
+ BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA, DstSize * 8, 0);
+ return -1;
+ }
+
+ zgfx_context_free(zgfx);
+ return 0;
+}
+
+int test_ZGfxCompressConsistent()
+{
+ int status;
+ UINT32 Flags;
+ BYTE* pSrcData;
+ UINT32 SrcSize;
+ UINT32 DstSize;
+ BYTE* pDstData;
+ UINT32 DstSize2;
+ BYTE* pDstData2;
+ ZGFX_CONTEXT* zgfx;
+ UINT32 expectedSize;
+ BYTE BigBuffer[65536];
+
+ memset(BigBuffer, 0xaa, sizeof(BigBuffer));
+ memcpy(BigBuffer, TEST_FOX_DATA, sizeof(TEST_FOX_DATA) - 1);
+ zgfx = zgfx_context_new(TRUE);
+
+ /* Compress */
+ expectedSize = SrcSize = sizeof(BigBuffer);
+ pSrcData = (BYTE*) BigBuffer;
+ Flags = 0;
+ status = zgfx_compress(zgfx, pSrcData, SrcSize, &pDstData2, &DstSize2, &Flags);
+ printf("Compress: flags: 0x%04X size: %d\n", Flags, DstSize2);
+
+ /* Decompress */
+ status = zgfx_decompress(zgfx, pDstData2, DstSize2, &pDstData, &DstSize, Flags);
+ printf("Decompress: flags: 0x%04X size: %d\n", Flags, DstSize);
+
+ if (DstSize != expectedSize)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize);
+ return -1;
+ }
+
+ if (memcmp(pDstData, BigBuffer, DstSize) != 0)
+ {
+ printf("test_ZGfxDecompressFoxSingle: output mismatch\n");
+ printf("Actual\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData, 64 * 8, 0);
+ printf("...\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData + DstSize - 64, 64 * 8, 0);
+ printf("Expected\n");
+ BitDump(__FUNCTION__, WLOG_INFO, BigBuffer, 64 * 8, 0);
+ printf("...\n");
+ BitDump(__FUNCTION__, WLOG_INFO, BigBuffer + DstSize - 64, 64 * 8, 0);
+ printf("Middle Result\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData2, 64 * 8, 0);
+ printf("...\n");
+ BitDump(__FUNCTION__, WLOG_INFO, pDstData2 + DstSize2 - 64, 64 * 8, 0);
+ return -1;
+ }
+
+ zgfx_context_free(zgfx);
+ return 0;
+}
int TestFreeRDPCodecZGfx(int argc, char* argv[])
{
+ if (test_ZGfxCompressFox() < 0)
+ return -1;
+
+ if (test_ZGfxDecompressFoxSingle() < 0)
+ return -1;
+
+ if (test_ZGfxDecompressFoxMultipart() < 0)
+ return -1;
+
+ if (test_ZGfxCompressConsistent() < 0)
+ return -1;
+
return 0;
}
#include <winpr/print.h>
#include <winpr/bitstream.h>
+#include <freerdp/log.h>
#include <freerdp/codec/zgfx.h>
+#define TAG FREERDP_TAG("codec")
+
/**
* RDP8 Compressor Limits:
*
while ((bytesLeft -= bytes) > 0);
}
-int zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, BYTE* pbSegment, UINT32 cbSegment)
+static int zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, BYTE* pbSegment, UINT32 cbSegment)
{
BYTE c;
BYTE flags;
return 1;
}
-int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
+static int zgfx_compress_segment(ZGFX_CONTEXT* zgfx, wStream* s, BYTE* pSrcData, UINT32 SrcSize, UINT32* pFlags)
{
+ /* FIXME: Currently compression not implemented. Just copy the raw source */
+
+ if (!Stream_EnsureRemainingCapacity(s, SrcSize + 1))
+ {
+ WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+ return -1;
+ }
+ (*pFlags) |= ZGFX_PACKET_COMPR_TYPE_RDP8; /* RDP 8.0 compression format */
+ Stream_Write_UINT8(s, (*pFlags)); /* header (1 byte) */
+ Stream_Write(s, pSrcData, SrcSize);
+
return 1;
}
+int zgfx_compress_to_stream(ZGFX_CONTEXT* zgfx, wStream* sDst, BYTE* pUncompressed, UINT32 uncompressedSize, UINT32* pFlags)
+{
+ int fragment;
+ UINT16 maxLength;
+ UINT32 totalLength;
+ UINT16* pSegmentCount = NULL;
+ BYTE* pSrcData;
+ int status = 0;
+
+ maxLength = ZGFX_SEGMENTED_MAXSIZE;
+
+ totalLength = uncompressedSize;
+ pSrcData = pUncompressed;
+ for (fragment = 0; (totalLength > 0) || (fragment == 0); fragment++)
+ {
+ UINT32 SrcSize;
+ UINT32* pDstSize;
+ UINT32 position;
+
+ SrcSize = (totalLength > maxLength) ? maxLength : totalLength;
+ pDstSize = NULL;
+ totalLength -= SrcSize;
+
+ if (fragment == 0)
+ {
+ /* First fragment */
+
+ /* descriptor (1 byte) */
+ Stream_Write_UINT8(sDst, (totalLength == 0) ?
+ ZGFX_SEGMENTED_SINGLE : ZGFX_SEGMENTED_MULTIPART);
+ if (totalLength > 0)
+ {
+ pSegmentCount = (UINT16*)Stream_Pointer(sDst); /* segmentCount (2 bytes) */
+ Stream_Seek(sDst, 2);
+ Stream_Write_UINT32(sDst, uncompressedSize); /* uncompressedSize (4 bytes) */
+ }
+ }
+ else if (totalLength == 0)
+ {
+ /* Last fragment */
+ if (pSegmentCount)
+ {
+ (*pSegmentCount) = fragment + 1;
+ }
+ }
+
+ if (fragment > 0 || totalLength > 0)
+ {
+ /* Multipart */
+ pDstSize = (UINT32*)Stream_Pointer(sDst); /* size (4 bytes) */
+ Stream_Seek(sDst, 4);
+ }
+
+ position = Stream_GetPosition(sDst);
+ if ((status = zgfx_compress_segment(zgfx, sDst, pSrcData, SrcSize, pFlags)) < 0)
+ {
+ return status;
+ }
+
+ if (pDstSize)
+ {
+ (*pDstSize) = Stream_GetPosition(sDst) - position;
+ }
+
+ pSrcData += SrcSize;
+ }
+
+ return status;
+}
+
+int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags)
+{
+ int status;
+ wStream* s = Stream_New(NULL, SrcSize);
+
+ status = zgfx_compress_to_stream(zgfx, s, pSrcData, SrcSize, pFlags);
+ (*ppDstData) = Stream_Buffer(s);
+ (*pDstSize) = Stream_GetPosition(s);
+
+ Stream_Free(s, FALSE);
+ return status;
+}
+
+
void zgfx_context_reset(ZGFX_CONTEXT* zgfx, BOOL flush)
{
zgfx->HistoryIndex = 0;
shadow_rdpsnd.h
shadow_audin.c
shadow_audin.h
+ shadow_rdpgfx.c
+ shadow_rdpgfx.h
shadow_subsystem.c
shadow_subsystem.h
shadow_mcevent.c
size_t numRects;
const CGRect* rects;
RECTANGLE_16 invalidRect;
+ rdpShadowSurface* surface = subsystem->server->surface;
rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects, &numRects);
invalidRect.bottom /= 2;
}
- region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
}
return 0;
surfaceRect.right = surface->width;
surfaceRect.bottom = surface->height;
- region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect);
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
- if (!region16_is_empty(&(subsystem->invalidRegion)))
+ if (!region16_is_empty(&(surface->invalidRegion)))
{
- extents = region16_extents(&(subsystem->invalidRegion));
+ extents = region16_extents(&(surface->invalidRegion));
x = extents->left;
y = extents->top;
ArrayList_Unlock(server->clients);
- region16_clear(&(subsystem->invalidRegion));
+ region16_clear(&(surface->invalidRegion));
}
if (status != kCGDisplayStreamFrameStatusFrameComplete)
{
switch(message->id)
{
- case SHADOW_MSG_IN_REFRESH_OUTPUT_ID:
- {
- UINT32 index;
- SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam;
-
- if (msg->numRects)
- {
- for (index = 0; index < msg->numRects; index++)
- {
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &msg->rects[index]);
- }
- }
- else
- {
- RECTANGLE_16 refreshRect;
-
- refreshRect.left = 0;
- refreshRect.top = 0;
- refreshRect.right = subsystem->width;
- refreshRect.bottom = subsystem->height;
-
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &refreshRect);
- }
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem);
break;
- }
- case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID:
- {
- SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam;
-
- subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE;
-
- if (msg->allow)
- {
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &(msg->rect));
- }
- break;
- }
default:
WLog_ERR(TAG, "Unknown message id: %u", message->id);
break;
RECT* pDirtyRectsBuffer;
DXGI_OUTDUPL_MOVE_RECT* pMoveRect;
DXGI_OUTDUPL_MOVE_RECT* pMoveRectBuffer;
+ rdpShadowSurface* surface = subsystem->server->surface;
if (subsystem->dxgiFrameInfo.AccumulatedFrames == 0)
return 0;
invalidRect.right = (UINT16) pDstRect->right;
invalidRect.bottom = (UINT16) pDstRect->bottom;
- region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
}
numDirtyRects = DirtyRectsBufferSize / sizeof(RECT);
invalidRect.right = (UINT16) pDirtyRect->right;
invalidRect.bottom = (UINT16) pDirtyRect->bottom;
- region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
}
return 1;
rdpGdi* gdi = context->gdi;
shwContext* shw = (shwContext*) context;
winShadowSubsystem* subsystem = shw->subsystem;
+ rdpShadowSurface* surface = subsystem->server->surface;
ninvalid = gdi->primary->hdc->hwnd->ninvalid;
cinvalid = gdi->primary->hdc->hwnd->cinvalid;
invalidRect.right = cinvalid[index].x + cinvalid[index].w;
invalidRect.bottom = cinvalid[index].y + cinvalid[index].h;
- region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
}
SetEvent(subsystem->RdpUpdateEnterEvent);
if (ArrayList_Count(server->clients) < 1)
{
- region16_clear(&(subsystem->invalidRegion));
+ region16_clear(&(surface->invalidRegion));
return 1;
}
surfaceRect.right = surface->x + surface->width;
surfaceRect.bottom = surface->y + surface->height;
- region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect);
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
- if (region16_is_empty(&(subsystem->invalidRegion)))
+ if (region16_is_empty(&(surface->invalidRegion)))
return 1;
- extents = region16_extents(&(subsystem->invalidRegion));
+ extents = region16_extents(&(surface->invalidRegion));
CopyMemory(&invalidRect, extents, sizeof(RECTANGLE_16));
shadow_capture_align_clip_rect(&invalidRect, &surfaceRect);
ArrayList_Unlock(server->clients);
- region16_clear(&(subsystem->invalidRegion));
+ region16_clear(&(surface->invalidRegion));
return 1;
}
if (count < 1)
return 1;
- if ((count == 1) && subsystem->suppressOutput)
- return 1;
-
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = surface->width;
XUnlockDisplay(subsystem->display);
- region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
- region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
- if (!region16_is_empty(&(subsystem->invalidRegion)))
+ if (!region16_is_empty(&(surface->invalidRegion)))
{
- extents = region16_extents(&(subsystem->invalidRegion));
+ extents = region16_extents(&(surface->invalidRegion));
x = extents->left;
y = extents->top;
}
}
- region16_clear(&(subsystem->invalidRegion));
+ region16_clear(&(surface->invalidRegion));
}
if (!subsystem->use_xshm)
{
switch(message->id)
{
- case SHADOW_MSG_IN_REFRESH_OUTPUT_ID:
- {
- UINT32 index;
- SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam;
-
- if (msg->numRects)
- {
- for (index = 0; index < msg->numRects; index++)
- {
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &msg->rects[index]);
- }
- }
- else
- {
- RECTANGLE_16 refreshRect;
-
- refreshRect.left = 0;
- refreshRect.top = 0;
- refreshRect.right = subsystem->width;
- refreshRect.bottom = subsystem->height;
-
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &refreshRect);
- }
- break;
- }
- case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID:
- {
- SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam;
-
- subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE;
-
- if (msg->allow)
- {
- region16_union_rect(&(subsystem->invalidRegion),
- &(subsystem->invalidRegion), &(msg->rect));
- }
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem);
break;
- }
default:
WLog_ERR(TAG, "Unknown message id: %u", message->id);
break;
shadow_client_audin_init(client);
+ if (client->context.settings->SupportGraphicsPipeline)
+ {
+ shadow_client_rdpgfx_init(client);
+ }
+
return CHANNEL_RC_OK;
}
void shadow_client_channels_free(rdpShadowClient* client)
{
+ if (client->context.settings->SupportGraphicsPipeline)
+ {
+ shadow_client_rdpgfx_uninit(client);
+ }
+
shadow_client_audin_uninit(client);
shadow_client_rdpsnd_uninit(client);
#include "shadow_remdesk.h"
#include "shadow_rdpsnd.h"
#include "shadow_audin.h"
+#include "shadow_rdpgfx.h"
#ifdef __cplusplus
extern "C" {
#include <freerdp/log.h>
+#include "libfreerdp/core/server.h"
#include "shadow.h"
#define TAG CLIENT_TAG("shadow")
+struct _SHADOW_GFX_STATUS
+{
+ BOOL gfxOpened;
+ BOOL gfxSurfaceCreated;
+};
+typedef struct _SHADOW_GFX_STATUS SHADOW_GFX_STATUS;
+
+static void shadow_client_rdpgfx_new_surface(rdpShadowClient *client)
+{
+ RDPGFX_CREATE_SURFACE_PDU createSurface;
+ RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU surfaceToOutput;
+ RdpgfxServerContext* context = client->rdpgfx;
+ rdpSettings* settings = ((rdpContext*) client)->settings;
+
+ createSurface.width = settings->DesktopWidth;
+ createSurface.height = settings->DesktopHeight;
+ createSurface.pixelFormat = PIXEL_FORMAT_XRGB_8888;
+ createSurface.surfaceId = 0;
+
+ surfaceToOutput.outputOriginX = 0;
+ surfaceToOutput.outputOriginY = 0;
+ surfaceToOutput.surfaceId = 0;
+ surfaceToOutput.reserved = 0;
+
+ IFCALL(context->CreateSurface, context, &createSurface);
+ IFCALL(context->MapSurfaceToOutput, context, &surfaceToOutput);
+}
+
+static void shadow_client_rdpgfx_release_surface(rdpShadowClient *client)
+{
+ RDPGFX_DELETE_SURFACE_PDU pdu;
+ RdpgfxServerContext* context = client->rdpgfx;
+
+ pdu.surfaceId = 0;
+ IFCALL(context->DeleteSurface, context, &pdu);
+}
+
+static void shadow_client_rdpgfx_reset_graphic(rdpShadowClient *client)
+{
+ RDPGFX_RESET_GRAPHICS_PDU pdu;
+ RdpgfxServerContext* context = client->rdpgfx;
+ rdpSettings* settings = ((rdpContext*) client)->settings;
+
+ pdu.width = settings->DesktopWidth;
+ pdu.height = settings->DesktopHeight;
+ pdu.monitorCount = client->subsystem->numMonitors;
+ pdu.monitorDefArray = client->subsystem->monitors;
+
+ IFCALL(context->ResetGraphics, context, &pdu);
+}
+
static void shadow_client_free_queued_message(void *obj)
{
wMessage *message = (wMessage*)obj;
}
}
-BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
+static BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
{
rdpSettings* settings;
rdpShadowServer* server;
settings->BitmapCacheV3Enabled = TRUE;
settings->FrameMarkerCommandEnabled = TRUE;
settings->SurfaceFrameMarkerEnabled = TRUE;
- settings->SupportGraphicsPipeline = FALSE;
+ settings->SupportGraphicsPipeline = TRUE;
+ settings->GfxH264 = FALSE;
settings->DrawAllowSkipAlpha = TRUE;
settings->DrawAllowColorSubsampling = TRUE;
return FALSE;
}
-void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
+static void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
{
rdpShadowServer* server = client->server;
ArrayList_Remove(server->clients, (void*) client);
- DeleteCriticalSection(&(client->lock));
-
- region16_uninit(&(client->invalidRegion));
-
- WTSCloseServer((HANDLE) client->vcm);
-
- /* Clear queued messages and free resource */
- MessageQueue_Clear(client->MsgQueue);
- MessageQueue_Free(client->MsgQueue);
-
if (client->encoder)
{
shadow_encoder_free(client->encoder);
client->encoder = NULL;
}
+
+ /* Clear queued messages and free resource */
+ MessageQueue_Clear(client->MsgQueue);
+ MessageQueue_Free(client->MsgQueue);
+
+ WTSCloseServer((HANDLE) client->vcm);
+ client->vcm = NULL;
+
+ region16_uninit(&(client->invalidRegion));
+
+ DeleteCriticalSection(&(client->lock));
}
void shadow_client_message_free(wMessage* message)
{
switch(message->id)
{
- case SHADOW_MSG_IN_REFRESH_OUTPUT_ID:
- free(((SHADOW_MSG_IN_REFRESH_OUTPUT*)message->wParam)->rects);
- free(message->wParam);
- break;
-
- case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID:
- free(message->wParam);
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ /* Refresh request do not have message to free */
break;
default:
}
}
-BOOL shadow_client_capabilities(freerdp_peer* peer)
+static void shadow_client_mark_invalid(rdpShadowClient* client, int numRects, const RECTANGLE_16* rects)
{
- rdpShadowSubsystem* subsystem;
- rdpShadowClient* client;
+ int index;
+ RECTANGLE_16 screenRegion;
+ rdpSettings* settings = ((rdpContext*) client)->settings;
- client = (rdpShadowClient*) peer->context;
- subsystem = client->server->subsystem;
+ EnterCriticalSection(&(client->lock));
- if (subsystem->ClientCapabilities)
+ /* Mark client invalid region. No rectangle means full screen */
+ if (numRects > 0)
{
- return subsystem->ClientCapabilities(subsystem, client);
+ for (index = 0; index < numRects; index++)
+ {
+ region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]);
+ }
}
- return TRUE;
+ else
+ {
+ screenRegion.left = 0;
+ screenRegion.top = 0;
+ screenRegion.right = settings->DesktopWidth;
+ screenRegion.bottom = settings->DesktopHeight;
+
+ region16_union_rect(&(client->invalidRegion),
+ &(client->invalidRegion), &screenRegion);
+ }
+
+ LeaveCriticalSection(&(client->lock));
}
-static INLINE void shadow_client_calc_desktop_size(rdpShadowServer* server, int* pWidth, int* pHeight)
+/**
+ * Function description
+ * Recalculate client desktop size
+ *
+ * @return TRUE if width/height changed.
+ */
+static BOOL shadow_client_recalc_desktop_size(rdpShadowClient* client)
{
- RECTANGLE_16 viewport = {0, 0, server->screen->width, server->screen->height};
+ int width, height;
+ rdpShadowServer* server = client->server;
+ rdpSettings* settings = client->context.settings;
+
+ RECTANGLE_16 viewport = {0, 0, server->surface->width, server->surface->height};
if (server->shareSubRect)
{
rectangles_intersection(&viewport, &(server->subRect), &viewport);
}
- (*pWidth) = viewport.right - viewport.left;
- (*pHeight) = viewport.bottom - viewport.top;
+ width = viewport.right - viewport.left;
+ height = viewport.bottom - viewport.top;
+
+ if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height)
+ {
+ settings->DesktopWidth = width;
+ settings->DesktopHeight = height;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOL shadow_client_capabilities(freerdp_peer* peer)
+{
+ rdpShadowSubsystem* subsystem;
+ rdpShadowClient* client;
+
+ client = (rdpShadowClient*) peer->context;
+ subsystem = client->server->subsystem;
+
+ IFCALL(subsystem->ClientCapabilities, subsystem, client);
+
+ /* Make sure we send correct width/height to client */
+ (void)shadow_client_recalc_desktop_size(client);
+
+ return TRUE;
}
-BOOL shadow_client_post_connect(freerdp_peer* peer)
+static BOOL shadow_client_post_connect(freerdp_peer* peer)
{
int authStatus;
- int width, height;
rdpSettings* settings;
rdpShadowClient* client;
rdpShadowServer* server;
- RECTANGLE_16 invalidRect;
rdpShadowSubsystem* subsystem;
client = (rdpShadowClient*) peer->context;
server = client->server;
subsystem = server->subsystem;
- shadow_client_calc_desktop_size(server, &width, &height);
- settings->DesktopWidth = width;
- settings->DesktopHeight = height;
-
if (settings->ColorDepth == 24)
settings->ColorDepth = 16; /* disable 24bpp */
if (settings->MultifragMaxRequestSize < 0x3F0000)
settings->NSCodec = FALSE; /* NSCodec compressor does not support fragmentation yet */
- WLog_ERR(TAG, "Client from %s is activated (%dx%d@%d)",
- peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
+ WLog_INFO(TAG, "Client from %s is activated (%dx%d@%d)",
+ peer->hostname, settings->DesktopWidth,
+ settings->DesktopHeight, settings->ColorDepth);
- peer->update->DesktopResize(peer->update->context);
+ /* Resize client if necessary */
+ if (shadow_client_recalc_desktop_size(client))
+ {
+ peer->update->DesktopResize(peer->update->context);
+ WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)",
+ peer->hostname, settings->DesktopWidth,
+ settings->DesktopHeight, settings->ColorDepth);
+ }
if (shadow_client_channels_post_connect(client) != CHANNEL_RC_OK)
return FALSE;
- invalidRect.left = 0;
- invalidRect.top = 0;
- invalidRect.right = server->screen->width;
- invalidRect.bottom = server->screen->height;
-
- region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &invalidRect);
+ shadow_client_mark_invalid(client, 0, NULL);
authStatus = -1;
/* Convert rects in sub rect coordinate to client/surface coordinate */
static INLINE void shadow_client_convert_rects(rdpShadowClient* client,
- RECTANGLE_16* dst, RECTANGLE_16* src, UINT32 numRects)
+ RECTANGLE_16* dst, const RECTANGLE_16* src, UINT32 numRects)
{
if (client->server->shareSubRect)
{
}
else
{
- CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16));
+ if (src != dst)
+ {
+ CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16));
+ }
}
}
-BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
+static BOOL shadow_client_refresh_request(rdpShadowClient* client)
{
wMessage message = { 0 };
- SHADOW_MSG_IN_REFRESH_OUTPUT* wParam;
wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
- if (count && !areas)
- return FALSE;
+ message.id = SHADOW_MSG_IN_REFRESH_REQUEST_ID;
+ message.wParam = NULL;
+ message.lParam = NULL;
+ message.context = (void*) client;
+ message.Free = NULL;
- if (!(wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT))))
- return FALSE;
+ return MessageQueue_Dispatch(MsgPipe->In, &message);
+}
- wParam->numRects = (UINT32) count;
+static BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
+{
+ RECTANGLE_16* rects;
+
+ /* It is invalid if we have area count but no actual area */
+ if (count && !areas)
+ return FALSE;
- if (wParam->numRects)
+ if (count)
{
- wParam->rects = (RECTANGLE_16*) calloc(wParam->numRects, sizeof(RECTANGLE_16));
+ rects = (RECTANGLE_16*) calloc(count, sizeof(RECTANGLE_16));
- if (!wParam->rects)
+ if (!rects)
{
- free (wParam);
return FALSE;
}
- shadow_client_convert_rects(client, wParam->rects, areas, wParam->numRects);
- }
+ shadow_client_convert_rects(client, rects, areas, count);
- message.id = SHADOW_MSG_IN_REFRESH_OUTPUT_ID;
- message.wParam = (void*) wParam;
- message.lParam = NULL;
- message.context = (void*) client;
- message.Free = shadow_client_message_free;
+ shadow_client_mark_invalid(client, count, rects);
- return MessageQueue_Dispatch(MsgPipe->In, &message);
+ free(rects);
+ }
+ else
+ {
+ shadow_client_mark_invalid(client, 0, NULL);
+ }
+
+ return shadow_client_refresh_request(client);
}
-BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
+static BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
{
- wMessage message = { 0 };
- SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam;
- wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
-
- wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_SUPPRESS_OUTPUT));
- if (!wParam)
- return FALSE;
+ RECTANGLE_16 region;
- wParam->allow = (UINT32) allow;
-
- if (area)
- shadow_client_convert_rects(client, &(wParam->rect), area, 1);
-
- message.id = SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID;
- message.wParam = (void*) wParam;
- message.lParam = NULL;
- message.context = (void*) client;
- message.Free = shadow_client_message_free;
-
- return MessageQueue_Dispatch(MsgPipe->In, &message);
+ client->suppressOutput = allow ? FALSE : TRUE;
+ if (allow)
+ {
+ if (area)
+ {
+ shadow_client_convert_rects(client, ®ion, area, 1);
+ shadow_client_mark_invalid(client, 1, ®ion);
+ }
+ else
+ {
+ shadow_client_mark_invalid(client, 0, NULL);
+ }
+ }
+ return shadow_client_refresh_request(client);
}
-BOOL shadow_client_activate(freerdp_peer* peer)
+static BOOL shadow_client_activate(freerdp_peer* peer)
{
rdpSettings* settings = peer->settings;
rdpShadowClient* client = (rdpShadowClient*) peer->context;
shadow_encoder_reset(client->encoder);
+ /* Update full screen in next update */
return shadow_client_refresh_rect(client, 0, NULL);
}
return TRUE;
}
-BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
+static BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
{
/*
* Record the last client acknowledged frame id to
return TRUE;
}
-int shadow_client_send_surface_frame_marker(rdpShadowClient* client, UINT32 action, UINT32 id)
+static UINT shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge)
{
- SURFACE_FRAME_MARKER surfaceFrameMarker;
- rdpContext* context = (rdpContext*) client;
- rdpUpdate* update = context->update;
+ shadow_client_surface_frame_acknowledge((rdpShadowClient *)context->custom,
+ frameAcknowledge->frameId);
+ return CHANNEL_RC_OK;
+}
- surfaceFrameMarker.frameAction = action;
- surfaceFrameMarker.frameId = id;
+static int shadow_client_send_surface_gfx(rdpShadowClient* client,
+ BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
+{
+ rdpUpdate* update;
+ rdpContext* context;
+ rdpSettings* settings;
+ rdpShadowServer* server;
+ rdpShadowEncoder* encoder;
+ RDPGFX_SURFACE_COMMAND cmd;
+ RDPGFX_START_FRAME_PDU cmdstart;
+ RDPGFX_END_FRAME_PDU cmdend;
+ SYSTEMTIME sTime;
+
+ context = (rdpContext*) client;
+ update = context->update;
+ settings = context->settings;
+
+ server = client->server;
+ encoder = client->encoder;
- IFCALL(update->SurfaceFrameMarker, context, &surfaceFrameMarker);
+ cmdstart.frameId = shadow_encoder_create_frame_id(encoder);
+
+ GetSystemTime(&sTime);
+ cmdstart.timestamp = sTime.wHour << 22 | sTime.wMinute << 16 |
+ sTime.wSecond << 10 | sTime.wMilliseconds;
+
+ cmdend.frameId = cmdstart.frameId;
+
+ cmd.surfaceId = 0;
+ cmd.contextId = 0;
+ cmd.format = PIXEL_FORMAT_XRGB_8888;
+ cmd.left = nXSrc;
+ cmd.top = nYSrc;
+ cmd.right = cmd.left + nWidth;
+ cmd.bottom = cmd.top + nHeight;
+ cmd.width = nWidth;
+ cmd.height = nHeight;
+
+ if (settings->GfxH264)
+ {
+ RDPGFX_AVC420_BITMAP_STREAM avc420;
+ RECTANGLE_16 regionRect;
+ RDPGFX_H264_QUANT_QUALITY quantQualityVal;
+
+ shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC420);
+
+ avc420_compress(encoder->h264, pSrcData, PIXEL_FORMAT_RGB32, nSrcStep,
+ nWidth, nHeight, &avc420.data, &avc420.length);
+
+ cmd.codecId = RDPGFX_CODECID_AVC420;
+ cmd.extra = (void *)&avc420;
+ regionRect.left = cmd.left;
+ regionRect.top = cmd.top;
+ regionRect.right = cmd.right;
+ regionRect.bottom = cmd.bottom;
+ quantQualityVal.qp = encoder->h264->QP;
+ quantQualityVal.r = 0;
+ quantQualityVal.p = 0;
+ quantQualityVal.qualityVal = 100 - quantQualityVal.qp;
+ avc420.meta.numRegionRects = 1;
+ avc420.meta.regionRects = ®ionRect;
+ avc420.meta.quantQualityVals = &quantQualityVal;
+
+ IFCALL(client->rdpgfx->StartFrame, client->rdpgfx, &cmdstart);
+ IFCALL(client->rdpgfx->SurfaceCommand, client->rdpgfx, &cmd);
+ IFCALL(client->rdpgfx->EndFrame, client->rdpgfx, &cmdend);
+ }
return 1;
}
-int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight)
+static int shadow_client_send_surface_bits(rdpShadowClient* client,
+ BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
int i;
BOOL first;
BOOL last;
wStream* s;
- int nSrcStep;
- BYTE* pSrcData;
int numMessages;
UINT32 frameId = 0;
rdpUpdate* update;
server = client->server;
encoder = client->encoder;
- pSrcData = surface->data;
- nSrcStep = surface->scanline;
-
- if (server->shareSubRect)
- {
- int subX, subY;
- int subWidth, subHeight;
-
- subX = server->subRect.left;
- subY = server->subRect.top;
- subWidth = server->subRect.right - server->subRect.left;
- subHeight = server->subRect.bottom - server->subRect.top;
-
- nXSrc -= subX;
- nYSrc -= subY;
- pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)];
- }
-
if (encoder->frameAck)
frameId = shadow_encoder_create_frame_id(encoder);
return 1;
}
-int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight)
+static int shadow_client_send_bitmap_update(rdpShadowClient* client,
+ BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight)
{
BYTE* data;
BYTE* buffer;
int yIdx, xIdx, k;
int rows, cols;
- int nSrcStep;
- BYTE* pSrcData;
UINT32 DstSize;
UINT32 SrcFormat;
BITMAP_DATA* bitmap;
else
shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR);
- pSrcData = surface->data;
- nSrcStep = surface->scanline;
SrcFormat = PIXEL_FORMAT_RGB32;
- if (server->shareSubRect)
- {
- int subX, subY;
- int subWidth, subHeight;
-
- subX = server->subRect.left;
- subY = server->subRect.top;
- subWidth = server->subRect.right - server->subRect.left;
- subHeight = server->subRect.bottom - server->subRect.top;
-
- nXSrc -= subX;
- nYSrc -= subY;
- pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)];
- }
-
if ((nXSrc % 4) != 0)
{
nWidth += (nXSrc % 4);
return 1;
}
-int shadow_client_send_surface_update(rdpShadowClient* client)
+static int shadow_client_send_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
{
int status = -1;
int nXSrc, nYSrc;
REGION16 invalidRegion;
RECTANGLE_16 surfaceRect;
const RECTANGLE_16* extents;
+ BYTE* pSrcData;
+ int nSrcStep;
+ int index;
+ int numRects = 0;
+ const RECTANGLE_16* rects;
context = (rdpContext*) client;
settings = context->settings;
LeaveCriticalSection(&(client->lock));
+ rects = region16_rects(&(surface->invalidRegion), &numRects);
+ for (index = 0; index < numRects; index++)
+ {
+ region16_union_rect(&invalidRegion, &invalidRegion, &rects[index]);
+ }
+
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = surface->width;
if (region16_is_empty(&invalidRegion))
{
+ /* No image region need to be updated */
region16_uninit(&invalidRegion);
return 1;
}
extents = region16_extents(&invalidRegion);
- nXSrc = extents->left - 0;
- nYSrc = extents->top - 0;
+ nXSrc = extents->left;
+ nYSrc = extents->top;
nWidth = extents->right - extents->left;
nHeight = extents->bottom - extents->top;
+ pSrcData = surface->data;
+ nSrcStep = surface->scanline;
+
+ /* Move to new pSrcData / nXSrc / nYSrc according to sub rect */
+ if (server->shareSubRect)
+ {
+ int subX, subY;
+
+ subX = server->subRect.left;
+ subY = server->subRect.top;
+
+ nXSrc -= subX;
+ nYSrc -= subY;
+ pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)];
+ }
+
//WLog_INFO(TAG, "shadow_client_send_surface_update: x: %d y: %d width: %d height: %d right: %d bottom: %d",
// nXSrc, nYSrc, nWidth, nHeight, nXSrc + nWidth, nYSrc + nHeight);
- if (settings->RemoteFxCodec || settings->NSCodec)
+ if (settings->SupportGraphicsPipeline &&
+ settings->GfxH264 &&
+ pStatus->gfxOpened)
+ {
+ /* GFX/h264 always full screen encoded */
+ nWidth = settings->DesktopWidth;
+ nHeight = settings->DesktopHeight;
+
+ /* Create primary surface if have not */
+ if (!pStatus->gfxSurfaceCreated)
+ {
+ /* Only init surface when we have h264 supported */
+ shadow_client_rdpgfx_new_surface(client);
+ shadow_client_rdpgfx_reset_graphic(client);
+ pStatus->gfxSurfaceCreated = TRUE;
+ }
+
+ status = shadow_client_send_surface_gfx(client, pSrcData, nSrcStep, 0, 0, nWidth, nHeight);
+ }
+ else if (settings->RemoteFxCodec || settings->NSCodec)
{
- status = shadow_client_send_surface_bits(client, surface, nXSrc, nYSrc, nWidth, nHeight);
+ status = shadow_client_send_surface_bits(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight);
}
else
{
- status = shadow_client_send_bitmap_update(client, surface, nXSrc, nYSrc, nWidth, nHeight);
+ status = shadow_client_send_bitmap_update(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight);
}
region16_uninit(&invalidRegion);
return status;
}
-int shadow_client_surface_update(rdpShadowClient* client, REGION16* region)
+static void shadow_client_send_resize(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
{
- int index;
- int numRects = 0;
- const RECTANGLE_16* rects;
+ rdpContext* context;
+ rdpSettings* settings;
+ rdpShadowServer* server;
+ freerdp_peer* peer;
- EnterCriticalSection(&(client->lock));
+ server = client->server;
+ context = (rdpContext*) client;
+ peer = context->peer;
+ settings = context->settings;
- rects = region16_rects(region, &numRects);
+ /**
+ * Unset client activated flag to avoid sending update message during
+ * resize. DesktopResize will reactive the client and
+ * shadow_client_activate would be invoked later.
+ */
+ client->activated = FALSE;
- for (index = 0; index < numRects; index++)
+ /* Close Gfx surfaces */
+ if (pStatus->gfxSurfaceCreated)
{
- region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]);
+ shadow_client_rdpgfx_release_surface(client);
+ pStatus->gfxSurfaceCreated = FALSE;
}
+ /* Send Resize */
+ peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize
+
+ /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */
+ EnterCriticalSection(&(client->lock));
+ region16_clear(&(client->invalidRegion));
LeaveCriticalSection(&(client->lock));
+ WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)",
+ peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
+}
+
+static INLINE void shadow_client_no_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
+{
+ rdpShadowServer* server;
+ rdpShadowSurface* surface;
+
+ server = client->server;
+ surface = client->inLobby ? server->lobby : server->surface;
+
+ shadow_client_surface_update(client, &(surface->invalidRegion));
+}
+
+int shadow_client_surface_update(rdpShadowClient* client, REGION16* region)
+{
+ int numRects = 0;
+ const RECTANGLE_16* rects;
+
+ rects = region16_rects(region, &numRects);
+ shadow_client_mark_invalid(client, numRects, rects);
+
return 1;
}
-int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
+static int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
{
rdpContext* context = (rdpContext*) client;
rdpUpdate* update = context->update;
return 1;
}
-void* shadow_client_thread(rdpShadowClient* client)
+static void* shadow_client_thread(rdpShadowClient* client)
{
DWORD status;
DWORD nCount;
rdpShadowEncoder* encoder;
rdpShadowSubsystem* subsystem;
wMessageQueue* MsgQueue = client->MsgQueue;
+ /* This should only be visited in client thread */
+ SHADOW_GFX_STATUS gfxstatus;
+
+ gfxstatus.gfxOpened = FALSE;
+ gfxstatus.gfxSurfaceCreated = FALSE;
server = client->server;
screen = server->screen;
if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
{
- if (client->activated)
+ /* The UpdateEvent means to start sending current frame. It is
+ * triggered from subsystem implementation and it should ensure
+ * that the screen and primary surface meta data (width, height,
+ * scanline, invalid region, etc) is not changed until it is reset
+ * (at shadow_multiclient_consume). As best practice, subsystem
+ * implementation should invoke shadow_subsystem_frame_update which
+ * triggers the event and then wait for completion */
+
+ if (client->activated && !client->suppressOutput)
{
- int index;
- int numRects = 0;
- const RECTANGLE_16* rects;
- int width, height;
+ /* Send screen update or resize to this client */
/* Check resize */
- shadow_client_calc_desktop_size(server, &width, &height);
- if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height)
+ if (shadow_client_recalc_desktop_size(client))
{
/* Screen size changed, do resize */
- settings->DesktopWidth = width;
- settings->DesktopHeight = height;
-
- /**
- * Unset client activated flag to avoid sending update message during
- * resize. DesktopResize will reactive the client and
- * shadow_client_activate would be invoked later.
- */
- client->activated = FALSE;
-
- /* Send Resize */
- peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize
-
- /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */
- region16_clear(&(client->invalidRegion));
-
- WLog_ERR(TAG, "Client from %s is resized (%dx%d@%d)",
- peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
+ shadow_client_send_resize(client, &gfxstatus);
}
else
{
/* Send frame */
- rects = region16_rects(&(subsystem->invalidRegion), &numRects);
-
- for (index = 0; index < numRects; index++)
- {
- region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]);
- }
-
- shadow_client_send_surface_update(client);
+ shadow_client_send_surface_update(client, &gfxstatus);
}
}
+ else
+ {
+ /* Our client don't receive graphic updates. Just save the invalid region */
+ shadow_client_no_surface_update(client, &gfxstatus);
+ }
/*
* The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event.
WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
break;
}
+
+ if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, "drdynvc"))
+ {
+ /* Dynamic channel status may have been changed after processing */
+ if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_NONE)
+ {
+ /* Call this routine to Initialize drdynvc channel */
+ if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
+ {
+ WLog_ERR(TAG, "Failed to initialize drdynvc channel");
+ break;
+ }
+ }
+ else if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_READY)
+ {
+ /* Init RDPGFX dynamic channel */
+ if (settings->SupportGraphicsPipeline && client->rdpgfx &&
+ !gfxstatus.gfxOpened)
+ {
+ if (!client->rdpgfx->Open(client->rdpgfx))
+ {
+ WLog_ERR(TAG, "Failed to open GraphicsPipeline");
+ settings->SupportGraphicsPipeline = FALSE;
+ }
+
+ client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge;
+
+ gfxstatus.gfxOpened = TRUE;
+ WLog_INFO(TAG, "Gfx Pipeline Opened");
+ }
+ }
+ }
}
if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
}
/* Free channels early because we establish channels in post connect */
+ if (gfxstatus.gfxOpened)
+ {
+ if (gfxstatus.gfxSurfaceCreated)
+ {
+ shadow_client_rdpgfx_release_surface(client);
+ }
+ (void)client->rdpgfx->Close(client->rdpgfx);
+ }
shadow_client_channels_free(client);
if (UpdateSubscriber)
int shadow_encoder_init_rfx(rdpShadowEncoder* encoder)
{
- rdpContext* context = (rdpContext*) encoder->client;
- rdpSettings* settings = context->settings;
-
if (!encoder->rfx)
encoder->rfx = rfx_context_new(TRUE);
rfx_context_set_pixel_format(encoder->rfx, RDP_PIXEL_FORMAT_B8G8R8A8);
- encoder->fps = 16;
- encoder->maxFps = 32;
- encoder->frameId = 0;
- encoder->lastAckframeId = 0;
- encoder->frameAck = settings->SurfaceFrameMarkerEnabled;
-
encoder->codecs |= FREERDP_CODEC_REMOTEFX;
return 1;
encoder->nsc = nsc_context_new();
if (!encoder->nsc)
- return -1;
-
- nsc_context_set_pixel_format(encoder->nsc, RDP_PIXEL_FORMAT_B8G8R8A8);
+ goto fail;
- encoder->fps = 16;
- encoder->maxFps = 32;
- encoder->frameId = 0;
- encoder->lastAckframeId = 0;
- encoder->frameAck = settings->SurfaceFrameMarkerEnabled;
+ if (!nsc_context_reset(encoder->nsc, encoder->width, encoder->height))
+ goto fail;
encoder->nsc->ColorLossLevel = settings->NSCodecColorLossLevel;
encoder->nsc->ChromaSubsamplingLevel = settings->NSCodecAllowSubsampling ? 1 : 0;
encoder->nsc->DynamicColorFidelity = settings->NSCodecAllowDynamicColorFidelity;
+ nsc_context_set_pixel_format(encoder->nsc, RDP_PIXEL_FORMAT_B8G8R8A8);
+
encoder->codecs |= FREERDP_CODEC_NSCODEC;
return 1;
+
+fail:
+ nsc_context_free(encoder->nsc);
+ return -1;
}
int shadow_encoder_init_planar(rdpShadowEncoder* encoder)
}
if (!encoder->planar)
- return -1;
+ goto fail;
+
+ if (!freerdp_bitmap_planar_context_reset(encoder->planar))
+ goto fail;
encoder->codecs |= FREERDP_CODEC_PLANAR;
return 1;
+
+fail:
+ freerdp_bitmap_planar_context_free(encoder->planar);
+ return -1;
}
int shadow_encoder_init_interleaved(rdpShadowEncoder* encoder)
encoder->interleaved = bitmap_interleaved_context_new(TRUE);
if (!encoder->interleaved)
- return -1;
+ goto fail;
+
+ if (!bitmap_interleaved_context_reset(encoder->interleaved))
+ goto fail;
encoder->codecs |= FREERDP_CODEC_INTERLEAVED;
return 1;
+
+fail:
+ bitmap_interleaved_context_free(encoder->interleaved);
+ return -1;
+}
+
+int shadow_encoder_init_h264(rdpShadowEncoder* encoder)
+{
+ if (!encoder->h264)
+ encoder->h264 = h264_context_new(TRUE);
+
+ if (!encoder->h264)
+ goto fail;
+
+ if (!h264_context_reset(encoder->h264, encoder->width, encoder->height))
+ goto fail;
+
+ encoder->codecs |= FREERDP_CODEC_AVC420;
+
+ return 1;
+
+fail:
+ h264_context_free(encoder->h264);
+ return -1;
}
int shadow_encoder_init(rdpShadowEncoder* encoder)
return 1;
}
+int shadow_encoder_uninit_h264(rdpShadowEncoder* encoder)
+{
+ if (encoder->h264)
+ {
+ h264_context_free(encoder->h264);
+ encoder->h264= NULL;
+ }
+
+ encoder->codecs &= ~FREERDP_CODEC_AVC420;
+
+ return 1;
+}
+
int shadow_encoder_uninit(rdpShadowEncoder* encoder)
{
shadow_encoder_uninit_grid(encoder);
shadow_encoder_uninit_interleaved(encoder);
}
+ if (encoder->codecs & FREERDP_CODEC_AVC420)
+ {
+ shadow_encoder_uninit_h264(encoder);
+ }
+
return 1;
}
{
int status;
UINT32 codecs = encoder->codecs;
+ rdpContext* context = (rdpContext*) encoder->client;
+ rdpSettings* settings = context->settings;
status = shadow_encoder_uninit(encoder);
if (status < 0)
return -1;
+ encoder->fps = 16;
+ encoder->maxFps = 32;
+ encoder->frameId = 0;
+ encoder->lastAckframeId = 0;
+ encoder->frameAck = settings->SurfaceFrameMarkerEnabled;
+
return 1;
}
return -1;
}
+ if ((codecs & FREERDP_CODEC_AVC420) && !(encoder->codecs & FREERDP_CODEC_AVC420))
+ {
+ status = shadow_encoder_init_h264(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
return 1;
}
NSC_CONTEXT* nsc;
BITMAP_PLANAR_CONTEXT* planar;
BITMAP_INTERLEAVED_CONTEXT* interleaved;
+ H264_CONTEXT* h264;
int fps;
int maxFps;
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2016 Jiang Zihao <zihao.jiang@yahoo.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#include "shadow_rdpgfx.h"
+
+#define TAG SERVER_TAG("shadow")
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT rdpgfx_caps_advertise(RdpgfxServerContext* context, RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise)
+{
+ UINT16 index;
+ RDPGFX_CAPS_CONFIRM_PDU pdu;
+ rdpSettings* settings = context->rdpcontext->settings;
+ UINT32 flags = 0;
+
+ for (index = 0; index < capsAdvertise->capsSetCount; index++)
+ {
+ pdu.capsSet = &(capsAdvertise->capsSets[index]);
+ if (pdu.capsSet->version == RDPGFX_CAPVERSION_81)
+ {
+ if (settings)
+ {
+ flags = pdu.capsSet->flags;
+ settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT);
+ settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE);
+ settings->GfxH264 = (flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED);
+ }
+
+ return context->CapsConfirm(context, &pdu);
+ }
+ }
+
+ return CHANNEL_RC_UNSUPPORTED_VERSION;
+}
+
+int shadow_client_rdpgfx_init(rdpShadowClient* client)
+{
+ RdpgfxServerContext* rdpgfx;
+ rdpgfx = client->rdpgfx = rdpgfx_server_context_new(client->vcm);
+ if (!rdpgfx)
+ {
+ return 0;
+ }
+
+ rdpgfx->rdpcontext = &client->context;
+
+ rdpgfx->custom = client;
+
+ rdpgfx->CapsAdvertise = rdpgfx_caps_advertise;
+
+ return 1;
+}
+
+void shadow_client_rdpgfx_uninit(rdpShadowClient* client)
+{
+ if (client->rdpgfx)
+ {
+ rdpgfx_server_context_free(client->rdpgfx);
+ client->rdpgfx = NULL;
+ }
+}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2016 Jiang Zihao <zihao.jiang@yahoo.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_SHADOW_SERVER_RDPGFX_H
+#define FREERDP_SHADOW_SERVER_RDPGFX_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int shadow_client_rdpgfx_init(rdpShadowClient* client);
+void shadow_client_rdpgfx_uninit(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SHADOW_SERVER_RDPGFX_H */
if (!(subsystem->updateEvent = shadow_multiclient_new()))
goto fail;
- region16_init(&(subsystem->invalidRegion));
-
if ((status = subsystem->ep.Init(subsystem)) >= 0)
return status;
shadow_multiclient_free(subsystem->updateEvent);
subsystem->updateEvent = NULL;
}
-
- if (subsystem->invalidRegion.data)
- region16_uninit(&(subsystem->invalidRegion));
}
int shadow_subsystem_start(rdpShadowSubsystem* subsystem)