rdpgfx: Implementation for server side channel
authorzihao.jiang <zihao.jiang@yahoo.com>
Sat, 23 Apr 2016 17:49:10 +0000 (01:49 +0800)
committerzihao.jiang <zihao.jiang@yahoo.com>
Sun, 7 Aug 2016 12:14:33 +0000 (20:14 +0800)
server/shadow: support h264 codec with gfx channel

27 files changed:
channels/rdpgfx/CMakeLists.txt
channels/rdpgfx/client/CMakeLists.txt
channels/rdpgfx/rdpgfx_common.c [moved from channels/rdpgfx/client/rdpgfx_common.c with 98% similarity]
channels/rdpgfx/rdpgfx_common.h [moved from channels/rdpgfx/client/rdpgfx_common.h with 100% similarity]
channels/rdpgfx/server/CMakeLists.txt [new file with mode: 0644]
channels/rdpgfx/server/rdpgfx_main.c [new file with mode: 0644]
channels/rdpgfx/server/rdpgfx_main.h [new file with mode: 0644]
include/freerdp/channels/rdpgfx.h
include/freerdp/codec/zgfx.h
include/freerdp/server/rdpgfx.h [new file with mode: 0644]
include/freerdp/server/shadow.h
libfreerdp/codec/test/TestFreeRDPCodecZGfx.c
libfreerdp/codec/zgfx.c
server/shadow/CMakeLists.txt
server/shadow/Mac/mac_shadow.c
server/shadow/Win/win_dxgi.c
server/shadow/Win/win_rdp.c
server/shadow/Win/win_shadow.c
server/shadow/X11/x11_shadow.c
server/shadow/shadow_channels.c
server/shadow/shadow_channels.h
server/shadow/shadow_client.c
server/shadow/shadow_encoder.c
server/shadow/shadow_encoder.h
server/shadow/shadow_rdpgfx.c [new file with mode: 0644]
server/shadow/shadow_rdpgfx.h [new file with mode: 0644]
server/shadow/shadow_subsystem.c

index acc50da..04820de 100644 (file)
@@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS)
        add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
 endif()
 
+if(WITH_SERVER_CHANNELS)
+       add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
index 19c5d21..0de358a 100644 (file)
@@ -22,8 +22,8 @@ set(${MODULE_PREFIX}_SRCS
        rdpgfx_main.h
        rdpgfx_codec.c
        rdpgfx_codec.h
-       rdpgfx_common.c
-       rdpgfx_common.h)
+       ../rdpgfx_common.c
+       ../rdpgfx_common.h)
 
 include_directories(..)
 
similarity index 98%
rename from channels/rdpgfx/client/rdpgfx_common.c
rename to channels/rdpgfx/rdpgfx_common.c
index a7518a5..849554d 100644 (file)
@@ -54,7 +54,8 @@ const char* RDPGFX_CMDID_STRINGS[] =
        "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)
diff --git a/channels/rdpgfx/server/CMakeLists.txt b/channels/rdpgfx/server/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b4aadc2
--- /dev/null
@@ -0,0 +1,34 @@
+# 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")
diff --git a/channels/rdpgfx/server/rdpgfx_main.c b/channels/rdpgfx/server/rdpgfx_main.c
new file mode 100644 (file)
index 0000000..ee31262
--- /dev/null
@@ -0,0 +1,1239 @@
+/**
+ * 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);
+}
diff --git a/channels/rdpgfx/server/rdpgfx_main.h b/channels/rdpgfx/server/rdpgfx_main.h
new file mode 100644 (file)
index 0000000..caad8cc
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * 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 */
index e197149..fdc643b 100644 (file)
@@ -73,6 +73,7 @@ typedef BYTE RDPGFX_PIXELFORMAT;
 #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
 
@@ -372,6 +373,14 @@ struct _RDPGFX_AVC444_BITMAP_STREAM
 };
 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 */
 
index 6fd3582..53a41f8 100644 (file)
 #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;
@@ -53,8 +57,9 @@ typedef struct _ZGFX_CONTEXT ZGFX_CONTEXT;
 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);
 
diff --git a/include/freerdp/server/rdpgfx.h b/include/freerdp/server/rdpgfx.h
new file mode 100644 (file)
index 0000000..7f4d033
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * 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 */
index 60bb87d..e718ea0 100644 (file)
@@ -33,6 +33,7 @@
 #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>
@@ -87,6 +88,7 @@ struct rdp_shadow_client
        BOOL inLobby;
        BOOL mayView;
        BOOL mayInteract;
+       BOOL suppressOutput;
        wMessageQueue* MsgQueue;
        CRITICAL_SECTION lock;
        REGION16 invalidRegion;
@@ -102,6 +104,7 @@ struct rdp_shadow_client
        RemdeskServerContext* remdesk;
        RdpsndServerContext* rdpsnd;
        audin_server_context* audin;
+       RdpgfxServerContext* rdpgfx;
 };
 
 struct rdp_shadow_server
@@ -169,9 +172,12 @@ struct _RDP_SHADOW_ENTRY_POINTS
        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; \
@@ -201,22 +207,7 @@ struct rdp_shadow_subsystem
 };
 
 /* 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 */
index 7284424..ec71287 100644 (file)
 #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;
 }
 
index 6049479..4d53734 100644 (file)
 #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:
  *
@@ -188,7 +191,7 @@ void zgfx_history_buffer_ring_read(ZGFX_CONTEXT* zgfx, int offset, BYTE* dst, UI
        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;
@@ -383,11 +386,106 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** p
        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;
index 55597db..8e4e8e1 100644 (file)
@@ -45,6 +45,8 @@ set(${MODULE_PREFIX}_SRCS
        shadow_rdpsnd.h
        shadow_audin.c
        shadow_audin.h
+       shadow_rdpgfx.c
+       shadow_rdpgfx.h
        shadow_subsystem.c
        shadow_subsystem.h
        shadow_mcevent.c
index 10508c9..e281ffd 100644 (file)
@@ -268,6 +268,7 @@ int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
        size_t numRects;
        const CGRect* rects;
        RECTANGLE_16 invalidRect;
+       rdpShadowSurface* surface = subsystem->server->surface;
        
        rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects, &numRects);
        
@@ -290,7 +291,7 @@ int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
                        invalidRect.bottom /= 2;
                }
                
-               region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
+               region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
        }
        
        return 0;
@@ -326,11 +327,11 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa
        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;
@@ -375,7 +376,7 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa
                
                ArrayList_Unlock(server->clients);
                        
-               region16_clear(&(subsystem->invalidRegion));
+               region16_clear(&(surface->invalidRegion));
        }
        
        if (status != kCGDisplayStreamFrameStatusFrameComplete)
@@ -446,46 +447,9 @@ int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage
 {
        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;
index bd2f5d6..50a1881 100644 (file)
@@ -638,6 +638,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem)
        RECT* pDirtyRectsBuffer;
        DXGI_OUTDUPL_MOVE_RECT* pMoveRect;
        DXGI_OUTDUPL_MOVE_RECT* pMoveRectBuffer;
+       rdpShadowSurface* surface = subsystem->server->surface;
 
        if (subsystem->dxgiFrameInfo.AccumulatedFrames == 0)
                return 0;
@@ -704,7 +705,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem)
                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);
@@ -718,7 +719,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem)
                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;
index 231996e..0bfc9e6 100644 (file)
@@ -63,6 +63,7 @@ BOOL shw_end_paint(rdpContext* context)
        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;
@@ -74,7 +75,7 @@ BOOL shw_end_paint(rdpContext* context)
                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);
index 0f23b9c..4cc86cc 100644 (file)
@@ -219,7 +219,7 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem)
 
        if (ArrayList_Count(server->clients) < 1)
        {
-               region16_clear(&(subsystem->invalidRegion));
+               region16_clear(&(surface->invalidRegion));
                return 1;
        }
 
@@ -228,12 +228,12 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem)
        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);
@@ -286,7 +286,7 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem)
 
        ArrayList_Unlock(server->clients);
 
-       region16_clear(&(subsystem->invalidRegion));
+       region16_clear(&(surface->invalidRegion));
 
        return 1;
 }
index 8c5e159..0d0f624 100644 (file)
@@ -763,9 +763,6 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
        if (count < 1)
                return 1;
 
-       if ((count == 1) && subsystem->suppressOutput)
-               return 1;
-
        surfaceRect.left = 0;
        surfaceRect.top = 0;
        surfaceRect.right = surface->width;
@@ -814,12 +811,12 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
 
        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;
@@ -849,7 +846,7 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
                        }
                }
 
-               region16_clear(&(subsystem->invalidRegion));
+               region16_clear(&(surface->invalidRegion));
        }
 
        if (!subsystem->use_xshm)
@@ -868,46 +865,9 @@ int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage
 {
        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;
index bc90ab4..983b16f 100644 (file)
@@ -43,11 +43,21 @@ UINT shadow_client_channels_post_connect(rdpShadowClient* client)
 
        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);
index edba39c..e35b6f6 100644 (file)
@@ -28,6 +28,7 @@
 #include "shadow_remdesk.h"
 #include "shadow_rdpsnd.h"
 #include "shadow_audin.h"
+#include "shadow_rdpgfx.h"
 
 #ifdef __cplusplus
 extern "C" {
index 8d433e2..010289c 100644 (file)
 
 #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;
@@ -44,7 +96,7 @@ static void shadow_client_free_queued_message(void *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;
@@ -62,7 +114,8 @@ BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
        settings->BitmapCacheV3Enabled = TRUE;
        settings->FrameMarkerCommandEnabled = TRUE;
        settings->SurfaceFrameMarkerEnabled = TRUE;
-       settings->SupportGraphicsPipeline = FALSE;
+       settings->SupportGraphicsPipeline = TRUE;
+       settings->GfxH264 = FALSE;
 
        settings->DrawAllowSkipAlpha = TRUE;
        settings->DrawAllowColorSubsampling = TRUE;
@@ -130,40 +183,36 @@ fail_cert_file:
        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:
@@ -173,42 +222,90 @@ void shadow_client_message_free(wMessage* message)
        }
 }
 
-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;
@@ -216,30 +313,29 @@ BOOL shadow_client_post_connect(freerdp_peer* peer)
        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;
 
@@ -271,7 +367,7 @@ BOOL shadow_client_post_connect(freerdp_peer* peer)
 
 /* 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)
        {
@@ -289,71 +385,79 @@ static INLINE void shadow_client_convert_rects(rdpShadowClient* client,
        }
        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, &region, area, 1);
+                       shadow_client_mark_invalid(client, 1, &region);
+               }
+               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;
@@ -375,6 +479,7 @@ BOOL shadow_client_activate(freerdp_peer* peer)
 
        shadow_encoder_reset(client->encoder);
 
+       /* Update full screen in next update */
        return shadow_client_refresh_rect(client, 0, NULL);
 }
 
@@ -439,7 +544,7 @@ BOOL shadow_client_logon(freerdp_peer* peer, SEC_WINNT_AUTH_IDENTITY* identity,
        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 
@@ -454,28 +559,91 @@ BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 fra
        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 = &regionRect;
+               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;
@@ -492,24 +660,6 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
        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);
 
@@ -613,14 +763,13 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
        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;
@@ -649,25 +798,8 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
        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);
@@ -827,7 +959,7 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
        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;
@@ -840,6 +972,11 @@ int shadow_client_send_surface_update(rdpShadowClient* client)
        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;
@@ -856,6 +993,12 @@ int shadow_client_send_surface_update(rdpShadowClient* client)
 
        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;
@@ -870,27 +1013,63 @@ int shadow_client_send_surface_update(rdpShadowClient* client)
 
        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);
@@ -898,27 +1077,67 @@ int shadow_client_send_surface_update(rdpShadowClient* client)
        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;
@@ -1013,7 +1232,7 @@ int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* m
        return 1;
 }
 
-void* shadow_client_thread(rdpShadowClient* client)
+static void* shadow_client_thread(rdpShadowClient* client)
 {
        DWORD status;
        DWORD nCount;
@@ -1034,6 +1253,11 @@ void* shadow_client_thread(rdpShadowClient* client)
        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;
@@ -1080,50 +1304,35 @@ void* shadow_client_thread(rdpShadowClient* client)
 
                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.
@@ -1139,6 +1348,38 @@ void* shadow_client_thread(rdpShadowClient* client)
                                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)
@@ -1220,6 +1461,14 @@ void* shadow_client_thread(rdpShadowClient* client)
        }
 
        /* 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)
index 07bf880..be3194c 100644 (file)
@@ -130,9 +130,6 @@ int shadow_encoder_uninit_grid(rdpShadowEncoder* encoder)
 
 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);
 
@@ -148,12 +145,6 @@ int shadow_encoder_init_rfx(rdpShadowEncoder* encoder)
 
        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;
@@ -172,23 +163,24 @@ int shadow_encoder_init_nsc(rdpShadowEncoder* encoder)
                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)
@@ -209,11 +201,18 @@ 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)
@@ -222,11 +221,38 @@ 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)
@@ -300,6 +326,19 @@ int shadow_encoder_uninit_interleaved(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);
@@ -330,6 +369,11 @@ int shadow_encoder_uninit(rdpShadowEncoder* encoder)
                shadow_encoder_uninit_interleaved(encoder);
        }
 
+       if (encoder->codecs & FREERDP_CODEC_AVC420)
+       {
+               shadow_encoder_uninit_h264(encoder);
+       }
+
        return 1;
 }
 
@@ -337,6 +381,8 @@ int shadow_encoder_reset(rdpShadowEncoder* encoder)
 {
        int status;
        UINT32 codecs = encoder->codecs;
+       rdpContext* context = (rdpContext*) encoder->client;
+       rdpSettings* settings = context->settings;
 
        status = shadow_encoder_uninit(encoder);
 
@@ -353,6 +399,12 @@ int shadow_encoder_reset(rdpShadowEncoder* encoder)
        if (status < 0)
                return -1;
 
+       encoder->fps = 16;
+       encoder->maxFps = 32;
+       encoder->frameId = 0;
+       encoder->lastAckframeId = 0;
+       encoder->frameAck = settings->SurfaceFrameMarkerEnabled;
+
        return 1;
 }
 
@@ -392,6 +444,14 @@ int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs)
                        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;
 }
 
index fd7d46a..b2c01b9 100644 (file)
@@ -49,6 +49,7 @@ struct rdp_shadow_encoder
        NSC_CONTEXT* nsc;
        BITMAP_PLANAR_CONTEXT* planar;
        BITMAP_INTERLEAVED_CONTEXT* interleaved;
+       H264_CONTEXT* h264;
 
        int fps;
        int maxFps;
diff --git a/server/shadow/shadow_rdpgfx.c b/server/shadow/shadow_rdpgfx.c
new file mode 100644 (file)
index 0000000..51459ec
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * 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;
+       }
+}
diff --git a/server/shadow/shadow_rdpgfx.h b/server/shadow/shadow_rdpgfx.h
new file mode 100644 (file)
index 0000000..0ac810d
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * 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 */
index 0cbb46f..7f3173a 100644 (file)
@@ -86,8 +86,6 @@ int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server
        if (!(subsystem->updateEvent = shadow_multiclient_new()))
                goto fail;
 
-       region16_init(&(subsystem->invalidRegion));
-
        if ((status = subsystem->ep.Init(subsystem)) >= 0)
                return status;
 
@@ -141,9 +139,6 @@ void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem)
                shadow_multiclient_free(subsystem->updateEvent);
                subsystem->updateEvent = NULL;
        }
-
-       if (subsystem->invalidRegion.data)
-               region16_uninit(&(subsystem->invalidRegion));
 }
 
 int shadow_subsystem_start(rdpShadowSubsystem* subsystem)